diff -r 000000000000 -r af10295192d8 networksecurity/ipsec/ipsec6/src/sadb.cpp --- /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 +#include "sadb.h" +#include "sa_crypt.h" +#include "pfkeymsg.h" +#include +#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 + /** + * 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 + * + * + * + * + * 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::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;iCount();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! + }