vpnengine/ikev1lib/src/ikev1natdiscovery.cpp
changeset 0 33413c0669b9
equal deleted inserted replaced
-1:000000000000 0:33413c0669b9
       
     1 /*
       
     2 * Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Negotiation of NAT-Traversal in the IKE
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include "ikev1natdiscovery.h"
       
    20 #include "ikev1crypto.h"
       
    21 #include "ikev1negotiation.h"
       
    22 #include "ikev1isakmpstream.h"
       
    23 
       
    24 //  "03" version hash data
       
    25 //const TUint8  IETF_NATT_VID_DATA[16] = {0x7d, 0x94, 0x19, 0xa6, 0x53, 0x10, 0xca, 0x6f,
       
    26 //                                        0x2c, 0x17, 0x9d, 0x92, 0x15, 0x52, 0x9d, 0x56};
       
    27 _LIT8(KIetfNatTHashSeed,"draft-ietf-ipsec-nat-t-ike-03");
       
    28 _LIT8(KIetfRfcNatTHashSeed,"RFC 3947");
       
    29 
       
    30 CIkev1NatDiscovery* CIkev1NatDiscovery::NewL(TUint32 aNatFlags)
       
    31 {
       
    32     CIkev1NatDiscovery* NatDiscovery = new (ELeave)CIkev1NatDiscovery();
       
    33 
       
    34     if ( aNatFlags )
       
    35     {
       
    36     	NatDiscovery->iSupport = ETrue;  // Caller forces support indicator to OK
       
    37     	NatDiscovery->iRfcSupport= ETrue;	
       
    38     }
       
    39     else 
       
    40     {
       
    41     	NatDiscovery->iSupport = EFalse;
       
    42     	NatDiscovery->iRfcSupport= EFalse;
       
    43     }
       
    44    
       
    45 
       
    46     //
       
    47     // Build Vendor string for NAT discovery. This string is used later in
       
    48     // a ISAKMP phase 1 Vendor Id payload to inform remote host that local
       
    49     // end supprts NAT Traversal.
       
    50     // The vendor string is produced as the following hash:
       
    51     // Vendor Id string = MD5("draft-ietf-ipsec-nat-t-ike-03")
       
    52     //
       
    53     MD5HashL(KIetfNatTHashSeed, NatDiscovery->iIetfNattVidHash);  // Calculate hash value        
       
    54     MD5HashL(KIetfRfcNatTHashSeed, NatDiscovery->iIetfRfcNattVidHash);  // Calculate hash value 
       
    55     
       
    56     return NatDiscovery;
       
    57 }
       
    58 
       
    59 void CIkev1NatDiscovery::BuildNatVendorId(TIkev1IsakmpStream &aMsg)
       
    60 {
       
    61 /**---------------------------------------------------------------------------------------
       
    62  *
       
    63  *  This method builds a NAT traversal related Vendor ID payload and adds it into 
       
    64  *  the IKE message. The vendor id content is the following:
       
    65  *  MD5 hash of "draft-ietf-ipsec-nat-t-ike-05" (calculated earlier in NewL())
       
    66  *  
       
    67  *---------------------------------------------------------------------------------------*/
       
    68     TInetAddr DummyAddr;
       
    69 
       
    70     aMsg.IsakmpVendorId(IETF_NATT_VENDOR_ID,
       
    71                         NULL, NULL, DummyAddr, // These parameters has no relevance with IETF_NATT_VID_DATA
       
    72                         (TUint8*)iIetfNattVidHash.Ptr(), iIetfNattVidHash.Length());
       
    73 
       
    74      
       
    75 }
       
    76 
       
    77 void CIkev1NatDiscovery::BuildRfcNatVendorId(TIkev1IsakmpStream &aMsg)
       
    78 {
       
    79 /**---------------------------------------------------------------------------------------
       
    80  *
       
    81  *  This method builds a NAT traversal related Vendor ID payload and adds it into 
       
    82  *  the IKE message. The vendor id content is the following:
       
    83  *  MD5 hash of "RFC 3947" (calculated earlier in NewL())
       
    84  *  
       
    85  *---------------------------------------------------------------------------------------*/
       
    86     TInetAddr DummyAddr;
       
    87 
       
    88     aMsg.IsakmpVendorId(IETF_RFC_NATT_VENDOR_ID,
       
    89                         NULL, NULL, DummyAddr, // These parameters has no relevance with IETF_NATT_VID_DATA
       
    90                         (TUint8*)iIetfRfcNattVidHash.Ptr(), iIetfRfcNattVidHash.Length());
       
    91 
       
    92      
       
    93 }
       
    94 
       
    95 TBool CIkev1NatDiscovery::CheckNatVendorId(const TVendorISAKMP *aVendorPayload)
       
    96 {
       
    97 /**---------------------------------------------------------------------------------------
       
    98  *
       
    99  *  This method checks does the remote end support IETF NAT traversal <draft-ietf-ipsec-nat-t-ike-03>
       
   100  *  The vendor id content MUST be the following:
       
   101  *  
       
   102  *---------------------------------------------------------------------------------------*/
       
   103     TInt vid_lth = aVendorPayload->GetLength() - sizeof(TPayloadISAKMP);
       
   104     if ( vid_lth == iIetfNattVidHash.Length() ) {
       
   105        if ( Mem::Compare(aVendorPayload->VIDData(), vid_lth, iIetfNattVidHash.Ptr(), vid_lth) == 0 ) {
       
   106           iSupport = ETrue;  // Remote end supports IETF NAT traversal
       
   107        }    
       
   108     }
       
   109 
       
   110     return iSupport;
       
   111 
       
   112 }
       
   113 
       
   114 TBool CIkev1NatDiscovery::CheckRfcNatVendorId(const TVendorISAKMP *aVendorPayload)
       
   115 {
       
   116 /**---------------------------------------------------------------------------------------
       
   117  *
       
   118  *  This method checks does the remote end support IETF NAT traversal RFC 3947
       
   119  *  The vendor id content MUST be the following:
       
   120  *  
       
   121  *---------------------------------------------------------------------------------------*/
       
   122     TInt vid_lth = aVendorPayload->GetLength() - sizeof(TPayloadISAKMP);
       
   123     if ( vid_lth == iIetfRfcNattVidHash.Length() ) {
       
   124        if ( Mem::Compare(aVendorPayload->VIDData(), vid_lth, iIetfRfcNattVidHash.Ptr(), vid_lth) == 0 ) {
       
   125           //iSupport = ETrue;  // Remote end supports IETF NAT traversal according to IETF draft 03
       
   126           iRfcSupport= ETrue; // Remote end supports IETF NAT traversal according to RFC 3947
       
   127        }    
       
   128     }
       
   129     return iRfcSupport;
       
   130 }
       
   131 
       
   132    
       
   133 void CIkev1NatDiscovery::BuildDiscoveryPayloadsL(TIkev1IsakmpStream &aMsg, TUint16 aHashType,
       
   134                                                  TUint8 *aICOOKIE, TUint8 *aRCOOKIE,
       
   135                                                  TInetAddr &aLocalAddr, TInetAddr &aRemoteAddr)
       
   136 {
       
   137 /**---------------------------------------------------------------------------------------
       
   138  *
       
   139  *  This builds NAT Discovery payloads for negotiation.
       
   140  *  from draft-ietf-ipsec-nat-t-ike-03;
       
   141  *  "
       
   142  *  The purpose of the NAT-D payload is twofold, It not only detects the
       
   143  *  presence of NAT between two IKE peers, it also detects where the NAT is.
       
   144  *  The location of the NAT device is important in that the keepalives need
       
   145  *  to initiate from the peer "behind" the NAT.
       
   146  *
       
   147  *  To detect the NAT between the two hosts, we need to detect if the IP
       
   148  *  address or the port changes along the path. This is done by sending the
       
   149  *  hashes of IP address and port of both source and destination addresses
       
   150  *  from each end to another. When both ends calculate those hashes and get
       
   151  *  same result they know there is no NAT between. If the hashes do not
       
   152  *  match, somebody translated the address or port between, meaning we need
       
   153  *  to do NAT-Traversal to get IPsec packet through.
       
   154  *
       
   155  *  If the sender of the packet does not know his own IP address (in case of
       
   156  *  multiple interfaces, and implementation don't know which is used to
       
   157  *  route the packet out), he can include multiple local hashes to the
       
   158  *  packet (as separate NAT-D payloads). In this case the NAT is detected if
       
   159  *  and only if none of the hashes match.
       
   160  *
       
   161  *  The hashes are sent as a series of NAT-D (NAT discovery) payloads.  Each
       
   162  *  payload contains one hash, so in case of multiple hashes, multiple NAT-D
       
   163  *  payloads are sent. In normal case there is only two NAT-D payloads.
       
   164  * 
       
   165  *   The format of the NAT-D packet is
       
   166  *
       
   167  *    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
       
   168  *   +---------------+---------------+---------------+---------------+
       
   169  *   | Next Payload  |    RESERVED   |        Payload length         |
       
   170  *   +---------------+---------------+---------------+---------------+
       
   171  *   ~               HASH of the address and port                    ~
       
   172  *   +---------------+---------------+---------------+---------------+
       
   173  *
       
   174  *  The payload type for the NAT discovery payload is 130 (XXX CHANGE).
       
   175  *
       
   176  *  The HASH is calculated as follows:
       
   177  *
       
   178  *   HASH = HASH(CKY-I | CKY-R | IP | Port)
       
   179  *
       
   180  *  using the negotiated HASH algorithm. All data inside the HASH is in the
       
   181  *  network byte-order. The IP is 4 octets for the IPv4 address and 16
       
   182  *  octets for the IPv6 address. The port number is encoded as 2 octet
       
   183  *  number in network byte-order. The first NAT-D payload contains the
       
   184  *  remote ends IP address and port (i.e the destination address of the UDP
       
   185  *  packet). The rest of the NAT-D payloads contain possible local end IP
       
   186  *  addresses and ports (i.e all possible source addresses of the UDP packet)."
       
   187  *  
       
   188  *---------------------------------------------------------------------------------------*/
       
   189     if ( iSupport || iRfcSupport) {
       
   190        CalculateAddrPortHashL(aHashType, aICOOKIE, aRCOOKIE, aLocalAddr, aRemoteAddr);
       
   191 	   	
       
   192        aMsg.IsakmpNatD(iRfcSupport, iRemoteAddrPortHash); // NAT-D payload with HASH(CKY-I | CKY-R | Remote_IP | Remote_Port)
       
   193        aMsg.IsakmpNatD(iRfcSupport, iLocalAddrPortHash);  // NAT-D payload with HASH(CKY-I | CKY-R | Local_IP | Local_Port)          
       
   194     }   
       
   195 }
       
   196 
       
   197 TUint32 CIkev1NatDiscovery::CheckDiscoveryPayloadsL(const CArrayFixFlat<const TNATDISAKMP *> *aNatDPayloadArray,
       
   198                                                     TUint16 aHashType, TUint8 *aICOOKIE, TUint8 *aRCOOKIE,
       
   199                                                     TInetAddr &aLocalAddr, TInetAddr &aRemoteAddr)
       
   200 {
       
   201 /**---------------------------------------------------------------------------------------
       
   202  *
       
   203  *  This check NAT Discovery payloads received from remote end.
       
   204  *  from draft-ietf-ipsec-nat-t-ike-03;
       
   205  *  "
       
   206  *  If there is no NAT between then the first NAT-D payload should match one
       
   207  *  of the local NAT-D packet (i.e the local NAT-D payloads this host is
       
   208  *  sending out), and the one of the other NAT-D payloads must match the
       
   209  *  remote ends IP address and port. If the first check fails (i.e first
       
   210  *  NAT-D payload does not match any of the local IP addresses and ports),
       
   211  *  then it means that there is dynamic NAT between, and this end should
       
   212  *  start sending keepalives as defined in the <draft-ietf-ipsec-udp-encaps-03.txt>.
       
   213  *
       
   214  *---------------------------------------------------------------------------------------*/
       
   215     TUint32 NatFlags = 0;
       
   216     
       
   217     if ( iSupport || iRfcSupport ) {
       
   218         
       
   219        TInt count = aNatDPayloadArray->Count();
       
   220        if ( count > 1 ) {
       
   221           //
       
   222           // Check that the first hash corresponds current local address port pair
       
   223           //
       
   224           CalculateAddrPortHashL(aHashType, aICOOKIE, aRCOOKIE, aLocalAddr, aRemoteAddr);
       
   225           
       
   226           const TNATDISAKMP *NatDPayload = aNatDPayloadArray->At(0);
       
   227           if ( !CompareHashData(NatDPayload->HashData(), NatDPayload->HashLth(), iLocalAddrPortHash) ) {
       
   228              NatFlags |= LOCAL_END_NAT; //Local end is behind a NAT device 
       
   229           }
       
   230           
       
   231           //
       
   232           // Check the rest of NAT discovery payloads. One of them must correspond remote hash data
       
   233           // calculated in local end
       
   234           //
       
   235           NatFlags |= REMOTE_END_NAT;  // Remote end is behind a NAT device (as default)
       
   236           
       
   237           for ( TInt i = 1; (i < count); i++ )
       
   238           {
       
   239               NatDPayload = aNatDPayloadArray->At(i);
       
   240               if ( CompareHashData(NatDPayload->HashData(), NatDPayload->HashLth(), iRemoteAddrPortHash) ) {
       
   241                  NatFlags &= ~REMOTE_END_NAT; //Remote end is NOT behind a NAT device
       
   242                  break;
       
   243               }
       
   244               
       
   245           }
       
   246        }
       
   247         
       
   248     }
       
   249 
       
   250     return NatFlags;
       
   251      
       
   252 }
       
   253 
       
   254 
       
   255 void CIkev1NatDiscovery::BuildNatOaPayload(TIkev1IsakmpStream &aMsg, TInetAddr &aLocalAddr, CProposal_IIList *aProposalList)
       
   256 {
       
   257 (void)aMsg; (void)aLocalAddr; (void)aProposalList; 
       
   258     return;
       
   259 }
       
   260 
       
   261 TBool CIkev1NatDiscovery::GetPeerOriginalAddress(const TNATOaISAKMP *aNatOaPayload, TInetAddr& aRemoteOrigAddr, CProposal_IIList *aProposalList)
       
   262 {
       
   263 (void)aNatOaPayload; (void)aRemoteOrigAddr; (void)aProposalList;    
       
   264     aRemoteOrigAddr.Init(KAFUnspec);  // Set address value undefined
       
   265     return EFalse;
       
   266 }   
       
   267 
       
   268 
       
   269 
       
   270 void CIkev1NatDiscovery::CalculateAddrPortHashL(TUint16 aHashType,
       
   271                                                 TUint8 *aICOOKIE, TUint8 *aRCOOKIE,
       
   272                                                 TInetAddr &aLocalAddr, TInetAddr &aRemoteAddr)
       
   273 {
       
   274     if ( iHashExists ) {
       
   275        return;  //Hash has been already calculated
       
   276     }   
       
   277 /**---------------------------------------------------------------------------------------
       
   278  *
       
   279  *  Calculate HASH = HASH(CKY-I | CKY-R | IP | Port) both for local- and remote IP address/port
       
   280  *
       
   281  *---------------------------------------------------------------------------------------*/
       
   282     TBuf8<64> in_data;
       
   283     const TUint8 *pnum;
       
   284     TUint32 ipv4addr;
       
   285     TUint16 port;
       
   286     
       
   287     in_data.Append(aICOOKIE, ISAKMP_COOKIE_SIZE);
       
   288     in_data.Append(aRCOOKIE, ISAKMP_COOKIE_SIZE);
       
   289 
       
   290 	TInetAddr HashAddr = aLocalAddr;
       
   291 	HashAddr.SetPort(500);  //Set local port to default IKE port value
       
   292 	TInt i = 0;
       
   293 	
       
   294 	while ( i < 2 ) {
       
   295 	
       
   296     	if ( HashAddr.Family() == KAfInet ) {
       
   297 		    ipv4addr = ByteOrder::Swap32(HashAddr.Address());//Put in network order
       
   298 		    pnum = (TUint8*)&ipv4addr;
       
   299 		    in_data.Append(pnum, sizeof(TUint32));
       
   300 	    }   
       
   301 	    else {
       
   302 		    if ( HashAddr.IsV4Mapped() ) {
       
   303     	       HashAddr.ConvertToV4();  // IPv4 format
       
   304 		       ipv4addr = ByteOrder::Swap32(HashAddr.Address());//Put in network order
       
   305 		       pnum = (TUint8*)&ipv4addr;
       
   306 		       in_data.Append(pnum, sizeof(TUint32));
       
   307 		    }   
       
   308 		    else {
       
   309                pnum = &HashAddr.Ip6Address().u.iAddr8[0];  //Address in a bytestream
       
   310 		       in_data.Append(pnum, 16);
       
   311 			}   
       
   312 		}
       
   313 
       
   314 		port = ByteOrder::Swap16(HashAddr.Port());//Put in network order
       
   315 		pnum = (TUint8*)&port;  
       
   316 		in_data.Append(pnum, sizeof(TUint16));
       
   317 
       
   318 		if ( i ) {
       
   319     	   if ( aHashType == HASH_MD5 )
       
   320 	            MD5HashL(in_data, iRemoteAddrPortHash);  // Calculate hash value
       
   321 		   else SHA1HashL(in_data, iRemoteAddrPortHash);
       
   322 		}
       
   323 		else {
       
   324 			if ( aHashType == HASH_MD5 )
       
   325 				 MD5HashL(in_data, iLocalAddrPortHash);  // Calculate hash value
       
   326 			else SHA1HashL(in_data, iLocalAddrPortHash);
       
   327 		}		
       
   328 		in_data.SetLength(ISAKMP_COOKIE_SIZE + ISAKMP_COOKIE_SIZE);  // Reset lenght to Icookie + Rcookie
       
   329 		HashAddr = aRemoteAddr; // Process remote address next
       
   330 					 
       
   331 		i ++;
       
   332 	}
       
   333 
       
   334     iHashExists = ETrue;    
       
   335     
       
   336 }
       
   337 
       
   338 TBool CIkev1NatDiscovery::CompareHashData(TUint8 *aHashData, TUint32 aHashLth, TDesC8 &aReferenceHash)
       
   339 {
       
   340 /**---------------------------------------------------------------------------------------
       
   341  *
       
   342  *  Compare current hash data to the reference hash data provided
       
   343  *
       
   344  *---------------------------------------------------------------------------------------*/
       
   345     TBool result = EFalse;
       
   346                   
       
   347     if ( (TInt)aHashLth == aReferenceHash.Length() ) {
       
   348        if ( Mem::Compare(aHashData, aHashLth, aReferenceHash.Ptr(), aHashLth) == 0 ) {
       
   349           result = ETrue; 
       
   350        }       
       
   351     }   
       
   352 
       
   353     return result;
       
   354 }
       
   355