--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vpnengine/ikev1lib/src/ikev1negotiation.cpp Thu Dec 17 09:14:51 2009 +0200
@@ -0,0 +1,8306 @@
+/*
+* 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: IKEv1/IPSEC SA negotiation
+*
+*/
+
+#include <x509cert.h>
+#include <x500dn.h>
+#include <random.h>
+#include <vpnlogmessages.rsg>
+
+#include "ikev1negotiation.h"
+#include "ikedebug.h"
+#include "ikev1isakmpstream.h"
+#include "ikev1timeout.h"
+#include "ikev1payload.h"
+#include "ikev1plugin.h"
+#include "ikev1crack.h"
+#include "ikev1trans.h"
+#include "ipsecsadata.h"
+#include "ipsecsalifetime.h"
+#include "pfkeymsg.h"
+#include "kmdapi.h"
+#include "ikev1pkiservice.h"
+#include "ikev1crypto.h"
+#include "ikev1natdiscovery.h"
+#include "ikev1private.h"
+#include "vpnapidefs.h"
+#include "ikepkiutils.h"
+#include "vpnclientuids.h"
+#include "ikecalist.h"
+#include "ikecaelem.h"
+#include "ikecert.h"
+#include "ikev1pluginsession.h"
+#include "ikesocketdefs.h"
+#include "kmdeventloggerif.h"
+#include "pfkeyextdatautil.h"
+#include "ipsecsalist.h"
+
+const TInt KSubjectName = 1;
+
+/////////////////////////////////////////////////////////////////////////////////
+// CIkev1Negotiation related stuff
+/////////////////////////////////////////////////////////////////////////////////
+
+
+//
+//Proto negotiation to send a informational payload and be destroyed
+//
+CIkev1Negotiation* CIkev1Negotiation::NewL( CIkev1PluginSession* aPluginSession,
+ CPFKeySocketIf& aPFKeySocketIf,
+ MIkeDebug& aDebug,
+ const TInetAddr& aRemote,
+ const TCookie& aInitiator,
+ const TCookie& aResponder )
+ {
+ CIkev1Negotiation* self = new ( ELeave ) CIkev1Negotiation( aPluginSession,
+ aPFKeySocketIf,
+ aDebug );
+ CleanupStack::PushL( self );
+ self->iRemoteAddr = aRemote;
+ self->iCookie_I = aInitiator;
+ self->iCookie_R = aResponder;
+ self->iExchange = ISAKMP_EXCHANGE_INFO;
+ self->iDOI = IPSEC_DOI;
+ self->iTimer = CIkev1Timeout::NewL( *self );
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+CIkev1Negotiation* CIkev1Negotiation::NewL( CIkev1PluginSession* aPluginSession,
+ CPFKeySocketIf& aPFKeySocketIf,
+ MIkeDebug& aDebug,
+ const TInetAddr& aRemote,
+ const TCookie& aInitiator,
+ TBool aAutoLogin )
+ {
+ CIkev1Negotiation* self = new ( ELeave ) CIkev1Negotiation( aPluginSession,
+ aPFKeySocketIf,
+ aDebug,
+ aRemote,
+ aInitiator );
+ CleanupStack::PushL( self );
+ self->ConstructL( aAutoLogin );
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+CIkev1Negotiation* CIkev1Negotiation::NewL( CIkev1PluginSession* aPluginSession,
+ CPFKeySocketIf& aPFKeySocketIf,
+ MIkeDebug& aDebug,
+ TIkev1SAData* aIkev1SAdata,
+ TUint aRole,
+ const TPfkeyMessage *aReq )
+ {
+ CIkev1Negotiation* self = new ( ELeave ) CIkev1Negotiation( aPluginSession,
+ aPFKeySocketIf,
+ aDebug );
+ CleanupStack::PushL( self );
+ self->ConstructL( aIkev1SAdata, aRole, aReq );
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+
+CIkev1Negotiation* CIkev1Negotiation::NewL( CIkev1PluginSession* aPluginSession,
+ CPFKeySocketIf& aPFKeySocketIf,
+ MIkeDebug& aDebug,
+ const TInetAddr& aRemote,
+ TBool aAutoLogin )
+ {
+ CIkev1Negotiation* self = new ( ELeave ) CIkev1Negotiation( aPluginSession,
+ aPFKeySocketIf,
+ aDebug,
+ aRemote );
+ CleanupStack::PushL( self );
+ self->iCookie_I = self->CreateCookieL();
+ self->ConstructL( aAutoLogin );
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+CIkev1Negotiation* CIkev1Negotiation::NewL( CIkev1PluginSession* aPluginSession,
+ CPFKeySocketIf& aPFKeySocketIf,
+ MIkeDebug& aDebug,
+ const TInetAddr& aRemote,
+ const TPfkeyMessage& aReq )
+ {
+ CIkev1Negotiation* self = new ( ELeave ) CIkev1Negotiation( aPluginSession,
+ aPFKeySocketIf,
+ aDebug,
+ aRemote );
+ CleanupStack::PushL( self );
+ self->iCookie_I = self->CreateCookieL();
+ self->ConstructL( aReq );
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+//
+// Destructor
+//
+CIkev1Negotiation::~CIkev1Negotiation()
+ {
+ DEBUG_LOG1( _L("CIkev1Negotiation::~CIkev1Negotiation this=0x%x"), this );
+
+
+ if ( iRole == INITIATOR &&
+ iAcquirePending )
+ {
+ // Response with error to pending Acquire.
+ AcquireSAErrorResponse( KKmdIkeNegotFailed );
+ }
+
+ delete iIpsecSaSpiRetriever;
+ delete iSavedIkeMsgBfr;
+
+ if( iPluginSession )
+ {
+ iPluginSession->RemoveNegotiation( this );
+ }
+
+ iCookie_I.FillZ(ISAKMP_COOKIE_SIZE);
+
+ if(iTimer)
+ {
+ iTimer->Cancel();
+ delete iTimer;
+ }
+
+ delete iOwnCert;
+ delete iPeerX509Cert;
+ delete iPeerTrustedCA; // Peer trusted CA name
+ delete iICA1;
+ delete iICA2;
+
+ delete iPkiService; // Trusted CA certificate list
+
+ delete iSAPayload;
+ delete iPeerIdentPayload;
+ delete iOwnIdentPayload;
+
+ //Keys
+ delete iOwnKeys; //structure containing the public and private keys
+ delete iOwnPublicKey; //Public Key
+
+ //Phase_I Proposal
+ TAttrib *attr;
+ while ((attr = iProposal_I.iAttrList) != NULL)
+ {
+ iProposal_I.iAttrList = attr->iNext;
+ delete attr;
+ }
+
+ delete iChosenProposal_I.iAttrList; //Only one transformation chosen
+
+ //Phase_II proposals (include transformations)
+ delete iProposal_IIList;
+
+ //Phase_II chosen Proposal
+ delete iChosenProp_IIList;
+ delete iInboundSPIList;
+ delete iCRACKneg;
+ delete iTransactionNeg; // Transaction exchange
+ delete iInternalAddr;
+ delete iNatDiscovery;
+ delete iSARekeyInfo;
+ delete iLastMsg;
+}
+
+
+//
+// CIkev1Negotiation
+// Constructor for remote initiated negotiation
+//
+CIkev1Negotiation::CIkev1Negotiation( CIkev1PluginSession* aPluginSession,
+ CPFKeySocketIf& aPFKeySocketIf,
+ MIkeDebug& aDebug,
+ const TInetAddr& aRemote,
+ const TCookie& aInitiator )
+ : iCookie_I( aInitiator ),
+ iRemoteAddr( aRemote ),
+ iPluginSession( aPluginSession ),
+ iPFKeySocketIf( aPFKeySocketIf ),
+ iDebug( aDebug )
+{
+
+ DEBUG_LOG1( _L("CIkev1Negotiation::CIkev1Negotiation, RESPONDER this=0x%x"), this );
+ iCookie_R.FillZ(ISAKMP_COOKIE_SIZE);
+ iSAId = iPluginSession->GetSAId();
+ iPluginSession->LinkNegotiation(this);
+ iRole = RESPONDER;
+ iStage = 1; //next phase for the responder is 2
+}
+
+//
+// Constructor for local initiated negotiation
+//
+
+CIkev1Negotiation::CIkev1Negotiation( CIkev1PluginSession* aPluginSession,
+ CPFKeySocketIf& aPFKeySocketIf,
+ MIkeDebug& aDebug,
+ const TInetAddr& aRemote )
+ : iRemoteAddr( aRemote ),
+ iPluginSession( aPluginSession ),
+ iPFKeySocketIf( aPFKeySocketIf ),
+ iDebug( aDebug )
+{
+ DEBUG_LOG1( _L("CIkev1Negotiation::CIkev1Negotiation, INITIATOR this=0x%x"), this );
+ iCookie_I.FillZ(ISAKMP_COOKIE_SIZE);
+ iCookie_R.FillZ(ISAKMP_COOKIE_SIZE);
+ iSAId = iPluginSession->GetSAId();
+ iPluginSession->LinkNegotiation(this);
+ iRole = INITIATOR;
+ iStage = 0;
+}
+
+//
+// Constructor for Phase II initiated negotiations
+//
+
+CIkev1Negotiation::CIkev1Negotiation( CIkev1PluginSession* aPluginSession,
+ CPFKeySocketIf& aPFKeySocketIf,
+ MIkeDebug& aDebug )
+ : iPluginSession( aPluginSession ),
+ iPFKeySocketIf( aPFKeySocketIf ),
+ iDebug( aDebug )
+{
+ DEBUG_LOG1( _L("CIkev1Negotiation::CIkev1Negotiation, this=0x%x"), this );
+ iCookie_I.FillZ(ISAKMP_COOKIE_SIZE);
+ iCookie_R.FillZ(ISAKMP_COOKIE_SIZE);
+ iSAId = iPluginSession->GetSAId();
+ iPluginSession->LinkNegotiation(this);
+}
+
+//Used when creating a negotiation as a RESPONDER!
+//Used also when Initiator (= Phase 1 negotiation stated by the policy activation)
+//Leaves if error detected
+void CIkev1Negotiation::ConstructL(TBool aAutoLogin)
+{
+ iIpsecSaSpiRetriever = CIpsecSaSpiRetriever::NewL( *this,
+ iPFKeySocketIf );
+
+ iAutoLogin = aAutoLogin;
+ CommonConstructL();
+}
+
+
+//Used when an Acquire is received
+//Leaves if error detected
+void CIkev1Negotiation::ConstructL(const TPfkeyMessage &aReq)
+{
+ iIpsecSaSpiRetriever = CIpsecSaSpiRetriever::NewL( *this,
+ iPFKeySocketIf );
+
+ CommonConstructL();
+ GetAcquireDataL(aReq);
+}
+
+//Used when initiating a new Phase II negotiation from a negotiated ISAKMP SA.
+void CIkev1Negotiation::ConstructL( TIkev1SAData* aSA,
+ TUint aRole,
+ const TPfkeyMessage *aReq )
+{
+
+ iIpsecSaSpiRetriever = CIpsecSaSpiRetriever::NewL( *this,
+ iPFKeySocketIf );
+
+ iPhase = PHASE_II;
+
+ //Almost like common construct
+ iTimer = CIkev1Timeout::NewL(*this);
+
+ //Phase II proposal lists
+ iProposal_IIList = new (ELeave) CProposal_IIList(1);
+
+ iRemoteAddr.SetPort(IKE_PORT);
+
+ iSeq = aSA->iSeq;
+ iLocalAddr = aSA->iLocalAddr;
+ iRemoteAddr = aSA->iRemoteAddr; // Remote Address from peer
+ iLastRemoteAddr = aSA->iDestinAddr; // Remote Address last transmitted
+
+ iLastIKEMsgInfo = aSA->iLastIKEMsgInfo;
+ iLastMsg = HBufC8::NewL(4096);
+ if ( aSA->iLastMsg != NULL )
+ {
+ *iLastMsg = *aSA->iLastMsg;
+ }
+
+ iRole = aRole; // If we are initiator or responder
+
+ //Header Data
+ iCookie_I = aSA->iCookie_I; // Initiator Cookie (Used with responder to create KEYID)
+ iCookie_R = aSA->iCookie_R; // Responder Cookie
+ iPrevExchange = aSA->iPrevExchange;
+ iExchange = IKE_QUICK_MODE;
+
+ iMessageId = RandomMessageId();
+
+ iFlags = aSA->iFlags; // Flags in the msg header
+
+// iNotifyMessageId = aSA->iHdr.iNotifyMessageId; // Message Id. for Informational Exchanges
+
+ //SA Data
+ iDOI = IPSEC_DOI;
+
+ iChosenProposal_I.iProtocol = PROTO_ISAKMP;
+ iChosenProposal_I.iProposalNum = 0;
+// iChosenProposal_I.iSPI.Copy(aSA->iChosenProposal_I.iSPI);
+ iChosenProposal_I.iNumTransforms = 1;
+ iChosenProposal_I.iAttrList = new (ELeave) TAttrib; //allocated now and deleted when destroying the obj
+// *iChosenProposal_I.iAttrList = *aSA->iChosenProposal_I.iAttrList;
+ iChosenProposal_I.iAttrList->iEncrAlg = aSA->iEncrAlg;
+ iChosenProposal_I.iAttrList->iHashAlg = aSA->iHashAlg;
+ iChosenProposal_I.iAttrList->iGroupDesc = aSA->iGroupDesc;
+ iChosenProposal_I.iAttrList->iGroupType = aSA->iGroupType;
+ iChosenProposal_I.iAttrList->iKeyLength = aSA->iKeyLength;
+ if ( aSA->iLifeTimeSecs )
+ iChosenProposal_I.iAttrList->iLifeDurationSecs.Copy((TUint8 *)&aSA->iLifeTimeSecs, sizeof(TUint32));
+ if ( aSA->iLifeTimeKB )
+ iChosenProposal_I.iAttrList->iLifeDurationKBytes.Copy((TUint8 *)&aSA->iLifeTimeKB, sizeof(TUint32));
+
+
+ iHostData = aSA->iIkeData;
+ if ( iHostData->iCAList && iHostData->iCAList->Count() )
+ {
+ iPkiService = CIkeV1PkiService::NewL(iHostData, iDebug);
+ }
+
+ if (aRole == INITIATOR)
+ {
+ GetAcquireDataL(*aReq); //Data needed from the acquire
+ }
+ else //RESPONDER
+ {
+ iAcquireSeq = 1; //msg Seq. number
+ }
+
+ //
+ //NAT Traversal data
+ //
+ iFamiliarPeer = aSA->iFamiliarPeer;
+ iNAT_T_Required = aSA->iNAT_T_Required;
+
+ //
+ //Copy an Internal address object (if exists) to the new negotiation
+ //
+ if ( aSA->iVirtualIp )
+ {
+ iInternalAddr = CInternalAddress::NewL(*(aSA->iVirtualIp));
+ }
+
+ iNAT_D_Flags = aSA->iNAT_D_Flags;
+ if ( iNAT_D_Flags ) {
+ //
+ // Use IETF specified NAT traversal
+ //
+ iNatDiscovery = CIkev1NatDiscovery::NewL(aSA->iNAT_D_Flags);
+ }
+
+ //Keys (DH Generated public value when own)
+ iSKEYID.Copy(aSA->iSKEYID);
+ iSKEYID_d.Copy(aSA->iSKEYID_d);
+ iSKEYID_a.Copy(aSA->iSKEYID_a);
+ iSKEYID_e.Copy(aSA->iSKEYID_e);
+
+ //IV used by des_cbc and des3_cbc is 8 but digest returns 16 bytes for MDx and 20 for SHS (first 8 used)
+ iIV = aSA->iIV; //normal IV
+ iIVSize = iIV.Length();
+
+ iLastIV = aSA->iLastIV; //Saves the last IV of PHASE_I to compute iNotifIV everytime
+
+ //Nonces
+// iNONCE_I = aSA->iNONCE_I;
+// iNONCE_R = aSA->iNONCE_R;
+ //
+ // If the ISAKMP SA (=aSA) has been originally negotiated due the policy activation (iAutoLogin = ETrue)
+ // The iLocalAddr maybe then undefined in aSA.
+ // If local end is acting as an initiator, method GetAcquireDataL updates iLocalAddr in CIkev1Negotiation object.
+ // In that case take iLocalAddr value from there and store it to CIsakmpSa (aSA) object.
+ // If local end is acting as a responder, resolve iLocalAddr value.
+ //
+ if ( aSA->iAutoLogin )
+ {
+ if ( aRole == INITIATOR)
+ iLocalAddr = aSA->iLocalAddr;
+ else User::LeaveIfError( iPluginSession->GetLocalAddress( iLocalAddr ) );
+ }
+ iDPDSupported = aSA->iDPDSupported;
+
+ iSAId = aSA->iSAId; // Reference to existin ISAKMP SA
+ iStage = 1; //if initiator the stage will be set in a subsequent call to InitPhase_IIL()
+}
+
+//Leaves if error detected
+void CIkev1Negotiation::CommonConstructL()
+{
+ iPhase = PHASE_I;
+ iTimer = CIkev1Timeout::NewL(*this);
+
+ //Phase II proposal lists
+ iProposal_IIList = new (ELeave) CProposal_IIList(1);
+
+ iRemoteAddr.SetPort(IKE_PORT);
+
+ iHostData = &iPluginSession->IkeData();
+ if (!iHostData)
+ {
+ DEBUG_LOG(_L("The host has no data. Negotiation aborted"));
+ User::Leave(KKmdIkeNoPolicyErr);
+ }
+
+ if ( iHostData->iCAList && iHostData->iCAList->Count() )
+ {
+ iPkiService = CIkeV1PkiService::NewL(iHostData, iDebug);
+ TInt stat ( EFalse );
+ stat=ReadCAsL(iHostData->iCAList); // Build trusted CA list
+ if ( !stat )
+ {
+ SetErrorStatus( KVpnErrInvalidCaCertFile );
+ User::Leave(KVpnErrInvalidCaCertFile);
+ }
+ }
+ iSendCert = iHostData->iAlwaysSendCert; //If true will always be sent, otherwise only with a CR
+
+ //Proposals
+ if (!BuildProposals1L())
+ {
+ SetFinished();
+ User::Leave(KKmdIkePolicyFileErr); //if any error returns
+ }
+
+ iEncoding = X509_CERT_SIG; //Only type in use
+
+ iChosenProposal_I.iAttrList = new (ELeave) TAttrib; //allocated now and deleted when destroying the obj
+ User::LeaveIfError( iPluginSession->GetLocalAddress( iLocalAddr ) );
+
+ //PFKEY data (is overwritten if initiator because we use the data given in the acquire
+
+ iAcquireSeq = 1;//msg Seq. number of acquire needed for the UPDATE
+ iSeq = 1; //Sequence number for pfkey messages
+
+ iLastRemoteAddr = iRemoteAddr; // Used as destination address when sending data
+#ifdef _DEBUG
+ TBuf<40> txt_addr;
+ iRemoteAddr.OutputWithScope(txt_addr);
+#endif
+
+ iLastMsg = HBufC8::NewL(4096);
+
+ DEBUG_LOG1( _L("New negotiation with Host %S"),
+ &txt_addr );
+
+ if ( !iHostData->iUseNatProbing && iHostData->iEspUdpPort == 0 )
+ {
+ //
+ // Use IETF specified NAT traversal
+ //
+ iNatDiscovery = CIkev1NatDiscovery::NewL(0);
+ }
+
+ if ( iRole == RESPONDER )
+ {
+ //
+ // Get base value internal address (=VPN virtual IP)
+ //
+ iInternalAddr = iPluginSession->InternalAddressL();
+ }
+}
+
+
+TBool CIkev1Negotiation::ReadCAsL(CArrayFixFlat<TCertInfo*> *aCAList)
+{
+
+ TBool Status = iPkiService->ImportCACertsL(aCAList);
+
+#ifdef _DEBUG
+ if ( !Status )
+ {
+ DEBUG_LOG( _L("Trusted CA list creation failed!") );
+ }
+#endif // _DEBUG
+
+ return Status;
+
+}
+
+TBool CIkev1Negotiation::ReadOwnCertL()
+{
+ //
+ // Read own certificate from PKI store using own trusted CA as
+ // specified issuer
+ //
+ TBool Status = EFalse;
+ _LIT(KVpnApplUid, "101F7993");
+ if ( iPkiService && iHostData->iCAList )
+ {
+ TCertInfo* CertInfo;
+ HBufC8* CAName = HBufC8::NewLC(256);
+ TInt i = 0;
+ if ( iHostData->iCAList->At(0)->iData!=KVpnApplUid )
+ {
+ while ( i < iHostData->iCAList->Count() )
+ {
+
+ CertInfo = iHostData->iCAList->At(i);
+ CAName->Des().Copy(CertInfo->iData); // Assure that CA name is in ASCII format
+ if ( iPkiService->ReadUserCertWithNameL(CAName->Des(), iHostData, EFalse) == KErrNone )
+ {
+ Status = ETrue;
+ delete iOwnCert; // delete old if exists
+ iOwnCert = iPkiService->GetCertificate();
+ delete iPeerTrustedCA; // for sure
+ iPeerTrustedCA = iPkiService->GetTrustedCA();
+ CleanupStack::PopAndDestroy(CAName); //CAName
+ CAName=NULL;
+ break;
+ }
+ i ++;
+ }
+ if (!Status)
+ {
+ CleanupStack::PopAndDestroy(CAName);
+ CAName=NULL;
+ }
+ }
+ else
+ {
+ CIkeCaList* trustedCaList = iPkiService->CaList();
+ CleanupStack::PopAndDestroy(CAName);
+ CAName=NULL;
+ while ( i < trustedCaList->Count() )
+ {
+
+ CIkeCaElem* CaElem = (*trustedCaList)[i];
+ CAName = IkeCert::GetCertificateFieldDERL(CaElem->Certificate(), KSubjectName);
+ if (CAName == NULL)
+ {
+ User::Leave(KErrArgument);
+ }
+ CleanupStack::PushL(CAName);
+ if ( iPkiService->ReadUserCertWithNameL(*CAName, iHostData, ETrue) == KErrNone)
+ {
+ Status = ETrue;
+ delete iOwnCert; // delete old if exists
+ iOwnCert = iPkiService->GetCertificate();
+ delete iPeerTrustedCA;
+ iPeerTrustedCA = iPkiService->GetTrustedCA();
+ CleanupStack::PopAndDestroy(CAName);
+ CAName=NULL;
+
+
+ break;
+ }
+ i ++;
+
+ CleanupStack::PopAndDestroy(CAName);
+ CAName=NULL;
+ }
+ }
+ if ( !Status )
+ {
+ DEBUG_LOG( _L("Error loading Own Certificate!") );
+ }
+ }
+ return Status;
+}
+
+
+
+void CIkev1Negotiation::GetAcquireDataL(const TPfkeyMessage &aReq)
+{
+ // ACQUIRE Contains: <Base, Addr(Src & Dest) (Addr(Proxy)), (Ident(S & D)), (Sensitivity), Proposal>
+
+ iLocalAddr = *aReq.iSrcAddr.iAddr; //Copies our own address because it's the only way to know it
+
+ //Phase II proposals
+ //Only one combination received so only one transform
+ CProposal_II *prop_II = new (ELeave) CProposal_II();
+ CleanupStack::PushL(prop_II);
+ prop_II->ConstructL();
+ iProposal_IIList->AppendL(prop_II);
+ CleanupStack::Pop(); //prop_II safe in case of leave
+
+ prop_II->iProposalNum = FIRST_IPSEC_PROPOSAL;
+ prop_II->iNumTransforms = 1;
+ prop_II->iReplayWindowLength = aReq.iProposal.iExt->sadb_prop_replay;
+ TAttrib_II *attr_II = new (ELeave) TAttrib_II();
+ CleanupStack::PushL(attr_II);
+ prop_II->iAttrList->AppendL(attr_II); //added into the proposal so it's safe if function leaves
+ CleanupStack::Pop(); //attr_II safe in case of leave
+
+ attr_II->iTransformNum = FIRST_IPSEC_TRANSFORM;
+
+ if (aReq.iBase.iMsg->sadb_msg_satype==SADB_SATYPE_AH)
+ {
+ prop_II->iProtocol = PROTO_IPSEC_AH;
+ if (aReq.iProposal.iComb->sadb_comb_auth == SADB_AALG_MD5HMAC)
+ {
+ attr_II->iTransformID = AH_MD5;
+ attr_II->iAuthAlg = DOI_HMAC_MD5;
+ }
+ else if (aReq.iProposal.iComb->sadb_comb_auth == SADB_AALG_SHA1HMAC)
+ {
+ attr_II->iTransformID=AH_SHA;
+ attr_II->iAuthAlg = DOI_HMAC_SHA;
+ }
+ else
+ {
+ DEBUG_LOG(_L("Unsupported Authentication Algorithm in Acquire"));
+ SetFinished();
+ return;
+ }
+ // No auth with variable encryption
+ }
+ else if (aReq.iBase.iMsg->sadb_msg_satype == SADB_SATYPE_ESP)
+ {
+ prop_II->iProtocol=PROTO_IPSEC_ESP;
+ /* Request ESP from peer host */
+
+ attr_II->iTransformID = aReq.iProposal.iComb->sadb_comb_encrypt;
+ switch ( attr_II->iTransformID )
+ {
+ case ESP_DES_CBC:
+ case ESP_3DES_CBC:
+ case ESP_NULL:
+ break;
+
+ case ESP_AES_CBC:
+ attr_II->iKeyLength = aReq.iProposal.iComb->sadb_comb_encrypt_maxbits;
+ break;
+
+ default:
+ DEBUG_LOG(_L("IPsec Encryption algorithm is not implemented. Wrong algorithms file"));
+ SetFinished();
+ return;
+
+ }
+ if (aReq.iProposal.iComb->sadb_comb_auth != SADB_AALG_NONE)
+ {
+ if (aReq.iProposal.iComb->sadb_comb_auth == SADB_AALG_MD5HMAC)
+ attr_II->iAuthAlg = DOI_HMAC_MD5;
+ else if (aReq.iProposal.iComb->sadb_comb_auth == SADB_AALG_SHA1HMAC)
+ attr_II->iAuthAlg = DOI_HMAC_SHA;
+ else
+ {
+ DEBUG_LOG(_L("Unsupported Authentication Algorithm in Acquire"));
+ SetFinished();
+ return;
+ }
+ }
+ }
+
+ //Check if PFS in use...
+ if (aReq.iProposal.iComb->sadb_comb_flags & SADB_SAFLAGS_PFS)
+ {
+ iPFS=ETrue;
+ switch (iHostData->iGroupDesc_II)
+ {
+ case IKE_PARSER_MODP_768:
+ attr_II->iGroupDesc = MODP_768;
+ break;
+ case IKE_PARSER_MODP_1024:
+ attr_II->iGroupDesc = MODP_1024;
+ break;
+ case IKE_PARSER_MODP_1536:
+ attr_II->iGroupDesc = MODP_1536;
+ break;
+ case IKE_PARSER_MODP_2048:
+ attr_II->iGroupDesc = MODP_2048;
+ break;
+ default:
+ DEBUG_LOG(_L("ISAKMP Proposals error (Bad Group description)"));
+ SetFinished();
+ return;
+ }
+ }
+ else
+ {
+ iPFS=EFalse;
+ attr_II->iGroupDesc = 0; //No group assigned because no PFS
+ }
+
+ if (aReq.iProposal.iComb->sadb_comb_flags & SADB_SAFLAGS_TUNNEL)
+ {
+ attr_II->iEncMode = DOI_TUNNEL;
+ }
+ else
+ {
+ attr_II->iEncMode = DOI_TRANSPORT;
+ }
+
+ iIDLocalPort = (TUint16)aReq.iSrcAddr.iAddr->Port();
+ iIDRemotePort = (TUint16)aReq.iDstAddr.iAddr->Port();
+ iIDProtocol = aReq.iDstAddr.iExt->sadb_address_proto; //May be needed for phase II ID
+
+ //Source Identity
+ if (aReq.iSrcIdent.iExt)
+ {
+ if (aReq.iSrcIdent.iExt->sadb_ident_type == SADB_IDENTTYPE_PREFIX)
+ {
+ if ( !ProcessIdentityData(aReq.iSrcIdent.iData, &iLocalIDType_II,
+ &iLocalAddr1_ID_II, &iLocalAddr2_ID_II) )
+ {
+ SetFinished();
+ return;
+ }
+ }
+ else //Invalid identity type
+ {
+ DEBUG_LOG1(_L("Invalid Local identity type (%d)"), aReq.iSrcIdent.iExt->sadb_ident_type);
+ SetFinished();
+ return;
+ }
+ }
+ else
+ {
+ //We need to assign a default address if other info is to be sent in the same payload
+ if (attr_II->iEncMode == DOI_TUNNEL || iIDLocalPort != 0 || iIDRemotePort != 0 ||
+ iIDProtocol != 0 || aReq.iDstIdent.iExt)
+ {
+ DEBUG_LOG(_L("Local Identity not defined and needed. Using Own address as local identity."));
+ iLocalAddr1_ID_II = iLocalAddr;
+ if ((iLocalAddr.Family() == KAfInet) || iLocalAddr.IsV4Mapped() )
+ iLocalIDType_II = ID_IPV4_ADDR;
+ else
+ iLocalIDType_II = ID_IPV6_ADDR;
+ iDefaultLocalID = ETrue; //Must be sent but won't be used when updating the SAD
+ }
+ }
+
+ //Destination Identity
+ if (aReq.iDstIdent.iExt)
+ {
+ if (aReq.iDstIdent.iExt->sadb_ident_type == SADB_IDENTTYPE_PREFIX)
+ {
+ if ( !ProcessIdentityData(aReq.iDstIdent.iData, &iRemoteIDType_II,
+ &iRemoteAddr1_ID_II, &iRemoteAddr2_ID_II) )
+ {
+ SetFinished();
+ return;
+ }
+ }
+ else //Invalid identity type
+ {
+
+ DEBUG_LOG1( _L("Invalid Destination identity type (%d)"), aReq.iDstIdent.iExt->sadb_ident_type );
+ SetFinished();
+ return;
+ }
+ }
+ else
+ {
+ //
+ // RemoteID_II is required only if LocalID_II already exists
+ // If transports mode
+ // Build RemoteID_II for Quick mode negotiation from specified remote IP address
+ // else Use subnet 0/0 as remote id
+ //
+ if ( iLocalIDType_II != 0 )
+ {
+ if ( attr_II->iEncMode == DOI_TUNNEL ) {
+ if ( iLocalIDType_II == ID_IPV4_ADDR || iLocalIDType_II == ID_IPV4_ADDR_SUBNET ) {
+ iRemoteAddr1_ID_II.Init(KAfInet);
+ iRemoteAddr2_ID_II.Init(KAfInet);
+ iRemoteIDType_II = ID_IPV4_ADDR_SUBNET;
+ }
+ else {
+ iRemoteAddr1_ID_II.Init(KAfInet6);
+ iRemoteAddr2_ID_II.Init(KAfInet6);
+ iRemoteIDType_II = ID_IPV6_ADDR_SUBNET;
+ }
+ }
+ else {
+ iRemoteAddr1_ID_II = *aReq.iDstAddr.iAddr;
+ if ( iRemoteAddr1_ID_II.Family() == KAfInet6 )
+ iRemoteIDType_II = ID_IPV6_ADDR;
+ else iRemoteIDType_II = ID_IPV4_ADDR;
+ }
+ iDefaultRemoteID = ETrue; //Must be sent but won't be used when updating the SAD
+ }
+ }
+
+ //Only Hard Lifetimes taken into account
+ TInt64 lifetime64 = aReq.iProposal.iComb->sadb_comb_soft_addtime;
+ iHardLifetime = aReq.iProposal.iComb->sadb_comb_hard_addtime;
+
+ if ( lifetime64 == 0 )
+ {
+ lifetime64 = iHardLifetime;
+ }
+
+ TUint high = 0;
+ TUint low = 0;
+ if (lifetime64!=0)
+ {
+ high = ByteOrder::Swap32(I64HIGH(lifetime64));
+ if (high > 0)
+ attr_II->iLifeDurationSecs.Copy((TUint8 *)&high, sizeof(high));
+ low = ByteOrder::Swap32(I64LOW(lifetime64));
+ attr_II->iLifeDurationSecs.Append((TUint8 *)&low, sizeof(low));
+ }
+
+ //Bytes lifetime
+ lifetime64 = aReq.iProposal.iComb->sadb_comb_soft_bytes;
+ lifetime64 = (lifetime64/1024); //Bytes to KB
+ if (lifetime64 != 0)
+ {
+ high = ByteOrder::Swap32(I64HIGH(lifetime64));
+ if (high > 0)
+ attr_II->iLifeDurationKBytes.Copy((TUint8 *)&high, sizeof(high));
+ low = ByteOrder::Swap32(I64LOW(lifetime64));
+ attr_II->iLifeDurationKBytes.Append((TUint8 *)&low, sizeof(low));
+ }
+
+ //Save some pf_key data to use later in PFKEY_UPDATE msg
+ iAcquireSeq = aReq.iBase.iMsg->sadb_msg_seq; //msg Seq. number
+ iPfkeyAcquirePID = aReq.iBase.iMsg->sadb_msg_pid; //msg PID.
+ iAcquirePending = ETrue;
+ DEBUG_LOG2( _L("Acq seq= %d , PID= %d"), iAcquireSeq, iPfkeyAcquirePID );
+
+}
+
+//
+// CIkev1Negotiation::ExecuteL()
+// An ISAKMP message has been received belonging to this negotiation.
+// Process the message and advance the negotiation session to appropriate
+// next state/stage.
+//
+TBool CIkev1Negotiation::ExecuteL( const ThdrISAKMP& aHdr,
+ const TInetAddr& aRemote,
+ TInt aLocalPort )
+{
+ aLocalPort = aLocalPort;
+
+ TBool ret=EFalse;
+ const ThdrISAKMP *hdr;
+ TUint8 *msg = NULL; //to place the new msg
+ TBuf8<IKEV1_MAX_IV_SIZE> tmp_IV(iIV); //Temporal IV. Used to update the real one if the msg OK
+
+ TLastIKEMsg msg_info(aHdr); //For retransmitted IKE msg detection
+ if ( IsRetransmit(msg_info) ) {
+ DEBUG_LOG(_L("Retransmitted IKE message received."));
+ TBool FloatedPort = EFalse;
+ if ( iNAT_D_Flags & (REMOTE_END_NAT + LOCAL_END_NAT) )
+ FloatedPort = ETrue;
+ TPtr8 lastMsg(iLastMsg->Des());
+ iPluginSession->SendIkeMsgL(lastMsg, iLastRemoteAddr, FloatedPort);
+ return EFalse;
+ }
+
+ if ( iPhase == PHASE_II &&
+ aHdr.GetExchange() != IKE_QUICK_MODE )
+ {
+ DEBUG_LOG(_L("Bad packet (retransmission?)"));
+#ifdef _DEBUG
+ const TPtrC8 ikeMsgPtr( (TUint8 *)&aHdr, (TUint16)aHdr.GetLength() );
+ TInetAddr dstAddr;
+ iPluginSession->GetLocalAddress( dstAddr );
+ dstAddr.SetPort( aLocalPort );
+ TRACE_MSG_IKEV1( ikeMsgPtr, iRemoteAddr, dstAddr );
+#endif // _DEBUG
+
+ SetFinished();
+ return EFalse; //Bad packet, is a retransmission
+ }
+
+ iLastRemoteAddr = aRemote; //Save last remote address (used in NAT cases)
+
+ iLengthLeft = aHdr.GetLength(); //Used to check the size in the payload are OK
+
+ DEBUG_LOG2( _L("---------- Phase %d - Stage %d ----------"), iPhase, iStage );
+
+ if ((iStage==1) && (iPhase==PHASE_I)) //Only saved for the first message
+ iExchange = aHdr.GetExchange();
+
+ DEBUG_LOG1( _L("Exchange %d"), aHdr.GetExchange() );
+
+ if (aHdr.GetFlags() & ISAKMP_HDR_EFLAG) //if encrypted
+ {
+ //before anything, prints the packet
+ DEBUG_LOG(_L("Received message (encr)."));
+ DEBUG_LOG(_L("Decrypting..."));
+ msg = new (ELeave)(TUint8[aHdr.GetLength()]); //to place the new msg
+ CleanupStack::PushL(msg);
+
+ Mem::Copy(msg,(TUint8 *)&aHdr,sizeof(aHdr)); //The header is not encrypted
+
+ if ((iPhase==PHASE_II) && (iStage == 1))
+ {
+ iMessageId = aHdr.GetMessageId(); //Saves the ID to compute IV
+ DEBUG_LOG(_L("Quick IV:"));
+ ComputeIVL(iIV, iMessageId);
+ }
+
+ DecryptL((TUint8 *)aHdr.Next(),&msg[sizeof(aHdr)], aHdr.GetLength()-sizeof(aHdr),iIV, iSKEYID_e, iChosenProposal_I.iAttrList->iEncrAlg);
+ if ((iStage == 6 && iExchange == ISAKMP_EXCHANGE_ID) ||
+ (iStage == 3 && iExchange == ISAKMP_EXCHANGE_AGGR))
+ {
+ iLastIV.Copy(iIV); //Saves last IV in Phase 1
+ DEBUG_LOG(_L("Last IV Saved!"));
+ }
+ hdr=(ThdrISAKMP *)msg; //decrypted msg
+
+ }
+ else if (iFlags & ISAKMP_HDR_EFLAG) // IKE message SHOULD be encrypted
+ {
+ hdr=&aHdr; //no encryption
+ DEBUG_LOG(_L("Received message."));
+#ifdef _DEBUG
+ const TPtrC8 ikeMsgPtr( (TUint8 *)hdr, (TUint16)hdr->GetLength() );
+ TInetAddr dstAddr;
+ iPluginSession->GetLocalAddress( dstAddr );
+ dstAddr.SetPort( aLocalPort );
+ TRACE_MSG_IKEV1( ikeMsgPtr, iRemoteAddr, dstAddr );
+#endif // _DEBUG
+ DEBUG_LOG(_L("The message is NOT encrypted (ignored)"));
+ return EFalse;
+ }
+ else
+ hdr=&aHdr; //no encryption
+
+ DEBUG_LOG(_L("Received message."));
+#ifdef _DEBUG
+ const TPtrC8 ikeMsgPtr( (TUint8 *)hdr, (TUint16)hdr->GetLength() );
+ TInetAddr dstAddr;
+ iPluginSession->GetLocalAddress( dstAddr );
+ dstAddr.SetPort( aLocalPort );
+ TRACE_MSG_IKEV1( ikeMsgPtr, iRemoteAddr, dstAddr );
+#endif // _DEBUG
+
+ if (iPhase==PHASE_I)
+ ret = Phase_IExchangeL(*hdr);//MAIN MODE && AGGRESSIVE MODE
+ else
+ {
+ ret = Phase_IIExchangeL(*hdr);//QUICK MODE
+ }
+ if (!ret) //Incorrect packet. Restore the IV
+ {
+ DEBUG_LOG(_L("Restoring previous IV"));
+ iIV.Copy(tmp_IV);
+ }
+ else //correct packet
+ {
+ SaveRetransmitInfo(msg_info); // store new last received IKE message info
+ }
+
+ if ( msg ) //If used erase it (when encryption)
+ CleanupStack::PopAndDestroy();
+
+ return ret;
+}
+
+TBool CIkev1Negotiation::ExecutePhase2L( const ThdrISAKMP &aHdr,
+ const TInetAddr &aRemote,
+ TInt aLocalPort )
+{
+ return ExecuteL( aHdr, aRemote, aLocalPort );
+}
+
+//
+// CIkev1Negotiation::ExecuteTransactionL
+// An ISAKMP Transaction exchange message received.
+// Call CTransNegotiation::ExecuteL method and process returned status
+//
+TBool CIkev1Negotiation::ExecuteTransactionL( const ThdrISAKMP& aHdr,
+ const TInetAddr& aRemote,
+ TInt aLocalPort )
+{
+ TInt status;
+ TBool ret = ETrue;
+ if ( iTransactionNeg ) {
+ status = iTransactionNeg->ExecuteL( aHdr, aRemote, aLocalPort );
+ if ( status == TRANSACTION_SUCCESS ) {
+ //
+ // XAUTH / CONFIG-MODE completed succesfully
+ //
+ IsakmpPhase1CompletedL();
+ }
+ else {
+ if ( status == TRANSACTION_FAILED ) {
+ //
+ // XAUTH / CONFIG-MODE completed succesfully
+ //
+ LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
+ R_VPN_MSG_VPN_GW_AUTH_FAIL,
+ KKmdIkeAuthFailedErr,
+ iPluginSession->VpnIapId(),
+ &iRemoteAddr );
+ SendDeleteL(PROTO_ISAKMP);
+ SetFinished();
+ }
+ }
+ }
+ else ret = EFalse;
+
+ return ret;
+}
+
+
+//
+// CIkev1Negotiation::AuthDialogCompletedL
+// Authentication dialog is completed. Check CAuthDialogInfo object ID
+// and call eithet CIKECRACKNegotiation::ProcessUserResponseL or
+// CTransNegotiation::ProcessUserResponseL to handle dialog data
+//
+void CIkev1Negotiation::AuthDialogCompletedL(CAuthDialogInfo *aUserInfo)
+{
+ if ( !aUserInfo || (!aUserInfo->iUsername && !aUserInfo->iSecret) )
+ {
+ DEBUG_LOG(_L("Legacy authentication cancelled by user!"));
+ SendDeleteL(PROTO_ISAKMP);
+ LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
+ R_VPN_MSG_VPN_GW_AUTH_FAIL,
+ KErrCancel,
+ iPluginSession->VpnIapId(),
+ &iRemoteAddr );
+ SetErrorStatus(KErrCancel);
+ return;
+ }
+
+ TInt status;
+
+ if ( aUserInfo->GetObjId() == XAUTH_DIALOG_ID )
+ {
+ if ( iTransactionNeg )
+ status = iTransactionNeg->ProcessUserResponseL(aUserInfo);
+ else status = TRANSACTION_FAILED;
+
+ if ( status == TRANSACTION_FAILED )
+ {
+ /*--------------------------------------------------------
+ *
+ * XAUTH negotiation failed. Negotiation shall be deleted
+ *
+ *--------------------------------------------------------*/
+ LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
+ R_VPN_MSG_VPN_GW_AUTH_FAIL,
+ status,
+ iPluginSession->VpnIapId(),
+ &iRemoteAddr );
+ SetErrorStatus(KKmdIkeAuthFailedErr);
+ AcquireSAErrorResponse(KKmdIkeAuthFailedErr);
+ }
+ }
+ else
+ {
+ if ( iCRACKneg )
+ status = iCRACKneg->ProcessUserResponseL(aUserInfo);
+ else status = CRACK_FAILED;
+
+ if ( status == CRACK_FAILED )
+ {
+ /*--------------------------------------------------------
+ *
+ * Crack negotiation failed. Negotiation shall be deleted
+ *
+ *--------------------------------------------------------*/
+ LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
+ R_VPN_MSG_VPN_GW_AUTH_FAIL,
+ status,
+ iPluginSession->VpnIapId(),
+ &iRemoteAddr );
+ SetErrorStatus(KKmdIkeAuthFailedErr);
+ AcquireSAErrorResponse(KKmdIkeAuthFailedErr);
+ }
+ }
+}
+
+
+//
+// CIkev1Negotiation::StartCRACKAuthL
+// Start CRACK authentication phase of IKE phase I negotiation
+// - Create CIKECRACKNegotiation object and call it`s ConstructL
+// - Set iStage variable to 7 to indicate that CRACK authentication
+// is going
+//
+TBool CIkev1Negotiation::StartCRACKAuthL()
+{
+TInt status;
+
+ iStage = 7;
+
+ if ( !iCRACKneg ) {
+ iCRACKneg = new(ELeave) CIKECRACKNegotiation( iDebug );
+ TBuf<2> DummyDomain;
+ //
+ // If the IKE Id-type value is configured to value "Opaque
+ // String" and iFQDN length is larger than zero in the current
+ // policy ==> iFQDN value contains "Group name" value
+ // which shall be conveyed to the peer in CRACK "Domain name"
+ // attribute
+ //
+ iFlags |= ISAKMP_HDR_EFLAG; //From now on encryption is used
+
+ if ( (iHostData->iIdType == ID_KEY_ID) && (iHostData->iFQDN.Length() > 0 ) )
+ status = iCRACKneg->ConstructL(iHostData->iCRACKLAMType, this, iHostData->iFQDN);
+ else status = iCRACKneg->ConstructL(iHostData->iCRACKLAMType, this, DummyDomain);
+ if ( status == CRACK_FAILED ) {
+ return EFalse;
+ }
+ }
+
+ return ETrue;
+}
+
+//
+// CIkev1Negotiation::IsakmpPhase1CompletedL
+// This method is called when an ISAKMP phase 1 negotiation is succesfully
+// completed. The following actions are taken:
+// -- If either Extended authentication or/and Config mode required
+// If iTransactionNeg data member exists it means that XAUTH/CONFIG mode
+// has been succesfully completed.
+// If iTransactionNeg data member is NULL, XAUTH/CONFIG mode shall be initiated
+// -- If No XAUTH/CONFIG mode (or if XAUTH/CONFIG mode already completed).
+// If iAutoLogin is TRUE save ISAKMP SA and deconstruct negotiation.
+// If iAutoLogin is False, save ISAKMP SA and;
+// if negotiation role is initiator continue with Quick mode exchange.
+// if negotiation role is responder deconstruct negotiation.
+//
+TBool CIkev1Negotiation::IsakmpPhase1CompletedL()
+{
+ TBool Status;
+
+ if ( iTransactionNeg ) {
+ //
+ // Try to get Internal address information and delete iTransactionNeg
+ //
+ delete iInternalAddr;
+ iInternalAddr = iTransactionNeg->GetInternalAddr();
+ delete iTransactionNeg;
+ iTransactionNeg = NULL;
+ }
+ else {
+ if ( (iRole == INITIATOR) && (iHostData->iUseXauth || iHostData->iUseCfgMode) )
+ {
+ TBool useModeCfg;
+ if ( iSARekeyInfo )
+ useModeCfg = EFalse; // Use existing virtual Ip, if any
+ else
+ useModeCfg = iHostData->iUseCfgMode;
+
+ iTransactionNeg = CTransNegotiation::NewL( iHostData->iUseXauth,
+ useModeCfg,
+ iPluginSession,
+ this,
+ iDebug );
+
+ // If only MODE_CFG is needed a request is sent
+ if(useModeCfg && !iHostData->iUseXauth)
+ iTransactionNeg->BuildConfigRequestL();
+
+ iStage = 8;
+ return ETrue;
+ }
+ }
+
+ SaveISAKMPSAL();
+
+ if ( iAutoLogin ) {
+ //
+ // ISAKMP Phase 1 completed. Quick mode is NOT started because there is no acquire pending
+ // but phase 1 negotiation is started by the policy activation.
+ //
+ CInternalAddress* internalAddr = NULL;
+ if ( iInternalAddr != NULL )
+ {
+ internalAddr = CInternalAddress::NewL(*iInternalAddr);
+ }
+ iPluginSession->IkeSaCompleted(KErrNone, internalAddr);
+ SetFinished(); // Causes negotiation object destruction
+ iAutoLogin = EFalse;
+ Status = EFalse;
+ }
+ else {
+ if ( iRole == INITIATOR ) {
+ TBool internalAddressChanged = EFalse;
+ if ( iInternalAddr ) {
+ //
+ // Report internal IP address changed event
+ //
+ internalAddressChanged = iPluginSession->InternalAddressChangedL(*iInternalAddr);
+ }
+ if ( (!iSARekeyInfo ||
+ iPhaseIIAfterIkeSaRekey) &&
+ !internalAddressChanged )
+ {
+ iPhaseIIAfterIkeSaRekey = EFalse;
+ iPrevExchange = iExchange; //Needed to know how to begin Phase II (Sending or receiving)
+ iExchange = IKE_QUICK_MODE;
+ iPhase = PHASE_II;
+ iStage = 1;
+ Status = ETrue;
+ iMessageId = RandomMessageId();
+ InitPhase2L(); //Immediately inits PHASE_II. No reply expected.
+ }
+ else {
+ //
+ // Rekeyed IKE SA or internal address changed. No IKE quick mode started
+ // Pending acquire will be failed, if internal address has changed.
+ //
+ SetFinished();
+ Status = EFalse;
+ }
+ }
+ else {
+ SetFinished(); // Causes negotiation object destruction
+ Status = EFalse;
+ }
+ }
+
+ LOG_KMD_EVENT( MKmdEventLoggerIf::KLogInfo,
+ R_VPN_MSG_VPN_GW_AUTH_OK,
+ KErrNone,
+ iPluginSession->VpnIapId(),
+ &iRemoteAddr );
+
+ TInt NatStatus = iNAT_D_Flags; // Record "IETF" NAT status
+ if ( NatStatus == 0 )
+ {
+ if ( iNAT_T_Required )
+ NatStatus = 4; // Local end is NAT:ted (discovered via "Nokia" NAT-T)
+ else if ( !iHostData->iUseNatProbing )
+ NatStatus = iHostData->iEspUdpPort; // "Forced" ESP UDP encapsulation ?
+ }
+
+ TInetAddr localAddr;
+ iPluginSession->GetLocalAddress( localAddr );
+ LOG_KMD_EVENT2( MKmdEventLoggerIf::KLogInfo,
+ R_VPN_MSG_ADDR_INFO_FOR_VPN_AP,
+ NatStatus, iPluginSession->VpnIapId(),
+ (iInternalAddr ? &iInternalAddr->iClientIntAddr : NULL),
+ &localAddr );
+
+ return Status;
+}
+
+
+//Sends the initial IKE packets to start the negotiation. PHASE I
+void CIkev1Negotiation::InitNegotiationL() //Equiv. to stage 1
+{
+ TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
+
+ TInt vendor_id_type;
+
+ //Main mode stage 1
+
+ switch (iHostData->iMode)
+ {
+ case IKE_PARSER_MAIN:
+ iExchange = ISAKMP_EXCHANGE_ID; // Identity Protection (Main mode in IKE)
+ DEBUG_LOG(_L("IKE: Initiating negotiation (Main Mode)"));
+ break;
+ case IKE_PARSER_AGGRESSIVE:
+ iExchange = ISAKMP_EXCHANGE_AGGR; // Agressive
+ DEBUG_LOG(_L("IKE: Initiating negotiation (Aggressive Mode)"));
+ break;
+ default:
+ DEBUG_LOG1(_L("Bad Mode used (%d)"), iHostData->iMode);
+ return;
+ }
+ iStage = 1;
+ DEBUG_LOG2(_L("---------- Phase %d - Stage %d ----------"),iPhase, iStage);
+
+ iCookie_R.FillZ(ISAKMP_COOKIE_SIZE); //Set responder Cookie to 0
+
+ iDOI = IPSEC_DOI;
+ iEncoding = X509_CERT_SIG; //Only cert Allowed
+ msg->IsakmpInit(this);
+ msg->IsakmpSa();
+
+ const TUint8 *ptr = msg->iBuf.Ptr() + sizeof(ThdrISAKMP);
+ const TSAISAKMP *sa = (TSAISAKMP*)ptr;
+ //Generic payload NOT included
+ iSAPayloadSize = sa->GetLength() - sizeof(TPayloadISAKMP);
+ delete iSAPayload;
+ iSAPayload = NULL;
+ iSAPayload = new (ELeave) TUint8[iSAPayloadSize];
+ ptr += sizeof(TPayloadISAKMP);
+ Mem::Copy(iSAPayload, ptr,iSAPayloadSize);
+
+ TBool cert_required = EFalse; //If any proposal requires a cert to send a CR if needed
+ TBool preshared_key = EFalse; //Preshared key authentication
+ TBool crack_used = EFalse;
+
+ TAttrib *transf = iProposal_I.iAttrList;
+ for (TInt i=0; (i < iProposal_I.iNumTransforms) && (!cert_required); i++)
+ {
+ switch (transf->iAuthMethod)
+ {
+ case RSA_SIG: //Proposals involving certificates
+ case DSS_SIG:
+ cert_required = ETrue;
+ break;
+ case IKE_A_CRACK:
+ cert_required = ETrue;
+ crack_used = ETrue;
+ break;
+ default: // No cert involved
+ preshared_key = ETrue;
+ transf = transf->iNext;
+ }
+ }
+
+ if (crack_used &&
+ !iHostData->iCRACKLAMUserName &&
+ !iHostData->iCRACKLAMPassword)
+ {
+ TBuf<256> UserName;
+ TBuf<64> Password;
+ CIkev1Dialog* Dialog = CIkev1Dialog::NewL(iPluginSession, iPluginSession->DialogAnchor(), iDebug);
+ if (KErrNone != Dialog->GetSyncUNPWCacheDialog(UserName, Password))
+ {
+ DEBUG_LOG(_L("Failed to get credentials for crack auth!"));
+ SetFinished();
+ delete Dialog;
+ return;
+ }
+ iHostData->iCRACKLAMUserName = TStringData::NewL(UserName);
+ iHostData->iCRACKLAMPassword = TStringData::NewL(Password);
+ delete Dialog;
+ }
+
+ if (iExchange == ISAKMP_EXCHANGE_AGGR) //Aggressive contains more payloads
+ {
+ if ( preshared_key && !cert_required ) {
+ //
+ // Only pre-shared key authentication proposal(s) exists
+ // Check if is necessary to ask user name/password (= IKE ID/preshared key)
+ // from user
+ //
+ if ( CheckCredentials(iHostData) != KErrNone ) {
+ DEBUG_LOG(_L("Failed to get credentials for Aggressive pre-shared auth!"));
+ SetFinished();
+ return;
+ }
+ }
+ ComputeNonceL(); //Nonce to be sent
+ if ( cert_required && !iOwnCert )
+ ReadOwnCertL(); // For possible DER ASN1 distuingish name Ident
+ msg->IsakmpKeyL();
+ msg->IsakmpNonce();
+ msg->IsakmpOwnIdentL();
+
+ //For aggressive mode we send a CR if a cert is going to be needed
+ if ((!iPeerX509Cert) && (cert_required))
+ {
+ msg->IsakmpCertificateReqL();
+ }
+ if ( iHostData->iUseNatProbing )
+ vendor_id_type = EXPANDED_VENDOR_ID;
+ else vendor_id_type = HASH_VENDOR_ID;
+ msg->IsakmpVendorId(vendor_id_type,
+ (TUint8*)iCookie_I.Ptr(),
+ (TUint8*)iCookie_R.Ptr(), iLocalAddr);
+
+ }
+
+ if ( iHostData->iDPDHeartBeat != 0 )
+ BuildDPDVendorId(*msg);
+
+ if ( iNatDiscovery ) {
+ iNatDiscovery->BuildNatVendorId(*msg);
+ iNatDiscovery->BuildRfcNatVendorId(*msg);
+ }
+
+ if ( iHostData->iUseXauth || iHostData->iUseCfgMode ) {
+ CTransNegotiation::BuildXauthVendorId(*msg);
+ }
+
+ SendL(*msg);
+ iStage = 2;
+}
+
+//Builds the proposal list from the structure in the engine
+TBool CIkev1Negotiation::BuildProposals1L()
+{
+ TProposalData *p_list = iHostData->iPropList;
+
+ if (!p_list)
+ {
+ DEBUG_LOG(_L("ERROR: No proposals in the configuration file. Negotiation Cancelled"));
+ return EFalse;
+ }
+ iProposal_I.iSPI.FillZ(4); //filled with 0 (not send anyway)
+ iProposal_I.iProposalNum = FIRST_ISAKMP_PROPOSAL;
+ iProposal_I.iProtocol = PROTO_ISAKMP;
+ iProposal_I.iNumTransforms = 0;
+
+ TUint8 trans_num = FIRST_ISAKMP_TRANSFORM;
+
+ TAttrib *prev=NULL,*attrlist=NULL;
+
+ while ( p_list )
+ {
+ iProposal_I.iNumTransforms ++;
+ attrlist = new (ELeave) TAttrib;
+ if (!iProposal_I.iAttrList) //First transform
+ iProposal_I.iAttrList=attrlist; //attrlist safe
+ if (prev)
+ prev->iNext = attrlist;
+ prev = attrlist;
+
+ attrlist->iTransformID = KEY_IKE; //only one allowed by Protocol ISAKMP (KEY_IKE)
+ attrlist->iTransformNum = trans_num;
+ trans_num++;
+ switch (p_list->iEncrAlg)
+ {
+ case IKE_PARSER_DES_CBC:
+ attrlist->iEncrAlg=DES_CBC; //DES_CBC
+ break;
+ case IKE_PARSER_DES3_CBC:
+ attrlist->iEncrAlg=DES3_CBC; //DES3_CBC
+ break;
+ case IKE_PARSER_AES_CBC:
+ attrlist->iEncrAlg = AES_CBC; //AES_CBC
+ attrlist->iKeyLength = (TUint16)p_list->iEncrKeyLth;
+ if (!attrlist->iKeyLength)
+ attrlist->iKeyLength = 128; //default AES key size
+ break;
+ default:
+ DEBUG_LOG(_L("ISAKMP Proposals error (Bad Encryption algorithm)"));
+ return EFalse;
+ }
+ switch (p_list->iHashAlg)
+ {
+ case IKE_PARSER_MD5:
+ attrlist->iHashAlg=HASH_MD5; //HASH_MD5
+ break;
+ case IKE_PARSER_SHA1:
+ attrlist->iHashAlg=HASH_SHA1; //HASH_SHA1
+ break;
+ default:
+ DEBUG_LOG(_L("ISAKMP Proposals error (Bad Hash algorithm)"));
+ return EFalse;
+ }
+ switch (p_list->iAuthMeth)
+ {
+ case IKE_PARSER_RSA_SIG:
+ attrlist->iAuthMethod=RSA_SIG;
+ break;
+ case IKE_PARSER_DSS_SIG:
+ attrlist->iAuthMethod=DSS_SIG;
+ break;
+ case IKE_PARSER_PRE_SHARED:
+ attrlist->iAuthMethod=PRE_SHARED;
+ break;
+ case IKE_PARSER_CRACK:
+ attrlist->iAuthMethod=IKE_A_CRACK; //CRACK authentication
+ break;
+
+ default:
+ DEBUG_LOG(_L("ISAKMP Proposals error (Bad Authentication Method)"));
+ return EFalse;
+ }
+
+ switch (p_list->iGroupDesc)
+ {
+ case IKE_PARSER_MODP_768:
+ attrlist->iGroupDesc = MODP_768;
+ break;
+ case IKE_PARSER_MODP_1024:
+ attrlist->iGroupDesc = MODP_1024;
+ break;
+ case IKE_PARSER_MODP_1536:
+ attrlist->iGroupDesc = MODP_1536;
+ break;
+ case IKE_PARSER_MODP_2048:
+ attrlist->iGroupDesc = MODP_2048;
+ break;
+ default:
+ DEBUG_LOG(_L("ISAKMP Proposals error (Bad Group description)"));
+ return EFalse;
+ }
+
+ switch (p_list->iGroupType)
+ {
+ case IKE_PARSER_MODP:
+ attrlist->iGroupType = MODP;
+ break;
+ case IKE_PARSER_DEFAULT:
+ break;
+ default:
+ DEBUG_LOG(_L("ISAKMP Proposals error (Bad Group Type)"));
+ return EFalse;
+ }
+
+ switch (p_list->iPRF)
+ {
+ case IKE_PARSER_DES3_CBC_MAC:
+ attrlist->iPRF=OAKLEY_PRF_3DES_CBC_MAC;
+ break;
+ case IKE_PARSER_NONE:
+ break;
+ default:
+ DEBUG_LOG(_L("ISAKMP Proposals error (Bad PRF specified)"));
+ return EFalse;
+ }
+
+ TUint32 lifetime = ByteOrder::Swap32(p_list->iLifetimeSec);
+ if (lifetime)
+ attrlist->iLifeDurationSecs.Copy((TUint8 *)&lifetime, sizeof(lifetime));
+
+ lifetime = ByteOrder::Swap32(p_list->iLifetimeKb);
+ if (lifetime)
+ attrlist->iLifeDurationKBytes.Copy((TUint8 *)&lifetime, sizeof(lifetime));
+
+ //
+ // Store parameters for extended authentication
+ //
+ attrlist->iXauthUsed = iHostData->iUseXauth;
+ attrlist->iRole = iRole;
+
+ p_list = p_list->iNext;
+
+ }
+ attrlist->iNext=NULL; //Last transform
+
+
+ return ETrue;
+}
+
+//Builds Phase_II proposals from the config. file to when acting as RESPONDER to see if
+//proposals received are acceptable.
+TInt CIkev1Negotiation::BuildProposals2L()
+{
+ CIpsecSaSpecList* SaList = NULL;
+ TInetAddr empty_addr; //empty address
+ TInt err = KErrNone;
+
+ TRAP(err,
+ if ( iLocalIDType_II == 0 ) //Local ID not received (so remote neither) so it's the same as gateway
+ {
+ TInetAddr localSelector(iLocalAddr);
+ localSelector.SetPort(iIDLocalPort);
+ TInetAddr remoteSelector(iRemoteAddr);
+ remoteSelector.SetPort(iIDRemotePort);
+
+ SaList = iPluginSession->GetIpseSaSpecListLC(localSelector, empty_addr,
+ remoteSelector, empty_addr,
+ iIDProtocol );
+ }
+ else // either none or both (RFC 2409 5.5 so we must have both)
+ {
+ TInetAddr localSelector(iLocalAddr1_ID_II);
+ localSelector.SetPort(iIDLocalPort);
+ TInetAddr remoteSelector(iRemoteAddr1_ID_II);
+ remoteSelector.SetPort(iIDRemotePort);
+
+
+ SaList = iPluginSession->GetIpseSaSpecListLC(localSelector,
+ iLocalAddr2_ID_II,
+ remoteSelector,
+ iRemoteAddr2_ID_II,
+ iIDProtocol );
+ }
+
+ CleanupStack::Pop(SaList);
+ );
+
+ if (err != KErrNone)
+ return err;
+
+ CleanupStack::PushL(SaList);
+
+ //Phase II proposals
+
+ CProposal_II *prop = NULL;
+ TAttrib_II *attr_II = NULL;
+ const TIpsecSaSpec *spec = NULL;
+ TInt count = SaList->Count();
+ TInt i = 0;
+
+ while ( i < count )
+ {
+ prop = new (ELeave) CProposal_II();
+ CleanupStack::PushL(prop);
+ prop->ConstructL(1);
+ iProposal_IIList->AppendL(prop);
+ CleanupStack::Pop(); //prop safe
+ //
+ // Only 1 proposal which may be AND'd (many prop with same num) if many bundles
+ // Only 1 transform because no OR'ing implemented in IPSEC
+ //
+ prop->iProposalNum = FIRST_IPSEC_PROPOSAL;
+ prop->iNumTransforms = 1;
+
+ attr_II = new (ELeave) TAttrib_II();
+ CleanupStack::PushL(attr_II);
+ prop->iAttrList->AppendL(attr_II);
+ CleanupStack::Pop(); //attr_II safe
+
+ attr_II->iTransformNum = FIRST_IPSEC_TRANSFORM;
+
+ spec = &(SaList->At(i));
+
+ if (spec->iType == SADB_SATYPE_AH)
+ {
+ prop->iProtocol=PROTO_IPSEC_AH;
+ if (spec->iAalg == SADB_AALG_MD5HMAC)
+ {
+ attr_II->iTransformID = AH_MD5;
+ attr_II->iAuthAlg = DOI_HMAC_MD5;
+ }
+ else if (spec->iAalg == SADB_AALG_SHA1HMAC)
+ {
+ attr_II->iTransformID=AH_SHA;
+ attr_II->iAuthAlg = DOI_HMAC_SHA;
+ }
+ else
+ {
+ DEBUG_LOG(_L("Unsupported Authentication Algorithm in IPsec Policy"));
+ err = KKmdIkeNoProposalErr;
+ break;
+ }
+ // No auth with variable encryption
+ }
+ else if (spec->iType == SADB_SATYPE_ESP)
+ {
+ prop->iProtocol = PROTO_IPSEC_ESP;
+ // Request ESP from peer host
+
+ attr_II->iTransformID = spec->iEalg;
+ switch ( attr_II->iTransformID )
+ {
+ case ESP_DES_CBC:
+ case ESP_3DES_CBC:
+ case ESP_NULL:
+ break;
+
+ case ESP_AES_CBC:
+ attr_II->iKeyLength = spec->iEalgLen; //If 0 it won't be sent
+ break;
+
+ default:
+ DEBUG_LOG(_L("IPsec Encryption algorithm is not implemented. Wrong algorithms file"));
+ err = KKmdIkeNoProposalErr;
+ break;
+ }
+
+ if (err != KErrNone)
+ {
+ break;
+ }
+
+ if (spec->iAalg != SADB_AALG_NONE)
+ {
+ if (spec->iAalg == SADB_AALG_MD5HMAC)
+ attr_II->iAuthAlg = DOI_HMAC_MD5;
+ else if (spec->iAalg == SADB_AALG_SHA1HMAC)
+ attr_II->iAuthAlg = DOI_HMAC_SHA;
+ else
+ {
+ DEBUG_LOG(_L("Unsupported Authentication Algorithm in IPsec Policy"));
+ err = KKmdIkeNoProposalErr;
+ break;
+ }
+ }
+ }
+
+ //Check if PFS in use...
+ iPFS = spec->iPfs;
+
+ if (spec->iTransportMode)
+ attr_II->iEncMode = DOI_TRANSPORT;
+ else attr_II->iEncMode = DOI_TUNNEL;
+
+ //////////////////////////////////////////////////////////
+ //
+ // Check if remote identity defined in Security Policy
+ // If it is not set iDefaultLocalID = ETrue.
+ // This prevents destination identity information to IPSEC
+ // in PFKEY Update- and Add primitives
+ // (see UpdateSADatabaseL() method)
+ //
+ //////////////////////////////////////////////////////////
+ if ( spec->iRemoteIdentity.Length() == 0 )
+ iDefaultLocalID = ETrue;
+ if ( !ExamineRemoteIdentity(spec->iRemoteIdentity) )
+ {
+ DEBUG_LOG(_L("Remote Identity mismatch with IPsec Policy"));
+ err = KKmdIkeNoProposalErr;
+ break;
+ }
+
+ //Only Hard Lifetimes taken into account
+ TInt64 lifetime64 = spec->iHard.iAddTime;
+ TUint high = 0;
+ TUint low = 0;
+ if (lifetime64!=0)
+ {
+ high = ByteOrder::Swap32(I64HIGH(lifetime64));
+ if (high > 0)
+ attr_II->iLifeDurationSecs.Copy((TUint8 *)&high, sizeof(high));
+ low = ByteOrder::Swap32(I64LOW(lifetime64));
+ attr_II->iLifeDurationSecs.Append((TUint8 *)&low, sizeof(low));
+ }
+
+ //Bytes lifetime
+ lifetime64 = spec->iHard.iBytes;
+ lifetime64 = (lifetime64/1024); //Bytes to KB
+ if (lifetime64 != 0)
+ {
+ high = ByteOrder::Swap32(I64HIGH(lifetime64));
+ if (high > 0)
+ attr_II->iLifeDurationKBytes.Copy((TUint8 *)&high, sizeof(high));
+ low = ByteOrder::Swap32(I64LOW(lifetime64));
+ attr_II->iLifeDurationKBytes.Append((TUint8 *)&low, sizeof(low));
+ }
+ if (iPFS)
+ {
+ switch (iHostData->iGroupDesc_II)
+ {
+ case IKE_PARSER_MODP_768:
+ attr_II->iGroupDesc = MODP_768;
+ break;
+ case IKE_PARSER_MODP_1024:
+ attr_II->iGroupDesc = MODP_1024;
+ break;
+ case IKE_PARSER_MODP_1536:
+ attr_II->iGroupDesc = MODP_1536;
+ break;
+ case IKE_PARSER_MODP_2048:
+ attr_II->iGroupDesc = MODP_2048;
+ break;
+ default: //Shouldn't happen but the error will be detected later
+ err = KKmdIkeNoProposalErr;
+ break;
+ }
+
+ if (err != KErrNone)
+ {
+ break;
+ }
+ }
+
+ prop->iReplayWindowLength = spec->iReplayWindowLength;
+
+ i ++;
+ } //while
+
+ if (err != KErrNone)
+ {
+ delete iProposal_IIList;
+ iProposal_IIList = NULL;
+ }
+
+ CleanupStack::PopAndDestroy(SaList); //SAList
+
+ return err;
+}
+
+TBool CIkev1Negotiation::ExamineRemoteIdentity(const TDesC8& aRemoteIdInPolicy)
+{
+ //////////////////////////////////////////////////////////////////
+ //
+ // This method is called when we are acting as a Quick mode responder.
+ // The purpose of this method is compare remote Identity information
+ // received in Quick Mode IKE message (=IDi) to the remote Identity
+ // information in the local Ipsec Policy.
+ // If Identitiea are NOT matching the FALSE response is returned.
+ // There is the following special "kludge" is done in these test:
+ // Identity IPv4 address and IPv4 subnet with prefix 32 are
+ // interpreted to be same as well as IPv6 address and IPv6 subnet
+ // with prefix 128. When this situation occurs a special flag
+ // (iSwapRemoteIdType = ETrue) is set in CIkev1Negotiation object
+ //
+ //////////////////////////////////////////////////////////////////
+ TBool AddrMatch;
+ TInetAddr IpAddr;
+ TInetAddr IpMask;
+ TUint8 IdType;
+ TInt PrefixLength = 0;
+ TInetAddr* PrefixPtr = NULL;
+
+ if ( !ProcessIdentityData(aRemoteIdInPolicy, &IdType, &IpAddr, &IpMask) )
+ return EFalse;
+
+ AddrMatch = IpAddr.Match(iRemoteAddr1_ID_II);
+
+ if ( AddrMatch )
+ {
+ if ( (IdType == ID_IPV4_ADDR_SUBNET ) || (IdType == ID_IPV6_ADDR_SUBNET) )
+ {
+ //
+ // Identity in policy is IP subnet
+ //
+ if ( iRemoteIDType_II == IdType )
+ {
+ //
+ // Both identity in policy and identity in IKE IDi payload are
+ // IP subnets. The subnet masks MUST match, too
+ //
+ AddrMatch = IpMask.Match(iRemoteAddr2_ID_II);
+ }
+ else
+ {
+ if ( ( (IdType == ID_IPV4_ADDR_SUBNET ) && (iRemoteIDType_II == ID_IPV4_ADDR)) ||
+ ( (IdType == ID_IPV6_ADDR_SUBNET ) && (iRemoteIDType_II == ID_IPV6_ADDR)))
+ {
+ //
+ // Identity in IKE IDi payload is an IP address
+ // Do the special check: There must be full mask in the
+ // subnet identity configured in policy
+ //
+ if ( IdType == ID_IPV6_ADDR_SUBNET )
+ PrefixLength = 128;
+ else PrefixLength = 32;
+ PrefixPtr = &IpMask;
+ DEBUG_LOG(_L("Peer is using IP address IDi, full mask subnet required in local end!"));
+ }
+ else AddrMatch = EFalse;
+ }
+ }
+ else
+ {
+ //
+ // Identity in policy is IP address.
+ //
+ if ( ( (IdType == ID_IPV4_ADDR ) && (iRemoteIDType_II == ID_IPV4_ADDR_SUBNET)) ||
+ ( (IdType == ID_IPV6_ADDR ) && (iRemoteIDType_II == ID_IPV6_ADDR_SUBNET)))
+ {
+ //
+ // Identity in IKE IDi payload is an IP subnet
+ // Do the special check: There must be full mask in the
+ // subnet identity in IKE IDi payload
+ //
+ if ( IdType == ID_IPV6_ADDR )
+ PrefixLength = 128;
+ else PrefixLength = 32;
+ PrefixPtr = &iRemoteAddr2_ID_II;
+ DEBUG_LOG(_L("An IP address ID used in local end, full mask subnet required in peer IDi!"));
+ }
+ else if ( iRemoteIDType_II != IdType )
+ AddrMatch = EFalse;
+
+ }
+ }
+
+ if ( PrefixPtr )
+ {
+ if ( PrefixLength == PrefixLen(*PrefixPtr) )
+ {
+ iSwapRemoteIdType = ETrue;
+ DEBUG_LOG(_L("Required ID OK, modified remote IDi informed to IPsec!"));
+ }
+ else AddrMatch = EFalse;
+ }
+
+ return AddrMatch;
+}
+
+TBool CIkev1Negotiation::ProcessIdentityData(const TDesC8& aIdentity, TUint8* aToIdType,
+ TInetAddr* aToIpAddr1, TInetAddr* aToIpAddr2)
+{
+ if ( !aToIdType || !aToIpAddr1 || !aToIpAddr2 )
+ return EFalse;
+
+ aToIpAddr1->Init(KAFUnspec);
+ aToIpAddr2->Init(KAFUnspec);
+ *aToIdType = 0;
+
+ if ( aIdentity.Length() )
+ {
+ TInt offset = aIdentity.Find(_L8("/"));
+
+ switch (offset)
+ {
+ case KErrNotFound: //Simple address
+ {
+#ifdef _UNICODE
+ HBufC *unibuf = HBufC::New(aIdentity.Length());
+ if ( !unibuf )
+ return EFalse;
+ unibuf->Des().Copy(aIdentity);
+ if ( aToIpAddr1->Input(unibuf->Des()) != KErrNone )
+ {
+ delete unibuf;
+ DEBUG_LOG(_L("Bad IP address identity!"));
+ return EFalse;
+ }
+ delete unibuf;
+#else
+ if (aToIpAddr1->Input(aIdentity) != KErrNone)
+ {
+ DEBUG_LOG(_L("Bad IP address identity!"));
+ return EFalse;
+ }
+#endif
+ if ( aToIpAddr1->Family() == KAfInet )
+ *aToIdType = ID_IPV4_ADDR;
+ else *aToIdType = ID_IPV6_ADDR;
+ break;
+ }
+
+ default: //Subnet
+ {
+ //addr1 - subnet
+ TInt prefix_len;
+#ifdef _UNICODE
+ HBufC *unibuf = HBufC::New(aIdentity.Length());
+ if ( !unibuf )
+ return EFalse;
+ unibuf->Des().Copy(aIdentity);
+ TPtrC addr_buf(unibuf->Ptr(), offset);
+ if (aToIpAddr1->Input(addr_buf) != KErrNone)
+ {
+ delete unibuf;
+ DEBUG_LOG(_L("Bad Subnet Identity address!"));
+ return EFalse;
+ }
+ TPtrC prefix_ptr(unibuf->Ptr() + offset + 1, unibuf->Length() - offset - 1);
+#else
+ TPtrC addr_buf(aIdentity.Ptr(), offset);
+ if (aToIpAddr1->.Input(addr_buf) != KErrNone)
+ {
+ DEB(LogError(_L("Bad Subnet Identity address!"));)
+ return EFalse;
+ }
+ TPtrC prefix_ptr(aIdentity.Ptr() + offset + 1, aIdentity.Length() - offset - 1);
+#endif
+ //addr2 - mask
+ TLex lex(prefix_ptr);
+ if (lex.Val(prefix_len) != KErrNone)
+ {
+ DEBUG_LOG(_L("Bad Subnet Identity PREFIX Length!"));
+ return EFalse;
+ }
+#ifdef _UNICODE
+ delete unibuf;
+#endif
+ if ( aToIpAddr1->Family() == KAfInet )
+ {
+ if ( prefix_len > 32 )
+ {
+ DEBUG_LOG(_L("Bad Subnet Identity: Prefix too long!"));
+ return EFalse;
+ }
+ *aToIdType = ID_IPV4_ADDR_SUBNET;
+ PrefixMask(*aToIpAddr2, prefix_len, KAfInet);
+ }
+ else //KAfInet6
+ {
+ if ( prefix_len > 128 )
+ {
+ DEBUG_LOG(_L("Bad Subnet Identity: Prefix too long!"));
+ return EFalse;
+ }
+ *aToIdType = ID_IPV6_ADDR_SUBNET;
+ PrefixMask(*aToIpAddr2, prefix_len, KAfInet);
+ }
+
+ }
+
+ } //end switch
+ }
+
+ return ETrue;
+}
+
+//First msg of PHASE_II as initiator
+void CIkev1Negotiation::InitPhase2L()
+{
+ //Quick mode stage 1
+ DEBUG_LOG(_L("-------- IKE: Initiating PHASE II --------"));
+ iPhase = PHASE_II;
+ iDOI=IPSEC_DOI;
+ iStage = 1;
+
+ GetSPIL();
+ //the rest will be done in ReceiveSPI
+}
+
+//Requests an SPI from the kernel
+
+void CIkev1Negotiation::GetSPIL()
+{
+ CProposal_IIList *propII_List;
+ CProposal_II *prop=NULL;
+ TUint8 sa_type = 0;
+
+ DEBUG_LOG1(_L("GetSPI in stage: %d"), iStage);
+
+ iInboundSPIList = new (ELeave) CArrayFixFlat<TSPINode>(1);
+
+ if (iRole == RESPONDER) //If Phase II proposal Chosen
+ propII_List = iChosenProp_IIList; //If RESPONDER
+ else
+ propII_List = iProposal_IIList; //If INITIATOR
+
+ TInt i, count = propII_List->Count();
+ for (i = 0; i < count; i++) //May have many Phase_II proposals
+ {
+ prop = propII_List->At(i);
+ if (prop->iProtocol == PROTO_IPSEC_AH)
+ sa_type = SADB_SATYPE_AH;
+ else if (prop->iProtocol == PROTO_IPSEC_ESP)
+ sa_type = SADB_SATYPE_ESP;
+ else
+ sa_type = 0; //Unknown Protocol
+
+ TInetAddr myAddr(iLocalAddr);
+ if ( myAddr.IsUnspecified() )
+ User::LeaveIfError( iPluginSession->GetLocalAddress( myAddr ) );
+ TInetAddr peerAddr(iRemoteAddr);
+ peerAddr.SetPort(0);
+ TSPINode node;
+ node.iPropNum = prop->iProposalNum;
+ node.iSeq = iSeq;
+ iInboundSPIList->AppendL(node);
+
+
+ __ASSERT_DEBUG( iIpsecSaSpiRetriever != NULL,
+ User::Invariant() );
+ iIpsecSaSpiRetriever->Cancel();
+ iIpsecSaSpiRetriever->GetIpsecSaSpi( iSeq++, sa_type, peerAddr, myAddr );
+ iPendingSPI++; //To know when all received in ReceiveSPIL()
+ }
+
+ DEBUG_LOG1(_L("GetSPI seq= %d"), iSeq);
+}
+
+//aSPI received in Network order.
+void CIkev1Negotiation::ReceiveSPIL(TUint32 aSPI, TUint32 aSeq)
+{
+ TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
+
+ DEBUG_LOG2(_L("ReceiveSPIL: SPI=%x, Seq=%u"), ByteOrder::Swap32(aSPI), aSeq);
+
+ CProposal_IIList *propII_List = iProposal_IIList;
+ TInt i;
+ for (i = 0; i < iInboundSPIList->Count(); i++)
+ {
+ if (iInboundSPIList->At(i).iSeq == aSeq)
+ {
+ iInboundSPIList->At(i).iSPI = aSPI;
+ break;
+ }
+ //prop = prop->iNext; //To assign the SPI to the correct proposal
+ }
+ if (iRole==INITIATOR)
+ propII_List->At(i)->iSPI.Copy((TUint8*)&aSPI,sizeof(TUint32)); //needed to send it to the other Peer
+
+ if (--iPendingSPI) // Check if still waiting for some SPI
+ return;
+
+ iExchange = IKE_QUICK_MODE; //Current mode being used.
+ //Builds and send the packet
+
+ if ((iHostData->iCommit) && (iRole==RESPONDER)) //Responder && if commit bit used we set the flag
+ iFlags |= ISAKMP_HDR_CFLAG; //Sets the Commit bit if this side set it else
+
+ msg->IsakmpInit(this);
+ msg->IsakmpHashL();
+ msg->IsakmpSa();
+ ComputeNonceL(); //Computes a new Nonce for PHASE_II
+ msg->IsakmpNonce();
+ if (iPFS) //Only sent if PFS in use...
+ msg->IsakmpKeyL();
+
+ if (iStage==1) //Initiator
+ {
+ //
+ // If Internal address private extension used change iLocalAddr1_ID_II to
+ // correspond that address
+ //
+ if ( iInternalAddr ) {
+ iLocalAddr1_ID_II = iInternalAddr->iClientIntAddr;
+ iLocalIDType_II = ID_IPV4_ADDR;
+ iDefaultLocalID = ETrue;
+ }
+ DEBUG_LOG(_L("PhaseII IV:")); //New IV for phase II
+ ComputeIVL(iIV, iMessageId);
+ //IDs must be sent if TUNNEL mode or is explicitly specified in the acquire
+ if ((!iLocalAddr1_ID_II.IsUnspecified()) || (!iRemoteAddr1_ID_II.IsUnspecified()) ||
+ (iIDProtocol != 0) || (iIDLocalPort != 0) || (iIDRemotePort != 0))
+ {
+ msg->IsakmpOwnIdentL(); //Own proxy
+ msg->IsakmpPeerIdentL(); //Peer Proxy
+ }
+ }
+ else if (iStage==2) //Responder
+ {
+ if (iIDReceived) //If received we send it back, otherwise no
+ {
+ msg->IsakmpPeerIdentL();
+ msg->IsakmpOwnIdentL();
+ }
+ if (iHostData->iResponderLifetime)
+ CheckSendResponderLifetime(*msg); //Adds to the message the RESPONDER_LIFETIME payload if needed
+ }
+ else return;
+
+ if (iHostData->iReplayStatus)
+ {
+ DEBUG_LOG(_L("Constructing REPLAY-STATUS"));
+
+ TInt i;
+ CProposal_II *prop;
+ for (i = 0 ; i < iProposal_IIList->Count(); i++)
+ {
+ prop = iProposal_IIList->At(i);
+ msg->IsakmpReplayStatus(prop->iProtocol, iInboundSPIList->At(i).iSPI, prop->iReplayWindowLength);
+ }
+ }
+ msg->IsakmpHashContL(); //inserts the hash in the correct position of the buffer
+ SendL(*msg);
+
+ iStage++; //Next stage
+}
+
+void CIkev1Negotiation::AcquireSAErrorResponse(TInt aError)
+ {
+ DEBUG_LOG(_L("CIkev1Negotiation::AcquireSAErrorResponse"));
+
+ if ( iProposal_IIList )
+ {
+ for ( TInt j=0; j<iProposal_IIList->Count(); j++ )
+ {
+ TIpsecSAData sa_data;
+ sa_data.iSeq = iAcquireSeq;
+ sa_data.iPid = iPfkeyAcquirePID;
+ sa_data.iSPI = 0;
+ sa_data.iDst = iRemoteAddr;
+
+ CProposal_II* prop_II = iProposal_IIList->At( j );
+
+ if ( prop_II->iProtocol == PROTO_IPSEC_AH )
+ {
+ sa_data.iSAType = SADB_SATYPE_AH;
+ }
+ else if ( prop_II->iProtocol == PROTO_IPSEC_ESP )
+ {
+ sa_data.iSAType = SADB_SATYPE_ESP;
+ }
+ iPluginSession->AcquireSAError( sa_data, aError );
+ }
+ }
+ iAcquirePending = EFalse;
+ }
+
+//Will update the outbound
+void CIkev1Negotiation::UpdateSADatabaseL()
+{
+#ifdef _DEBUG
+ TBuf<40> addr_buf;
+#endif
+ TUint8 sa_type=0;
+ TUint8 auth_alg=0;
+ TUint8 encr_alg=0;
+ TUint32 updateSPI,addSPI;
+ TIpsecSAData sa_data;
+ TBool TunnelMode;
+ //
+ // Received Phase II key (Might be Auth + Encr keys)
+ // (Buffers are allocated for max 1024 bits key material
+ HBufC8* outboundKey_II = HBufC8::NewLC(128);
+ HBufC8* inboundKey_II = HBufC8::NewLC(128);
+ TPtrC8 in_authKey(0,0), out_authKey(0,0);
+ TPtrC8 in_encrKey(0,0), out_encrKey(0,0);
+ //Identities
+#ifdef _UNICODE
+ TBuf<80> id_work;
+#endif
+ HBufC8* local_id = HBufC8::NewLC(128);
+ HBufC8* remote_id = HBufC8::NewLC(128);
+ // ESP UDP Encapsulation extension data
+ HBufC8* gen_ext_data = HBufC8::NewLC(128);
+ TPtr8 GenExtData((TUint8*)gen_ext_data->Ptr(), 0, gen_ext_data->Des().MaxLength());
+
+ DEBUG_LOG(_L("---UPDATING SAD---"));
+
+ if ((!iDefaultLocalID) && (iLocalIDType_II != 0))
+ {
+ switch (iLocalIDType_II)
+ {
+ case ID_IPV4_ADDR:
+ case ID_IPV6_ADDR:
+#ifdef _UNICODE
+ iLocalAddr1_ID_II.OutputWithScope(id_work);
+ local_id->Des().Copy(id_work);
+#else
+ iLocalAddr1_ID_II.OutputWithScope(local_id->Des());
+#endif
+ break;
+
+ case ID_IPV4_ADDR_SUBNET:
+ case ID_IPV6_ADDR_SUBNET:
+#ifdef _UNICODE
+ iLocalAddr1_ID_II.OutputWithScope(id_work);
+ local_id->Des().Copy(id_work);
+#else
+ iLocalAddr1_ID_II.OutputWithScope(local_id->Des());
+#endif
+ local_id->Des().AppendFormat(_L8("/%d"),PrefixLen(iLocalAddr2_ID_II)); //PrefixLen can't fail because checked before
+ break;
+ default: //Should never come here
+ DEBUG_LOG1(_L("Local ID type %d not supported"), iLocalIDType_II);
+ CleanupStack::PopAndDestroy(5); //key buffer, identities and ESP UDP encaps data
+ return;
+ }
+ }
+
+ if ((!iDefaultRemoteID) && (iRemoteIDType_II != 0) )
+ {
+ switch (iRemoteIDType_II)
+ {
+ case ID_IPV4_ADDR:
+ case ID_IPV6_ADDR:
+#ifdef _UNICODE
+ iRemoteAddr1_ID_II.OutputWithScope(id_work);
+ remote_id->Des().Copy(id_work);
+#else
+ iRemoteAddr1_ID_II.OutputWithScope(remote_id->Des());
+#endif
+ if ( iSwapRemoteIdType )
+ {
+ if ( iRemoteIDType_II == ID_IPV4_ADDR )
+ remote_id->Des().AppendFormat(_L8("/%d"), 32);
+ else remote_id->Des().AppendFormat(_L8("/%d"), 128);
+ }
+ break;
+
+ case ID_IPV4_ADDR_SUBNET:
+ case ID_IPV6_ADDR_SUBNET:
+#ifdef _UNICODE
+ iRemoteAddr1_ID_II.OutputWithScope(id_work);
+ remote_id->Des().Copy(id_work);
+#else
+ iRemoteAddr1_ID_II.OutputWithScope(remote_id->Des());
+#endif
+ if ( !iSwapRemoteIdType )
+ remote_id->Des().AppendFormat(_L8("/%d"),PrefixLen(iRemoteAddr2_ID_II)); //PrefixLen can't fail because checked before
+ break;
+
+ default: //Should never come here
+ DEBUG_LOG1(_L("Remote ID type %d not supported"), iRemoteIDType_II);
+ CleanupStack::PopAndDestroy(5); //key buffer, identities and ESP UDP encaps data
+ return;
+ }
+ }
+
+ TUint32 flags = 0;
+ if (iPFS)
+ {
+ flags = SADB_SAFLAGS_PFS;
+ DEBUG_LOG(_L("PFS enabled"));
+ }
+
+ TUint32 addPID;
+ if (iRole==INITIATOR)
+ addPID = iPfkeyAcquirePID; //Require to remove the Larval SA
+ else
+ addPID = iPluginSession->Uid();
+
+ DEBUG_LOG2(_L("SAD seq= %d , PID= %d"), iAcquireSeq, addPID);
+
+ TInt i, j;
+ for (i = 0; i < iInboundSPIList->Count(); i++)
+ {
+ if (iInboundSPIList->At(i).iPropNum == iProposalNum)
+ break;
+ }
+
+ CProposal_II *prop;
+ TSPINode inboundspi_node;
+ TInt key_len, encr_len, auth_len;
+ TChosenAttrib_II *attr_II;
+ TInt count = iChosenProp_IIList->Count();
+
+ for (j = 0 ; j < count; j++)
+ {
+ prop = iChosenProp_IIList->At(j);
+ inboundspi_node = iInboundSPIList->At(i);
+ attr_II = (TChosenAttrib_II *)prop->iAttrList->At(0); //only 1 transform is chosen no matter how many there are
+ if (prop->iProtocol == PROTO_IPSEC_AH)
+ {
+
+ sa_type = SADB_SATYPE_AH;
+ encr_alg = 0;
+ auth_alg = attr_II->iTransformID;
+ auth_len = (TInt)HMAC_KeyLength((TUint8)auth_alg);
+
+ TPtr8 AHOutKey((TUint8*)outboundKey_II->Ptr(), 0, outboundKey_II->Des().MaxLength());
+ TPtr8 AHInKey((TUint8*)inboundKey_II->Ptr(), 0, inboundKey_II->Des().MaxLength());
+
+ ComputeKeys2L(prop, auth_len, inboundspi_node, AHOutKey, AHInKey);
+ in_encrKey.Set(NULL, 0);
+ out_encrKey.Set(NULL, 0);
+ in_authKey.Set(inboundKey_II->Ptr(), auth_len/8);
+ out_authKey.Set(outboundKey_II->Ptr(),auth_len/8);
+ }
+ else if (prop->iProtocol == PROTO_IPSEC_ESP)
+ {
+ sa_type = SADB_SATYPE_ESP;
+ encr_alg = attr_II->iTransformID;
+ if (attr_II->iKeyLength!=0)
+ encr_len = attr_II->iKeyLength;
+ else //not sent means constant size or variable and use default
+ {
+ switch ( encr_alg )
+ {
+ case ESP_DES_CBC:
+ encr_len = 64;
+ break;
+ case ESP_3DES_CBC:
+ encr_len = 3*64;
+ break;
+ case ESP_NULL:
+ encr_len = 0;
+ break;
+ case ESP_AES_CBC:
+ encr_len = 128;
+ break;
+ default:
+ encr_len = 0;
+ break;
+
+ }
+ }
+
+ if (attr_II->iAuthAlg==DOI_HMAC_MD5)
+ auth_alg = SADB_AALG_MD5HMAC;
+ else if (attr_II->iAuthAlg==DOI_HMAC_SHA)
+ auth_alg = SADB_AALG_SHA1HMAC;
+ else
+ auth_alg = 0;
+
+ auth_len = (TInt)HMAC_KeyLength((TUint8)auth_alg);
+ key_len = encr_len + auth_len;
+
+ TPtr8 ESPOutKey((TUint8*)outboundKey_II->Ptr(), 0, outboundKey_II->Des().MaxLength());
+ TPtr8 ESPInKey((TUint8*)inboundKey_II->Ptr(), 0, inboundKey_II->Des().MaxLength());
+ ComputeKeys2L(prop, key_len, inboundspi_node, ESPOutKey, ESPInKey);
+
+ in_encrKey.Set(inboundKey_II->Ptr(), encr_len/8);
+ out_encrKey.Set(outboundKey_II->Ptr(), encr_len/8);
+
+ //If no HMAC selected the next instr does nothing because size will be 0
+ in_authKey.Set(inboundKey_II->Ptr() + in_encrKey.Length(),auth_len/8);
+ out_authKey.Set(outboundKey_II->Ptr() + out_encrKey.Length(),auth_len/8);
+ //
+ // Nokia specific NAT traversal info (=ESP UDP tunneling)
+ // If iNAT_T_Required is true connection is over NAT:ted
+ // newtork (=local end behind NAT).
+ //
+ if ( iNAT_T_Required ) {
+ flags |= SADB_SAFLAGS_NAT_T;
+ }
+ }
+ else
+ {
+ DEBUG_LOG1(_L("Unknown IPsec protocol %d"), prop->iProtocol);
+ CleanupStack::PopAndDestroy(5); //key buffer, identities and ESP UDP encaps data
+ return;
+ }
+
+ updateSPI = inboundspi_node.iSPI;
+ Mem::Copy((TUint8*)&addSPI, prop->iSPI.Ptr(), sizeof(TUint32));
+
+ TInetAddr local_addr(iLocalAddr);
+ local_addr.SetPort(iIDLocalPort);
+ TInetAddr remote_addr(iRemoteAddr);
+ remote_addr.SetPort(iIDRemotePort);
+
+ //This will be always outbound
+
+ TInt64 time(0), bytes(0);
+ TPtrC8 time_ptr(attr_II->iLifeDurationSecs);
+ TPtrC8 bytes_ptr(attr_II->iLifeDurationKBytes);
+ if (attr_II->iReducedLifeSecs.Length() != 0)
+ time_ptr.Set(attr_II->iReducedLifeSecs);
+
+ if (attr_II->iReducedLifeKBytes.Length() != 0)
+ bytes_ptr.Set(attr_II->iReducedLifeKBytes);
+
+ ComputeLifetimes_II(time_ptr, bytes_ptr, time, bytes);
+ if (time == 0) //default lifetime applied
+ time = DEFAULT_IPSEC_SA_LIFETIME;
+
+ if (iHardLifetime > time)
+ {
+ DEBUG_LOG2(_L("Time %u, hard lifetime %d"),
+ I64LOW(time), I64LOW(iHardLifetime));
+ time = iHardLifetime;
+ }
+
+ if (attr_II->iEncMode==DOI_TUNNEL)
+ {
+ TunnelMode = ETrue;
+ DEBUG_LOG(_L("TUNNEL MODE"));
+#ifdef _DEBUG
+ iLocalAddr1_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG1(_L("Local ID: net %S"), &addr_buf);
+
+ if ((iLocalIDType_II == ID_IPV4_ADDR_SUBNET) || (iLocalIDType_II == ID_IPV6_ADDR_SUBNET))
+ {
+ iLocalAddr2_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG1(_L(", mask %S"), &addr_buf);
+ }
+ DEBUG_LOG1(_L(" (port %d)"), iIDLocalPort);
+ iRemoteAddr1_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG1(_L("Remote ID: addr %S"), &addr_buf);
+ if ((iRemoteIDType_II == ID_IPV4_ADDR_SUBNET) || (iRemoteIDType_II == ID_IPV6_ADDR_SUBNET))
+ {
+ iRemoteAddr2_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG1(_L(", mask %S"), &addr_buf);
+ }
+ DEBUG_LOG1(_L(" (port %d)"), iIDRemotePort);
+#endif // _DEBUG
+
+ }
+ else {
+ TunnelMode = EFalse;
+ DEBUG_LOG(_L("TRANSPORT MODE"));
+ }
+ DEBUG_LOG1(_L("Sec Lifetime set to %u"),I64LOW(time));
+ DEBUG_LOG1(_L("KB Lifetime set to %u"),I64LOW(bytes));
+
+ //Inbound SA.
+ sa_data.iSAType = sa_type;
+ sa_data.iSeq = iAcquireSeq;
+ sa_data.iSrc = remote_addr;
+ sa_data.iDst = local_addr;
+ sa_data.iProtocol = iIDProtocol;
+ sa_data.iSrcIdType = iRemoteIDType_II;
+ sa_data.iDstIdType = iLocalIDType_II;
+ sa_data.iSrcIdent.Set((TUint8 *)remote_id->Ptr(), remote_id->Length(), remote_id->Length());
+ sa_data.iDstIdent.Set((TUint8 *)local_id->Ptr(), local_id->Length(), local_id->Length());
+ sa_data.iPid = iPluginSession->Uid();
+ sa_data.iSPI = updateSPI;
+ sa_data.iAuthAlg = auth_alg;
+ sa_data.iEncrAlg = encr_alg;
+ sa_data.iAuthKey.Set(in_authKey);
+ sa_data.iEncrKey.Set(in_encrKey);
+ TIpsecSALifetime lifetime(0, bytes, time, 0);
+ sa_data.iHard = &lifetime;
+ sa_data.iReplayWindowLength = prop->iReplayWindowLength;
+
+ if ( (sa_type == SADB_SATYPE_ESP) && (GenExtData.Length() == 0) ) {
+ //
+ // Store possible NAT traversal info for IPSEC to do ESP UDP encapsulation correctly
+ //
+ PFKeyExtDataUtil::BuildUdpEncExtensionData( GenExtData,
+ iNAT_D_Flags,
+ (flags & SADB_SAFLAGS_NAT_T),
+ iHostData->iUseNatProbing,
+ (TUint16)iHostData->iEspUdpPort,
+ UDP_KEEPALIVE_TIME,
+ iLastRemoteAddr,
+ iRemoteOriginalAddr );
+ }
+
+ if ( GenExtData.Length() ) {
+ sa_data.iGenericExtension.Set(GenExtData);
+ }
+
+ if ( TunnelMode ) {
+ //
+ // Get VPN interface index
+ //
+ TUint32 vpnInterfaceIndex = iPluginSession->VpnInterfaceIndex();
+ if ( vpnInterfaceIndex != 0 )
+ {
+ sa_data.iInternalAddress.Init( KAfInet6 );
+ sa_data.iInternalAddress.SetScope( vpnInterfaceIndex );
+ flags |= SADB_SAFLAGS_INT_ADDR;
+ }
+ }
+ sa_data.iFlags = flags;
+ iPluginSession->UpdateSAL(sa_data);
+ TIpsecSPI SpiData;
+ SpiData.iProtocol = sa_type;
+ SpiData.iSrcAddr = remote_addr;
+ SpiData.iDstAddr = local_addr;
+ SpiData.iSPI = updateSPI;
+ SpiData.iInbound = ETrue;
+ iPluginSession->AddIpsecSPIToSAL(iSAId, SpiData);
+
+ sa_data.iFlags &= ~SADB_SAFLAGS_INT_ADDR; //No VPN interface index to outbound SA
+ //Outbound SA.
+ //First check there's no other SA with the same parameters and
+ //erase it if happens (very unlikely, but still possible)
+ SpiData.iDstAddr = remote_addr;
+ SpiData.iSrcAddr = local_addr;
+ SpiData.iSPI = addSPI;
+ SpiData.iInbound = EFalse;
+ if ( iPluginSession->DeleteIpsecSpi(iSAId, addSPI, EFalse) )
+ {
+ DEBUG_LOG(_L("Deleting previously negotiated IPsec SA"));
+ iPluginSession->DeleteIpsecSA(SpiData.iSPI, SpiData.iSrcAddr, SpiData.iDstAddr,
+ SpiData.iProtocol);
+ }
+ //Some changes in the SA, the rest is the same
+ sa_data.iSrc = local_addr;
+ sa_data.iDst = remote_addr;
+ sa_data.iSrcIdType = iLocalIDType_II;
+ sa_data.iDstIdType = iRemoteIDType_II;
+ sa_data.iSrcIdent.Set((TUint8 *)local_id->Ptr(), local_id->Length(), local_id->Length());
+ sa_data.iDstIdent.Set((TUint8 *)remote_id->Ptr(), remote_id->Length(), remote_id->Length());
+ sa_data.iPid = addPID;
+ sa_data.iSPI = addSPI;
+ sa_data.iAuthKey.Set(out_authKey);
+ sa_data.iEncrKey.Set(out_encrKey);
+
+ iPluginSession->AddSAL(sa_data);
+ iPluginSession->AddIpsecSPIToSAL(iSAId, SpiData);
+
+ i++; //To get the correct SPIs from iInboundSPIList
+ } //end for
+
+ CleanupStack::PopAndDestroy(5); //key buffer, identities and ESP UDP encaps data
+
+ iAcquirePending = EFalse;
+
+}
+
+void CIkev1Negotiation::ComputeLifetimes_II(const TDesC8 &aLifetime, const TDesC8 &aLifesize, TInt64 &aTime, TInt64 &aBytes)
+{
+ TInt64 maxnum = MAKE_TINT64(0x7fffffffu, 0xffffffffu);
+
+ if (Desc8ToTInt64(aLifetime, aTime) != KErrNone)
+ {
+ DEBUG_LOG(_L("Phase_II Lifetime(sec) Overflowed Setting to maximum value"));
+ }
+ if (Desc8ToTInt64(aLifesize, aBytes) != KErrNone) {
+ DEBUG_LOG(_L("Phase_II Lifetime(kbytes) Overflowed Setting to maximum value"));
+ }
+ else
+ { if (aBytes < maxnum / 1024) //Make sure no overflow
+ aBytes = aBytes * 1024; //KB to Bytes
+ else
+ {
+ aBytes = MAKE_TINT64(KMaxTInt, KMaxTUint);
+ DEBUG_LOG(_L("Phase_II Lifetime(kbytes) Overflowed Setting to maximum value"));
+ }
+ }
+
+}
+
+
+TBool CIkev1Negotiation::Phase_IExchangeL(const ThdrISAKMP &aHdr)
+{
+ if (!ProcessHeaderL(aHdr))
+ return EFalse;
+
+ DEBUG_LOG2(_L("---------- Phase %d - Stage %d ----------"),iPhase, iStage);
+ if (iPhase == PHASE_I)
+ {
+ if (aHdr.GetExchange() == ISAKMP_EXCHANGE_ID)
+ MainModeReplyL(); //Main Mode
+ else
+ AggressiveReplyL(); //Aggressive Mode
+ }
+ else
+ {
+ QuickModeReplyL();
+ }
+ return ETrue;
+}
+
+TBool CIkev1Negotiation::Phase_IIExchangeL(const ThdrISAKMP &aHdr)
+{
+ if (!ProcessHeaderL(aHdr))
+ return EFalse;
+
+ DEBUG_LOG2(_L("---------- Phase %d - Stage %d ----------"),iPhase, iStage);
+
+ QuickModeReplyL();
+
+ return ETrue;
+}
+
+void CIkev1Negotiation::QuickModeReplyL()
+{
+
+ TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
+ switch(iStage)
+ {
+ //INITIATOR cases
+ case 3: //Send last message (if no COMMIT)
+ msg->IsakmpInit(this);
+ msg->IsakmpHashL();
+ SendL(*msg);
+ iStage = 4;
+
+ if (iRecvFlags & ISAKMP_HDR_CFLAG) //Commit Bit set
+ return; //Not finished yet. We wait for CONNECTED
+
+ //No Commot bit, Update SA database
+ UpdateSADatabaseL();
+ SetFinished(); //No more stages.
+ break;
+ case 1:
+ DEBUG_LOG(_L("QuickModeReplyL in Stage 1 ?"));
+ break;
+ case 5: //Send last message (extra message waiting for commit(
+ //No more processing required
+ SetFinished(); //No more stages.
+ break;
+ //RESPONDER cases
+ case 2:
+ GetSPIL();
+ //rest done in receiveSPI
+ break;
+ case 4:
+ if (iRecvFlags & ISAKMP_HDR_CFLAG) //Commit Bit set
+ {
+ DEBUG_LOG(_L("Sending CONNECTED Status message"));
+ msg->IsakmpInit(this);
+
+ //HASH Payload only if payload protected with encryption
+ if (iFlags & ISAKMP_HDR_EFLAG)
+ msg->IsakmpHashL();
+
+ msg->IsakmpNotification(CONNECTED, iChosenProp_IIList->At(0)->iProtocol);
+
+ if (iFlags & ISAKMP_HDR_EFLAG)
+ msg->IsakmpHashContL();
+
+ SendL(*msg);
+ }
+ SetFinished();
+ break;
+ }
+}
+
+//Builds and sends a Phase I reply for Main Mode
+void CIkev1Negotiation::MainModeReplyL()
+{
+ TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
+ TInt vendor_type;
+
+ switch(iStage)
+ {
+ //
+ //RESPONDER replies
+ //
+ case 2:
+ msg->IsakmpInit(this);
+ msg->IsakmpSa();
+ if ( iHostData->iDPDHeartBeat != 0 )
+ BuildDPDVendorId(*msg);
+ if ( iNatDiscovery ) {
+ iNatDiscovery->BuildNatVendorId(*msg);
+ iNatDiscovery->BuildRfcNatVendorId(*msg);
+ }
+ SendL(*msg);
+ iStage = 3;
+ break;
+ case 4:
+ ComputeNonceL(); //Nonce to be sent
+ msg->IsakmpInit(this);
+ msg->IsakmpKeyL();
+ msg->IsakmpNonce();
+ if (((iChosenProposal_I.iAttrList->iAuthMethod == RSA_SIG) ||
+ (iChosenProposal_I.iAttrList->iAuthMethod == DSS_SIG)) && !iPeerX509Cert)
+ msg->IsakmpCertificateReqL();
+ if ( iNatDiscovery ) {
+ iNatDiscovery->BuildDiscoveryPayloadsL(*msg, iChosenProposal_I.iAttrList->iHashAlg,
+ (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
+ iLocalAddr, iLastRemoteAddr);
+ }
+ SendL(*msg);
+ iStage = 5; //next stage
+ iFlags |= ISAKMP_HDR_EFLAG; //From now on encryption is used
+ if (!ComputeKeysL()) //Generates keying material for encryption stages
+ return; //error don't send reply packet
+ break;
+ case 6:
+ msg->IsakmpInit(this);
+
+ switch(iChosenProposal_I.iAttrList->iAuthMethod)
+ {
+ case RSA_SIG:
+ case DSS_SIG:
+ msg->IsakmpOwnIdentL();
+ msg->IsakmpCertificateL();
+ msg->IsakmpSignatureL();
+ break;
+ case PRE_SHARED:
+ msg->IsakmpOwnIdentL();
+ msg->IsakmpHashL();
+ break;
+ }
+
+ if ( iHostData->iInitialContact &&
+ iRole == INITIATOR &&
+ iSARekeyInfo == NULL )
+ {
+ DEBUG_LOG(_L("Constructing INITIAL-CONTACT"));
+ msg->IsakmpNotification(DOI_INITIAL_CONTACT, PROTO_ISAKMP); //Not protected by the hash!
+ }
+
+ SendL(*msg);
+
+ IsakmpPhase1CompletedL();
+ break;
+ //
+ //INITIATOR replies
+ //
+ case 3:
+ ComputeNonceL(); //Nonce to be sent
+ msg->IsakmpInit(this);
+ msg->IsakmpKeyL();
+ msg->IsakmpNonce();
+ if ( iChosenProposal_I.iAttrList->iAuthMethod == IKE_A_CRACK && !iPeerX509Cert )
+ {
+ msg->IsakmpCertificateReqL();
+ }
+ if ( iHostData->iUseNatProbing )
+ vendor_type = EXPANDED_VENDOR_ID;
+ else vendor_type = HASH_VENDOR_ID;
+ msg->IsakmpVendorId(vendor_type,
+ (TUint8*)iCookie_I.Ptr(),
+ (TUint8*)iCookie_R.Ptr(), iLocalAddr);
+ if ( iNatDiscovery )
+ {
+ iNatDiscovery->BuildDiscoveryPayloadsL(*msg, iChosenProposal_I.iAttrList->iHashAlg,
+ (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
+ iLocalAddr, iLastRemoteAddr);
+ }
+ SendL(*msg);
+ iStage = 4; //next stage
+ break;
+
+ case 5:
+ iFlags |= ISAKMP_HDR_EFLAG; //From now on encryption is used
+ if (!ComputeKeysL()) //Generates keying material for encryption stages
+ return; //error don't send reply packet
+
+ msg->IsakmpInit(this);
+
+ switch(iChosenProposal_I.iAttrList->iAuthMethod)
+ {
+ case RSA_SIG:
+ case DSS_SIG:
+ msg->IsakmpOwnIdentL(); //Also fills iOwnIdentPayload!
+ msg->IsakmpCertificateL();
+ msg->IsakmpSignatureL();
+ if (!iPeerX509Cert)
+ msg->IsakmpCertificateReqL();
+ break;
+ case PRE_SHARED:
+ msg->IsakmpOwnIdentL();
+ msg->IsakmpHashL();
+ break;
+ }
+
+
+ if ( iFamiliarPeer && iHostData->iUseInternalAddr )
+ msg->IsakmpIntnet(0); /* null IPV4 address as parameter */
+
+ if ( iHostData->iInitialContact &&
+ !iPluginSession->FindIkev1SADataWithAddr(iRemoteAddr) &&
+ iRole == INITIATOR &&
+ iSARekeyInfo == NULL )
+ {
+ DEBUG_LOG(_L("Constructing INITIAL-CONTACT"));
+ msg->IsakmpNotification(DOI_INITIAL_CONTACT, PROTO_ISAKMP); //Not protected by the hash!
+ }
+
+ SendL(*msg);
+ iStage = 6; //next stage
+
+ break;
+ case 7:
+ // CRACK authentication going. No actions required here !
+ break;
+ default:
+ DEBUG_LOG1(_L("Main mode Wrong Phase number requested (%d) !!"),iStage);
+ }
+
+}
+
+
+//Builds and sends a Phase I reply for Aggressive Mode
+void CIkev1Negotiation::AggressiveReplyL()
+{
+ TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
+
+ switch(iStage)
+ {
+ case 2://RESPONDER Sends the 2nd msg. of the exchange
+ ComputeNonceL(); //Nonce to be sent
+ msg->IsakmpInit(this);
+ msg->IsakmpSa();
+ msg->IsakmpKeyL();
+ msg->IsakmpNonce();
+ msg->IsakmpOwnIdentL();
+
+ if (!ComputeKeysL()) //Needed to compute hash before computing signature
+ {
+ return;
+ }
+
+ if ( iHostData->iDPDHeartBeat != 0 )
+ BuildDPDVendorId(*msg);
+ if ( iNatDiscovery ) {
+ iNatDiscovery->BuildNatVendorId(*msg);
+ iNatDiscovery->BuildRfcNatVendorId(*msg);
+ iNatDiscovery->BuildDiscoveryPayloadsL(*msg, iChosenProposal_I.iAttrList->iHashAlg,
+ (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
+ iLocalAddr, iLastRemoteAddr);
+ }
+
+ switch (iChosenProposal_I.iAttrList->iAuthMethod)
+ {
+ case RSA_SIG:
+ case DSS_SIG:
+ msg->IsakmpCertificateL();
+ msg->IsakmpSignatureL();
+ if (!iPeerX509Cert) //No stored cert so send a CR
+ {
+ msg->IsakmpCertificateReqL();
+ }
+ break;
+ case PRE_SHARED:
+ msg->IsakmpHashL();
+ break;
+ }
+ SendL(*msg);
+
+ iStage = 3; //next stage
+
+ break;
+ case 3://INITIATOR
+ iFlags |= ISAKMP_HDR_EFLAG; //From now on encryption is used
+ msg->IsakmpInit(this);
+ if ( iNatDiscovery ) {
+ iNatDiscovery->BuildDiscoveryPayloadsL(*msg, iChosenProposal_I.iAttrList->iHashAlg,
+ (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
+ iLocalAddr, iLastRemoteAddr);
+ }
+
+ switch (iChosenProposal_I.iAttrList->iAuthMethod)
+ {
+ case RSA_SIG:
+ case DSS_SIG:
+ msg->IsakmpCertificateL();
+ msg->IsakmpSignatureL();
+ break;
+ case PRE_SHARED:
+ msg->IsakmpHashL();
+ break;
+ }
+ if ( iHostData->iInitialContact &&
+ !iPluginSession->FindIkev1SADataWithAddr(iRemoteAddr) &&
+ iRole == INITIATOR &&
+ iSARekeyInfo == NULL ) //Only sent if no ISAKMP SA established
+ {
+ DEBUG_LOG(_L("Constructing INITIAL-CONTACT"));
+ msg->IsakmpNotification(DOI_INITIAL_CONTACT, PROTO_ISAKMP); //Not protected by the hash!
+ }
+
+ SendL(*msg);
+ IsakmpPhase1CompletedL();
+ break;
+ case 4:
+ // Aggressive mode as responder completed
+ DEBUG_LOG(_L("Aggressive mode as responder completed"));
+ break;
+
+ case 7:
+ // CRACK authentication going. No actions required here !
+ break;
+ default:
+ DEBUG_LOG1(_L("Main mode Wrong Phase number requested (%d) !! "),iStage);
+ }
+
+}
+
+void CIkev1Negotiation::SaveISAKMPSAL()
+{
+ //
+ // Create a new IKEv1 SA object
+ //
+ TIkev1SAData SaData;
+ SaData.iCookie_I = iCookie_I;
+ SaData.iCookie_R = iCookie_R;
+ SaData.iSAId = iSAId;
+ SaData.iSAState = 0;
+ SaData.iInitiator = (iRole == INITIATOR);
+ SaData.iAutoLogin = iAutoLogin;
+ SaData.iDPDSupported = iDPDSupported;
+ SaData.iFamiliarPeer = iFamiliarPeer;
+ SaData.iNAT_T_Required = iNAT_T_Required;
+ SaData.iNAT_D_Flags = iNAT_D_Flags;
+ SaData.iIkeData = iHostData;
+ SaData.iLocalAddr = iLocalAddr;
+ SaData.iRemoteAddr = iRemoteAddr;
+ SaData.iDestinAddr = iLastRemoteAddr;
+ SaData.iVirtualIp = iInternalAddr;
+ SaData.iSeq = iSeq;
+ SaData.iPrevExchange = iPrevExchange;
+ SaData.iFlags = iFlags;
+
+ SaData.iEncrAlg = iChosenProposal_I.iAttrList->iEncrAlg;
+ SaData.iHashAlg = iChosenProposal_I.iAttrList->iHashAlg;
+ SaData.iGroupDesc = iChosenProposal_I.iAttrList->iGroupDesc;
+ SaData.iGroupType = iChosenProposal_I.iAttrList->iGroupType;
+ SaData.iKeyLength = iChosenProposal_I.iAttrList->iKeyLength;
+
+ TUint32 Lifetime = 0;
+ TUint Len = iChosenProposal_I.iAttrList->iLifeDurationSecs.Length();
+ if (Len > 0)
+ {
+ if (Len > sizeof(TUint32))
+ {
+ Lifetime = KMaxTUint32;
+ }
+ else // (len <= sizeof(TUint32))
+ {
+ Mem::Copy(&Lifetime, iChosenProposal_I.iAttrList->iLifeDurationSecs.Ptr(), Len);
+ Lifetime = ByteOrder::Swap32(Lifetime);
+ Lifetime = Lifetime >> (sizeof(TUint32)*8 - Len*8); //To set the correct value (shift in bits)
+ }
+ }
+ SaData.iLifeTimeSecs = Lifetime;
+
+ Lifetime = 0;
+ Len = iChosenProposal_I.iAttrList->iLifeDurationKBytes.Length();
+ if (Len > 0)
+ {
+ if (Len > sizeof(TUint32))
+ {
+ Lifetime = KMaxTUint32;
+ }
+ else // (len <= sizeof(TUint32))
+ {
+ Mem::Copy(&Lifetime, iChosenProposal_I.iAttrList->iLifeDurationKBytes.Ptr(), Len);
+ Lifetime = ByteOrder::Swap32(Lifetime);
+ Lifetime = Lifetime >> (sizeof(TUint32)*8 - Len*8); //To set the correct value (shift in bits)
+ }
+ }
+ SaData.iLifeTimeKB = Lifetime;
+
+ SaData.iSKEYID = iSKEYID;
+ SaData.iSKEYID_d = iSKEYID_d;
+ SaData.iSKEYID_a = iSKEYID_a;
+ SaData.iSKEYID_e = iSKEYID_e;
+ SaData.iLastIV = iLastIV;
+ SaData.iIV = iIV;
+
+ if ( iDPDSupported && iHostData->iDPDHeartBeat )
+ {
+ //
+ // Initialize DPD protocol parameters in TIkev1SAData
+ //
+ TPtr8 ptr((TUint8*)&SaData.iDPDSequence, sizeof(TUint32));
+ ptr.SetLength(sizeof(TUint32));
+ TRandom::RandomL(ptr);
+ SaData.iDPDSequence &= 0x7fffffff;
+ SaData.iDPDRetry = 0;
+ SaData.iPendingDPDSequence = 0;
+ SaData.iExpectedDPDSequence = 0;
+ }
+
+ iLastIKEMsgInfo.Store(SaData.iLastIKEMsgInfo);
+ SaData.iLastMsg = iLastMsg;
+ iPluginSession->CreateIkev1SAL(SaData, iSARekeyInfo); // Add rekey info later
+}
+
+TBool CIkev1Negotiation::ProcessHeaderL(const ThdrISAKMP &aHdr)
+{
+ //checks on the header
+ if (!CheckCookies(aHdr.GetCookieI(), aHdr.GetCookieR()))
+ return EFalse;
+
+ if (iStage == 1)
+ {
+ if (iPhase == PHASE_I)
+ {
+ iCookie_I = aHdr.GetCookieI(); //save initiator cookie
+ iCookie_R = CreateCookieL(); //create responder cookie
+ }
+ iMessageId = aHdr.GetMessageId();
+ }
+
+ //checks on the header
+ if (!CheckPayloadCode(aHdr.GetPayload()))
+ return EFalse;
+
+ if (!CheckVersionL(aHdr.GetVersion()))
+ return EFalse;
+
+ if (!CheckExchangeTypeL(aHdr.GetExchange()))
+ return EFalse;
+
+ if (!CheckFlagsL(aHdr.GetFlags()))
+ return EFalse;
+ iRecvFlags = aHdr.GetFlags(); //Save the flags for later use
+
+ if (!CheckMessageIdL(aHdr.GetMessageId()))
+ return EFalse;
+
+ iLengthLeft -= ISAKMP_HEADER_SIZE; //Updates the length left in the buffer
+
+ //EVEN stages RESPONDER, ODD ones INITIATOR
+ DEBUG_LOG(_L("Processing packet..."));
+ if (iPhase == PHASE_I)
+ {
+ switch (iStage)
+ {
+ case 1:
+ if (!ProcessStage1L(aHdr))
+ return EFalse;
+ break;
+ case 2:
+ if (!ProcessStage2L(aHdr))
+ return EFalse;
+ break;
+ case 3:
+ if (iExchange == ISAKMP_EXCHANGE_ID)
+ {
+ if (!ProcessStage3MainL(aHdr))
+ return EFalse;
+ }
+ else //ISAKMP_EXCHANGE_AGGR
+ {
+ if (!ProcessStage3AggrL(aHdr))
+ return EFalse;
+ }
+ break;
+ case 4:
+ if (!ProcessStage4L(aHdr))
+ return EFalse;
+ break;
+ case 5:
+ if (!ProcessStage5L(aHdr))
+ return EFalse;
+ break;
+ case 6:
+ if (!ProcessStage6L(aHdr))
+ return EFalse;
+ break;
+ case 7:
+ if (!ProcessStage7L(aHdr)) /* For CRACK negotiation */
+ return EFalse;
+ break;
+ default:
+ return EFalse;
+ }
+ }
+ else //PHASE_II
+ {
+ switch (iStage)
+ {
+ case 1:
+ if (!ProcessStage1Phase2L(aHdr))
+ return EFalse;
+ break;
+ case 2:
+ if (!ProcessStage2Phase2L(aHdr))
+ return EFalse;
+ break;
+ case 3:
+ if (!ProcessStage3Phase2L(aHdr))
+ return EFalse;
+ break;
+ case 4:
+ if (!ProcessCONNECTEDL(aHdr))
+ return EFalse;
+ break;
+ default:
+ DEBUG_LOG(_L("Quick Bad Stage"));
+ }
+ }
+ return ETrue;
+}
+
+//Process payloads appearing in Stage 1. Responder Role
+TBool CIkev1Negotiation::ProcessStage1L(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+
+ //SA Payload processing (all modes)
+ if (!ProcessSAL(payload->iSa, NULL))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ //Process vendor ID:s
+ ProcessVendorL(payload->iVids);
+
+ TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
+
+ switch (auth_method)
+ {
+ case RSA_SIG:
+ case DSS_SIG:
+ //Process the possible CR payloads
+ if ( !ProcessCertificateReqArrayL(payload->iCertReqs) ) {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //Process the possible CERT payloads
+ if ( !ProcessCertificateArrayL(payload->iCerts) ) {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+
+ if (iExchange == ISAKMP_EXCHANGE_ID)
+ {
+ if ( payload->iKe || payload->iNonce || payload->iHash || payload->iSign ||
+ payload->iIds->Count() )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ }
+ else //ISAKMP_EXCHANGE_AGGR
+ {
+ if ( !payload->iKe || !payload->iNonce || (payload->iIds->Count() != 1) )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ //Key Payload processing (all modes)
+ if (!ProcessKeyL(payload->iKe))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ TIdentISAKMP* id_payload = (TIdentISAKMP*)payload->iIds->At(0); // The first ID
+ TNonceISAKMP* nonce_payload = (TNonceISAKMP*)payload->iNonce;
+
+ //Nonce Payload processing
+ if (!ProcessNonceL(nonce_payload))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //ID Payload processing
+ if (!CheckIdentL(id_payload))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //Save the ident payload for HASH_I/R computing
+ delete iPeerIdentPayload;
+ iPeerIdentPayload = NULL;
+ iPeerIdentPayloadSize = id_payload->GetLength() - sizeof(TPayloadISAKMP);
+ iPeerIdentPayload = new (ELeave) TUint8[iPeerIdentPayloadSize];
+ Mem::Copy(iPeerIdentPayload,((TUint8 *)id_payload)+sizeof(TPayloadISAKMP),iPeerIdentPayloadSize);
+
+ if ( iNatDiscovery )
+ {
+ if ( payload->iNatDs->Count() )
+ {
+ iNAT_D_Flags = iNatDiscovery->CheckDiscoveryPayloadsL(payload->iNatDs, iChosenProposal_I.iAttrList->iHashAlg,
+ (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
+ iLocalAddr, iLastRemoteAddr);
+ if ( iNAT_D_Flags )
+ iLastRemoteAddr.SetPort(FLOATED_IKE_PORT);
+ }
+ else
+ {
+ if ( iLastRemoteAddr.Port() == FLOATED_IKE_PORT )
+ iNAT_D_Flags |= LOCAL_END_NAT;
+ }
+ }
+
+ }//end aggressive
+
+
+ CleanupStack::PopAndDestroy(); //payload
+ iStage = 2;
+ return ETrue;
+
+}
+
+//Process payloads appearing in Stage 3. Initiator Role
+TBool CIkev1Negotiation::ProcessStage2L(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+
+ iCookie_R = aHdr.GetCookieR(); //Save the responder Cookie
+
+ //SA Payload processing (all modes)
+ if (!ProcessSAL(payload->iSa, NULL)) {
+ CleanupStack::PopAndDestroy(); // payload
+ return EFalse;
+ }
+
+ ProcessVendorL(payload->iVids);
+
+ if ( iAutoLogin && (iLocalAddr.Family() == KAFUnspec ) ) {
+ User::LeaveIfError( iPluginSession->GetLocalAddress( iLocalAddr ) ); //No local address, get it now (used later with NAT traversal)
+ }
+
+ //NOW we know the auth method chosen!
+ TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
+
+ switch (auth_method)
+ {
+ case RSA_SIG:
+ case DSS_SIG:
+ //Process the possible CR payloads
+ if ( !ProcessCertificateReqArrayL(payload->iCertReqs) )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ if (!ProcessCertificateArrayL(payload->iCerts))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (iExchange == ISAKMP_EXCHANGE_ID)
+ {
+ if ( payload->iKe || payload->iNonce || payload->iHash || payload->iSign ||
+ payload->iIds->Count() )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ }
+ else //ISAKMP_EXCHANGE_AGGR
+ {
+ if ( !payload->iKe ||
+ !payload->iNonce ||
+ (!payload->iHash && auth_method == PRE_SHARED) // hash is a must only with PSK
+ || (payload->iIds->Count() != 1) )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //Key Payload processing (all modes)
+ if (!ProcessKeyL(payload->iKe))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ TIdentISAKMP* id_payload = (TIdentISAKMP*)payload->iIds->At(0); // The first ID
+ TNonceISAKMP* nonce_payload = (TNonceISAKMP*)payload->iNonce;
+
+ //Nonce Payload processing
+ if (!ProcessNonceL(nonce_payload))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ //ID Payload processing
+ if (!CheckIdentL(id_payload))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ //Save the ident payload for HASH_I/R computing
+ delete iPeerIdentPayload;
+ iPeerIdentPayload = NULL;
+ iPeerIdentPayloadSize = id_payload->GetLength()-sizeof(TPayloadISAKMP);
+ iPeerIdentPayload = new (ELeave) TUint8[iPeerIdentPayloadSize];
+ Mem::Copy(iPeerIdentPayload,((TUint8 *)id_payload)+sizeof(TPayloadISAKMP),iPeerIdentPayloadSize);
+
+ if (!ComputeKeysL()) //Computes the keys to be used. Needed to compute HASH_R
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ switch (auth_method)
+ {
+ case PRE_SHARED:
+ if (!ProcessHashL(payload->iHash))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ DEBUG_LOG(_L("HASH OK!"));
+ break;
+ case RSA_SIG:
+ case DSS_SIG:
+ case IKE_A_CRACK:
+ //Signature payload processing and checking
+ if (!ProcessSignatureL(payload->iSign)) {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ if ( auth_method == IKE_A_CRACK ) {
+ CleanupStack::PopAndDestroy(); //payload
+ return StartCRACKAuthL();
+ }
+ else {
+ if (!CertifyRemoteIdentityL(id_payload))
+ {
+ DEBUG_LOG(_L("ProcessStage2L RSA_SIG CertifyRemoteIdentityL failed"));
+ DEBUG_LOG(_L("AUTHENTICATION_FAILED"));
+ SetErrorStatus( KKmdIkeAuthFailedErr );
+ SendNotifyL(AUTHENTICATION_FAILED);
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ }
+ break;
+
+ default:
+ break;
+
+ }
+
+ if ( iNatDiscovery && payload->iNatDs->Count()) {
+ iNAT_D_Flags = iNatDiscovery->CheckDiscoveryPayloadsL(payload->iNatDs, iChosenProposal_I.iAttrList->iHashAlg,
+ (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
+ iLocalAddr, iLastRemoteAddr);
+ if ( iNAT_D_Flags )
+ iLastRemoteAddr.SetPort(FLOATED_IKE_PORT);
+ }
+ }
+
+ CleanupStack::PopAndDestroy(); //payload
+
+ iStage = 3;
+ return ETrue;
+}
+
+// Process payloads appearing in Stage 4. Order NOT relevant. Only Main Mode
+// Handles message: HDR, KE, [HASH(1),] <IDii>PubKey_r, <Ni>PubKey_r for RSAENCR
+// Handles message: HDR, KE, Ni for the rest [CERTREQ] in certificates
+TBool CIkev1Negotiation::ProcessStage3MainL(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+
+ if ( payload->iSa || !payload->iNonce || payload->iHash || payload->iSign )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
+
+ //Process payloads
+ //Main mode (ONLY)
+ //Key Payload processing (all methods)
+ if (!ProcessKeyL(payload->iKe))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ if ((auth_method == RSA_SIG) || (auth_method == DSS_SIG))
+ {
+ //Process the possible CR payloads
+ if ( !ProcessCertificateReqArrayL(payload->iCertReqs) )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ }
+
+ TNonceISAKMP* nonce_payload = (TNonceISAKMP*)payload->iNonce;
+
+ ProcessVendorL(payload->iVids);
+
+ if ( iNatDiscovery && payload->iNatDs->Count() )
+ {
+ iNAT_D_Flags = iNatDiscovery->CheckDiscoveryPayloadsL(payload->iNatDs, iChosenProposal_I.iAttrList->iHashAlg,
+ (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
+ iLocalAddr, iLastRemoteAddr);
+ if ( iNAT_D_Flags )
+ iLastRemoteAddr.SetPort(FLOATED_IKE_PORT);
+ }
+
+ //Nonce Payload processing (all methods)
+ TBool Status = ProcessNonceL(nonce_payload);
+ if ( Status )
+ {
+ iStage = 4;
+ }
+ CleanupStack::PopAndDestroy(); //payload
+
+ return Status;
+}
+
+
+//Process payloads appearing in 3(Aggressive). Order NOT relevant
+TBool CIkev1Negotiation::ProcessStage3AggrL(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+
+ TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
+
+ //Mode-dependent processing
+ switch (auth_method)
+ {
+ case PRE_SHARED:
+ //Hash payload processing
+ if (!ProcessHashL(payload->iHash))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ DEBUG_LOG(_L("HASH OK!"));
+ break;
+ case RSA_SIG:
+ case DSS_SIG:
+ //Certificate payload processing
+ if (!ProcessCertificateArrayL(payload->iCerts))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //Signature payload processing
+ if (!ProcessSignatureL(payload->iSign))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ProcessVendorL(payload->iVids);
+
+ if ( iNatDiscovery && payload->iNatDs->Count() ) {
+ iNAT_D_Flags = iNatDiscovery->CheckDiscoveryPayloadsL(payload->iNatDs, iChosenProposal_I.iAttrList->iHashAlg,
+ (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
+ iLocalAddr, iLastRemoteAddr);
+ if ( iNAT_D_Flags )
+ iLastRemoteAddr.SetPort(FLOATED_IKE_PORT);
+ }
+
+ CleanupStack::PopAndDestroy(); //payload
+
+ iFlags |= ISAKMP_HDR_EFLAG; //From now on encryption is used
+ IsakmpPhase1CompletedL();
+ iStage = 4;
+ return ETrue;
+
+}
+
+
+//Process payloads appearing in Stage 5. Order NOT relevant. ONLY for MAIN Mode
+// Preshared and signatures: HDR, KE, Nr
+// RSA Encr. : HDR, <IDir>PubKey_i, <Nr_b>PubKey_i
+// RSA Revised Encr. : HDR, <Nr_b>PubKey_i , <KE_b>Ke_r, <IDir>Ke_r
+TBool CIkev1Negotiation::ProcessStage4L(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+
+ if ( payload->iSa || !payload->iNonce || !payload->iKe )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
+
+ if ((auth_method == RSA_SIG) || (auth_method == DSS_SIG))
+ {
+ //Process the possible CR payloads
+ if ( !ProcessCertificateReqArrayL(payload->iCertReqs) )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ }
+
+ //Key Payload processing (all methods)
+ if (!ProcessKeyL(payload->iKe))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ if ( auth_method != PRE_SHARED ) {
+ if (!ProcessCertificateArrayL(payload->iCerts)) {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ }
+ TNonceISAKMP* nonce_payload = (TNonceISAKMP*)payload->iNonce;
+
+ //Nonce Payload processing (all modes)
+ TBool Status = ProcessNonceL(nonce_payload);
+ if (!Status)
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ ProcessVendorL(payload->iVids);
+
+ if ( iNatDiscovery && payload->iNatDs->Count() )
+ {
+ iNAT_D_Flags = iNatDiscovery->CheckDiscoveryPayloadsL(payload->iNatDs, iChosenProposal_I.iAttrList->iHashAlg,
+ (TUint8*)iCookie_I.Ptr(), (TUint8*)iCookie_R.Ptr(),
+ iLocalAddr, iLastRemoteAddr);
+ if ( iNAT_D_Flags )
+ iLastRemoteAddr.SetPort(FLOATED_IKE_PORT);
+ }
+
+ if ( auth_method == IKE_A_CRACK )
+ {
+ /*---------------------------------------------------
+ * Process message: HDR, [CERT], KEr, Nr, SIG
+ * - Verify CRACK signature and if OK
+ * - Initialize CRACK authentication
+ *---------------------------------------------------*/
+ Status = ComputeKeysL();
+ if ( Status ) //Generates keying material for encryption stages
+ {
+ Status = ProcessSignatureL(payload->iSign);
+ if ( Status ) //Signature payload processing and checking
+ {
+ Status = StartCRACKAuthL();
+ }
+ }
+ }
+ else
+ {
+ iStage = 5;
+ }
+
+ CleanupStack::PopAndDestroy(); //payload
+ return Status;
+}
+
+
+//Process payloads appearing in Stage 6. Order NOT relevant
+TBool CIkev1Negotiation::ProcessStage5L(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+ if ( payload->iSa || payload->iNonce || payload->iKe )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
+ TIdentISAKMP* id_payload = NULL;
+ // ID Payload processing (all modes)
+ if ( payload->iIds->Count() != 1 )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ id_payload = (TIdentISAKMP*)payload->iIds->At(0); // The first ID
+ if (!CheckIdentL(id_payload))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //Save the ident payload for HASH_I/R computing
+ delete iPeerIdentPayload;
+ iPeerIdentPayload = NULL;
+ iPeerIdentPayloadSize=id_payload->GetLength()-sizeof(TPayloadISAKMP);
+ iPeerIdentPayload = new (ELeave) TUint8[iPeerIdentPayloadSize];
+ Mem::Copy(iPeerIdentPayload,((TUint8 *)id_payload)+sizeof(TPayloadISAKMP),iPeerIdentPayloadSize);
+
+ //Mode-dependent processing
+ switch (auth_method)
+ {
+ case PRE_SHARED:
+ //Hash payload processing
+ if (!ProcessHashL(payload->iHash))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ DEBUG_LOG(_L("HASH OK!"));
+ break;
+ case RSA_SIG:
+ case DSS_SIG:
+ //Certificate payload processing
+ if (!ProcessCertificateArrayL(payload->iCerts))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ if (!CertifyRemoteIdentityL(id_payload))
+ {
+ DEBUG_LOG(_L("ProcessStage5L RSA_SIG CertifyRemoteIdentityL failed"));
+ DEBUG_LOG(_L("AUTHENTICATION_FAILED"));
+ SetErrorStatus( KKmdIkeAuthFailedErr );
+ SendNotifyL(AUTHENTICATION_FAILED);
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //Signature payload processing and checking
+ if (!ProcessSignatureL(payload->iSign))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //Process the possible CR payloads (needed if we are responder)
+ if ( iRole == RESPONDER )
+ {
+ if ( !ProcessCertificateReqArrayL(payload->iCertReqs) )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ if ( !iOwnCert )
+ ReadOwnCertL(); // Peer does not required a specific cert. Get any
+ }
+ break;
+
+ default:
+ break;
+ }//end switch
+
+ // Process notification payloads
+ TInt i = 0;
+ while ( i < payload->iNotifs->Count() ) {
+ if (!ProcessNotificationL(payload->iNotifs->At(i)))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ i ++;
+ }
+
+ ProcessVendorL(payload->iVids);
+
+ CleanupStack::PopAndDestroy(); //payload
+
+ if (iExchange == ISAKMP_EXCHANGE_ID)
+ iStage = 6;
+ else iStage = 3;
+
+ return ETrue;
+}
+
+
+//Process payloads appearing in Stage 6(Main) Order NOT relevant
+TBool CIkev1Negotiation::ProcessStage6L(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+ if ( payload->iSa || payload->iNonce || payload->iKe )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+
+ TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
+ TIdentISAKMP* id_payload = NULL;
+ //ID Payload processing (all modes)
+ if ( payload->iIds->Count() != 1 )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ id_payload = (TIdentISAKMP*)payload->iIds->At(0); // The first ID
+ if (!CheckIdentL(id_payload))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //Save the ident payload for HASH_I/R computing
+ delete iPeerIdentPayload;
+ iPeerIdentPayload = NULL;
+ iPeerIdentPayloadSize=id_payload->GetLength()-sizeof(TPayloadISAKMP);
+ iPeerIdentPayload = new (ELeave) TUint8[iPeerIdentPayloadSize];
+ Mem::Copy(iPeerIdentPayload,((TUint8 *)id_payload)+sizeof(TPayloadISAKMP),iPeerIdentPayloadSize);
+
+ //Mode-dependent processing
+ switch (auth_method)
+ {
+ case PRE_SHARED:
+ //Hash payload processing
+ if (!ProcessHashL(payload->iHash))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ DEBUG_LOG(_L("HASH OK!"));
+ break;
+ case RSA_SIG:
+ case DSS_SIG:
+ //Certificate payload processing
+ if (!ProcessCertificateArrayL(payload->iCerts))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ if (!CertifyRemoteIdentityL(id_payload))
+ {
+ DEBUG_LOG(_L("ProcessStage6L RSA_SIG CertifyRemoteIdentityL failed"));
+ DEBUG_LOG(_L("AUTHENTICATION_FAILED"));
+ SetErrorStatus( KKmdIkeAuthFailedErr );
+ SendNotifyL(AUTHENTICATION_FAILED);
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //Signature payload processing and checking
+ if (!ProcessSignatureL(payload->iSign))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Process notification payloads
+ TInt i = 0;
+ while ( i < payload->iNotifs->Count() ) {
+ if (!ProcessNotificationL(payload->iNotifs->At(i)))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ i ++;
+ }
+
+ ProcessVendorL(payload->iVids);
+ // Internal address payload processing
+ ProcessIntAddrL(payload->iIaddr);
+
+ CleanupStack::PopAndDestroy(); //payload
+
+ return IsakmpPhase1CompletedL();
+
+}
+
+//Process payloads appearing in Stage 7 = CRACK authentication going
+TBool CIkev1Negotiation::ProcessStage7L(const ThdrISAKMP &aHdr)
+{
+TBool status = ETrue;
+TInt crack_status;
+
+ if ( iCRACKneg ) {
+ crack_status = iCRACKneg->ExecuteCRACKMsgL(aHdr);
+
+ switch ( crack_status ) {
+
+ case CRACK_SUCCESS:
+ /*-------------------------------------------------------
+ * CRACK authentication has been succesfully completed
+ * Take actions to start Quick mode negotiation
+ *------------------------------------------------------*/
+ delete iCRACKneg;
+ iCRACKneg = NULL;
+ iLastIV.Copy(iIV); //Saves last IV in Phase 1
+ DEBUG_LOG(_L("Last IV Saved!"));
+ IsakmpPhase1CompletedL();
+ break;
+
+ case CRACK_CONTINUE:
+ /*----------------------------------------------------------
+ * CRACK authentication continues, no further actions needed
+ *----------------------------------------------------------*/
+ break;
+
+ case CRACK_IGNORE_MSG:
+ /*----------------------------------------------------------
+ * CRACK authentication continues, received message ignored
+ *----------------------------------------------------------*/
+ status = EFalse;
+ break;
+
+ default:
+ /*----------------------------------------------------------
+ * CRACK authentication failed, negotiation failed
+ *----------------------------------------------------------*/
+ LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
+ R_VPN_MSG_VPN_GW_AUTH_FAIL,
+ KKmdIkeAuthFailedErr,
+ iPluginSession->VpnIapId(),
+ &iRemoteAddr );
+ status = EFalse;
+ SetErrorStatus(KKmdIkeAuthFailedErr);
+ AcquireSAErrorResponse(KKmdIkeAuthFailedErr);
+ break;
+
+ }
+ }
+
+ return status;
+}
+
+//Called as a RESPONDER for PHASE_II
+//Checks HASH(1),SA,KE,NONCE,[ID,ID] from INITIATOR
+TBool CIkev1Negotiation::ProcessStage1Phase2L(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+
+ if ( !payload->iSa || !payload->iNonce )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ //Payload Processing
+
+ CProposal_IIList *recv_proposals = new (ELeave) CProposal_IIList(1); //, *prop
+ CleanupStack::PushL(recv_proposals);
+
+ //SA
+ if (!ProcessSAL(payload->iSa, (TUint8 *)recv_proposals))
+ {
+ CleanupStack::PopAndDestroy(2); //recv_proposals + payload
+ return EFalse;
+ }
+
+ //ID payloads (if existing)
+ if ( payload->iIds->Count() == 2 )
+ {
+ if (!ProcessStage1_II_IDsL(payload->iIds->At(0), payload->iIds->At(1), recv_proposals))
+ {
+ CleanupStack::PopAndDestroy(2); //recv_proposals + payload
+ return EFalse;
+ }
+ }
+ else if ( payload->iIds->Count() != 0 )
+ {
+ DEBUG_LOG(_L("Unsupported Phase II ID payload count"));
+ CleanupStack::PopAndDestroy(2); //recv_proposals + payload
+ return EFalse;
+ }
+ //After ID to know what address to search in the "SAD"
+ TInt err = BuildProposals2L();
+ if (err != KErrNone)
+ {
+ DEBUG_LOG(_L("NO_PROPOSAL_CHOSEN: No policy matching"));
+ SendNotifyL(NO_PROPOSAL_CHOSEN);
+ CleanupStack::PopAndDestroy(2); //recv_proposals + payload
+ return EFalse;
+ }
+
+ //Contains the transform nums matching if multiple proposals
+ CTransModifierList *trans_array = new (ELeave) CTransModifierList(1);
+ CleanupStack::PushL(trans_array);
+
+ TInt num = iProposal_IIList->MultiMatchL(recv_proposals, iRole == RESPONDER, trans_array);//If RESPONDER relaxed comparison (no lifetimes checked)
+ iProposalNum = num; // Set to num not 1
+#ifdef _DEBUG
+ TBuf<128> err_buf;
+#endif
+ if (num < 0)
+ {
+#ifdef _DEBUG
+ err_buf.Copy(_L("NO_PROPOSAL_CHOSEN: Phase II proposal not accepted - "));
+ AppendAttributeError(num, err_buf);
+ DEBUG_LOG(err_buf);
+#endif
+ SetErrorStatus( KKmdIkeNoProposalErr );
+ SendNotifyL(NO_PROPOSAL_CHOSEN);
+ CleanupStack::PopAndDestroy(3); //transarray + recv_proposals + payload
+ return EFalse;
+ }
+
+ //Copy the chosen transform
+ //Actually is the same one as recv_proposals because we should always receive a single proposal with a single transform.
+ CreateChosenProposalL(recv_proposals, num, trans_array);
+
+ CleanupStack::PopAndDestroy(2); //transarray + recv_proposals
+
+ //Process the possible NOTIFICATION payloads
+ for (TInt i = 0; i < payload->iNotifs->Count(); i++)
+ {
+ if (!ProcessNotificationL(payload->iNotifs->At(i)))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ }
+
+ if (!ProcessNonceL(payload->iNonce))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //HASH
+ if (!ProcessHash2L(aHdr, payload->iHash, payload->iPadding))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ if (!ProcessKeyL(payload->iKe))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ ProcessVendorL(payload->iVids);
+
+ if ( iNatDiscovery ) {
+ iNAT_D_Flags |= iNatDiscovery->GetPeerOriginalAddress(payload->iNatOa, iRemoteOriginalAddr, iChosenProp_IIList);
+ }
+
+ CleanupStack::PopAndDestroy(); //notif_payload_array
+ iStage = 2;
+ return ETrue;
+}
+
+//Creates a new proposal list from a selected part of another list (Certain proposal and transforms)
+void CIkev1Negotiation::CreateChosenProposalL(CProposal_IIList* aPropList, TInt aPropNum, CTransModifierList *aTransArray)
+{
+
+ CProposal_II *prop, *new_propII;
+ TAttrib_II *attr_II;
+ TChosenAttrib_II *new_attr_II;
+ TInt count = aPropList->Count();
+ TInt i = 0;
+ TInt j = 0;
+
+ //
+ // Find selected proroposal from list
+ //
+ while ( i < count)
+ {
+ if (aPropList->At(i)->iProposalNum == aPropNum)
+ break;
+ i ++;
+ }
+
+ TTransModifier *tmodif;
+ TInt64 own_time, own_bytes, peer_time, peer_bytes;
+ delete iChosenProp_IIList; //Must be erased because can contain data from previous retransmissions
+ iChosenProp_IIList = NULL;
+ iChosenProp_IIList = new (ELeave) CProposal_IIList(1);
+ while ( i < count )
+ {
+ prop = aPropList->At(i);
+ if ( prop->iProposalNum != aPropNum)
+ break; // Stop, another Proposal
+
+ new_propII = new (ELeave) CProposal_II();
+ CleanupStack::PushL(new_propII);
+ new_propII->ConstructL(1);
+ iChosenProp_IIList->AppendL(new_propII);
+ CleanupStack::Pop(); //new_propII safe
+
+ new_propII->iProtocol = prop->iProtocol;
+ new_propII->iNumTransforms = 1; //We only choose 1 transform for each proposal
+ new_propII->iProposalNum = (TUint8)aPropNum;
+ new_propII->iSPI.Copy(prop->iSPI);
+ tmodif = aTransArray->At(j);
+ new_propII->iReplayWindowLength = tmodif->iReplayWindowLength;
+
+ attr_II = prop->iAttrList->At(tmodif->iTransNum); //look for the chosen transform in the prop
+ ComputeLifetimes_II(tmodif->iReducedLifeSecs, tmodif->iReducedLifeKBytes, own_time, own_bytes);
+ ComputeLifetimes_II(attr_II->iLifeDurationSecs, attr_II->iLifeDurationKBytes, peer_time, peer_bytes);
+
+ //Only copy the chosen transform
+ new_attr_II = new (ELeave) TChosenAttrib_II();
+ CleanupStack::PushL(new_attr_II);
+ new_attr_II->Copy(*attr_II);
+ if ((peer_time > own_time) && (own_time != 0))
+ {
+ new_attr_II->iReducedLifeSecs.Set(tmodif->iReducedLifeSecs);
+ DEBUG_LOG1(_L("Lifetime bigger than the one set. Reducing to %d"), own_time);
+ }
+ else
+ new_attr_II->iReducedLifeSecs.Set(NULL, 0);
+
+ if ((peer_bytes > own_bytes) && (own_bytes != 0))
+ {
+ new_attr_II->iReducedLifeKBytes.Set(tmodif->iReducedLifeKBytes);
+ DEBUG_LOG1(_L("Lifesize bigger than the one set. Reducing to %d"), own_bytes);
+ }
+ else
+ new_attr_II->iReducedLifeKBytes.Set(NULL, 0);
+
+ new_propII->iAttrList->AppendL(new_attr_II);
+ CleanupStack::Pop(); //new_attrII safe
+
+ j++; // Next transform modifer
+ i++; // Next proposal
+
+ }
+}
+
+
+//Called as a INITIATOR for PHASE_II
+//Checks HASH(1),SA, ,NONCE,[KE] [ID,ID] from RESPONDER
+TBool CIkev1Negotiation::ProcessStage2Phase2L(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+
+ if ( !payload->iSa || !payload->iNonce )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ CProposal_IIList *recv_proposals = new (ELeave) CProposal_IIList(1);
+ CleanupStack::PushL(recv_proposals);
+
+ //SA
+ if (!ProcessSAL(payload->iSa, (TUint8 *)recv_proposals))
+ {
+ CleanupStack::PopAndDestroy(2); //recv_proposals + payload
+ return EFalse;
+ }
+
+ //ID payloads (if existing)
+ if ( payload->iIds->Count() == 2 )
+ {
+ if (!ProcessStage2_II_IDsL(payload->iIds->At(0), payload->iIds->At(1)))
+ {
+ CleanupStack::PopAndDestroy(2); //recv_proposals + payload
+ return EFalse;
+ }
+ }
+ else if ( payload->iIds->Count() != 0 )
+ {
+ DEBUG_LOG(_L("Unsupported Phase II ID payload count"));
+ CleanupStack::PopAndDestroy(2); //recv_proposals + payload
+ return EFalse;
+ }
+
+ //Contains the transform nums matching if multiple proposals
+ CTransModifierList *trans_array = new (ELeave) CTransModifierList(1);
+ CleanupStack::PushL(trans_array);
+
+ //Check the received proposals match the proposed one (Got from
+ //acquire msg.)
+ TInt num = iProposal_IIList->MultiMatchL(recv_proposals, iRole == RESPONDER, trans_array);//If RESPONDER relaxed comparison (no lifetimes checked)
+#ifdef _DEBUG
+ TBuf<128> err_buf;
+#endif
+ if (num < 0)
+ {
+#ifdef _DEBUG
+ err_buf.Copy(_L("BAD_PROPOSAL_SYNTAX: Phase II reply doesn't match proposal - "));
+ AppendAttributeError(num, err_buf);
+ DEBUG_LOG(err_buf);
+#endif
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ CleanupStack::PopAndDestroy(3); //transarray + recv_proposals + payload
+ return EFalse;
+ }
+ // iProposalNum set to correspond local proposal numbering
+ iProposalNum = trans_array->At(0)->iPropNum;
+ //Copy the chosen transform
+ CreateChosenProposalL(recv_proposals, num, trans_array);
+
+ CleanupStack::PopAndDestroy(2); //transarray + recv_proposals
+
+ //Process the possible NOTIFICATION payloads
+ for (TInt i = 0; i < payload->iNotifs->Count(); i++)
+ {
+ if (!ProcessNotificationL(payload->iNotifs->At(i)))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ }
+ //NONCE
+ if (!ProcessNonceL(payload->iNonce))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //HASH
+ if (!ProcessHash2L(aHdr, payload->iHash, payload->iPadding))
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+ //KEY (if present (PFS))
+ if (!ProcessKeyL(payload->iKe))
+ {
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(PAYLOAD_MALFORMED);
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ ProcessVendorL(payload->iVids);
+
+ if ( iNatDiscovery ) {
+ iNAT_D_Flags |= iNatDiscovery->GetPeerOriginalAddress(payload->iNatOa, iRemoteOriginalAddr, iChosenProp_IIList);
+ }
+
+ if ( iRecvFlags & ISAKMP_HDR_CFLAG ) //Commit Bit set
+ {
+ iFlags |= ISAKMP_HDR_CFLAG; //Sets the Commit bit if this side set it else
+ DEBUG_LOG(_L("SAD update delayed until CONNECTED received"));
+ }
+
+ CleanupStack::PopAndDestroy(); //payload
+ iStage = 3;
+ return ETrue;
+
+}
+
+TBool CIkev1Negotiation::ProcessStage3Phase2L(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+
+ if ( payload->iSa || payload->iNonce || payload->iKe || payload->iIds->Count() != 0 )
+ {
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ //Hash Payload
+ TBool Status = ProcessHash2L(aHdr, payload->iHash, payload->iPadding);
+ if ( Status )
+ {
+ //END OF THE PHASE II (Quick mode) negotiation.
+ //Now we need to update the PFKEY SA database
+ ProcessVendorL(payload->iVids);
+ UpdateSADatabaseL();
+ iStage = 4;
+ }
+ CleanupStack::PopAndDestroy(); //payload
+ return Status;
+}
+
+TBool CIkev1Negotiation::ProcessCONNECTEDL(const ThdrISAKMP &aHdr)
+{
+ CIkev1Payloads* payload = CIkev1Payloads::NewL(aHdr, *this, iDebug);
+ if (!payload)
+ return EFalse;
+
+ CleanupStack::PushL(payload);
+
+ if ( !payload->iHash || (payload->iNotifs->Count() != 1) || payload->iSa ||
+ payload->iNonce || payload->iKe || (payload->iIds->Count() != 0) )
+ {
+ DEBUG_LOG(_L("PAYLOAD_MALFORMED (no hash or notfic payload)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(PAYLOAD_MALFORMED);
+ CleanupStack::PopAndDestroy(); //payload
+ return EFalse;
+ }
+
+ //Checks if the hash value is OK. Here because need the
+ //notification payload
+ const TNotificationISAKMP* notif_payload = payload->iNotifs->At(0);
+ TBool Status = VerifyInformationalHashL(payload->iHash, notif_payload, iMessageId);
+ if ( Status )
+ { //Hash OK
+ if ((notif_payload->GetDOI() == IPSEC_DOI) && (notif_payload->GetMsgType() == CONNECTED))
+ {
+ //END OF THE PHASE II (Quick mode) negotiation.
+ //Now we need to update the PFKEY SA database
+ DEBUG_LOG(_L("CONNECTED message received. Updating SAD"));
+ UpdateSADatabaseL();
+ iStage = 5;
+ }
+ }
+ else
+ {
+ DEBUG_LOG(_L("AUTHENTICATION_FAILED (Informational hash)"));
+ SetErrorStatus( KKmdIkeAuthFailedErr );
+ SendNotifyL(AUTHENTICATION_FAILED);
+ }
+ CleanupStack::PopAndDestroy(); //payload
+ return Status;
+}
+
+//returns KErrNone if OK, otherwise error already treated.
+TBool CIkev1Negotiation::ProcessSAL(const TSAISAKMP *aSA, TUint8 *aRecvProposals)
+{
+ //payload not present
+ if (!aSA)
+ {
+ DEBUG_LOG(_L("NO SA PAYLOAD"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return EFalse;
+ }
+
+ //requires a special length check to know the proposals and transforms are not bigger than
+ //the size specified in th SA
+ TUint32 SALengthLeft = aSA->GetLength() - aSA->Size();
+
+ TUint8 next_payload = aSA->GetPayload();
+ if ((next_payload == ISAKMP_PAYLOAD_P) || (next_payload == ISAKMP_PAYLOAD_T))
+ {
+ DEBUG_LOG(_L("INVALID_PAYLOAD_TYPE (Bad next payload for the SA)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_PAYLOAD_TYPE); // Payloads and transforms are processed as a part of the SA,
+ return EFalse;
+ }
+
+ TUint32 doi=aSA->GetDOI();
+ if (!CheckDOI(doi))
+ {
+ DEBUG_LOG(_L("DOI_NOT_SUPPORTED in SA payload"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(DOI_NOT_SUPPORTED); //send the informational exchange
+ return EFalse;
+ }
+
+ if (iStage == 1)
+ iDOI = doi;
+
+ //Process SITuation depending on DOI. In this implementation always is IPSEC DOI
+ if (!CheckSituationL(aSA->GetSIT()))
+ return EFalse;
+
+ if (aSA->HasLDId()) //If no Labeled Domain Identifier no more processing for the SA
+ {
+ DEBUG_LOG(_L("Label Domain Identifier (LDI) not supported"));
+ return EFalse;
+ }
+
+ if (iPhase==PHASE_I) //Only used in Phase_I. PHASE_II hashs check it directly from received paylaod
+ {
+ //Store the SA payload for further calculations in Hash.
+ if (iRole==RESPONDER)
+ {
+ iSAPayloadSize = aSA->GetLength()-sizeof(TPayloadISAKMP);
+ delete iSAPayload;
+ iSAPayload = NULL;
+ iSAPayload = new (ELeave) (TUint8[iSAPayloadSize]); //Generic payload NOT included
+ Mem::Copy(iSAPayload,(((TUint8 *)aSA)+sizeof(TPayloadISAKMP)),iSAPayloadSize);
+ }
+ }
+ const TPayloadISAKMP *payload = aSA->Payload();
+ return ProcessProposalsL(payload, SALengthLeft, aRecvProposals);
+}
+
+
+//Do the process required for proposals and transforms. The aPayload given must be a proposal
+TBool CIkev1Negotiation::ProcessProposalsL(const TPayloadISAKMP *aPayload, TUint32 aLengthLeft, TUint8 *aRecvProposals)
+{
+ TProposalISAKMP *proposal;
+ const TPayloadISAKMP *ppayload=aPayload;
+ TUint8 payType = ppayload->GetPayload();
+ TUint32 len_left;
+ CProposal_IIList *recv_proposals = (CProposal_IIList *)aRecvProposals;
+ CProposal_II *auxProp_II;
+ TBool found = EFalse; //At least 1 transform matching
+
+ //Many Proposals. The RESPONDER MUST choose a transform for each proposal or reject the
+ //full suite of attributes.
+ do
+ {
+ //General payload check
+ if ((payType != ISAKMP_PAYLOAD_NONE) && (payType != ISAKMP_PAYLOAD_P))
+ {
+ DEBUG_LOG(_L("INVALID_PAYLOAD_TYPE (Bad next payload for the proposal)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_PAYLOAD_TYPE);
+ return EFalse;
+ }
+
+ if (aPayload->GetReserved() != 0) //Must be always 0
+ {
+ DEBUG_LOG(_L("INVALID RESERVED FIELD"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return EFalse;
+ }
+
+ //requires special length check
+ if ((aPayload->GetLength() < MIN_ISAKMP_PAYLOAD_SIZE) || (aPayload->GetLength() > aLengthLeft))
+ {
+ DEBUG_LOG(_L("BAD PAYLOAD SIZE"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return EFalse;
+ }
+
+ aLengthLeft -= aPayload->GetLength(); //dcreases the length of the prop. and its transforms
+
+ proposal = TProposalISAKMP::Ptr(ppayload);
+
+
+ if (!CheckProtocolL(proposal->GetProtocol()))
+ return EFalse;
+
+ if (!CheckSPIL(proposal))
+ return EFalse;
+
+ len_left = proposal->GetLength() - (proposal->Size() + proposal->GetSPISize());
+ //len_left contains the length of the transforms only
+
+ TInt ret = KErrNotFound;
+ if (iPhase == PHASE_I)
+ {
+ iChosenProposal_I.iProtocol = proposal->GetProtocol();
+ iChosenProposal_I.iSPI.Copy((TUint8 *)proposal->SPI(), proposal->GetSPISize());
+ iChosenProposal_I.iNumTransforms = 1; //Phase I only one transf chosen
+ iChosenProposal_I.iProposalNum = proposal->GetNum();//Not compulsory but preferable to speed up the search process in the peer
+
+ ret = ProcessTransformsL(ppayload,len_left);
+
+ if (ret == KErrNone)
+ return ETrue;//valid transform found
+ else if (ret != KErrNotFound)
+ {
+ DEBUG_LOG(_L("ATTRIBUTES_NOT_SUPPORTED"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
+ return EFalse;
+ }
+ //If not found there may be other proposal to check
+ }
+ else //PHASE_II
+ {
+ auxProp_II = new (ELeave) CProposal_II();
+ CleanupStack::PushL(auxProp_II);
+ auxProp_II->ConstructL(proposal->GetNumTrans());
+ auxProp_II->iProtocol=proposal->GetProtocol();
+ auxProp_II->iSPI.Copy((TUint8 *)proposal->SPI(), proposal->GetSPISize());
+ auxProp_II->iNumTransforms = proposal->GetNumTrans(); //Number of transforms in the proposal
+ auxProp_II->iProposalNum = proposal->GetNum(); //Proposal num
+
+ ret = ProcessTransforms2L(ppayload, auxProp_II, len_left);
+ if (ret==KErrNone)//valid transform found
+ {
+ //Adds the new proposal to the list of chosen proposals
+ recv_proposals->AppendL(auxProp_II);
+ CleanupStack::Pop(); //auxProp_II safe
+ found = ETrue;
+ //go for the next proposal
+ }
+ else
+ {
+ DEBUG_LOG(_L("ATTRIBUTES_NOT_SUPPORTED"));
+ SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
+ CleanupStack::PopAndDestroy(); //delete auxProp_II; //delete the current proposal
+ return EFalse;
+ }
+ }
+
+ payType = ppayload->GetPayload();
+ ppayload = ppayload->Next(); //Next payload if there's any
+ } while (payType!=ISAKMP_PAYLOAD_NONE); //Proposal loop
+
+ if (!found)
+ {
+ DEBUG_LOG(_L("ATTRIBUTES_NOT_SUPPORTED"));
+ SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
+ return EFalse;
+ }
+
+ return found;
+}
+
+//processes all the PHASE I transforms. The parameter payload is the proposal containing
+//the transforms to be able to access the # of transforms.
+//Returns if any transform accepted (KErrNone) or not (KErrNotFound) or processing error (KErrGeneral)
+TInt CIkev1Negotiation::ProcessTransformsL(const TPayloadISAKMP *aPayload,TUint32 aLengthLeft)
+{
+ DEBUG_LOG(_L("-> CIkev1Negotiation::ProcessTransformsL()"));
+ TUint16 reason;
+ TInt ret = KErrGeneral;
+ const TTransformISAKMP *transf;
+ const TProposalISAKMP *proposal=TProposalISAKMP::Ptr(aPayload);
+ //First transform. Not Next() because would be the next proposal or non-sa payload
+ const TPayloadISAKMP *tpayload=proposal->Payload();
+
+ TInt payType = tpayload->GetPayload(); //Type of the payload following the first transform
+ transf = TTransformISAKMP::Ptr(tpayload);
+
+ TInt numTransf = (TInt)transf->GetNum(); // First transform number
+ TInt lastTransf = numTransf + (TInt)proposal->GetNumTrans(); // Last transform number
+
+ while ( numTransf < lastTransf )
+ {
+ //only permited payload codes
+ if ((payType != ISAKMP_PAYLOAD_NONE) && (payType != ISAKMP_PAYLOAD_T))
+ {
+ DEBUG_LOG(_L("INVALID_PAYLOAD_TYPE (Bad next payload for the transform)"));
+ SendNotifyL(INVALID_PAYLOAD_TYPE);
+ return KErrGeneral;
+ }
+
+ if (tpayload->GetReserved() != 0) //Must be always 0
+ {
+ DEBUG_LOG(_L("INVALID RESERVED FIELD"));
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return KErrGeneral;
+ }
+
+ //requires special length check
+ if ((tpayload->GetLength() < MIN_ISAKMP_PAYLOAD_SIZE) || (tpayload->GetLength() > aLengthLeft))
+ {
+ DEBUG_LOG(_L("BAD PAYLOAD SIZE"));
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return KErrGeneral;
+ }
+
+ if (!CheckTransformID(PROTO_ISAKMP,transf->GetID()))
+ {
+ DEBUG_LOG(_L(" Continue"));
+
+ numTransf++; // Next supposed transform #
+ payType = tpayload->GetPayload();
+ tpayload = tpayload->Next(); //next payload (transform)
+ transf = TTransformISAKMP::Ptr(tpayload);
+
+ continue; //If fails, transform discarded!, not error
+ }
+
+ if ( transf->GetNum() != numTransf ) //Not the correct #
+ {
+ DEBUG_LOG1(_L("BAD_PROPOSAL_SYNTAX (Non conscutive transform number (%d)"),transf->GetNum());
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrGeneral"));
+ return KErrGeneral;
+ }
+
+ if (transf->GetReserved() != 0 ) //Should be always 0
+ {
+ DEBUG_LOG(_L("INVALID RESERVED FIELD"));
+ SendNotifyL(PAYLOAD_MALFORMED);
+ DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrGeneral"));
+ return KErrGeneral;
+ }
+
+ numTransf++; // Next supposed transform #
+ //Attributes to be checked depending on Transf ID
+ TAttrib attrib;
+
+ ret = ProcessAttributesL(tpayload, &attrib);
+
+ if (ret != KErrNone)
+ {
+ if (ret != KErrNotFound)
+ {
+ DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrGeneral"));
+ return KErrGeneral; //Error in the attributes. Already reported
+ }
+ //Not accepted but correct
+ }
+ else // Accepted attributes if (AttrChosen(attrib))
+ {
+ //Checks the response or proposal is the same as one of our proposals
+ TAttrib *attr_list = iProposal_I.iAttrList;
+ TInt ret = KErrNotFound;
+#ifdef _DEBUG
+ TBuf<256> buf;
+#endif
+ while (attr_list && (ret != KErrNone) )
+ {
+ ret = attrib.Compare(*attr_list, iRole==RESPONDER); //If RESPONDER relaxed comparison (no lifetimes checked)
+ if (ret != KErrNone)
+ {
+#ifdef _DEBUG
+ DEBUG_LOG1(_L("Transform #%d not matching proposal Reason: "), attrib.iTransformNum);
+ AppendAttributeError(ret, buf);
+ DEBUG_LOG(buf);
+#endif
+ }
+ attr_list = attr_list->iNext; //next transform proposed
+
+ }
+ if (ret == KErrNone)
+ {
+ *iChosenProposal_I.iAttrList = attrib;
+ DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrNone"));
+ return KErrNone; //If the attibute are supported there's no need to check more SA
+ }
+ else //No proposal matches
+ {
+ if ( numTransf == lastTransf ) //Is there more transforms to check
+ {
+ if (iRole == INITIATOR)
+ {
+ reason = BAD_PROPOSAL_SYNTAX;
+ DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Phase I reply don't match proposal)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ }
+ else
+ {
+ reason = NO_PROPOSAL_CHOSEN;
+ DEBUG_LOG(_L("NO_PROPOSAL_CHOSEN (Received Proposal doesn't match accepted attributes (Check own proposals))"));
+ }
+ SendNotifyL(reason);
+ DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrNotFound"));
+ return KErrNotFound;
+ }
+ }
+ }
+
+ payType = tpayload->GetPayload();
+ tpayload = tpayload->Next(); //next payload (transform)
+ transf = TTransformISAKMP::Ptr(tpayload);
+
+ }
+
+ DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransformsL() KErrNotFound"));
+
+ //if reaches this point meanse no transform accepted in this proposal
+ //Notifies to the upper level
+ return KErrNotFound;
+
+}
+
+
+//Logs the error when comparing a proposal
+//void CIkev1Negotiation::LogAttributeError(TInt aTransformNum, TInt aErr)
+void CIkev1Negotiation::AppendAttributeError(TInt aErr, TDes &aBuf) const
+{
+#ifndef _DEBUG
+ (void)aErr;
+ (void)aBuf;
+#endif
+
+#ifdef _DEBUG
+ switch (aErr)
+ {
+ case KErrNotFound:
+ aBuf.Append(_L("No proposals\n"));
+ break;
+ case KErrTransformID:
+ aBuf.Append(_L("Different Transform Algorithm\n"));
+ break;
+ case KErrEncrAlg:
+ aBuf.Append(_L("Different Encryption Algorithm\n"));
+ break;
+ case KErrHashAlg:
+ aBuf.Append(_L("Different Hash Algorithm\n"));
+ break;
+ case KErrAuthMethod:
+ aBuf.Append(_L("Different Authentication Method\n"));
+ break;
+ case KErrGroupDesc:
+ aBuf.Append(_L("Different Group Description\n"));
+ break;
+ case KErrGroupType:
+ aBuf.Append(_L("Different Group Type\n"));
+ break;
+ case KErrGroupPrime:
+ aBuf.Append(_L("Different Group Prime\n"));
+ break;
+ case KErrGroupGen1:
+ aBuf.Append(_L("Different Group Generator 1\n"));
+ break;
+ case KErrGroupGen2:
+ aBuf.Append(_L("Different Group Generator 2\n"));
+ break;
+ case KErrGroupCurveA:
+ aBuf.Append(_L("Different Group Curve A\n"));
+ break;
+ case KErrGroupCurveB:
+ aBuf.Append(_L("Different Group Curve A\n"));
+ break;
+ case KErrPRF:
+ aBuf.Append(_L("Different PRF\n"));
+ break;
+ case KErrKeyLength:
+ aBuf.Append(_L("Different Key Length\n"));
+ break;
+ case KErrFieldSize:
+ aBuf.Append(_L("Different Field Size\n"));
+ break;
+ case KErrGroupOrder:
+ aBuf.Append(_L("Different Group Order\n"));
+ break;
+ case KErrLifeTime:
+ aBuf.Append(_L("Different Lifetime\n"));
+ break;
+ case KErrLifeSize:
+ aBuf.Append(_L("Different LifeSize\n"));
+ break;
+ case KErrEncMode:
+ aBuf.Append(_L("Different Encapsulation Mode\n"));
+ break;
+ case KErrAuthAlg:
+ aBuf.Append(_L("Different Authentication Algorithm\n"));
+ break;
+ case KErrKeyRounds:
+ aBuf.Append(_L("Different Key Rounds\n"));
+ break;
+ case KErrComprDicSize:
+ aBuf.Append(_L("Different Compress Dictionary Size\n"));
+ break;
+ case KErrComprPrivAlg:
+ aBuf.Append(_L("Different Compress Private Algorithm\n"));
+ break;
+ case KErrTransformNum:
+ aBuf.Append(_L("Different Transform Num.\n"));
+ break;
+ case KErrPropProtocol:
+ aBuf.Append(_L("Proposals have different protocol.\n"));
+ break;
+ case KErrNoTransforms:
+ aBuf.Append(_L("Proposal has no transforms \n"));
+ break;
+ case KErrNoRemoteProposals:
+ aBuf.Append(_L("Remote Proposals list is empty\n"));
+ break;
+ case KErrNoLocalProposals:
+ aBuf.Append(_L("Local Proposals list is empty\n"));
+ break;
+ case KErrPropNumberMismatch:
+ aBuf.Append(_L("The proposals lists have diferent number of AND'd proposals"));
+ break;
+ default:
+ aBuf.Append(_L("Unknown\n"));
+ }
+#endif
+}
+
+//processes all the PHASE II transforms. The parameter payload is the proposal containing
+//the transforms to be able to access the # of transforms.
+//Returns if any transform accepted (KErrNone) or not (KErrNotFound) or processing error (KErrGeneral)
+TInt CIkev1Negotiation::ProcessTransforms2L(const TPayloadISAKMP *aPayload,CProposal_II *aProp,TUint32 aLengthLeft)
+{
+ DEBUG_LOG(_L("-> CIkev1Negotiation::ProcessTransforms2L()"));
+
+ TInt ret;
+ const TTransformISAKMP *transf;
+ const TProposalISAKMP *proposal=TProposalISAKMP::Ptr(aPayload);
+ //First transform. Not Next() because would be the next proposal or non-sa payload
+ const TPayloadISAKMP *tpayload = proposal->Payload();
+
+ TAttrib_II *attr_II = NULL;
+ TInt payType = tpayload->GetPayload(); //Type of the payload following the first transform
+ transf = TTransformISAKMP::Ptr(tpayload);
+
+ TInt numTransf = (TInt)transf->GetNum(); // First transform number
+ TInt lastTransf = numTransf + (TInt)proposal->GetNumTrans(); // Last transform number
+
+ while ( numTransf < lastTransf )
+ {
+ //only permited payload codes
+ if ((payType != ISAKMP_PAYLOAD_NONE) && (payType != ISAKMP_PAYLOAD_T))
+ {
+ DEBUG_LOG(_L("INVALID_PAYLOAD_TYPE (Bad next payload for the transform)"));
+ SendNotifyL(INVALID_PAYLOAD_TYPE);
+ return KErrGeneral;
+ }
+
+ if (tpayload->GetReserved() != 0) //Must be always 0
+ {
+ DEBUG_LOG(_L("INVALID RESERVED FIELD"));
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return KErrGeneral;
+ }
+
+ //requires special length check
+ if ((tpayload->GetLength() < MIN_ISAKMP_PAYLOAD_SIZE) || (tpayload->GetLength() > aLengthLeft))
+ {
+ DEBUG_LOG(_L("BAD PAYLOAD SIZE"));
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return KErrGeneral;
+ }
+
+ if (!CheckTransformID(aProp->iProtocol,transf->GetID()))
+ {
+ DEBUG_LOG(_L(" Transform doesn't match, moving on to the next one"));
+ payType = tpayload->GetPayload();
+ tpayload = tpayload->Next(); //next payload (transform)
+ transf = TTransformISAKMP::Ptr(tpayload);
+ numTransf++; // Next supposed transform #
+ continue; //If fails, transform discarded!, not error
+ }
+
+ if ( transf->GetNum() != numTransf ) //Not the correct #. Must be consecutive
+ {
+ DEBUG_LOG1(_L("BAD_PROPOSAL_SYNTAX (Non conscutive transform number (%d)"),transf->GetNum());
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ return KErrGeneral;
+ }
+
+ if (transf->GetReserved() != 0 ) //Must be always 0
+ {
+ DEBUG_LOG(_L("INVALID RESERVED FIELD"));
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return KErrGeneral;
+ }
+
+ //Attributes to be checked depending on Transf ID
+ attr_II = new (ELeave) TAttrib_II();
+ CleanupStack::PushL(attr_II);
+ ret = ProcessAttributes2L(tpayload, attr_II, aProp->iProtocol);
+ if (ret != KErrNone) //Some invalid attribute
+ {
+ DEBUG_LOG(_L(" Invalid attribute"));
+ CleanupStack::PopAndDestroy(); //delete attr_II;
+ if (ret != KErrNotFound)
+ return KErrGeneral; //Error in the attributes. Already reported
+ else //Invalid transform- Ignored
+ {
+ DEBUG_LOG2(_L("Transform %d of proposal %d ignored"), transf->GetNum(), aProp->iProposalNum);
+ }
+ }
+ else //Accepted, must check if really proposed or if matches the configuration if RESPONDER
+ {
+ DEBUG_LOG(_L(" Adding new attribute"));
+ aProp->iAttrList->AppendL(attr_II); //Add the new attribute
+ CleanupStack::Pop(); //attr_II saf
+ }
+ payType = tpayload->GetPayload();
+ tpayload = tpayload->Next(); //next payload (transform)
+ transf = TTransformISAKMP::Ptr(tpayload);
+ numTransf++; // Next supposed transform #
+
+ }
+
+ //No valid transform found
+ if (aProp->iAttrList->Count() == 0)
+ {
+ DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransforms2L() KErrNotFound"));
+ return KErrNotFound;
+ }
+
+ DEBUG_LOG(_L("<- CIkev1Negotiation::ProcessTransforms2L() KErrNone"));
+ return KErrNone;
+}
+
+
+//Copies and checks the values of the attributes. The parameter aPayload must be a transform
+//aAttrib will contain the sent attributes if the return value is KErrNone, otherwise should be
+//ignore because there was an error reading them (KErrGeneral) or the transform was not accepted (KErrNotFound)
+TInt CIkev1Negotiation::ProcessAttributesL(const TPayloadISAKMP *aPayload, TAttrib *aAttrib)
+{
+ const TTransformISAKMP *transf = TTransformISAKMP::Ptr(aPayload);
+ TInt length= aPayload->GetLength() - sizeof(*transf); //To process the attribs
+ TDataISAKMP *attr= transf->SAAttrib();
+
+ aAttrib->iTransformNum = transf->GetNum();
+ aAttrib->iTransformID = transf->GetID();
+ //
+ // Store parameters for extended authentication
+ //
+ aAttrib->iXauthUsed = iHostData->iUseXauth;
+ aAttrib->iRole = iRole;
+
+ TUint16 lifeType = 0; //No type assigned yet
+ TUint32 lifeValue = 0; //No value assigned yet
+ TUint16 val;
+
+ while ( length>0 )
+ {
+ length = length - attr->Size();
+ if (length<0) //Mismatch between lengths!!!
+ {
+ DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Length mismatch in the attibutes)"));
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ return KErrGeneral;
+ }
+ switch (attr->Type())
+ {
+ case OAKLEY_ATTR_TYPE_ENCR_ALG:
+ if (!CheckEncrAlg(attr->Value()))
+ return KErrNotFound;
+ aAttrib->iEncrAlg=attr->Value();
+ break;
+ case OAKLEY_ATTR_TYPE_HASH_ALG:
+ if (!CheckHashAlg(attr->Value()))
+ return KErrNotFound;
+ aAttrib->iHashAlg = attr->Value();
+ break;
+ case OAKLEY_ATTR_TYPE_AUTH_METH:
+ val = CTransNegotiation::GetAuthMethod(attr->Value(), iHostData->iUseXauth, iRole);
+ if (!CheckAuthMethod(val))
+ return KErrNotFound;
+ aAttrib->iAuthMethod = val;
+ break;
+ case OAKLEY_ATTR_TYPE_GROUP_DESC:
+ if (!CheckGroupDesc(attr->Value()))
+ return KErrNotFound;
+ aAttrib->iGroupDesc = attr->Value();
+ break;
+ case OAKLEY_ATTR_TYPE_GROUP_TYPE:
+ if (!CheckGroupType(attr->Value()))
+ return KErrNotFound;
+ aAttrib->iGroupType = attr->Value();
+ break;
+ case OAKLEY_ATTR_TYPE_GROUP_PRIME:
+ if (attr->IsBasic())
+ {
+ val = attr->Value();
+ aAttrib->iGroupPrime.Copy((TUint8*)&val, sizeof(val));
+ }
+ else
+ aAttrib->iGroupPrime.Copy(attr->VarValue(),attr->Length());
+ break;
+ case OAKLEY_ATTR_TYPE_GROUP_GEN1:
+ if (attr->IsBasic())
+ {
+ val = attr->Value();
+ aAttrib->iGroupGen1.Copy((TUint8*)&val, sizeof(val));
+ }
+ else
+ aAttrib->iGroupGen1.Copy(attr->VarValue(),attr->Length());
+ break;
+ case OAKLEY_ATTR_TYPE_GROUP_GEN2:
+ if (attr->IsBasic())
+ {
+ val = attr->Value();
+ aAttrib->iGroupGen2.Copy((TUint8*)&val, sizeof(val));
+ }
+ else
+ aAttrib->iGroupGen2.Copy(attr->VarValue(),attr->Length());
+ break;
+ case OAKLEY_ATTR_TYPE_GROUP_CRVA:
+ if (attr->IsBasic())
+ {
+ val = attr->Value();
+ aAttrib->iGroupCurveA.Copy((TUint8*)&val, sizeof(val));
+ }
+ else
+ aAttrib->iGroupCurveA.Copy(attr->VarValue(),attr->Length());
+ break;
+ case OAKLEY_ATTR_TYPE_GROUP_CRVB:
+ if (attr->IsBasic())
+ {
+ val = attr->Value();
+ aAttrib->iGroupCurveB.Copy((TUint8*)&val, sizeof(val));
+ }
+ else
+ aAttrib->iGroupCurveB.Copy(attr->VarValue(),attr->Length());
+ break;
+ case OAKLEY_ATTR_TYPE_LIFE_TYPE:
+ lifeType = attr->Value();
+ if (!CheckLifeType(lifeType))
+ {
+ DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Invalid lifetime type)"));
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ return KErrGeneral;
+ }
+ break;
+ case OAKLEY_ATTR_TYPE_LIFE_DUR:
+ if (attr->IsBasic())
+ {
+ lifeValue = ByteOrder::Swap32(attr->Value());
+ if (lifeType == SECONDS)
+ aAttrib->iLifeDurationSecs.Copy((TUint8 *)&lifeValue, sizeof(lifeValue));
+ else if (lifeType == KBYTES)
+ aAttrib->iLifeDurationKBytes.Copy((TUint8 *)&lifeValue, sizeof(lifeValue));
+ else
+ {
+ DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Invalid lifetime type)"));
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ return KErrGeneral;
+ }
+ }
+ else //Not basic
+ {
+ if (lifeType == SECONDS)
+ aAttrib->iLifeDurationSecs.Copy(attr->VarValue(),attr->Length());
+ else if (lifeType == KBYTES)
+ aAttrib->iLifeDurationKBytes.Copy(attr->VarValue(),attr->Length());
+ else
+ {
+ DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Invalid lifetime type)"));
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ return KErrGeneral;
+ }
+ }
+ break;
+ case OAKLEY_ATTR_TYPE_PRF:
+ if (!CheckPRF(attr->Value()))
+ return KErrNotFound;
+ aAttrib->iPRF=attr->Value();
+ break;
+ case OAKLEY_ATTR_TYPE_KEY_LEN:
+ aAttrib->iKeyLength = attr->Value();
+ break;
+ case OAKLEY_ATTR_TYPE_FIELD_SIZE:
+ if (!CheckFieldSize(attr->Value()))
+ return KErrNotFound;
+ aAttrib->iFieldSize=attr->Value();
+ break;
+ case OAKLEY_ATTR_TYPE_GROUP_ORDER:
+ if (attr->IsBasic())
+ {
+ val = attr->Value();
+ aAttrib->iGroupOrder.Copy((TUint8*)&val, sizeof(val));
+ }
+ else
+ aAttrib->iGroupOrder.Copy(attr->VarValue(),attr->Length());
+ break;
+ default:
+ DEBUG_LOG(_L("ATTRIBUTES_NOT_SUPPORTED (Invalid attribute number)"));
+ SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
+ return KErrGeneral;
+ }
+ attr = attr->Next();
+ }
+
+ //Done here to ensure both received
+ if (aAttrib->iKeyLength !=0)
+ if (!CheckKeyLength(aAttrib->iKeyLength, (TUint8)aAttrib->iEncrAlg ,PROTO_ISAKMP))
+ return KErrNotFound;
+
+ return KErrNone;
+}
+
+//Copies and checks the values of the attributes. The parameter aPayload must be a transform
+//aAttrib will contain the sent attributes if the return value is KErrNone, otherwise should be
+//ignore because there was an error reading them (KErrGeneral) or the transform was not accepted (KErrNotFound)
+TInt CIkev1Negotiation::ProcessAttributes2L(const TPayloadISAKMP *aPayload, TAttrib_II *aAttrib,TUint8 aProtocol)
+{
+ const TTransformISAKMP *transf = TTransformISAKMP::Ptr(aPayload);
+ TInt length= aPayload->GetLength() - sizeof(*transf); //To process the attribs
+
+ aAttrib->iTransformNum = transf->GetNum();
+ aAttrib->iTransformID = transf->GetID();
+ TDataISAKMP *attr= transf->SAAttrib();
+ TUint16 lifeType = 0; //No type assigned yet
+ TUint32 lifeValue = 0;
+ while (length>0)
+ {
+ length = length - attr->Size();
+ if (length<0) //Mismatch between lengths!!!
+ {
+ DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Length mismatch in the attibutes)"));
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ return KErrGeneral;
+ }
+ switch (attr->Type())
+ {
+ case DOI_ATTR_TYPE_LIFE_TYPE:
+ lifeType=attr->Value();
+ if (!CheckLifeType(lifeType))
+ {
+ DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (Invalid lifetime type)"));
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ return KErrGeneral;
+ }
+ break;
+ case DOI_ATTR_TYPE_LIFE_DUR:
+ if (attr->IsBasic())
+ {
+ lifeValue = ByteOrder::Swap32(attr->Value());
+ if (lifeType==SECONDS)
+ aAttrib->iLifeDurationSecs.Copy((TUint8 *)&lifeValue, sizeof(lifeValue));
+ else if (lifeType==KBYTES)
+ aAttrib->iLifeDurationKBytes.Copy((TUint8 *)&lifeValue, sizeof(lifeValue));
+ else
+ {
+ DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (No lifetime type received)"));
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ return KErrGeneral;
+ }
+ }
+ else
+ {
+ if (lifeType==SECONDS)
+ aAttrib->iLifeDurationSecs.Copy(attr->VarValue(),attr->Length());
+ else if (lifeType==KBYTES)
+ aAttrib->iLifeDurationKBytes.Copy(attr->VarValue(),attr->Length());
+ else
+ {
+ DEBUG_LOG(_L("BAD_PROPOSAL_SYNTAX (No lifetime type received)"));
+ SendNotifyL(BAD_PROPOSAL_SYNTAX);
+ return KErrGeneral;
+ }
+ }
+ lifeType = 0; //Cannot received another lifetime without setting the type again
+ break;
+ case DOI_ATTR_TYPE_GROUP_DESC:
+ if (!CheckGroupDesc(attr->Value()))
+ return KErrNotFound;
+ aAttrib->iGroupDesc=attr->Value();
+ break;
+ case DOI_ATTR_TYPE_ENC_MODE: //Encapsulation Mode
+ if (!CheckEncMode(attr->Value()))
+ return KErrNotFound;
+ aAttrib->iEncMode=attr->Value();
+ if ( aAttrib->iEncMode == UDP_ENC_TUNNEL || aAttrib->iEncMode == UDP_RFC_ENC_TUNNEL )
+ aAttrib->iEncMode = DOI_TUNNEL;
+ if ( aAttrib->iEncMode == UDP_ENC_TRANSPORT || aAttrib->iEncMode == UDP_RFC_ENC_TRANSPORT)
+ aAttrib->iEncMode = DOI_TRANSPORT;
+ break;
+ case DOI_ATTR_TYPE_AUTH_ALG:
+ if (!CheckAuthAlg(attr->Value()))
+ return KErrNotFound;
+ aAttrib->iAuthAlg=attr->Value();
+ break;
+ case DOI_ATTR_TYPE_KEY_LEN:
+ aAttrib->iKeyLength = attr->Value();
+ break;
+ case DOI_ATTR_TYPE_KEY_ROUNDS:
+ aAttrib->iKeyRounds=attr->Value();
+ break;
+/*
+ case DOI_ATTR_TYPE_COMP_DIC_SIZE: //Compress Dictionary size
+ aAttrib->iComprDicSize=attr->Value();
+ break;
+ case DOI_ATTR_TYPE_COMP_PRIV_ALG: //Compress Dictionary size
+ aAttrib->iComprPrivAlg=attr->Value();
+ break;
+*/
+ default:
+ DEBUG_LOG(_L("ATTRIBUTES_NOT_SUPPORTED (Invalid attribute number)"));
+ SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
+ return KErrGeneral;
+ }
+ attr = attr->Next();
+ }
+
+ if (lifeType != 0) //Type set but not sent
+ {
+ DEBUG_LOG(_L("Lifetime type set but value not sent!"));
+ return KErrNotFound;
+ }
+
+ if (aAttrib->iKeyLength !=0)
+ if (!CheckKeyLength(aAttrib->iKeyLength,transf->GetID(),aProtocol)) //Check key length correct
+ return KErrNotFound;
+
+ return KErrNone;
+}
+
+
+//returns KErrNone if OK, otherwise error already treated.
+TBool CIkev1Negotiation::ProcessKeyL(const TKeyISAKMP *aKey)
+{
+ //const TKeyISAKMP *key = TKeyISAKMP::Ptr(aPayload);
+
+ //payload not present
+ if (iPhase==PHASE_I)
+ {
+ if (!aKey)
+ {
+ DEBUG_LOG(_L("NO KEY Payload"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return EFalse;
+ }
+ //Doesn't return yet because it needs to copy the received key
+ }
+ else //PHASE_II
+ {
+ if (!aKey)
+ {
+ if (iPFS)
+ {
+ DEBUG_LOG(_L("KEY Payload Expected (PFS is enabled)"));
+ return EFalse;
+ }
+
+ return ETrue;
+
+ }
+ else // Key present
+ {
+ if (!iPFS)
+ {
+ DEBUG_LOG(_L("KEY Payload NOT Expected (PFS is disabled)"));
+ return EFalse;
+ }
+ //Doesn't return yet because it needs to copy the received key
+ }
+ }
+
+ //stores the public key sent by the other peer. Only if key received and PFS enabled (PHASE II only)
+ iPeerPublicKey.Copy(aKey->KeyData(), aKey->GetLength() - sizeof(*aKey));
+ return ETrue;
+}
+
+
+TBool CIkev1Negotiation::ProcessNonceL(const TPayloadISAKMP *aPayload)
+{
+ const TNonceISAKMP *nonce = TNonceISAKMP::Ptr(aPayload);
+
+ //payload not present
+ if (!nonce)
+ {
+ DEBUG_LOG(_L("NO NONCE PAYLOAD"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return EFalse;
+ }
+
+ //stores the nonce sent by the other peer
+ if (iRole==RESPONDER)
+ iNONCE_I.Copy(nonce->NonceData(),nonce->NonceDataLen());
+ else
+ iNONCE_R.Copy(nonce->NonceData(),nonce->NonceDataLen());
+
+ return ETrue;
+
+}
+
+TBool CIkev1Negotiation::ProcessStage1_II_IDsL(const TIdentISAKMP *aInit_ID_payload,const TIdentISAKMP *aResp_ID_payload, CProposal_IIList *aRecv_proposals)
+{
+ //IDci
+ //First we check the received IDs to be able to build the proposals for phase_II
+ TInt32 addr; //Contains a numeric IPv4 addr to be sent
+ TBuf<40> addr_buf; //Contains a text IPv4/IPv6 addr to be sent
+
+ TIp6Addr ip6addr; //IPV6 raw address
+
+ //We receive the peer proxy address or gateway client
+ TAttrib_II *attr_II = aRecv_proposals->At(0)->iAttrList->At(0);
+ if (aInit_ID_payload) //ID Payload received
+ {
+ iIDReceived = ETrue;
+ if (!CheckIdentL(aInit_ID_payload))
+ return EFalse;
+
+ iIDRemotePort = aInit_ID_payload->GetPort();
+ iIDProtocol = aInit_ID_payload->GetProtocol();
+ iRemoteIDType_II = aInit_ID_payload->GetIDType();
+
+ switch (aInit_ID_payload->GetIDType())
+ {
+ case ID_IPV4_ADDR:
+ Mem::Copy((TUint8 *)&addr, aInit_ID_payload->IDData(),sizeof(TInt32));
+ iRemoteAddr1_ID_II.SetAddress(ByteOrder::Swap32(addr));
+ DEBUG_LOG(_L("Remote ID received"));
+ DEBUG_LOG(_L("Setting Remote ID to:"));
+ iRemoteAddr1_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(addr_buf);
+ if (iRemoteAddr.Match(iRemoteAddr1_ID_II))
+ iDefaultRemoteID = ETrue; //Must be sent but won't be used when updating the SAD
+ else if (attr_II->iEncMode == DOI_TRANSPORT)
+ {
+ DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Received ID MUST match the Remote addr in Transport mode)"));
+ SendNotifyL(ADDRESS_NOTIFICATION);
+ return EFalse;
+ }
+ break;
+ case ID_IPV4_ADDR_SUBNET:
+ Mem::Copy((TUint8 *)&addr, aInit_ID_payload->IDData(),sizeof(TInt32)); //Address
+ iRemoteAddr1_ID_II.SetAddress(ByteOrder::Swap32(addr));
+ Mem::Copy((TUint8 *)&addr, aInit_ID_payload->IDData() + sizeof(TInt32),sizeof(TInt32)); //Mask
+ iRemoteAddr2_ID_II.SetAddress(ByteOrder::Swap32(addr));
+ iRemoteAddr1_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(_L("Setting Remote ID to: addr = "));
+ DEBUG_LOG(addr_buf);
+ iRemoteAddr2_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(_L(" mask = "));
+ DEBUG_LOG(addr_buf);
+ if (PrefixLen(iRemoteAddr2_ID_II) < KErrNone) //Invalid Mask (can't be > 32 bec. we get only 4 bytes)
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Invalid peer proxy mask for type ID_IPV4_ADDR_SUBNET)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ if (attr_II->iEncMode == DOI_TRANSPORT)
+ {
+ if (!iRemoteAddr.Match(iRemoteAddr1_ID_II, iRemoteAddr2_ID_II))
+ {
+ DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Remote ID MUST match the net & mask received)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(ADDRESS_NOTIFICATION);
+ return EFalse;
+ }
+ }
+ break;
+ case ID_IPV6_ADDR:
+ Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
+ iRemoteAddr1_ID_II.SetAddress(ip6addr);
+ DEBUG_LOG(_L("Remote ID received"));
+ DEBUG_LOG(_L("Setting Remote ID to:"));
+ iRemoteAddr1_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(addr_buf);
+ if (iRemoteAddr.Match(iRemoteAddr1_ID_II))
+ iDefaultRemoteID = ETrue; //Must be sent but won't be used when updating the SAD
+ else if (attr_II->iEncMode == DOI_TRANSPORT)
+ {
+ DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Remote ID doesn't match received IDi in Transport mode)"));
+ SendNotifyL(ADDRESS_NOTIFICATION);
+ return EFalse;
+ }
+ break;
+ case ID_IPV6_ADDR_SUBNET:
+ Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8)); //Address
+ iRemoteAddr1_ID_II.SetAddress(ip6addr);
+ Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData() + sizeof(ip6addr.u.iAddr8), sizeof(ip6addr.u.iAddr8)); //Mask
+ iRemoteAddr2_ID_II.SetAddress(ip6addr);
+ DEBUG_LOG(_L("Remote ID (subnet) received"));
+ DEBUG_LOG(_L("Setting Remote ID to: addr = "));
+ iRemoteAddr1_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(addr_buf);
+ DEBUG_LOG(_L(" mask = "));
+ iRemoteAddr2_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(addr_buf);
+ if (PrefixLen(iRemoteAddr2_ID_II) < 0) //Invalid Mask
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Invalid peer proxy mask for type ID_IPV6_ADDR_SUBNET)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ if (attr_II->iEncMode == DOI_TRANSPORT)
+ {
+ if (!iRemoteAddr.Match(iRemoteAddr1_ID_II, iRemoteAddr2_ID_II))
+ {
+ DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Remote ID MUST match the net & mask received)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(ADDRESS_NOTIFICATION);
+ return EFalse;
+ }
+ }
+ break;
+ default: //redundant. Detected in CheckIdentL()
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (ID Type not supported)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }//switch
+ }
+ else //No id received (That means we're negotiating directly with the end host) (RFC 2409 5.5)
+ {
+ //For TRANSPORT we don't need to do anything
+ iIDLocalPort = 0;
+ iIDRemotePort = 0;
+ iIDProtocol = 0;
+ return ETrue; //No need to check the Responder ID if no Initiator ID received
+ }
+
+ //IDcr
+ //Receive our proxy. We don't know it because we are responders so the other peer tells us who does it
+ //want to communicate with
+ if (aResp_ID_payload) //ID Payload received
+ {
+ if (!CheckIdentL(aResp_ID_payload))
+ return EFalse;
+
+ iIDLocalPort = aResp_ID_payload->GetPort();
+ iLocalIDType_II = aResp_ID_payload->GetIDType();
+ if (iIDProtocol != aResp_ID_payload->GetProtocol()) //Must be the same sent in the IDCi
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Local ID Protocol different from Remote ID Protocol. Must be the same)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+
+ switch (aResp_ID_payload->GetIDType())
+ {
+ case ID_IPV4_ADDR:
+ Mem::Copy((TUint8 *)&addr, aResp_ID_payload->IDData(),sizeof(TInt32));
+ iLocalAddr1_ID_II.SetAddress(ByteOrder::Swap32(addr));
+ DEBUG_LOG(_L("Local ID received"));
+ DEBUG_LOG(_L("Setting Local ID to:"));
+ iLocalAddr1_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(addr_buf);
+ if ( iInternalAddr ) {
+ //
+ // Check ID against internal address instead of local address
+ //
+ if (iInternalAddr->iClientIntAddr.Match(iLocalAddr1_ID_II))
+ iDefaultLocalID = ETrue; //Must be sent but won't be used when updating the SAD
+ }
+ else {
+ if (iLocalAddr.Match(iLocalAddr1_ID_II))
+ iDefaultLocalID = ETrue; //Must be sent but won't be used when updating the SAD
+ else if (attr_II->iEncMode == DOI_TRANSPORT)
+ {
+ DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Local ID MUST match the net & mask received)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(ADDRESS_NOTIFICATION);
+ return EFalse;
+ }
+ }
+ break;
+ case ID_IPV4_ADDR_SUBNET:
+ Mem::Copy((TUint8 *)&addr, aResp_ID_payload->IDData(),sizeof(TInt32)); //Address
+ iLocalAddr1_ID_II.SetAddress(ByteOrder::Swap32(addr));
+ Mem::Copy((TUint8 *)&addr, aResp_ID_payload->IDData() + sizeof(TInt32),sizeof(TInt32)); //Mask
+ iLocalAddr2_ID_II.SetAddress(ByteOrder::Swap32(addr));
+ DEBUG_LOG(_L("Setting Local ID to: addr = "));
+ iLocalAddr1_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(addr_buf);
+ DEBUG_LOG(_L(" mask = "));
+ iLocalAddr2_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(addr_buf);
+ if (PrefixLen(iLocalAddr2_ID_II) < 0) //Invalid Mask
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Invalid Remote ID mask for type ID_IPV4_ADDR_SUBNET)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ if (attr_II->iEncMode == DOI_TRANSPORT)
+ {
+ if (!iLocalAddr.Match(iLocalAddr1_ID_II, iLocalAddr2_ID_II))
+ {
+ DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Local ID MUST match the net & mask received)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(ADDRESS_NOTIFICATION);
+ return EFalse;
+ }
+ }
+ break;
+ case ID_IPV6_ADDR:
+ Mem::Copy(&ip6addr.u.iAddr8, aResp_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
+ //iOwnProxyAddr.SetAddress(ip6addr);
+ iLocalAddr1_ID_II.SetAddress(ip6addr);
+ DEBUG_LOG(_L("Own Proxy received"));
+ DEBUG_LOG(_L("Setting Own Proxy address to:"));
+ iLocalAddr1_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(addr_buf);
+ if (iLocalAddr.Match(iLocalAddr1_ID_II))
+ iDefaultLocalID = ETrue; //Must be sent but won't be used when updating the SAD
+ else if (attr_II->iEncMode == DOI_TRANSPORT)
+ {
+ DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Local ID MUST match the net & mask received)"));
+ SendNotifyL(ADDRESS_NOTIFICATION);
+ return EFalse;
+ }
+ break;
+ case ID_IPV6_ADDR_SUBNET:
+ Mem::Copy(&ip6addr.u.iAddr8, aResp_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8)); //Address
+ iLocalAddr1_ID_II.SetAddress(ip6addr);
+ Mem::Copy(&ip6addr.u.iAddr8, aResp_ID_payload->IDData() + sizeof(ip6addr.u.iAddr8), sizeof(ip6addr.u.iAddr8)); //Mask
+ iLocalAddr2_ID_II.SetAddress(ip6addr);
+ DEBUG_LOG(_L("Setting Own Proxy to: addr = "));
+ iLocalAddr1_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(addr_buf);
+ DEBUG_LOG(_L(" mask = "));
+ iLocalAddr2_ID_II.OutputWithScope(addr_buf);
+ DEBUG_LOG(addr_buf);
+ if (PrefixLen(iLocalAddr2_ID_II) < 0) //Invalid Mask
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Invalid Remote ID mask for type ID_IPV6_ADDR_SUBNET)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ if (attr_II->iEncMode == DOI_TRANSPORT)
+ {
+ if (!iLocalAddr.Match(iLocalAddr1_ID_II, iLocalAddr2_ID_II))
+ {
+ DEBUG_LOG(_L("ADDRESS_NOTIFICATION (Local ID MUST match the net & mask received)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(ADDRESS_NOTIFICATION);
+ return EFalse;
+ }
+ }
+ break;
+ default:
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (ID Type not supported)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }//switch
+ }
+
+ return ETrue;
+}
+
+
+TBool CIkev1Negotiation::ProcessStage2_II_IDsL(const TIdentISAKMP *aInit_ID_payload,const TIdentISAKMP *aResp_ID_payload)//, CProposal_IIList *aRecv_proposals)
+{
+ TInt32 addr4_int; //Contains a numeric IPv4 addr to be sent
+ TIp6Addr ip6addr; //IPV6 raw address
+ TInetAddr tmp_addr;
+ //Here we check the initator proxy (Our client) sent by us has been received correctly
+ if (aInit_ID_payload) //ID Payload received
+ {
+ if (!CheckIdentL(aInit_ID_payload))
+ return EFalse;
+
+ if (aInit_ID_payload->GetPort() != iIDLocalPort)
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Local ID Port different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+
+ if (aInit_ID_payload->GetProtocol() != iIDProtocol)
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Local ID Protocol different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+
+ switch (aInit_ID_payload->GetIDType())
+ {
+ case ID_IPV4_ADDR:
+ Mem::Copy((TUint8 *)&addr4_int,aInit_ID_payload->IDData(),sizeof(TInt32));
+ tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));
+ if (!tmp_addr.Match(iLocalAddr1_ID_II))
+ {
+ DEBUG_LOG(_L("Wrong Own ID received (Different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ break;
+ case ID_IPV4_ADDR_SUBNET:
+ //Subnet
+ Mem::Copy((TUint8 *)&addr4_int,aInit_ID_payload->IDData(),sizeof(TInt32));
+ tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));
+ if (!tmp_addr.Match(iLocalAddr1_ID_II))
+ {
+ //The ID subnet is not the one we sent!
+ DEBUG_LOG(_L("Wrong Own ID subnet received (Different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ //Mask
+ Mem::Copy((TUint8 *)&addr4_int,aInit_ID_payload->IDData() + sizeof(TInt32),sizeof(TInt32));
+ tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));
+ if (!tmp_addr.Match(iLocalAddr2_ID_II))
+ {
+ //The ID mask is not the one we sent!
+ DEBUG_LOG(_L("Wrong Own ID mask received (Different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ break;
+ case ID_IPV6_ADDR:
+ Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
+ tmp_addr.SetAddress(ip6addr);
+ if (!tmp_addr.Match(iLocalAddr1_ID_II))
+ {
+ //The ID is not the one we sent!
+ DEBUG_LOG(_L("Wrong Local ID received (Different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ break;
+ case ID_IPV6_ADDR_SUBNET:
+ //subnet
+ Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
+ tmp_addr.SetAddress(ip6addr);
+ if (!tmp_addr.Match(iLocalAddr1_ID_II))
+ {
+ //The ID is not the one we sent!
+ DEBUG_LOG(_L("Wrong Local ID subnet received (Different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ //mask
+ Mem::Copy(&ip6addr.u.iAddr8, aInit_ID_payload->IDData() + sizeof(ip6addr.u.iAddr8), sizeof(ip6addr.u.iAddr8));
+ tmp_addr.SetAddress(ip6addr);
+ if (!tmp_addr.Match(iLocalAddr2_ID_II))
+ {
+ //The ID is not the one we sent!
+ DEBUG_LOG(_L("Wrong Local ID mask received (Different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ break;
+ default:
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (ID Type not supported)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }//switch
+ }
+ else //No id sent (That means we're negotiating directly with the end host
+ {
+ if (!iLocalAddr1_ID_II.IsUnspecified())
+ {
+ DEBUG_LOG(_L("IDci expected and not received!"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ }
+
+ //We receive the peer (responder) proxy address or gateway client
+ if (aResp_ID_payload) //ID Payload received
+ {
+ if (!CheckIdentL(aResp_ID_payload))
+ return EFalse;
+
+ if (aResp_ID_payload->GetPort() != iIDRemotePort)
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Remote Port different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+
+ if (aResp_ID_payload->GetProtocol() != iIDProtocol)
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Responder ID Protocol different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+
+ switch (aResp_ID_payload->GetIDType())
+ {
+ case ID_IPV4_ADDR:
+ Mem::Copy((TUint8 *)&addr4_int,aResp_ID_payload->IDData(),sizeof(TInt32));
+ tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));
+ DEBUG_LOG(_L("IDcr received"));
+ if (!iRemoteAddr1_ID_II.Match(tmp_addr))
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Wrong Remote ID, doesn't match sent one)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ break;
+ case ID_IPV4_ADDR_SUBNET:
+ //subnet address
+ Mem::Copy((TUint8 *)&addr4_int,aResp_ID_payload->IDData(),sizeof(TInt32));
+ tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));
+ if (!tmp_addr.Match(iRemoteAddr1_ID_II))
+ {
+ //The ID subnet is not the one we sent!
+ DEBUG_LOG(_L("Wrong Remote ID subnet received (Different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ //Mask address
+ Mem::Copy((TUint8 *)&addr4_int,aResp_ID_payload->IDData() + sizeof(TInt32),sizeof(TInt32));
+ tmp_addr.SetAddress(ByteOrder::Swap32(addr4_int));
+ if (!tmp_addr.Match(iRemoteAddr2_ID_II))
+ {
+ //The ID mask is not the one we sent!
+ DEBUG_LOG(_L("Wrong Remote ID mask received (Different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ break;
+ case ID_IPV6_ADDR:
+ Mem::Copy(&ip6addr.u.iAddr8,aResp_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
+ tmp_addr.SetAddress(ip6addr);
+ DEBUG_LOG(_L("IDcr received"));
+ if (!iRemoteAddr1_ID_II.Match(tmp_addr))
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Wrong ID, doesn't match sent proxy)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ break;
+ case ID_IPV6_ADDR_SUBNET:
+ //subnet
+ Mem::Copy(&ip6addr.u.iAddr8, aResp_ID_payload->IDData(), sizeof(ip6addr.u.iAddr8));
+ tmp_addr.SetAddress(ip6addr);
+ if (!tmp_addr.Match(iRemoteAddr1_ID_II))
+ {
+ //The ID is not the one we sent!
+ DEBUG_LOG(_L("Wrong Remote ID subnet received (Different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ //mask
+ Mem::Copy(&ip6addr.u.iAddr8, aResp_ID_payload->IDData() + sizeof(ip6addr.u.iAddr8), sizeof(ip6addr.u.iAddr8));
+ tmp_addr.SetAddress(ip6addr);
+ if (!tmp_addr.Match(iRemoteAddr2_ID_II))
+ {
+ //The ID is not the one we sent!
+ DEBUG_LOG(_L("Wrong Remote ID mask received (Different from the one sent)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ break;
+ default: //Only these 2 modes make sense no reason for subnets or range
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION (Remote ID Type not supported)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }//switch
+ }
+ else //No id sent (That means we're negotiating directly with the end host. We check it's TRUE!
+ {
+ if (!iRemoteAddr1_ID_II.IsUnspecified())
+ {
+ DEBUG_LOG(_L("IDcr expected and not received!"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ }
+
+ return ETrue;
+}
+
+
+TBool CIkev1Negotiation::CheckIdentL(const TPayloadISAKMP *aPayload)
+{
+ const TIdentISAKMP *ident = TIdentISAKMP::Ptr(aPayload);
+
+ //payload not present
+ if (!ident)
+ {
+ DEBUG_LOG(_L("NO ID PAYLOAD"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return EFalse;
+ }
+
+ if (iPhase == PHASE_I)
+ {
+ TUint8 protocol = ident->GetProtocol();
+ if ((protocol != KProtocolInetUdp) && (protocol != 0))
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION: Bad Phase I Protocol (Only UDP(17) or 0 accepted)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ TUint16 port = ident->GetPort();
+ if ((port != 0) && (port != IKE_PORT) && (port != FLOATED_IKE_PORT) )
+ {
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION: Invalid Bad Phase I Port. (Only 0, 500 or 4500 accepted)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ switch (ident->GetIDType())
+ {
+ case ID_IPV4_ADDR:
+ case ID_IPV6_ADDR:
+ case ID_FQDN:
+ case ID_USER_FQDN:
+ case ID_DER_ASN1_DN:
+ break;
+ default:
+ DEBUG_LOG(_L("INVALID_ID_INFORMATION: Invalid Type (Only IPV4/IPV6/User FQDN and DER ASN1 DN accepted in PHASE I)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_ID_INFORMATION);
+ return EFalse;
+ }
+ }
+
+ return ETrue;
+}
+
+
+//Certificate Request Payload processing (all modes).
+TBool CIkev1Negotiation::ProcessCertificateReqL(const TCertificateReqISAKMP *aCertReq)
+{
+ if (iChosenProposal_I.iAttrList->iAuthMethod == IKE_A_CRACK)
+ {
+ DEBUG_LOG(_L("CR ignored when CRACK auth !)"));
+ return ETrue;
+ }
+
+ TInt ret = CheckEncodingL(aCertReq->GetEncoding());
+ switch (ret)
+ {
+ case KErrGeneral:
+ return EFalse;
+ case KErrNotSupported: //Not supported but not an error, just ignored
+ return ETrue;
+ }
+
+ if ( !iPkiService )
+ return EFalse;
+ TBool Status = EFalse;
+ //
+ // No specific CA asked. Find a certificate using own trusted CA list
+ //
+ if ( ReadOwnCertL())
+ {
+ Status = ETrue;
+ }
+
+ return Status;
+
+}
+
+
+//Certificate Request Payload(s) processing (all modes).
+TBool CIkev1Negotiation::ProcessCertificateReqArrayL(const CArrayFixFlat<const TCertificateReqISAKMP *> *aCRPayloadArray)
+{
+
+ TInt count = aCRPayloadArray->Count();
+ if ( count == 0 )
+ {
+ return ETrue; // No Certificate requests
+ }
+
+ if ( ProcessCertificateReqL(aCRPayloadArray->At(0)) )
+ {
+ DEBUG_LOG(_L("User Certificate required by peer found"));
+ iSendCert = ETrue; //Requires sending our cert in next interchange where allowed/expected, otherwise not sent
+ return ETrue;
+ }
+ HBufC8* CAName = NULL;
+ CIkeCaList* trustedCaList = iPkiService->CaList();
+
+ TInt Status=0;
+
+ for (TInt i=0; i < trustedCaList->Count(); i++)
+ {
+ CIkeCaElem* CaElem = (*trustedCaList)[i];
+
+ CAName = IkeCert::GetCertificateFieldDERL(CaElem->Certificate(), KSubjectName);
+ CleanupStack::PushL(CAName);
+
+ TRAP_IGNORE(Status=iPkiService->ReadChainL(iHostData, CAName));
+
+ CleanupStack::PopAndDestroy(CAName);
+ CAName=NULL;
+
+ if ( Status == KErrNone )
+ {
+ delete iOwnCert;
+ iOwnCert = iPkiService->GetCertificate();
+
+ iICA1 = iPkiService->GetTrustedICA1();
+
+ iICA2 = iPkiService->GetTrustedICA2();
+
+ iPeerTrustedCA = iPkiService->GetTrustedCA();
+
+ iSendCert = ETrue; //Requires sending our cert in next interchange where allowed/expected, otherwise not sent
+
+ DEBUG_LOG(_L("Certificate chain Found!"));
+ return ETrue;
+ }
+ }
+
+
+ if ( Status == KVpnErrInvalidCaCertFile)
+ {
+ SetErrorStatus(KVpnErrInvalidCaCertFile);
+
+ SendNotifyL(CERTIFICATE_UNAVAILABLE);
+
+ DEBUG_LOG(_L("Certificate chain read failed!"));
+
+ return EFalse;
+ }
+
+ SetErrorStatus(KKmdIkeNoCertFoundErr);
+ SendNotifyL(CERTIFICATE_UNAVAILABLE);
+ DEBUG_LOG(_L("Certificate Chain r!"));
+
+ return EFalse;
+}
+
+//Certificate Payload(s) processing (all modes).
+TBool CIkev1Negotiation::ProcessCertificateArrayL(CArrayFixFlat<const TCertificateISAKMP *>* aCertArray)
+{
+ TBool Status;
+ if ( iCertRequested )
+ Status = EFalse;
+ else Status = ETrue;
+
+ if ( iPkiService && aCertArray->Count() )
+ {
+ const CIkeCaList* trustedCaList = iPkiService->CaList();
+ CX509Certificate* PeerCert = IkePkiUtils::VerifyCertificateL(*aCertArray,
+ *trustedCaList);
+ if ( PeerCert )
+ {
+ delete iPeerX509Cert;
+ iPeerX509Cert = PeerCert;
+ DEBUG_LOG(_L("Peer Certificate is OK"));
+ Status = ETrue;
+ }
+ else
+ {
+ Status = EFalse;
+ DEBUG_LOG(_L("Peer Certificate is rejected"));
+ }
+ }
+
+ return Status;
+}
+
+//Checks the signature sent by the peer host
+TBool CIkev1Negotiation::ProcessSignatureL(const TSignatureISAKMP *aSigPayload)
+{
+ TBool ret;
+ //payload not present
+ if (!aSigPayload || !iPeerX509Cert )
+ {
+ DEBUG_LOG(_L("NO SIG PAYLOAD"));
+ SetErrorStatus(KKmdIkePeerAuthFailed);
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return EFalse;
+ }
+
+ //DSS only allows SHA1 as hash
+ TUint16 tmp = iChosenProposal_I.iAttrList->iHashAlg;
+ if (iChosenProposal_I.iAttrList->iAuthMethod==DSS_SIG)
+ iChosenProposal_I.iAttrList->iHashAlg = HASH_SHA1;
+
+ TBuf8<ISAKMP_HASH_SIZE> hash;
+ //Verify the peer signature
+ if (iRole==RESPONDER)
+ {
+ ComputeHash1L(hash); //Computes the value of iHASH_I the signature checking
+ //Nothing else to compute.
+ }
+ else //Initiator
+ {
+ ComputeHashrL(hash); //Computes the value of CRACK digest for signature checking
+ }
+
+ ret = VerifySignatureL(iPeerX509Cert, (TUint8 *)hash.Ptr(), hash.Length(), aSigPayload->SigData(),aSigPayload->GetDataLength());
+
+ //restores the value of the Hash alg.
+ iChosenProposal_I.iAttrList->iHashAlg = tmp;
+
+ if (!ret)
+ {
+ DEBUG_LOG(_L("INVALID_SIGNATURE 2"));
+ SetErrorStatus(KKmdIkePeerAuthFailed);
+ SendNotifyL(INVALID_SIGNATURE);
+ return EFalse;
+ }
+
+ DEBUG_LOG(_L("Peer Signature is OK"));
+ return ETrue;
+}
+
+
+TBool CIkev1Negotiation::ProcessHashL(const THashISAKMP *aHashPayload)
+{
+ TBool Status = EFalse;
+ if (aHashPayload)
+ {
+ //Compute peer's hash
+ TBuf8<ISAKMP_HASH_SIZE> hash;
+ if ( (iStage == 6) || ((iStage == 2) && (iExchange == ISAKMP_EXCHANGE_AGGR)))
+ ComputeHashrL(hash);
+ else ComputeHash1L(hash);
+ Status = (Mem::Compare((TUint8 *)hash.Ptr(), hash.Length(), aHashPayload->Data(), aHashPayload->DataLen()) == 0 );
+ if ( !Status )
+ {
+ DEBUG_LOG(_L("INVALID_HASH_INFORMATION"));
+ SendNotifyL(INVALID_HASH_INFORMATION);
+ }
+ }
+
+ return Status;
+}
+
+
+TBool CIkev1Negotiation::ProcessHash2L(const ThdrISAKMP &aHdr, const THashISAKMP *aHashPayload, TUint aPadding)
+{
+ TBool Status = EFalse;
+ if ( aHashPayload )
+ {
+ TUint8* hashMsg = (TUint8*)aHashPayload->Next();
+ TInt hashMsgLen = aHdr.GetLength() - sizeof(aHdr) - aHashPayload->GetLength() - aPadding;
+ Status = VerifyHash2L(aHashPayload, hashMsg, hashMsgLen);
+ if (!Status)
+ {
+ DEBUG_LOG(_L("INVALID_HASH_INFORMATION"));
+ SendNotifyL(INVALID_HASH_INFORMATION);
+ }
+ }
+ else
+ {
+ DEBUG_LOG(_L("PAYLOAD_MALFORMED"));
+ SendNotifyL(PAYLOAD_MALFORMED);
+ }
+
+ return Status;
+}
+
+
+//Check a notification Payload inserted in a normal exchange (MAIN , AGGR, QUICK)
+TBool CIkev1Negotiation::ProcessNotificationL(const TNotificationISAKMP *aNotifPayload)
+{
+ if (!aNotifPayload)
+ return ETrue; //optional so noting happens
+
+ if (!CheckDOI(aNotifPayload->GetDOI()))
+ {
+ DEBUG_LOG(_L("Bad DOI in the NOT payload"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(DOI_NOT_SUPPORTED); //send the informational exchange
+ return EFalse;
+ }
+
+ switch(aNotifPayload->GetMsgType())
+ {
+ case DOI_RESPONDER_LIFETIME:
+ return ProcessResponderLifetimeL(aNotifPayload);
+ case DOI_REPLAY_STATUS:
+ return ProcessReplayStatus(aNotifPayload);
+ case DOI_INITIAL_CONTACT:
+ return ProcessInitialContactL(aNotifPayload);
+ default:
+ DEBUG_LOG(_L("INVALID MESSAGE TYPE in NOT payload"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return EFalse;
+ }
+
+}
+
+
+//Processes a RESPONDER-LIFETIME NOT payload
+TBool CIkev1Negotiation::ProcessResponderLifetimeL(const TNotificationISAKMP *aNotifPayload)
+{
+ TBuf8<2 * ISAKMP_COOKIE_SIZE> spi, own_neg_spi;
+ DEBUG_LOG(_L("Processing RESPONDER-LIFETIME"));
+
+
+ if (!((iPhase == PHASE_II) && (iStage == 2)))
+ {
+ if ( iPhase == PHASE_I ) {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME payload in phase 1, ignored !!"));
+ return ETrue;
+ }
+ else {
+ DEBUG_LOG(_L("Unexpected RESPONDER-LIFETIME payload (Bad stage)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_PAYLOAD_TYPE);
+ return EFalse;
+ }
+ }
+
+ TUint8 protocol = aNotifPayload->GetProtocol();
+ if ((protocol != PROTO_IPSEC_AH) && (protocol != PROTO_IPSEC_ESP) &&
+ (protocol != PROTO_ISAKMP) && (protocol != 0))
+ {
+ DEBUG_LOG(_L("Bad protocol in the RESPONDER-LIFETIME payload"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_PROTOCOL_ID);
+ return EFalse;
+ }
+
+ TUint8 spi_size = aNotifPayload->GetSPISize();
+ CProposal_II *prop;
+ TInt i;
+ //If SPI sent
+ switch (spi_size)
+ {
+ case 2 * ISAKMP_COOKIE_SIZE: //ISAKMP spi
+ spi.Copy(aNotifPayload->GetSPI(), aNotifPayload->GetSPISize());
+ own_neg_spi.Copy(iCookie_I);
+ own_neg_spi.Append(iCookie_R);
+
+ if (spi.Compare(own_neg_spi) != 0)
+ {
+ DEBUG_LOG(_L("Invalid SPI size in the RESPONDER-LIFETIME payload. Payload ignored"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_SPI);
+ return EFalse;
+ }
+ if (iChosenProp_IIList->Count() > 1)
+ {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME ignored. More than one SA (need a IPsec SPI to know which one to use) "));
+ return EFalse;
+ }
+ prop = iChosenProp_IIList->At(0); //Only one proposal
+ break;
+ case 0: //Compatibility with Alchemy cc500
+ if (iChosenProp_IIList->Count() > 1)
+ {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME ignored. More than one SA (need a IPsec SPI to know which one to use) "));
+ return EFalse;
+ }
+
+ prop = iChosenProp_IIList->At(0); //Only one proposal
+ break;
+ case sizeof(TUint32): //IPSEC SPI
+ spi.Copy(aNotifPayload->GetSPI(), aNotifPayload->GetSPISize());
+ prop = NULL; //Only for the debugger, the loop will have at least one proposal
+ for (i = 0; i < iChosenProp_IIList->Count(); i++)
+ {
+ prop = iChosenProp_IIList->At(i); //Only one proposal
+ if (((prop->iSPI.Compare(spi) == 0) && prop->iProtocol == protocol)) //right prop
+ break;
+ }
+
+ if (i == iChosenProp_IIList->Count()) //No prop matches
+ {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME ignored. IPsec SPI doesn't match any chosen proposal"));
+ return EFalse;
+ }
+ break;
+ default:
+ DEBUG_LOG(_L("Bad SPI size in the RESPONDER-LIFETIME payload"));
+ return EFalse;
+ }
+ TAttrib_II *transform = prop->iAttrList->At(0); //Only one transform
+ TInt data_len = aNotifPayload->GetNotifDataSize();
+ TUint8 *data_ptr = aNotifPayload->GetNotifData();
+ TUint16 lifeType = 0; //No type assigned yet
+ TInt64 lifeValue = 0; //No type assigned yet
+ TInt64 lifeValue32;
+ TInt64 curr_lifeValue = 0;
+ TInt32 duration;
+
+ TDataISAKMP *attr = (TDataISAKMP*)data_ptr;
+ while (data_len > 0)
+ {
+ data_len = data_len - attr->Size();
+ if (data_len < 0) //Mismatch between lengths!!!
+ {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME (Length mismatch in the attibutes)"));
+ return EFalse;
+ }
+ switch (attr->Type())
+ {
+ case DOI_ATTR_TYPE_LIFE_TYPE:
+ case OAKLEY_ATTR_TYPE_LIFE_TYPE:
+ lifeType = attr->Value();
+ if (!CheckLifeType(lifeType))
+ {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME (Invalid lifetime type)"));
+ return EFalse;
+ }
+ break;
+ case DOI_ATTR_TYPE_LIFE_DUR:
+ case OAKLEY_ATTR_TYPE_LIFE_DUR:
+ if (attr->IsBasic())
+ {
+ duration = ByteOrder::Swap32(attr->Value());
+ lifeValue = MAKE_TINT64(0, duration);
+ lifeValue32 = I64LOW(lifeValue);
+ if (lifeType == SECONDS)
+ {
+ Desc8ToTInt64(transform->iLifeDurationSecs, curr_lifeValue); //can't fail
+ if (lifeValue < curr_lifeValue)
+ transform->iLifeDurationSecs.Copy((TUint8 *)&lifeValue32, sizeof(lifeValue32));
+ }
+ else if (lifeType == KBYTES)
+ {
+ Desc8ToTInt64(transform->iLifeDurationKBytes, curr_lifeValue); //can't fail
+ if (lifeValue < curr_lifeValue)
+ transform->iLifeDurationKBytes.Copy((TUint8 *)&lifeValue32, sizeof(lifeValue32));
+ }
+ else
+ {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME (Invalid lifetime type)"));
+ return EFalse;
+ }
+ }
+ else //Not basic
+ {
+ TPtrC8 ptr(attr->VarValue(),attr->Length());
+
+ if (lifeType == SECONDS)
+ {
+ if (Desc8ToTInt64(ptr, lifeValue) != KErrNone)
+ {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME Lifetime(Sec) Overflowed Setting to maximum value"));
+ }
+ Desc8ToTInt64(transform->iLifeDurationSecs, curr_lifeValue); //can't fail
+ if (lifeValue < curr_lifeValue)
+ transform->iLifeDurationSecs.Copy(attr->VarValue(),attr->Length());
+ }
+ else if (lifeType == KBYTES)
+ {
+ if (Desc8ToTInt64(ptr, lifeValue) != KErrNone)
+ {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME Lifetime(KBytes) Overflowed Setting to maximum value"));
+ }
+ Desc8ToTInt64(transform->iLifeDurationKBytes, curr_lifeValue); //can't fail
+ if (lifeValue < curr_lifeValue)
+ transform->iLifeDurationKBytes.Copy(attr->VarValue(),attr->Length());
+ }
+ else
+ {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME (Invalid lifetime type)"));
+ return EFalse;
+ }
+ }
+ break;
+ default:
+ DEBUG_LOG1(_L("RESPONDER-LIFETIME (Invalid attribute (%d) received)"), attr->Type());
+ return EFalse;
+ }//switch
+ attr = attr->Next();
+ }//while
+
+ return ETrue;
+}
+
+//Processes a REPLAY-STATUS NOT payload
+TBool CIkev1Negotiation::ProcessReplayStatus(const TNotificationISAKMP *aNotifPayload)
+{
+ TBuf8<2 * ISAKMP_COOKIE_SIZE> spi, own_neg_spi;
+
+ DEBUG_LOG(_L("Processing REPLAY-STATUS"));
+
+ if (!((iPhase == PHASE_II) && ((iStage == 1) || (iStage == 2))))
+ {
+ DEBUG_LOG(_L("Unexpected REPLAY-STATUS payload (Bad stage)"));
+ return EFalse;
+ }
+
+ TUint8 protocol = aNotifPayload->GetProtocol();
+ if ((protocol != PROTO_IPSEC_AH) && (protocol != PROTO_IPSEC_ESP) &&
+ (protocol != PROTO_ISAKMP) && (protocol != 0))
+ {
+ DEBUG_LOG(_L("Bad protocol in the REPLAY-STATUS payload"));
+ return EFalse;
+ }
+
+ TInt i;
+ TUint8 spi_size = aNotifPayload->GetSPISize();
+ CProposal_II *prop;
+ //If SPI sent
+ switch (spi_size)
+ {
+ case 2 * ISAKMP_COOKIE_SIZE: //ISAKMP spi
+ spi.Copy(aNotifPayload->GetSPI(), aNotifPayload->GetSPISize());
+ own_neg_spi.Copy(iCookie_I);
+ own_neg_spi.Append(iCookie_R);
+ if (spi.Compare(own_neg_spi) != 0)
+ {
+ DEBUG_LOG(_L("Invalid SPI size in the REPLAY-STATUS payload. Payload ignored"));
+ return EFalse;
+ }
+ if (iChosenProp_IIList->Count() > 1)
+ {
+ DEBUG_LOG(_L("REPLAY-STATUS ignored. More than one IPsec SA (need an IPsec SPI to know which one to use) "));
+ return EFalse;
+ }
+ break;
+ case 0: //Compatibility with Alchemy cc500
+ if (iChosenProp_IIList->Count() > 1)
+ {
+ DEBUG_LOG(_L("RESPONDER-LIFETIME ignored. More than one SA (need a IPsec SPI to know which one to use) "));
+ return EFalse;
+ }
+ prop = iChosenProp_IIList->At(0); //Only one proposal
+ break;
+ case sizeof(TUint32): //IPSEC SPI
+ spi.Copy(aNotifPayload->GetSPI(), aNotifPayload->GetSPISize());
+ for (i = 0; i < iChosenProp_IIList->Count(); i++)
+ {
+ prop = iChosenProp_IIList->At(i); //Only one proposal
+ if (((prop->iSPI.Compare(spi) == 0) && prop->iProtocol == protocol)) //right prop
+ break;
+ }
+ if (i == iChosenProp_IIList->Count()) //No prop matches
+ {
+ DEBUG_LOG(_L("REPLAY-STATUS ignored. IPsec SPI doesn't match any chosen proposal"));
+ return EFalse;
+ }
+ break;
+ default:
+ DEBUG_LOG(_L("Bad SPI size in the REPLAY-STATUS payload"));
+ return EFalse;
+ }
+
+ TInt data_len = aNotifPayload->GetNotifDataSize();
+ TUint32 *data = (TUint32 *)aNotifPayload->GetNotifData();
+ if (STATIC_CAST(TUint, data_len) < sizeof(*data))
+ {
+ DEBUG_LOG(_L("REPLAY-STATUS (Length mismatch in the attibutes)"));
+ return EFalse;
+ }
+
+#ifdef _DEBUG
+ if (ByteOrder::Swap32(*data) == 0)
+ DEBUG_LOG(_L("Anti-Replay Disabled on Peer Host"));
+ else
+ DEBUG_LOG(_L("Anti-Replay Enabled on Peer Host"));
+#endif
+ return ETrue;
+}
+
+
+TBool CIkev1Negotiation::ProcessInitialContactL(const TNotificationISAKMP *aNotifPayload)
+{
+ TBuf8<2 * ISAKMP_COOKIE_SIZE> spi, neg_spi;
+
+ DEBUG_LOG(_L("Processing INITIAL-CONTACT"));
+ // 7 = CRACK
+ if (!(iPhase == PHASE_I && (iStage == 5 || iStage == 6 || iStage == 7)))
+ {
+ DEBUG_LOG(_L("Unexpected INITIAL-CONTACT payload (Bad stage)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_PAYLOAD_TYPE);
+ return EFalse;
+ }
+
+ if (aNotifPayload->GetProtocol() != PROTO_ISAKMP &&
+ aNotifPayload->GetProtocol() != 0 )
+
+ {
+ DEBUG_LOG(_L("Bad protocol in the INITIAL_CONTACT payload"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_PROTOCOL_ID);
+ return EFalse;
+ }
+
+ if (aNotifPayload->GetSPISize() != 2 * ISAKMP_COOKIE_SIZE)
+ {
+ DEBUG_LOG(_L("Bad SPI size in the INITIAL_CONTACT payload"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_SPI);
+ return EFalse;
+ }
+ spi.Copy(aNotifPayload->GetSPI(), aNotifPayload->GetSPISize());
+ neg_spi.Copy(iCookie_I);
+ neg_spi.Append(iCookie_R);
+
+ if (spi.Compare(neg_spi) != 0)
+ {
+ DEBUG_LOG(_L("Invalid SPI size in the INITIAL_CONTACT payload"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_SPI);
+ return EFalse;
+ }
+
+ if ( iRole == RESPONDER )
+ {
+ // Expired SAs are not returned.
+ TIkev1SAData* sa = iPluginSession->FindIkev1SADataWithAddr( iRemoteAddr );
+ while ( sa != NULL )
+ {
+ iPluginSession->UpdateIkev1SAL( sa->iSAId, ETrue );
+ sa = iPluginSession->FindIkev1SADataWithAddr( iRemoteAddr );
+ }
+
+ // Delete other ongoing negotiations.
+ CIkev1Negotiation* next = iPluginSession->FirstNegotiation();
+ while ( next != NULL )
+ {
+ CIkev1Negotiation* current = next;
+ next = current->iNext;
+ if ( current != this )
+ {
+ delete current;
+ }
+ }
+ }
+
+ return ETrue;
+}
+
+
+
+void CIkev1Negotiation::ProcessVendorL(CArrayFixFlat<const TVendorISAKMP*>* aVids)
+{
+ TBool result;
+ TInt i = 0;
+
+ while ( i < aVids->Count() )
+ {
+ TVendorISAKMP* VendorPayload = (TVendorISAKMP*)aVids->At(i);
+ DEBUG_LOG(_L("Vendor ID received!\nHex: "));
+ DEBUG_LOG_ARRAY(VendorPayload->VIDData(), VendorPayload->GetLength() - sizeof(*VendorPayload));
+
+ if ( iLocalAddr.Family() == KAFUnspec )
+ User::LeaveIfError( iPluginSession->GetLocalAddress( iLocalAddr ) ); // No local address info, get it !
+
+ result = EFalse;
+ iNAT_T_Required = ProcessVendorId(&result,
+ (TUint8*)iCookie_I.Ptr(),
+ (TUint8*)iCookie_R.Ptr(),
+ iLocalAddr,
+ VendorPayload);
+ if ( result )
+ {
+ iFamiliarPeer = result;
+#ifdef _DEBUG
+ DEBUG_LOG(_L("Nokia VPN gateway in peer!"));
+ if ( iNAT_T_Required ) {
+ DEBUG_LOG(_L("NAT Traversal needed!"));
+ if ( !iHostData->iUseNatProbing )
+ DEBUG_LOG(_L(" NAT probe not requested! NAT-T not used!"));
+ }
+#endif // _DEBUG
+ iNAT_T_Required = iNAT_T_Required & iHostData->iUseNatProbing;
+ }
+ else
+ {
+ if ( CheckDPDVendorId(VendorPayload) )
+ {
+ DEBUG_LOG(_L("Peer supports IETF Dead Peer Detection!"));
+ iDPDSupported = ETrue;
+ }
+ else if ( iNatDiscovery )
+ {
+ result = iNatDiscovery->CheckNatVendorId(VendorPayload);
+ if ( result )
+ {
+ DEBUG_LOG(_L("Peer supports IETF (draft-03) NAT Traversal!"));
+ }
+ else
+ {
+ result = iNatDiscovery->CheckRfcNatVendorId(VendorPayload);
+ if ( result )
+ {
+ iVendorIDRfc=ETrue;
+ DEBUG_LOG(_L("Peer supports IETF NAT Traversal!"));
+ }
+ }
+ }
+ }
+
+ i ++;
+ }
+
+}
+
+//
+//Process Internal address payload received
+//
+void CIkev1Negotiation::ProcessIntAddrL(const TINTNETISAKMP *aIntnetPayload)
+{
+ if ( aIntnetPayload && iFamiliarPeer && iHostData->iUseInternalAddr ) {
+ delete iInternalAddr; //delete if already exists (old)
+ iInternalAddr = NULL;
+ iInternalAddr = ProcessIntNetL((TINTNETISAKMP*) aIntnetPayload);
+#ifdef _DEBUG
+ if ( iInternalAddr) {
+ TBuf<80> buf;
+ TBuf<40> txt_addr;
+ iInternalAddr->iClientIntAddr.OutputWithScope(txt_addr);
+ DEBUG_LOG1(_L("Internal address received: %S"),&txt_addr);
+ }
+#endif
+ }
+}
+
+//Computes the hash for phase II
+void CIkev1Negotiation::ComputeHash2L(TDes8& aHash, TInt aStage, const TUint8 *aHashMsg, TInt aHashMsgLen)
+{
+
+ HBufC8* prf_data =
+ HBufC8::NewLC(((aHashMsgLen + iNONCE_I.Length() + iNONCE_R.Length() + (2*sizeof(TUint32))) | 0x3) + 1);
+
+ if (aStage == 3)
+ prf_data->Des().Append(0);
+
+ TUint32 id = ByteOrder::Swap32(iMessageId);
+ prf_data->Des().Append((TUint8*)&id,sizeof(iMessageId));
+ DEBUG_LOG(_L("ID"));
+ switch (aStage)
+ {
+ case 2:
+ prf_data->Des().Append(iNONCE_I);
+ //No break is intended
+ case 1:
+ prf_data->Des().Append(aHashMsg,aHashMsgLen);
+ break;
+ case 3:
+
+ prf_data->Des().Append(iNONCE_I);
+ DEBUG_LOG(_L("iNONCE_I"));
+ prf_data->Des().Append(iNONCE_R);
+ DEBUG_LOG(_L("iNONCE_R"));
+ break;
+ default:
+ CleanupStack::PopAndDestroy(); //prf_data
+ return;
+ }
+
+ DEBUG_LOG1(_L("Hash_II(%d) prf"),aStage);
+
+ ComputePRFL(aHash, iSKEYID_a, prf_data->Des());
+
+ DEBUG_LOG(_L("HASH"));
+
+ CleanupStack::PopAndDestroy(); //prf_data
+
+}
+
+//Computes the hash for a protected informational exchange
+void CIkev1Negotiation::ComputeHashInfL(TDes8& aHash, const TUint8 *aHashMsg, TInt aHashMsgLen)
+{
+
+ HBufC8* prf_data =
+ HBufC8::NewLC(((aHashMsgLen + sizeof(iMessageId)) | 0x3) + 1);
+
+ //prf(SKEYID_a, M_ID | N/D)
+ TUint32 id = ByteOrder::Swap32(iMessageId);
+ prf_data->Des().Append((TUint8*)&id, sizeof(iMessageId));
+
+ prf_data->Des().Append(aHashMsg, aHashMsgLen);
+
+ DEBUG_LOG(_L("Hash_NOT prf"));
+
+ ComputePRFL(aHash, iSKEYID_a, prf_data->Des());
+
+ DEBUG_LOG(_L("HASH"));
+
+ CleanupStack::PopAndDestroy(); //prf_data
+
+}
+
+//Verifies that aHash is correct
+TBool CIkev1Negotiation::VerifyHash2L(const THashISAKMP *aHash,const TUint8 *aHashMsg, TInt aHashMsgLen)
+{
+ TBuf8<ISAKMP_HASH_SIZE> tmp_hash;
+
+ ComputeHash2L(tmp_hash, iStage, aHashMsg, aHashMsgLen); //Computes the specified phase II hash
+
+ TBool Status = (Mem::Compare((TUint8*)tmp_hash.Ptr(), tmp_hash.Length(), aHash->Data(), aHash->DataLen()) == 0);
+
+ return Status;
+}
+
+//Verifies the hash of a Notification or Delete payload
+// Used also to verify the hash of Transaction exchange (Attribute payload)
+TBool CIkev1Negotiation::VerifyInformationalHashL(const THashISAKMP *aHash,const TPayloadISAKMP *aPayload, TUint32 aMessageId)
+{
+ TBuf8<MAX_PRF_LENGTH> tmp_hash;
+
+ TUint32 tmp_id = ByteOrder::Swap32(aMessageId);
+ HBufC8 *prf_buf = HBufC8::NewLC(sizeof(tmp_id) + aPayload->GetLength());
+ prf_buf->Des().Copy((TUint8 *)&tmp_id , sizeof(tmp_id));
+ prf_buf->Des().Append((TUint8 *)aPayload, aPayload->GetLength());
+
+ ComputePRFL(tmp_hash, iSKEYID_a, prf_buf->Des());
+ TPtrC8 hash_ptr(aHash->Data(),aHash->DataLen());
+ TBool b = (tmp_hash.Compare(hash_ptr)==0);
+ CleanupStack::PopAndDestroy(); //prf_buf
+ return (b);
+}
+
+
+//Computes Own Nonce using current time a seed
+void CIkev1Negotiation::ComputeNonceL()
+{
+ DEBUG_LOG(_L("Computed NONCE."));
+ if (iRole==INITIATOR)
+ {
+ iNONCE_I.SetLength(OAKLEY_DEFAULT_NONCE_SIZE);
+ TRandom::RandomL(iNONCE_I);
+ }
+ else
+ {
+ iNONCE_R.SetLength(OAKLEY_DEFAULT_NONCE_SIZE);
+ TRandom::RandomL(iNONCE_R);
+ }
+}
+
+//Computes HASH_R value
+void CIkev1Negotiation::ComputeHashrL(TDes8 &aHash)
+{
+ TInt id_size = 0;
+ TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
+
+ DEBUG_LOG(_L("Computing HASH_R"));
+
+ if ( auth_method != IKE_A_CRACK ) {
+ if (iRole==INITIATOR)
+ { //peer id. payload
+ id_size = iPeerIdentPayloadSize;
+ DEBUG_LOG(_L("PeerID"));
+ }
+ else
+ { //Own identification payload
+ id_size = iOwnIdentPayloadSize;
+ DEBUG_LOG(_L("OwnID"));
+ }
+ }
+
+ DEBUG_LOG(_L("SKEYID"));
+
+ HBufC8 *prf_data;
+ if (iRole==INITIATOR) //peer id. payload
+ {
+ prf_data = HBufC8::NewLC(iPeerPublicKey.Length() + iOwnPublicKey_ptr.Length() + iCookie_R.Length()
+ + iCookie_I.Length() + iSAPayloadSize + id_size);
+ prf_data->Des().Copy(iPeerPublicKey);
+ prf_data->Des().Append(iOwnPublicKey_ptr);
+ prf_data->Des().Append(iCookie_R);
+ prf_data->Des().Append(iCookie_I);
+ prf_data->Des().Append(iSAPayload,iSAPayloadSize); //stored at the begining
+ if ( auth_method != IKE_A_CRACK )
+ prf_data->Des().Append(iPeerIdentPayload, iPeerIdentPayloadSize);
+ }
+ else //RESPONDER
+ {
+ prf_data = HBufC8::NewLC(iOwnPublicKey_ptr.Length() + iPeerPublicKey.Length() + iCookie_R.Length()
+ + iCookie_I.Length() + iSAPayloadSize + id_size);
+ prf_data->Des().Copy(iOwnPublicKey_ptr);
+ prf_data->Des().Append(iPeerPublicKey);
+ prf_data->Des().Append(iCookie_R);
+ prf_data->Des().Append(iCookie_I);
+ prf_data->Des().Append(iSAPayload,iSAPayloadSize); //stored at the begining
+ if ( auth_method != IKE_A_CRACK )
+ prf_data->Des().Append(iOwnIdentPayload, iOwnIdentPayloadSize);
+ }
+
+ DEBUG_LOG(_L("PRF"));
+
+ ComputePRFL(aHash, iSKEYID, prf_data->Des());
+
+ CleanupStack::PopAndDestroy(); //prf_data
+
+ DEBUG_LOG(_L("HASH_R"));
+
+}
+
+
+//Computes the value of iHASH_I
+void CIkev1Negotiation::ComputeHash1L(TDes8 &aHash)
+{
+ TInt id_size = 0;
+ TUint16 auth_method = iChosenProposal_I.iAttrList->iAuthMethod;
+
+ DEBUG_LOG(_L("Computing HASH_I"));
+
+ if ( auth_method != IKE_A_CRACK ) {
+ if (iRole==INITIATOR) //Own identification payload
+ {
+ id_size = iOwnIdentPayloadSize;
+ DEBUG_LOG(_L("OwnID"));
+ }
+ else //peer id. payload
+ {
+ id_size = iPeerIdentPayloadSize;
+ DEBUG_LOG(_L("PeerID"));
+ }
+ }
+
+
+ DEBUG_LOG(_L("SKEYID"));
+
+ HBufC8 *prf_data;
+ if (iRole==INITIATOR)
+ {
+ prf_data = HBufC8::NewLC(iOwnPublicKey_ptr.Length() + iPeerPublicKey.Length() + iCookie_I.Length()
+ + iCookie_R.Length() + iSAPayloadSize + id_size);
+ prf_data->Des().Copy(iOwnPublicKey_ptr);
+ prf_data->Des().Append(iPeerPublicKey);
+ prf_data->Des().Append(iCookie_I);
+ prf_data->Des().Append(iCookie_R);
+ prf_data->Des().Append(iSAPayload,iSAPayloadSize); //stored at the begining
+ if ( auth_method != IKE_A_CRACK )
+ prf_data->Des().Append(iOwnIdentPayload,iOwnIdentPayloadSize);
+ }
+ else //RESPONDER
+ {
+ prf_data = HBufC8::NewLC(iPeerPublicKey.Length() + iOwnPublicKey_ptr.Length() + iCookie_I.Length()
+ + iCookie_R.Length() + iSAPayloadSize + id_size);
+ prf_data->Des().Copy(iPeerPublicKey);
+ prf_data->Des().Append(iOwnPublicKey_ptr);
+ prf_data->Des().Append(iCookie_I);
+ prf_data->Des().Append(iCookie_R);
+ prf_data->Des().Append(iSAPayload,iSAPayloadSize); //stored at the begining
+
+ if ( auth_method != IKE_A_CRACK )
+ prf_data->Des().Append(iPeerIdentPayload, iPeerIdentPayloadSize);
+ }
+
+ DEBUG_LOG(_L("PRF"));
+
+ ComputePRFL(aHash, iSKEYID, prf_data->Des());
+ CleanupStack::PopAndDestroy(); //prf_buf
+ DEBUG_LOG(_L("HASH_I"));
+}
+
+
+//Checks the encryption alg is valid
+TBool CIkev1Negotiation::CheckEncrAlg(TUint16 aValue)
+{
+ switch (aValue)
+ {
+ case DES_CBC:
+ case DES3_CBC:
+ case AES_CBC:
+ return ETrue;
+ case IDEA_CBC:
+ case BLOWFISH_CBC:
+ case RC5_R16_B64_CBC:
+ case CAST_CBC:
+ DEBUG_LOG(_L("Not implemented Encr algorithm"));
+ return ETrue; // Unknown attribute value NOT a fatal error !
+ }
+ DEBUG_LOG(_L("Bad Encr algorithm"));
+
+ return ETrue; // Unknown attribute value NOT a fatal error !
+}
+
+
+
+TBool CIkev1Negotiation::CheckHashAlg(TUint16 aValue)
+{
+ switch (aValue)
+ {
+ case HASH_MD5:
+ case HASH_SHA1:
+ return ETrue;
+ case HASH_TIGER:
+ DEBUG_LOG(_L("Not implemented Hash algorithm"));
+ return ETrue; // Unknown attribute value NOT a fatal error !
+ }
+ DEBUG_LOG(_L("Bad Hash algorithm"));
+ return ETrue; // Unknown attribute value NOT a fatal error !
+
+}
+
+
+TBool CIkev1Negotiation::CheckAuthMethod(TUint16 aValue)
+{
+ switch (aValue)
+ {
+ case PRE_SHARED:
+ if (iHostData->iPresharedKey.iKey.Length()==0) //No preshared key defined
+ {
+ DEBUG_LOG(_L("Authentication method error (No Preshared key available"));
+ return EFalse;
+ }
+ return ETrue;
+ case RSA_SIG:
+ case DSS_SIG:
+ case IKE_A_CRACK:
+ return ETrue;
+ }
+ DEBUG_LOG(_L("Bad Authentication method"));
+ return ETrue; // Unknown attribute value NOT a fatal error !
+
+}
+
+TBool CIkev1Negotiation::CheckGroupDesc(TUint16 aValue)
+{
+ switch (aValue)
+ {
+ case MODP_768:
+ case MODP_1024:
+ case MODP_1536:
+ case MODP_2048:
+ return ETrue;
+ case EC2N_155:
+ case EC2N_185:
+ break;
+ }
+
+ return ETrue; // Unknown attribute value NOT a fatal error !
+}
+
+
+TBool CIkev1Negotiation::CheckGroupType(TUint16 aValue)
+{
+ switch(aValue)
+ {
+ case MODP:
+ return ETrue;
+ case ECP:
+ case EC2N:
+ break;
+ }
+
+ return ETrue; // Unknown attribute value NOT a fatal error !
+
+}
+
+TBool CIkev1Negotiation::CheckGroupPrime(const TUint8* /* aValue */, TUint16 /* length */)
+{
+ return ETrue;
+}
+
+TBool CIkev1Negotiation::CheckGroupGen(const TUint8* /* aValue */, TUint16 /* length */)
+{
+ return ETrue;
+}
+
+TBool CIkev1Negotiation::CheckGroupCurve(const TUint8* /* aValue */, TUint16 /* length */)
+{
+ return ETrue;
+}
+
+//Used for Phase I and II
+TBool CIkev1Negotiation::CheckLifeType(TUint16 aValue)
+{
+ switch(aValue)
+ {
+ case SECONDS:
+ case KBYTES:
+ return ETrue;
+ }
+ return EFalse;
+}
+
+TBool CIkev1Negotiation::CheckLifeDuration(const TUint8* /* aValue */, TUint16 /* length */)
+{
+ return ETrue;
+}
+
+TBool CIkev1Negotiation::CheckPRF(TUint16 aValue)
+{
+ if (aValue!=OAKLEY_PRF_3DES_CBC_MAC)
+ {
+ DEBUG_LOG(_L("Bad PRF"));
+ return EFalse;
+ }
+ return ETrue;
+}
+
+TBool CIkev1Negotiation::CheckKeyLength(TUint16 /*aValue*/,TUint8 aID,TUint8 aProtocol)
+{
+ TBool Status = ETrue;
+ switch (aProtocol)
+ {
+ case PROTO_ISAKMP:
+ if ( aID != AES_CBC )
+ {
+ Status = EFalse; //all other supported algs have fixed size
+ DEBUG_LOG(_L("Key length specified with fixed ISAKMP encryption algorithm"));
+ }
+ break;
+ case PROTO_IPSEC_AH:
+ Status = EFalse; //Supported algorithms have fixed key length
+ DEBUG_LOG(_L("Key length specified with fixed AH integrity algorithm"));
+ break;
+ case PROTO_IPSEC_ESP:
+ if ( aID != ESP_AES_CBC )
+ {
+ Status = EFalse;
+ DEBUG_LOG(_L("Key length specified with fixed ESP encryption algorithm"));
+ }
+ break;
+ default: //Unsupported SA type
+ Status = EFalse; //Supported algorithms have fixed key length
+ break;
+ }
+
+ return Status;
+}
+
+TBool CIkev1Negotiation::CheckFieldSize(TUint16 /* aValue */)
+{
+ DEBUG_LOG(_L("Field size not supported"));
+ return EFalse;
+}
+
+TBool CIkev1Negotiation::CheckGroupOrder(const TUint8* /* aValue */, TUint16 /* length */)
+{
+ DEBUG_LOG(_L("Group Order not supported "));
+ return ETrue; // Unknown attribute value NOT a fatal error !
+}
+
+//Encapsulation mode
+TBool CIkev1Negotiation::CheckEncMode(TUint16 aValue)
+{
+ switch (aValue)
+ {
+ case DOI_TUNNEL:
+ case DOI_TRANSPORT:
+ return ETrue;
+ case UDP_ENC_TUNNEL:
+ case UDP_RFC_ENC_TUNNEL:
+// case UDP_ENC_TRANSPORT:
+ if ( iNAT_D_Flags )
+ return ETrue;
+ break;
+ }
+
+ DEBUG_LOG(_L("Bad Encapsulation mode"));
+ return EFalse;
+
+}
+
+//defined authentication algorithm types. Other are invalid.
+TBool CIkev1Negotiation::CheckAuthAlg(TUint16 aValue)
+{
+ switch (aValue)
+ {
+ case DOI_HMAC_MD5:
+ case DOI_HMAC_SHA:
+ return ETrue;
+ case DOI_DES_MAC:
+ case DOI_KPDK:
+ DEBUG_LOG(_L("Unimplemented Auhentication Algorithm"));
+ }
+ DEBUG_LOG(_L("Bad Auhentication Algorithm"));
+ return ETrue; // Unknown attribute value NOT a fatal error !
+}
+
+//By now only X509_CERT_SIG. Tristate error codes:
+//KErrNone -> accepted
+//KErrNotSupported ignored but no error Notification.
+//KErrGeneral: NOT accepted
+TInt CIkev1Negotiation::CheckEncodingL(TUint8 aEncoding)
+{
+ switch (aEncoding)
+ {
+ case X509_CERT_SIG://X.509 Certificate - Signature
+ break;
+ case CRL://Certificate Revocation List (CRL)
+ DEBUG_LOG(_L("WARNING: CRL ignored because not supported"));
+ return KErrNotSupported; //No notification, just ignored!
+ case PKCS://PKCS #7 wrapped X.509 certificate
+ case PGP://PGP Certificate
+ case DNS ://DNS Signed Key
+ case X509_CERT_KE://X.509 Certificate - Key Exchange
+ case KERBEROS://Kerberos Tokens
+ case ARL://Authority Revocation List (ARL)
+ case SPKI://SPKI Certificate
+ case X509_CERT_ATTR://X.509 Certificate - Attribute
+ DEBUG_LOG(_L("CERT_TYPE_UNSUPPORTED (not supported CERT type)"));
+ SendNotifyL(CERT_TYPE_UNSUPPORTED);
+ return KErrNotSupported; // No notification, just ignored!
+
+ default://Invalid encoding type
+ DEBUG_LOG(_L("INVALID_CERT_ENCODING (not existent CERT type)"));
+ SendNotifyL(INVALID_CERT_ENCODING);
+ return KErrNotSupported; // No notification, just ignored!
+
+ }
+
+ return KErrNone;
+}
+
+
+//Provisional (Uses as a seed time, Address and port)
+TCookie CIkev1Negotiation::CreateCookieL() const
+{
+ TCookie c;
+ //Cookie generation is Random no longer uses known data like addr
+ //or port (wrong?)
+ c.SetLength(ISAKMP_COOKIE_SIZE);
+ TRandom::RandomL(c);
+ return c;
+}
+
+TInt32 CIkev1Negotiation::RandomMessageId()
+{
+ TTime tmp_time;
+ tmp_time.UniversalTime();
+ TInt64 seed = tmp_time.Int64();
+ TInt32 rand = Math::Rand(seed);
+ return rand;
+}
+
+TBool CIkev1Negotiation::CheckCookies(const TCookie& aInit, const TCookie& aResp)
+ {
+ TCookie NULL_COOKIE;
+ NULL_COOKIE.FillZ(ISAKMP_COOKIE_SIZE);
+
+ if ( iCookie_I.Compare(NULL_COOKIE) != 0 &&
+ iCookie_I.Compare(aInit) != 0 )
+ {
+ DEBUG_LOG(_L("Initiator COOKIE incorrect"));
+ return EFalse;
+ }
+ if ( iCookie_R.Compare(NULL_COOKIE) != 0 &&
+ iCookie_R.Compare(aResp) != 0 )
+ {
+ DEBUG_LOG(_L("Responder COOKIE incorrect"));
+ return EFalse;
+ }
+
+ return ETrue;
+ }
+
+//Checks if the payload value is correct
+TBool CIkev1Negotiation::CheckPayloadCode(TUint8 aPayload)
+{
+ switch (aPayload)
+ {
+ case ISAKMP_PAYLOAD_NONE:
+ case ISAKMP_PAYLOAD_SA:
+ case ISAKMP_PAYLOAD_P:
+ case ISAKMP_PAYLOAD_T:
+ case ISAKMP_PAYLOAD_KE:
+ case ISAKMP_PAYLOAD_ID:
+ case ISAKMP_PAYLOAD_CERT:
+ case ISAKMP_PAYLOAD_CR:
+ case ISAKMP_PAYLOAD_HASH:
+ case ISAKMP_PAYLOAD_SIG:
+ case ISAKMP_PAYLOAD_NONCE:
+ case ISAKMP_PAYLOAD_NOTIF:
+ case ISAKMP_PAYLOAD_D:
+ case ISAKMP_PAYLOAD_VID:
+ case ISAKMP_PAYLOAD_ATTRIBUTES:
+ case ISAKMP_PAYLOAD_CHRE:
+ case ISAKMP_INT_NETWORK:
+ case IETF_NAT_DISCOVERY:
+ case IETF_RFC_NAT_DISCOVERY:
+ case IETF_NAT_ORIG_ADDR:
+ case IETF_RFC_NAT_ORIG_ADDR:
+ return ETrue; //supported payload type
+ }
+ DEBUG_LOG1(_L("INVALID_PAYLOAD_TYPE (%x)"),aPayload);
+ return EFalse;
+
+}
+
+
+//Checks if the version (major,minor) is supported
+TBool CIkev1Negotiation::CheckVersionL(TUint8 aVersion)
+{
+ if (aVersion >> 4 > MAJOR)
+ {
+ DEBUG_LOG(_L("INVALID_MAJOR_VERSION"));
+ SendNotifyL(INVALID_MAJOR_VERSION);
+ return EFalse;
+ }
+
+ if (aVersion & (0x0f) > MINOR)
+ {
+ DEBUG_LOG(_L("INVALID_MINOR_VERSION"));
+ SendNotifyL(INVALID_MINOR_VERSION);
+ return EFalse;
+ }
+
+ return ETrue; //version correct
+}
+
+//Checks if the exchange type is valid and the same as in the negotiation
+TBool CIkev1Negotiation::CheckExchangeTypeL(TUint8 aType)
+{
+ switch (aType)
+ {
+ case ISAKMP_EXCHANGE_ID: //Main
+ case ISAKMP_EXCHANGE_AGGR: // Agressive
+ case ISAKMP_EXCHANGE_INFO:
+ case IKE_QUICK_MODE:
+ //invalid Exchange Type Not the same being used and not an error notification
+ if (aType != iExchange)
+ {
+ DEBUG_LOG(_L("INVALID_EXCHANGE_TYPE")); //send the informational exchange
+ SendNotifyL(INVALID_EXCHANGE_TYPE); //send the informational exchange
+ return EFalse; //invalid Exchange Type
+ }
+ break;
+ case ISAKMP_EXCHANGE_BASE: // Base
+ case ISAKMP_EXCHANGE_NONE: // Identity Protection (Main mode in IKE)
+ case ISAKMP_EXCHANGE_AUTH: // Authentication Only
+ case IKE_NEW_GROUP_MODE: // New Group Mode
+ DEBUG_LOG(_L("INVALID_EXCHANGE_TYPE")); //send the informational exchange
+ SendNotifyL(UNSUPPORTED_EXCHANGE_TYPE); //send the informational exchange
+ return EFalse;
+ }
+
+ return ETrue;
+}
+
+//Checks the non-relevant bits are 0. Other comprovations are done when needed
+TBool CIkev1Negotiation::CheckFlagsL(TUint8 aFlags)
+{
+ if (aFlags >> 3 != 0)
+ {
+ DEBUG_LOG(_L("INVALID_FLAGS")); //send the informational exchange
+ SendNotifyL(INVALID_FLAGS); //send the informational exchange
+ return EFalse;
+ }
+
+ return ETrue;
+}
+
+//Checks the Id has a correct value. 0 in Phase I, the correct one in Phase II
+TBool CIkev1Negotiation::CheckMessageIdL(TUint32 aId)
+{
+ if (aId != iMessageId) //iMessageId will be 0 during Phase I
+ {
+ DEBUG_LOG2(_L("INVALID_MESSAGE_ID %u (neg=%u)"),aId, iMessageId);
+ SendNotifyL(INVALID_MESSAGE_ID); //send the informational exchange
+ return EFalse;
+ }
+ return ETrue;
+}
+
+//Checks the DOI is valid
+TBool CIkev1Negotiation::CheckDOI(TUint32 aDOI)
+{
+ if (aDOI > IPSEC_DOI) //Not IPSEC nor ISAKMP DOI
+ return EFalse;
+
+ return ETrue;
+}
+
+//Checks the SIT is valid
+TBool CIkev1Negotiation::CheckSituationL(TUint32 aSIT)
+{
+ //Secrecy and integrity not yet supported
+ if ((aSIT & IPSEC_SIT_SECRECY) || (aSIT & IPSEC_SIT_INTEGRITY))
+ {
+ DEBUG_LOG(_L("SITUATION_NOT_SUPPORTED")); //send the informational exchange
+ SendNotifyL(SITUATION_NOT_SUPPORTED); //send the informational exchange
+ return EFalse;
+ }
+
+ return ETrue;
+}
+//check the generic payload is OK. Correct payload + Reserved==0
+TBool CIkev1Negotiation::CheckGenericPayloadL(const TPayloadISAKMP *aPayload)
+{
+ if (!CheckPayloadCode(aPayload->GetPayload()))
+ return EFalse;
+
+ if (aPayload->GetReserved() != 0) //Must be always 0
+ {
+ DEBUG_LOG(_L("INVALID RESERVED FIELD"));
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return EFalse;
+ }
+
+ if ((aPayload->GetLength() < MIN_ISAKMP_PAYLOAD_SIZE) || (aPayload->GetLength() > iLengthLeft))
+ {
+ DEBUG_LOG(_L("BAD PAYLOAD SIZE"));
+ SendNotifyL(PAYLOAD_MALFORMED);
+ return EFalse;
+ }
+
+ iLengthLeft -= aPayload->GetLength(); //Updates the length left in the buffer
+
+ return ETrue;
+
+}
+
+//checks if protocol supported
+TBool CIkev1Negotiation::CheckProtocolL(TUint8 aProtocol)
+{
+ switch (aProtocol)
+ {
+ //PHASE_I Protocol
+ case PROTO_ISAKMP: //Only when establishing own SA??
+ if (iPhase != PHASE_I)
+ {
+ DEBUG_LOG(_L("INVALID_PROTOCOL_ID (ISAKMP only allowed in Phase I)"));
+ SendNotifyL(INVALID_PROTOCOL_ID);
+ return EFalse;
+ }
+ return ETrue;
+
+ //PHASE_II Protocols
+ case PROTO_IPSEC_AH:
+ case PROTO_IPSEC_ESP:
+ if (iPhase != PHASE_II)
+ {
+ DEBUG_LOG1(_L("INVALID_PROTOCOL_ID (Prot (%u) only allowed in Phase II)"),aProtocol);
+ SendNotifyL(INVALID_PROTOCOL_ID);
+ return EFalse;
+ }
+ return ETrue;
+ }
+ DEBUG_LOG1(_L("INVALID_PROTOCOL_ID (Unknown Protocol (%u))"),aProtocol);
+ SendNotifyL(INVALID_PROTOCOL_ID);
+ return EFalse;
+}
+
+TBool CIkev1Negotiation::CheckSPIL(const TProposalISAKMP *aProposal)
+{
+
+ TUint size=aProposal->GetSPISize();
+ if (iPhase == PHASE_I)
+ {
+ if (size > MAX_SPI_SIZE)
+ {
+ DEBUG_LOG(_L("INVALID_SPI (Bad Size)"));
+ SendNotifyL(INVALID_SPI);
+ return EFalse;
+ }
+ }
+ else //Phase II
+ {
+ TUint32 spi = 0;
+ if (aProposal->GetSPISize() > sizeof(TUint32))
+ {
+ DEBUG_LOG(_L("INVALID_SPI (Too big. Max. is 32 bits)"));
+ SendNotifyL(INVALID_SPI);
+ return EFalse;
+ }
+ Mem::Copy((TUint8 *)&spi, ((TProposalISAKMP *)aProposal)->SPI(), aProposal->GetSPISize());
+ spi = ByteOrder::Swap32(spi);
+ if (spi < 256) //The first 256 are reserved
+ {
+ DEBUG_LOG(_L("INVALID_SPI (spi's < 256 are RESERVED)"));
+ SendNotifyL(INVALID_SPI);
+ return EFalse;
+ }
+ }
+ return ETrue;
+}
+
+//Checks for transform payloads. MUST NOT abort processing, just discard the payload
+TBool CIkev1Negotiation::CheckTransformID(TUint8 aProtocol,TUint8 aID)
+{
+ switch (aProtocol)
+ {
+ case PROTO_ISAKMP:
+ if (aID != KEY_IKE)
+ return EFalse;
+ break;
+
+ case PROTO_IPSEC_AH:
+ if ( (aID != AH_MD5) && (aID != AH_SHA))
+ {
+ DEBUG_LOG(_L("Unsupported Authentication Algorithm"));
+ return EFalse;
+ }
+ break;
+ case PROTO_IPSEC_ESP:
+ switch ( aID )
+ {
+ case ESP_DES_CBC:
+ case ESP_3DES_CBC:
+ case ESP_NULL:
+ case ESP_AES_CBC:
+ break;
+ default:
+ DEBUG_LOG(_L("Unsupported Encryption Algorithm"));
+ return EFalse;
+ }
+ break;
+
+ default:
+ return EFalse;
+ }
+
+ return ETrue;
+}
+
+
+//Diffie-Hellman key exchange
+//The info in iNegotiation MUST be correct
+TBool CIkev1Negotiation::ComputeDHPublicValueL()
+{
+ TUint desc;
+ if (iPhase == PHASE_I)
+ {
+ //If aggressive sends the SA and KE at the same time b4 knowing the chosen group.
+ //The group in the first proposed transform is chosen. Shouldn't be transforms with
+ //different groups sent. Checked when using the configuration tool?
+ if ((iExchange == ISAKMP_EXCHANGE_AGGR) && (iRole == INITIATOR))
+ desc = iProposal_I.iAttrList->iGroupDesc;
+ else
+ desc = iChosenProposal_I.iAttrList->iGroupDesc;
+ }
+ else
+ {
+ if (iRole == INITIATOR) //We have to use one of the proposals because we don't have the reply yet
+ //Anyay only one group can be specified for phase II so it's fine
+ desc = iProposal_IIList->At(0)->iAttrList->At(0)->iGroupDesc;
+ else //RESPONDER
+ desc = iChosenProp_IIList->At(0)->iAttrList->At(0)->iGroupDesc;
+ }
+
+ delete iOwnKeys; //Happens in phase II because we may recalculate the DH value.
+ iOwnKeys = NULL;
+ delete iOwnPublicKey; // Happens in phase II because we may recalculate the DH value.
+ iOwnPublicKey = NULL;
+
+ iOwnKeys = GeneratePubPrivKeysL(desc);
+ if (!iOwnKeys)
+ {
+ DEBUG_LOG(_L("Error generating DH public and private keys"));
+ return EFalse;
+ }
+ iOwnPublicKey = iOwnKeys->GetPubKey(); //save the public key in a buffer to have easy access
+ iOwnPublicKey_ptr.Set(iOwnPublicKey->Des());
+ return ETrue;
+}
+
+
+//Initial IV computation
+//Pre:Requires correct Public keys alredy stored!!!
+TBool CIkev1Negotiation::InitIVL()
+{
+ HBufC8* prf_data =
+ HBufC8::NewLC(((iOwnPublicKey_ptr.Length() + iPeerPublicKey.Length()) | 0x3) + 1);
+
+ if (iRole == INITIATOR)
+ {
+ prf_data->Des().Copy(iOwnPublicKey_ptr);
+ prf_data->Des().Append(iPeerPublicKey);
+
+ }
+ else //RESPONDER
+ {
+ prf_data->Des().Copy(iPeerPublicKey);
+ prf_data->Des().Append(iOwnPublicKey_ptr);
+ }
+ if (iChosenProposal_I.iAttrList->iHashAlg == HASH_MD5)
+ MD5HashL(prf_data->Des(), iIV);
+ else SHA1HashL(prf_data->Des(), iIV);
+
+ if (iChosenProposal_I.iAttrList->iEncrAlg == AES_CBC )
+ iIVSize = 16;
+ else iIVSize = 8;
+ iIV.SetLength(iIVSize);
+ DEBUG_LOG(_L("Init"));
+
+ CleanupStack::PopAndDestroy(); //prf_data
+
+ return ETrue;
+}
+
+
+
+//subsequent IV computations. Like when send notifications or beginning of Phase II
+TBool CIkev1Negotiation::ComputeIVL(TDes8 &aIV, TInt32 aMessageId)
+{
+ HBufC8* prf_data =
+ HBufC8::NewLC(((aIV.Length() + sizeof(aMessageId)) | 0x3) + 1);
+
+ if ((iChosenProposal_I.iAttrList->iEncrAlg != DES3_CBC) &&
+ (iChosenProposal_I.iAttrList->iEncrAlg != AES_CBC) &&
+ (iChosenProposal_I.iAttrList->iEncrAlg != DES_CBC))
+ {
+ return EFalse;
+ }
+ //former IV
+ prf_data->Des().Copy(aIV);
+ //Message ID
+ TInt32 id = ByteOrder::Swap32(aMessageId); //Needed to add it
+
+ prf_data->Des().Append((TUint8 *)&id, sizeof(id));
+
+ DEBUG_LOG(_L("prf"));
+ if (iChosenProposal_I.iAttrList->iHashAlg == HASH_MD5)
+ MD5HashL(prf_data->Des(), aIV);
+ else SHA1HashL(prf_data->Des(), aIV);
+
+ DEBUG_LOG(_L("Computed IV"));
+
+ CleanupStack::PopAndDestroy(); //prf_data
+ return ETrue;
+}
+
+//Generates all the keying material SKEYID,SKEYID_d,SKEYID_a,SKEYID_e
+TBool CIkev1Negotiation::ComputeKeysL()
+{
+ TUint desc;
+ //If aggressive sends the SA and KE at the same time b4 knowing the chosen group.
+ //The group in the first proposed transform is chosen. Shouldn't be transforms with
+ //different groups sent
+ if ((iExchange == ISAKMP_EXCHANGE_AGGR) && (iRole == INITIATOR))
+ desc = iProposal_I.iAttrList->iGroupDesc;
+ else
+ desc = iChosenProposal_I.iAttrList->iGroupDesc;
+ //
+ //Computes agreed key
+ //
+ HBufC8* agreedKey = ComputeAgreedKeyL(desc, iPeerPublicKey, iOwnKeys); //(gxy)
+ if ( !agreedKey ) {
+ DEBUG_LOG(_L("DH secret creation failed (ComputeAgreedKeyL)"));
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(INVALID_KEY_INFORMATION);
+ return EFalse;
+ }
+ CleanupStack::PushL(agreedKey); //agreedKey
+
+ // Use prf and agreed DH-key to generate keying material
+ HBufC8* prf_data =
+ HBufC8::NewLC(((iNONCE_I.Length() + iNONCE_R.Length() + 2*ISAKMP_COOKIE_SIZE) | 0x3) + 1);
+ HBufC8* prf_key =
+ HBufC8::NewLC(((iNONCE_I.Length() + iNONCE_R.Length() ) | 0x3) + 1);
+
+ DEBUG_LOG(_L("Agreed Key."));
+
+ switch(iChosenProposal_I.iAttrList->iAuthMethod)
+ {
+ case RSA_SIG:
+ case DSS_SIG:
+ case IKE_A_CRACK:
+ //For signatures:
+ //SKEYID = prf(Ni_b|Nr_b, g^xy)
+ //key
+ prf_key->Des().Copy(iNONCE_I);
+ prf_key->Des().Append(iNONCE_R);
+ ComputePRFL(iSKEYID, prf_key->Des(), agreedKey->Des());
+ break;
+ case PRE_SHARED:
+ {
+ //pre-shared keys:
+ //SKEYID=prf(pre_shared key, Ni_b | Nr_b);
+ //data
+ prf_data->Des().Copy(iNONCE_I.Ptr(),iNONCE_I.Length());
+ prf_data->Des().Append(iNONCE_R.Ptr(),iNONCE_R.Length());
+ DEBUG_LOG(_L("Pre-shared Key"));
+#ifdef _UNICODE
+ HBufC8 *preshared_key_buf = HBufC8::NewLC(iHostData->iPresharedKey.iKey.Length());
+ preshared_key_buf->Des().Copy(iHostData->iPresharedKey.iKey);
+ TPtrC8 preshared_key_ptr(preshared_key_buf->Des());
+#else
+ TPtrC8 preshared_key_ptr(iHostData->iPresharedKey.iKey);
+#endif
+ ComputePRFL(iSKEYID, preshared_key_ptr, prf_data->Des());
+#ifdef _UNICODE
+ CleanupStack::PopAndDestroy(); //presharedkey_buf
+#endif
+ }
+ break;
+ default://method not implemented
+ DEBUG_LOG1(_L("ATTRIBUTES_NOT_SUPPORTED:Auth Method %d not supported "),iChosenProposal_I.iAttrList->iAuthMethod);
+ SetErrorStatus( KKmdIkeNegotFailed );
+ SendNotifyL(ATTRIBUTES_NOT_SUPPORTED);
+ return EFalse;
+ }
+
+ CleanupStack::PopAndDestroy(2); //prf_data and prf_key
+ prf_data =
+ HBufC8::NewLC(((agreedKey->Length() + iSKEYID.Length() + 2*ISAKMP_COOKIE_SIZE + 4) | 0x3) + 1);
+
+ prf_data->Des().Copy(agreedKey->Des());
+ prf_data->Des().Append(iCookie_I);
+ prf_data->Des().Append(iCookie_R);
+ prf_data->Des().Append(0);
+ ComputePRFL(iSKEYID_d, iSKEYID, prf_data->Des());
+
+ prf_data->Des().Copy(iSKEYID_d);
+ prf_data->Des().Append(agreedKey->Des());
+ prf_data->Des().Append(iCookie_I);
+ prf_data->Des().Append(iCookie_R);
+ prf_data->Des().Append(1);
+ ComputePRFL(iSKEYID_a, iSKEYID, prf_data->Des());
+
+ prf_data->Des().Copy(iSKEYID_a);
+ prf_data->Des().Append(agreedKey->Des());
+ prf_data->Des().Append(iCookie_I);
+ prf_data->Des().Append(iCookie_R);
+ prf_data->Des().Append(2);
+ ComputePRFL(iSKEYID_e, iSKEYID, prf_data->Des());
+
+ agreedKey->Des().FillZ(); // Zeroe DH secret g^xy
+
+ //Builds the IV
+ if (!InitIVL())
+ {
+ DEBUG_LOG(_L("Error Computing IV"));
+ return EFalse;
+ }
+ if (iExchange == ISAKMP_EXCHANGE_AGGR)
+ {
+ iLastIV.Copy(iIV); //Saves last IV in Phase 1
+ iLastIV.SetLength(iIVSize);
+ DEBUG_LOG(_L("Last IV Saved!"));
+ }
+
+ //if key extension required:
+ TUint8 *key;
+ TInt key_len=0;
+ TInt total_key_len = ISAKMPEncrKeyLength((TUint8)iChosenProposal_I.iAttrList->iEncrAlg);
+ if (iSKEYID_e.Length() < total_key_len)
+ {
+ DEBUG_LOG(_L("Extending encrytion key..."));
+ key = new (ELeave) TUint8[total_key_len*2];
+ CleanupStack::PushL(key);
+ key[0] = 0;
+ TPtr8 kx0_ptr(key, 1, 1);
+ TPtr8 kx1_ptr(key, 0, total_key_len * 2);
+ ComputePRFL(kx1_ptr, iSKEYID_e, kx0_ptr); //K1=prf(SKEYID_e,0)
+ key_len += kx1_ptr.Length();
+
+ while (key_len < total_key_len)
+ {
+ kx0_ptr.Set(&key[key_len - kx1_ptr.Length()], kx1_ptr.Length(), total_key_len);
+ kx1_ptr.Set(&key[key_len], 0, total_key_len);
+ ComputePRFL(kx1_ptr, iSKEYID_e, kx0_ptr); //Kx=prf(SKEYID_e,K<x-1>)
+ key_len += kx1_ptr.Length();
+ }
+ iSKEYID_e.Copy(key, total_key_len);
+ CleanupStack::PopAndDestroy(); //key
+ DEBUG_LOG(_L(" SKEYID_e (EXTENDED)"));
+ }
+ else
+ {
+ iSKEYID_e.SetLength(total_key_len);
+ DEBUG_LOG(_L(" SKEYID_e"));
+ }
+
+ DEBUG_LOG(_L(" Init IV"));
+
+ CleanupStack::PopAndDestroy(2); //prf_data and agreedKey
+
+ return ETrue;
+}
+
+//Computes the IPSEC keys needed for each IPSEC SA
+//KEYMAT = prf(SKEY_ID, protocol | SPI | Ni_b | Nr_b)
+//if PFS:
+//KEYMAT = prf(SKEY_ID, g(qm)^xy | protocol | SPI | Ni_b | Nr_b)
+void CIkev1Negotiation::ComputeKeys2L(const CProposal_II *aProp, TInt aKeyLen, TSPINode &aInboundSpiNode, TDes8& aOutboundKey_II, TDes8& aInboundKey_II)
+{
+ DEBUG_LOG(_L("Computing PHASE II keys "));
+
+ aOutboundKey_II.SetLength(0);
+ aInboundKey_II.SetLength(0);
+ DEBUG_LOG(_L("Total Computed Key Length="));
+ DEBUG_LOG_NUM(aKeyLen);
+
+ HBufC8* agreedKey = NULL; //g(qm)^xy
+ TInt prf_len = iSKEYID_d.Length() + iNONCE_I.Length() + iNONCE_R.Length() + 8; // 8 for protocol and SPI
+
+ if (iPFS)
+ {
+ agreedKey = ComputeAgreedKeyL(iChosenProp_IIList->At(0)->iAttrList->At(0)->iGroupDesc, iPeerPublicKey, iOwnKeys);
+ if ( !agreedKey )
+ User::Leave(KErrGeneral);
+ CleanupStack::PushL(agreedKey);
+ prf_len += agreedKey->Length();
+ DEBUG_LOG(_L(" Agreed Key"));
+ }
+#ifdef _DEBUG
+ else DEBUG_LOG(_L("(NO PFS)"));
+#endif // _DEBUG
+
+ DEBUG_LOG(_L("Protocol:"));
+ DEBUG_LOG_NUM(aProp->iProtocol);
+
+ TUint32 in_spi = aInboundSpiNode.iSPI; //inbound spi in Network Order
+ DEBUG_LOG(_L("InSPI:"));
+ DEBUG_LOG_NUM(ByteOrder::Swap32(in_spi));
+ TUint32 out_spi;
+ Mem::Copy((TUint8 *)&out_spi, aProp->iSPI.Ptr(), aProp->iSPI.Length());
+ DEBUG_LOG1(_L("OutSPI: %x"), out_spi);
+ //Inbound and outbound calculations only differ in the SPI
+ //If the key is not long enough we need to extend it
+ TPtr8 key_ptr((TUint8 *)aOutboundKey_II.Ptr() + aOutboundKey_II.Length(), 0, aOutboundKey_II.MaxLength());
+
+ HBufC8* prf_data = HBufC8::NewLC((prf_len | 0x3) + 1);
+
+ while ((aOutboundKey_II.Length() * 8) < aKeyLen) //include the key extension algorithm
+ {
+ prf_data->Des().Copy(key_ptr);
+ if (agreedKey)//Only used if PFS
+ {
+ prf_data->Des().Append(agreedKey->Des());
+ }
+ prf_data->Des().Append(&aProp->iProtocol, sizeof(aProp->iProtocol));
+ prf_data->Des().Append(aProp->iSPI.Ptr(),aProp->iSPI.Length());
+ prf_data->Des().Append(iNONCE_I);
+ prf_data->Des().Append(iNONCE_R);
+ key_ptr.Set((TUint8 *)aOutboundKey_II.Ptr() + aOutboundKey_II.Length(), 0, aOutboundKey_II.MaxLength() - aOutboundKey_II.Length());
+ ComputePRFL(key_ptr, iSKEYID_d, prf_data->Des());
+ aOutboundKey_II.SetLength(aOutboundKey_II.Length() + key_ptr.Length());
+ }
+
+ key_ptr.Set((TUint8 *)aInboundKey_II.Ptr() + aInboundKey_II.Length(), 0, aOutboundKey_II.MaxLength());
+ while ((aInboundKey_II.Length() * 8) < aKeyLen) //include the key extension algorithm
+ {
+ prf_data->Des().Copy(key_ptr);
+ if (agreedKey)//Only used if PFS
+ {
+ prf_data->Des().Append(agreedKey->Des());
+ }
+ prf_data->Des().Append(&aProp->iProtocol,sizeof(aProp->iProtocol));
+ prf_data->Des().Append((TUint8 *)&in_spi, sizeof(TUint32));
+ prf_data->Des().Append(iNONCE_I);
+ prf_data->Des().Append(iNONCE_R);
+
+ key_ptr.Set((TUint8 *)aInboundKey_II.Ptr() + aInboundKey_II.Length(), 0, aInboundKey_II.MaxLength() - aInboundKey_II.Length());
+ ComputePRFL(key_ptr, iSKEYID_d, prf_data->Des());
+ aInboundKey_II.SetLength(aInboundKey_II.Length() + key_ptr.Length());
+ }
+
+ if ( agreedKey )
+ CleanupStack::PopAndDestroy(2); //prf_data and agreedKey
+ else CleanupStack::PopAndDestroy(); //prf_data
+
+}
+
+
+/* This function is called by oakley module to do pseudo random computation for
+ given data */
+//IMPORTANT: If some func added or modified check MAX_PRF_LENGTH is correct in ike.h
+
+void CIkev1Negotiation::ComputePRFL(TDes8 &prf_output, const TDesC8 &prf_key, const TDesC8 &prf_data)
+{
+// All actions taken are moved into the crypto module IKE_CRYPTO.CPP
+
+ if ( iChosenProposal_I.iAttrList->iPRF == OAKLEY_PRF_3DES_CBC_MAC)
+ {
+ DEBUG_LOG(_L("PRF (3DES_CBC_MAC)"));
+ Hmac3DesCbcL(prf_data, prf_output, prf_key);
+ }
+ else
+ {
+ // Use HMAC version of the negotiated hash function as default PRF
+ if ( iChosenProposal_I.iAttrList->iHashAlg == HASH_MD5 ) {
+ DEBUG_LOG(_L("PRF (MD5)"));
+ MD5HmacL(prf_data, prf_output, prf_key);
+ }
+ else {
+ if ( iChosenProposal_I.iAttrList->iHashAlg == HASH_SHA1 ) {
+ DEBUG_LOG(_L("PRF (SHA1)"));
+ SHA1HmacL(prf_data, prf_output, prf_key);
+ }
+ }
+ }
+
+}
+
+
+//NOTE: Must be called after ProcessCertificate() !!!
+TBool CIkev1Negotiation::CertifyRemoteIdentityL(const TIdentISAKMP *aIdPayload)
+{
+ TBool Status = EFalse;
+ TInt id_len = aIdPayload->IDDataLen();
+
+ if ( id_len && iPkiService && iPeerX509Cert )
+ {
+ TPtrC8 IdData((TUint8 *)aIdPayload->IDData(), id_len);
+
+ // Leave is trapped and handled here because the event log is easily accessible
+ // and in the upper layers it would not be possible to identify the
+ // cause of the error based on the generic leave code.
+ TRAPD(err, Status = IkePkiUtils::CertifyIdentityL(iPeerX509Cert, IdData, (TInt)aIdPayload->GetIDType()));
+
+ if( KErrNotSupported == err )
+ {
+ LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
+ R_VPN_MSG_CERT_ERROR,
+ KErrNotSupported,
+ iPluginSession->VpnIapId(),
+ &iRemoteAddr );
+ }
+
+#ifdef _DEBUG
+ if (Status)
+ DEBUG_LOG(_L("Remote identity has been certified"));
+#endif // _DEBUG
+ }
+
+ return Status;
+}
+
+
+//Digests aHash with the key in aCert and compares it to the stored value aSig
+//Needs to be check after the Signature payload has been processed
+TBool CIkev1Negotiation::VerifySignatureL(CX509Certificate *aCert,TUint8 *aHash, TInt aHashLength,TUint8 *aSig, TUint aSigLength)
+{
+
+ TBool ret = EFalse;
+ if ( iPkiService )
+ {
+ TPtrC8 Signature(aSig, aSigLength);
+ TPtrC8 RefHash(aHash, aHashLength);
+ ret = IkePkiUtils::VerifyIkev1SignatureL(Signature, RefHash, *aCert);
+ }
+
+ return ret;
+
+
+}
+
+//NOTE!!! The func sets iFinished to TRUE always so after detecting an error the communication finishes.
+//If it doesn't have to iFinished should be set outside the function.
+void CIkev1Negotiation::SendNotifyL(TUint16 aError)
+{
+ SetFinished(); //Ends the negotiation (error condition detected) If CONNECTED also should end
+
+#ifdef _DEBUG
+ TBuf<60> buf;
+ DEBUG_LOG(_L("SendNotifyL(), Reason: "));
+ buf.Append(TextNotifyType(aError));
+ DEBUG_LOG(buf);
+#endif // _DEBUG
+ TUint8 protocol = PROTO_ISAKMP;
+ TUint8 tmp_exchange = iExchange;
+ TUint32 tmp_msg_id = iMessageId;
+ TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
+ //If configured to send notification payloads for errors
+ if (iHostData->iNotify)
+ {
+ iExchange = ISAKMP_EXCHANGE_INFO;
+ iMessageId = RandomMessageId();
+ msg->IsakmpInit(this);
+
+ //HASH Payload only if payload protected with encyption
+ if (iFlags & ISAKMP_HDR_EFLAG)
+ msg->IsakmpHashL();
+
+ if (iPhase == PHASE_I)
+ protocol = iChosenProposal_I.iProtocol;
+ else
+ {
+ if (iChosenProp_IIList) //May be the begining of PHASE_II when still not decided
+ protocol = iChosenProp_IIList->At(0)->iProtocol;
+ }
+ msg->IsakmpNotification(aError, protocol);
+
+ if (iFlags & ISAKMP_HDR_EFLAG)
+ {
+ msg->IsakmpHashContL();
+ }
+
+ iExchange = tmp_exchange;
+ iMessageId = tmp_msg_id;
+
+ SendL(*msg);
+
+ LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
+ R_VPN_MSG_SENT_ERROR_RESPONSE,
+ aError,
+ iPluginSession->VpnIapId(),
+ &iRemoteAddr );
+ }
+
+}
+
+
+//Hash function key length (in bytes)
+TInt CIkev1Negotiation::HashLength()
+{
+ if (iChosenProposal_I.iAttrList->iPRF == OAKLEY_PRF_3DES_CBC_MAC)
+ return 24;
+
+ //If no PRF hash alg used instead.
+ if (iChosenProposal_I.iAttrList->iPRF==0)
+ {
+ if (iChosenProposal_I.iAttrList->iHashAlg==HASH_MD5)
+ return HMAC_MD5_SIZE/8; //16 bytes
+ //return MD5_DIGEST_LENGTH;
+ else if (iChosenProposal_I.iAttrList->iHashAlg==HASH_SHA1)
+ return HMAC_SHA1_SIZE/8;
+ //return SHA_DIGEST_LENGTH;
+ }
+
+ return 0;
+}
+
+
+//Could be done using directly iEncrAlg from the chosen transform (in bytes)
+TUint32 CIkev1Negotiation::ISAKMPEncrKeyLength(TUint8 aAlgId) const
+{
+ TUint32 KeyLth = 0;
+ switch (aAlgId) {
+ case DES_CBC:
+ KeyLth = 8;
+ break;
+ case DES3_CBC:
+ KeyLth = 24;
+ break;
+ case AES_CBC:
+ if ( iChosenProposal_I.iAttrList->iKeyLength )
+ KeyLth = (TUint32)(iChosenProposal_I.iAttrList->iKeyLength/8);
+ else KeyLth = 16; // default
+ break;
+ default:
+ break;
+ }
+
+ return KeyLth;
+}
+
+TIkev1IsakmpStream* CIkev1Negotiation::SaveIkeMsgBfr(TIkev1IsakmpStream* aMsg)
+{
+ delete iSavedIkeMsgBfr;
+ iSavedIkeMsgBfr = aMsg;
+ return aMsg;
+}
+
+//Returns the key length (in bits) of an algorithm for iAuth (TAttrib_II) field when protocol is AH
+TUint32 CIkev1Negotiation::HMAC_KeyLength(TUint8 aId) const
+{
+ if (aId == SADB_AALG_MD5HMAC)
+ return (HMAC_MD5_SIZE);
+ else if (aId == SADB_AALG_SHA1HMAC)
+ return (HMAC_SHA1_SIZE);
+
+ return (0); // Error
+}
+
+//Exchange Type
+TPtrC CIkev1Negotiation::TextNotifyType(TUint16 aNotif)
+{
+#ifndef _DEBUG
+ (void)aNotif;
+#endif
+
+#ifdef _DEBUG
+ //TBuf<35> err;
+ switch(aNotif)
+ {
+ case INVALID_PAYLOAD_TYPE:
+ return _L("INVALID_PAYLOAD_TYPE");
+ case DOI_NOT_SUPPORTED:
+ return _L("DOI_NOT_SUPPORTED");
+ case SITUATION_NOT_SUPPORTED:
+ return _L("SITUATION_NOT_SUPPORTED");
+ case INVALID_COOKIE:
+ return _L("INVALID_COOKIE");
+ case INVALID_MAJOR_VERSION:
+ return _L("INVALID_MAJOR_VERSION");
+ case INVALID_MINOR_VERSION:
+ return _L("INVALID_MINOR_VERSION");
+ case INVALID_EXCHANGE_TYPE:
+ return _L("INVALID_EXCHANGE_TYPE");
+ case INVALID_FLAGS:
+ return _L("INVALID_FLAGS");
+ case INVALID_MESSAGE_ID:
+ return _L("INVALID_MESSAGE_ID");
+ case INVALID_PROTOCOL_ID:
+ return _L("INVALID_PROTOCOL_ID");
+ case INVALID_SPI:
+ return _L("INVALID_SPI");
+ case INVALID_TRANSFORM_ID:
+ return _L("INVALID_TRANSFORM_ID");
+ case ATTRIBUTES_NOT_SUPPORTED:
+ return _L("ATTRIBUTES_NOT_SUPPORTED");
+ case NO_PROPOSAL_CHOSEN:
+ return _L("NO_PROPOSAL_CHOSEN");
+ case BAD_PROPOSAL_SYNTAX:
+ return _L("BAD_PROPOSAL_SYNTAX");
+ case PAYLOAD_MALFORMED:
+ return _L("PAYLOAD_MALFORMED");
+ case INVALID_KEY_INFORMATION:
+ return _L("INVALID_KEY_INFORMATION");
+ case INVALID_ID_INFORMATION:
+ return _L("INVALID_ID_INFORMATION");
+ case INVALID_CERT_ENCODING:
+ return _L("INVALID_CERT_ENCODING");
+ case INVALID_CERTIFICATE:
+ return _L("INVALID_CERTIFICATE");
+ case CERT_TYPE_UNSUPPORTED:
+ return _L("CERT_TYPE_UNSUPPORTED");
+ case INVALID_CERT_AUTHORITY:
+ return _L("INVALID_CERT_AUTHORITY");
+ case INVALID_HASH_INFORMATION:
+ return _L("INVALID_HASH_INFORMATION");
+ case AUTHENTICATION_FAILED:
+ return _L("AUTHENTICATION_FAILED");
+ case INVALID_SIGNATURE:
+ return _L("INVALID_SIGNATURE");
+ case ADDRESS_NOTIFICATION:
+ return _L("ADDRESS_NOTIFICATION");
+ case NOTIFY_SA_LIFETIME:
+ return _L("NOTIFY_SA_LIFETIME");
+ case CERTIFICATE_UNAVAILABLE:
+ return _L("CERTIFICATE_UNAVAILABLE");
+ case UNSUPPORTED_EXCHANGE_TYPE:
+ return _L("UNSUPPORTED_EXCHANGE_TYPE");
+ case UNEQUAL_PAYLOAD_LENGTHS:
+ return _L("UNEQUAL_PAYLOAD_LENGTHS");
+ case CONNECTED:
+ return _L("CONNECTED");
+ case DOI_RESPONDER_LIFETIME:
+ return _L("RESPONDER_LIFETIME");
+ case DOI_REPLAY_STATUS:
+ return _L("REPLAY_STATUS");
+ case DOI_INITIAL_CONTACT:
+ return _L("INITIAL_CONTACT");
+ }
+ return _L("Unknown ");
+#else
+ return NULL;
+#endif
+}
+
+
+void CIkev1Negotiation::TextPayload(TDes &aBuf, TUint8 aPayload)
+{
+#ifndef _DEBUG
+ (void)aBuf;
+ (void)aPayload;
+#endif
+
+#ifdef _DEBUG
+ switch(aPayload)
+ {
+ case ISAKMP_PAYLOAD_NONE:// (Terminator)
+ aBuf = _L("ISAKMP_PAYLOAD_NONE");
+ break;
+ case ISAKMP_PAYLOAD_SA:// Security Association
+ aBuf = _L("ISAKMP_PAYLOAD_SA");
+ break;
+ case ISAKMP_PAYLOAD_P:// Proposal
+ aBuf = _L("ISAKMP_PAYLOAD_P");
+ break;
+ case ISAKMP_PAYLOAD_T:// Transform
+ aBuf = _L("ISAKMP_PAYLOAD_T");
+ break;
+ case ISAKMP_PAYLOAD_KE:// Key Exchange
+ aBuf = _L("ISAKMP_PAYLOAD_KE");
+ break;
+ case ISAKMP_PAYLOAD_ID:// Identification
+ aBuf = _L("ISAKMP_PAYLOAD_ID");
+ break;
+ case ISAKMP_PAYLOAD_CERT:// Certificate
+ aBuf = _L("ISAKMP_PAYLOAD_CERT");
+ break;
+ case ISAKMP_PAYLOAD_CR:// Certificate Request
+ aBuf = _L("ISAKMP_PAYLOAD_CR");
+ break;
+ case ISAKMP_PAYLOAD_HASH:// Hash
+ aBuf = _L("ISAKMP_PAYLOAD_HASH");
+ break;
+ case ISAKMP_PAYLOAD_SIG:// Signature
+ aBuf = _L("ISAKMP_PAYLOAD_SIG");
+ break;
+ case ISAKMP_PAYLOAD_NONCE:// Nonce
+ aBuf = _L("ISAKMP_PAYLOAD_NONCE");
+ break;
+ case ISAKMP_PAYLOAD_NOTIF:// Notification
+ aBuf = _L("ISAKMP_PAYLOAD_NOTIF");
+ break;
+ case ISAKMP_PAYLOAD_D:// Delete
+ aBuf = _L("ISAKMP_PAYLOAD_D");
+ break;
+ case ISAKMP_PAYLOAD_VID:// Vendor ID
+ aBuf = _L("ISAKMP_PAYLOAD_VID");
+ break;
+ case ISAKMP_PAYLOAD_PRIVATE:// Private use (up to 255)
+ aBuf = _L("ISAKMP_PAYLOAD_PRIVATE");
+ break;
+ default:
+ aBuf.Format(_L("Unknown (%d) "),aPayload);
+ }
+#endif
+}
+
+//Sends the built message through the socket
+void CIkev1Negotiation::SendL(TIkev1IsakmpStream &aMsg)
+{
+ if (aMsg.iError)
+ {
+ DEBUG_LOG(_L("Error Building message"));
+ return;
+ }
+ TBuf8<IKEV1_MAX_IV_SIZE> tmp_IV;
+
+ ThdrISAKMP *hdr = (ThdrISAKMP *)aMsg.iBuf.Ptr();
+ hdr->SetLength(aMsg.iBuf.Length());
+ DEBUG_LOG(_L("Sending (clear)..."));
+
+ TBool FloatedPort = EFalse;
+ if ( iNAT_D_Flags & (REMOTE_END_NAT + LOCAL_END_NAT) )
+ FloatedPort = ETrue;
+
+#ifdef _DEBUG
+ const TPtrC8 ikeMsgPtr( aMsg.iBuf.Ptr(), aMsg.iBuf.Length() );
+ TInetAddr localAddr;
+ iPluginSession->GetLocalAddress( localAddr );
+ TInt port = ( FloatedPort ? IkeSocket::KIkePort4500 : IkeSocket::KIkePort500 );
+ localAddr.SetPort( port );
+ TRACE_MSG_IKEV1( ikeMsgPtr, localAddr, iLastRemoteAddr );
+#endif // _DEBUG
+
+ TPtr8 lastMsg(iLastMsg->Des());
+
+ if (hdr->GetFlags() & ISAKMP_HDR_EFLAG)
+ {
+ DEBUG_LOG(_L("Encrypting..."));
+
+ if (hdr->GetExchange()==ISAKMP_EXCHANGE_INFO || hdr->GetExchange()==ISAKMP_EXCHANGE_TRANSACT )
+ {
+ if ( hdr->GetExchange()==ISAKMP_EXCHANGE_TRANSACT )
+ {
+ //
+ // Get current IV via CTransNegotiation object linked into
+ // CIkev1Negotiation
+ //
+ if ( !iTransactionNeg ||
+ !iTransactionNeg->GetIV(hdr->GetMessageId(), tmp_IV) )
+ {
+ DEBUG_LOG(_L("Send error ! Cannot get Transaction IV !"));
+ return;
+ }
+ DEBUG_LOG(_L("Transaction IV"));
+ EncryptL(aMsg.iBuf, lastMsg, tmp_IV, iSKEYID_e, iChosenProposal_I.iAttrList->iEncrAlg);
+ iTransactionNeg->SetIV(hdr->GetMessageId(), tmp_IV);
+ }
+ else
+ {
+ if (iLastIV.Length() != 0)
+ tmp_IV.Copy(iLastIV);
+ else //iLastIV not yet computed so current iIV is used
+ tmp_IV.Copy(iIV);
+ ComputeIVL(tmp_IV, hdr->GetMessageId());
+ DEBUG_LOG(_L("Notif IV"));
+ EncryptL(aMsg.iBuf, lastMsg, tmp_IV, iSKEYID_e, iChosenProposal_I.iAttrList->iEncrAlg);
+ }
+
+ }
+ else //Normal exchange MAIN, AGGR or QUICK
+ {
+ DEBUG_LOG(_L("IV"));
+ EncryptL(aMsg.iBuf, lastMsg, iIV, iSKEYID_e, iChosenProposal_I.iAttrList->iEncrAlg);
+ DEBUG_LOG(_L("New IV (dec)"));
+ //Saves last iIV in Phase 1
+ if (((iStage==6) && (iExchange == ISAKMP_EXCHANGE_ID)) ||
+ ((iStage==3) && (iExchange == ISAKMP_EXCHANGE_AGGR)))
+ {
+ iLastIV.Copy(iIV);
+ DEBUG_LOG(_L("Last IV Saved!"));
+ }
+ }
+
+ DEBUG_LOG(_L("Sending ..."));
+ DEBUG_LOG1(_L("EncrLen = %d"), lastMsg.Length());
+ }
+ else
+ {
+ lastMsg.Copy(aMsg.iBuf);
+ }
+
+ hdr = (ThdrISAKMP *)lastMsg.Ptr();
+ hdr->SetLength(lastMsg.Length()); //Set the total length!!!
+
+ if (hdr->GetExchange() == ISAKMP_EXCHANGE_INFO)
+ {
+ SendAndSaveIkeMsgL(lastMsg, iLastRemoteAddr, FloatedPort); //No timers!
+ }
+ else //Normal msg.
+ {
+ iTimer->Cancel(); //Cancel previous timer because reply received & processed
+ DEBUG_LOG(_L("Timer Cancelled!"));
+ iRetryNum = 0;
+ SendAndSaveIkeMsgL(lastMsg, iLastRemoteAddr, FloatedPort);
+ iTimer->IssueRequest(MAX_RETRANS_TIMER * 1000000); // 1000000 = 1 second
+ }
+
+}
+
+
+void CIkev1Negotiation::ReSendL()
+{
+ //Will resend a packet in the interval (MAX_RETRANS_TIMER/2 , MAX_RETRANS_TIMER)
+ if ( iRetryNum < MAX_RETRANS_COUNT )
+ {
+ DEBUG_LOG2(_L("---------- Phase %d - Stage %d ----------"),iPhase, iStage - 1);
+ DEBUG_LOG1(_L("ReSending(%d)..."), iRetryNum);
+ TBool FloatedPort = EFalse;
+ if ( iNAT_D_Flags & (REMOTE_END_NAT + LOCAL_END_NAT) )
+ FloatedPort = ETrue;
+ TPtr8 lastMsg(iLastMsg->Des());
+ iPluginSession->SendIkeMsgL(lastMsg, iLastRemoteAddr, FloatedPort);
+ //next retransmission between MAX_RETRANS_TIMER/2 and MAX_RETRANS_TIMER seconds
+ TTime tmp_time;
+ TReal secs = 0;
+ tmp_time.UniversalTime();
+ TInt64 seed = tmp_time.Int64();
+ TInt rand = Math::Rand(seed);
+ TInt err = Math::Round(secs, rand / (KMaxTInt / MAX_RETRANS_TIMER/2), 0);
+ secs = Math::Round(secs, secs + MAX_RETRANS_TIMER/2, 0);
+ if ((!secs) || (err != KErrNone))
+ secs = MAX_RETRANS_TIMER/2;
+ iTimer->IssueRequest((TInt)secs * 1000000); // 1000000 = 1 second
+ iRetryNum++;
+ }
+ else
+ {
+ SendDeleteL(PROTO_ISAKMP);
+ if ( iPhase == PHASE_I )
+ {
+ DEBUG_LOG(_L("Max num retries reached!!!"));
+ }
+ else
+ {
+ DEBUG_LOG(_L("Quick mode failed, Max num retries reached!!!"));
+ }
+
+ if ( iPhase == PHASE_I &&
+ iPluginSession->FindIkev1SA() == NULL )
+ {
+ // Set error status in Phase 1, if there are no IKE SAs.
+ if ( GetNotifyStatus() )
+ SetErrorStatus(KKmdIkeNoProposalErr);
+ else SetErrorStatus(KKmdIkeNoResponseErr);
+ }
+
+ LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
+ R_VPN_MSG_VPN_GW_NO_RESP,
+ ErrorStatus(),
+ iPluginSession->VpnIapId(),
+ &iRemoteAddr );
+ iPluginSession->DeleteNegotiation( this );
+ }
+}
+
+void CIkev1Negotiation::SendDeleteL(TUint8 aProtocol, TUint32 aIpsecSPI)
+{
+ TIkev1IsakmpStream* msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
+
+ TBuf8<MAX_SPI_SIZE> SPI;
+
+ DEBUG_LOG(_L("Sending Delete payload..."));
+
+ //Creates a DUMMY negotiation with the info to send the packet
+ TUint8 tmp_exchange = iExchange;
+ iExchange=ISAKMP_EXCHANGE_INFO; //Set the exchange type as info
+
+ msg->IsakmpInit(this);
+
+ //HASH Payload only if payload protected with encyption
+ if (iFlags & ISAKMP_HDR_EFLAG)
+ msg->IsakmpHashL();
+
+ if (aProtocol == PROTO_ISAKMP) //ISAKMP SPI are the cookies
+ {
+ SPI.Copy(iCookie_I);
+ SPI.Append(iCookie_R);
+ }
+ else //IPSECSPI
+ {
+ SPI.Copy((TUint8 *)&aIpsecSPI, sizeof(TUint32));
+ }
+ msg->IsakmpDelete(SPI, aProtocol);
+
+ if (iFlags & ISAKMP_HDR_EFLAG)
+ {
+ msg->IsakmpHashContL();
+ }
+ DEBUG_LOG(_L("Sending Delete Payload..."));
+ SendL(*msg);
+
+ iExchange = tmp_exchange;
+}
+
+void CIkev1Negotiation::CheckSendResponderLifetime(TIkev1IsakmpStream &aMsg)
+{
+ TInt count = iChosenProp_IIList->Count();
+ CProposal_II *prop;
+ TChosenAttrib_II *attr_II;
+ TSPINode inboundspi_node;
+ TInt j;
+ for (j = 0 ; j < count; j++) //Check all the chosen proposals (Probably one)
+ {
+ prop = iChosenProp_IIList->At(j);
+ inboundspi_node = iInboundSPIList->At(j);
+ attr_II = (TChosenAttrib_II *)prop->iAttrList->At(0); //only 1 transform is chosen no matter how many there are
+
+ if ((attr_II->iReducedLifeSecs.Length() != 0) || (attr_II->iReducedLifeKBytes.Length() != 0)) //Any lifetime to update
+ aMsg.IsakmpResponderLifetime(prop->iProtocol, inboundspi_node.iSPI, attr_II->iReducedLifeSecs, attr_II->iReducedLifeKBytes);
+
+ }
+}
+
+
+/**--------------------------------------------------------------------
+ *
+ * The following methods are used to implement the Dead Peer Detection
+ * protocol defined in <draft-ietf-ipsec-dpd-04.txt>
+ * When timeout expires the R-U-THERE notify message is transmitted
+ * if there has not been any activity during the last timeout
+ *
+ *--------------------------------------------------------------------*/
+void CIkev1Negotiation::DpdNotifyMessageReceivedL(TIkev1SAData* aSa, TUint16 aMsgType, TUint32 aSequence)
+{
+
+ if ( aMsgType == DPD_R_U_THERE )
+ {
+ //
+ // -- Assure that sequence number in notify data is what expected
+ // -- If ok, transmit a R-U-THERE-ACK
+ //
+ DEBUG_LOG(_L("DPD R-U-THERE Notify received"));
+ if ( (aSa->iExpectedDPDSequence == aSequence) || (aSa->iExpectedDPDSequence == 0) )
+ {
+ aSa->iExpectedDPDSequence = GetNextSequence(aSequence);
+ DEBUG_LOG(_L("Sending DPD R-U-THERE_ACK notify"));
+ SendDpdNotifyMessageL(DPD_R_U_THERE_ACK, aSequence);
+ iPluginSession->UpdateIkev1SAL(aSa->iSAId, EFalse, aSa);
+ }
+#ifdef _DEBUG
+ else DEBUG_LOG(_L("Wrong sequence number in DPD notify message"));
+#endif // _DEBUG
+ }
+ else if ( aMsgType == DPD_R_U_THERE_ACK )
+ {
+ //
+ // -- Assure that sequence number in notify data is corresponds
+ // current pending sequence
+ //
+ DEBUG_LOG(_L("DPD R-U-THERE-ACK Notify received"));
+ if ( aSa->iPendingDPDSequence == aSequence )
+ {
+ aSa->iPendingDPDSequence = 0;
+ aSa->iDPDRetry = 0;
+ iPluginSession->UpdateIkev1SAL(aSa->iSAId, EFalse, aSa);
+ }
+#ifdef _DEBUG
+ else DEBUG_LOG(_L("Wrong sequence number in DPD notify ack message"));
+#endif // _DEBUG
+ }
+}
+
+void CIkev1Negotiation::SendKeepAliveMsgL(TIkev1SAData* aSa)
+{
+ if ( aSa->iDPDRetry > KMaxDpdRetryCount )
+ {
+ //
+ // DPD Retry count exhausted, current IKE SA in interpreted to
+ // be closed
+ //
+ LOG_KMD_EVENT( MKmdEventLoggerIf::KLogError,
+ R_VPN_MSG_VPN_GW_NO_RESP,
+ KKmdIkeNoResponseErr,
+ iPluginSession->VpnIapId(),
+ &iRemoteAddr );
+
+ iPluginSession->DeleteIpsecSAs(aSa->iSAId);
+ iPluginSession->UpdateIkev1SAL(aSa->iSAId, ETrue);
+ }
+ else
+ {
+ //
+ // Send DPD R-U-THERE notify message
+ //
+ if ( aSa->iPendingDPDSequence == 0 )
+ {
+ aSa->iPendingDPDSequence = aSa->iDPDSequence;
+ aSa->iDPDSequence = GetNextSequence(aSa->iDPDSequence);
+ }
+ else
+ {
+ aSa->iDPDRetry ++;
+ }
+ DEBUG_LOG(_L("Sending DPD R-U-THERE notify"));
+ SendDpdNotifyMessageL(DPD_R_U_THERE, aSa->iPendingDPDSequence);
+ }
+ SetFinished();
+}
+
+MKmdEventLoggerIf& CIkev1Negotiation::EventLogger()
+{
+ return iPluginSession->EventLogger();
+}
+
+void CIkev1Negotiation::IpsecSaSpiRetrieved(TUint32 aSpiRequestId,
+ TInt aStatus,
+ TUint32 aSpi)
+{
+ DEBUG_LOG3(_L("IPsec SA SPI retrieved, seq=%d, SPI=%d, status=%d"),
+ aSpiRequestId, aSpi, aStatus);
+ if ( aStatus == KErrNone )
+ {
+ TRAP( aStatus, ReceiveSPIL( aSpi, aSpiRequestId ) );
+ }
+ else
+ {
+ iPluginSession->HandleError( aStatus );
+ }
+}
+
+TBool CIkev1Negotiation::IsRekeyingIkeSa()
+{
+ return ( iSARekeyInfo != NULL );
+}
+
+void CIkev1Negotiation::PreparePhase2L(const TPfkeyMessage &aReq)
+{
+ DEBUG_LOG(_L("Prepare for Phase II"));
+ GetAcquireDataL(aReq);
+ iPhaseIIAfterIkeSaRekey = ETrue;
+}
+
+TUint32 CIkev1Negotiation::GetNextSequence(TUint32 aSequence)
+{
+ aSequence ++;
+ if ( aSequence == 0 )
+ aSequence = 1;
+ return aSequence;
+}
+
+void CIkev1Negotiation::SendDpdNotifyMessageL(TUint16 aMsgType, TUint32 aSequence)
+{
+ iExchange = ISAKMP_EXCHANGE_INFO;
+ iMessageId = RandomMessageId();
+ TIkev1IsakmpStream* Msg = SaveIkeMsgBfr( new (ELeave) TIkev1IsakmpStream(iDebug) );
+ Msg->IsakmpInit(this);
+ Msg->IsakmpHashL();
+ TUint32 NotifData = ByteOrder::Swap32(aSequence);
+ Msg->IsakmpNotification(aMsgType, PROTO_ISAKMP, (TUint8*)&NotifData, sizeof(NotifData));
+ Msg->IsakmpHashContL();
+ SendL(*Msg);
+}
+
+TInt CIkev1Negotiation::ErrorStatus()
+{
+ TInt ret( KErrNone );
+ if ( iPluginSession )
+ {
+ ret = iPluginSession->ErrorStatus();
+ }
+ return ret;
+}
+
+void CIkev1Negotiation::SetErrorStatus(TInt aStatus)
+{
+ SetFinished();
+ iPluginSession->SetErrorStatus(aStatus);
+}
+
+void CIkev1Negotiation::SendAndSaveIkeMsgL( const TDesC8& aIkeMsg,
+ TInetAddr& aDestAddr,
+ TBool aUseNatPort )
+{
+ iPluginSession->SendIkeMsgL( aIkeMsg, aDestAddr, aUseNatPort );
+ SaveLastMsgL();
+}
+
+
+TBool CIkev1Negotiation::IsRetransmit(TLastIKEMsg& aRef)
+{
+ TBool isRetransmit(EFalse);
+ if (iLastIKEMsgInfo.IsUninitialized())
+ {
+ TIkev1SAData* ikev1SAData = iPluginSession->FindIkev1SAData(iSAId);
+ if (ikev1SAData && ikev1SAData->iLastIKEMsgInfo.IsReTransmit(aRef))
+ {
+ isRetransmit = ETrue;
+ }
+ }
+ else
+ {
+ isRetransmit = iLastIKEMsgInfo.IsReTransmit(aRef);
+ }
+ return isRetransmit;
+}
+
+void CIkev1Negotiation::SaveRetransmitInfo(TLastIKEMsg& aRef)
+{
+ aRef.Store(iLastIKEMsgInfo);
+ TIkev1SAData* ikev1SAData = iPluginSession->FindIkev1SAData(iSAId);
+ if (ikev1SAData != NULL)
+ {
+ aRef.Store(ikev1SAData->iLastIKEMsgInfo);
+ }
+}
+
+void CIkev1Negotiation::SaveLastMsgL()
+{
+ if ( iLastMsg != NULL )
+ {
+ TIkev1SAData* ikev1SAData = iPluginSession->FindIkev1SAData(iSAId);
+ if ( ikev1SAData != NULL )
+ {
+ delete ikev1SAData->iLastMsg;
+ ikev1SAData->iLastMsg = iLastMsg->AllocL();
+ }
+ }
+}
+
+
+