diff -r 000000000000 -r 33413c0669b9 vpnengine/ikev2lib/src/ikev2negotiation.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vpnengine/ikev2lib/src/ikev2negotiation.cpp Thu Dec 17 09:14:51 2009 +0200 @@ -0,0 +1,4059 @@ +/* +* 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: IKEv2/IPSEC SA negotiation +* +*/ + +#include +#include + +#include "ikev2Negotiation.h" +#include "ikedebug.h" +#include "ikev2natt.h" +#include "ikev2mobike.h" +#include "ikev2proposal.h" +#include "ikev2SAdata.h" +#include "ikev2pluginsession.h" +#include "ikev2pfkey.h" +#include "ikev2config.h" +#include "ikev2EapInterface.h" +#include "ikev2payloads.h" +#include "ikev2const.h" +#include "ikemsgrec.h" +#include "ipsecproposal.h" +#include "ipsecselectors.h" +#include "ikepolparser.h" +#include "kmdapi.h" +#include "ikecaelem.h" +#include "ikecalist.h" +#include "ikepkiutils.h" +#include "vpnapidefs.h" +#include "kmdeventloggerif.h" +#include "ipsecsalist.h" +#include "ikev2message.h" +#include "ikev2identity.h" +#include "ikev2acquire.h" +#include "ikev2expire.h" +#include "ikev2ipsecsarekeydata.h" +#include "ikev2messagesendqueue.h" + +_LIT8(KIkev2PSKData, "Key Pad for IKEv2"); +_LIT8(KZeroDesc, ""); + +CIkev2Negotiation* CIkev2Negotiation::NewL(CIkev2PluginSession& aIkeV2PlugInSession, + CPFKeySocketIf& aPfKeySocketIf, + MKmdEventLoggerIf& aEventLogger, + CIkev2MessageSendQueue& aMessageSendQue, + MIkeDebug& aDebug, + CIkeData* aIkeData, + TUint32 aVpnIapId, + TUint32 aSaId, + TInetAddr aPhysicalInterfaceAddress, + TInetAddr aRemoteAddress) + { + + CIkev2Negotiation* self = new (ELeave) CIkev2Negotiation(aIkeV2PlugInSession, aPfKeySocketIf, + aEventLogger, aMessageSendQue, + aDebug, aSaId); + CleanupStack::PushL(self); + self->ConstructL(); + + self->iHdr.iIkeData = aIkeData; + self->iHdr.iVpnIapId = aVpnIapId; + self->iProcessEvents = ETrue; + self->iHdr.iRemoteAddr = aRemoteAddress; + self->iHdr.iRemoteAddr.SetPort(IKE_PORT); + + // + // Get IP address information for IKE SA negotiation + // Remote address is taken from current IKE policy data (CIkeData) + // Local address is resolved via IKE policy using policy handle + // + if ( self->iHdr.iRemoteAddr.IsUnspecified() ) + { + self->iHdr.iRemoteAddr = self->iHdr.iIkeData->iAddr; + self->iHdr.iRemoteAddr.SetPort(IKE_PORT); + } + self->iHdr.iDestinAddr = self->iHdr.iRemoteAddr; + self->iHdr.iLocalAddr = aPhysicalInterfaceAddress; + TInt Scope = self->iHdr.iRemoteAddr.Scope(); + if ( Scope ) + self->iHdr.iLocalAddr.SetScope(Scope); // Set local scope same with remote scope + + CleanupStack::Pop(self); + return self; + } + + +CIkev2Negotiation* CIkev2Negotiation::NewL(CIkev2PluginSession& aIkeV2PlugInSession, + CPFKeySocketIf& aPfKeySocketIf, + MKmdEventLoggerIf& aEventLogger, + CIkev2MessageSendQueue& aMessageSendQue, + MIkeDebug& aDebug, + TIkev2SAData& aIkev2SAdata) + { + CIkev2Negotiation* self = new (ELeave) CIkev2Negotiation(aIkeV2PlugInSession, aPfKeySocketIf, + aEventLogger, aMessageSendQue, + aDebug, aIkev2SAdata.SaId()); + CleanupStack::PushL(self); + self->ConstructL(); + + self->iHdr.Copy(aIkev2SAdata); + self->iState = KStateIkeSaCompleted; + + CleanupStack::Pop(self); + return self; + } + + +CIkev2Negotiation::CIkev2Negotiation(CIkev2PluginSession& aIkeV2PlugInSession, CPFKeySocketIf& aPfKeySocketIf, + MKmdEventLoggerIf& aEventLogger, CIkev2MessageSendQueue& aMessageSendQue, + MIkeDebug& aDebug, TUint32 aSaId) +: iChild(aDebug), iIkeV2PlugInSession(aIkeV2PlugInSession), iPfKeySocketIf(aPfKeySocketIf), + iEventLogger(aEventLogger), iMessageSendQue(aMessageSendQue),iDebug(aDebug), iDHGroupGuess(1) + { + DEBUG_LOG1(_L("CIkev2Negotiation::CIkev2Negotiation: 0x%08x"), this); + + iHdr.SetSaId(aSaId); + iHdr.iWindowSize = DEF_MSG_ID_WINDOW; + } + + +void CIkev2Negotiation::ConstructL() + { + iTimer = CIkev2RetransmitTimer::NewL(*this); + iSpiRetriever = CIpsecSaSpiRetriever::NewL(*this, iPfKeySocketIf); + + iIkeV2PlugInSession.LinkNegotiation(this); // <- takes ownership of this + iProcessEvents = ETrue; + } + + +CIkev2Negotiation::~CIkev2Negotiation() + { + + delete iSpiRetriever; + // Turn off event processing to prevent EAPVPNIF event + iProcessEvents = EFalse; + delete iTimer; + + DEBUG_LOG1(_L("CIkev2Negotiation::~CIkev2Negotiation: 0x%08x"), this); + iIkeV2PlugInSession.RemoveNegotiation(this); + + iHdr.CleanUp(); + + // + // Purge Acquire, Expire and Info message queues + // + CIkev2Acquire::PurgeQue(GetAcquireQue()); + CIkev2Expire::PurgeQue(GetExpireQue()); + + delete iPeerCert; + delete iSavedSaInit; + delete iProposedSA; + delete iDHKeys; + delete iDHPublicPeer; + delete iNonce_I; + delete iNonce_R; + delete iAuthMsgInit; + delete iAuthMsgResp; + delete iRemoteIdentity; + delete iLocalIdentity; + delete iNatNotify; + delete iConfigMode; + delete iEapPlugin; + delete iPkiService; + delete iPresharedKey; + delete iChildSaRequest; + } + +void CIkev2Negotiation::StartIkeSANegotiationL() + { + __ASSERT_DEBUG(iChildSaRequest == NULL, User::Invariant()); + + //This method should be called only if we have IA in use. + //Otherwise the negotiation should start with ProcessAcquire + __ASSERT_DEBUG(iHdr.iIkeData->iUseInternalAddr, User::Invariant()); + + // + // This method is called when an IKE SA negotiation is started due + // a RKMD::Activate() request with policy that uses IA. + // + iHdr.iInitiator = ETrue; + LoadEapPluginL(); + + GetNonceDataL(ETrue); + + CIkev2Acquire* Acquire = IpsecSelectors::BuildVirtualAcquireL(iIkeV2PlugInSession); + CleanupStack::PushL(Acquire); + + if ( !InitPkiServiceL() ) + { + //No PkiService Needed. + //Continue by requesting SPI for IPsecSA. + CIkev2Acquire::Link(Acquire, GetAcquireQue()); + // + // Get SPI for inbound SA with PFKEY GETSPI primitive + // + GetIpsecSPI(Acquire); + } + else + { + iChildSaRequest = Acquire; + } + CleanupStack::Pop(Acquire); + } + +TBool CIkev2Negotiation::StartRespondingL(const ThdrISAKMP& aIkeMessage) + { + // + // This method is called when local end is going to ACT as a + // responder of an IKE SA negotiation. + // Initialize PKI service usage, if needed. Because PKI service + // initialisation is an asynchronous operation we must take a copy + // of incoming IKE message from where it is processed when PKI + // service initialisation is completed. + // + TBool Status( InitPkiServiceL() ); + if ( Status ) + { + TInt MsgLth = (TInt)aIkeMessage.GetLength(); + delete iSavedSaInit; + iSavedSaInit = NULL; + iSavedSaInit = HBufC8::NewL(MsgLth); + iSavedSaInit->Des().Copy((TUint8*)&aIkeMessage, MsgLth); + } + return !Status; + } + +void CIkev2Negotiation::StartIkeSADeleteL() +{ + // + // This method is called when an IKE SA shall be deleted either due + // IKE SA timeout or due a RKMD::Deactivate() request + // + BuildDeleteRequestL(NULL); +} + + +void CIkev2Negotiation::IkeSaCompletedL() +{ + + // + // This method is when an IKE SA negotiation has been succesfully + // completed. + // The following actions are taken: + // -- Get Virtual IP from iConfigMode object, if present and + // modify IKE SA lifetime if Virtual Ip expiration time is + // shorter than configured iKE SA lifetime + // -- Create a new IKE SA object, if not a rekeyd IKE SA + // -- If activation going, call IkeSaCompleted method in plug-in + // + TVPNAddress VirtualIp; + if ( iConfigMode ) + { + VirtualIp = iConfigMode->VirtualIp(); + iHdr.StoreVirtualIp(VirtualIp.iVPNIfAddr); + TUint32 ExpireTime = iConfigMode->ExpireTime(); + if ( ExpireTime && (ExpireTime < iHdr.iLifetime) ) + iHdr.iLifetime = ExpireTime; + } + + if(!iIkeV2PlugInSession.FindIkev2SA(iHdr.SaId(), KSaStateNotDefined, KSaStateNotDefined)) + { + iIkeV2PlugInSession.CreateIkev2SAL(iHdr); + } + + iIkeV2PlugInSession.IkeSaCompleted(KErrNone, VirtualIp); + + iEventLogger.LogEvent(MKmdEventLoggerIf::KLogInfo, R_VPN_MSG_VPN_GW_AUTH_OK, KErrNone, + iHdr.iVpnIapId, &iHdr.iRemoteAddr); + iEventLogger.LogEvent(MKmdEventLoggerIf::KLogInfo, R_VPN_MSG_ADDR_INFO_FOR_VPN_AP, + iHdr.iNATFlags, iHdr.iVpnIapId, + (!VirtualIp.iVPNIfAddr.IsUnspecified() ? &(VirtualIp.iVPNIfAddr) : NULL)); + + if ( iChildSaRequest ) + { + IpsecSANegotiatedL(); + } + if ( RequestsPending() ) + { + ContinueIkeNegotiationL(); + } + else + { + if ( !iHdr.iInitiator ) + { + iIkeV2PlugInSession.StopResponding(); + delete this; // Current negotiation can be deleted + } + else iStopped = ETrue; + } +} + +void CIkev2Negotiation::IkeSaFailed(TInt Status) + { + // + // This method is when a IKE SA negotiation has failed + // The following actions are taken: + // + + TVPNAddress dummyVirtualIp; + + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + + if ( !iHdr.iInitiator ) + iIkeV2PlugInSession.StopResponding(); + + if ( (iSendAttempt <= KMaxSendAttemps ) && + ((iState == KStateIkeSaEapStarted) || + (iState == KStateIkeSaEapGoing))) + iDeleteIkeSA = ETrue; + else iStopped = ETrue; + + iEventLogger.LogEvent(MKmdEventLoggerIf::KLogError, R_VPN_MSG_REAL_IAP_ACT_FAILED, Status, + iHdr.iVpnIapId, &iHdr.iRemoteAddr); + + iIkeV2PlugInSession.IkeSaCompleted(Status, dummyVirtualIp); + } + +void CIkev2Negotiation::IpsecSANegotiatedL() +{ + // + // This method is when an Ipsec SA negotiation has been succesfully + // completed. + // -- Update Ipsec SADB using PFKEY Update and Add primitives + // -- Find a new IKE SA object and queue Ipsec SA data into it + // -- Try to start a new exchange from queue, if there is nothing + // to start in queues mark current negotiation stopped + // + iChild.iSrcSpecific = iChildSaRequest->SrcSpecific(); + Ikev2Pfkey::UpdateIpsecSaDataBaseL(iHdr, iChild, iIkeV2PlugInSession, *iChildSaRequest); + + CIpsecSARekeyData* rekeyData = + CIpsecSARekeyData::NewL(iChildSaRequest->ReplayWindow(), + iChildSaRequest->HardLifetime(), + iChildSaRequest->SoftLifetime(), + iChildSaRequest->TS_i(), + iChildSaRequest->TS_r(), + *iChildSaRequest->LocalId(), + *iChildSaRequest->RemoteId()); + + iChild.PurgeKeyMaterial(); // Ipsec Keymaterial not saved into IKE SA + iChild.DeleteRekeyData(); + iChild.iRekeyData = rekeyData; + + iIkeV2PlugInSession.UpdateIkev2SAL(&iHdr, &iChild); + + delete iChildSaRequest; + iChildSaRequest = NULL; + + if ( RequestsPending() ) + ContinueIkeNegotiationL(); + else + { if ( iState == KStateIkeChildSAResponse ) + delete this; + else iStopped = ETrue; + } +} + + +void CIkev2Negotiation::ProcessIkeMessageL(const ThdrISAKMP& aIkeMessage, + const TInetAddr& aRemote, + TUint16 aLocalPort) + { + // + // Start to process received IKE message by constructing a + // CIkev2Payloads object. CIkev2Payloads construction takes also + // care of the decryption of an Encrypted payload if present. + // + TBool Status( ETrue ); + + CIkev2Payloads* IkeMsg = CIkev2Payloads::NewL(aIkeMessage, iHdr); + CleanupStack::PushL(IkeMsg); + + DEBUG_LOG2(_L("Process IKE message, SAID=%d, Msg ID=%d"), + iHdr.SaId(), aIkeMessage.GetMessageId()); + if ( IkeMsg->Status() ) + { + // + // An error occurred during IKE message parsing + // + SetNotifyCode(IkeMsg->Status()); + DEBUG_LOG1(_L("Error in parsing of received IKE message: %d"), IkeMsg->Status()); + + if ( !iHdr.iInitiator && iState == KStateIdle ) + { + iStopped = ETrue; // Negotiation object shall be released + } + else + { + CheckNotifyCodeL(IkeMsg); + } + CleanupStack::PopAndDestroy(IkeMsg); // IkeMsg + return; + } + + if ( (iHdr.iNATFlags & (REMOTE_END_NAT + MOBIKE_USED)) && + IkeMsg->Encrypted() ) + { + // + // Received IKE message contains Encrypted payload. Save source + // IP as new destination IP to negotiation object + // + iHdr.iDestinAddr = aRemote; + iHdr.iDestinAddr.SetPort(FLOATED_IKE_PORT); + } + + TPtrC8 ikeMsgDes((TUint8*)&aIkeMessage, aIkeMessage.GetLength()); + + TInetAddr localAddr(iHdr.iLocalAddr); + localAddr.SetPort(aLocalPort); + TRACE_MSG(ikeMsgDes, aRemote, localAddr, + (CIkePcapTrace::TEncryptionType)iHdr.iEncrAlg); + + // + // Process received IKE message according to Exchange type + // + switch ( aIkeMessage.GetExchange() ) + { + case IKE_SA_INIT: + DEBUG_LOG(_L("IKE_SA_INIT message received")); + Status = ProcessIkeSaInitL(IkeMsg, aRemote); + if ( !Status ) + IkeSaFailed(KKmdIkeNegotFailed); + break; + + case IKE_AUTH: + DEBUG_LOG(_L("IKE_AUTH message received")); + Status = ProcessIkeAuthL(IkeMsg); + if ( !Status ) + IkeSaFailed(KKmdIkeAuthFailedErr); + break; + + case CREATE_CHILD_SA: + DEBUG_LOG(_L("CREATE_CHILD_SA message received")); + Status = ProcessChildSaL(IkeMsg); + break; + + case INFORMATIONAL: + DEBUG_LOG(_L("INFORMATION message received")); + Status = ProcessInfoMsgL(IkeMsg); + break; + + default: + DEBUG_LOG(_L("UNKNOWN message received\n")); + Status = EFalse; // Negotiation object shall be released + break; + } + + if ( !Status ) + { + if ( iDeleteIkeSA ) + { + // + // Used IKE SA shall be deleted due the fatal error occurred. + // + iDeleteIkeSA = EFalse; + iIkeV2PlugInSession.DeleteIkev2SA(iHdr.SaId()); + BuildDeleteRequestL(NULL); + } + else + { + CheckNotifyCodeL(IkeMsg); + } + } + CleanupStack::PopAndDestroy(IkeMsg); +} + +void CIkev2Negotiation::ProcessAcquireL(const TPfkeyMessage &aPfkeyMsg) + { + // + // Process received PFKEY Acquire primitive + // There is now the following possibilities: + // -- There already exists an IKE SA so new IPSEC SA is negotiated + // using IKE_CHILD_SA exchange + // -- The is no IKE SA yet. + // IPSEC SA can be negotiated concatenated during IKE_AUTH. + // If Virtual IP is specified, the CP payload is used to get + // that virtual IP address. + // + CIkev2Acquire* Acquire = CIkev2Acquire::NewL(aPfkeyMsg, iIkeV2PlugInSession.GetSAId(), + GetLocalAddr(), + Ikev2Proposal::GetDHGroup(iHdr.iIkeData->iGroupDesc_II), ImplicitChildSa()); + + if ( iState == KStateIdle ) + { + CleanupStack::PushL(Acquire); + LoadEapPluginL(); + iHdr.iInitiator = ETrue; + GetNonceDataL(ETrue); // For IKE SA + if ( iHdr.iIkeData->iUseInternalAddr ) + { + CArrayFix* TsI = new (ELeave) CArrayFixFlat(1); + CleanupStack::PushL(TsI); + + TInetAddr StartIp; + TInetAddr EndIp; + StartIp.SetAddress(KInetAddrNone); // 0.0.0.0 + StartIp.SetPort(0); + EndIp.SetAddress(KInetAddrAll); // 255.255.255.255 + EndIp.SetPort(0xffff); + + TIkeV2TrafficSelector ts(StartIp, EndIp, + aPfkeyMsg.iDstAddr.iExt->sadb_address_proto); + TsI->AppendL(ts); + CleanupStack::Pop(TsI); + Acquire->ReplaceTS_i(TsI); + Acquire->SetVirtualIp(); + } + + if ( InitPkiServiceL() ) + { + // Store Acquire to wait PKI service init + iChildSaRequest = Acquire; + CleanupStack::Pop(Acquire); + return; + } + CleanupStack::Pop(Acquire); + } + CIkev2Acquire::Link(Acquire, GetAcquireQue()); + GetIpsecSPI(Acquire); + } + + +void CIkev2Negotiation::ProcessExpireL(const TPfkeyMessage &aPfkeyMsg) + { + // + // Process received PFKEY Expire primitive + // Try to find first IPSEC SA data from the "parent" IKE SA and set + // inbound SA to zero in TIpsecSAData + // + TPtrC8 spi(reinterpret_cast(&aPfkeyMsg.iSa.iExt->sadb_sa_spi), + sizeof(aPfkeyMsg.iSa.iExt->sadb_sa_spi)); + + TIkeV2IpsecSAData* SaData = + iIkeV2PlugInSession.FindIpsecSAData(iHdr.SaId(), spi, ETrue); + if ( !SaData ) + { + DEBUG_LOG(_L("PFKEY Expire received but no SA data found, stop negotiation")); + + iStopped = ETrue; + return; + } + SaData->iSPI_In.Zero(); + CIkev2Expire* Expire = CIkev2Expire::NewL(aPfkeyMsg); + CIkev2Expire::Link(Expire, GetExpireQue()); + + ContinueIkeNegotiationL(); +} + +void CIkev2Negotiation::StartIpsecSaRekeyingL(const TPfkeyMessage &aPfkeyMsg) +{ + + TPtrC8 spi(reinterpret_cast(&aPfkeyMsg.iSa.iExt->sadb_sa_spi), + sizeof(aPfkeyMsg.iSa.iExt->sadb_sa_spi)); + TIkeV2IpsecSAData* SaData = + iIkeV2PlugInSession.FindIpsecSAData(iHdr.SaId(), spi, ETrue); + if ( !SaData ) + { + DEBUG_LOG(_L("No IPSec SA data found, stop rekeying")); + iStopped = ETrue; + return; + } + + iStopped = ETrue; + + CArrayFix* tsIArray = SaData->iRekeyData->TsIL(); + CleanupStack::PushL(tsIArray); + + CArrayFix* tsRArray = SaData->iRekeyData->TsRL(); + CleanupStack::PushL(tsRArray); + + __ASSERT_DEBUG(tsIArray->Count() > 0, User::Invariant()); + __ASSERT_DEBUG(tsRArray->Count() > 0, User::Invariant()); + + TIkeV2TrafficSelector tsI = (*tsIArray)[0]; + TIkeV2TrafficSelector tsR = (*tsRArray)[0]; + + CleanupStack::PopAndDestroy(tsRArray); + CleanupStack::PopAndDestroy(tsIArray); + + + TInetAddr localSelector; + TInetAddr localSelectorMask; + + TInetAddr remoteSelector; + TInetAddr remoteSelectorMask; + + if (iHdr.iInitiator) + { + localSelector = tsI.StartingAddress(); + localSelectorMask = tsI.Mask(); + + remoteSelector = tsR.StartingAddress(); + remoteSelectorMask = tsR.Mask(); + } + else + { + localSelector = tsR.StartingAddress(); + localSelectorMask = tsR.Mask(); + + remoteSelector = tsI.StartingAddress(); + remoteSelectorMask = tsI.Mask(); + } + + CIpsecSaSpecList* SaList = iIkeV2PlugInSession.GetIPsecSaSpecListL(localSelector, localSelectorMask, //local address/port info + remoteSelector, remoteSelectorMask, + aPfkeyMsg.iDstAddr.iExt->sadb_address_proto); + + + CleanupStack::PushL(SaList); + __ASSERT_DEBUG(SaList != NULL, User::Invariant()); + __ASSERT_DEBUG(SaList->Count() > 0, User::Invariant()); + iStopped = EFalse; + + const TIpsecSaSpec& saSpec = SaList->At(0); + + CIkev2Acquire* Acquire = CIkev2Acquire::NewL(aPfkeyMsg, iIkeV2PlugInSession.GetSAId(), GetLocalAddr(), + Ikev2Proposal::GetDHGroup(iHdr.iIkeData->iGroupDesc_II), ImplicitChildSa(), + &saSpec, SaData->iRekeyData); + CleanupStack::PopAndDestroy(SaList); //SaList + + Acquire->SetSPI_ToBeRekeyed(spi); + + if ( iState == KStateIdle ) + { + CleanupStack::PushL(Acquire); + LoadEapPluginL(); + iHdr.iInitiator = ETrue; + GetNonceDataL(ETrue); // For IKE SA + if ( iHdr.iIkeData->iUseInternalAddr ) + { + CArrayFix* TsI = new (ELeave) CArrayFixFlat(1); + CleanupStack::PushL(TsI); + + TInetAddr StartIp; + TInetAddr EndIp; + StartIp.SetAddress(KInetAddrNone); // 0.0.0.0 + StartIp.SetPort(0); + EndIp.SetAddress(KInetAddrAll); // 255.255.255.255 + EndIp.SetPort(0xffff); + + TIkeV2TrafficSelector ts(StartIp, EndIp, + aPfkeyMsg.iDstAddr.iExt->sadb_address_proto); + TsI->AppendL(ts); + Acquire->ReplaceTS_i(TsI); + CleanupStack::Pop(TsI); + Acquire->SetVirtualIp(); + } + + if ( InitPkiServiceL() ) + { + iChildSaRequest = Acquire; // Store Acquire to wait PKI service init + CleanupStack::Pop(Acquire); + return; + } + CleanupStack::Pop(Acquire); + } + CIkev2Acquire::Link(Acquire, GetAcquireQue()); + GetIpsecSPI(Acquire); +} + +void CIkev2Negotiation::GetIpsecSPI(CIkev2Acquire* aAcquire) + { + ASSERT(aAcquire); + // + // Get SPI for inbound SA with PFKEY GETSPI primitive + // + TInetAddr DstAddr; + if ( aAcquire->SrcSpecific() ) + DstAddr = iHdr.iLocalAddr; + else DstAddr.Init(0); + DstAddr.SetPort(0); + TInetAddr SrcAddr = iHdr.iRemoteAddr; + SrcAddr.SetPort(0); + + iSpiRetriever->GetIpsecSaSpi(aAcquire->Id(), + aAcquire->IpsecProtocol(), + SrcAddr, DstAddr); + } + + +void CIkev2Negotiation::IpsecSaSpiRetrieved(TUint32 aSpiRequestId, + TInt aStatus, + TUint32 aSpi) + { + if (aStatus == KErrNone) + { + TRAP(aStatus, IpsecSaSpiRetrievedL(aSpiRequestId, aSpi)); + } + + if (aStatus != KErrNone) + { + //Leave that we have not been able to handle + //above layers. We bail out and report error. + iIkeV2PlugInSession.DeleteIkev2SA(iHdr.SaId()); + iIkeV2PlugInSession.IkeSaDeleted(aStatus); + delete this; + } + } + +void CIkev2Negotiation::CancelOperation() + { + if ( iTimer != NULL ) + { + iTimer->Cancel(); + } + if ( iSpiRetriever != NULL ) + { + iSpiRetriever->Cancel(); + } + } + +void CIkev2Negotiation::IpsecSaSpiRetrievedL(TUint32 aSpiRequestId, TUint32 aSpi) + { + DEBUG_LOG(_L("CIkev2Negotiation::SpiRetrievedL")); + + // + // Ipsec SPI received. Find an Acquire object for received SPI and + // save SPI into found object. + // + CIkev2Acquire* Acquire = CIkev2Acquire::Find(aSpiRequestId, GetAcquireQue()); + __ASSERT_DEBUG(Acquire, User::Invariant()); + + TPtrC8 spiPtr(reinterpret_cast(&aSpi), sizeof(aSpi)); + Acquire->SetSPI_In(spiPtr); + // + // Ipsec SPI received. Find an Acquire object for received SPI and + // save SPI into found object. + // + ContinueIkeNegotiationL(); + } + +void CIkev2Negotiation::ContinueIkeNegotiationL() +{ + // + // This method takes actions according to current state (iState) of + // the negotiation. + // + CIkev2Acquire* Acquire; + CIkev2Expire* Expire; + + switch ( iState ) + { + case KStateIdle: + // + // Start IKE_SA_INIT exchange + // + StartIkeSaInitL(); + break; + + case KStateIkeSaAuthWaitSpi: + { + // + // Complete IKE_AUTH exchange (with concatenated Child SA) + // + iChildSaRequest = CIkev2Acquire::GetNext(GetAcquireQue(), ETrue); + + DEBUG_LOG(_L("CIkev2Negotiation::ContinueIkeNegotiationL")); + DEBUG_LOG1(_L("iChildSaRequest is %d"), (TInt)iChildSaRequest); + + SendIkeAuthMessageL(); + } + break; + + case KStateIkeSaCompleted: + // + // There is no activity going on this negotiation + // If there is something in request queues start process + // them in the following order: + // -- Check if there is something in info queue (NIY) + // -- Check if there is something in expire queue (NIY) + // -- Check if there is ready responses in acquire queue + // -- Check if there is ready request in acquire queue + // + Expire = CIkev2Expire::GetNext(GetExpireQue()); + if ( Expire ) + { + CleanupStack::PushL(Expire); + BuildDeleteRequestL(Expire); + CleanupStack::PopAndDestroy(Expire); + } + else + { + Acquire = CIkev2Acquire::GetNext(GetAcquireQue(), ETrue); + if ( Acquire ) + { + BuildChildSAMessageL(Acquire, EFalse); + } + else + { + Acquire = CIkev2Acquire::GetNext(GetAcquireQue(), EFalse); + BuildChildSAMessageL(Acquire, ETrue); + } + } + break; + + default: + break; + } +} + +void CIkev2Negotiation::StartIkeSaInitL() + { + // + // Create Initiator SPI for the new IKE SA + // + CreateIkeSPI(iHdr.SpiI()); + + // + // Get required peer identity from policy (IDr) + // + iRemoteIdentity = Ikev2Proposal::GetRemoteIdentityL(iHdr.iIkeData); + + __ASSERT_ALWAYS(iHdr.iInitiator, User::Invariant()); + // + // Build and send the first IKE_SA_INIT message (request) + // HDR, SAi1, KEi, Ni, N[NAT_SRC], N[NAT_DST] + // + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + IKE_SA_INIT, + iHdr.iInitiator, + EFalse, + iHdr.NextRequestId(), + iDebug); + CleanupStack::PushL(ikeMsg); + + HBufC8* saBfr = Ikev2Proposal::FromPolicyToProposaL(iHdr, iSPI_Rekey, iDHGroupGuess); + CleanupStack::PushL(saBfr); + ikeMsg->AppendSaPayloadL(*saBfr); + CleanupStack::Pop(saBfr); + SetProposedSa(saBfr); + + AppendKEPayloadL(*ikeMsg, iHdr.iDHGroup); + ikeMsg->AppendNoncePayloadL(*iNonce_I); + if ( !iHdr.iIkeData->iUseNatProbing ) + { + delete iNatNotify; + iNatNotify = NULL; + + TInetAddr LocalIp; + if ( iHdr.iIkeData->iUseMobIke ) + LocalIp.SetAddress(KInetAddrNone); + else LocalIp = iHdr.iLocalAddr; + iNatNotify = CIkev2NatT::NewL( + LocalIp, iHdr.iRemoteAddr, IKE_PORT, ikeMsg->InitiatorSpi(), ikeMsg->ResponderSpi()); + + ikeMsg->AppendNotifyPayloadL(IKEV2_PROTOCOL, KZeroDesc, NAT_DETECTION_SOURCE_IP, + iNatNotify->SourceNofify()); + ikeMsg->AppendNotifyPayloadL(IKEV2_PROTOCOL, KZeroDesc, NAT_DETECTION_DESTINATION_IP, + iNatNotify->DestinNofify()); + } + CleanupStack::Pop(ikeMsg); + + SendIkeMsgL(ikeMsg); + iState = KStateIkeSaInitRequest; + } + +void CIkev2Negotiation::SendIkeAuthMessageL() +{ + // + // Build and send IKE_AUTH message + // IKE_AUTH message sent by the initiator is the following: + // HDR(A,B), SK {IDi, [CERT] [CERTREQ], [IDr], [AUTH], [CP], [SAi2, + // TSi, TSr]} + // IKE_AUTH message sent by the responder is the following: + // HDR(A,B), SK {IDr, [CERT,] AUTH, [CP], [SAr2, TSi, TSr]} + // CERT and CERTREQ payloads are added into message on when needed. + // AUTH payload is missing from initiators message when EAP in use. + // IPSEC SA:s are not always negotiated within IKE_AUTH messages. + // In this sitution SAx2, TSi and TSr payloads shall be missing. + // CP payload is used the Virtual IP address (secure network DNS + // IP:s) for client Virtual IP interface. + // Initiators CP payload shall contain CFG_REQUEST and and + // responders CP payload CFG_REPLY. + // When CP payload is used IKE_AUTH message MUST always contain + // IPSEC SA negotiation payloads within. + // In case INITIAL_CONTACT is used, the first IKE_AUTH request on given + // IKE SA contains INITIAL_CONTACT Notification Payload that is added in + // the end of the IKE_AUTH message. + // + + if ( !iLocalIdentity ) + { + // + // Own identity does not exists yet. Do not build IKE_AUTH + // message now + // + iState = KStateIkeWaitingId; + return; + } + + TUint32 MsgId = (iHdr.iInitiator) ? iHdr.NextRequestId() : iHdr.ExpectedRequestId(); + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + IKE_AUTH, + iHdr.iInitiator, + !iHdr.iInitiator, //Initiator sends only requests + MsgId, + iDebug); + CleanupStack::PushL(ikeMsg); + + ikeMsg->AppendEncryptedPayloadL(iHdr.iCipherBlkLth); + + __ASSERT_DEBUG(iLocalIdentity != NULL, User::Invariant()); + if (iHdr.iInitiator) + { + ikeMsg->AppendIdiPayloadL(*iLocalIdentity); + } + else + { + ikeMsg->AppendIdrPayloadL(*iLocalIdentity); + } + + if ( iPkiService && + iPkiService->UserCertificateData().Length() > 0) + { + ikeMsg->AppendCertPayloadL(iPkiService->UserCertificateData()); + } + + if ( iPkiService && + iPkiService->I2CertificateData().Length() > 0) + { + ikeMsg->AppendCertPayloadL(iPkiService->I2CertificateData()); + } + + if ( iPkiService && + iPkiService->I1CertificateData().Length() > 0) + { + ikeMsg->AppendCertPayloadL(iPkiService->I1CertificateData()); + } + + if ( iHdr.iInitiator && iHdr.iIkeData->iInitialContact ) + { + ikeMsg->AppendNotifyPayloadL(IKEV2_PROT_NONE, KZeroDesc, INITIAL_CONTACT, KZeroDesc); + } + + + if ( iHdr.iInitiator && iPkiService != NULL && iPkiService->CaList().Count() > 0) + { + ikeMsg->AppendCertReqPayloadL(iPkiService->CaList()); + } + + if ( iHdr.iInitiator && iRemoteIdentity ) + { + // + // Add IDr payload + // + ikeMsg->AppendIdrPayloadL(*iRemoteIdentity); + } + if ( !iEapPlugin ) + { + HBufC8* authData = NULL; + if ( iHdr.iInitiator ) + { + authData = SignAuthDataL(*iAuthMsgInit, (TUint8)iHdr.iAuthMethod); + } + else + { + authData = SignAuthDataL(*iAuthMsgResp, (TUint8)iHdr.iAuthMethod); + } + CleanupStack::PushL(authData); + ikeMsg->AppendAuthPayloadL(iHdr.iAuthMethod, *authData); + CleanupStack::PopAndDestroy(authData); + } + if ( iHdr.iIkeData->iUseMobIke ) + { + // + // Add MOBIKE_SUPPORTED notify payload + // + ikeMsg->AppendNotifyPayloadL(IKEV2_PROT_NONE, + KZeroDesc, + MOBIKE_SUPPORTED, + KZeroDesc); + } + + + // + // Add Child SA and Traffic selector payloads into IKE_AUTH message + // if required + // + if ( iChildSaRequest ) + { + iChild.iSPI_In = iChildSaRequest->SPI_In(); + iChildSaRequest->AddIpsecSpiToSa(iChild.iSPI_In); + if ( iChildSaRequest->ForVirtualIp() ) + { + // + // As Virtual Ip from peer SGW using Config Payload + // Build CP request data by constructing CIkev2Config Object + // + if ( !iConfigMode ) + iConfigMode = CIkev2Config::NewL(iChildSaRequest); + + ikeMsg->AppendConfigurationPayloadL(iConfigMode->CpType(), iConfigMode->Cp()); + } + ikeMsg->AppendSaPayloadL(*iChildSaRequest->SA()); + + ikeMsg->AppendTsiPayloadL(iChildSaRequest->TS_i()); + ikeMsg->AppendTsrPayloadL(iChildSaRequest->TS_r()); + } + + CleanupStack::Pop(ikeMsg); + SendIkeMsgL(ikeMsg); + + if ( iHdr.iInitiator ) + { + if ( iEapPlugin ) + iState = KStateIkeSaEapStarted; + else iState = KStateIkeSaAuthRequest; + } + else + { + iState = KStateIkeSaCompleted; + IkeSaCompletedL(); + } + +} + +void CIkev2Negotiation::SendKeepAliveMsgL() + { + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + INFORMATIONAL, + iHdr.iInitiator, + EFalse, + iHdr.NextRequestId(), + iDebug); + + ikeMsg->AppendEncryptedPayloadL(iHdr.iCipherBlkLth); + SendIkeMsgL(ikeMsg); + iState = KStateIkeInfoRequest; + + DEBUG_LOG(_L("CIkev2Negotiation::SendKeepAliveMsgL")); + } + + +void CIkev2Negotiation::BuildChildSAMessageL( + CIkev2Acquire* aAcquire, TBool aInitiator) + { + ASSERT(aAcquire); + // + // Build and send CREATE_CHILD_SA message + // CREATE_CHILD_SA request message sent is the following: + // HDR(A,B), SK {[N], SA, Ni, [KEi], [TSi, TSr]} + // CREATE_CHILD_SA response message is the following: + // HDR(A,B), SK {SA, Nr, [KEi], [TSi, TSr]} + // + iChild.iSPI_In = aAcquire->SPI_In(); + iChildSaRequest = aAcquire; + //TPayloadIkev2* PreviousPayload; + //TPayloadIkev2* EncrPayload; + GetNonceDataL(aInitiator); + aAcquire->AddIpsecSpiToSa(aAcquire->SPI_In()); + + TUint32 MsgId = (aInitiator) ? iHdr.NextRequestId() : iHdr.ExpectedRequestId(); + + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + CREATE_CHILD_SA, + iHdr.iInitiator, + !aInitiator, + MsgId, + iDebug); + + ikeMsg->AppendEncryptedPayloadL(iHdr.iCipherBlkLth); + + if (aInitiator && aAcquire->SPI_ToBeRekeyed().Length() > 0) + { + ikeMsg->AppendNotifyPayloadL(aAcquire->IpsecProtocol(), + aAcquire->SPI_ToBeRekeyed(), + REKEY_SA, KZeroDesc); + } + ikeMsg->AppendSaPayloadL(*aAcquire->SA()); + + if ( aInitiator ) + { + ikeMsg->AppendNoncePayloadL(*iNonce_I); + } + else + { + ikeMsg->AppendNoncePayloadL(*iNonce_R); + } + + delete iDHKeys; // Delete old DH object + iDHKeys = NULL; + if ( aAcquire->DHGroup() ) + { + AppendKEPayloadL(*ikeMsg, aAcquire->DHGroup()); + } + ikeMsg->AppendTsiPayloadL(aAcquire->TS_i()); + ikeMsg->AppendTsrPayloadL(aAcquire->TS_r()); + + SendIkeMsgL(ikeMsg); + + if ( aInitiator ) + { + iState = KStateIkeChildSARequest; + } + else + { + if (iDHKeys && iDHPublicPeer) + { + HBufC8* g_ir = iDHKeys->ComputeAgreedKeyL(iDHPublicPeer->Des()); + CleanupStack::PushL(g_ir); + + iChild.GenerateIpsecKeysL(iHdr.iSK_d, *g_ir, + *iNonce_I, *iNonce_R, iHdr.iPRFAlg); + + g_ir->Des().FillZ(); // Wipe out shared secret value from buffer + CleanupStack::PopAndDestroy(); //g_ir + } + else + { + iChild.GenerateIpsecKeysL(iHdr.iSK_d, KZeroDesc, + *iNonce_I, *iNonce_R, iHdr.iPRFAlg); + } + + IpsecSANegotiatedL(); + iState = KStateIkeChildSAResponse; + } +} + +void CIkev2Negotiation::BuildDeleteRequestL(CIkev2Expire* aExpire) +{ + // + // Build and send INFORMATIONAL exchange message with delete payload + // HDR(A,B), SK {D} + // If CIkev2Expire object defined, build a Delete payload with Ipsec + // SPI and protocl stored into CIkev2Expire object. If no CIkev2Expire build + // Delete payload for IKE SA. + // + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + INFORMATIONAL, + iHdr.iInitiator, + EFalse, + iHdr.NextRequestId(), + iDebug); + + ikeMsg->AppendEncryptedPayloadL(iHdr.iCipherBlkLth); + + CDesC8Array* spiArray = new (ELeave) CDesC8ArrayFlat(2); + CleanupStack::PushL(spiArray); + if ( aExpire ) + { + spiArray->AppendL(aExpire->SPI()); + ikeMsg->AppendDeletePayloadL(aExpire->Protocol(), *spiArray); + } + else + { + ikeMsg->AppendDeletePayloadL(IKEV2_PROTOCOL, *spiArray); + } + CleanupStack::PopAndDestroy(spiArray); + + SendIkeMsgL(ikeMsg); + DEBUG_LOG(_L("CIkev2Negotiation::BuildDeleteRequestL() Delete send OK")); + + if ( aExpire ) + { + iState = KStateChildDeleteRequest; + } + else + { + iState = KStateIkeDeleteRequest; + } +} + +void CIkev2Negotiation::BuildIkeSaRekeyMsgL(TBool aRequest) +{ + // + // Build and send CHILD_SA exchange message which contains IKE SA + // rekey message (either request or response) + // HDR, SA, Nonce, KE + // + HBufC8* SaBfr; + HBufC8* Nonce; + TUint32 MsgId; + + if ( aRequest ) + { + // Get a new SA Id for rekeyed IKE SA + iSAid_Rekey = iIkeV2PlugInSession.GetSAId(); + CreateIkeSPI(iSPI_Rekey, ETrue); + SaBfr = Ikev2Proposal::FromPolicyToProposaL(iHdr, iSPI_Rekey, iDHGroupGuess, ETrue); + SetProposedSa(SaBfr); // Save SA payload buffer + GetNonceDataL(ETrue); + Nonce = iNonce_I; + MsgId = iHdr.NextRequestId(); + } + else + { + SaBfr = PeekProposedSa(); + Ikev2Proposal::ChangeSpiInProposal(SaBfr, iSPI_Rekey); + GetNonceDataL(EFalse); + Nonce = iNonce_R; + MsgId = iHdr.ExpectedRequestId(); + } + + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + CREATE_CHILD_SA, + iHdr.iInitiator, + !aRequest, + MsgId, + iDebug); + + ikeMsg->AppendEncryptedPayloadL(iHdr.iCipherBlkLth); + ikeMsg->AppendSaPayloadL(*SaBfr); + ikeMsg->AppendNoncePayloadL(*Nonce); + AppendKEPayloadL(*ikeMsg, iHdr.iDHGroup); + + SendIkeMsgL(ikeMsg); + + if ( aRequest ) + { + iState = KStateIkeSARekeyRequest; + } +} + +void CIkev2Negotiation::CheckNotifyCodeL(CIkev2Payloads* aIkeMsg) + { + ASSERT(aIkeMsg); + // + // Some error has occurred during incoming IKE message handling + // Build an error response with specified Notify message type + // + TInt MsgType( GetNotifyCode() ); + + if ( MsgType ) + { + // + // Build and error response/request with Notify payload + // If received message with error condition is a request + // Notify payload is transmitted in the response IKE message + // of ongoing exchange (with erronous request message id) + // If received message with error conditions is a response + // an informational exchange is initiated with Notify payload + // + CIkeV2Message* XmitHdr = NULL; + TBool Response(aIkeMsg->GetIkeMsg()->GetFlags() & IKEV2_RESPONSE_MSG); + if ( Response ) + { + iState = KStateIkeInfoRequest; + TUint32 MsgId = aIkeMsg->GetIkeMsg()->GetMessageId(); + XmitHdr = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + INFORMATIONAL, + iHdr.iInitiator, + EFalse, + MsgId, + iDebug); + + } + else + { + XmitHdr = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + aIkeMsg->GetIkeMsg()->GetExchange(), + iHdr.iInitiator, + ETrue, + iHdr.ExpectedRequestId(), + iDebug); + } + + if (aIkeMsg->Encrypted()) + { + XmitHdr->AppendEncryptedPayloadL(iHdr.iCipherBlkLth); + } + + TInt notifyDataLength = 0; + TUint8* notifyData = NotifyData(notifyDataLength); + + if (notifyDataLength == 0) + { + XmitHdr->AppendNotifyPayloadL(IKEV2_PROT_NONE, KZeroDesc, MsgType, KZeroDesc); + } + else + { + TPtrC8 notifyDataPtrC(notifyData, notifyDataLength); + XmitHdr->AppendNotifyPayloadL(IKEV2_PROT_NONE, KZeroDesc, MsgType, notifyDataPtrC); + iNotifyDataLth = 0; //Reset notifydata + } + + SendIkeMsgL(XmitHdr); + + iEventLogger.LogEvent(MKmdEventLoggerIf::KLogError, R_VPN_MSG_SENT_ERROR_RESPONSE, + MsgType, iHdr.iVpnIapId, &iHdr.iRemoteAddr); + } + } + + +void CIkev2Negotiation::GetNatStatus(TBool aSupported, const TInetAddr& aRemote) +{ + // + // Examine NAT discovery status (from iHdr.iNATFlags) and set + // floated port usage indicator, if required. + // + if ( aSupported ) + { + if ( iHdr.iNATFlags & (REMOTE_END_NAT + LOCAL_END_NAT) ) + { + if ( iHdr.iNATFlags & REMOTE_END_NAT ) + { + // + // Remote end is behind NAT. Save current source IP to be + // used as further destination address. + // When remote and is behind NAT it is supposed that it + // must be pure mapping between public- and private IP + // addresses (remote NAPT is NOT supported) + // + DEBUG_LOG(_L("Remote end is behind NAT")); + iHdr.iDestinAddr = aRemote; // Remote end behind NAT, use current source IP as destin + } + + if ( iHdr.iNATFlags & LOCAL_END_NAT ) + { + DEBUG_LOG(_L("NAT discovery result: Local end is behind NAT")); + } + iHdr.iFloatedPort = ETrue; + iHdr.iDestinAddr.SetPort(FLOATED_IKE_PORT); + } + else + { + if ( iHdr.iMobikeUsed ) + { + iHdr.iFloatedPort = ETrue; + iHdr.iDestinAddr.SetPort(FLOATED_IKE_PORT); + } + DEBUG_LOG(_L("NAT discovery result: There is no NAT between negotiating ends")); + } + } + else + { + DEBUG_LOG(_L("NAT discovery operation failed")); + } +} + +void CIkev2Negotiation::CreateIkeSPI(TIkeSPI& aSPI, TBool aRekey) +{ + // + // Create IKE SPI for local end. + // The SPI value is created from the following "parameters" in + // IKEv2 negotiation object: + // - The first 4 octets of SPI value are the SAId (32 bit value) + // - The last 4 octets of SPI contains "pseudo random" value: + // X = (SAId + negotiation object pointer) >> (SAId & 3) + // + TUint32 SpiValue1; + TUint32 SpiValue2; + if ( aRekey ) + SpiValue1 = iSAid_Rekey; + else SpiValue1 = iHdr.SaId(); + Mem::Copy((TUint8*)&SpiValue2, (TUint8*)this, 4); + SpiValue2 = (SpiValue2 + SpiValue1) >> (SpiValue1 & 3); + PUT32(aSPI.Ptr(), SpiValue1); + PUT32((aSPI.Ptr() + 4), SpiValue2); + aSPI.SetLength(IKEV2_SPI_SIZE); +} + +void CIkev2Negotiation::LoadEapPluginL() +{ + // + // If EAP configured in policy, construct EAP interface object to + // communicate EAP ECOM plug-in + // If consruction causes an error, stop negotiation request + // + iHdr.iEAPType = iHdr.iIkeData->iEAPProtocol; + if ( !iEapPlugin && iHdr.iEAPType ) + { + iEapPlugin = CIkev2EapIf::NewL(*this, (TUint8)iHdr.iEAPType, iHdr.iIkeData, iDebug); + TInt Status = iEapPlugin->Status(); + if ( Status != KErrNone ) + { + iStopped = ETrue; + } + else iEapPlugin->QueryIdentity(); + } +} + +TBool CIkev2Negotiation::InitPkiServiceL() +{ + DEBUG_LOG(_L("-> CIkev2Negotiation::InitPkiServiceL")); + // + // If EAP configured in policy, construct EAP interface object to + // communicate EAP ECOM plug-in + // If consruction causes an error, return corresponding error code + // to stop negotiation request + // + TBool Status = EFalse; + if ( !iPkiService && Ikev2Proposal::PkiServiceNeeded(iHdr.iIkeData) ) + { + iPkiService = CIkeV2PkiService::NewL(*this, iDebug); + + if (iHdr.iIkeData->iCAList->Count() == 0) + { + User::Leave(KVpnErrInvalidCaCertFile); + } + + iPkiService->InitIkeV2PkiService(iHdr.iIkeData); + iState = KStateIkeInitPkiService; + Status = ETrue; + } + + DEBUG_LOG(_L("<- CIkev2Negotiation::InitPkiServiceL")); + return Status; +} + + +void CIkev2Negotiation::SendIkeMsgL(CIkeV2Message* aMsg) +{ + ASSERT(aMsg); + + TPtrC8 encryptionKey; + TPtrC8 integrityKey; + if ( iHdr.iInitiator ) + { + encryptionKey.Set(iHdr.iSK_ei); + integrityKey.Set(iHdr.iSK_ai); + } + else + { + encryptionKey.Set(iHdr.iSK_er); + integrityKey.Set(iHdr.iSK_ar); + } + + TInetAddr sourceAddr(iHdr.iLocalAddr); + if (iHdr.iFloatedPort) + { + sourceAddr.SetPort(FLOATED_IKE_PORT); + } + else + { + sourceAddr.SetPort(IKE_PORT); + } + aMsg->PrepareIkeMessageDatagramL(iHdr.iEncrAlg, encryptionKey, + iHdr.iIntegAlg, integrityKey, + sourceAddr, iHdr.iDestinAddr); + iMessageSendQue.SendIkeMessageL(aMsg->IkeMessageDatagram(), iHdr.iFloatedPort); + + if (aMsg->Flags() & IKEV2_RESPONSE_MSG ) + { + iHdr.SaveRespMsg(aMsg); + iHdr.iRespRetryCount = 0; + } + else + { + iSendAttempt = 1; + iTimer->Cancel(); + iTimer->IssueRequest(iSendAttempt); // Start retry timer + + iHdr.SaveRequestMsg(aMsg); + } +} + +void CIkev2Negotiation::RetransmitRequest() + { + TRAPD(err, DoRetransmitL(EFalse)); + if ( err != KErrNone ) + { + iIkeV2PlugInSession.IkeSaDeleted( err ); + } + } + +void CIkev2Negotiation::DoRetransmitL(TBool aResponse) +{ + if ( aResponse ) + { + // + // Peer has retransmitted a request, retransmit last response + // message saved. + // + if ( iHdr.iLastResponse && (iHdr.iRespRetryCount <= KMaxSendAttemps) ) + { + iHdr.iRespRetryCount ++; + //iHdr.iLastResponse = NULL; + DEBUG_LOG3(_L("IKE response message rexmitted on SAId: %d , Retry: %d , State: %d"), iHdr.SaId(), iHdr.iRespRetryCount, iState ); + + iMessageSendQue.SendIkeMessageL(iHdr.iLastResponse->IkeMessageDatagram(), + iHdr.iFloatedPort); + } + else iStopped = ETrue; + } + else + { + // + // No response received to a transmitted IKE request message + // Retransmit message if retry count not exhausted + // + DEBUG_LOG(_L("No response received for transmitted IKE request.")); + + iSendAttempt++; + iMessageSendQue.CancelSend(iHdr.iLastRequest->IkeMessageDatagram()); + + if ( iSendAttempt <= KMaxSendAttemps ) + { + DEBUG_LOG3(_L("IKE Message rexmitted on SAId: %d , State: %d , Retry: %d"),iHdr.SaId(), iState, iSendAttempt ); + iMessageSendQue.SendIkeMessageL(iHdr.iLastRequest->IkeMessageDatagram(), + iHdr.iFloatedPort); + iTimer->IssueRequest(iSendAttempt); // Restart retry timer + } + else + { + DEBUG_LOG3(_L("Transmit retry count reached on SAId: %d , State: %d , retry: %d"),iHdr.SaId(), iState, iSendAttempt ); + if ( iState < KStateIkeSaCompleted ) + { + IkeSaFailed(KKmdIkeNegotFailed); // IKE SA negotiation going + } + else + { + iIkeV2PlugInSession.DeleteIkev2SA(iHdr.SaId()); + iIkeV2PlugInSession.IkeSaDeleted(KKmdIkeNoResponseErr); //IKE SA deletion going + delete this; + } + } + } + } + + +void CIkev2Negotiation::IkeV2PkiInitCompleteL(TInt aStatus) + { + + DEBUG_LOG(_L("-> CIkev2Negotiation::IkeV2PkiInitCompleteL")); + // + // The implementation for class MPkiServiceComplete virtual function + // This method is called when a PKI service operation is + // completed. + // + + __ASSERT_ALWAYS( iPkiService != NULL, User::Invariant()); + __ASSERT_ALWAYS(iState == KStateIkeInitPkiService, User::Invariant()); + + switch(aStatus) + { + case KErrNone: + // + // PKI service object has been constructed + // Start IKE_SA_INIT exchange + // + iState = KStateIdle; + if ( iChildSaRequest ) + { + CIkev2Acquire* Acquire = iChildSaRequest; + iChildSaRequest = NULL; + CIkev2Acquire::Link(Acquire, GetAcquireQue()); + GetIpsecSPI(Acquire); + } + else if ( iSavedSaInit ) + { + TPtr8 IkeMsg(iSavedSaInit->Des()); + const ThdrISAKMP* IkeMessage = ThdrISAKMP::Ptr(IkeMsg); + ProcessIkeMessageL(*IkeMessage, iHdr.iRemoteAddr, IKE_PORT); + if ( Stopped() ) + delete this; + } + break; + case KErrNotFound: + DEBUG_LOG(_L("IKEv2 CA certificate retrieve failed. Certificate not found")); + IkeSaFailed(KVpnErrInvalidCaCertFile); + break; + default: + { + DEBUG_LOG1(_L("IKEv2 CA certificate retrieve failed (%d)"), aStatus); + IkeSaFailed(aStatus); + } + break; + } + + DEBUG_LOG(_L("<- CIkev2Negotiation::IkeV2PkiInitCompleteL")); + } + + +void CIkev2Negotiation::SendEapDataL(HBufC8* aEapData) +{ + // + // Send an IKE containing an EAP payload (within Encrypted Payload) + // The entire EAP payload data is provided in aEapData buffer + // + CleanupStack::PushL(aEapData); + if ( iState == KStateIkeSaEapGoing ) + { + __ASSERT_DEBUG(iHdr.iInitiator, User::Invariant()); + + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), iHdr.SpiR(), + IKE_AUTH, + iHdr.iInitiator, + EFalse, + iHdr.NextRequestId(), + iDebug); + CleanupStack::PushL(ikeMsg); + ikeMsg->AppendEncryptedPayloadL(iHdr.iCipherBlkLth); + ikeMsg->AppendEapPayloadL(*aEapData); + CleanupStack::Pop(ikeMsg); + SendIkeMsgL(ikeMsg); + } + CleanupStack::PopAndDestroy(aEapData); +} + +void CIkev2Negotiation::EapEventL(TInt aEvent) + { + // See whether the object is accepting any events + // (it is, by default, but will not take events during destruction phase) + if (!iProcessEvents) + { + return; + } + // + // An event idicated by the EAP plugin process event according to + // event type + // + switch ( aEvent ) + { + case KEapEventSuccess: + if ( (iState == KStateIkeSaEapGoing) || (iState == KStateIkeSaEapStarted) ) + { + // + // EAP auhtentication succeeded. + // Build IKE message HDR, SK {AUTH} + // + __ASSERT_DEBUG( iHdr.iInitiator, User::Invariant()); + + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), iHdr.SpiR(), + IKE_AUTH, + iHdr.iInitiator, + EFalse, + iHdr.NextRequestId(), + iDebug); + CleanupStack::PushL(ikeMsg); + ikeMsg->AppendEncryptedPayloadL(iHdr.iCipherBlkLth); + + + HBufC8* authData = SignAuthDataL(*iAuthMsgInit, (TUint8)iHdr.iAuthMethod); + CleanupStack::PushL(authData); + ikeMsg->AppendAuthPayloadL(iHdr.iAuthMethod, *authData); + CleanupStack::PopAndDestroy(authData); + + CleanupStack::Pop(ikeMsg); + SendIkeMsgL(ikeMsg); + iState = KStateIkeSaAuthRequest; + iEapCompleted = ETrue; + } + break; + + case KEapEventGetIdentity: + GetOwnIdentityL(ETrue); // Gets the Identity from EAP plugin + if ( iState == KStateIkeWaitingId ) + { + // + // Identity data provided by the EAP plug-in + // Complete local signed data and send the first + // IKE_AUTH message + // + AddIdToSignedDataL(ETrue, iAuthMsgInit, iLocalIdentity->PayloadData()); + SendIkeAuthMessageL(); + } + break; + + case KEapEventGetPSK: + if ( iState == KStateIkeSaEapGoing ) + { + // + // Preshared key provided by the EAP plug-in + // Get key data and link it into negotiation object + // + iPresharedKey = iEapPlugin->MSK(); + } + break; + + default: // = KEapEventFailed + // + // EAP authentication is failed. Stop negotiation + // + IkeSaFailed(KKmdIkeAuthFailedErr); // IKE SA negotiation going + break; + } + } + +TBool CIkev2Negotiation::ProcessIkeSaInitL(CIkev2Payloads* aIkeMsg, const TInetAddr& aRemote) +{ + ASSERT(aIkeMsg); + // + // Process IKE message of exchange type IKE_SA_INIT + // + ThdrISAKMP* IkeHdr = aIkeMsg->GetIkeMsg(); // IKE Message fixed header + TBool Response = IkeHdr->GetFlags() & IKEV2_RESPONSE_MSG; + TBool Initiator = IkeHdr->GetFlags() & IKEV2_INITIATOR; + TUint32 MsgId = IkeHdr->GetMessageId(); + + if ( iHdr.iInitiator ) + { + if ( Initiator ) { + DEBUG_LOG1(_L("IKEv2 Message with Orig_Init-bit in wrong state: %d"), iState); + return ETrue; + } + if ( !Response ) + { + DEBUG_LOG1(_L("IKEv2 Message is not response; state: %d"), iState); + return ETrue; + } + if ( MsgId != iHdr.ExpectedResponseId() ) + { + DEBUG_LOG1(_L("Wrong message id in response; state: %d"), iState); + return ETrue; + } + + if (iState == KStateIkeSaInitRequest) + { + //record responder SPI + aIkeMsg->GetIkeMsg()->GetSPI_R(iHdr.SpiR()); + + // + // Received message should be a response to a + // IKE_SA_INIT request transmitted. + // + if (IkeHdr->GetPayload() == IKEV2_PAYLOAD_NOTIF) + { + return ProcessNotifyPayloadsL(*aIkeMsg->iNotifs, EFalse, IKE_SA_INIT); + } + // + // Response message should be format: + // HDR(A,B), SAr1, KEr, Nr, [CERTREQ] + // + if ( !CheckPayloadsOrder(aIkeMsg, IKE_SA_INIT, ETrue) ) + { + DEBUG_LOG1(_L("Erroneous IKE_SA_INIT response: %d"), iState); + return EFalse; + } + if ( !Ikev2Proposal::VerifySaResponseL(iHdr, iChild, *PeekProposedSa(), *aIkeMsg) ) + { + DEBUG_LOG1(_L("Unaccepted SA content in IKE_SA_INIT response: %d"),iState); + return EFalse; + } + if ( aIkeMsg->iNonce->PlDataLen() < IKEV2_MIN_NONCE_SIZE ) + { + DEBUG_LOG1(_L("Nonce data too short %d"), iState); + return EFalse; + } + + if ( iNatNotify ) + { + TBool Supported; + TInetAddr LocalIp; + if ( iHdr.iIkeData->iUseMobIke ) + LocalIp.SetAddress(KInetAddrNone); + else LocalIp = iHdr.iLocalAddr; + +#ifdef _DEBUG + TBuf<80> debugBuf; + DEBUG_LOG(_L("Calculating NAT detection:")); + LocalIp.Output(debugBuf); + DEBUG_LOG2(_L("LocalIp %S:%d"), &debugBuf, IKE_PORT); + iHdr.iRemoteAddr.Output(debugBuf); + DEBUG_LOG2(_L("RemoteIp %S:%d"), &debugBuf, IKE_PORT); +#endif + + iHdr.iNATFlags = CIkev2NatT::CheckPeerNotifysL(*aIkeMsg->iNotifs, LocalIp, iHdr.iRemoteAddr, IKE_PORT, + iHdr.SpiI(), iHdr.SpiR(), Supported); + GetNatStatus(Supported, aRemote); + } + + delete iNonce_R; + iNonce_R = NULL; + + iNonce_R = HBufC8::NewL(aIkeMsg->iNonce->PlDataLen()); + iNonce_R->Des().Copy(aIkeMsg->iNonce->PayloadData(), aIkeMsg->iNonce->PlDataLen()); + if ( !ProcessKeyExchangeL((TKEPayloadIkev2*)aIkeMsg->iKe, iHdr.iDHGroup) ) + { + return EFalse; + } + + // + // IKE_SA_INIT request is completed enter IKE_AUTH + // + GenerateIkeKeysL(); + TPtrC8 ikeHdrPtr((TUint8*)IkeHdr, IkeHdr->GetLength()); + SaveSignedDataL(EFalse, ikeHdrPtr); // Save IKE_AUTH message 2 + + //We ignore possible cert req payloads and just work + //according our policy + if ( !iHdr.iEAPType && + (iHdr.iAuthMethod == RSA_DIGITAL_SIGN || iHdr.iAuthMethod == DSS_DIGITAL_SIGN) ) + { + SaveSignedDataL(ETrue, iHdr.iLastRequest->IkeMessageDatagram()); // Own identity not yet saved to signed data + + GetOwnIdentityL(); // Get own Identity from Certificate (or policy) + AddIdToSignedDataL(ETrue, iAuthMsgInit, iLocalIdentity->PayloadData()); + } + else + { + // + // Check if "implicit" Child SA exchange required + // by getting request CIkev2Acquire object from queue + // + GetOwnIdentityL(); + SaveSignedDataL(ETrue, iHdr.iLastRequest->IkeMessageDatagram()); // Own identity saved to signed data + } + iChildSaRequest = CIkev2Acquire::GetNext(GetAcquireQue(), EFalse); + SendIkeAuthMessageL(); + } + else + { + // + // Ignore received message silently + // + DEBUG_LOG1(_L("IKE_SA_INIT response received in state %d"), iState); + } + } + else { + if ( !Initiator ) + { + DEBUG_LOG1(_L("IKEv2 Message without Orig_Init-bit in wrong, state: %d"), iState); + return ETrue; + } + if ( Response ) + { + DEBUG_LOG1(_L("IKEv2 Message is not request, state: %d"), iState); + return ETrue; + } + + switch ( iState ) + { + case KStateIdle: + case KStateIkeSaInitResponse: + //Record Initiator SPI + aIkeMsg->GetIkeMsg()->GetSPI_I(iHdr.SpiI()); + iHdr.SpiI().SetLength(IKEV2_SPI_SIZE); + + // + // Received message should be an IKE_SA_INIT request + // Request message should be format: + // HDR(A,0), SAi1, KEi, Ni, [CERTREQ] + // + { + if ( !CheckPayloadsOrder(aIkeMsg, IKE_SA_INIT, EFalse) ) + { + DEBUG_LOG1(_L("Erroneous IKE_SA_INIT request: %d"), iState); + return EFalse; + } + if ( MsgId != iHdr.ExpectedRequestId() ) { + if ( iHdr.iLastResponse != NULL && + MsgId == iHdr.iLastResponse->MessageId() && + iState == KStateIkeSaInitResponse ) + { + // + // Retransmission of an earlier IKE_SA_INIT + // request. Retransmit current IKE_SA_INIT + // response (if retry count not exhausted) + // + DoRetransmitL(ETrue); + return ETrue; + } + else { + DEBUG_LOG1(_L("Wrong message id in request, state: %d"), iState); + return EFalse; + } + } + if ( iState == KStateIkeSaInitResponse ) + return EFalse; // IKE_SA_INIT request retry with a new message ID + iIkeV2PlugInSession.StartResponding(); + + // + // Build a SA payload from current IKE policy and + // verify received IKE SA request with it + // + HBufC8* SaBfr = Ikev2Proposal::FromPolicyToProposaL(iHdr, iSPI_Rekey, iDHGroupGuess); + CleanupStack::PushL(SaBfr); + HBufC8* proposedSa = NULL; + TBool SaOk = Ikev2Proposal::VerifySaRequestAndGetProposedSaBufferL(iHdr, iChild, + *SaBfr, *aIkeMsg, + proposedSa); + CleanupStack::PopAndDestroy(); + if ( !SaOk ) + { + DEBUG_LOG1(_L("Unaccepted SA content in IKE_SA_INIT request: %d"),iState); + SetNotifyCode(NO_PROPOSAL_CHOSEN); + return EFalse; + } + SetProposedSa(proposedSa); + proposedSa = NULL; + if ( aIkeMsg->iNonce->PlDataLen() < IKEV2_MIN_NONCE_SIZE ) + { + DEBUG_LOG1(_L("Nonce data too short %d"), iState); + return EFalse; + } + + //Check peer NAT status + TBool useNatDetection = EFalse; + if ( !iHdr.iIkeData->iUseNatProbing ) + { + TInetAddr LocalIp; + if ( iHdr.iIkeData->iUseMobIke ) + LocalIp.SetAddress(KInetAddrNone); + else LocalIp = iHdr.iLocalAddr; + iHdr.iNATFlags = CIkev2NatT::CheckPeerNotifysL(*aIkeMsg->iNotifs, LocalIp, + iHdr.iRemoteAddr, IKE_PORT, + iHdr.SpiI(), iHdr.SpiR(), useNatDetection); + } + + if ( !ProcessKeyExchangeL((TKEPayloadIkev2*)aIkeMsg->iKe, iHdr.iDHGroup) ) + return EFalse; + + + // + // Create own SPI (responder) + // + CreateIkeSPI(iHdr.SpiR()); + delete iNonce_I; + iNonce_I = NULL; + iNonce_I = HBufC8::NewL(aIkeMsg->iNonce->PlDataLen()); + iNonce_I->Des().Copy(aIkeMsg->iNonce->PayloadData(), aIkeMsg->iNonce->PlDataLen()); + GetNonceDataL(EFalse); + + TPtrC8 ikeHdrPtr((TUint8*)IkeHdr, IkeHdr->GetLength()); + SaveSignedDataL(EFalse, ikeHdrPtr); // Save IKE_AUTH message 2 + + // + // Build IKE_SA_INIT response message: HDR, SAr1, KEr, Nr, [CERTREQ] + // + __ASSERT_DEBUG(!iHdr.iInitiator, User::Invariant()); + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), iHdr.SpiR(), + IKE_SA_INIT, + iHdr.iInitiator, + ETrue, + iHdr.ExpectedRequestId(), + iDebug); + CleanupStack::PushL(ikeMsg); + + HBufC8* saBfr = Ikev2Proposal::FromPolicyToProposaL(iHdr, iSPI_Rekey, iDHGroupGuess); + CleanupStack::PushL(saBfr); + ikeMsg->AppendSaPayloadL(*saBfr); + CleanupStack::Pop(saBfr); + SetProposedSa(saBfr); + + AppendKEPayloadL(*ikeMsg, iHdr.iDHGroup); + ikeMsg->AppendNoncePayloadL(*iNonce_R); + + if ( iPkiService ) + { + ikeMsg->AppendCertReqPayloadL(iPkiService->CaList()); + } + + if ( useNatDetection ) + { + delete iNatNotify; + iNatNotify = NULL; + + TInetAddr LocalIp; + if ( iHdr.iIkeData->iUseMobIke ) + LocalIp.SetAddress(KInetAddrNone); + else LocalIp = iHdr.iLocalAddr; + + + iNatNotify = CIkev2NatT::NewL(LocalIp, iHdr.iRemoteAddr, IKE_PORT, ikeMsg->InitiatorSpi(), ikeMsg->ResponderSpi()); + ikeMsg->AppendNotifyPayloadL(IKEV2_PROTOCOL, KZeroDesc, NAT_DETECTION_SOURCE_IP, + iNatNotify->SourceNofify()); + ikeMsg->AppendNotifyPayloadL(IKEV2_PROTOCOL, KZeroDesc, NAT_DETECTION_DESTINATION_IP, + iNatNotify->DestinNofify()); + } + GetNatStatus(useNatDetection, aRemote); + CleanupStack::Pop(ikeMsg); + SendIkeMsgL(ikeMsg); + GenerateIkeKeysL(); + + SaveSignedDataL(ETrue, ikeMsg->IkeMessageDatagram()); // Own identity is not yet saved to signed data + iState = KStateIkeSaInitResponse; + } + break; + + default: + // + // Ignore received message silently + // + DEBUG_LOG1(_L("IKE_SA_INIT message received in state %d"), iState); + break; + + } + } + + return ETrue; +} + +TBool CIkev2Negotiation::ProcessIkeAuthL(CIkev2Payloads* aIkeMsg) +{ + ASSERT(aIkeMsg); + // + // Process IKE message of exchange type IKE_AUTH + // + ThdrISAKMP* IkeHdr = aIkeMsg->GetIkeMsg(); // IKE Message fixed header + TBool Response = IkeHdr->GetFlags() & IKEV2_RESPONSE_MSG; + TBool Initiator = IkeHdr->GetFlags() & IKEV2_INITIATOR; + TUint32 MsgId = IkeHdr->GetMessageId(); + + if ( iHdr.iInitiator ) + { + if ( Initiator ) + { + DEBUG_LOG1(_L("IKEv2 Message with Orig_Init-bit in wrong state: %d"), iState); + return ETrue; + } + if ( !Response ) + { + DEBUG_LOG1(_L("IKEv2 Message is not response; state: %d"), iState); + return ETrue; + } + if ( MsgId != iHdr.ExpectedResponseId() ) + { + DEBUG_LOG1(_L("Wrong message id in response; state: %d"), iState); + return ETrue; + } + + switch ( iState ) + { + case KStateIkeSaAuthRequest: + DEBUG_LOG(_L("Handling IKE_AUTH response")); + // + // Received message should be a response to a + // IKE_AUTH request transmitted. + // Response message should be format: + // HDR(A,B), SK {IDr, [CERT,] AUTH, [CP], SAr2, TSi, TSr} + // + if ( aIkeMsg->iEncr ) + { + ProcessNotifyPayloadsL(*aIkeMsg->iNotifs, EFalse, IKE_AUTH); + if ( iDeleteIkeSA ) + { + DEBUG_LOG1(_L("Error Notify in IKE_AUTH response: %d"), iState); + + //Because we are just in IKE_AUTH no IKE_SAs exists --> we don't + //want to delete one. So we set iDeleteIkeSA back to false. + iDeleteIkeSA = EFalse; + + return EFalse; + } + } + if ( !CheckPayloadsOrder(aIkeMsg, IKE_AUTH, ETrue) ) + { + DEBUG_LOG1(_L("Erroneous IKE_AUTH response: %d"), iState); + SetNotifyCode(INVALID_SYNTAX); + + if ( iChildSaRequest && !iChildSARejected && !aIkeMsg->iSa ) + { + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + } + + return EFalse; + } + DEBUG_LOG(_L("IKE_AUTH payload order check passed")); + TBool Status; + if ( iEapCompleted ) + { + Status = AuthenticatePeerL(aIkeMsg->iAuth); + } + else + { + if ( iPkiService && !VerifyPeerCertificateL(aIkeMsg->iCerts, aIkeMsg->iIdR) ) + { + SetNotifyCode(AUTHENTICATION_FAILED); + return EFalse; + } + Status = AddIdAndAuthenticatePeerL(aIkeMsg); + } + if ( !Status ) + { + SetNotifyCode(AUTHENTICATION_FAILED); + return EFalse; + } + // + // If implicit Child SA negotiation requested, + // verify IPSEC SA- and Traffic selector payloads, too + // + if ( iChildSaRequest ) + { + DEBUG_LOG(_L("Processing CHILD_SA creation")); + if ( !iChildSARejected ) + { + if ( !Ikev2Proposal::VerifySaResponseL(iHdr, iChild, *iChildSaRequest->SA(), *aIkeMsg) ) + { + DEBUG_LOG1(_L("Unaccepted SA payload content in IKE_AUTH response: %d"),iState); + SetNotifyCode(NO_PROPOSAL_CHOSEN); + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + return EFalse; + } + DEBUG_LOG(_L("SA response verified")); + if ( !IpsecSelectors::VerifyTrafficSelectorsL(iChildSaRequest, (TTSPayloadIkev2*)aIkeMsg->iTsI, (TTSPayloadIkev2*)aIkeMsg->iTsR ) ) + { + DEBUG_LOG1(_L("Unaccepted Traffic Selectors in IKE_AUTH response: %d"),iState); + SetNotifyCode(TS_UNACCEPTABLE); + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + return EFalse; + } + DEBUG_LOG(_L("Traffic selectors verified")); + if ( aIkeMsg->iCp ) + { + if ( iConfigMode ) + { + iConfigMode->ProcessCpL(aIkeMsg->iCp); + } + else + { + DEBUG_LOG(_L("Unsolicited CP payload in IKE_AUTH response")); + } + } + iChildSaRequest->SetSPI_Out(iChild.iSPI_Out); + DEBUG_LOG(_L("Generating IPsec keys")); + iChild.GenerateIpsecKeysL(iHdr.iSK_d, KZeroDesc, + *iNonce_I, *iNonce_R, iHdr.iPRFAlg); + DEBUG_LOG(_L("IPsec keys generated")); + } + else + { + DEBUG_LOG1(_L("Implicit CHILD_SA rejected Notify in IKE_AUTH response: %d"), iState); + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + } + } + // + // IKE_AUTH request is completed and IKE SA has been + // negotiated + // + IkeSaCompletedL(); + break; + + case KStateIkeSaEapStarted: + // + // Received message should be an IKE_AUTH response + // containing an EAP payload. + // The content of received IKE message shall be: + // HDR, SK {IDr, [CERT,] AUTH, EAP } + // + if ( !aIkeMsg->iEncr || !aIkeMsg->iIdR || !aIkeMsg->iAuth || !aIkeMsg->iEap ) + { + DEBUG_LOG1(_L("Erroneous IKE_AUTH response: %d"), iState); + SetNotifyCode(INVALID_SYNTAX); + return EFalse; + } + if ( iPkiService && !VerifyPeerCertificateL(aIkeMsg->iCerts, aIkeMsg->iIdR) ) + { + SetNotifyCode(AUTHENTICATION_FAILED); + return EFalse; + } + if ( !AddIdAndAuthenticatePeerL(aIkeMsg) ) + { + SetNotifyCode(AUTHENTICATION_FAILED); + return EFalse; + } + iState = KStateIkeSaEapGoing; + iEapPlugin->EapDataInbound(aIkeMsg->iEap); + break; + + case KStateIkeSaEapGoing: + // + // Received message should be an IKE_AUTH response + // containing an EAP payload. + // The content of received IKE message shall be: + // HDR, SK {EAP} + // + if ( !aIkeMsg->iEncr || !aIkeMsg->iEap ) + { + DEBUG_LOG1(_L("Erroneous IKE_AUTH response: %d"), iState); + SetNotifyCode(INVALID_SYNTAX); + return EFalse; + } + iEapPlugin->EapDataInbound(aIkeMsg->iEap); + break; + + default: + // + // Ignore received message silently + // + DEBUG_LOG1(_L("IKE_AUTH response received in state %d"), iState); + break; + + } + } + else + { + if ( !Initiator ) + { + DEBUG_LOG1(_L("IKEv2 Message without Orig_Init-bit in wrong, state: %d"), iState); + return ETrue; + } + if ( Response ) + { + DEBUG_LOG1(_L("IKEv2 Message is not request, state: %d"), iState); + return ETrue; + } + switch ( iState ) + { + case KStateIkeSaInitResponse: + case KStateIkeSaCompleted: + // + // Received message should be an IKE_AUTH request + // Request message should be format: + // HDR(A,B), SK {IDi, [CERT,] [CERTREQ,] [IDr,] AUTH, SAi2, TSi, TSr} + // + if ( !CheckPayloadsOrder(aIkeMsg, IKE_AUTH, EFalse) ) + { + DEBUG_LOG1(_L("Erroneous IKE_AUTH request: %d"), iState); + SetNotifyCode(INVALID_SYNTAX); + return EFalse; + } + if ( MsgId != iHdr.ExpectedRequestId() ) { + if ( iHdr.iLastResponse != NULL && + MsgId == iHdr.iLastResponse->MessageId() && + iState == KStateIkeSaCompleted ) + { + // + // Retransmission of an earlier IKE_SA_INIT + // request. Retransmit current IKE_SA_INIT + // response (if retry count not exhausted) + // + DoRetransmitL(ETrue); + return ETrue; + } + else { + DEBUG_LOG1(_L("Wrong message id in request, state: %d"), iState); + SetNotifyCode(INVALID_MESSAGE_ID); + StoreNotifyData32(MsgId); + return EFalse; + } + } + if ( iState == KStateIkeSaCompleted ) + return EFalse; // IKE_AUTH request retry with a new message ID + + //if ( aIkeMsg->iEncr ) + //{ + ProcessNotifyPayloadsL(*aIkeMsg->iNotifs, EFalse, IKE_AUTH); + if ( iDeleteIkeSA ) + { + DEBUG_LOG1(_L("Error Notify in IKE_AUTH response: %d"), iState); + return EFalse; + } + //} + + if ( iPkiService && !VerifyPeerCertificateL(aIkeMsg->iCerts, aIkeMsg->iIdI) ) + { + DEBUG_LOG(_L("Peer certificate validation failed.")); + SetNotifyCode(AUTHENTICATION_FAILED); + return EFalse; + } + if ( !AddIdAndAuthenticatePeerL(aIkeMsg) ) + { + SetNotifyCode(AUTHENTICATION_FAILED); + return EFalse; + } + // + // Process "concatenated" Child SA- and Traffic + // Selector payloads if present + // + if ( aIkeMsg->iSa ) + { + DEBUG_LOG(_L("IKE_AUTH request has SA and TS payload.")); + CIkev2Acquire* Acquire = IpsecSelectors::GetIpsecPolicyL(iIkeV2PlugInSession, aIkeMsg); + if ( !Acquire ) + { + DEBUG_LOG1(_L("Unaccepted Traffic Selectors in IKE_AUTH request: %d"),iState); + SetNotifyCode(TS_UNACCEPTABLE); + return EFalse; + } + CleanupStack::PushL(Acquire); + HBufC8* proposedSaBuffer = NULL; + if (!Ikev2Proposal::VerifySaRequestAndGetProposedSaBufferL(iHdr, iChild, + *Acquire->SA(), *aIkeMsg, + proposedSaBuffer)) + { + CleanupStack::PopAndDestroy(Acquire); + DEBUG_LOG1(_L("Unaccepted SA content in IKE_AUTH request: %d"),iState); + SetNotifyCode(NO_PROPOSAL_CHOSEN); + return EFalse; + } + SetProposedSa(proposedSaBuffer); + proposedSaBuffer = NULL; + // + // Replace SA payload buffer in CIkev2Acquire with + // selected SA payload built in VerifySaRequestL + // + CleanupStack::Pop(Acquire); + Acquire->ReplaceSA(GetProposedSa()); + Acquire->SetSPI_Out(iChild.iSPI_Out); + Acquire->SetResponse(); + if ( iChild.iTransport ) + { + + Acquire->SetTransport(); + } + CIkev2Acquire::Link(Acquire, GetAcquireQue()); + DEBUG_LOG(_L("Acquire linked.")); + + if ( aIkeMsg->iCp ) + { + // + // CP payload received as IKE SA responder + // Handle CP payload and return "dummy" + // virtual IP to initiator. + // + delete iConfigMode; + iConfigMode = NULL; + iConfigMode = CIkev2Config::NewL(Acquire, (TInetAddr*)&iHdr.iRemoteAddr); + iConfigMode->ProcessCpL(aIkeMsg->iCp); + Acquire->SetVirtualIp(); + } + // + // Get SPI for new inbound SA + // + iChild.GenerateIpsecKeysL(iHdr.iSK_d, KZeroDesc, + *iNonce_I, *iNonce_R, iHdr.iPRFAlg); + + if ( iPkiService && !iEapPlugin && + aIkeMsg->iCertReqs && + aIkeMsg->iCertReqs->Count() ) + { + GetOwnIdentityL(); // Get own Identity from Certificate (or policy) + AddIdToSignedDataL(ETrue, iAuthMsgResp, iLocalIdentity->PayloadData()); + + CIkev2Acquire* Acquire = CIkev2Acquire::PeekFirst(GetAcquireQue()); + if ( Acquire ) + { + GetIpsecSPI(Acquire); + iState = KStateIkeSaAuthWaitSpi; + } + else + { + DEBUG_LOG(_L("CIkev2Acquire::PeekFirst returned NULL.")); + DEBUG_LOG(_L("Sending IKE_AUTH response.")); + SendIkeAuthMessageL(); + } + + } + else + { + GetOwnIdentityL(); + AddIdToSignedDataL(ETrue, iAuthMsgResp, iLocalIdentity->PayloadData()); + GetIpsecSPI(Acquire); + iState = KStateIkeSaAuthWaitSpi; + } + } + else + { + if ( iPkiService && !iEapPlugin && + aIkeMsg->iCertReqs && + aIkeMsg->iCertReqs->Count() ) + { + GetOwnIdentityL(); // Get own Identity from Certificate (or policy) + AddIdToSignedDataL(ETrue, iAuthMsgResp, iLocalIdentity->PayloadData()); + + CIkev2Acquire* Acquire = CIkev2Acquire::PeekFirst(GetAcquireQue()); + if ( Acquire ) + { + GetIpsecSPI(Acquire); + iState = KStateIkeSaAuthWaitSpi; + } + else + { + SendIkeAuthMessageL(); + } + } + else + { + // + // Build and send an IKE_AUTH response + // + GetOwnIdentityL(); + AddIdToSignedDataL(ETrue, iAuthMsgResp, iLocalIdentity->PayloadData()); + SendIkeAuthMessageL(); + } + } + break; + + default: + // + // Ignore received message silently + // + DEBUG_LOG1(_L("IKE_SA_INIT message received in state %d"), iState); + break; + } + } + + return ETrue; +} + +TBool CIkev2Negotiation::ProcessChildSaL(CIkev2Payloads* aIkeMsg) +{ + ASSERT(aIkeMsg); + // + // Process IKE message of exchange type CREATE_CHILD_SA + // + TUint16 PfsDHGroup; + ThdrISAKMP* IkeHdr = aIkeMsg->GetIkeMsg(); // IKE Message fixed header + TBool Response = IkeHdr->GetFlags() & IKEV2_RESPONSE_MSG; + TBool Initiator = IkeHdr->GetFlags() & IKEV2_INITIATOR; + TUint32 MsgId = IkeHdr->GetMessageId(); + + if ( iHdr.iInitiator ) + { + if ( Initiator ) + { + DEBUG_LOG1(_L("IKEv2 Message with Orig_Init-bit in wrong state: %d"), iState); + SetNotifyCode(INVALID_SYNTAX); + return EFalse; + } + } + else + { + if ( !Initiator ) + { + DEBUG_LOG1(_L("IKEv2 Message without Orig_Init-bit in wrong state: %d"), iState); + SetNotifyCode(INVALID_SYNTAX); + return EFalse; + } + } + + if ( Response ) + { + // + // CREATE_CHILD_SA response message received + // + switch ( iState ) + { + case KStateIkeChildSARequest: + // + // Received message should be a response to a + // CREATE_CHILD_SA request transmitted. + // Response message should be format: + // HDR(A,B), SK { SA, Nr, [KEr], [TSi, TSr]} + // + if ( MsgId != iHdr.ExpectedResponseId() ) + { + DEBUG_LOG1(_L("Wrong message id in response; state: %d"), iState); + + SetNotifyCode(INVALID_MESSAGE_ID); + StoreNotifyData32(MsgId); + return EFalse; + } + if ( aIkeMsg->iEncr ) + { + ProcessNotifyPayloadsL(*aIkeMsg->iNotifs, EFalse, CREATE_CHILD_SA); + if ( iDeleteIkeSA ) + { + DEBUG_LOG1(_L("Error Notify in CREATE_CHILD_SA response: %d"), iState); + return EFalse; + } + if ( iChildSARejected ) + { + DEBUG_LOG1(_L("CHILD_SA rejected Notify in CREATE_CHILD_SA response: %d"), iState); + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + iStopped = ETrue; + return EFalse; + } + } + if ( !CheckPayloadsOrder(aIkeMsg, CREATE_CHILD_SA, ETrue) ) + { + DEBUG_LOG1(_L("Erroneous CREATE_CHILD_SA response: %d"), iState); + SetNotifyCode(INVALID_SYNTAX); + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + return EFalse; + } + if ( aIkeMsg->iNonce->PlDataLen() < IKEV2_MIN_NONCE_SIZE ) + { + DEBUG_LOG1(_L("Nonce data too short %d"), iState); + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + iStopped = ETrue; + return EFalse; + } + + if ( !Ikev2Proposal::VerifySaResponseL(iHdr, iChild, *iChildSaRequest->SA(), *aIkeMsg) ) + { + DEBUG_LOG1(_L("Unaccepted SA content in CREATE_CHILD_SA response: %d"),iState); + SetNotifyCode(NO_PROPOSAL_CHOSEN); + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + return EFalse; + } + if ( !IpsecSelectors::VerifyTrafficSelectorsL(iChildSaRequest, (TTSPayloadIkev2*)aIkeMsg->iTsI, (TTSPayloadIkev2*)aIkeMsg->iTsR ) ) + { + DEBUG_LOG1(_L("Unaccepted Traffic Selectors in CREATE_CHILD_SA response: %d"),iState); + SetNotifyCode(TS_UNACCEPTABLE); + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + return EFalse; + } + delete iNonce_R; + iNonce_R = NULL; + iNonce_R = HBufC8::NewL(aIkeMsg->iNonce->PlDataLen()); + iNonce_R->Des().Copy(aIkeMsg->iNonce->PayloadData(), aIkeMsg->iNonce->PlDataLen()); + PfsDHGroup = iChildSaRequest->DHGroup(); + if ( PfsDHGroup ) + { + if ( !ProcessKeyExchangeL((TKEPayloadIkev2*)aIkeMsg->iKe, PfsDHGroup) ) + { + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + return EFalse; + } + HBufC8* g_ir = iDHKeys->ComputeAgreedKeyL(iDHPublicPeer->Des()); + CleanupStack::PushL(g_ir); + + iChild.GenerateIpsecKeysL(iHdr.iSK_d, *g_ir, + *iNonce_I, *iNonce_R, iHdr.iPRFAlg); + + + g_ir->Des().FillZ(); // Wipe out shared secret value from buffer + CleanupStack::PopAndDestroy(); //g_ir + + } + else if ( aIkeMsg->iKe ) + { + DEBUG_LOG1(_L("Unsolicted Key Exchange payload present in CREATE_CHILD_SA response: %d"),iState); + SetNotifyCode(INVALID_KE_PAYLOAD); + iChildSaRequest = Ikev2Pfkey::DeleteInboundSPI(iHdr, iIkeV2PlugInSession, iChildSaRequest); + return EFalse; + } + else + { + iChild.GenerateIpsecKeysL(iHdr.iSK_d, KZeroDesc, + *iNonce_I, *iNonce_R, iHdr.iPRFAlg); + + } + // + // CREATE_CHILD_SA request is completed Update + // + IpsecSANegotiatedL(); + break; + + case KStateIkeSARekeyRequest: + // + // Received message should be a response to a + // IKE SA rekey CHILD_SA request transmitted. + // Response message should be format: + // HDR(A,B), SK { SA, Nr, KEr } + // + if ( CheckPayloadsOrder(aIkeMsg, CREATE_CHILD_SA, ETrue) && aIkeMsg->iKe ) + { + DEBUG_LOG1(_L("IKE SA rekey message received as CHILD_SA response: %d"), iState); + ProcessIkeSARekeyL(aIkeMsg); + } + else + { + DEBUG_LOG1(_L("Erroneous IKE SA rekey message received as CHILD_SA response: %d"),iState); + } + //iIkeV2PlugInSession.UpdateIkev2SAL(&iHdr, NULL, EFalse); + BuildDeleteRequestL(NULL); // Delete IKE SA rekeyed + iIkeV2PlugInSession.DeleteIkev2SA(iHdr.SaId()); + break; + + default: + // + // Ignore received message silently + // + DEBUG_LOG1(_L("CREATE_CHILD_SA response received in state %d"), iState); + break; + + } + } + else + { + // + // CREATE_CHILD_SA request message received + // + if ( MsgId != iHdr.ExpectedRequestId() ) { + if ( iHdr.iLastResponse != NULL && + MsgId == iHdr.iLastResponse->MessageId() ) + { + // + // Retransmission of an earlier request. + // Retransmit current response + // + iState = KStateIkeChildSAResponse; + DoRetransmitL(ETrue); + return ETrue; + } + else { + DEBUG_LOG1(_L("Wrong message id in request, state: %d"), iState); + SetNotifyCode(INVALID_MESSAGE_ID); + StoreNotifyData32(MsgId); + return EFalse; + } + } + + if ( iState >= KStateIkeSaCompleted ) + { + // + // Received CREATE_CHILD_SA message can be one of the + // following: + // -- Create new Ipsec SA request: + // HDR(A,B), SK { SA, Nr, [KEi], [TSi, TSr]} + // -- Rekey Ipsec SA request: + // HDR(A,B), SK { N, SA, Ni, [KEi], [TSi, TSr]} + // -- Rekey IKE SA request: + // HDR(A,B), SK { SA, Ni, KEi} + // + if ( !CheckPayloadsOrder(aIkeMsg, CREATE_CHILD_SA, EFalse) ) + { + DEBUG_LOG1(_L("Erroneous CREATE_CHILD_SA request: %d"), iState); + SetNotifyCode(INVALID_SYNTAX); + return EFalse; + } + // + // Check is the current request an IKE SA rekey by checking + // Proposal payload protocol value + // + if ( Ikev2Proposal::IkeSaRekey(aIkeMsg) ) + { + TBool Status; + if ( iState == KStateIkeSARekeyRequest ) + { + DEBUG_LOG1(_L("IKE SA Rekey collision for SAID: %d"), iHdr.SaId()); + SetNotifyCode(NO_ADDITIONAL_SAS); + Status = EFalse; + } + else + { + DEBUG_LOG1(_L("IKE SA Rekey started by peer for SAID: %d"), iHdr.SaId()); + iState = KStateIkeSARekeyResponse; + Status = ProcessIkeSARekeyL(aIkeMsg); + iIkeV2PlugInSession.UpdateIkev2SAL(&iHdr, NULL); + } + return Status; + } + if ( CIkev2Acquire::Responding(GetAcquireQue()) ) + { + DEBUG_LOG1(_L("CREATE_CHILD_SA IKE SA request already pending: %d"), iState); + SetNotifyCode(NO_ADDITIONAL_SAS); + return EFalse; + } + + // + // Get acceptable Ipsec policy for peer defined traffic + // selectors (and peer address) + // + CIkev2Acquire* Acquire = IpsecSelectors::GetIpsecPolicyL(iIkeV2PlugInSession, aIkeMsg, + iHdr.iIkeData->iGroupDesc_II); + if ( !Acquire ) + { + DEBUG_LOG1(_L("Unaccepted Traffic Selectors in CREATE_CHILD_SA request: %d"),iState); + SetNotifyCode(TS_UNACCEPTABLE); + return EFalse; + } + CleanupStack::PushL(Acquire); + HBufC8* proposedSaBuffer = NULL; + if (!Ikev2Proposal::VerifySaRequestAndGetProposedSaBufferL(iHdr, iChild, *Acquire->SA(), + *aIkeMsg, proposedSaBuffer)) + { + CleanupStack::PopAndDestroy(Acquire); + DEBUG_LOG1(_L("Unaccepted SA content in CREATE_CHILD_SA request: %d"),iState); + SetNotifyCode(NO_PROPOSAL_CHOSEN); + return EFalse; + } + this->SetProposedSa(proposedSaBuffer); + proposedSaBuffer = NULL; + delete iNonce_I; + iNonce_I = NULL; + iNonce_I = HBufC8::NewL(aIkeMsg->iNonce->PlDataLen()); + iNonce_I->Des().Copy(aIkeMsg->iNonce->PayloadData(), aIkeMsg->iNonce->PlDataLen()); + if ( aIkeMsg->iKe ) + { + PfsDHGroup = Acquire->DHGroup(); + if ( PfsDHGroup == 0 ) + { + PfsDHGroup = Ikev2Proposal::GetDHGroup(iHdr.iIkeData->iGroupDesc_II); + Acquire->DHGroup(PfsDHGroup); + } + if ( !ProcessKeyExchangeL((TKEPayloadIkev2*)aIkeMsg->iKe, PfsDHGroup) ) + { + CleanupStack::PopAndDestroy(Acquire); + return EFalse; + } + } + CleanupStack::Pop(Acquire); + Acquire->SetSPI_Out(iChild.iSPI_Out); + Acquire->SetResponse(); + if ( iChild.iTransport ) + Acquire->SetTransport(); + CIkev2Acquire::Link(Acquire, GetAcquireQue()); + // + // Get SPI for new inbound SA + // + GetIpsecSPI(Acquire); + } + else + { + // + // Ignore received message silently + // + DEBUG_LOG1(_L("CREATE_CHILD_SA request received in state %d"), iState); + } + } + + return ETrue; +} + +TBool CIkev2Negotiation::ProcessIkeSARekeyL(CIkev2Payloads* aIkeMsg) +{ + ASSERT(aIkeMsg); + // + // Process IKE SA rekey message (as IKE_CHILD_SA exchange) + // HDR(A,B), SK { SA, Nonce, KE} + // + + + if ( (iState == KStateIkeSARekeyRequest) || (iState == KStateIkeSARekeyResponse) ) + { + // + // Received CREATE_CHILD_SA message for IKE SA rekey must + // look like the following: HDR(A,B), SK { SA, Ni, [KEi]} + // Allocate a new CIkev2Negotiation object for new IKE SA + // + // + CIkev2Negotiation* NewSA = new (ELeave) CIkev2Negotiation(iIkeV2PlugInSession, iPfKeySocketIf, + iEventLogger, iMessageSendQue, iDebug, 0); + CleanupStack::PushL(NewSA); + + //Do not copy the previous sent request and response: + CIkeV2Message* lastResponse = iHdr.iLastResponse; + iHdr.iLastResponse = NULL; + CIkeV2Message* lastRequest = iHdr.iLastRequest; + iHdr.iLastRequest = NULL; + NewSA->iHdr.Copy(iHdr); + iHdr.iLastResponse = lastResponse; + iHdr.iLastRequest = lastRequest; + + + NewSA->iHdr.iWindowSize = 1; + NewSA->iHdr.iEncrAlg = 0; + NewSA->iHdr.iPRFAlg = 0; + NewSA->iHdr.iIntegAlg = 0; + NewSA->iHdr.iDHGroup = 0; + NewSA->iHdr.iAuthMethod = 0; + NewSA->iHdr.iCipherKeyLth = 0; + NewSA->iHdr.iCipherBlkLth = 0; + NewSA->iHdr.iIntChkSumLth = 0; + + if ( iState == KStateIkeSARekeyRequest ) + { + NewSA->iHdr.iInitiator = ETrue; + NewSA->iHdr.SetSaId(iSAid_Rekey); + NewSA->iHdr.SetSpiI(iSPI_Rekey); + NewSA->iNonce_I = iNonce_I; // Nonce was created in BuildIkeSaRekeyMsgL() earlier + NewSA->iDHKeys = iDHKeys; // DH keys object was created in BuildIkeSaRekeyMsgL() earlier + iNonce_I = NULL; + iDHKeys = NULL; + } + else + { + NewSA->iHdr.iInitiator = EFalse; + NewSA->iHdr.SetSaId(iIkeV2PlugInSession.GetSAId()); // Get a new SA Id + NewSA->CreateIkeSPI(NewSA->iHdr.SpiR()); + } + // + // Build a SA payload from current IKE policy and + // verify received IKE SA request with it + // + HBufC8* SaBfr = Ikev2Proposal::FromPolicyToProposaL(NewSA->iHdr, NewSA->iSPI_Rekey, NewSA->iDHGroupGuess, ETrue); + CleanupStack::PushL(SaBfr); + HBufC8* proposedSaBuffer = NULL; + TBool SaOk = Ikev2Proposal::VerifySaRequestAndGetProposedSaBufferL(NewSA->iHdr, NewSA->iChild, + *SaBfr, *aIkeMsg, proposedSaBuffer); + CleanupStack::PopAndDestroy(); + if ( iState == KStateIkeSARekeyRequest ) + SaOk &= Ikev2Proposal::GetRekeySpi(aIkeMsg, NewSA->iHdr.SpiR()); + else SaOk &= Ikev2Proposal::GetRekeySpi(aIkeMsg, NewSA->iHdr.SpiI()); + if ( !SaOk ) + { + DEBUG_LOG1(_L("Unaccepted SA content in IKE_SA Rekey request: %d"), iState); + SetNotifyCode(NO_PROPOSAL_CHOSEN); + CleanupStack::PopAndDestroy(NewSA); + return EFalse; + } + NewSA->SetProposedSa(proposedSaBuffer); + proposedSaBuffer = NULL; + if ( aIkeMsg->iNonce->PlDataLen() < IKEV2_MIN_NONCE_SIZE ) + { + DEBUG_LOG1(_L("Nonce data too short in IKE_SA Rekey request %d"), iState); + SetNotifyCode(INVALID_SYNTAX); + CleanupStack::PopAndDestroy(NewSA); + return EFalse; + } + if ( iState == KStateIkeSARekeyRequest ) + { + NewSA->iNonce_R = HBufC8::NewL(aIkeMsg->iNonce->PlDataLen()); + NewSA->iNonce_R->Des().Copy(aIkeMsg->iNonce->PayloadData(), aIkeMsg->iNonce->PlDataLen()); + } + else + { + NewSA->iNonce_I = HBufC8::NewL(aIkeMsg->iNonce->PlDataLen()); + NewSA->iNonce_I->Des().Copy(aIkeMsg->iNonce->PayloadData(), aIkeMsg->iNonce->PlDataLen()); + } + + if ( !NewSA->ProcessKeyExchangeL((TKEPayloadIkev2*)aIkeMsg->iKe, NewSA->iHdr.iDHGroup) ) + { + //If there was notify code set, copy it to current negotiation before destroying NewSa + if(NewSA->GetNotifyCode()) + { + SetNotifyCode(NewSA->GetNotifyCode()); + } + TInt dataLth(0); + TUint8* notifyData = NewSA->NotifyData(dataLth); + if(dataLth == 2) + { + StoreNotifyData16(GET16(notifyData)); + } + else if(dataLth == 4) + { + StoreNotifyData32(GET32(notifyData)); + } + CleanupStack::PopAndDestroy(NewSA); + return EFalse; + } + + if ( iState == KStateIkeSARekeyResponse ) + { + // + // Build IKE SA rekey response (CHILD_SA response): + // HDR, SAr, Nr, KEr + // + iDHKeys = NewSA->iDHKeys; // To calculate own DH value + iSPI_Rekey = NewSA->iHdr.SpiR(); + SetProposedSa(NewSA->GetProposedSa()); + BuildIkeSaRekeyMsgL(EFalse); + NewSA->iNonce_R = iNonce_R; // Nonce is created in BuildIkeSaRekeyMsgL() + iNonce_R = NULL; + iDHKeys = NULL; + } + // + // Generate key material for new IKE SA + // + NewSA->GenerateIkeKeysL(&iHdr); + + // + // Create a new IKE SA and swap IPSec SA from rekeyed IKE SA + // to the new just created SA + // + iIkeV2PlugInSession.CreateIkev2SAL(NewSA->iHdr); + iIkeV2PlugInSession.InheritIpsecSas(NewSA->iHdr.SaId(), iHdr.SaId()); + + CleanupStack::PopAndDestroy(NewSA); + } + + return ETrue; +} + +TBool CIkev2Negotiation::ProcessInfoMsgL(CIkev2Payloads* aIkeMsg) +{ + ASSERT(aIkeMsg); + // + // Process IKE message of exchange type INFORMATIONAL + // HDR, SK {[N,] [D,] [CP,] ...} + // Only encyrpted and authenitcated are processed. + // + if ( !aIkeMsg->Encrypted() ) + { + if ( iState == KStateIdle) + iStopped = ETrue; + return EFalse; + } + ThdrISAKMP* IkeHdr = aIkeMsg->GetIkeMsg(); // IKE Message fixed header + TBool Response = IkeHdr->GetFlags() & IKEV2_RESPONSE_MSG; + TUint32 MsgId = IkeHdr->GetMessageId(); + + if ( Response ) + { + if ( (iState == KStateIkeInfoRequest) || (iState == KStateIkeDeleteRequest) ) + { + // + // A response received to a transmitted Informational request + // + DEBUG_LOG(_L("Response received to a transmitted Informational request")); + + if ( MsgId == iHdr.ExpectedResponseId() ) + { + iIkeV2PlugInSession.UpdateIkev2SAL(&iHdr, NULL); + if ( iState == KStateIkeDeleteRequest ) + { + iIkeV2PlugInSession.IkeSaDeleted(KErrNone); //IKE SA deletion going + } + else + { + if ( aIkeMsg->iNotifs->Count() ) + { + ProcessNotifyPayloadsL(*aIkeMsg->iNotifs, EFalse, INFORMATIONAL); + } + } + iStopped = ETrue; + return EFalse; + } + } + else if ( iState == KStateChildDeleteRequest ) + { + // + // A response received to a transmitted Child SA delete request + // + if ( aIkeMsg->iDeletes->Count() ) + { + ProcessDeletePayloadsL(*aIkeMsg->iDeletes, EFalse); + } + + // + // Update sequence numbers and IKE SA data + // + iIkeV2PlugInSession.UpdateIkev2SAL(&iHdr, NULL); + iStopped = ETrue; + return EFalse; + } + } + else + { + // + // A Informational request received. Process request according + // to payload content and send informational response. + // + DEBUG_LOG1(_L("INFORMATIONAL request received in state %d"), iState); + if ( MsgId == iHdr.ExpectedRequestId() ) + { + TBool BuildResponse = ETrue; + if ( aIkeMsg->iDeletes->Count() ) + { + BuildResponse = ProcessDeletePayloadsL(*aIkeMsg->iDeletes, ETrue); + } + if ( aIkeMsg->iNotifs->Count() ) + { + BuildResponse = ProcessNotifyPayloadsL(*aIkeMsg->iNotifs, ETrue, INFORMATIONAL); + } + if ( BuildResponse ) + { + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + INFORMATIONAL, + iHdr.iInitiator, + ETrue, + iHdr.ExpectedRequestId(), + iDebug); + CleanupStack::PushL(ikeMsg); + ikeMsg->AppendEncryptedPayloadL(iHdr.iCipherBlkLth); + CleanupStack::Pop(ikeMsg); + SendIkeMsgL(ikeMsg); + if ( (iState != KStateIkeInfoRequest) && (iState != KStateIkeDeleteRequest) && (iState != KStateIkeDeleteResponse) ) + { + iState = KStateIkeInfoResponse; + iIkeV2PlugInSession.UpdateIkev2SAL(&iHdr, NULL); + } + } + } + } + + return ETrue; +} + +TBool CIkev2Negotiation::ProcessNotifyPayloadsL(const CArrayFixFlat& aNotifys, + TBool aRequest, TInt aExchange) +{ + if ( Ikev2MobIke::ProcessNotifysL(this, aNotifys, aRequest, aExchange) ) + { + return EFalse; // Notify payload(s) was processed by MOBIKE protocol + } + + TInt MsgType; + TNotifPayloadIkev2* Payload; + TInt Count = aNotifys.Count(); + TInt i = 0; + + while ( i < Count ) + { + Payload = aNotifys.At(i); + MsgType = (TInt)Payload->GetMsgType(); + DEBUG_LOG1(_L("Received Notify payload message type %d"), MsgType); + // + // Process possible error type Notify messages + // + if (aExchange == IKE_SA_INIT) + { + switch ( MsgType ) + { + case INVALID_SYNTAX: + //Fall through + case NO_PROPOSAL_CHOSEN: + return EFalse; + case INVALID_KE_PAYLOAD: + ProcessInvalidKePayloadNotifyL(); + return ETrue; + case COOKIE: + return ProcessCookieL(aNotifys, aRequest); + default: + break; + } + } + else + { + switch ( MsgType ) + { + case UNSUPPORTED_CRITICAL_PAYLOAD: + case INVALID_SYNTAX: + case INVALID_MESSAGE_ID: + case AUTHENTICATION_FAILED: + case INTERNAL_ADDRESS_FAILURE: + case FAILED_CP_REQUIRED: + // + // When some of these error types received IKE SA shall + // corresponding IKE SA shall be deleted + // + iDeleteIkeSA = ETrue; + break; + + case NO_PROPOSAL_CHOSEN: + case SINGLE_PAIR_REQUIRED: + case NO_ADDITIONAL_SAS: + case TS_UNACCEPTABLE: + case INVALID_SELECTORS: + // + // When some of these error types received within + // IKE_AUTH or CREATE_CHILD_SA exchange (in response) + // Child SA request is interpreted to be failed + // + if ( ((aExchange == IKE_AUTH) || (aExchange == CREATE_CHILD_SA) ) && !aRequest ) + iChildSARejected = ETrue; + break; + + default: + break; + } + } + + i++; + } + + return ETrue; +} + +TBool CIkev2Negotiation::ProcessCookieL(const CArrayFixFlat& aNotifys, TBool aRequest) +{ + + // + // Special handling for COOKIE Notify payload. + // The following actions are taken: + // - Assure that the first Notify payload in array is cookie + // - When the COOKIE is received in response (aRequest = EFalse) + // - Retransmit IKE_SA_INIT request again in format: + // HDR(A,0), N(COOKIE), SAi1, KEi, Ni, [Nat Notifies] + // - When the COOKIE is received in request (aRequest = ETrue) + // - Assure that COOKIE returned by the initiator is the we + // have earlier transmitted. + // + if ( aNotifys.Count() ) + { + const TNotifPayloadIkev2* NotifyPayload = aNotifys.At(0); + if ( NotifyPayload->GetMsgType() == COOKIE && !aRequest) + { + // + // Local end COOKIE usage has not been implemented yet + // + + // + // Init a new IKE message buffer and copy received COOKIE + // Notify to the first payload. Concatenate then all + // payloads from original IKE_SA_INIT request to this new + // IKE message (and set next payload field in Notify) + // + DEBUG_LOG1(_L("Cookie received, IKE_SA_INIT repeated: %d"), iState); + if ( iCookieReturned ) + { + // + // One cookie already returned. Avoid cookie-loop + // by stopping ongoing IKE_SA_INIT exchange + // + DEBUG_LOG(_L("Cookie already returned once, IKE_SA_INIT exchange stopped")); + return EFalse; + } + CIkeV2Message* originalIkeSaInitRequest = iHdr.iLastRequest; + const TPtrC8 cookieData(NotifyPayload->NotifData(), NotifyPayload->NotifDataLength()); + originalIkeSaInitRequest->PrependCookieNotifyPayloadL(cookieData); + iHdr.iLastRequest = NULL; //claims the ownership of the message + + SendIkeMsgL(originalIkeSaInitRequest); + iTimer->Cancel(); // Reset transmit retry timer + iTimer->IssueRequest(iSendAttempt); // Start retry timer + iCookieReturned = ETrue; + return ETrue; + } + } + DEBUG_LOG1(_L("Cookie required, NO COOKIE Notify found: %d"), iState); + + return EFalse; +} + + + +TBool CIkev2Negotiation::ProcessDeletePayloadsL(const CArrayFixFlat& aDeletes, + TBool aRequest) +{ + // + // Process delete payloads received. + // + CDesC8ArrayFlat* SpiList = NULL; + TUint8 Protocol = IKEV2_PROT_NONE; + + for (TInt i = 0; i < aDeletes.Count(); ++i) + { + const TDeletePlIkev2* Payload = aDeletes.At(i); + Protocol = Payload->GetProtocolId(); + switch ( Protocol ) + { + case IKEV2_PROTOCOL: + // + // Deletion of current existing IKE SA. All IPSEC SA:s + // negotiated within IKE SA are deleted implicitly + // + iIkeV2PlugInSession.DeleteIkev2SA(iHdr.SaId()); + delete SpiList; + SpiList = NULL; + i = aDeletes.Count(); // Stop outer while loop + iState = KStateIkeDeleteResponse; // Set next state here + break; + + case IKEV2_IPSEC_AH: + case IKEV2_IPSEC_ESP: + if ( Payload->GetSPISize() == 4 ) + { + // + // Delete Ipsec SPI:s from IKE SA (and IPSEC SADB) + // If Delete payload received within Info request + // build inbound SPI list of corresponding inbound + // SA:s + // + TUint SpiCount = (TInt)Payload->GetNbrOfSpis(); + if ( TPayloadIkev2::Cast(Payload)->GetLength() == + ( TDeletePlIkev2::Size() + 4*SpiCount) ) + { + const TUint8* Spis = Payload->SPIs(); + if ( aRequest && !SpiList ) + { + SpiList = new (ELeave) CDesC8ArrayFlat(2); + CleanupStack::PushL(SpiList); + } + while ( SpiCount ) + { + TPtrC8 Spi(Spis, 4); + if ( SpiList ) + { + const TIkeV2IpsecSAData* IpsecSa = + iIkeV2PlugInSession.FindIpsecSAData(iHdr.SaId(), Spi, EFalse); + if ( IpsecSa && IpsecSa->iSPI_In.Length() > 0 ) + { + SpiList->AppendL(IpsecSa->iSPI_In); + } + } + iIkeV2PlugInSession.DeleteIpsecSAData(iHdr.SaId(), Spi, EFalse); + Spis += 4; + SpiCount--; + } + } + } + break; + default: + break; + } + } + + if ( SpiList ) + { + // + // Build Informational exchange response with a + // Delete payload containing SPI:s of corresponding + // inbound SA:s. + // + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + INFORMATIONAL, + iHdr.iInitiator, + ETrue, + iHdr.ExpectedRequestId(), iDebug); + CleanupStack::PushL(ikeMsg); + ikeMsg->AppendEncryptedPayloadL(iHdr.iCipherBlkLth); + ikeMsg->AppendDeletePayloadL(Protocol, *SpiList); + CleanupStack::Pop(ikeMsg); + SendIkeMsgL(ikeMsg); + CleanupStack::PopAndDestroy(SpiList); + iState = KStateIkeInfoResponse; + iIkeV2PlugInSession.UpdateIkev2SAL(&iHdr, NULL); + aRequest = EFalse; + } + + return aRequest; +} + +void CIkev2Negotiation::ProcessInvalidKePayloadNotifyL() +{ + // Build and send new IKE_SA_INIT message (request) with another DH group # + // HDR, SAi1, KEi, Ni, N[NAT_SRC], N[NAT_DST] + // + iDHGroupGuess++; + delete iDHKeys; // Delete old DH object + iDHKeys = NULL; + iHdr.iDHGroup = 0; + + TUint32 lastRequestMsgId = 0; + if(iHdr.iLastRequest != NULL) + { + lastRequestMsgId = iHdr.iLastRequest->MessageId(); + } + + CIkeV2Message* ikeMsg = CIkeV2Message::NewL(iHdr.SpiI(), + iHdr.SpiR(), + IKE_SA_INIT, + iHdr.iInitiator, + EFalse, + lastRequestMsgId, + iDebug); + CleanupStack::PushL(ikeMsg); + + HBufC8* saBfr = Ikev2Proposal::FromPolicyToProposaL(iHdr, iSPI_Rekey, iDHGroupGuess); + CleanupStack::PushL(saBfr); + ikeMsg->AppendSaPayloadL(*saBfr); + CleanupStack::Pop(saBfr); + SetProposedSa(saBfr); + + AppendKEPayloadL(*ikeMsg, iHdr.iDHGroup); + ikeMsg->AppendNoncePayloadL(*iNonce_I); + if ( !iHdr.iIkeData->iUseNatProbing ) + { + delete iNatNotify; + iNatNotify = NULL; + + TInetAddr LocalIp; + if ( iHdr.iIkeData->iUseMobIke ) + LocalIp.SetAddress(KInetAddrNone); + else LocalIp = iHdr.iLocalAddr; + iNatNotify = CIkev2NatT::NewL( + LocalIp, iHdr.iRemoteAddr, IKE_PORT, ikeMsg->InitiatorSpi(), ikeMsg->ResponderSpi()); + + ikeMsg->AppendNotifyPayloadL(IKEV2_PROTOCOL, KZeroDesc, NAT_DETECTION_SOURCE_IP, + iNatNotify->SourceNofify()); + ikeMsg->AppendNotifyPayloadL(IKEV2_PROTOCOL, KZeroDesc, NAT_DETECTION_DESTINATION_IP, + iNatNotify->DestinNofify()); + } + + CleanupStack::Pop(ikeMsg); + SendIkeMsgL(ikeMsg); + + iState = KStateIkeSaInitRequest; +} + + + +void CIkev2Negotiation::GetNonceDataL(TBool aInitiator) +{ + // + // Get random data for local nonce + // + HBufC8* Nonce; + if ( aInitiator ) + { + Nonce = HBufC8::NewL(IKEV2_DEF_NONCE_SIZE); + delete iNonce_I; + iNonce_I = Nonce; + } + else + { + Nonce = HBufC8::NewL(IKEV2_DEF_NONCE_SIZE); + delete iNonce_R; + iNonce_R = Nonce; + } + TPtr8 RandOctet(Nonce->Des()); + RandOctet.SetLength(IKEV2_DEF_NONCE_SIZE); + TRandom::RandomL(RandOctet); +} + +void CIkev2Negotiation::GetOwnIdentityL(TBool aEapIdResponse) +{ + if ( iLocalIdentity ) + { + return; // We already have own identity data + } + + // + // The own IKE identity data is built with the following system: + // -- If Own Certificate exist take try to get identity data from it + // If no Certificate or identity cannot be get from certificate + // -- If EAP used use identity speficied by the EAP plugin + // -- If EAP not used, get own identity data information from current IKE policy + // If no identity information found use local IP address as + // identity. Own IKE Identity information is stored in iLocalIdentity + // buffer (linked into negotiation object) in format of Identity + // payload (TIDPayloadIkev2) + // + + + HBufC8* IdBfr = NULL; + TUint8 IdType = ID_NOT_DEFINED; + + + //Try to get the identity from the user certificate + if ( iPkiService && iPkiService->UserCertificateData().Length() > 0 ) + { + IdType = iHdr.iIkeData->iIdType; + IdBfr = IkePkiUtils::GetIdentityFromCertL(IdType, iPkiService->UserCertificateData()); + if ( IdBfr != NULL) + { + if (IdType == ID_NOT_DEFINED) + { + IdType = ID_DER_ASN1_DN; + } + } + else + { + //We didn't get the ID data from the user certificate. + //Make sure that the type is set to not defined. + IdType = ID_NOT_DEFINED; + } + } + + //If we didn't get the identity from the user certificate, + //try to get it from the EAP plugin. + if ( IdType == ID_NOT_DEFINED && iEapPlugin ) + { + __ASSERT_DEBUG(IdBfr == NULL, User::Invariant()); + // + // Try to get Own identity data from EAP Plug-in + // + IdBfr = iEapPlugin->Identity(); + if ( IdBfr != NULL && IdBfr->Length() ) + { + // + // Identity data provided by EAP plug-in. Define IKE Id type + // value according to Identity data content. If Id data + // contains realm (format username@realm) Id type + // ID_RFC822_ADDR is used. If no realm in ID use type ID_KEY_ID + // + TInt offset = IdBfr->Find(_L8("@")); + IdType = ( offset == KErrNotFound ) ? ID_KEY_ID : ID_RFC822_ADDR; + } + else + { + delete IdBfr; + IdBfr = NULL; + if ( !aEapIdResponse ) + return; // Identity not yet available continue waiting + } + } + + //If we don't have identity so far, try to get it from the + //policy: + if ( IdType == ID_NOT_DEFINED && + iHdr.iIkeData->iIdType != ID_NOT_DEFINED && + iHdr.iIkeData->iFQDN.Length() > 0) + { + __ASSERT_DEBUG(IdBfr == NULL, User::Invariant()); + IdBfr = HBufC8::NewL(iHdr.iIkeData->iFQDN.Length()); + IdBfr->Des().Copy(iHdr.iIkeData->iFQDN); + IdType = iHdr.iIkeData->iIdType; + } + + + //If we have not been able to get the identity so far, we are using the default + //identity, which is our own IP address. + if ( IdType == ID_NOT_DEFINED) + { + __ASSERT_DEBUG(IdBfr == NULL, User::Invariant()); + if ( (iHdr.iLocalAddr.Family()== KAfInet) || iHdr.iLocalAddr.IsV4Mapped() ) + { + TUint32 num = ByteOrder::Swap32(iHdr.iLocalAddr.Address());//Put in network order + IdBfr = HBufC8::NewL(sizeof(num)); + IdBfr->Des().Append(reinterpret_cast(&num), sizeof(num)); + IdType = ID_IPV4_ADDR; + } + else + { + IdBfr = HBufC8::NewL(16); + const TUint8* pnum = &iHdr.iLocalAddr.Ip6Address().u.iAddr8[0]; //Address in a bytestream + IdBfr->Des().Append(pnum, 16); + IdType = ID_IPV6_ADDR; + } + } + + __ASSERT_DEBUG((IdType != ID_NOT_DEFINED && IdBfr != NULL), User::Invariant()); + CleanupStack::PushL(IdBfr); + iLocalIdentity = CIkeV2Identity::NewL(IdType, *IdBfr); + CleanupStack::PopAndDestroy(IdBfr); +} + + +void CIkev2Negotiation::GenerateIkeKeysL(TIkev2SAData* aRekeydSaData) +{ + // + // Generate IKE keying material. Start by calculating + // Diffie-Hellman secret. + // + User::LeaveIfNull(iDHPublicPeer); + if ( !iDHKeys ) + { + iDHKeys = CDHKeys::CreateDHKeyL(iHdr.iDHGroup); + iDHKeys->XValueL(); // Calculate own DH public value + } + HBufC8* g_ir = iDHKeys->ComputeAgreedKeyL(iDHPublicPeer->Des()); + CleanupStack::PushL(g_ir); + delete iDHKeys; + iDHKeys = NULL; + + HBufC8* Ni_Nr; + HBufC8* SKEYSEED; + TUint16 prfAlg(0); + + if ( aRekeydSaData ) + { + // + // Calculate IKE keying material seed SKEYDSEED = prf(SK_d(old), [g^ir (new)] | Ni | Nr) + // + Ni_Nr = HBufC8::NewL(g_ir->Length() + iNonce_I->Length() + iNonce_R->Length()); + CleanupStack::PushL(Ni_Nr); + Ni_Nr->Des().Copy(g_ir->Des()); + Ni_Nr->Des().Append(iNonce_I->Des()); + Ni_Nr->Des().Append(iNonce_R->Des()); + + prfAlg = aRekeydSaData->iPRFAlg; + SKEYSEED = IkeCrypto::PrfhmacL(*Ni_Nr, aRekeydSaData->iSK_d, prfAlg); + CleanupStack::PushL(SKEYSEED); + } + else + { + // + // Calculate IKE keying material seed SKEYDSEED = prf(Ni | Nr, g^ir) + // + Ni_Nr = HBufC8::NewL(iNonce_I->Length() + iNonce_R->Length()); + CleanupStack::PushL(Ni_Nr); + Ni_Nr->Des().Copy(iNonce_I->Des()); + Ni_Nr->Des().Append(iNonce_R->Des()); + + prfAlg = iHdr.iPRFAlg; + SKEYSEED = IkeCrypto::PrfhmacL(*g_ir, *Ni_Nr, prfAlg); + CleanupStack::PushL(SKEYSEED); + } + + g_ir->Des().FillZ(); // Wipe out shared secret value from buffer + + iHdr.GenerateIkeKeyDerivatesL(SKEYSEED->Des(),prfAlg, *iNonce_I, *iNonce_R); + SKEYSEED->Des().FillZ(); // Wipe out SKEYSEED value from buffer + + CleanupStack::PopAndDestroy(3); //g_ir , Ni_Nr and SKEYSEED +} + + +void CIkev2Negotiation::SaveSignedDataL(TBool aLocal, const TDesC8& aIkeMsg) +{ + // + // Allocate buffer for signed octets needed for IKE SA + // authentication with AUTH payload. + // The signed octet contains the following data: + // Initiator: + // - IKE_SA_INIT message content (message number 1) + // concatenated with responder nonce data and value + // prf(SK_pi,IDi") where IDi" is initiator ID data without fixed + // payload header + // + // Responder: + // - IKE_SA_INIT message content (message number 2) + // concatenated with initiator nonce data and value + // prf(SK_pr,IDr") where IDr" is responder ID data without fixed + // payload header + // + TInt SignedLth = aIkeMsg.Length(); // Initial value + HBufC8* Nonce; + HBufC8** SignedBfrPtr; + if ( aLocal ) + { + if ( iHdr.iInitiator ) + { + SignedBfrPtr = &iAuthMsgInit; + Nonce = iNonce_R; + } + else { + SignedBfrPtr = &iAuthMsgResp; + Nonce = iNonce_I; + } + } + else + { + if ( iHdr.iInitiator ) + { + SignedBfrPtr = &iAuthMsgResp; + Nonce = iNonce_I; + } + else + { + SignedBfrPtr = &iAuthMsgInit; + Nonce = iNonce_R; + } + } + + SignedLth += Nonce->Length() + IkeCrypto::AlgorithmInfo(IKEV2_PRF, iHdr.iPRFAlg); + HBufC8* Signed = HBufC8::NewL(SignedLth); + Signed->Des().Copy(aIkeMsg); + Signed->Des().Append(Nonce->Des()); + + if ( aLocal && iLocalIdentity ) + { + // + // Add value prf(SK_px,IDx") into local signed data buffer end + // + AddIdToSignedDataL(ETrue, Signed, iLocalIdentity->PayloadData()); + } + + delete *SignedBfrPtr; + *SignedBfrPtr = Signed; +} + + +void CIkev2Negotiation::AddIdToSignedDataL(TBool aLocal, HBufC8* aSigned, const TDesC8& aIdData) +{ + ASSERT(aSigned); + // + // Add value prf(SK_px,IDx") into signed data buffer end + // + + HBufC8* signedIdData = NULL;; + if ( iHdr.iInitiator ) + { + if ( aLocal ) + { + signedIdData = IkeCrypto::PrfhmacL(aIdData, iHdr.iSK_pi, iHdr.iPRFAlg); + } + else + { + signedIdData = IkeCrypto::PrfhmacL(aIdData, iHdr.iSK_pr, iHdr.iPRFAlg); + } + } + else + { + if ( aLocal ) + { + signedIdData = IkeCrypto::PrfhmacL(aIdData, iHdr.iSK_pr, iHdr.iPRFAlg); + } + else + { + signedIdData = IkeCrypto::PrfhmacL(aIdData, iHdr.iSK_pi, iHdr.iPRFAlg); + } + } + aSigned->Des().Append(*signedIdData); + delete signedIdData; +} + +HBufC8* CIkev2Negotiation::SignAuthDataL(const TDesC8& aAuthData, TUint8 aAuthMethod) +{ + // + // Sign aMsgData according to authentication method parameter + // + HBufC8* signedAuthData = NULL; + + if ( iPkiService && + iPkiService->TrustedCaName().Length() > 0 && + iPkiService->UserCertificateData().Length() > 0 ) + { + // + // Message data is signed using private key + // + TPtrC8 TrustedCa(iPkiService->TrustedCaName()); + signedAuthData = HBufC8::NewLC(320); // reserved for sign (aware for 2048 bits signatures) + TPtr8 signedAuthDataPtr(signedAuthData->Des()); + + iPkiService->Ikev2SignatureL(TrustedCa, iHdr.iIkeData->iOwnCert, aAuthData, signedAuthDataPtr, aAuthMethod); + CleanupStack::Pop(signedAuthData); + } + else + { + // + // Message data is signed using negotiated PRF function as + // follows: + // AUTH = + // prf(prf(Shared Secret,"Key Pad for IKEv2"), ) + // If EAP method that creates a shared key as a side effect of + // authentication used, this shared key is used as Shared Secret + // Otherwise preshared key configured into policy is used as + // Shared secret. + // + if ( !iPresharedKey ) + iPresharedKey = Ikev2Proposal::GetPSKFromPolicyL(iHdr.iIkeData); + // + // Calculate KEY = prf(Shared Secret,"Key Pad for IKEv2") + // + HBufC8* PskKey = IkeCrypto::PrfhmacL(KIkev2PSKData, *iPresharedKey, iHdr.iPRFAlg); + CleanupStack::PushL(PskKey); + // + // Calculate prf(KEY, ) + // + signedAuthData = IkeCrypto::PrfhmacL(aAuthData, *PskKey, iHdr.iPRFAlg); + CleanupStack::PopAndDestroy(PskKey); + } + return signedAuthData; +} + +TBool CIkev2Negotiation::AddIdAndAuthenticatePeerL(CIkev2Payloads* aIkeMsg) +{ + ASSERT(aIkeMsg); + // + // Verify that authentication payload of peer is correct + // To do this the signed data octets of peer must be filled with + // value: prf(SK_px,IDx") + // So the peer ID payload must be verified first. + // + HBufC8* Signed; + TIDPayloadIkev2* Id; + if ( iHdr.iInitiator ) + { + Signed = iAuthMsgResp; + Id = (TIDPayloadIkev2*)aIkeMsg->iIdR; + } + else + { + Signed = iAuthMsgInit; + Id = (TIDPayloadIkev2*)aIkeMsg->iIdI; + } + if ( !Signed || !Id ) + return EFalse; + + if ( !iPeerIdInSignedData ) + { + TUint16 IdLth = TPayloadIkev2::Cast(Id)->GetLength(); + if ( IdLth < TIDPayloadIkev2::Size() ) + { + DEBUG_LOG1(_L("Peer ID payload too short; Length %d"), IdLth); + return EFalse; + } + // + // Add value prf(SK_px,IDx") into peer signed data buffer end + // + TPayloadIkev2* idPayload = TPayloadIkev2::Cast(Id); + TPtrC8 idPtr(idPayload->PayloadData(), (idPayload->GetLength() - TPayloadIkev2::Size())); + AddIdToSignedDataL(EFalse, Signed, idPtr); + iPeerIdInSignedData = ETrue; + } + + return AuthenticatePeerL(aIkeMsg->iAuth); + +} + +TBool CIkev2Negotiation::AuthenticatePeerL(TAuthPayloadIkev2* aAuth) +{ + // + // Authenticate peer tication payload of peer is correct + // To do this the signed data octets of peer must be filled with + // value: prf(SK_px,IDx") + // So the peer ID payload must be verified first. + // + HBufC8* Signed; + if ( iHdr.iInitiator ) + Signed = iAuthMsgResp; + else Signed = iAuthMsgInit; + + if ( !Signed || !aAuth ) + return EFalse; + + TUint16 AuthLth = TPayloadIkev2::Cast(aAuth)->GetLength(); + if ( AuthLth < TAuthPayloadIkev2::Size() ) + { + DEBUG_LOG1(_L("Peer Auth payload too short; Length %d"), AuthLth); + return EFalse; + } + AuthLth = (TUint16)(AuthLth - TAuthPayloadIkev2::Size()); + TBool Status = EFalse; + + if ( aAuth->GetAuthMethod() == PRESHARED_KEY ) + { + DEBUG_LOG(_L("Authenticating SGW with PSK")); + // + // Pre shared key authentication is not accepted for peer if we + // have requested an certificate from peer (PKI authentication + // required) + // + if ( !iPkiAuthRequired ) + { + HBufC8* AuthRef = SignAuthDataL(*Signed, PRESHARED_KEY); + CleanupStack::PushL(AuthRef); + if ( AuthRef->Length() == AuthLth ) + { + Status = (Mem::Compare(AuthRef->Ptr(), AuthRef->Length(), aAuth->AuthData(), AuthLth ) == 0); + } + CleanupStack::PopAndDestroy(); // AuthRef + } + } + else + { + // + // Authentication based on PKI (private key signature) + // + if ( iPkiService && iPeerCert ) + { + DEBUG_LOG(_L("Authenticating SGW with certs")); + + TPtrC8 AuthData(Signed->Des()); + TPtrC8 Signature(aAuth->AuthData(), AuthLth); + Status = IkePkiUtils::VerifyIkev2SignatureL(Signature, AuthData, *iPeerCert); + iPkiAuthRequired = EFalse; + } + } + if (Status) + { + DEBUG_LOG(_L("SGW authentication success")); + } + else + { + DEBUG_LOG(_L("SGW authentication failed")); + } + + return Status; +} + + +TBool CIkev2Negotiation::VerifyPeerCertificateL(CArrayFixFlat* aCerts, TIDPayloadIkev2* aId ) +{ + TBool Status = EFalse; + + const CIkeCaList& trustedCaList = iPkiService->CaList(); + CX509Certificate* PeerCert = IkePkiUtils::VerifyCertificateL(*aCerts, trustedCaList); + + if ( PeerCert && aId ) + { + CleanupStack::PushL(PeerCert); + TPtrC8 IdData(aId->IdData(), (TPayloadIkev2::Cast(aId)->GetLength() - TIDPayloadIkev2::Size())); + Status = IkePkiUtils::CertifyIdentityL(PeerCert, IdData, (TInt)aId->GetIdType()); + if ( Status ) + { + DEBUG_LOG(_L("IDr matches the SGW certificate")); + if (iRemoteIdentity && !iHdr.iIkeData->iSkipRemoteIdCheck ) //iRemoteIdentity if the REMOTE_IF from the policy + { + //TIDPayloadIkev2* peerIdentityPayload = TIDPayloadIkev2::Cast(iRemoteIdentity->Ptr()); + if (iRemoteIdentity->IdType() == aId->GetIdType()) + { + TPtrC8 idPtr(aId->IdData(), + TPayloadIkev2::Cast(aId)->GetLength() - TIDPayloadIkev2::Size()); + TPtrC8 peerIdentityPtr(iRemoteIdentity->Identity()); + + //Check if we accept partial remote id + if (iHdr.iIkeData->iAcceptPartialRemoteId && + iRemoteIdentity->IdType() == ID_FQDN && + peerIdentityPtr.Length() > idPtr.Length()) + { + DEBUG_LOG(_L("Using PARTIAL_REMOTE_ID_CHECK")); + peerIdentityPtr.Set(peerIdentityPtr.Right(idPtr.Length())); + } + if (idPtr.Compare(peerIdentityPtr) == 0) + { + DEBUG_LOG(_L("IDr matches the REMOTE_ID")); + Status = ETrue; + } + else + { + DEBUG_LOG(_L("IDr does not match the REMOTE_ID")); + Status = EFalse; + } + } + else + { + DEBUG_LOG(_L("IDr payload ID does not match REMOTE_ID_TYPE")); + Status = EFalse; + } + } + } + else + { + DEBUG_LOG(_L("IDr does not match the SGW certificate")); + } + + if ( Status ) + { + CleanupStack::Pop(PeerCert); + delete iPeerCert; + iPeerCert = PeerCert; + } + else CleanupStack::PopAndDestroy(PeerCert); + } + return Status; +} + + +TBool CIkev2Negotiation::ProcessKeyExchangeL(TKEPayloadIkev2* aKePayload, TUint16 aGroup) +{ + // + // Process key exchange payload received from peer + // + if ( !aKePayload ) + { + DEBUG_LOG1(_L("Key Exchange payload not present, required Group %d"), aGroup); + SetNotifyCode(INVALID_KE_PAYLOAD); + StoreNotifyData16(aGroup); + return EFalse; + } + TUint16 PlLth = TPayloadIkev2::Cast(aKePayload)->GetLength(); + if (( PlLth <= TKEPayloadIkev2::Size() ) || ( aKePayload->GetDHGroup() != aGroup )) + { + DEBUG_LOG1(_L("Peer Key Exchange DH group does not match, Group %d"), aKePayload->GetDHGroup()); + SetNotifyCode(INVALID_KE_PAYLOAD); + StoreNotifyData16(aGroup); + return EFalse; + } + if ( !iDHKeys ) + iDHKeys = CDHKeys::CreateDHKeyL(aGroup); + PlLth = (TUint16)(PlLth - TKEPayloadIkev2::Size()); + if ( PlLth != iDHKeys->ModulusLength() ) + { + DEBUG_LOG1(_L("Peer DH public value length does not match group, Length %d"), PlLth); + SetNotifyCode(INVALID_KE_PAYLOAD); + StoreNotifyData16(aGroup); + return EFalse; + } + delete iDHPublicPeer; + iDHPublicPeer = NULL; + iDHPublicPeer = HBufC8::NewL(PlLth); + iDHPublicPeer->Des().Copy(aKePayload->DHPublic(), PlLth); + + return ETrue; +} + +void CIkev2Negotiation::AppendKEPayloadL(CIkeV2Message& aIkeMsg, TUint16 aDHGroup) +{ + if ( !iDHKeys ) + iDHKeys = CDHKeys::CreateDHKeyL(aDHGroup); + + iDHKeys->XValueL(); // Calculate own DH public value + HBufC8* dHPublic = iDHKeys->GetPubKey(); //save the public key in a buffer to have easy access + User::LeaveIfNull(dHPublic); + CleanupStack::PushL(dHPublic); + + TInt modulusLength = iDHKeys->ModulusLength(); + HBufC8* kePayloadData = HBufC8::NewLC(modulusLength); + TPtr8 kePayloadDataPtr(kePayloadData->Des()); + + __ASSERT_DEBUG(modulusLength == dHPublic->Length(), User::Invariant()); + + kePayloadDataPtr.Append(*dHPublic); + kePayloadDataPtr.SetLength(modulusLength); //adds zero padding, if needed + + aIkeMsg.AppendKePayloadL(aDHGroup, *kePayloadData); + + CleanupStack::PopAndDestroy(kePayloadData); + CleanupStack::PopAndDestroy(dHPublic); +} + + + +TBool CIkev2Negotiation::CheckPayloadsOrder(CIkev2Payloads* aIkeMsg, TUint8 aExchange, TBool aResponse) + { + switch ( aExchange ) + { + case IKE_SA_INIT: + if(!aIkeMsg->iSa || !aIkeMsg->iKe || !aIkeMsg->iNonce) return EFalse; + if(aIkeMsg->iSa->GetNextPayload() != IKEV2_PAYLOAD_KE) return EFalse; + if(aIkeMsg->iKe->GetNextPayload() != IKEV2_PAYLOAD_NONCE) return EFalse; + break; + + case IKE_AUTH: + if(!iEapPlugin) + { + if(!aIkeMsg->iEncr || !aIkeMsg->iAuth || !aIkeMsg->iSa || !aIkeMsg->iTsI || !aIkeMsg->iTsR) + { + DEBUG_LOG(_L("1")); + return EFalse; + } + if(aIkeMsg->iSa->GetNextPayload() != IKEV2_PAYLOAD_TS_I) + { + DEBUG_LOG(_L("2")); + return EFalse; + } + if(aIkeMsg->iTsI->GetNextPayload() != IKEV2_PAYLOAD_TS_R) + { + DEBUG_LOG(_L("3")); + return EFalse; + } + + if(aResponse) + { + if(!aIkeMsg->iIdR) + { + DEBUG_LOG(_L("4")); + return EFalse; + } + if(!aIkeMsg->iCerts || aIkeMsg->iCerts->Count() == 0) + { + if(aIkeMsg->iIdR->GetNextPayload() != IKEV2_PAYLOAD_AUTH) + { + DEBUG_LOG(_L("5")); + return EFalse; + } + } + else + { + if(aIkeMsg->iIdR->GetNextPayload() != IKEV2_PAYLOAD_CERT) + { + DEBUG_LOG(_L("6")); + return EFalse; + } + TInt c = aIkeMsg->iCerts->Count(); + if(aIkeMsg->iCerts->At(c-1)->GetNextPayload() != IKEV2_PAYLOAD_AUTH) + { + DEBUG_LOG(_L("7")); + return EFalse; + } + } + } + else + { + if(!aIkeMsg->iIdI) + { + DEBUG_LOG(_L("8")); + return EFalse; + } + if(aIkeMsg->iCerts && aIkeMsg->iCerts->Count() != 0) + { + if(aIkeMsg->iIdI->GetNextPayload() != IKEV2_PAYLOAD_CERT) + { + DEBUG_LOG(_L("9")); + return EFalse; + } + } + if(aIkeMsg->iCertReqs && aIkeMsg->iCertReqs->Count() != 0) + { + TInt c = aIkeMsg->iCertReqs->Count(); + if(aIkeMsg->iIdR && aIkeMsg->iCertReqs->At(c-1)->GetNextPayload() != IKEV2_PAYLOAD_ID_R) + { + DEBUG_LOG(_L("10")); + return EFalse; + } + if(!aIkeMsg->iIdR && aIkeMsg->iCertReqs->At(c-1)->GetNextPayload() != IKEV2_PAYLOAD_AUTH) + { + DEBUG_LOG(_L("11")); + return EFalse; + } + } + if(aIkeMsg->iIdR && aIkeMsg->iIdR->GetNextPayload() != IKEV2_PAYLOAD_AUTH) + { + DEBUG_LOG(_L("12")); + return EFalse; + } + } + } + break; + + case CREATE_CHILD_SA: + if(!aIkeMsg->iEncr || !aIkeMsg->iSa || !aIkeMsg->iNonce) return EFalse; + if(aIkeMsg->iSa->GetNextPayload() != IKEV2_PAYLOAD_NONCE) return EFalse; + if(aIkeMsg->iKe && aIkeMsg->iNonce->GetNextPayload() != IKEV2_PAYLOAD_KE) return EFalse; + if(aIkeMsg->iTsI) + { + if(aIkeMsg->iKe && aIkeMsg->iKe->GetNextPayload() != IKEV2_PAYLOAD_TS_I) return EFalse; + if(!aIkeMsg->iKe && aIkeMsg->iNonce->GetNextPayload() != IKEV2_PAYLOAD_TS_I) return EFalse; + if(aIkeMsg->iTsI->GetNextPayload() != IKEV2_PAYLOAD_TS_R) return EFalse; + } + break; + + default: + break; + } + DEBUG_LOG(_L("13")); + return ETrue; + } + + +TBool CIkev2Negotiation::Stopped() + { + return iStopped; + } + + +TBool CIkev2Negotiation::ImplicitChildSa() + { + return (iState < KStateIkeSaCompleted); + } + + +HBufC8* CIkev2Negotiation::PeekProposedSa() + { + return iProposedSA; + } + + +HBufC8* CIkev2Negotiation::GetProposedSa() + { + HBufC8* Sa = iProposedSA; + iProposedSA = NULL; + return Sa; + } + + +void CIkev2Negotiation::SetProposedSa(HBufC8* aSaPl) + { + delete iProposedSA; + iProposedSA = aSaPl; + } + + +CIkev2Acquire** CIkev2Negotiation::GetAcquireQue() + { + return &iAcquireFirst; + } + + +CIkev2Expire** CIkev2Negotiation::GetExpireQue() + { + return &iExpireFirst; + } + + +TBool CIkev2Negotiation::RequestsPending() + { + return (iAcquireFirst || iExpireFirst); + } + + +void CIkev2Negotiation::SetNotifyCode(TInt aMsgType) + { + if (iNotifyCode == 0) + iNotifyCode = aMsgType; + } + + +TInt CIkev2Negotiation::GetNotifyCode() + { + return iNotifyCode; + } + + +void CIkev2Negotiation::StoreNotifyData32(TUint32 aData) + { + PUT32(iNotifyData, aData); + iNotifyDataLth = 4; + } + + +void CIkev2Negotiation::StoreNotifyData16(TUint16 aData) + { + PUT16(iNotifyData, aData); + iNotifyDataLth = 2; + } + + +TUint8* CIkev2Negotiation::NotifyData(TInt& aDataLth) + { + aDataLth = iNotifyDataLth; + if ( iNotifyDataLth ) + return iNotifyData; + else return NULL; + } + + +TInetAddr CIkev2Negotiation::GetLocalAddr() const + { + if ( iHdr.iVirtualAddr.IsUnspecified() ) + { + return iHdr.iLocalAddr; + } + else + { + return iHdr.iVirtualAddr; + } + } +