|
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 |