--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/networksecurity/ipsec/ipsec6/src/sc_prt6.cpp Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,2304 @@
+// Copyright (c) 2006-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:
+// sc_prt.cpp - IPv6/IPv4 security check protocol
+// An instance of a procotol that hooks into IP protocol instance,
+// performing IPSEC checking and processing to packets.
+//
+
+
+#include <posthook.h>
+#include <icmp6_hdr.h>
+#include <udp_hdr.h>
+#include <in_pkt.h>
+#include <ext_hdr.h>
+#include <in6_opt.h>
+#include <in6_event.h>
+#include <es_prot_internal.h>
+
+#include "ipsec.h"
+#include "sc.h"
+#include "epdb.h"
+#include "spdb.h"
+#include "sadb.h"
+#include <networking/ipsecerr.h>
+#include "ipseclog.h"
+#include "tunlintunl.h"
+
+const TIp6Addr KOuterIsInner = {{{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}};
+
+class CProtocolIpsec;
+class CIpsecHook;
+
+/**
+* Mobility Protocol id.
+* (this should be in insock.h?)
+*/
+const TUint KProtocolInetMh = 135;
+/**
+* SCTP Protocol id
+* (this should be in insock.h?)
+*/
+const TUint KProtocolInetSctp = 132;
+
+/**
+* Max number of nested IPSEC.
+*/
+const int KIpsecMaxNesting = 10;
+
+/**
+* Max number of fragment bookkeeping records.
+*/
+const TInt KIpsecMaxFragmentInfo = 10;
+
+class RHookSA : public RSecurityAssociation
+ /**
+ * Security association handle.
+ *
+ * Extends the RSecurityAssociation handle to include a back link
+ * to the CIpsecHook which actually contains the RHookSA
+ * (for outbound processing)
+ */
+ {
+public:
+ static void Callback(RSecurityAssociation &aAssociation);
+
+ RHookSA(CFlowContext &aFlow, CPolicyAction *const aItem);
+ /**
+ * The last state of the association.
+ *
+ * iState is mainly used to detect whether initial
+ * acquire failed, when SA dies. If PENDING at DEAD,
+ * then SA died before ever becoming ready,
+ */
+ TInt iState;
+ CPolicyAction *const iItem;
+private:
+ CFlowContext &iFlow; // ..to change the flow state when SA state changes (see CallBack)
+ };
+
+class TUpperLayerSnoop
+ /**
+ * A snooping class.
+ * A simple class to access the first 4 bytes of the packet.
+ * This is used to snoop the upper layer information.
+ *
+ * Thus max "port offset" = 2 (because port is 16 bits),
+ * and max "type/code offset = 3.
+ */
+ {
+public:
+ // Basic
+ inline static TInt MinHeaderLength() {return 4; }
+ inline static TInt MaxHeaderLength() {return 4; }
+ // Access values
+ inline TUint16 Port(TUint8 aOff) const { return (i[aOff] << 8) + i[aOff+1]; }
+ inline TUint8 Byte(TUint8 aOff) const { return i[aOff]; }
+private:
+ TUint8 i[4];
+ };
+
+class TSnoopHeader
+ /**
+ * Describes the positions of the port/type/code information in a protocol headers.
+ */
+ {
+public:
+ TUint8 iProtocol; //< 0..255
+ TUint8 iSelector; //< 0..3 (ports, localport, type+code, type)
+ TUint8 iO1; //< Offset of the source port [0..2] or type [0..3]),
+ TUint8 iO2; //< Offset of the destination port [0..2] or code [0..3])
+ };
+
+const TUint KLoadPorts = 0; //< Load local and remote port
+const TUint KLoadLocal = 1; //< Load local port only
+const TUint KLoadType = 2; //< Load Type only (into local port as 'type << 8')
+const TUint KLoadBoth = 3; //< Load Type and code (into local port as 'type << 8 | code')
+
+/**
+* The list of currently known protocols.
+*
+* This table defines the "known protocols" for IPsec, and this must be updated
+* if some new protocol uses ports or type/code system, and which needs to
+* selected in IPsec selectors.
+*/
+static const TSnoopHeader snooper[] =
+ {
+ { KProtocolInetUdp, KLoadPorts, 0, 2 }, // UDP uses src and dst port
+ { KProtocolInetTcp, KLoadPorts, 0, 2 }, // TCP uses src and dst port
+ { KProtocolInetIcmp, KLoadBoth, 0, 1 }, // ICMPv4 uses type and code
+ { KProtocolInet6Icmp, KLoadBoth, 0, 1 }, // ICMPv6 uses type and code
+ { KProtocolInetMh, KLoadType, 2, 0 }, // Mobile IPv6 uses type
+ { KProtocolInetSctp, KLoadPorts, 0, 2 } // SCTP uses src and dst port
+ };
+static const TSnoopHeader *const snooper_end = &snooper[sizeof(snooper)/sizeof(snooper[0])];
+
+
+class CIpsecHook : public CIpsecReferenceCountObject, public MFlowHook
+ /**
+ * The MFlowHook for outbound IPsec processing.
+ *
+ * An instance of this class is attached to the flows for which
+ * IPsec processing is required.
+ *
+ * The allocation size of this object is variable and it depends
+ * the required IPsec processing. The variable length information
+ * is at the end of fixed member variables consists of two
+ * arrays of structures as follows:
+ * @code
+ * RHookSA[iCount] // The handles for IPsec associations
+ * RIpAdddress[iTunnels] // The src addresses of the tunnels (if iTunnels > 0)
+ * @endcode
+ */
+ {
+private:
+ CIpsecHook(
+ MAssociationManager &aMgr,
+ CFlowContext &aFlow,
+ const RPolicySelectorInfo &aInfo,
+ const TInt aCount,
+ const TInt aTunnels);
+ ~CIpsecHook();
+
+ RHookSA &Assoc(TInt aIndex) const
+ { return ((RHookSA *)(((char *)this) + sizeof(*this)))[aIndex]; }
+ RIpAddress &Tunnel(TInt aIndex) const
+ { return ((RIpAddress *)&Assoc(iCount))[aIndex]; }
+
+public:
+ // for CProtocolSecpol
+ static CIpsecHook *NewL(
+ MAssociationManager &aMgr,
+ CFlowContext &aFlow,
+ const RPolicySelectorInfo &aInfo,
+ TInt aCount, CPolicyAction **aItems,
+ TInt aTunnels, const RIpAddress *aSrc);
+
+ // MFlowHook
+ void Open();
+ TInt ReadyL(TPacketHead &aHead);
+ TInt ApplyL(RMBufSendPacket &aPacket, RMBufSendInfo &aInfo);
+ void Close();
+
+private:
+ // The internal state information
+
+ MAssociationManager &iMgr; //< The association manager.
+ CFlowContext &iFlow; //< The associated flow
+ RPolicySelectorInfo iInfo; //< The selector information extracted at OpenL
+ const TUint8 iCount; //< Number of IPSEC actions (usually SA) to apply
+ const TUint8 iTunnels; //< ..and how many of them include tunnel action
+
+ // Followed by arrays of
+ // - RHookSA[iCount]
+ // - RIpAdddress[iTunnels]
+ };
+
+
+
+RHookSA::RHookSA(CFlowContext &aFlow, CPolicyAction *const aItem) : iItem(aItem), iFlow(aFlow)
+ /**
+ * Constructor.
+ *
+ * @param aFlow The flow.
+ * @param aItem The action
+ */
+ {
+ iState = EFlow_PENDING;
+ RSecurityAssociation::Init(Callback);
+ aItem->Open();
+ }
+
+void RHookSA::Callback(RSecurityAssociation &aAssociation)
+ /**
+ * Association has changed state.
+ *
+ * @param aAssociation The handle.
+ */
+ {
+ const RHookSA &assoc = (RHookSA &)aAssociation;
+
+ switch (assoc.Status())
+ {
+ case SADB_SASTATE_MATURE:
+ case SADB_SASTATE_DYING:
+ // Either of these states means that SA is usable,
+ // The previous state makes no difference.
+ // In case of READY, should probably check all other
+ // required SA's, and not try to wakeup the flow
+ // until ALL of them are available. -- msa
+ if (assoc.iState != KErrNone && assoc.iItem && assoc.iItem->iOptional)
+ assoc.iFlow.SetChanged(); // When optional SA becomes available, "recompute" flow!
+ assoc.iFlow.SetStatus(EFlow_READY);
+ break;
+ case SADB_SASTATE_DEAD:
+ //
+ // The SA is dead. Now it depends on the previous state
+ // whether a new SA negotiation should be started or if
+ // the flow should be shut down.
+ if (assoc.iState == EFlow_PENDING)
+ {
+ //
+ // Flow was waiting for negotiation, which expired
+ //
+ /* here the error value passed by the kmd server
+ * will be passed to the tcp/ip stack. This error
+ * value was set in the RSecurityAssociation class.
+ *
+ */
+ TInt errorValue = assoc.ReadErr();
+ if(errorValue==EIpsec_Ok)
+ {
+ errorValue = EIpsec_AcquireFailed;
+ }
+ assoc.iFlow.SetStatus(errorValue);
+ break;
+ }
+ // SA was not a LARVAL SA. This means that a real SA
+ // has just expired or been deleted for some reason,
+ // the flow just needs to acquire a new SA.
+ // *FALL THROUGH*
+ default:
+ // SA not available, but may be acquired, set/keep
+ // the flow in pending state (a ReadyL call later
+ // will activate the acquire, if needed).
+ assoc.iFlow.SetStatus(EFlow_PENDING);
+ break;
+ }
+ }
+
+
+class TIpsecFragmentData
+ /**
+ * The state information for single IPsec transfor.
+ */
+ {
+public:
+ RSecurityAssociation iSA; //< The IPsec SA that was used
+ TIpAddress iTunnel; //< The outer Tunnel End Point (src).
+ };
+
+class CIpsecFragmentInfo : public CBase
+ /**
+ * The IPsec fragment tracking.
+ *
+ * When IPsec has been applied to tunneled fragments, it must
+ * be guaranteed that all fragments are correctly protected
+ * as required by the policy. However, the realy policy
+ * requirement is only known after the final fragment has
+ * arrived and when the full packet is available.
+ *
+ * This structure keeps track of applied IPsec for a
+ * packet being assembled.
+ *
+ * This is variable sized object, and the tail end
+ * is an array of state info:
+ * @code TIpsecFragmentData[iCount]
+ * @endcode
+ */
+ {
+private:
+ CIpsecFragmentInfo(TUint aCount) : iCount(aCount) {}
+public:
+ static CIpsecFragmentInfo *New(TUint aCount);
+ inline TIpsecFragmentData & operator[](TInt aIndex) const;
+ ~CIpsecFragmentInfo();
+
+ CIpsecFragmentInfo *iNext; //< The link to next info block.
+
+ // Id + src + dst identify the fragment.
+
+ TUint32 iId; //< The fragment ID
+ TIpAddress iSrc; //< The (inner) source address
+ TIpAddress iDst; //< The (inner) destination address
+
+ // The applied IPsec
+ const TUint iCount; //< The number of TIpsecFragmentData that follow after this.
+
+ // Followed by array of TIpsecFragmentData[iCount]
+ };
+
+class CProtocolSecpol : public CProtocolPosthook, public MSecurityPolicyManager, MEventListener
+ /**
+ * Security Policy component of the IPsec.
+ *
+ * This protocol handles the Security Policy Database and also acts as a
+ * hook for the IP layer.
+ */
+ {
+public:
+ // ESOCK Protocol Basics
+ CProtocolSecpol();
+ ~CProtocolSecpol();
+ void BindToL(CProtocolBase *protocol);
+ void Identify(TServerProtocolDesc *) const;
+ CServProviderBase* NewSAPL(TUint aSockType);
+
+ // for CProtocolIpsec (inbound transforms)
+ TInt TransformL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo);
+
+ // Hook methods (inbound direction)
+ TInt ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo);
+
+ // Hook methods (outbound direction)
+ MFlowHook *OpenL(TPacketHead &aHead, CFlowContext *aFlow);
+
+ // MEventListener
+ void Notify(TUint aEventClass, TUint aEventType, const void *aData);
+
+private:
+ TUint32 GetInterfaceIndex(const TDesC &aName);
+ TBool UpdateInterfaceIndex(const TDesC &aName, TUint32 aIndex);
+ void FixupInterfaceIndexes(CSecurityPolicy *aPolicy);
+
+ // Security Policy Management section
+ inline CSecurityPolicy *Policy() const { return iPolicy; }
+ TInt SetPolicy(const TDesC &aPolicy, TUint &aOffset);
+
+ void Deliver(RMBufPacketBase& aPacket);
+ void NetworkAttachedL();
+ void NetworkDetached();
+ void InterfaceAttached(const TDesC &aName, CNifIfBase *aIf);
+
+ TInt SelectSource(const RIpAddress &aDst, RIpAddress &aSrc) const;
+ TInt CollectBundle(TPolicyFilterInfo &aFilter, RPolicySelectorInfo &aKey, const TInt aMaxItems,
+ CPolicyAction **aItems, RIpAddress *aSrc, TInt &aTunnels) const;
+ void UpdateTunnelInterface(RMBufRecvInfo &aInfo, const CSecurityAssoc *const aSa) /*const*/;
+ void CheckPacketId(RMBufHookPacket &aPacket);
+ TInt CheckFragmentPolicy();
+
+ MAssociationManager *iAssociationManager;
+ CProtocolIpsec *iProtocolIpsec;
+ MEventService *iEventService;
+ CSecurityPolicy* iPolicy;
+ TDblQue<CProviderSecpol> iSAPlist;
+ //
+ // The working space for incoming packet processing
+ //
+ TInt8 iCountSA; //< Number of slots used from the arrays below
+ TUint iIsFragment:1; //< Current packet is a fragment.
+ TUint iIsTunnelMode:1; //< The last transform was in tunnel mode.
+ TUint32 iPacketId; //< Id of the current packet.
+ TUint32 iId; //< The fragment id.
+ TIpAddress iSrc; //< The currently effective src address of incoming packet
+ TIpAddress iDst; //< The currently effective dst address of incoming packet
+ RPolicySelectorInfo iPktInfo; //< Incoming packet info
+ RSecurityAssociation iSA[KIpsecMaxNesting]; //< Used Security associations.
+ RIpAddress iTunnel[KIpsecMaxNesting]; //< Related tunnel addresses (outer source)
+ RIpAddress iMyself[KIpsecMaxNesting]; //< Related destination adresses (outer).
+ // List of incomplete fragmented packets.
+ CIpsecFragmentInfo *iFrags; //< Unfinished fragments.
+
+ };
+
+void IPSEC::IdentifySecpol(TServerProtocolDesc &aEntry)
+ /**
+ * Return IPsec protocol description.
+ *
+ * @retval aEntry The protocol description.
+ */
+ {
+ _LIT(KSecpol, "secpol6");
+
+ aEntry.iName = KSecpol;;
+ aEntry.iAddrFamily = KAfIpsec;
+ aEntry.iSockType = KSockRaw;
+ aEntry.iProtocol = KProtocolSecpol;
+ aEntry.iVersion = TVersion(1, 0, 0);
+ aEntry.iByteOrder = ELittleEndian;
+ aEntry.iServiceInfo = KSIConnectionLess | KSIMessageBased | KSIBroadcast;
+ aEntry.iNamingServices = 0;
+ aEntry.iSecurity = KSocketNoSecurity;
+ aEntry.iMessageSize = 0xffff;
+ aEntry.iServiceTypeInfo= ESocketSupport | ENeedMBufs;
+ aEntry.iNumSockets = KUnlimitedSockets;
+ }
+
+CProtocolBase *IPSEC::NewSecpolL()
+ /**
+ * Create a new SECPOL protocol instance.
+ *
+ * @return The Secpol
+ */
+ {
+ return new (ELeave) CProtocolSecpol();
+ }
+
+//
+
+static void NoCallback(RSecurityAssociation &)
+ /** Dummy do nothing callback */
+ {
+ }
+
+
+// CIpsecFragmentInfo
+//
+
+inline TIpsecFragmentData & CIpsecFragmentInfo::operator[](TInt aIndex) const
+ /*
+ * Return Nth fragment info.
+ * @param aIndex The N value.
+ */
+ {
+ ASSERT((TUint)aIndex < iCount);
+ return ((TIpsecFragmentData *)((char *)this + sizeof(*this)))[aIndex];
+ }
+
+CIpsecFragmentInfo *CIpsecFragmentInfo::New(TUint aCount)
+ /**
+ * Create a new fragment info.
+ *
+ * @param aCount The number of TIpsecFragmentData
+ * @return Fragment info block.
+ */
+ {
+ CIpsecFragmentInfo *f = new (aCount * sizeof(TIpsecFragmentData)) CIpsecFragmentInfo(aCount);
+ if (f)
+ {
+ for (TInt i = 0; i < f->iCount; ++i)
+ (*f)[i].iSA.Init(NoCallback);
+ }
+ return f;
+ }
+
+CIpsecFragmentInfo::~CIpsecFragmentInfo()
+ /**
+ * Desctructor.
+ */
+ {
+ // Detach the security associations, if any
+ for (TInt i = 0; i < iCount; ++i)
+ {
+ (*this)[i].iSA.None();
+ }
+ }
+
+class CProtocolIpsec : public CIp6Hook
+ /**
+ * Internal protocol for ESP, AH, etc.
+ *
+ * Internal instance which gets to handle the
+ * specific protocols AH, ESP and UPD for inbound
+ * packets.
+ */
+ {
+public:
+ CProtocolIpsec(CProtocolSecpol &aSecpol) : iSecpol(aSecpol)
+ {};
+ ~CProtocolIpsec();
+ void Identify(TServerProtocolDesc *aEntry) const
+ { IPSEC::IdentifySecpol(*aEntry); }
+ TInt ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo)
+ { return iSecpol.TransformL(aPacket, aInfo); }
+ /**
+ * The "master" parent.
+ *
+ * This "protocol" has no modifiable members, it borrows
+ * everything from the parent CProtocolSecpol.
+ */
+ CProtocolSecpol &iSecpol;
+ };
+
+CProtocolIpsec::~CProtocolIpsec()
+ /**
+ * Destructor.
+ *
+ * Cancel all binds with the network using CProtocolInet6Binder::Unbind(),
+ * if the network is still attached
+ */
+ {
+ if (iSecpol.NetworkService())
+ iSecpol.NetworkService()->Protocol()->Unbind(this, 0);
+ }
+
+//
+
+#ifdef _LOG
+
+static TInt MaskLength(TUint32 aAddr)
+ /**
+ * Return count of contiguous most significant 1-bits in TUint32
+ */
+ {
+ TInt count = 0;
+ // obviously, this is "brute force" counting
+ while (aAddr & 0x80000000)
+ {
+ count++;
+ aAddr <<= 1;
+ }
+ return count;
+ }
+
+static TInt MaskLength(const TIp6Addr &aAddr)
+ /**
+ * Return count of contiguous most significant 1-bits in IPv6 address.
+ */
+ {
+ TInt count = 0;
+ for (TUint i = 0; i < sizeof(aAddr.u.iAddr8) / sizeof(aAddr.u.iAddr8[0]); ++i)
+ if (aAddr.u.iAddr8[i] == 0xFF)
+ count += 8;
+ else
+ {
+ count += MaskLength(aAddr.u.iAddr8[i] << 24);
+ break;
+ }
+ return count;
+ }
+
+class TAddressBuf : public TBuf<70>
+ {
+public:
+ TAddressBuf(const TIpAddress &aAddr, TUint16 aPort = 0);
+ };
+
+TAddressBuf::TAddressBuf(const TIpAddress &aAddr, TUint16 aPort)
+ {
+ TInetAddr addr(aAddr, 0);
+ addr.SetScope(aAddr.iScope);
+ addr.OutputWithScope(*this);
+ if (aPort)
+ {
+ _LIT(KFormat, "#%u");
+ AppendFormat(KFormat, (TInt)aPort);
+ }
+ }
+
+static void LogSelectorInfo(const TDesC &aStr, const RPolicySelectorInfo &aSelector, const TPolicyFilterInfo &aFilter)
+ {
+ _LIT(KIn, "inbound ");
+ _LIT(KOut, "outbound ");
+ _LIT(KBoth, "");
+
+ TAddressBuf local(aSelector.iLocal(), (aSelector.iFlags & KTransportSelector_PORTS) == 0 ? 0 : aSelector.iPortLocal);
+ TAddressBuf remote(aSelector.iRemote(), aSelector.iPortRemote);
+
+ const TUint mode = aFilter.iFlags & KPolicyFilter_SYMMETRIC;
+
+ _LIT(KPorts, "%S%Sif=%d local=%S remote=%S prot=%d");
+ _LIT(KTypeCode, "%S%Sif=%d local=%S remote=%S prot=%d type=%d code=%d");
+
+ Log::Printf((aSelector.iFlags & KTransportSelector_PORTS) == 0 ? KTypeCode() : KPorts(),
+ &aStr,
+ mode == KPolicyFilter_INBOUND ? &KIn() : mode == KPolicyFilter_OUTBOUND ? &KOut() : &KBoth(),
+ (TInt)aFilter.iIndex,
+ &local, &remote,
+ (TInt)aSelector.iProtocol,
+ (TInt)((aSelector.iPortLocal >> 8) & 0xFF),
+ (TInt)(aSelector.iPortLocal & 0xFF));
+ }
+
+static void LogBundleItem(const TDesC &aStr, const CPolicyAction &aItem, const CSecurityAssoc *aSA = NULL)
+ {
+ _LIT(KTunnel, "tunnel");
+ _LIT(KOptional, "?");
+ _LIT(KObligatory, "");
+
+ TBuf<15> spi;
+ if (aSA)
+ {
+ spi.Format(_L(" spi %u"), ByteOrder::Swap32(aSA->SPI()));
+ }
+ TAddressBuf tmp(aItem.iTunnel());
+ Log::Printf(_L("%S%S%S(%S)%S"),
+ &aStr,
+ aItem.iOptional ? &KOptional : &KObligatory,
+ aItem.iSpec == NULL ? &KTunnel : aItem.iSpec->iName,
+ &tmp,
+ &spi);
+ }
+
+static void LogAddressSelector(TDes &aBuf, const TDesC &aLabel, const TIpAddress &aAddr, const TIpAddress &aMask)
+ {
+ if (!aMask.IsUnspecified() || aMask.iScope)
+ {
+ TBuf<70> str;
+ TInetAddr addr(aAddr, 0);
+ addr.SetScope(aAddr.iScope);
+ addr.Output(str);
+ aBuf.AppendFormat(_L("%S%S "), &aLabel, &str);
+ const TInt mlen = MaskLength(aMask);
+ if (mlen < 128)
+ {
+ // Non-trivial mask, output it.
+ TInetAddr msk(aMask, 0);
+ if (aAddr.IsV4Mapped() && mlen >= 64)
+ msk.SetAddress(BigEndian::Get32(&aMask.u.iAddr8[12]));
+ msk.Output(str);
+ aBuf.AppendFormat(_L(" mask=%S "), &str);
+ }
+ }
+ }
+
+static void LogPolicySelector(const TDesC &aStr, const CPolicySelector &aPS)
+ {
+ // The following code assumes that partial masks (not covering the full field) are
+ // only possible for address fields. For others, the non-zero mask is assumed to be
+ // all-ones.
+ TBuf<200> buf(aStr);
+ if (aPS.iFilterData & KPolicyFilter_FINAL)
+ buf.Append(_L("final "));
+ if ((aPS.iFilterMask & KPolicyFilter_MERGE) == 0)
+ buf.Append(_L("merge "));
+ switch (aPS.iFilterData & KPolicyFilter_SYMMETRIC)
+ {
+ case KPolicyFilter_INBOUND:
+ buf.Append(_L("inbound "));
+ break;
+ case KPolicyFilter_OUTBOUND:
+ buf.Append(_L("outbound "));
+ break;
+ default:
+ break;
+ }
+ if (aPS.iInterface)
+ buf.AppendFormat(_L("if=%d "), aPS.iInterface->iInterfaceIndex);
+ for (CTransportSelector *ts = aPS.iTS; ts != NULL; ts = ts->iOr)
+ {
+ LogAddressSelector(buf, _L("remote="), ts->iData.iRemote(), ts->iMask.iRemote());
+ LogAddressSelector(buf, _L("local="), ts->iData.iLocal(), ts->iMask.iLocal());
+ if (ts->iData.iFlags & KTransportSelector_PORTS)
+ {
+ if (ts->iMask.iPortLocal)
+ buf.AppendFormat(_L("local_port=%u "), ts->iData.iPortLocal);
+ if (ts->iMask.iPortRemote)
+ buf.AppendFormat(_L("remote_port=%u "), ts->iData.iPortRemote);
+ }
+ else
+ {
+ if (ts->iMask.iPortLocal & 0xFF00)
+ buf.AppendFormat(_L("type=%u "), ts->iData.iPortLocal >> 8);
+ if (ts->iMask.iPortLocal & 0x00FF)
+ buf.AppendFormat(_L("code=%u"), ts->iData.iPortLocal & 0xFF);
+ }
+ Log::Write(buf);
+ buf = _L("\t\t| ");
+ }
+ Log::Write(buf);
+ }
+
+#endif
+
+//
+
+
+CProtocolSecpol::CProtocolSecpol()
+ /**
+ * Constructor.
+ */
+ {
+ iSAPlist.SetOffset(_FOFF(CProviderSecpol,iSAPlink));
+
+ // The inbound associations don't need any special processing
+ // for SA status changes. Just put dummy callback routine in.
+ for (TInt i = KIpsecMaxNesting; --i >= 0; )
+ iSA[i].Init(NoCallback);
+ }
+
+CProtocolSecpol::~CProtocolSecpol()
+ /**
+ * Destructor.
+ */
+ {
+ // Deregister event notifications, if still active
+ if (iEventService)
+ iEventService->RemoveListener(this);
+
+ // Detach all linked assocations, if any left
+ for (TInt i = KIpsecMaxNesting; --i >= 0; )
+ iSA[i].None();
+
+ delete iPolicy;
+ iPolicy = NULL;
+
+ delete iProtocolIpsec;
+
+ // Cleanup the fragment information
+ while (iFrags)
+ {
+ CIpsecFragmentInfo *tmp = iFrags;
+ iFrags = iFrags->iNext;
+ delete tmp;
+ }
+
+ if (iAssociationManager)
+ {
+ iAssociationManager->Close();
+ iAssociationManager = NULL;
+ }
+
+ if (NetworkService())
+ {
+ // The IPsec is going away, all flows need to be
+ // re-connected...
+ NetworkService()->SetChanged();
+ }
+
+ // If assumptions are correct, iSAPlist should be empty!!
+ ASSERT(iSAPlist.IsEmpty());
+#if __WINS__
+ LOG(Log::Printf(_L("CProtocolSecpol::~CProtocolSecpol() done IPSEC_OBJECT_COUNT=%d"), IPSEC_OBJECT_COUNT));
+#endif
+ }
+
+void CProtocolSecpol::Identify(TServerProtocolDesc *aInfo) const
+ /**
+ * @retval aInfo A minimal TServerProtocolDesc to satisfy the Socket server.
+ */
+ {
+ IPSEC::IdentifySecpol(*aInfo);
+ }
+
+TUint32 CProtocolSecpol::GetInterfaceIndex(const TDesC &aName)
+ /**
+ * Find interface index by interface name.
+ *
+ * Query the interface index from the network layer. The query uses
+ * @code
+ * MInterfaceManager::GetOption(KSolInetIfQuery, KSoInetIfQueryByName, opt),
+ * @endcode
+ * where the opt is TSoInetIfQuery.
+ *
+ * @param aName The interface name
+ * @return The interface index (= 0, if not found)
+ */
+ {
+ MInterfaceManager *const mgr = NetworkService()->Interfacer();
+ TPckgBuf<TSoInetIfQuery> opt;
+ opt().iName = aName;
+ opt().iIndex = 0;
+ // Ignore GetOption error. If interface does not exist yet,
+ // this returns ZERO.
+ (void)mgr->GetOption(KSolInetIfQuery, KSoInetIfQueryByName, opt);
+ return opt().iIndex;
+ }
+
+TBool CProtocolSecpol::UpdateInterfaceIndex(const TDesC &aName, TUint32 aIndex)
+ /**
+ * Update interface index in the cached interface in the SPD.
+ *
+ * @param aName The interface name
+ * @param aIndex The interface index.
+ * @return ETrue, if name exists, and EFalse otherwise.
+ *
+ * The CSelectorInterface contains a cached interface in CSecurityPolicy.
+ *
+ * If the aIndex == 0, then ask the interface index (GetInterfaceIndex())
+ * from the network service.
+ *
+ * If the aIndex > 0, assume it is correct for the named interface
+ * (not verified).
+ */
+ {
+ if (iPolicy == NULL)
+ return EFalse;
+ for (CSelectorInterface *si = iPolicy->iInterfaces; si != NULL; si = si->iNext)
+ {
+ if (aName.Compare(*si->iName) == 0)
+ {
+ if (aIndex != 0 || (aIndex = GetInterfaceIndex(aName)) != 0)
+ {
+ LOG(Log::Printf(_L("<>\tipsec Interface Index Update IF [%S] index %d -> %d"),
+ &aName, si->iInterfaceIndex, aIndex));
+ si->iInterfaceIndex = aIndex;
+ }
+ // The name was located!
+ return ETrue;
+ }
+ }
+ // The name not located.
+ return EFalse;
+ }
+
+
+// CProtocolSecpol::InterfaceAttached
+// **********************************
+void CProtocolSecpol::InterfaceAttached(const TDesC &aName, CNifIfBase *)
+ /**
+ * Interface has attached to the network layer.
+ *
+ * This exists only because the interface create/destroy does not
+ * currently generate events. And, this event only occurs when a
+ * real NIF attached.
+ *
+ * The IPsec will not know if someone creates a "virtual" interface
+ * and does not specify any addresses, or otherwise modify it.
+ *
+ * At this point the interface index is not known, use the UpdateInterfaceIndex()
+ * without specified index (aIndex = 0), and it will query the index, if this
+ * interface is referenced in the current policy.
+ *
+ * @param aName The interface name
+ * @param aIf Not used.
+ */
+ {
+ (void)UpdateInterfaceIndex(aName, 0);
+ }
+
+
+// CProtocolSecpol::Notify
+// ***********************
+void CProtocolSecpol::Notify(TUint aEventClass, TUint aEventType, const void *aData)
+ {
+ (void)aEventType; // (just silence warning for release compilation)
+ if (aEventClass != EClassInterface)
+ return;
+
+ const TInetInterfaceInfo &ii = *(TInetInterfaceInfo *)aData;
+
+ if (UpdateInterfaceIndex(ii.iName, ii.iIndex))
+ return;
+ LOG(Log::Printf(_L("<>\tipsec Interface Event(%d) IF [%S] index %d not used in policy"),
+ aEventType, &ii.iName, ii.iIndex));
+ }
+
+
+// *********************************
+// CProtocolSecpol::NetworkAttachedL
+// *********************************
+// Do the bindings to the IP layer
+void CProtocolSecpol::NetworkAttachedL()
+ /**
+ * The bind to TCP/IP stack has been processed.
+ *
+ * The TCP/IP stack (MNetworkService) is now available and IPSEC can
+ * add the required hooks to it and do other initializations.
+ */
+ {
+ // If policy is loaded, need to redo the interfaces
+ // (network may have been down, and indexes changed)
+ if (iPolicy)
+ FixupInterfaceIndexes(iPolicy);
+
+ // Create inbound internal protocol only when really needed.
+ if (iProtocolIpsec == NULL)
+ iProtocolIpsec = new (ELeave) CProtocolIpsec(*this);
+
+ NetworkService()->BindL(iProtocolIpsec, BindHookFor(KProtocolInetAh));
+ NetworkService()->BindL(iProtocolIpsec, BindHookFor(KProtocolInetEsp));
+ NetworkService()->BindL(iProtocolIpsec, BindHookFor(KProtocolInetUdp));
+
+ NetworkService()->BindL(this, BindHookAll()); // All inbound packets
+ NetworkService()->BindL(this, BindFlowHook(1)); // All outbound packets
+
+ // To handle tunneled fragments within IPSEC, need to catch the IPv6
+ // Fragment Header and IPv4-in-IP headers
+ NetworkService()->BindL(iProtocolIpsec, BindHookFor(KProtocolInet6Fragment));
+ NetworkService()->BindL(iProtocolIpsec, BindHookFor(KProtocolInetIpip));
+
+ iEventService = IMPORT_API_L(NetworkService()->Interfacer(), MEventService);
+ iEventService->RegisterListener(this, EClassInterface);
+ }
+
+// ********************************
+// CProtocolSecpol::NetworkDetached
+// ********************************
+void CProtocolSecpol::NetworkDetached()
+ {
+ /**
+ * The biding to TCP/IP is being cut.
+ *
+ * Do only the internal housekeeping. The calling CProtocolPostHook code
+ * does all the necessary protocol Unbinding from the network, this does
+ * not need Unbind explicitly any of the bindings done in NetworkAttachedL.
+ */
+ ASSERT(iEventService != NULL);
+
+ iEventService->RemoveListener(this);
+ iEventService = NULL;
+ delete iProtocolIpsec;
+ iProtocolIpsec = NULL;
+ }
+
+// ************************
+// CProtocolSecpol::BindToL
+// ************************
+void CProtocolSecpol::BindToL(CProtocolBase *aProtocol)
+ /**
+ * Bind Secpol protocol to other protocols.
+ *
+ * This calls comes from the socket server, when it processed the "bindto/bindfrom" directives
+ * of the ESK files: "[secpol] bindto= ip6,pfkey". The secpol only accepts protocols
+ * KProtocolInet6Ip and KProtocolKey. Trying to bind anything else will result an error leave.
+ *
+ * The base class CProtocolPostHook::DoBindToL handles the KProtoclInet6Ip (network) binding
+ * and calls NetworkAttachedL method, it that happens.
+ * This code only needs to recognize the PFKEY (KProtocolKey) binding. The PFKEY provides
+ * MAssociationManager.
+ *
+ * @param aProtocol The target protocol to bind to.
+ */
+ {
+ const TUint id = (TUint)DoBindToL(aProtocol);
+ if (iAssociationManager == NULL && (iAssociationManager = IPSEC::FindAssociationManager(aProtocol, id)) != NULL)
+ {
+ iAssociationManager->Open();
+ }
+ else if (id)
+ User::Leave(KErrGeneral);
+ }
+
+// **********
+// CIpsecHook
+// **********
+CIpsecHook::CIpsecHook(
+ MAssociationManager &aMgr,
+ CFlowContext &aFlow,
+ const RPolicySelectorInfo &aInfo,
+ const TInt aCount,
+ const TInt aTunnels) :
+ iMgr(aMgr), iFlow(aFlow), iInfo(aInfo), iCount((TUint8)aCount), iTunnels((TUint8)aTunnels)
+ {
+ iMgr.Open(); // Association manager needs exist while this CIpsecHook exists.
+ };
+
+// ***********************
+// CIpsecHook::~CIpsecHook
+// ***********************
+CIpsecHook::~CIpsecHook()
+ {
+ // Detach associated Security Assocations
+ for (TInt i = 0; i < iCount; ++i)
+ {
+ RHookSA &h = Assoc(i);
+ h.None();
+ h.iItem->Close();
+ }
+
+ // Close tunnel src address endpoints
+ for (TInt j = 0; j < iTunnels; ++j)
+ {
+ RIpAddress &addr = Tunnel(j);
+ addr.Close();
+ }
+
+#ifdef IPSEC_OBJECT_TRACKING
+ // Close the remote/local RIpAddress (not really
+ // necessary, but automatic destructors are run
+ // so late that object counting debug would cause
+ // spurious panic...
+ iInfo.iRemote.Close();
+ iInfo.iLocal.Close();
+#endif
+
+ iMgr.Close(); // Release the association manager
+ LOG(Log::Printf(_L("\tMFlowHook[%u] destruct"), (TUint)this));
+ }
+
+// ****************
+// CIpsecHook::NewL
+// ****************
+CIpsecHook *CIpsecHook::NewL
+ (MAssociationManager &aMgr,
+ CFlowContext &aFlow,
+ const RPolicySelectorInfo &aInfo,
+ TInt aCount, CPolicyAction **aItems,
+ TInt aTunnels, const RIpAddress *aSrc)
+ /**
+ * Create a new IPSEC hook handle object.
+ *
+ * Allocating space only as much as is absolutely necessary.
+ *
+ * @param aMgr The association manager
+ * @param aFlow The flow context
+ * @param aInfo The policy selector information
+ * @param aCount The number of transforms (includes plain tunnels)
+ * @param aItems The transforms (aCount)
+ * @param aTunnels The number of tunnels within transforms
+ * @param aSrc The tunnel source addresses
+ */
+ {
+ const TInt extra = aCount * sizeof(RHookSA) + aTunnels * sizeof(RIpAddress);
+
+ CIpsecHook *const handle = new (extra) CIpsecHook(aMgr, aFlow, aInfo, aCount, aTunnels);
+ if (handle == NULL)
+ User::Leave(KErrNoMemory);
+
+ // Initialize RAssociation Hooks
+ for (TInt i = 0; i < aCount; ++i)
+ (void)new (&handle->Assoc(i)) RHookSA(aFlow, aItems[i]);
+
+ // Initialize tunnel srsc addresses
+ for (TInt j = 0; j < aTunnels; ++j)
+ {
+ (void)new (&handle->Tunnel(j)) RIpAddress(aSrc[j]);
+ }
+ return handle;
+ }
+
+TInt CProtocolSecpol::SelectSource(const RIpAddress &aDst, RIpAddress &aSrc) const
+ /**
+ * Select the source address for a destination.
+ *
+ * @param aDst The destination address
+ * @retval aSrc The selected source address
+ * @return KErrNone
+ */
+ {
+ TIp6Addr src;
+ if (NetworkService()->Interfacer()->CheckRoute(aDst(), aDst().iScope, src) != KErrNone)
+ return EIpsec_NoInnerSource; // Could not find the src addres!
+ // ARGHH! Checkroute does not return the source scope id...
+ // NEED to fetch some value by uncertain methods..
+ TUint32 scope = NetworkService()->Interfacer()->LocalScope(src, aDst().iScope, (TScopeType)(aDst().Scope()-1));
+ aSrc.Set(src, scope);
+ return KErrNone;
+ }
+
+
+static void AfterTunnelAction(RPolicySelectorInfo &aInfo)
+ /**
+ * Change policy selector to match state after applied tunnel.
+ *
+ * - ports = 0
+ * - protocol #KProtocolInetIpip or #KProtocolInet6Ipip
+ *
+ * Internal help function. Assume iLocal/iRemote have already
+ * been updated, if required.
+ */
+ {
+ aInfo.iPortLocal = 0;
+ aInfo.iPortRemote = 0;
+ aInfo.iFlags &= ~KTransportSelector_PORTS;
+ aInfo.iProtocol = aInfo.iRemote().IsV4Mapped() ? KProtocolInetIpip : KProtocolInet6Ipip;
+ }
+
+TInt CProtocolSecpol::CollectBundle(TPolicyFilterInfo &aFilter, RPolicySelectorInfo &aKey, const TInt aMaxItems,
+ CPolicyAction **aItems , RIpAddress *aSrc, TInt &aTunnels) const
+ /**
+ * Match the packet against the policy selectors and collect the IPsec actions.
+ *
+ * The function may modify the aFilter and aKey parameters.
+ *
+ * @retval aFilter The filter
+ * @retval aKey The packet information
+ * @param aMaxItems Max limit for actions.
+ * @retval aItems The collected actions.
+ * @retval aSrc The current source address.
+ * @retval aTunnels The number of tunnels.
+ * @return
+ * @li >= 0. The number of actions.
+ * @li EIpsec_NoSelectorMatch, packet does not match policy
+ * @li EIpsec_MaxTransforms, too many actions triggered.
+ * @li EIpsec_NoInnerSource, cannot select tunnel source (error is now misnamed).
+ */
+ {
+ TInt result = EIpsec_NoSelectorMatch;
+ aTunnels = 0;
+ TInt i = 0;
+ for (CPolicySelector *ps = iPolicy->iSelectors; ps != NULL; ps = ps->iNext)
+ {
+ // 1. Check filtering
+ if (((aFilter.iFlags ^ ps->iFilterData) & ps->iFilterMask) ||
+ (ps->iInterface && ps->iInterface->iInterfaceIndex != aFilter.iIndex))
+ continue; // -- this selector filtered out, try next.
+ // 2. check transport selectors (empty TS matches all)
+ if (ps->iTS == NULL || ps->iTS->Match(aKey))
+ {
+ if (ps->iFilterData & KPolicyFilter_DROP)
+ return EIpsec_NoSelectorMatch;
+
+ const TInt N = ps->iActions.Count();
+ for (TInt k = 0; k < N; k++)
+ {
+ CPolicyAction *const action = ps->iActions[k];
+ if (action == NULL)
+ continue;
+ if (i == aMaxItems)
+ return EIpsec_MaxTransforms;
+ aItems[i++] = action;
+ if (!action->iTunnel().IsNone())
+ {
+ // After tunneling, any futher matching is based on the
+ // selectors of a tunnel (addresses only, no ports)
+ aFilter.iFlags |= KPolicyFilter_TUNNEL;
+ if (!KOuterIsInner.IsEqual(action->iTunnel()))
+ {
+ aKey.iRemote = action->iTunnel;
+ if (SelectSource(aKey.iRemote, aKey.iLocal) != KErrNone)
+ return EIpsec_NoInnerSource;
+ }
+ AfterTunnelAction(aKey);
+ // Return list of source addresses, if the target array is supplied
+ if (aSrc)
+ aSrc[aTunnels] = aKey.iLocal;
+ aTunnels++;
+ }
+ }
+ result = i;
+ if (ps->iFilterData & KPolicyFilter_FINAL)
+ break; // Final selector, no merge allowed!
+ aFilter.iFlags |= KPolicyFilter_MERGE; // Mark "merge only".
+ }
+ }
+ return result;
+ }
+
+
+
+MFlowHook *CProtocolSecpol::OpenL(TPacketHead &aHead, CFlowContext *aFlow)
+ /**
+ * Attach IPsec processing to a flow (if needed).
+ *
+ * OpenL is called once when the flow is opened. It should decide
+ * whether further calls are necessary (ReadyL/ApplyL) for this flow.
+ *
+ * When called, the IPsec selector information is in TPacketHead.
+ *
+ * If OpenL() notices that the flow will never be opened,
+ * it will leave with error, causing the flow to be cancelled.
+ *
+ * The OpenL tasks:
+ *
+ * @li Consult the Security Policy and find out what actions
+ * need to be done for this flow: reject, pass clear or
+ * apply IPsec.
+ * @li If there are IPsec actions, then allocate a
+ * CIpHook object to handle the processing of the flow.
+ * @li If the actions include tunneling, update the flow
+ * selector information to match the outermost tunnel.
+
+ * @param aHead The flow information
+ * @param aFlow The flow context (always NON-NULL)
+ *
+ * @return CIpsecHook or NULL.
+ */
+ {
+ if (Policy() == NULL)
+ {
+ LOG(Log::Printf(_L("CProtocolSecpol::OpenL() -- No IPSEC policy loaded")));
+ return NULL; // No Policy, no IPSEC available
+ }
+ if (!iAssociationManager)
+ {
+ LOG(Log::Printf(_L("CProtocolSecpol::OpenL() -- PFKEY not avaiable")));
+ return NULL; // No CProtocolKey, no IPSEC available
+ // (should this cause packet drops instead?)
+ }
+
+ TPolicyFilterInfo filter;
+ filter.iIndex = aHead.iInterfaceIndex;
+ filter.iFlags = KPolicyFilter_OUTBOUND;
+
+ iPktInfo.FillZ();
+ iPktInfo.iProtocol = aHead.iProtocol;
+ for (const TSnoopHeader *f = snooper; f < snooper_end; ++f)
+ {
+ if (iPktInfo.iProtocol == f->iProtocol)
+ {
+ if (f->iSelector <= KLoadLocal)
+ {
+ iPktInfo.iFlags |= KTransportSelector_PORTS;
+ iPktInfo.iPortLocal = aHead.iSrcPort;
+ iPktInfo.iPortRemote = aHead.iDstPort;
+ }
+ else
+ {
+ iPktInfo.iPortLocal = (TUint16)((aHead.iIcmpType << 8) | aHead.iIcmpCode);
+ iPktInfo.iPortRemote = iPktInfo.iPortLocal;
+ }
+ break;
+ }
+ }
+ iPktInfo.iLocal.Set(aHead.ip6.SrcAddr(), aHead.iSrcId);
+ iPktInfo.iRemote.Set(aHead.ip6.DstAddr(), aHead.iDstId);
+
+ CPolicyAction *items[KIpsecMaxNesting]; // Collected actions [0..count-1]
+
+ TInt tunnels = 0;
+ LOG(LogSelectorInfo(_L("OpenL\t"), iPktInfo, filter));
+ RPolicySelectorInfo final_info = iPktInfo;
+ TInt count = CollectBundle(filter, final_info, KIpsecMaxNesting, items, iMyself, tunnels);
+ if (count < 0)
+ {
+ LOG(Log::Printf(_L("\t*DROP*")));
+ User::Leave(count);
+ }
+ else if (count == 0)
+ {
+ ASSERT(tunnels == 0);
+ LOG(Log::Printf(_L("\t*PASS*")));
+ return NULL;
+ }
+ ASSERT(tunnels <= count);
+
+ CIpsecHook *handle = CIpsecHook::NewL(*iAssociationManager, *aFlow, iPktInfo, count, items, tunnels, iMyself);
+
+ LOG(Log::Printf(_L("\tMFlowHook[%u] attached"), (TUint)handle));
+
+ if (tunnels)
+ {
+ // If tunneling happened, then update the packet head
+ // information accordingly.
+
+ aHead.ip6.SetDstAddr(final_info.iRemote());
+ aHead.iDstId = final_info.iRemote().iScope;
+ aHead.iSourceSet = 1;
+ aHead.ip6.SetSrcAddr(final_info.iLocal());
+ aHead.iSrcId = final_info.iLocal().iScope;
+ aHead.iProtocol = final_info.iProtocol;
+ aHead.iDstPort = 0;
+ aHead.iSrcPort = 0;
+ aHead.iIcmpType = 0;
+ aHead.iIcmpCode = 0;
+ // should set proper tunnel protocol? 4 or 41 depending on outermost inner header?
+ aHead.ip6.SetVersion(aHead.ip6.DstAddr().IsV4Mapped() ? 4 : 6);
+
+ // Comment on setting: aHead.iFragment = 1
+ // ---------------------------------------
+ // It is possible to request fragmentation before the
+ // IPSEC processing, if the IPSEC is adding the tunnel.
+ // *HOWEVER* This would be legal ONLY IF the tunneling
+ // SA is the FIRST (or only) transformation. If the policy
+ // actions says something like
+ // ESP + AH(tunnel)
+ // then ESP is in transport mode, and applying that to
+ // fragments is not ALLOWED. The fragmenting should happen
+ // AFTER the ESP transformation, and before the tunneling.
+ //
+ // This is not possible with current IPSEC hook.
+ // To enable such feature, either
+ // 1) IPSEC must do the fragmenting self
+ // 2) IPSEC processing needs to be split into two
+ // flow hooks, the first doing the ESP (transport only), and
+ // the second requesting fragmentation and doing tunnel + AH
+ // (and possible remaining transforms).
+ }
+ return handle;
+ }
+
+void CIpsecHook::Close()
+ /**
+ * Decrement reference count.
+ */
+ {
+ CIpsecReferenceCountObject::Close();
+ }
+
+void CIpsecHook::Open()
+ /**
+ * Increment reference count.
+ */
+ {
+ CIpsecReferenceCountObject::Open();
+ }
+
+TInt CIpsecHook::ReadyL(TPacketHead &aHead)
+ /**
+ * ReadyL implementation for IPsec MFlowHook.
+ *
+ * The ReadyL calls "propagate" interface ready state up the
+ * flow. The calls to hooks are made in reverse order, the
+ * "closest to interface" is called first.
+ *
+ * This function has following tasks
+ *
+ * @li Verify that all Security Associations (SA) required by the
+ * actions (CPolicyAction) collected in OpenL, are
+ * available (call MAssociationManager::Acquire() for
+ * each).
+ * @li If any of the required SA's is not yet available, then
+ * block the flow (and ReadyL) process by returning
+ * EFlow_PENDING. When the SA's become available, there
+ * will be a callback (RHookSA::Callback()). which can
+ * set the flow back to READY state and trigger a rerun
+ * of the ReadyL chain.
+ *
+ * @param aHead The packet flow information.
+ * @return
+ * @li == 0, hook is ready, proceed to refresh the next one or
+ * mark the flow as READY, if this was the first hook
+ * @li > 0 (EFlow_PENDING), hook is not ready, the ReadyL calling
+ * is stopped and flow is set to pending state.
+ * @li < 0, hook detected a permanent error condition. The flow
+ * goes into error state.
+ *
+ * *WARNING*
+ * The ReadyL method can be called multiple times!
+ */
+ {
+ TInt result = KErrNone;
+ TInt overhead = 0;
+
+ // ... could ASSERT that current dst/src in packet head is
+ // exactly the same as at the end of the OpenL above.
+
+ // Restore upper layer state
+ aHead.ip6.SetSrcAddr(iInfo.iLocal());
+ aHead.iSrcId = iInfo.iLocal().iScope;
+ aHead.ip6.SetDstAddr(iInfo.iRemote());
+ aHead.iDstId = iInfo.iRemote().iScope;
+ if (iInfo.iFlags & KTransportSelector_PORTS)
+ {
+ aHead.iSrcPort = iInfo.iPortLocal;
+ aHead.iDstPort = iInfo.iPortRemote;
+ aHead.iIcmpType = 0;
+ aHead.iIcmpCode = 0;
+ }
+ else
+ {
+ aHead.iSrcPort = 0;
+ aHead.iDstPort = 0;
+ aHead.iIcmpType = (TUint8)iInfo.iPortLocal;
+ aHead.iIcmpCode = (TUint8)(iInfo.iPortLocal >> 8);
+ }
+ aHead.ip6.SetVersion(aHead.ip6.DstAddr().IsV4Mapped() ? 4 : 6);
+
+ const RIpAddress *src = &iInfo.iLocal;
+ const RIpAddress *dst = &iInfo.iRemote;
+
+ LOG(Log::Printf(_L("ReadyL\tMFlowHook[%u]"), (TUint)this));
+
+ // Try to acquire the actual SA's required by the bundle
+
+ //
+ // Goes through all SA's, even if some of them fail. This is to
+ // get all possible ACQUIRE requests queued on one packet.
+ //
+ // When entering ready, the head contains the final destination
+ // address as defined by possible tunnels. However, this can be
+ // ignored, as we have the saved original destination which should
+ // be in the packet head after this ReadyL. (And the final destionation
+ // should be original destination or the last tunnel from the bundle.
+ TInt j = 0;
+ TInt tunnel_mode = 0;
+ RPolicySelectorInfo info = iInfo;
+ for (TInt i = 0; i < iCount; ++i)
+ {
+ RHookSA &h = Assoc(i);
+
+ // Save current dst/src for the case of optional item skipped.
+ const RIpAddress *const dst_opt = dst;
+ const RIpAddress *const src_opt = src;
+
+ if (!h.iItem->iTunnel().IsNone())
+ {
+ if (!KOuterIsInner.IsEqual(h.iItem->iTunnel()))
+ dst = &h.iItem->iTunnel; // destination changed
+ src = &Tunnel(j++); // Outer Source Address!
+ tunnel_mode = 1;
+ }
+ else
+ tunnel_mode = 0;
+
+ CSecurityAssoc *sa = NULL;
+ if (h.iItem->iSpec == NULL)
+ h.iState = KErrNone; // Always ready, no SA needed.
+ else
+ {
+ // Tranformation needs an IPSEC Security Association
+ // Because ReadyL can be called multiple times, it is
+ // possible to have associations already setup at this
+ // point (or only some of them not available yet).
+#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
+ if(h.iItem->iSpec->iPropList->Count() > MAX_PROPOSALS_PER_SA)
+ {
+ User::Leave(EIpsec_MaxTransforms);
+ }
+ TInt res2 = iMgr.Acquire(sa, h.iItem->iSpec->iSpec, h.iItem->iSpec->iPropList, h.iItem->iTS, *src, *dst, info, tunnel_mode);
+#else
+ TInt res2 = iMgr.Acquire(sa, h.iItem->iSpec->iSpec, h.iItem->iTS, *src, *dst, info, tunnel_mode);
+#endif // SYMBIAN_IPSEC_VOIP_SUPPORT
+ // Note: Acquire returns
+ // - KErrNone = EFlow_READY, when SA already exists and is usable
+ // - KRequestPending, when a larval SA is created and keymanagement Acquire in progress
+ // - KErrNotFound, when SA cannot be allocated for some reason (fatal error, abort flow!)
+ if (res2 == KRequestPending)
+ {
+ // The following test is temporary work-around for a
+ // bug in PFKEY implementation: the PKFEY does not properly
+ // check whether the SA reply to ACQUIRE matches request. It
+ // just changes the matching egg SA into MATURE state.
+ // If above acquire returns pending (no matching SA exists),
+ // and we already have a "MATURE" SA assigned, then assume
+ // the key manager is not providing a correct SA for the ACQUIRE.
+ // To prevent continous ACQUIRE looping, abort flow.
+ const CSecurityAssoc *const sa2 = h.Association();
+ if (sa != sa2 && sa2 && sa2->State() == SADB_SASTATE_MATURE)
+ res2 = EIpsec_AcquireFailed;
+ else
+ res2 = EFlow_PENDING;
+ }
+ h.iState = res2; // Remember the last result for SA.
+#ifdef _LOG
+ if (res2 > 0)
+ LogBundleItem(_L("\t\tWaiting SA: "), *h.iItem, sa);
+ else if (res2 == 0)
+ LogBundleItem(_L("\t\tSA ready: "), *h.iItem, sa);
+ else
+ LogBundleItem(_L("\t\tSA unavailable: "), *h.iItem, sa);
+#endif
+ //
+ // Keep the first error as final error... however, fatal error
+ // state ( < 0) should override any pending error state ( > 0).
+ //
+ if (h.iItem->iOptional)
+ {
+ if (res2 != KErrNone)
+ {
+ // Optional item not ready, cancel address changes.
+ // (this is not 100% correct--now the following
+ // SA's are requested with the old addresses, and
+ // will become incorrect, if the optional SA appears
+ // at some later stage without ReadyL being rerun!)
+ dst = dst_opt;
+ src = src_opt;
+ tunnel_mode = 0;
+ }
+ }
+ else
+ {
+ // Only obligatory items affect final result.
+ if (result == KErrNone || (result > 0 && res2 < 0))
+ result = res2;
+ }
+ // For after tunnel, the pktinfo must be changed too..
+ if (tunnel_mode)
+ {
+ info.iRemote = *dst;
+ info.iLocal = *src;
+ AfterTunnelAction(info);
+ }
+ }
+ //
+ // Whatever the result was, attach the new SA (possibly NULL or same as before)
+ // to the handle.
+ //
+ h.Reset(sa);
+ //
+ // Find out how much overhead this transform adds to a packet
+ //
+ overhead += iMgr.Overhead(sa, h.iItem->iTunnel());
+ }
+
+ if (result == KErrNone)
+ {
+ // If all SA's are available, the header space requirement
+ // is now known (coded in a way that allows this to change
+ // dynamicalle, if required)
+ iFlow.iHdrSize += overhead;
+ }
+ return result;
+ }
+
+TInt CIpsecHook::ApplyL(RMBufSendPacket &aPacket, RMBufSendInfo &aInfo)
+ /**
+ * Apply the IPsec actions to the outgoing packet.
+ *
+ * This is part of IPv6 outgoing packet hook processing.
+ * @li aPacket is the outgoing packet. It is *FULL* packet
+ * and already includes the outgoing IPv6 header. If
+ * the hook needs to do any modifications to the outgoing
+ * packet, it must make them into aPacket.
+ * @li aPacket has an info block associated. The info->iLength
+ * *MUST* be maintained, if any change affects it.
+ *
+ * @param aPacket The IP packet to process (unpacked).
+ * @param aInfo The unpacked info block associated with the aPacket.
+ *
+ * @return (as specified for the hook mechanism)
+ * @li Any Leave causes the packet to be dropped
+ * @li = 0, pass on to the next output hook
+ * @li < 0, the hook took "ownership" of the packet (usually
+ * just dropped it explicitly), "Send aborted" return!
+ * @li > 0, This return value is not used at this
+ * point. Do not return with > 0!
+ */
+ {
+ TInt tunnel = 0;
+ const TIpAddress *dst = &iInfo.iRemote();
+ for (TInt i = 0; i < iCount; ++i)
+ {
+ const RHookSA &h = Assoc(i);
+
+ // Save current dst for the case of optional item skipped.
+ // (src does not need to be saved, because for non-tunnels,
+ // the source is already in the packet, and for tunnels,
+ // the source is always taken from the Tunnel(x). And even
+ // the saved dst is only needed for the case where outer tunnel
+ // dst is defined to be same as inner).
+ const TIpAddress *const dst_opt = dst;
+
+ const TIpAddress *tunnel_address = &h.iItem->iTunnel();
+ if (!tunnel_address->IsNone())
+ {
+ // Pass outer src in the info
+ TInetAddr::Cast(aInfo.iSrcAddr).SetAddress(Tunnel(tunnel++)());
+ // [should also set aInfo.iDstAddr ?]
+
+ // Use the previous dst as tunnel address, if tunnel is defined as all-ones
+ if (KOuterIsInner.IsEqual(*tunnel_address))
+ tunnel_address = dst;
+ else
+ dst = tunnel_address;
+ }
+ // *NOTE*
+ // The IPSEC engine ApplyL does not follow the hook Apply return values
+ // fully. It never reseases the packet and error return is just a signal
+ // that transformation failed. Perhaps should do something? For now,
+ // just convert error return to leave! -- msa
+ if (h.iState == KErrNone)
+ User::LeaveIfError(iMgr.ApplyL(h.Association(), aPacket, aInfo, *tunnel_address));
+ else if (!h.iItem->iOptional)
+ User::Leave(EIpsec_LostSA);
+ else
+ dst = dst_opt;
+ //
+ // ApplyL may notice that SA is DYING or already dead, when DYING the flow
+ // should be set to pending after this packet (and renegotiation of SA
+ // sould be activated). If DEAD, the same happens, but additionally the
+ // packet is dropped.
+ }
+ return KErrNone;
+ }
+
+// CProtocolSecpol::UpdateTunnelInterface
+// **************************************
+// The problem: when applying IPSEC detunneling, the inner source and destination
+// addresses must be acceptable for the "inbound interface". When VPN tunnels are
+// in use, the addresses are normally not valid for the real interface and IPsec
+// must update the iInterfaceIndex to point to the proper VPN interface, so that
+// the checks pass.
+//
+// Just using the inner destination and searching for any interface that would
+// accept packets for that address only work for unique addresses. If there are
+// overlapping ranges (10-nets) or IPv6 scoped addresses (site or link local),
+// then the scope id is required.
+//
+// Assume CSecurityAssoc::TunnelIndex() must return the interface index of the
+// associated VPN tunnel interface, if non-zero. (this does not care how
+// such thing got associated with the SA...)
+void CProtocolSecpol::UpdateTunnelInterface(RMBufRecvInfo &aInfo, const CSecurityAssoc *const aSa)
+ {
+ if (aSa && aSa->TunnelIndex())
+ aInfo.iInterfaceIndex = aSa->TunnelIndex();
+ }
+
+void CProtocolSecpol::CheckPacketId(RMBufHookPacket &aPacket)
+ /**
+ * Detect when packet has changed.
+ *
+ * The IPsec hook can get multiple calls from the same packet
+ * to various hooks that it has installed, and they collect state
+ * information into the member variables of the CProtocolSecpol.
+ *
+ * The state information contains
+ * - IPsec transforms done
+ * - Fragment information
+ *
+ * Sometimes, some other hook or component just throws the packet
+ * away. IPsec needs to reset the collected information, if packet
+ * is changed.
+ *
+ * The network layer tags each packet with id, which can be used
+ * for this purposed (RMBufRecvInfo::HookValue(0)).
+ */
+ {
+ const TUint32 packet_id = aPacket.HookValue(0);
+ ASSERT(packet_id); // The packet id must be present!
+ if (packet_id == iPacketId)
+ return;
+
+ // A new packet is being processed, scrap previous
+ // SA and tunnel information.
+ if (iIsFragment)
+ {
+ CIpsecFragmentInfo **h = &iFrags;
+ CIpsecFragmentInfo *f;
+
+ // Count the number of currently stored states,
+ // and release all that are over the maximum
+ // count (from the end of chain, oldest ones first).
+ for (TInt count = 0; (f = *h) != NULL;)
+ {
+ if (++count >= KIpsecMaxFragmentInfo)
+ {
+ // Too many Fragment states stored, delete
+ // the entry
+ *h = f->iNext;
+ delete f;
+ }
+ else
+ h = &f->iNext;
+ }
+ // The other packet was a fragment, need to save
+ // the information for later use. Adds the new
+ // information in front of the list.
+ f = CIpsecFragmentInfo::New(iCountSA);
+ if (f)
+ {
+ f->iId = iId;
+ f->iSrc = iSrc;
+ f->iDst = iDst;
+ for (TInt i = 0; i < f->iCount; ++i)
+ {
+ // Copy a reference to the SA
+ (*f)[i].iSA.Reset(iSA[i].Association());
+ (*f)[i].iTunnel = iTunnel[i]();
+ }
+ f->iNext = iFrags;
+ iFrags = f;
+ }
+ }
+ iPacketId = packet_id;
+ iCountSA = 0;
+ iIsFragment = 0;
+ iIsTunnelMode = 0;
+ }
+
+TInt CProtocolSecpol::CheckFragmentPolicy()
+ /**
+ * Verify fragment policy.
+ *
+ * A fragment has been detected. Need to check that if there are other fragments
+ * of the same packet, they all have been protected with the same IPsec.
+ */
+ {
+ CIpsecFragmentInfo **h = &iFrags;
+ CIpsecFragmentInfo *f;
+ for (;(f = *h) != NULL; h = &f->iNext)
+ {
+ if (f->iId != iId || f->iSrc != iSrc || f->iDst != iDst)
+ continue;
+
+ // A matching fragment information already exists in the
+ // list. This means that we have already received other
+ // fragment(s) of this packet and now must check that all
+ // applied IPSEC is same for all fragments.
+ for (TInt i = 0; i < f->iCount; ++i)
+ {
+ if ((*f)[i].iSA.Association() != iSA[i].Association() ||
+ (*f)[i].iTunnel != iTunnel[i]())
+ {
+ // A mismatch has been detected. Either this or previous
+ // fragment had invalid IPSEC applied. There is no way
+ // to know which one, thus just drop the current.
+ return EIpsec_FragmentMismatch;
+ }
+ }
+ // All applied IPSEC matches, release the stored information
+ *h = f->iNext;
+ delete f;
+ break;
+ }
+ return KErrNone;
+ }
+
+TInt CProtocolSecpol::TransformL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo)
+ /**
+ * Do IPsec transforms for incoming packet.
+ *
+ * The CProtocolIpsec binds to AH, ESP and UDP (for NAT traversal), but delegates
+ * actual processing to this main class -- CProtocolIpsec::ApplyL() just calls
+ * this TransformL() directly.
+ *
+ * Additionally, CProtocolIpsec hooks the IPv6 Fragment header to detect
+ * IPv6 fragments.
+ *
+ * @li Recognize the AH and ESP, and unwrap them from the packet,
+ * and remember the transforms as packet state.
+ * @li Recognize the special UDP that does the NAT traversal
+ * @li Recognize fragments that are uncovered by detunneling
+ *
+ * @param aPacket The incoming packet
+ * @param aInfo The packet info.
+ * @return KIp6Hook_PASS, KIp6Hook_DONE or error, depending on conditions.
+ */
+ {
+ TInt reason = KErrNotSupported;
+ CheckPacketId(aPacket); // Do the packet id check
+
+ for (;;)
+ {
+ if (!iAssociationManager)
+ break; // No CProtocolKey ==> Drop Packet!
+ //
+ // A reminder: Nothing says that the same SA cannot apply
+ // multiple times to a packet (although it would be a weird
+ // policy to define). Thus, beware that table may include
+ // multiple pointers to the same SA. However, iProtocolKey->ApplyL
+ // returns an error, if the SA expires and the packet is dropped
+
+ const TUint id = aInfo.iProtocol;
+ const TInt is_udp = (id == KProtocolInetUdp);
+
+ if (aInfo.iIcmp)
+ {
+ // An ICMP Error report, where the returned packet contains
+ // ESP or AH header supposedly generated by this host...
+ if (is_udp)
+ return KIp6Hook_PASS;
+ reason = EIpsec_IcmpError;
+ break; // Not much one can do...
+ }
+
+ if (id == KProtocolInetEsp)
+ // There will be ESP processing, for temporary security fix,
+ // prevent this packet from being used in ICMP Error
+ // reports -- msa
+ aInfo.iFlags |= KIpNeverIcmpError;
+ else if (id == KProtocolInetIpip)
+ {
+ // Extract Fragment information from the IPv4 IP Header
+ TInet6Packet<TInet6HeaderIP4> ip(aPacket, aInfo.iOffset);
+ if (ip.iHdr == NULL)
+ {
+ reason = EIpsec_CorruptPacketIn;
+ break; // Drop! (packet too short)
+ }
+ if (ip.iHdr->MF() != 0 || ip.iHdr->FragmentOffset() != 0)
+ {
+ iIsFragment = 1;
+ iId = ip.iHdr->Identification() | (ip.iHdr->Protocol() << 16);
+ // What should the scope id be?
+ iSrc.SetAddress(ip.iHdr->SrcAddr());
+ iDst.SetAddress(ip.iHdr->DstAddr());
+ reason = CheckFragmentPolicy();
+ if (reason < 0)
+ break;
+ return KIp6Hook_PASS;
+ }
+ return KIp6Hook_PASS;
+ }
+ else if (id == KProtocolInet6Fragment)
+ {
+ // Extract Fragment information from the IPv6 Fragment Header
+ TInet6Packet<TInet6HeaderFragment> fh(aPacket, aInfo.iOffset);
+ if (fh.iHdr == NULL)
+ {
+ reason = EIpsec_CorruptPacketIn;
+ break; // Drop! (packet too short)
+ }
+ iIsFragment = 1;
+ iId = fh.iHdr->Id();
+ iSrc.SetAddress(aInfo.iSrcAddr);
+ iDst.SetAddress(aInfo.iDstAddr);
+ reason = CheckFragmentPolicy();
+ if (reason < 0)
+ break;
+ return KIp6Hook_PASS;
+ }
+
+ if (iCountSA == KIpsecMaxNesting)
+ {
+ reason = EIpsec_MaxTransforms;
+ break; // Too deep nesting!!!
+ }
+
+ iMyself[iCountSA].Set(TInetAddr::Cast(aInfo.iDstAddr));
+ CSecurityAssoc *sa = NULL;
+ TIpAddress tunnel;
+ reason = iAssociationManager->ApplyL(sa, aPacket, aInfo, id, tunnel);
+ iTunnel[iCountSA].Set(tunnel, tunnel.iScope);
+
+ if (reason == EIpsec_NotANATTPacket)
+ return KIp6Hook_PASS; // The packet was a normal UDP packet, pass through
+ else if (reason < 0)
+ {
+ //
+ // ApplyL wants the packet dropped
+ //
+ break;
+ }
+ //
+ // If id (= protocol) was UDP, do not increment iCountSA
+ // The current packet has been an UDP capsulated ESP packet from
+ // which the UDP header is now been removed
+ if (!is_udp)
+ {
+ // If detunneling happened, there is a need to change
+ // the interface index based on inner destination! -- msa
+ if (!iTunnel[iCountSA]().IsNone())
+ {
+ UpdateTunnelInterface(aInfo, sa);
+ iIsTunnelMode = 1; // (should actually take this flag from the SA --msa)
+ };
+ iSA[iCountSA++].Reset(sa); // Attach the returned sa to the handle
+ }
+ //
+ // The packet has been changed, report this to the
+ // caller.
+ aInfo.iProtocol = reason;
+ return KIp6Hook_DONE;
+ }
+ //
+ // A Packet is dropped, deliver a copy to potentially interested
+ // parties (should have some limiter here?)
+ //
+ //
+ // *NOTE*
+ // The reason for the packet drop is now passed in the Port
+ // field of the info.iSrcAddr. Somewhat kludgy... --msa
+ //
+ iCountSA = 0;
+ LOG(Log::Printf(_L("IPSEC Transform (%d) failed %d"), aInfo.iProtocol, reason));
+ aInfo.iSrcAddr.SetPort(reason);
+ Deliver(aPacket);
+ aPacket.Free();
+ return reason;
+ }
+
+TInt CProtocolSecpol::ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo)
+ /**
+ * Peek at the packet before "upper layer".
+ *
+ * The Secpol installs a hook that request the stack to show each packet
+ * to IPsec just before it is given to the final upper layer protocol,
+ * such as UDP and TCP, but also any other protocol which is not
+ * handled as an extension header. Note that this includes IP-in-IP
+ * tunneling protocols.
+ *
+ * The method compares an incoming packet and accumulated IPsec state
+ * against the security policy and decides whether the packet is to be
+ * allowed in.
+ *
+ * @param aPacket The packet in "unpacked" state
+ * @param aInfo The packet information block
+ * @return As specified for the hook mechanism
+ *
+ * - any Leave causes packet to be dropped
+ * - return < 0, the hook took "ownership" of the packet (usually just dropped it explicitly)
+ * - return == KIp6Hook_PASS, pass the packet to the next hook
+ *
+ * Note, the return "KIp6Hook_DONE" is not used by Secpol here.
+ */
+ {
+ CSecurityPolicy *const policy = Policy();
+ if (policy == NULL)
+ return KIp6Hook_PASS; // No Policy ==> No security Check, pass all
+
+ if (iIsTunnelMode)
+ {
+ // This IPSEC on top of IP-tunnel, and detunneling is being delegated to the
+ // IP layer. As it is treated as "transport protocol", IPSEC gets called
+ // for a policy check. But, if the IPSEC is in tunnel mode, the policy check
+ // needs to be delayed until some inner transport header..
+ ASSERT(aInfo.iProtocol == KProtocolInetIpip || aInfo.iProtocol == KProtocolInet6Ipip);
+ iIsTunnelMode = 0;
+ return KIp6Hook_PASS;
+ }
+
+ TInt reason = KErrNotSupported;
+ const TUint id = aInfo.iProtocol;
+ TPolicyFilterInfo filter;
+
+ if (!iAssociationManager)
+ User::Leave(reason); // No CProtocolKey ==> Drop Packet!
+ CheckPacketId(aPacket);
+
+ //
+ // Now is the time to check the whether the accumulated
+ // IPSEC matches the policy...
+ //
+ // The topmost header in packet now holds the packet that
+ // should correspond something to which the policy is
+ // applied.
+ //
+ // For Inbound, Local = Destination address, of the packet
+ // Remote = Source Address of the packet
+ filter.iIndex = aInfo.iInterfaceIndex;
+ filter.iFlags = KPolicyFilter_INBOUND;
+ iPktInfo.FillZ();
+ // (The addresses in info are already in IPv6 format)
+ iPktInfo.iLocal.Set(TInetAddr::Cast(aInfo.iDstAddr));
+ iPktInfo.iRemote.Set(TInetAddr::Cast(aInfo.iSrcAddr));
+
+ // Retrieve all available selector information from the packet
+ // (requires some layer breaking snooping...)
+ for (const TSnoopHeader *f = snooper; f < snooper_end; ++f)
+ {
+ if (f->iProtocol == id)
+ {
+ TInet6Packet<TUpperLayerSnoop> snoop(aPacket, aInfo.iOffset);
+ const TUpperLayerSnoop *const hdr = snoop.iHdr;
+ if (hdr == NULL)
+ {
+ reason = EIpsec_CorruptPacketIn;
+ goto drop;
+ }
+ switch (f->iSelector)
+ {
+ case KLoadPorts:
+ iPktInfo.iPortRemote = hdr->Port(f->iO1); // src port is remote port for incoming
+ /* Fall through */
+ case KLoadLocal:
+ iPktInfo.iPortLocal = hdr->Port(f->iO2); // dst port is local port for incoming
+ iPktInfo.iFlags |= KTransportSelector_PORTS;
+ break;
+ case KLoadBoth:
+ iPktInfo.iPortLocal = hdr->Byte(f->iO2); // ...load code to lower 8 bits.
+ /* Fall through */
+ case KLoadType:
+ iPktInfo.iPortLocal |= hdr->Byte(f->iO1) << 8; // ..load type to upper 8 bits.
+ iPktInfo.iPortRemote = iPktInfo.iPortLocal;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ iPktInfo.iProtocol = (TUint8)id;
+
+ if (aInfo.iIcmp == 0)
+ {
+ //
+ // However, if this call is normal processing call for ICMP, check if the
+ // ICMP is actually an error report. In such case trust the main loop
+ // to call this hook again with iIcmp != 0 (it's a bit delicate, as
+ // the check for ERROR ICMP must *exactly* match the check in the
+ // IP layer, or this will pass ICMP packets that are not allowed by
+ // the policy!!!
+ //
+ if (id == KProtocolInet6Icmp)
+ {
+ if (iPktInfo.iPortLocal < (128<<8))
+ return KIp6Hook_PASS; // Wait for error processing call!
+ }
+ else if (id == KProtocolInetIcmp)
+ {
+ switch (iPktInfo.iPortLocal >> 8)
+ {
+ case KInet4ICMP_Unreachable: // Destination unreachable
+ case KInet4ICMP_SourceQuench: // Source Quench
+ case KInet4ICMP_Redirect: // Redirect request
+ case KInet4ICMP_TimeExceeded: // Time Exceeded
+ case KInet4ICMP_ParameterProblem: // Parameter Problem
+ return KIp6Hook_PASS; // Wait for error processing call!
+ default:
+ break;
+ }
+ }
+ // Not an ICMP or normal ICMP, do the policy check with current info
+ }
+ for (;;)
+ {
+ CPolicyAction *items[KIpsecMaxNesting];
+ TInt tunnels = 0;
+
+ RPolicySelectorInfo final_info = iPktInfo;
+ const TInt count = CollectBundle(filter, final_info, KIpsecMaxNesting, items, NULL, tunnels);
+ if (count < 0)
+ {
+ reason = count;
+ break;
+ }
+
+ const RIpAddress *dst = &iPktInfo.iLocal; // initial destination is the inner dst (iLocal of incoming)
+ const RIpAddress *src = &iPktInfo.iRemote; // initial source is the inner src (iRemote of incoming)
+
+ for (TInt j = 0; j < count; j++)
+ {
+ const CPolicyAction &item = *items[j];
+
+ // Need to save current src/dst in case optional
+ // processing is done (only pointers saved).
+ const RIpAddress *const dst_opt = dst;
+ const RIpAddress *const src_opt = src;
+
+
+ if (--iCountSA < 0)
+ {
+ reason = EIpsec_TooFewTransforms;
+ goto optional;
+ }
+ //
+ // Verify that the tunnel address (if any) matches with
+ // the policy entry.
+ if (!item.iIsTunnel)
+ {
+ if (!iTunnel[iCountSA]().IsNone())
+ {
+ reason = EIpsec_TunnelMismatch;
+ goto optional;
+ }
+ }
+ else
+ {
+ //
+ // If a tunnel is specified, all remaining SA's have the src
+ // address of the this tunnel
+ //
+ if (item.iTunnel().IsNone())
+ src = &iTunnel[iCountSA]; // Match tunnel from any source.
+ else if (!KOuterIsInner.IsEqual(item.iTunnel()))
+ src = &item.iTunnel; // == Outer src
+
+ // From the result of TAHI Tunnel test, there is some reason that cause the
+ // "src" not eual to iTunnel[iCountSA] but equal to iMyself[iCountSA], that
+ // will leads to "EIpsec_TunnelMismatch" error in original code.
+ if (iTunnel[iCountSA]() != (*src)())
+ {
+ if (iMyself[iCountSA]() != (*src)())
+ {
+ reason = EIpsec_TunnelMismatch;
+ goto optional;
+ }
+ src = &iTunnel[iCountSA]; //set "src" to tunnel address
+ }
+ dst = &iMyself[iCountSA];
+ }
+
+ if (item.iSpec)
+ {
+ // An IPSEC SA is required
+ if (iSA[iCountSA].Association() == NULL)
+ {
+ //
+ // SA was present when the AH or ESP was processed, but now appears to have
+ // disappeared. Expired, because it was used in two transforms, and latter
+ // expired? I don't expect to see this error, but just in case... -- msa
+ //
+ reason = EIpsec_LostSA;
+ goto drop;
+ }
+#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
+ else if ((reason = iAssociationManager->Verify(iSA[iCountSA].Association(), item.iSpec->iSpec,item.iSpec->iPropList, *src, *dst, iPktInfo)) == KErrNone)
+#else
+ else if ((reason = iAssociationManager->Verify(iSA[iCountSA].Association(), item.iSpec->iSpec, *src, *dst, iPktInfo)) == KErrNone)
+#endif // SYMBIAN_IPSEC_VOIP_SUPPORT
+ {
+ if (item.iIsTunnel)
+ {
+ // If it was a tunnel mode, need update the packet specific
+ // information for the remaining SA's.
+ iPktInfo.iRemote = *src;
+ iPktInfo.iLocal = *dst;
+ AfterTunnelAction(iPktInfo);
+ }
+ continue;
+ }
+ }
+ else if (iSA[iCountSA].Association())
+ {
+ // Note: Above test may not work in general: if the packet contains two
+ // IPSEC layers using the same SA, and if the later application expires
+ // the SA, the iAssociation member of the first one disappears too!!
+ // [in such case, instead of reporting unrequired SA, it would just
+ // accept it--probably not a serious mishap...]
+ // However, would not get this far? Expiry would drop the packet at
+ // apply phase or the other SA would not verify anyway? -- msa
+ reason = EIpsec_UnrequiredSA;
+ }
+ else
+ continue;
+
+optional: // Only gets here in case there is a mismatch. If
+ // the item is optional, then retest the current
+ // with the next item.
+ if (!item.iOptional)
+ goto drop;
+ //
+ // Restore to same state for the next item
+ //
+ ++iCountSA;
+ dst = dst_opt;
+ src = src_opt;
+ }
+#ifdef __WINS__
+ if (iCountSA > 0)
+#else /*__WINS__*/
+ if ((KIPsecTunnelInTunnel == 0) && (iCountSA > 0))
+#endif /*__WINS__*/
+ {
+ reason = EIpsec_TooManyTransforms;
+ break;
+ }
+#ifndef __WINS__
+ //This is for logging only would be removed during MCL submission
+ else if(KIPsecTunnelInTunnel == 1)
+ {
+ //This is for logging only would be removed during MCL submission
+ LOG(Log::Printf(_L("\nKIPsecTunnelInTunnel=1 enabled")));
+ }
+#endif /* __WINS__*/
+ // *NOTE*
+ // There is no need to detach the SA's from the handles. Handle does
+ // not prevent SA from being deleted! Just clearing iCountSA is OK!
+ //
+ return KIp6Hook_PASS;
+ }
+drop:
+ // A Packet is dropped, deliver a copy to potentially interested
+ // parties (should have some limiter here?)
+ //
+ //
+ // *NOTE*
+ // The reason for the packet drop is now passed in the Port
+ // field of the info.iSrcAddr. Somewhat kludgy... --msa
+ //
+ LOG(LogSelectorInfo(_L("incoming: "), iPktInfo, filter));
+ LOG(Log::Printf(_L("\tpolicy check failed %d"), reason));
+
+ iCountSA = 0;
+ aInfo.iSrcAddr.SetPort(reason);
+ Deliver(aPacket);
+ aPacket.Free();
+ return reason;
+ }
+
+//
+
+CServProviderBase* CProtocolSecpol::NewSAPL(TUint aSockType)
+ /**
+ * Create a new instance of CProviderSecpol for a socket opened by an application.
+ *
+ * This socket can be used to load the security policy and to listen
+ * what packets are dropped due to current policy.
+ *
+ * This is called from socket server.
+ *
+ * @param aSockType Must be KSockRaw
+ * @return new CProviderSecpol instance.
+ */
+ {
+ if (aSockType!=KSockRaw)
+ User::Leave(KErrNotSupported);
+ CProviderSecpol *pSAP = new (ELeave) CProviderSecpol(*this);
+ iSAPlist.AddLast(*pSAP);
+ return pSAP;
+ }
+
+void CProtocolSecpol::FixupInterfaceIndexes(CSecurityPolicy *aPolicy)
+ {
+ /**
+ * Complete the interfaces with index.
+ *
+ * If the policy contains interface specific policies, then
+ * one must at this point convert the interface names into
+ * actual interface indexes.
+ */
+ if (aPolicy && aPolicy->iInterfaces && NetworkService())
+ {
+ // First, find the real interface indexes
+ for (CSelectorInterface *si = aPolicy->iInterfaces; si != NULL; si = si->iNext)
+ {
+ si->iInterfaceIndex = GetInterfaceIndex(*si->iName);
+ }
+ }
+ }
+
+TInt CProtocolSecpol::SetPolicy(const TDesC &aPolicy, TUint &aOffset)
+ /**
+ * Replace the current security policy database with a new one.
+ *
+ * Called from CProviderSecpol::Write(). The tasks are
+ *
+ * @li Convert the policy definition string into internal format (CSecurityPolicy)
+ * using the parser CSecurityPolicy::SetPolicy().
+ * @li Complete the CPolicyInterface objects with indexes (FixupInterfaceIndexes()).
+ * @li The policy parser creates the algorithm map (CAlgorithmList) as a "side effect".
+ * Pass the new algorithm map to the MAssociationManager::SetAlgorithms().
+ * @li Notify stack that all flows must be re-negotiated (MNetworkService::SetChanged()).
+ *
+ * @param aPolicy The new security policy
+ * @retval aOffset Current parsing point (also starting point initially)
+ * @return KErrNone, if success and < 0 otherwise
+ *
+ * In case of error, the aOffset indicates the position in
+ * aPolicy at which the parsing terminated.
+ */
+ {
+ if (iAssociationManager == NULL)
+ {
+ LOG(Log::Printf(_L("CProtocolSecpol::SetPolicy -- cannot load because PFKEY is not running")));
+ return KErrNotReady;
+ }
+
+ const TInt res = CSecurityPolicy::SetPolicy(iPolicy, aPolicy, aOffset, iAssociationManager->EndPointCollection());
+ if (res != KErrNone)
+ return res;
+
+ if (iPolicy == NULL)
+ {
+ LOG(Log::Printf(_L("New policy is empty")));
+ return KErrNone;
+ }
+ else
+ {
+ FixupInterfaceIndexes(iPolicy);
+#ifdef _LOG
+ Log::Printf(_L("New policy:"));
+ iAssociationManager->EndPointCollection().LogPrint(_L("ep\t%S = { %S }"));
+ TInt i = 0;
+ for (CPolicySelector *ps = iPolicy->iSelectors; ps != NULL; ps = ps->iNext)
+ {
+ const TInt N = ps->iActions.Count();
+ TBuf<6> seq;
+ seq.AppendFormat(_L("%3d.\t"), ++i);
+ LogPolicySelector(seq, *ps);
+ if (ps->iFilterData & KPolicyFilter_DROP)
+ Log::Printf(_L("\t\t*DROP*"));
+ else if (N == 0)
+ Log::Printf(_L("\t\t*PASS*"));
+ else
+ {
+ for (TInt i = 0; i < N; ++i)
+ LogBundleItem(_L("\t\t"), *ps->iActions[i]);
+ }
+ }
+ Log::Printf(_L("---"));
+#endif
+ }
+
+ // Currently, the algorithm map results as a side effect
+ // from the policy loading operation (it really should be
+ // independent configuration file). Pass the map to the
+ // PFKEY protocol.
+ iAssociationManager->SetAlgorithms(iPolicy->iAlgorithms);
+
+ if (NetworkService())
+ {
+ // New policy has been installed, request rechecking of
+ // all open flows...
+ NetworkService()->SetChanged();
+ }
+
+ return KErrNone;
+ }
+
+void CProtocolSecpol::Deliver(RMBufPacketBase& aPacket)
+ /**
+ * Deliver a copy of dropped packets to policy sockets.
+ *
+ * This method sends a copy of the packet to all currently open Policy sockets.
+ * This is called from the CProtocolSecpol ApplyL methods for each packet that
+ * is dropped. A copy of the packet is made for each open policy socket and
+ * delivered by a Deliver method of the SAP (see CProviderSecpol::Deliver).
+ *
+ * An application that opens a policy socket, but is not reading it, should close
+ * the input side (Shutdown), so that the queue of the dropped packets will
+ * not waste memory resources in the kernel.
+ *
+ * @param aPacket The packet to be dropped
+ */
+ {
+ CProviderSecpol* sap;
+ TDblQueIter<CProviderSecpol> iter(iSAPlist);
+
+ while (sap = iter++, sap != NULL)
+ if (sap->IsReceiving())
+ {
+ RMBufRecvPacket copy;
+ TRAPD(err, aPacket.CopyL(copy); aPacket.CopyInfoL(copy));
+ if (err == KErrNone)
+ {
+ copy.Pack();
+ sap->Deliver(copy);
+ }
+ // Can allways call free (if Deliver() took the packet, this is NOP)
+ copy.Free();
+ }
+ }