diff -r 000000000000 -r 33413c0669b9 vpnengine/ikev2lib/src/ikemsgrec.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vpnengine/ikev2lib/src/ikemsgrec.cpp Thu Dec 17 09:14:51 2009 +0200 @@ -0,0 +1,452 @@ +/* +* Copyright (c) 2003-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: Received IKE message handling rules. +* +*/ + +#include "ikev2SAdata.h" +#include "ikemsgrec.h" +#include "ikecrypto.h" +// +// CIkev2Payloads +// + +CIkev2Payloads::CIkev2Payloads(const TIkev2SAData& aIkeV2SaData) +:iIkeV2SaData(aIkeV2SaData) +{ +} + +CIkev2Payloads::~CIkev2Payloads() +{ + delete iProps; + delete iTrans; + delete iCerts; + delete iCertReqs; + delete iNotifs; + delete iDeletes; + delete iVids; + delete iGenPlds; +} + +CIkev2Payloads* CIkev2Payloads::NewL(const ThdrISAKMP &aHdr, const TIkev2SAData& aIkeV2SaData) +{ + CIkev2Payloads* Payloads = new (ELeave) CIkev2Payloads(aIkeV2SaData); + CleanupStack::PushL(Payloads); + + Payloads->ConstructL(); + + Payloads->iIkeMsg = (ThdrISAKMP*)&aHdr; // Save pointer to IKE message data + TInt Lth = aHdr.GetLength() - ThdrISAKMP::Size(); + Payloads->ParsePayloadsL(TPayloadIkev2::Cast(aHdr.Next()), Lth, aHdr.GetPayload(), IKEV2_PAYLOAD_NONE); + + CleanupStack::Pop(Payloads); + + return Payloads; +} + +CIkev2Payloads* CIkev2Payloads::NewL(TPayloadIkev2* aPayload, TUint8 aPayloadType, TIkev2SAData& aIkeV2SaData) +{ + User::LeaveIfNull((TAny*)aPayload); + CIkev2Payloads* Payloads = new (ELeave) CIkev2Payloads(aIkeV2SaData); + CleanupStack::PushL(Payloads); + + Payloads->ConstructL(); + + Payloads->ParsePayloadsL(aPayload, aPayload->GetLength(), aPayloadType, aPayloadType); + + CleanupStack::Pop(Payloads); + + return Payloads; +} + +TBool CIkev2Payloads::ParsePayloadL(TPayloadIkev2* aPayload, TUint16 aPlType) +{ + User::LeaveIfNull((TAny*)aPayload); + + if ( aPlType == IKEV2_PAYLOAD_SA ) + iProps->Reset(); + else if ( aPlType == IKEV2_PAYLOAD_PROP ) + iTrans->Reset(); + if ( ParsePayloadsL(aPayload, aPayload->GetLength(), aPlType, aPlType) != 0 ) + return EFalse; + else return ETrue; +} + +void CIkev2Payloads::ConstructL() +{ + iProps = new (ELeave) CArrayFixFlat(4); + iTrans = new (ELeave) CArrayFixFlat(4); + iCerts = new (ELeave) CArrayFixFlat(4); + iCertReqs = new (ELeave) CArrayFixFlat(2); + iNotifs = new (ELeave) CArrayFixFlat(2); + iDeletes = new (ELeave) CArrayFixFlat(2); + iVids = new (ELeave) CArrayFixFlat(2); + iGenPlds = new (ELeave) CArrayFixFlat(2); +} + + +TInt CIkev2Payloads::ParsePayloadsL(TPayloadIkev2* aPayload, TInt aLength, TUint16 aPlType, TUint16 aRefPlType ) +{ + ASSERT(aPayload); + TBool Critical; + TInt PlLth; + TInt RefLth; + + while ( aPlType != IKEV2_PAYLOAD_NONE ) + { + PlLth = aPayload->GetLength(); + RefLth = TPayloadIkev2::Size(); + + if ( ( aLength < TPayloadIkev2::Size() ) || (aLength < PlLth) ) + { + if ( aLength && ( aRefPlType != IKEV2_PAYLOAD_SA )) + SetStatus(INVALID_SYNTAX); // Payload length mismatch !! + return aLength; + } + + if ( (aRefPlType != IKEV2_PAYLOAD_NONE) && (aRefPlType != aPlType) ) + { + SetStatus(INVALID_SYNTAX); // Illegal payload type !! + return aLength; + } + + Critical = aPayload->GetCritical(); + + switch ( aPlType ) + { + case IKEV2_PAYLOAD_PROP: + iProps->AppendL(TProposalIkev2::Cast(aPayload)); + // + // Parse Transform payloads within a Proposal payload + // (recursively) + // + ParsePayloadsL(TPayloadIkev2::Cast(TProposalIkev2::Cast(aPayload)->TransformPl()), + (PlLth - TProposalIkev2::Cast(aPayload)->PropHdrLth()), + IKEV2_PAYLOAD_TRANS, IKEV2_PAYLOAD_TRANS); + if ( Status() ) + return aLength; + break; + + case IKEV2_PAYLOAD_TRANS: + RefLth = TTransformIkev2::Cast(aPayload)->Size(); + iTrans->AppendL(TTransformIkev2::Cast(aPayload)); + break; + + case IKEV2_PAYLOAD_SA: //also includes proposal and transform + if ( !iSa ) // Only one SA payload (The first) + { + iSa = aPayload; + // + // Parse Proposal payloads within a SA payload + // (recursively) + // + ParsePayloadsL(TPayloadIkev2::Cast(aPayload->PayloadData()), aPayload->PlDataLen(), + IKEV2_PAYLOAD_PROP, IKEV2_PAYLOAD_PROP); + if ( Status() ) + return aLength; + } + break; + + case IKEV2_PAYLOAD_KE: + RefLth = TKEPayloadIkev2::Size(); + if ( !iKe ) // Only one KE payload (The first) + { + iKe = TKEPayloadIkev2::Cast(aPayload); + } + break; + + case IKEV2_PAYLOAD_ID_I: + if ( !Encrypted() ) + { + SetStatus(INVALID_SYNTAX); // ID payload MUST be encrypted + return aLength; + } + RefLth = TIDPayloadIkev2::Size(); + if ( !iIdI ) // Only one Initiator ID payload (The first) + { + iIdI = TIDPayloadIkev2::Cast(aPayload); + } + break; + + case IKEV2_PAYLOAD_ID_R: + if ( !Encrypted() ) + { + SetStatus(INVALID_SYNTAX); // ID payload MUST be encrypted + return aLength; + } + RefLth = TIDPayloadIkev2::Size(); + if ( !iIdR ) // Only one Responder ID payload (The first) + { + iIdR = TIDPayloadIkev2::Cast(aPayload); + } + break; + + case IKEV2_PAYLOAD_CERT: + RefLth = TCertPayloadIkev2::Size(); + iCerts->AppendL(TCertPayloadIkev2::Cast(aPayload)); + break; + + case IKEV2_PAYLOAD_CR: + RefLth = TCReqPayloadIkev2::Size(); + iCertReqs->AppendL(TCReqPayloadIkev2::Cast(aPayload)); + break; + + case IKEV2_PAYLOAD_AUTH: + if ( !Encrypted() ) + { + SetStatus(INVALID_SYNTAX); // Auth payload MUST be encrypted + return aLength; + } + RefLth = TAuthPayloadIkev2::Size(); + if ( !iAuth ) // Only one Authentication payload (The first) + { + iAuth = TAuthPayloadIkev2::Cast(aPayload); + } + break; + + case IKEV2_PAYLOAD_NONCE: + if ( !iNonce ) + { // Only one SA payload (The first) + iNonce = aPayload; + } + break; + + case IKEV2_PAYLOAD_NOTIF: + RefLth = TNotifPayloadIkev2::Size(); + iNotifs->AppendL(TNotifPayloadIkev2::Cast(aPayload)); + break; + + case IKEV2_PAYLOAD_DELETE: + RefLth = TDeletePlIkev2::Size(); + iDeletes->AppendL(TDeletePlIkev2::Cast(aPayload)); + break; + + case IKEV2_PAYLOAD_VID: + iVids->AppendL(aPayload); + break; + + case IKEV2_PAYLOAD_TS_I: + if ( !Encrypted() ) + { + SetStatus(INVALID_SYNTAX); // ID payload MUST be encrypted + return aLength; + } + RefLth = TTSPayloadIkev2::Size(); + if ( !iTsI ) // Only one Initiator ID payload (The first) + { + iTsI = TTSPayloadIkev2::Cast(aPayload); + } + break; + + case IKEV2_PAYLOAD_TS_R: + if ( !Encrypted() ) + { + SetStatus(INVALID_SYNTAX); // ID payload MUST be encrypted + return aLength; + } + RefLth = TTSPayloadIkev2::Size(); + if ( !iTsR ) // Only one Initiator ID payload (The first) + { + iTsR = TTSPayloadIkev2::Cast(aPayload); + } + break; + + case IKEV2_PAYLOAD_ENCR: + if ( !iEncr ) // Only one Initiator ID payload (The first) + { + DecryptEncrPayloadL(aPayload); + iEncr = aPayload; + } + else SetStatus(INVALID_SYNTAX); // Only ONE encrypted payload per message + if ( Status() ) + return aLength; + break; + + case IKEV2_PAYLOAD_CONFIG: + if ( !Encrypted() ) + { + SetStatus(INVALID_SYNTAX); // ID payload MUST be encrypted + return aLength; + } + RefLth = TCPPayloadIkev2::Size(); + if ( !iCp ) // Only one Config payload (The first) + { + iCp = TCPPayloadIkev2::Cast(aPayload); + } + break; + + case IKEV2_PAYLOAD_EAP: + if ( !Encrypted() ) + { + SetStatus(INVALID_SYNTAX); // ID payload MUST be encrypted + return aLength; + } + if ( !iEap ) // Only one Config payload (The first) + { + iEap = aPayload; + } + break; + + // + // Unknown payload detected. If Critical bit is not set + // + // + default: + if ( Critical ) + { + SetStatus(UNSUPPORTED_CRITICAL_PAYLOAD); + return aLength; + } + else iGenPlds->AppendL(aPayload); + break; + + } + + if ( PlLth < RefLth ) + { + SetStatus(INVALID_SYNTAX); // Length mismatch + return aLength; + } + + aLength -= PlLth; + aPlType = aPayload->GetNextPayload(); + aPayload = aPayload->Next(); + } + + if ( aLength ) + SetStatus(INVALID_SYNTAX); // Length mismatch + + return aLength; +} + +void CIkev2Payloads::DecryptEncrPayloadL(TPayloadIkev2* aPayload) +{ + ASSERT(aPayload); + // + // Process Encrypted Payload + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Initialization Vector ! + // ! (length is block size for encryption algorithm) ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Encrypted IKE Payloads ! + // + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! Padding (0-255 octets) ! + // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + // ! ! Pad Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ~ Integrity Checksum Data ~ + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // Save integrity checksum from encrypted payload tail the + // integrity checksum length is defined by used integrity algorithm + // + TInt CheksumLth = iIkeV2SaData.iIntChkSumLth; + TInt CbcLth = iIkeV2SaData.iCipherBlkLth; + TInt EmbeddedLth = aPayload->GetLength(); + if ( EmbeddedLth < (CheksumLth + CbcLth) ) + { + SetStatus(INVALID_SYNTAX); + return; + } + TUint8* MsgChecksum = (TUint8*)aPayload + EmbeddedLth - CheksumLth; + TBuf8 CheckSum; + // + // Decrypt encryption payload content and payload tail the + // integrity checksum length is defined by used integrity algorithm + // + + TInt EntireLth = iIkeMsg->GetLength() - iIkeV2SaData.iIntChkSumLth; + TInt EncryptedDataLength = aPayload->GetLength() - (iIkeV2SaData.iIntChkSumLth + iIkeV2SaData.iCipherBlkLth + TPayloadIkev2::Size()); + TUint8* IvPtr = aPayload->PayloadData(); + TUint8* EncryptedData = IvPtr + iIkeV2SaData.iCipherBlkLth; + + TPtrC8 entireDataPtr((TUint8*)iIkeMsg, EntireLth); + if ( iIkeV2SaData.iInitiator ) + { + IkeCrypto::IntegHMACL(entireDataPtr, CheckSum, iIkeV2SaData.iSK_ar, iIkeV2SaData.iIntegAlg); + IkeCrypto::DecryptL(EncryptedData, EncryptedData, EncryptedDataLength, IvPtr, iIkeV2SaData.iSK_er, iIkeV2SaData.iEncrAlg); + } + else + { + IkeCrypto::IntegHMACL(entireDataPtr, CheckSum, iIkeV2SaData.iSK_ai, iIkeV2SaData.iIntegAlg); + IkeCrypto::DecryptL(EncryptedData, EncryptedData, EncryptedDataLength, IvPtr, iIkeV2SaData.iSK_ei, iIkeV2SaData.iEncrAlg); + } + + + // + // Check that integrity cheksum is correct + // + if ( Mem::Compare(MsgChecksum, CheksumLth ,CheckSum.Ptr(), CheksumLth) != 0 ) + { + SetStatus(INVALID_SYNTAX); // Length mismatch + return; + } + // + // Process embedded payloads inside the encrypted payload + // - Bypass IV in the begin of encrypted payload data + // - Assure that padded "embedded" payloads have length which + // equals with multiple of CBC block length + // - Ignore padding bytes from that length + // + EmbeddedLth -= (CheksumLth + CbcLth + TPayloadIkev2::Size()); + if ( EmbeddedLth % CbcLth ) + { + SetStatus(INVALID_SYNTAX); + return; + } + MsgChecksum --; // Move pointer to padding length + if ( EmbeddedLth < (TInt)*MsgChecksum ) + { + SetStatus(INVALID_SYNTAX); + return; + } + EmbeddedLth -= (TInt)*MsgChecksum; + EmbeddedLth --; // Pad Length itself + TPayloadIkev2* EmbeddedPl = TPayloadIkev2::Cast(aPayload->PayloadData() + CbcLth); + + iEncrypted = ETrue; // Set encrypted indicator + + ParsePayloadsL(EmbeddedPl, EmbeddedLth, + aPayload->GetNextPayload(), IKEV2_PAYLOAD_NONE); + +} + +ThdrISAKMP* CIkev2Payloads::GetIkeMsg() +{ + return iIkeMsg; +} + + +TInt CIkev2Payloads::Status() +{ + return iStatus; +} + + +void CIkev2Payloads::SetStatus(TInt aStatus) +{ + if ( iStatus == 0) iStatus = aStatus; +} + + +TBool CIkev2Payloads::Encrypted() +{ + return iEncrypted; +} + +