diff -r 000000000000 -r 33413c0669b9 vpnengine/ikev1lib/src/ikev1private.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vpnengine/ikev1lib/src/ikev1private.cpp Thu Dec 17 09:14:51 2009 +0200 @@ -0,0 +1,550 @@ +/* +* 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: +* This module contains the private vendor specific extension of IKE. +* All of the current private extensions are related to Nokia VPN gateway +* and shall be used ONLY when the EPOC IKE is acting as a Nokia VPN remote +* access client. +* The following private extension are implemented: +* +* 1) Internal Address payload usage +* Internal address payload is used to the deliver a secure network +* adderess and secure network DNS address(es) from VPN gateway to a client. +* The Internal address payloads are used in the last two IKE main mode +* messages as follows: +* +* Client (initiator) Gateway (responder) +* .. SA, KE ... ---> +* <--- ..SA, KE ... +* HDR*, INT_ADDR ---> +* <--- HDR*, INT_ADDR +* +* Client sends an INT_ADDR payload with PRI_INTERNAL_ADDRESS attribute +* Attribute value is 0.0.0.0. +* +* Gateway responds with an INT_ADDR payload with PRI_INTERNAL_ADDRESS +* attribute containing client internal address x.y.z.w +* Gateway INT_ADDR payload may also contain attributes PRI_INTERNAL_DNS and +* PRI_INTERNAL_WINS. PRI_INTERNAL_DNS contains a list of DNS IP addresses and +* PRI_INTERNAL_WINS a list of WINS IP addresses. +* +* +* 2) The NAT Traversal probing +* The expanded Vendor-Id payload usage for the NAT Traversal probing. +* The expanded Vendor-Id payloads contains the following information: +* +* Client (initiator) Gateway (responder) +* VID(hash, ip_addr, port) ---> +* <--- VID(hash, detected_ip_addr, +* detected_port) +* +* Client sends a expanded Vendor-Id payload containing the following information: +* hash = Nokia VPN vendor specific hash data (used to recognize peer) +* ip_addr = Client IKE own IP address +* port = Client IKE own port (=500) +* +* Gateway responds with expanded Vendor-Id payload containing the following information: +* hash = Nokia VPN vendor specific hash data (used to recognize peer) +* detected_ip_addr = Client IP address as detected in received IKE message +* IP header (=source IP address) +* detected_port = Client port as detected in received IKE message +* UDP header (=source port) +* +* Both client and gateway do the following examination +* if ( ip_addr != detected_ip_addr ) || ( port != detected_port ) +* then NAT Traversal shall be used IPSEC ESP traffic between +* the client and gateway +* +* Nokia VPN specific NAT Traversal means that IPSEC ESP traffic shall be +* capsulated with UDP header. +* The used UDP port for that purpose is 9872 +* +*/ + +#include "ikev1private.h" +#include "ikev1dialog.h" +#include "ikev1negotiation.h" +#include "ikev1isakmpstream.h" + +#include "ikepolparser.h" + +const TUint8 BASE_VID_DATA[16] = {0x06, 0x3d, 0xf4, 0x13, 0x91, 0xa9, 0x19, 0xa2, + 0x5a, 0x61, 0xa8, 0x7c, 0x45, 0x02, 0x5f, 0xaf}; + +const TUint8 DPD_VID_DATA[16] = {0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, 0xC9, + 0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57, 0x01, 0x00}; + +TInt BuildVendorIdHash(TUint8 *aICOOKIE, TUint8 *aRCOOKIE, TUint8 *hash_data) +{ +/*-------------------------------------------------------------------------------- + * + * Build Vendor Id hash data + * + *------------------------------------------------------------------------*/ +// +// base = MD5("Network Alchemy, Inc., Version 1.0"); /* ASCII-Z end null included) +// +TInt i; + + Mem::Copy(hash_data, &BASE_VID_DATA[0], 16); /* Hash base (MD5) */ + + for ( i = 0; i < (ISAKMP_COOKIE_SIZE * 2); i++ ) { + + if ( i < ISAKMP_COOKIE_SIZE ) + *(hash_data + i) ^= *(aICOOKIE + i); + else *(hash_data + i) ^= *(aRCOOKIE + (i - ISAKMP_COOKIE_SIZE)); + + } + + return 16; +} + + +TInt ConstructVendorId(TBool aNATProbe, + TUint8 *aICOOKIE, + TUint8 *aRCOOKIE, + TInetAddr &aLocalAddr, + TVendorISAKMP *aVendorPayload) +{ +/*------------------------------------------------------------------------ + * + * This method constructs a Vendor ID payload. If aNATProbe is TRUE + * an expanded format Vendor ID is constructed. + * Both Vendor ID formats contains a Nokia VPN vendor specific hash data + * which constructed as follows: + * base = MD5("Network Alchemy, Inc., Version 1.0"); ASCII-Z end null included) + * base = BASE_VID_DATA; + * Then the Vendor ID hash is consructed xor:ing ISAKMP cookies to hash as follows: + * + * for ( i = 0; i < (ISAKMP_COOKIE_SIZE * 2); i++ ) { + * if ( i < ISAKMP_COOKIE_SIZE ) + * base[i] ^= ICOOKIE[i]; + * else base[i] ^= RCOOKIE[i - ISAKMP_COOKIE_SIZE]; + * } + * + * The expanded vendor ID payload looks like so: + * + * General payload header (next payload is "real" next payload) + * General payload header (next payload is "VENDOR_OPTION_NAT_TRAVERSAL") + * option hash + * General payload header (next payload is "VENDOR_OPTION_VERSION") + * option VENDOR_OPTION_NAT_TRAVERSAL + * General payload header (next payload is "NULL") + * option VENDOR_OPTION_VERSION + * + * Expanded vendor id format is format is as follows: + * + * 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 ! RESERVED ! Payload Length = 44 ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! OPTION_NAT_T ! RESERVED ! Hash_lth + 4 = 20 ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Nokia VPN Vendor specific hash ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! OPTION_VERSION! RESERVED ! OPTION_NAT_T_LTH + 4 = 20 ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! sin_lth ! sin_family ! sin_port ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! sin_addr ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * . Zero * 2(?) . + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! 0 ! RESERVED ! OPTION_VERSION + 4 = 8 ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! MAJOR VERSION ! MINOR VERSION ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * + *------------------------------------------------------------------------*/ + TVendorISAKMP *nat_vendor_id; + TVendorISAKMP *ver_vendor_id; + TNATTOption *nat_t_option; + TVersionOption *version_option; + TUint8 *next_payload; + TUint32 vid_lth; + TInetAddr own_addr = aLocalAddr; + + next_payload = (TUint8 *)aVendorPayload; + *next_payload = ISAKMP_PAYLOAD_NONE; /* zeroe next payload field for sure */ + + if ( aNATProbe ) { + /*------------------------------------------------------------ + * + * Build expanded Vendor Id payload + * Build first VENDOR_OPTION_NAT_TRAVERSAL payload + * + *-----------------------------------------------------------*/ + nat_vendor_id = (TVendorISAKMP*)((TUint8*)aVendorPayload + + sizeof(TPayloadISAKMP) + + sizeof(TPayloadISAKMP) + 16); //bypass hash + next_payload = (TUint8 *)nat_vendor_id; + *next_payload = VENDOR_OPTION_VERSION; + nat_vendor_id->SetReserved(0); + nat_vendor_id->SetLength(sizeof(TPayloadISAKMP) + SIN_LTH); + nat_t_option = (TNATTOption*)nat_vendor_id->VIDData(); + nat_t_option->InitOption(); + nat_t_option->SetPort(500); + if ( own_addr.IsV4Mapped() ) + own_addr.ConvertToV4(); + nat_t_option->SetAddress(own_addr.Address()); + /*------------------------------------------------------------ + * + * Build next VENDOR_OPTION_VERSION payload + * Set major version X and minor Y. + * + *-----------------------------------------------------------*/ + ver_vendor_id = (TVendorISAKMP*)((TUint8*)nat_vendor_id + + sizeof(TPayloadISAKMP) + SIN_LTH); //bypass NAT-T + next_payload = (TUint8 *)ver_vendor_id; + *next_payload = ISAKMP_PAYLOAD_NONE; + ver_vendor_id->SetReserved(0); + ver_vendor_id->SetLength(sizeof(TPayloadISAKMP) + VERSION_LTH); + version_option = (TVersionOption*)ver_vendor_id->VIDData(); + version_option->SetVersion(MAJOR_VERSION, MINOR_VERSION); + + /*------------------------------------------------------------ + * + * Build "upper" Vendor Id payload general header + * + *-----------------------------------------------------------*/ + vid_lth = sizeof(TPayloadISAKMP) + /* "outer" Vendor ID payload */ + sizeof(TPayloadISAKMP) + 16 + /* VENDOR_OPTION_HASH */ + sizeof(TPayloadISAKMP) + SIN_LTH + /* VENDOR_OPTION_NAT_TRAVERSAL */ + sizeof(TPayloadISAKMP) + VERSION_LTH;/* VENDOR_OPTION_VERSION */ + aVendorPayload->SetLength((TUint16)vid_lth); + aVendorPayload->SetReserved(0); + + aVendorPayload = (TVendorISAKMP*)((TUint8*)aVendorPayload + sizeof(TPayloadISAKMP)); + next_payload = (TUint8 *)aVendorPayload; + *next_payload = VENDOR_OPTION_NAT_TRAVERSAL; + } + else { + vid_lth = sizeof(TPayloadISAKMP) + 16; + } + /*------------------------------------------------------------ + * + * Store Hash data into Vendor Id payload + * + *-----------------------------------------------------------*/ + aVendorPayload->SetReserved(0); + aVendorPayload->SetLength((TUint16)sizeof(TPayloadISAKMP) + 16); + + BuildVendorIdHash(aICOOKIE, aRCOOKIE, + aVendorPayload->VIDData()); + + return vid_lth; + +} + + +TBool ProcessVendorId(TBool *aFamiliarPeer, + TUint8 *aICOOKIE, + TUint8 *aRCOOKIE, + TInetAddr &aLocalAddr, + TVendorISAKMP *aVendorPayload) +{ +/*------------------------------------------------------------------------- + * + * Process Vendor Id payload received from peer. + * The following actions taken: + * -- Check if a Nokia VPN implementation i peer (recognize hash in Vendor Id) + * -- If Nokia VPN implementation detected process possible + * VENDOR_OPTION_NAT_TRAVERSAL in expanded Vendor Id payload + * + *------------------------------------------------------------------------*/ + TBool nokia_vpn_peer = EFalse; + TBool nat_t_required = EFalse; + TVendorISAKMP *option_payload; + TNATTOption *nat_t_option; + TInt vid_lth; + TInt tmp_lth; + TInt hash_lth; + TUint16 ptype; + TUint16 detected_port; + TUint8 ref_hash[20]; + TInetAddr detected_addr; + TInetAddr reference_addr = aLocalAddr; + + vid_lth = aVendorPayload->GetLength() - sizeof(TPayloadISAKMP); + if ( vid_lth > 15 ) { + /*------------------------------------------------------- + * + * Check if expanded Vendor Id format + * + *-------------------------------------------------------*/ + tmp_lth = vid_lth; + ptype = ISAKMP_PAYLOAD_NONE; + hash_lth = BuildVendorIdHash(aICOOKIE, aRCOOKIE, ref_hash); + option_payload = aVendorPayload; + if ( vid_lth > hash_lth ) { + /*--------------------------------------------------------------------- + * + * An expanded format Vendor Id, bypass "outer" payload general header + * And do sanity check for VENDOR_OPTION_HASH option payload + * + *--------------------------------------------------------------------*/ + option_payload = (TVendorISAKMP*)((TUint8*)option_payload + sizeof(TPayloadISAKMP)); + ptype = option_payload->GetPayload(); + tmp_lth = option_payload->GetLength(); + if ( tmp_lth == (sizeof(TPayloadISAKMP) + 16 ) ) +// && +// ( option_payload->GetReserved() == 0 ) ) { //Must be always 0 + tmp_lth -= sizeof(TPayloadISAKMP); + else tmp_lth = 0; + } + + if ( tmp_lth == hash_lth ) { + /*--------------------------------------------- + * + * Check that Vendor Id hash match + * + *---------------------------------------------*/ + if ( Mem::Compare(option_payload->VIDData(), tmp_lth, ref_hash, hash_lth) == 0 ) { + /*----------------------------------------------------------- + * + * Process other Vendor Id option payload(s) + * In this phase only VENDOR_OPTION_NAT_TRAVERSAL is processed + * other options are ignored + * + *-----------------------------------------------------------*/ + nokia_vpn_peer = ETrue; + tmp_lth += sizeof(TPayloadISAKMP); + option_payload = (TVendorISAKMP*)((TUint8*)option_payload + tmp_lth); + + while ( ptype != ISAKMP_PAYLOAD_NONE ) { + + if ( vid_lth <= tmp_lth ) { + break; + } + hash_lth = option_payload->GetLength(); + tmp_lth += hash_lth; + if ( ( hash_lth < (MIN_ISAKMP_PAYLOAD_SIZE + SIN_LTH) ) ) { +// && +// ( option_payload->GetReserved() != 0 ) ) } //Must be always 0 + break; + } + if ( ptype == VENDOR_OPTION_NAT_TRAVERSAL ) { + if ( reference_addr.IsV4Mapped() ) + reference_addr.ConvertToV4(); + hash_lth -= sizeof(TPayloadISAKMP); /* option data length */ + nat_t_option = (TNATTOption*)((TUint8*)option_payload + sizeof(TPayloadISAKMP)); + detected_port = nat_t_option->GetPort(); + detected_addr.SetAddress(nat_t_option->GetAddress()); + if ( (detected_port != 500) /* Port changed */ + || + !(detected_addr.Match(reference_addr))) { /* address changed */ + nat_t_required = ETrue; + } + break; + } + + ptype = option_payload->GetPayload(); //Next payload + option_payload = (TVendorISAKMP*)((TUint8*)option_payload + hash_lth); + + } + } + } + + } + + if ( aFamiliarPeer ) + *aFamiliarPeer = nokia_vpn_peer; + + return nat_t_required; + +} + +/**------------------------------------------------------------------- + * + * Function BuildDPDVendorId() + * This method builds a Dead Peer Detection (DPD) related Vendor ID + * payload and adds it into the IKE message. The vendor id is + * specified in the draft and its + * content is the following: + * 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! !M!M! + * ! HASHED_VENDOR_ID !J!N! + * ! !R!R! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Hash data is, + * {0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, 0xC9, 0x6B, 0x86, 0x96, + * 0xFC, 0x77, 0x57}, and MJR and MNR + * MJR = 1 and MNR = 0 + * + *--------------------------------------------------------------------*/ +void BuildDPDVendorId(TIkev1IsakmpStream &aMsg) +{ + TInetAddr DummyAddr; + + aMsg.IsakmpVendorId(IETF_NATT_VENDOR_ID, + NULL, NULL, DummyAddr, // These parameters has no relevance with IETF_NATT_VID_DATA + (TUint8*)DPD_VID_DATA, + sizeof(DPD_VID_DATA)); +} + +TBool CheckDPDVendorId(const TVendorISAKMP *aVendorPayload) +{ +/**--------------------------------------------------------------------------------------- + * + * This method checks does the remote end support DPD draft + * + *---------------------------------------------------------------------------------------*/ + TInt vid_lth = aVendorPayload->GetLength() - sizeof(TPayloadISAKMP); + if ( vid_lth == sizeof(DPD_VID_DATA) ) + { + if ( Mem::Compare(aVendorPayload->VIDData(), vid_lth, (TUint8*)DPD_VID_DATA, vid_lth) == 0 ) + return ETrue; // Remote end supports DPD draft + } + return EFalse; +} + +TInt CheckCredentials(CIkeData *aHostData ) +{ +/*------------------------------------------------------------------------- + * + * This function is called by CNegotiation::InitNegotiationL() method + * when the current IKE proposal defines aggresssive mode exchange with + * pre-shared key authentication. + * The following special actions are taken: + * -- If no pre-shared key data is defined, launch a dialog where + * user name and password information is asked from the user. + * -- User name information is store to current CIkeData iFQDN field + * (represent IKE identification) + * -- Password data shall be stored to current CIkeData iPresharedKey field + * + * This functionality is related to Checkpoint gateway. + * To use Aggressive mode exchange and pre-shared key authentication like + * this implement kind of "legacy authentication method" for IKE where + * client (=initiator) authentication is based on user name/password pair. + * User name is sent from initiator (=client) to responder (=Checkpoint GW) + * in the IKE ID payload. However, the password data is NOT transmitted in + * any payload, but it is used as pre-shared key in both ends. + * (= Checkpoint gateway shall use user name data received in IKE ID payload + * as a reference to the correct pre-shared key) + * + *------------------------------------------------------------------------*/ + if ( !aHostData || aHostData->iPresharedKey.iKey.Length() ) + return KErrNone; + + aHostData->iPresharedKey.iFormat = STRING_KEY; + aHostData->iFQDN.SetLength(0); // Override FQDN in host data with user name + + return CIkev1Dialog::GetSyncUNPWDialog(aHostData->iFQDN, aHostData->iPresharedKey.iKey); +} + + +CInternalAddress* ProcessIntNetL(TINTNETISAKMP *aIntNetpayload) +{ +/*------------------------------------------------------------------------- + * + * Process Internal address payload received (sanity check already done) + * Process payload attributes as follows: + * -- Parse PRI_INTERNAL_ADDRESS attribute and store value to aInternalAddr + * -- Parse PRI_INTERNAL_DNS attributes and build list of DNS addresses + * There exists an own attribute for all DNS addresses + * -- Ignore other attributes (=PRI_INTERNAL_WINS) + * + * In this phase only IPv4 Internal addresses are supported by the + * Nokia VPN gateway + * + *------------------------------------------------------------------------*/ + TInt length = (TInt)aIntNetpayload->GetLength(); + if ( STATIC_CAST(TUint, length) < sizeof(TINTNETISAKMP) ) { + return NULL; + } + + length -= sizeof(TINTNETISAKMP); /* Attribute data lengt in payload */ + + TUint32 ipv4_addr; + TBool internal_address = EFalse; + TInetAddr *dns_addr; + CInternalAddress *InternalAddr = new (ELeave)CInternalAddress(1); + CleanupStack::PushL(InternalAddr); + TDataISAKMP *attr = aIntNetpayload->INTNETAttrib(); + + while ( length > 0 ) { + + length = length - attr->Size(); + if ( length < 0 ) { + CleanupStack::PopAndDestroy(); /* delete InternalAddr */ + return NULL; + } + switch ( attr->Type() ) { + + case PRI_INTERNAL_ADDRESS: + /*----------------------------------------------------------- + * Internal address received from gateway. If several + * Internal address attributes detected use the first address + *------------------------------------------------------------*/ + if ( attr->IsBasic() || ( attr->Length() != 4) ) { + CleanupStack::PopAndDestroy(); /* delete InternalAddr */ + return NULL; + } + if ( !internal_address ) { + internal_address = ETrue; + ipv4_addr = GET32(attr->VarValue()); + ipv4_addr = ByteOrder::Swap32(ipv4_addr); //NOT IN NETWORK ORDER !!!! + InternalAddr->iClientIntAddr.SetAddress(ipv4_addr); + } + break; + + case PRI_INTERNAL_DNS: + /*----------------------------------------------------------- + * Internal DNS address received from gateway + *------------------------------------------------------------*/ + if ( attr->IsBasic() || ( attr->Length() != 4 ) ) { + CleanupStack::PopAndDestroy(); /* delete InternalAddr */ + return NULL; + } + ipv4_addr = GET32(attr->VarValue()); + ipv4_addr = ByteOrder::Swap32(ipv4_addr); //NOT IN NETWORK ORDER !!!! + dns_addr = new(ELeave)TInetAddr; + CleanupStack::PushL(dns_addr); + dns_addr->SetAddress(ipv4_addr); + InternalAddr->AppendL(dns_addr); + CleanupStack::Pop(); /* delete dns_addr */ + break; + + default: + /*----------------------------------------------------------- + * Other attributes (WINS address) are ignored + *------------------------------------------------------------*/ + break; + } + + attr = attr->Next(); + } + + if ( !internal_address ) { + /*----------------------------------------------------- + * No client internal address defined. + * Internal address negotiation failed + *----------------------------------------------------*/ + delete InternalAddr; + InternalAddr = NULL; + } + + CleanupStack::Pop(); // Remove InternalAddr from cleanup stack + + return InternalAddr; +} + +