--- /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 <draft-ietf-ipsec-nat-t-ike-03>
+ * 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<const TNATDISAKMP *> *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 <draft-ietf-ipsec-udp-encaps-03.txt>.
+ *
+ *---------------------------------------------------------------------------------------*/
+ 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;
+}
+