networksecurity/ipsec/ipsec6/src/sadb.cpp
changeset 0 af10295192d8
child 28 c1ae5b4952fc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networksecurity/ipsec/ipsec6/src/sadb.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,1002 @@
+// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// sadb.cpp - IPv6/IPv4 IPSEC security association database
+//
+
+#include <networking/pfkeyv2.h>
+#include "sadb.h"
+#include "sa_crypt.h"
+#include "pfkeymsg.h"
+#include <networking/ipsecerr.h>
+#include "ipseclog.h"
+#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
+#include "spdb.h"
+#endif // SYMBIAN_IPSEC_VOIP_SUPPORT
+
+// This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be
+// passed as a template parameter
+#if defined(__X86GCC__) || defined(__GCCE__)
+#define KSecurityAssocTimeoutOffset 256
+__ASSERT_COMPILE(KSecurityAssocTimeoutOffset == _FOFF(CSecurityAssoc, iTimeout));
+#else
+#define KSecurityAssocTimeoutOffset _FOFF(CSecurityAssoc, iTimeout)
+#endif
+
+class CSecurityAssocTimeoutLinkage : public TimeoutLinkage<CSecurityAssoc, KSecurityAssocTimeoutOffset>
+	/**
+	* Relay Timeout callback.
+	*
+	* This "linkage" receives the timeout callback from RTimeout::iTimeout
+	* and relays the event to CSecurityAssoc::TimerExprired() function. 
+	*/
+	{
+public:
+	static void Timeout(RTimeout &aLink, const TTime &aNow, TAny *aPtr)
+		{
+		Object(aLink)->TimerExpired(*(MAssociationManager*)aPtr, aNow);
+		}
+	};
+
+
+TLifetime::TLifetime(const struct sadb_lifetime &aLifetime)
+	/**
+	* Construct a lifetime object from the PF_KEY sadb_lifetime value.
+	*
+	* @param aLifetime The PFKEY lifetime value.
+	*/
+	{
+	iAllocations = aLifetime.sadb_lifetime_allocations;
+	iBytes = aLifetime.sadb_lifetime_bytes;
+
+	// Load the seconds values as is into TTime to be used
+	// later in Freeze to convert into a real time stamp.
+	iAddtime = aLifetime.sadb_lifetime_addtime;
+	iUsetime = aLifetime.sadb_lifetime_usetime;
+	}
+
+TLifetime::TLifetime() : iAllocations(0), iBytes(0), iAddtime(0), iUsetime(0)
+	/**
+	* Default Lifetime initializer.
+	*/
+	{
+	}
+
+
+void TLifetime::Freeze(TTime &aTime, const TTime &aNow)
+	/**
+	* Freeze the lifetime.
+	*
+	* Convert the relative time (aTime) stored in a TTime as seconds
+	* into absolute time counting from the given time stamp (aNow).
+	*
+	* @param aTime	The time to freeze
+	* @param aNow	The current time.
+	*/
+	{
+	TInt64 x = aTime.Int64();
+
+	aTime = Time::MaxTTime();
+	if (x <= 0)
+		// Zero is used to indicate no limit. PFKEY has 64 bit
+		// unsigned int values, but EPOC32 64 bit value is signed.
+		// if the requested value is large enough to set the sign,
+		// treat it the same as 0, use MaxTTime.
+		return;
+	if (x > aTime.Int64() / 1000000)
+		// Conversion to Microseconds would cause overflow, use MaxTTime
+		return;
+	x *= 1000000;
+	if (aTime.Int64() - x < aNow.Int64())
+		// Adding x to now would overflow, use MaxTTime
+		return;
+	// It is safe to add interval to now
+	aTime = aNow;
+	aTime += TTimeIntervalMicroSeconds(x);
+	}
+
+CSecurityAssoc::CSecurityAssoc(const TPfkeyMessage &aMsg) : iTimeout(CSecurityAssocTimeoutLinkage::Timeout)
+	/**
+	* Construct SA
+	*
+	* This constructor is only used through the GETSPI and ADD.
+	* It creates a larval SA
+	*
+	*		<base, address, SPI range>
+	*		<base, SA, (lifetimes(HS),) address(SD), (address(P)), ...>
+	*
+	* Only base, address and SPI from SA are handled by the constructor.
+	* ADD must call UpdateL() to get the remainging extensions processed!
+	*
+	* @li	Lifetimes are initialized, but the TIMER is not activated
+	*		here, because timer activation may cause an immediate
+	*		expiration and deletion of CSecurityAssoc.
+	*
+	* @param aMsg	The PFKEY message
+	*/
+	{
+	// iBase must always be present!
+	iType = aMsg.iBase.iMsg->sadb_msg_satype;
+	iState = SADB_SASTATE_LARVAL;
+	if (aMsg.iSa.iExt)
+		iSPI = aMsg.iSa.iExt->sadb_sa_spi;
+	
+	// When src_specific is off from the SA spsecification, then
+	// the incoming SA has the dst unspecified, and outgoing SA
+	// has the src unspecified. Unspecified can be expressed either
+	// by explicitly setting the address as unspecified or just
+	// leaving the extension out from the PFKEY message. Need to
+	// test the presense of extension in both src and dst.
+	// (By default everything is unspecified).
+	if (aMsg.iDstAddr.iExt)
+		{
+		iDst = aMsg.iDstAddr.iAddr;
+		iInfo.iPortDst = aMsg.iDstAddr.iPort;
+		}
+	if (aMsg.iSrcAddr.iExt)
+		{
+		iSrc = aMsg.iSrcAddr.iAddr;
+		iInfo.iPortSrc = aMsg.iSrcAddr.iPort;
+		}
+	if (aMsg.iProxyAddr.iExt)
+		{
+		iInfo.iSrc = aMsg.iProxyAddr.iAddr;
+		}
+
+	// Set some current times
+	TTime time_now;
+	time_now.UniversalTime();
+	iCurrent.iAddtime = time_now;
+	iCurrent.iUsetime = Time::NullTTime();	// as iUsed==0
+	TLifetime::Freeze(iSoft.iAddtime, time_now);
+
+	// Set the hard lifetime to the default Larval timeout
+	iHard.iAddtime = KLifetime_LARVAL_DEFAULT;
+	TLifetime::Freeze(iHard.iAddtime, time_now);
+
+	// All Other fields are assumed to contain initial
+	// default values (NULL, 0)
+	}
+
+CSecurityAssoc::CSecurityAssoc
+	(
+	const TSecurityAssocSpec &aSpec,
+	const RIpAddress &aSrc,
+	const RIpAddress &aDst,
+	const RAssociationInfo &aInfo
+	) : iTimeout(CSecurityAssocTimeoutLinkage::Timeout)
+	/**
+	* Construct "larval" SA.
+	*
+	* This constructor is used for the outgoing packet and it creates
+	* a special LARVAL EGG SA (SPI == 0). Called only for ACQUIRE
+	* processing.
+	*
+	* @param aSpec The SA template
+	* @param aSrc The SA src address
+	* @param aDst The SA dst address
+	* @param aInfo Additional information
+	*/
+	{
+	//
+	// Contrary to PFKEY text, this implementation contains two kinds of
+	// "larval" SA's. This constructor creates an "pre-larval" SA, an "egg"
+	// which is recognized from the fact that SPI == 0 (this implementation
+	// reserves 0 for internal use and never gives it out as an SPI value)
+	iType = aSpec.iType;
+	iSPI = 0;
+	iDst = aDst;
+	iSrc = aSrc;
+	iInfo = aInfo;
+
+	// Reference objects, if present need to be refcounted on copy
+	if (iInfo.iSrcIdentity)
+		iInfo.iSrcIdentity->Open();
+	if (iInfo.iDstIdentity)
+		iInfo.iDstIdentity->Open();
+
+	iState = SADB_SASTATE_LARVAL;
+
+	iFlags = aSpec.iPfs ? SADB_SAFLAGS_PFS : 0;
+	iReplayCheck = (aSpec.iReplayWindowLength != 0);
+	//
+	// Larval egg does not activate algorithm engines
+	// (they need to be created by update!)
+	iAalg = aSpec.iAalg;
+	iEalg = aSpec.iEalg;
+
+	//
+	// Set some base times
+	//
+	TTime time_now;
+	time_now.UniversalTime();
+	iCurrent.iAddtime = time_now;
+	iCurrent.iUsetime = Time::NullTTime();	// as iUsed==0
+	//
+	// Set the hard lifetime from the specification, soft
+	// lifetime is left as initial infinite. Unspecified
+	// iLarvalLifetime is a request to use the default
+	// timeout (one cannot request 'infinite' for this)
+	iHard.iAddtime = aSpec.iLarvalLifetime ?
+		aSpec.iLarvalLifetime : KLifetime_LARVAL_DEFAULT;
+	TLifetime::Freeze(iSoft.iAddtime, time_now);
+	TLifetime::Freeze(iHard.iAddtime, time_now);
+	}
+
+TInt CSecurityAssoc::UpdateL(MAssociationManager &aManager, const TPfkeyMessage &aMsg, CIpsecCryptoManager *aLib)
+	/**
+	* Update SA from PFKEY message.
+	*
+	* Initialize CSecurityAssoc from TPfkeyMessage. This is a "blind"
+	* operation, most fields are just updated unconditionally, it is
+	* assumed that the calling function has verified the legality of
+	* this operation. (Some basic checks are done)
+	*
+	* @param aManager The association manager
+	* @param aMsg The PFKEY message
+	* @param aLib The crypto manager
+	*/
+	{
+	TInt set_hard = 0;
+
+	// Loading Security Association
+	if (aMsg.iSa.iExt)
+		{
+		//
+		// If state is LARVAL, need to "defuse" the internal
+		// timeout from the HARD lifetime field, if set.
+		// (do this only if aMsg doesn't already rewrite the time)
+		//
+		set_hard = (iState == SADB_SASTATE_LARVAL);
+		if (aMsg.iSa.iExt->sadb_sa_state != SADB_SASTATE_MATURE)
+			return KErrGeneral;	 // Only MATURE state can be set.
+		if (iSPI && iSPI != aMsg.iSa.iExt->sadb_sa_spi)
+			return KErrGeneral;	 // Once SPI is set, it cannot be changed!
+		iSPI = aMsg.iSa.iExt->sadb_sa_spi;
+		iFlags = aMsg.iSa.iExt->sadb_sa_flags;
+		iReplayCheck = (aMsg.iSa.iExt->sadb_sa_replay != 0);
+
+		// This information can only be set for new SA, it cannot
+		// be changed or updated (applies to all 'selector' info,
+		// including Identity.
+		if (aMsg.iSrcAddr.iExt)
+			{
+			iSrc = aMsg.iSrcAddr.iAddr;
+			iInfo.iPortSrc = aMsg.iSrcAddr.iPort;
+			}
+		if (aMsg.iDstAddr.iExt)
+			{
+			iDst = aMsg.iDstAddr.iAddr;
+			iInfo.iPortDst = aMsg.iDstAddr.iPort;
+			iInfo.iProtocol = aMsg.iDstAddr.iExt->sadb_address_proto;
+			}
+		if (aMsg.iProxyAddr.iExt)
+			{
+			iInfo.iSrc = aMsg.iProxyAddr.iAddr;
+			//
+			// Do a special hack: if the proxy address portion is unspecified
+			// address, but scope id non-zero, then assume the scope id is
+			// actually the interface index of the VPN tunnel interface.
+			// (NOTE: this is only useful for inbound SA's?)
+			if (iInfo.iSrc().IsUnspecified() && iInfo.iSrc().iScope != 0)
+				{
+				iTunnelIndex = iInfo.iSrc().iScope;
+				iInfo.iSrc.Close();
+				}
+			}
+		}
+
+	// Updating identities should probably only be allowed
+	// for larval SA's
+	if (aMsg.iDstIdent.iExt && iInfo.iDstIdentity == NULL)
+		iInfo.iDstIdentity = CIdentity::NewL(aMsg.iDstIdent.iData, aMsg.iDstIdent.iExt->sadb_ident_type);
+	if (aMsg.iSrcIdent.iExt && iInfo.iSrcIdentity == NULL)
+		iInfo.iSrcIdentity = CIdentity::NewL(aMsg.iSrcIdent.iData, aMsg.iSrcIdent.iExt->sadb_ident_type);
+
+	//	time_now is used in initialize of the IV and
+	//	for lifetimes, so declare and initialize it here
+	TTime time_now;
+	time_now.UniversalTime();
+
+	// Activating crypto engines
+	//		but only if neither of them have been activated before.
+	//		If already active,just silently ignore the parameters
+	//		in the Message.
+	//		(the test below leaves NULL/NULL auth/encrypt situation
+	//		open to later update!)
+	if (aMsg.iSa.iExt && !iAeng && !iEeng)
+		{
+		iAalg = aMsg.iSa.iExt->sadb_sa_auth;
+
+		if (iAalg)
+			{
+#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT	
+			if (iAalg == SADB_AALG_AES_XCBC_MAC)
+				{
+				iAeng = aLib->NewMacL(iAalg, aMsg.iAuthKey.iData);
+				}
+			else
+				{
+#endif // SYMBIAN_IPSEC_VOIP_SUPPORT				
+				iAeng = aLib->NewAuthL(iAalg, aMsg.iAuthKey.iData);
+#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT				
+				}
+#endif 				
+			if (!iAeng)
+				User::Leave(KErrNotFound);	// Conf. Error: algorithm not installed!
+			}	
+		iEalg = aMsg.iSa.iExt->sadb_sa_encrypt;
+		if (iEalg)
+			{
+			iEeng = aLib->NewEncryptL(iEalg, aMsg.iEncryptKey.iData);
+			if (!iEeng)
+				User::Leave(KErrNotFound);	// Conf. Error: algorithm not installed!
+			}
+		if (iEeng)
+			{
+			// NOTE: this must be allocated even if 0 length!
+			delete iIV;		// ..just in case...
+			iIV = NULL;		// ..just in case
+			iIV = HBufC8::NewL(iEeng->IVSize());
+			// The initial content of the IV should be some random
+			// bit pattern (important thing is to have something that
+			// changes, and is not always Zero, thus the time_now is
+			// used). [Using uninitialized memory would be a security
+			// risk -- it might even contain the key information!!!]
+			// (Use of time is a dubious thing, as it might provide
+			// information to be used in some other attacks, as it
+			// reveals the exact CPU clock time; but it will do for now!)
+			TPtr8 iv(iIV->Des());
+			TPtr8 fill = TPtr8((TUint8 *)&time_now, sizeof(time_now), sizeof(time_now));
+			// Do not use iv.MaxLength(), it may be larger IV size!
+			TInt n = iEeng->IVSize();
+			while (n > 0)
+				{
+				if (fill.Length() > n)
+					fill.SetLength(n);
+				iv.Append(fill);
+				n -= fill.Length(); // This loop will hang, if fill.Length() == 0!!
+				}
+			}
+		}
+
+	//	Loading lifetimes
+	//		(Done after all "leaving" and error returns, so that initial life
+	//		timers won't get overridden by Update, if it fails
+	if (aMsg.iHard.iExt)
+		{
+		iHard = TLifetime(*aMsg.iHard.iExt);
+		if (iUsed)
+			TLifetime::Freeze(iHard.iUsetime, iCurrent.iUsetime);
+		TLifetime::Freeze(iHard.iAddtime, time_now);
+		}
+	else if (set_hard)
+		{
+		iHard = TLifetime();
+		TLifetime::Freeze(iHard.iAddtime, time_now);
+		}
+	if (aMsg.iSoft.iExt)
+		{
+		iSoft = TLifetime(*aMsg.iSoft.iExt);
+		if (iUsed)
+			TLifetime::Freeze(iSoft.iUsetime, iCurrent.iUsetime);
+		TLifetime::Freeze(iSoft.iAddtime, time_now);
+		}
+
+	// Load Traffic Selector
+	if (aMsg.iTs.iExt)
+		{
+		iTS.Reset();
+		RTrafficSelector dummy;
+		for (TInt i = 0; i < aMsg.iTs.iExt->sadb_x_ts_numsel; ++i)
+			{
+			User::LeaveIfError(iTS.Append(dummy));
+			RTrafficSelector &ts = iTS[i];
+			const T_sadb_selector &in_ts = aMsg.iTs.Selector(i);
+			ts.iProtocol = in_ts.sadb_x_selector_proto;
+			ts.iPortSrc = in_ts.iSrc.Port();
+			ts.iPortDst = in_ts.iDst.Port();
+			User::LeaveIfError(ts.iSrc.Open(aManager.EndPointCollection(), in_ts.iSrc));
+			User::LeaveIfError(ts.iDst.Open(aManager.EndPointCollection(), in_ts.iDst));
+			}
+		}
+
+	// Replace CNatTraversal object to handle ESP UDP encapsulation (created if required)
+	delete iNatTraversal;
+	iNatTraversal = CNatTraversal::New(iFlags, aMsg.iPrivateExtension);
+
+	// 'set_hard' is only defined when Update wants to change the SA
+	// state into MATURE. The actual state change is delayed until
+	// here, in case update fails before this. The SA will most likely
+	// be unusable, but at least it won't be MATURE either...
+	if (set_hard)
+		iState = SADB_SASTATE_MATURE;
+	return KErrNone;
+	}
+
+void CSecurityAssoc::Cleanup()
+	/**
+	* Release all memory resources attached to a SA.
+	*/
+	{
+	iTimeout.Cancel();
+	if (iInfo.iSrcIdentity)
+		{
+		iInfo.iSrcIdentity->Close();
+		iInfo.iSrcIdentity = NULL;
+		}
+	if (iInfo.iDstIdentity)
+		{
+		iInfo.iDstIdentity->Close();
+		iInfo.iDstIdentity = NULL;
+		}
+
+	iTS.Reset();
+
+	delete iIV; iIV = NULL;
+
+	// Detach all handles
+	while (!iHandles.IsDetached())
+		{
+		RSecurityAssociation *handle = (RSecurityAssociation *)iHandles.iNext;
+		ASSERT(handle->iAssociation == this);
+
+		 /* Since the iAssociation was never set, so the iErrValue is set over here
+	       * which will be used by sc_prt6.cpp to pass the error value
+           * from the kmd to the tcp/ip stack
+           */
+		handle->SetError(iErrValue);
+        		
+		handle->None();
+		(handle->iCallback)(*handle);
+		}
+
+	delete iEeng; iEeng = NULL;
+	delete iAeng; iAeng = NULL;
+
+	delete iNatTraversal; iNatTraversal = NULL;
+	}
+
+CSecurityAssoc::~CSecurityAssoc()
+	/**
+	* Destructor.
+	*/
+	{
+	Cleanup();
+	}
+
+TInt CSecurityAssoc::Close()
+	/**
+	* Decrement the reference count.
+	*
+	* @return
+	*	@li KErrNone, if association still exists
+	*	@li KErrDied, if association has been deleted.
+	*/
+	{
+	if (--iRefs < 0)
+		{
+		delete this;
+		return KErrDied;
+		}
+	return KErrNone;
+	}
+
+
+TBool RTrafficSelector::operator<=(const RTrafficSelector &aSel) const
+	/**
+	* The <= comparison for selector values.
+	*
+	* Every field must fullfull the "<="-condition individually for
+	* this to return true. This operator should only be used to
+	* compare individual packet values against a min and max
+	* range:
+	* @code min <= packetdata <= max
+	* @endcode
+	*
+	* @param aSel The selector (packet or max). With 'packet', this = min, and
+	* with 'max', this is 'packet'.
+	*
+	* @see CSecurityAssoc::Match
+	*/
+	{
+	return
+		iProtocol <= aSel.iProtocol &&
+		iPortSrc <= aSel.iPortSrc &&
+		iPortDst <= aSel.iPortDst &&
+		iSrc() <= aSel.iSrc() &&
+		iDst() <= aSel.iDst();
+	}
+
+// This is defined based on the assumption that RArray<>::Reset()/Close()
+// does not run destructor on the array elements. Because RTrafficSelector
+// holds handles for addresses, they must be closed before the actual
+// RArray methods are called.
+void RTrafficSelectorSet::Reset()
+	{
+	/**
+	* Release Traffic Selector space
+	*/
+	TInt i = Count();
+	while (--i >= 0)
+		{
+		(*this)[i].iSrc.Close();
+		(*this)[i].iDst.Close();
+		}
+	RArray<RTrafficSelector>::Reset();
+	}
+
+
+TInt CSecurityAssoc::MatchSpec
+	(
+	const TSecurityAssocSpec &aSpec,
+#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
+	const CPropList *aPropList,
+#endif	//SYMBIAN_IPSEC_VOIP_SUPPORT	
+	const RIpAddress &aSrc,
+	const RIpAddress &aDst,
+	const RAssociationInfo &aInfo,
+	const RTrafficSelector &aPkt
+	) const
+	/**
+	* Compare SA to SA template and per packet information.
+	*
+	* Test if the SA matches the requirements.
+	*
+	* @param aSpec	The SA template
+	* @param aSrc	The expected SA source address
+	* @param aDst	The expected SA destination address
+	* @param aInfo	The additional information (PFP information, etc)
+	* @param aPkt	The packet information (to check agains TS)
+	*
+	* @return KErrNone, when a match occurs, or negative error,
+	* when some mismatch is detected
+	*/
+	{
+	if (iDst() != aDst())
+		return EIpsec_MismatchedDestination;
+	if (iSrc() != aSrc())
+		return EIpsec_MismatchSource;
+	
+#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT	
+	if (((iFlags & SADB_SAFLAGS_PFS) == 0) != (aSpec.iPfs == 0))
+		return EIpsec_MismatchedPFS;
+	for (TInt i=0;i<aPropList->Count();i++)
+		{
+		CSecurityProposalSpec *proposal = aPropList->At(i);
+		TInt ret=KErrNone;
+	
+		if (iType != proposal->iType) ret= EIpsec_MismatchedType;
+		if (iAalg != proposal->iAalg) ret = EIpsec_MismatchedAuthAlg;
+		if (iEalg != proposal->iEalg) ret = EIpsec_MismatchedEncryptAlg;
+		if (ret == KErrNone) break;
+		if ( (i == aPropList->Count()-1)&&(ret != KErrNone) ) return ret;
+		}
+#else
+	if (iType != aSpec.iType)
+		return EIpsec_MismatchedType;
+	if (((iFlags & SADB_SAFLAGS_PFS) == 0) != (aSpec.iPfs == 0))
+		return EIpsec_MismatchedPFS;
+	if (iAalg != aSpec.iAalg)
+		return EIpsec_MismatchedAuthAlg;
+	if (iEalg != aSpec.iEalg)
+		return EIpsec_MismatchedEncryptAlg;
+#endif //SYMBIAN_IPSEC_VOIP_SUPPORT		
+
+	
+	// Fail ReplayCheck test only if the SA specification
+	// requires replay check, but SA doesn't enable it.
+	// But, allow SA to require replay check, even if spec doesn't.
+	if (aSpec.iReplayWindowLength && !iReplayCheck)
+		return EIpsec_MismatchReplayWindow;
+
+	if (iInfo.iProtocol != aInfo.iProtocol)
+		return EIpsec_MismatchProtocol;
+	if (iInfo.iPortSrc != aInfo.iPortSrc)
+		return EIpsec_MismatchSourcePort;
+	if (iInfo.iPortDst != aInfo.iPortDst)
+		return EIpsec_MismatchDestinationPort;
+	
+	// The "proxy" of PKFEY is stored in iInfo.iSrc of SA
+	if (iInfo.iSrc() != aInfo.iSrc())
+		return EIpsec_MismatchProxy;
+	if (iInfo.iDst() != aInfo.iDst())
+		return EIpsec_MismatchProxy;
+	
+	// *NOTE*
+	//		Identities need to be in aInfo, because TSecurityAssocSpec can
+	//		be shared between inbound and outboud, and identities are expressed
+	//		as remote and local. The caller knowing the context must copy the
+	//		pointers into RAssociationInfo properly!
+	//
+	//		As identity blocks are shared, do raw pointer compare first, it should
+	//		optimize at least the processing of outbound SA's (as for those, the
+	//		pointers are just references to the identities in TSecurityAssocSpec)!
+	//
+	if (iInfo.iDstIdentity != aInfo.iDstIdentity &&
+		(iInfo.iDstIdentity == NULL ||		// No match if, either is NULL
+		 aInfo.iDstIdentity == NULL ||		//
+		 !iInfo.iDstIdentity->Match(*aInfo.iDstIdentity)))
+		 return EIpsec_MismatchDestinationIdentity;
+	if (iInfo.iSrcIdentity != aInfo.iSrcIdentity &&
+		(iInfo.iSrcIdentity == NULL ||		// No match if, either is NULL
+		 aInfo.iSrcIdentity == NULL ||		//
+		 !iInfo.iSrcIdentity->Match(*aInfo.iSrcIdentity)))
+		 return EIpsec_MismatchSourceIdentity;
+		 
+	// Finally, verify the Traffic Selector Match
+
+	TInt i = iTS.Count();
+	if (i == 0)
+		return KErrNone;	// SA does not have traffic selectors (implicit match all)
+
+	while (i > 1)
+		{
+		const RTrafficSelector &max = iTS[--i];
+		const RTrafficSelector &min = iTS[--i];
+		// Is packet info within range?
+		if (min <= aPkt && aPkt <= max)
+			return KErrNone;
+		}
+
+	return EIpsec_MismatchProxy;	// [ Need a new error code for TS mismatch! -- msa]
+	}
+
+
+
+int CSecurityAssoc::ReplayCheck(TUint32 aSeq)
+	/**
+	* Check a sequence number against a replay window.
+	*
+	* Perform the sequence number tracking and replay window
+	* checking. After checking the iTestSeq will contain the
+	* correct sequence number, and if ESN is used, the high
+	* order part is assigned according to the ESN rules.
+	*
+	* @param aSeq The low order 32 bits of the sequence number
+	*
+	* @return
+	*	- 0, if too old or duplicate,
+	*	- 1, if not duplicate or after window
+	*/
+	{
+	iTestInWindow = 0;
+	iTestSeq = aSeq;
+	iTestSeq.SetHigh(iRecvSeq.High());
+	if (iFlags & SABD_SAFLAGS_ESN)
+		{
+		// Extended Sequence Number enabled. The authentication code
+		// requires the correct high order bits, and this code must
+		// be executed whether replay check is enabled or not
+		//
+		// *NOTE* The following is based modulo 2**32 (unsigned) arithmetic!
+		//
+		const TUint32 lower = iRecvSeq - KMaxReplayWindowLength + 1;
+		if (lower <= -KMaxReplayWindowLength)
+			{
+			if (aSeq >= lower)
+				{
+				// After window begin
+				if (aSeq > iRecvSeq)
+					return 1;		// After window end, Check integrity only
+				// Inside window, check replay bitmap
+				}
+			else
+				{
+				// Before window begin, wrapped around
+				iTestSeq.SetHigh(iTestSeq.High() + 1);
+				return 1;			// After window, check integrity only
+				}
+			}
+		else
+			{
+			if (aSeq >= lower)
+				{
+				iTestSeq.SetHigh(iTestSeq.High() - 1);
+				// Inside window, check replay bitmap
+				}
+			else
+				{
+				if (aSeq > iRecvSeq)
+					{
+					return 1;	// Check integrity only
+					}
+				// Inside window, check replay bitmap
+				}
+			}
+		if (!iReplayCheck)
+			return 1;	// ESN without replay check!
+		}
+	else if (!iReplayCheck)
+		{
+		// No replay check, Normal Sequence numbers
+		return 1;
+		}
+	else
+		{
+		// Replay Check, Normal Sequence Numbers
+		
+		//LOG(Log::Printf(_L("SA[%u] seq=%u:%u"), this, iTestSeq.High(), (TInt)iTestSeq));
+		if (aSeq == 0) 
+			return 0;			// Zero is illegal sequence number
+		if (aSeq > iRecvSeq)	// New larger sequence number
+			return 1;
+		const TUint32 diff = iRecvSeq - aSeq;
+		if (diff >= KMaxReplayWindowLength)
+			return 0;			// Too old or wrapped
+		}
+	iTestInWindow = 1;
+	const TInt i = BitmapWord(aSeq);
+	const TInt b = BitmapBit(aSeq);
+//	LOG(Log::Printf(_L("SA[%u] RC seq=%u:%u iBitmap[%d] bit %d = %d"),
+//		this, iTestSeq.High(), (TInt)iTestSeq, i, b, (iBitmap[i] & (1L << b)) != 0));
+	return (iBitmap[i] & (1L << b)) == 0;
+	}
+
+
+void CSecurityAssoc::ReplayUpdate(TUint32 aSeq)
+	/**
+	* Commit/accept the sequence number.
+	*
+	* This assumes that the ReplayCheck with the same sequence
+	* number was called before this. This function can only be
+	* called, if the packet authenticates correctly (and thus
+	* the sequence number is valid too).
+	*
+	* @param aSeq	The low order 32 bits of the sequence number.
+	*/
+	{
+	ASSERT(aSeq == iTestSeq);
+
+//	LOG(Log::Printf(_L("SA[%u] RU T=%u:%u"), this, iRecvSeq.High(), (TInt)iRecvSeq));
+	if (!iReplayCheck)
+		{
+		iRecvSeq = iTestSeq;
+		return;
+		}
+
+	if (!iTestInWindow)
+		{
+		// Need to adjust window, aSeq is the new leading window edge.
+		const TUint32 diff = aSeq - iRecvSeq;
+		if (diff < KMaxReplayWindowLength)
+			{
+			// need to clear bits iRecvSeq -> aSeq
+			// brute force loop...
+			while (aSeq != ++iRecvSeq)
+				{
+				const TInt ix = BitmapWord(iRecvSeq);
+				const TInt ib = BitmapBit(iRecvSeq);
+				iBitmap[ix] &= ~(1L << ib);
+				}
+			}
+		else
+			{
+			iBitmap[0] = 0;
+			iBitmap[1] = 0;
+			iBitmap[2] = 0;
+			iBitmap[3] = 0;
+			iRecvSeq = iTestSeq;
+			}
+		}
+	// *note*
+	//	If window is not adjusted, the earlier ReplayCheck already
+	//	guarantees that the sequence number is within the window.
+	const TInt i = BitmapWord(aSeq);
+	const TInt b = BitmapBit(aSeq);
+	iBitmap[i] |= (1L << b);
+//	LOG(Log::Printf(_L("SA[%u] RU seq=%u:%u iBitmap[%d] bit %d = %d"),
+//		this, iTestSeq.High(), (TInt)iTestSeq, i, b, (iBitmap[i] & (1L << b)) != 0));
+	}
+
+
+TInt CSecurityAssoc::TimerInit(MAssociationManager &aManager)
+	/**
+	* Initialize timer for the SA, if lifetimes require it.
+	*/
+	{
+	TTime now;
+	now.UniversalTime();
+	return TimerExpired(aManager, now);
+	}
+
+TInt CSecurityAssoc::MarkUsed(MAssociationManager &aManager)
+	/**
+	* SA has been used.
+	*
+	* MarkUsed is called when the SA has been used for something:
+	* Check the count based lifetimes and record the "first use" time.
+	*/
+	{
+	TInt result = CountExpired(aManager);
+	if (result == KErrNone && !iUsed)
+		{
+		//
+		// First Use, "freeze" the times
+		//
+		iUsed = 1;
+		iCurrent.iUsetime.UniversalTime();
+		TLifetime::Freeze(iHard.iUsetime, iCurrent.iUsetime);
+		TLifetime::Freeze(iSoft.iUsetime, iCurrent.iUsetime);
+		// Activate Timers (if required)
+		result = TimerExpired(aManager, iCurrent.iUsetime);
+		}
+	return result;
+	}
+
+
+TInt CSecurityAssoc::TimerExpired(MAssociationManager &aManager, const TTime &aNow)
+	/**
+	* Examine the lifetime expiration.
+	*
+	* TimerExpired is called when the life status of this SA
+	* should be re-examined relative to the current time (aNow).
+	*
+	* @li *NOTE*
+	*		This method just does not only expire. It also
+	*		re-activates the timer, if SA is not expired and
+	*		has lifetimes based on time. Thus, this is used
+	*		internally also to activate the timers for the
+	*		first time.
+	* @param aManager	Association Manager
+	* @param aNow		The current time.
+	*
+	* @return
+	*		KErrNone, if SA did not expire
+	*		KErrDied, if SA expired
+	*/
+	{
+	const TTime *time;
+	TTimeIntervalSeconds delta;
+	// 'inifinite' is the return value from SecondsFrom, and is set non-zero
+	// when the time interval in seconds does not fit into 32 bit integer.
+	//
+	// If the expiration is so far in the future that 32bit integer
+	// cannot hold the number of seconds, don't set the timer, just
+	// assume no expiration! (I hope nobody sends Psion on a probe
+	// to Alpha Centauri with this code, while trying to specify other
+	// than infinite lifetime -- msa)
+	TInt infinite;
+
+
+	// Choose the nearest of limits for study. If SA is never used
+	// yet, always checks the addtime only.
+	time = (!iUsed || iHard.iAddtime < iHard.iUsetime) ?
+		&iHard.iAddtime : &iHard.iUsetime;
+
+	if (*time <= aNow)
+		{
+		// Hard lifetime expired
+		iState = SADB_SASTATE_DEAD;
+		aManager.Expired(*this, SADB_EXT_LIFETIME_HARD, iHard);
+		aManager.Delete(this);
+		return KErrDied;
+		}
+	infinite = time->SecondsFrom(aNow, delta);
+	//
+	// If SOFT expire has already occurred (DYING), then ignore
+	// soft expire times (only generate SOFT Expire message once!)
+	//
+	if (iState != SADB_SASTATE_DYING)
+		{
+		// Choose the nearest of limits for study
+		time = (!iUsed || iSoft.iAddtime < iSoft.iUsetime) ?
+			&iSoft.iAddtime : &iSoft.iUsetime;
+		if (*time <= aNow)	// Soft lifetime expired?
+			{
+			iState = SADB_SASTATE_DYING;
+			aManager.Expired(*this, SADB_EXT_LIFETIME_SOFT, iSoft);
+			}
+		else
+			{
+			TTimeIntervalSeconds soft;
+			if (time->SecondsFrom(aNow, soft) == 0 && (infinite || soft < delta))
+				{
+				// Non-infinite soft time specified, and is less than
+				// than the hard time, use this as a base for the timer
+				// setup.
+				infinite = 0;	// Not infinite, a value exists.
+				delta = soft;
+				};
+			}
+		//
+		// DoCallbacks is needed when DYING is set in above,
+		// but is placed here, because PFKEY Update/Add message eventually
+		// ends up here (via TimerInit) when it changes the state of
+		// the SA, and callbacks are needed also then.. -- msa
+		if (DoCallbacks() == KErrDied)
+			return KErrDied;	// Hups, lost the SA!
+		}
+	//
+	// SA is not dead, reactivate timer, if needed
+	//
+	if (!infinite)
+		aManager.TimerOn(*this, delta.Int() > 0 ? delta.Int() : 1);
+	return KErrNone;
+	}
+
+TInt CSecurityAssoc::CountExpired(MAssociationManager &aManager)
+	/**
+	* Examine the count based lifetime.
+	*
+	* CountExpired is called by when the life status of this SA
+	* in respect of bytes or allocations counts should
+	* be re-examined.
+	*
+	* @param aManager The association manager
+	* @return
+	*	KErrNone, if SA did not expire
+	*	KErrDied, if SA expired (not available any more)
+	*/
+	{
+	if ((iHard.iBytes != 0 && iHard.iBytes < iCurrent.iBytes) ||
+		(iHard.iAllocations && iHard.iAllocations < iCurrent.iAllocations))
+		{
+		// Hard lifetime expired
+		iState = SADB_SASTATE_DEAD;
+		aManager.Expired(*this, SADB_EXT_LIFETIME_HARD, iHard);
+		aManager.Delete(this);	// <-- *this* is DELETED!!!
+		return KErrDied;
+		}
+	//
+	// If SOFT expire has already occurred (DYING), then ignore
+	// soft expire times (only generate SOFT Expire message once!)
+	//
+	if (iState != SADB_SASTATE_DYING)
+		{
+		if ((iSoft.iBytes != 0 && iSoft.iBytes < iCurrent.iBytes) ||
+			(iSoft.iAllocations && iSoft.iAllocations < iCurrent.iAllocations))
+			{
+			iState = SADB_SASTATE_DYING;
+			aManager.Expired(*this, SADB_EXT_LIFETIME_SOFT, iSoft);
+			}
+		}
+	return KErrNone;
+	}
+
+
+TInt CSecurityAssoc::DoCallbacks()
+	/**
+	* Call the callbacks registered for the SA.
+	*
+	* This is called for every state change of SA (except for DEAD,
+	* which is handled in the Delete() above)
+	*
+	* @return Either
+	*	KErrNone, if SA instance still exists
+	*	KErrDied, if SA instance was destroyed and none other!
+	*/
+	{
+	//
+	// *NOTE*
+	//		As the callback may delete the handle (and may even
+	//		cause deletion of handles before and after, things
+	//		get somewhat difficult...
+	//
+	RCircularList list(iHandles);
+	Open();	 // Can't have the SA being pulled from under us!
+	while ((list.iNext) != &list)
+		{
+		RSecurityAssociation *r = (RSecurityAssociation *)list.iNext;
+		r->iCallback(*r);
+		if (list.iNext == r)
+			{
+			//
+			// The handle was not deleted, move it back to the SA
+			//
+			r->Detach();
+			iHandles.Attach(*r);
+			}
+		}
+	return Close(); // Now, let it go, if someone deleted it!
+	}