diff -r 000000000000 -r 33413c0669b9 vpnengine/ikev1lib/src/ikev1negotiation.cpp --- /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 +#include +#include +#include + +#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 *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: + + 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 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(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; jCount(); 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),] PubKey_r, 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, PubKey_i, PubKey_i +// RSA Revised Encr. : HDR, PubKey_i , Ke_r, 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 *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* 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 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 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* 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 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 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) + 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 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 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 + * 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(); + } + } +} + + +