diff -r 000000000000 -r 33413c0669b9 vpnengine/ikev1lib/src/ikev1natdiscovery.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vpnengine/ikev1lib/src/ikev1natdiscovery.cpp Thu Dec 17 09:14:51 2009 +0200 @@ -0,0 +1,355 @@ +/* +* Copyright (c) 2005 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: Negotiation of NAT-Traversal in the IKE +* +*/ + + +#include "ikev1natdiscovery.h" +#include "ikev1crypto.h" +#include "ikev1negotiation.h" +#include "ikev1isakmpstream.h" + +// "03" version hash data +//const TUint8 IETF_NATT_VID_DATA[16] = {0x7d, 0x94, 0x19, 0xa6, 0x53, 0x10, 0xca, 0x6f, +// 0x2c, 0x17, 0x9d, 0x92, 0x15, 0x52, 0x9d, 0x56}; +_LIT8(KIetfNatTHashSeed,"draft-ietf-ipsec-nat-t-ike-03"); +_LIT8(KIetfRfcNatTHashSeed,"RFC 3947"); + +CIkev1NatDiscovery* CIkev1NatDiscovery::NewL(TUint32 aNatFlags) +{ + CIkev1NatDiscovery* NatDiscovery = new (ELeave)CIkev1NatDiscovery(); + + if ( aNatFlags ) + { + NatDiscovery->iSupport = ETrue; // Caller forces support indicator to OK + NatDiscovery->iRfcSupport= ETrue; + } + else + { + NatDiscovery->iSupport = EFalse; + NatDiscovery->iRfcSupport= EFalse; + } + + + // + // Build Vendor string for NAT discovery. This string is used later in + // a ISAKMP phase 1 Vendor Id payload to inform remote host that local + // end supprts NAT Traversal. + // The vendor string is produced as the following hash: + // Vendor Id string = MD5("draft-ietf-ipsec-nat-t-ike-03") + // + MD5HashL(KIetfNatTHashSeed, NatDiscovery->iIetfNattVidHash); // Calculate hash value + MD5HashL(KIetfRfcNatTHashSeed, NatDiscovery->iIetfRfcNattVidHash); // Calculate hash value + + return NatDiscovery; +} + +void CIkev1NatDiscovery::BuildNatVendorId(TIkev1IsakmpStream &aMsg) +{ +/**--------------------------------------------------------------------------------------- + * + * This method builds a NAT traversal related Vendor ID payload and adds it into + * the IKE message. The vendor id content is the following: + * MD5 hash of "draft-ietf-ipsec-nat-t-ike-05" (calculated earlier in NewL()) + * + *---------------------------------------------------------------------------------------*/ + TInetAddr DummyAddr; + + aMsg.IsakmpVendorId(IETF_NATT_VENDOR_ID, + NULL, NULL, DummyAddr, // These parameters has no relevance with IETF_NATT_VID_DATA + (TUint8*)iIetfNattVidHash.Ptr(), iIetfNattVidHash.Length()); + + +} + +void CIkev1NatDiscovery::BuildRfcNatVendorId(TIkev1IsakmpStream &aMsg) +{ +/**--------------------------------------------------------------------------------------- + * + * This method builds a NAT traversal related Vendor ID payload and adds it into + * the IKE message. The vendor id content is the following: + * MD5 hash of "RFC 3947" (calculated earlier in NewL()) + * + *---------------------------------------------------------------------------------------*/ + TInetAddr DummyAddr; + + aMsg.IsakmpVendorId(IETF_RFC_NATT_VENDOR_ID, + NULL, NULL, DummyAddr, // These parameters has no relevance with IETF_NATT_VID_DATA + (TUint8*)iIetfRfcNattVidHash.Ptr(), iIetfRfcNattVidHash.Length()); + + +} + +TBool CIkev1NatDiscovery::CheckNatVendorId(const TVendorISAKMP *aVendorPayload) +{ +/**--------------------------------------------------------------------------------------- + * + * This method checks does the remote end support IETF NAT traversal + * The vendor id content MUST be the following: + * + *---------------------------------------------------------------------------------------*/ + TInt vid_lth = aVendorPayload->GetLength() - sizeof(TPayloadISAKMP); + if ( vid_lth == iIetfNattVidHash.Length() ) { + if ( Mem::Compare(aVendorPayload->VIDData(), vid_lth, iIetfNattVidHash.Ptr(), vid_lth) == 0 ) { + iSupport = ETrue; // Remote end supports IETF NAT traversal + } + } + + return iSupport; + +} + +TBool CIkev1NatDiscovery::CheckRfcNatVendorId(const TVendorISAKMP *aVendorPayload) +{ +/**--------------------------------------------------------------------------------------- + * + * This method checks does the remote end support IETF NAT traversal RFC 3947 + * The vendor id content MUST be the following: + * + *---------------------------------------------------------------------------------------*/ + TInt vid_lth = aVendorPayload->GetLength() - sizeof(TPayloadISAKMP); + if ( vid_lth == iIetfRfcNattVidHash.Length() ) { + if ( Mem::Compare(aVendorPayload->VIDData(), vid_lth, iIetfRfcNattVidHash.Ptr(), vid_lth) == 0 ) { + //iSupport = ETrue; // Remote end supports IETF NAT traversal according to IETF draft 03 + iRfcSupport= ETrue; // Remote end supports IETF NAT traversal according to RFC 3947 + } + } + return iRfcSupport; +} + + +void CIkev1NatDiscovery::BuildDiscoveryPayloadsL(TIkev1IsakmpStream &aMsg, TUint16 aHashType, + TUint8 *aICOOKIE, TUint8 *aRCOOKIE, + TInetAddr &aLocalAddr, TInetAddr &aRemoteAddr) +{ +/**--------------------------------------------------------------------------------------- + * + * This builds NAT Discovery payloads for negotiation. + * from draft-ietf-ipsec-nat-t-ike-03; + * " + * The purpose of the NAT-D payload is twofold, It not only detects the + * presence of NAT between two IKE peers, it also detects where the NAT is. + * The location of the NAT device is important in that the keepalives need + * to initiate from the peer "behind" the NAT. + * + * To detect the NAT between the two hosts, we need to detect if the IP + * address or the port changes along the path. This is done by sending the + * hashes of IP address and port of both source and destination addresses + * from each end to another. When both ends calculate those hashes and get + * same result they know there is no NAT between. If the hashes do not + * match, somebody translated the address or port between, meaning we need + * to do NAT-Traversal to get IPsec packet through. + * + * If the sender of the packet does not know his own IP address (in case of + * multiple interfaces, and implementation don't know which is used to + * route the packet out), he can include multiple local hashes to the + * packet (as separate NAT-D payloads). In this case the NAT is detected if + * and only if none of the hashes match. + * + * The hashes are sent as a series of NAT-D (NAT discovery) payloads. Each + * payload contains one hash, so in case of multiple hashes, multiple NAT-D + * payloads are sent. In normal case there is only two NAT-D payloads. + * + * The format of the NAT-D packet is + * + * 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 + * +---------------+---------------+---------------+---------------+ + * | Next Payload | RESERVED | Payload length | + * +---------------+---------------+---------------+---------------+ + * ~ HASH of the address and port ~ + * +---------------+---------------+---------------+---------------+ + * + * The payload type for the NAT discovery payload is 130 (XXX CHANGE). + * + * The HASH is calculated as follows: + * + * HASH = HASH(CKY-I | CKY-R | IP | Port) + * + * using the negotiated HASH algorithm. All data inside the HASH is in the + * network byte-order. The IP is 4 octets for the IPv4 address and 16 + * octets for the IPv6 address. The port number is encoded as 2 octet + * number in network byte-order. The first NAT-D payload contains the + * remote ends IP address and port (i.e the destination address of the UDP + * packet). The rest of the NAT-D payloads contain possible local end IP + * addresses and ports (i.e all possible source addresses of the UDP packet)." + * + *---------------------------------------------------------------------------------------*/ + if ( iSupport || iRfcSupport) { + CalculateAddrPortHashL(aHashType, aICOOKIE, aRCOOKIE, aLocalAddr, aRemoteAddr); + + aMsg.IsakmpNatD(iRfcSupport, iRemoteAddrPortHash); // NAT-D payload with HASH(CKY-I | CKY-R | Remote_IP | Remote_Port) + aMsg.IsakmpNatD(iRfcSupport, iLocalAddrPortHash); // NAT-D payload with HASH(CKY-I | CKY-R | Local_IP | Local_Port) + } +} + +TUint32 CIkev1NatDiscovery::CheckDiscoveryPayloadsL(const CArrayFixFlat *aNatDPayloadArray, + TUint16 aHashType, TUint8 *aICOOKIE, TUint8 *aRCOOKIE, + TInetAddr &aLocalAddr, TInetAddr &aRemoteAddr) +{ +/**--------------------------------------------------------------------------------------- + * + * This check NAT Discovery payloads received from remote end. + * from draft-ietf-ipsec-nat-t-ike-03; + * " + * If there is no NAT between then the first NAT-D payload should match one + * of the local NAT-D packet (i.e the local NAT-D payloads this host is + * sending out), and the one of the other NAT-D payloads must match the + * remote ends IP address and port. If the first check fails (i.e first + * NAT-D payload does not match any of the local IP addresses and ports), + * then it means that there is dynamic NAT between, and this end should + * start sending keepalives as defined in the . + * + *---------------------------------------------------------------------------------------*/ + TUint32 NatFlags = 0; + + if ( iSupport || iRfcSupport ) { + + TInt count = aNatDPayloadArray->Count(); + if ( count > 1 ) { + // + // Check that the first hash corresponds current local address port pair + // + CalculateAddrPortHashL(aHashType, aICOOKIE, aRCOOKIE, aLocalAddr, aRemoteAddr); + + const TNATDISAKMP *NatDPayload = aNatDPayloadArray->At(0); + if ( !CompareHashData(NatDPayload->HashData(), NatDPayload->HashLth(), iLocalAddrPortHash) ) { + NatFlags |= LOCAL_END_NAT; //Local end is behind a NAT device + } + + // + // Check the rest of NAT discovery payloads. One of them must correspond remote hash data + // calculated in local end + // + NatFlags |= REMOTE_END_NAT; // Remote end is behind a NAT device (as default) + + for ( TInt i = 1; (i < count); i++ ) + { + NatDPayload = aNatDPayloadArray->At(i); + if ( CompareHashData(NatDPayload->HashData(), NatDPayload->HashLth(), iRemoteAddrPortHash) ) { + NatFlags &= ~REMOTE_END_NAT; //Remote end is NOT behind a NAT device + break; + } + + } + } + + } + + return NatFlags; + +} + + +void CIkev1NatDiscovery::BuildNatOaPayload(TIkev1IsakmpStream &aMsg, TInetAddr &aLocalAddr, CProposal_IIList *aProposalList) +{ +(void)aMsg; (void)aLocalAddr; (void)aProposalList; + return; +} + +TBool CIkev1NatDiscovery::GetPeerOriginalAddress(const TNATOaISAKMP *aNatOaPayload, TInetAddr& aRemoteOrigAddr, CProposal_IIList *aProposalList) +{ +(void)aNatOaPayload; (void)aRemoteOrigAddr; (void)aProposalList; + aRemoteOrigAddr.Init(KAFUnspec); // Set address value undefined + return EFalse; +} + + + +void CIkev1NatDiscovery::CalculateAddrPortHashL(TUint16 aHashType, + TUint8 *aICOOKIE, TUint8 *aRCOOKIE, + TInetAddr &aLocalAddr, TInetAddr &aRemoteAddr) +{ + if ( iHashExists ) { + return; //Hash has been already calculated + } +/**--------------------------------------------------------------------------------------- + * + * Calculate HASH = HASH(CKY-I | CKY-R | IP | Port) both for local- and remote IP address/port + * + *---------------------------------------------------------------------------------------*/ + TBuf8<64> in_data; + const TUint8 *pnum; + TUint32 ipv4addr; + TUint16 port; + + in_data.Append(aICOOKIE, ISAKMP_COOKIE_SIZE); + in_data.Append(aRCOOKIE, ISAKMP_COOKIE_SIZE); + + TInetAddr HashAddr = aLocalAddr; + HashAddr.SetPort(500); //Set local port to default IKE port value + TInt i = 0; + + while ( i < 2 ) { + + if ( HashAddr.Family() == KAfInet ) { + ipv4addr = ByteOrder::Swap32(HashAddr.Address());//Put in network order + pnum = (TUint8*)&ipv4addr; + in_data.Append(pnum, sizeof(TUint32)); + } + else { + if ( HashAddr.IsV4Mapped() ) { + HashAddr.ConvertToV4(); // IPv4 format + ipv4addr = ByteOrder::Swap32(HashAddr.Address());//Put in network order + pnum = (TUint8*)&ipv4addr; + in_data.Append(pnum, sizeof(TUint32)); + } + else { + pnum = &HashAddr.Ip6Address().u.iAddr8[0]; //Address in a bytestream + in_data.Append(pnum, 16); + } + } + + port = ByteOrder::Swap16(HashAddr.Port());//Put in network order + pnum = (TUint8*)&port; + in_data.Append(pnum, sizeof(TUint16)); + + if ( i ) { + if ( aHashType == HASH_MD5 ) + MD5HashL(in_data, iRemoteAddrPortHash); // Calculate hash value + else SHA1HashL(in_data, iRemoteAddrPortHash); + } + else { + if ( aHashType == HASH_MD5 ) + MD5HashL(in_data, iLocalAddrPortHash); // Calculate hash value + else SHA1HashL(in_data, iLocalAddrPortHash); + } + in_data.SetLength(ISAKMP_COOKIE_SIZE + ISAKMP_COOKIE_SIZE); // Reset lenght to Icookie + Rcookie + HashAddr = aRemoteAddr; // Process remote address next + + i ++; + } + + iHashExists = ETrue; + +} + +TBool CIkev1NatDiscovery::CompareHashData(TUint8 *aHashData, TUint32 aHashLth, TDesC8 &aReferenceHash) +{ +/**--------------------------------------------------------------------------------------- + * + * Compare current hash data to the reference hash data provided + * + *---------------------------------------------------------------------------------------*/ + TBool result = EFalse; + + if ( (TInt)aHashLth == aReferenceHash.Length() ) { + if ( Mem::Compare(aHashData, aHashLth, aReferenceHash.Ptr(), aHashLth) == 0 ) { + result = ETrue; + } + } + + return result; +} +