]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/IpSecDxe/IpSecImpl.c
NetworkPkg: Fix bug in IpSecImpl.c caused by missing parentheses
[mirror_edk2.git] / NetworkPkg / IpSecDxe / IpSecImpl.c
CommitLineData
a3bcde70 1/** @file\r
9166f840 2 The implementation of IPsec.\r
a3bcde70 3\r
e8837edd 4 (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>\r
47b27101 5 Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
a3bcde70
HT
6\r
7 This program and the accompanying materials\r
8 are licensed and made available under the terms and conditions of the BSD License\r
9 which accompanies this distribution. The full text of the license may be found at\r
10 http://opensource.org/licenses/bsd-license.php.\r
11\r
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14\r
15**/\r
16\r
9166f840 17#include "IpSecImpl.h"\r
18#include "IkeService.h"\r
19#include "IpSecDebug.h"\r
20#include "IpSecCryptIo.h"\r
a3bcde70
HT
21#include "IpSecConfigImpl.h"\r
22\r
a3bcde70
HT
23/**\r
24 Check if the specified Address is the Valid Address Range.\r
25\r
26 This function checks if the bytes after prefixed length are all Zero in this\r
6cf9230f 27 Address. This Address is supposed to point to a range address. That means it\r
9166f840 28 should gives the correct prefixed address and the bytes outside the prefixed are\r
29 zero.\r
a3bcde70
HT
30\r
31 @param[in] IpVersion The IP version.\r
32 @param[in] Address Points to EFI_IP_ADDRESS to be checked.\r
33 @param[in] PrefixLength The PrefixeLength of this address.\r
34\r
35 @retval TRUE The address is a vaild address range.\r
36 @retval FALSE The address is not a vaild address range.\r
37\r
38**/\r
39BOOLEAN\r
40IpSecValidAddressRange (\r
41 IN UINT8 IpVersion,\r
42 IN EFI_IP_ADDRESS *Address,\r
43 IN UINT8 PrefixLength\r
44 )\r
45{\r
46 UINT8 Div;\r
47 UINT8 Mod;\r
48 UINT8 Mask;\r
49 UINT8 AddrLen;\r
50 UINT8 *Addr;\r
51 EFI_IP_ADDRESS ZeroAddr;\r
52\r
53 if (PrefixLength == 0) {\r
54 return TRUE;\r
55 }\r
56\r
57 AddrLen = (UINT8) ((IpVersion == IP_VERSION_4) ? 32 : 128);\r
58\r
59 if (AddrLen <= PrefixLength) {\r
60 return FALSE;\r
61 }\r
62\r
63 Div = (UINT8) (PrefixLength / 8);\r
64 Mod = (UINT8) (PrefixLength % 8);\r
65 Addr = (UINT8 *) Address;\r
66 ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));\r
67\r
68 //\r
69 // Check whether the mod part of host scope is zero or not.\r
70 //\r
71 if (Mod > 0) {\r
72 Mask = (UINT8) (0xFF << (8 - Mod));\r
73\r
74 if ((Addr[Div] | Mask) != Mask) {\r
75 return FALSE;\r
76 }\r
77\r
78 Div++;\r
79 }\r
80 //\r
81 // Check whether the div part of host scope is zero or not.\r
82 //\r
83 if (CompareMem (\r
84 &Addr[Div],\r
85 &ZeroAddr,\r
86 sizeof (EFI_IP_ADDRESS) - Div\r
87 ) != 0) {\r
88 return FALSE;\r
89 }\r
90\r
91 return TRUE;\r
92}\r
93\r
94/**\r
95 Extrct the Address Range from a Address.\r
96\r
97 This function keep the prefix address and zero other part address.\r
98\r
99 @param[in] Address Point to a specified address.\r
100 @param[in] PrefixLength The prefix length.\r
101 @param[out] Range Contain the return Address Range.\r
102\r
103**/\r
104VOID\r
105IpSecExtractAddressRange (\r
106 IN EFI_IP_ADDRESS *Address,\r
107 IN UINT8 PrefixLength,\r
108 OUT EFI_IP_ADDRESS *Range\r
109 )\r
110{\r
111 UINT8 Div;\r
112 UINT8 Mod;\r
113 UINT8 Mask;\r
114 UINT8 *Addr;\r
115\r
116 if (PrefixLength == 0) {\r
117 return ;\r
118 }\r
119\r
120 Div = (UINT8) (PrefixLength / 8);\r
121 Mod = (UINT8) (PrefixLength % 8);\r
122 Addr = (UINT8 *) Range;\r
123\r
124 CopyMem (Range, Address, sizeof (EFI_IP_ADDRESS));\r
125\r
126 //\r
127 // Zero the mod part of host scope.\r
128 //\r
129 if (Mod > 0) {\r
130 Mask = (UINT8) (0xFF << (8 - Mod));\r
131 Addr[Div] = (UINT8) (Addr[Div] & Mask);\r
132 Div++;\r
133 }\r
134 //\r
135 // Zero the div part of host scope.\r
136 //\r
137 ZeroMem (&Addr[Div], sizeof (EFI_IP_ADDRESS) - Div);\r
138\r
139}\r
140\r
141/**\r
142 Checks if the IP Address in the address range of AddressInfos specified.\r
143\r
144 @param[in] IpVersion The IP version.\r
145 @param[in] IpAddr Point to EFI_IP_ADDRESS to be check.\r
146 @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check\r
147 the IP Address is matched.\r
148 @param[in] AddressCount The total numbers of the AddressInfo.\r
149\r
150 @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified.\r
151 @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified.\r
152\r
153**/\r
154BOOLEAN\r
155IpSecMatchIpAddress (\r
156 IN UINT8 IpVersion,\r
157 IN EFI_IP_ADDRESS *IpAddr,\r
158 IN EFI_IP_ADDRESS_INFO *AddressInfo,\r
159 IN UINT32 AddressCount\r
160 )\r
161{\r
162 EFI_IP_ADDRESS Range;\r
163 UINT32 Index;\r
164 BOOLEAN IsMatch;\r
165\r
166 IsMatch = FALSE;\r
167\r
168 for (Index = 0; Index < AddressCount; Index++) {\r
169 //\r
170 // Check whether the target address is in the address range\r
171 // if it's a valid range of address.\r
172 //\r
173 if (IpSecValidAddressRange (\r
174 IpVersion,\r
175 &AddressInfo[Index].Address,\r
176 AddressInfo[Index].PrefixLength\r
177 )) {\r
178 //\r
179 // Get the range of the target address belongs to.\r
180 //\r
181 ZeroMem (&Range, sizeof (EFI_IP_ADDRESS));\r
182 IpSecExtractAddressRange (\r
183 IpAddr,\r
184 AddressInfo[Index].PrefixLength,\r
185 &Range\r
186 );\r
187\r
188 if (CompareMem (\r
189 &Range,\r
190 &AddressInfo[Index].Address,\r
191 sizeof (EFI_IP_ADDRESS)\r
192 ) == 0) {\r
193 //\r
194 // The target address is in the address range.\r
195 //\r
196 IsMatch = TRUE;\r
197 break;\r
198 }\r
199 }\r
200\r
201 if (CompareMem (\r
202 IpAddr,\r
203 &AddressInfo[Index].Address,\r
204 sizeof (EFI_IP_ADDRESS)\r
205 ) == 0) {\r
206 //\r
207 // The target address is exact same as the address.\r
208 //\r
209 IsMatch = TRUE;\r
210 break;\r
211 }\r
212 }\r
a3bcde70
HT
213 return IsMatch;\r
214}\r
215\r
216/**\r
217 Check if the specified Protocol and Prot is supported by the specified SPD Entry.\r
218\r
219 This function is the subfunction of IPsecLookUpSpdEntry() that is used to\r
220 check if the sent/received IKE packet has the related SPD entry support.\r
221\r
222 @param[in] Protocol The Protocol to be checked.\r
223 @param[in] IpPayload Point to IP Payload to be check.\r
224 @param[in] SpdProtocol The Protocol supported by SPD.\r
225 @param[in] SpdLocalPort The Local Port in SPD.\r
226 @param[in] SpdRemotePort The Remote Port in SPD.\r
227 @param[in] IsOutbound Flag to indicate the is for IKE Packet sending or recieving.\r
228\r
229 @retval TRUE The Protocol and Port are supported by the SPD Entry.\r
230 @retval FALSE The Protocol and Port are not supported by the SPD Entry.\r
231\r
232**/\r
233BOOLEAN\r
234IpSecMatchNextLayerProtocol (\r
235 IN UINT8 Protocol,\r
236 IN UINT8 *IpPayload,\r
237 IN UINT16 SpdProtocol,\r
238 IN UINT16 SpdLocalPort,\r
239 IN UINT16 SpdRemotePort,\r
240 IN BOOLEAN IsOutbound\r
241 )\r
242{\r
243 BOOLEAN IsMatch;\r
244\r
245 if (SpdProtocol == EFI_IPSEC_ANY_PROTOCOL) {\r
246 return TRUE;\r
247 }\r
248\r
249 IsMatch = FALSE;\r
250\r
251 if (SpdProtocol == Protocol) {\r
252 switch (Protocol) {\r
253 case EFI_IP_PROTO_UDP:\r
254 case EFI_IP_PROTO_TCP:\r
255 //\r
256 // For udp and tcp, (0, 0) means no need to check local and remote\r
257 // port. The payload is passed from upper level, which means it should\r
258 // be in network order.\r
259 //\r
260 IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
261 IsMatch = (BOOLEAN) (IsMatch ||\r
262 (IsOutbound &&\r
263 (BOOLEAN)(\r
264 NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdLocalPort &&\r
265 NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdRemotePort\r
266 )\r
267 ));\r
268\r
269 IsMatch = (BOOLEAN) (IsMatch ||\r
270 (!IsOutbound &&\r
271 (BOOLEAN)(\r
272 NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdLocalPort &&\r
273 NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdRemotePort\r
274 )\r
275 ));\r
276 break;\r
277\r
278 case EFI_IP_PROTO_ICMP:\r
279 //\r
280 // For icmpv4, type code is replaced with local port and remote port,\r
281 // and (0, 0) means no need to check.\r
282 //\r
283 IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
284 IsMatch = (BOOLEAN) (IsMatch ||\r
285 (BOOLEAN) (((IP4_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&\r
286 ((IP4_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort\r
287 )\r
288 );\r
289 break;\r
290\r
291 case IP6_ICMP:\r
292 //\r
293 // For icmpv6, type code is replaced with local port and remote port,\r
294 // and (0, 0) means no need to check.\r
295 //\r
296 IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);\r
297\r
298 IsMatch = (BOOLEAN) (IsMatch ||\r
299 (BOOLEAN) (((IP6_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&\r
300 ((IP6_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort\r
301 )\r
302 );\r
303 break;\r
304\r
305 default:\r
306 IsMatch = TRUE;\r
307 break;\r
308 }\r
309 }\r
310\r
311 return IsMatch;\r
312}\r
313\r
314/**\r
315 Find the SAD through a specified SPD's SAD list.\r
316\r
317 @param[in] SadList SAD list related to a specified SPD entry.\r
318 @param[in] DestAddress The destination address used to find the SAD entry.\r
9166f840 319 @param[in] IpVersion The IP version. Ip4 or Ip6.\r
a3bcde70
HT
320\r
321 @return The pointer to a certain SAD entry.\r
322\r
323**/\r
324IPSEC_SAD_ENTRY *\r
325IpSecLookupSadBySpd (\r
326 IN LIST_ENTRY *SadList,\r
9166f840 327 IN EFI_IP_ADDRESS *DestAddress,\r
328 IN UINT8 IpVersion\r
a3bcde70
HT
329 )\r
330{\r
331 LIST_ENTRY *Entry;\r
332 IPSEC_SAD_ENTRY *SadEntry;\r
6cf9230f 333\r
9166f840 334 NET_LIST_FOR_EACH (Entry, SadList) {\r
a3bcde70
HT
335\r
336 SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);\r
337 //\r
9166f840 338 // Find the right SAD entry which contains the appointed dest address.\r
a3bcde70 339 //\r
9166f840 340 if (IpSecMatchIpAddress (\r
341 IpVersion,\r
a3bcde70 342 DestAddress,\r
9166f840 343 SadEntry->Data->SpdSelector->RemoteAddress,\r
344 SadEntry->Data->SpdSelector->RemoteAddressCount\r
6cf9230f 345 )){\r
a3bcde70
HT
346 return SadEntry;\r
347 }\r
348 }\r
349\r
350 return NULL;\r
351}\r
352\r
353/**\r
354 Find the SAD through whole SAD list.\r
355\r
356 @param[in] Spi The SPI used to search the SAD entry.\r
357 @param[in] DestAddress The destination used to search the SAD entry.\r
9166f840 358 @param[in] IpVersion The IP version. Ip4 or Ip6.\r
a3bcde70
HT
359\r
360 @return the pointer to a certain SAD entry.\r
361\r
362**/\r
363IPSEC_SAD_ENTRY *\r
364IpSecLookupSadBySpi (\r
365 IN UINT32 Spi,\r
9166f840 366 IN EFI_IP_ADDRESS *DestAddress,\r
367 IN UINT8 IpVersion\r
a3bcde70
HT
368 )\r
369{\r
370 LIST_ENTRY *Entry;\r
371 LIST_ENTRY *SadList;\r
372 IPSEC_SAD_ENTRY *SadEntry;\r
373\r
374 SadList = &mConfigData[IPsecConfigDataTypeSad];\r
375\r
9166f840 376 NET_LIST_FOR_EACH (Entry, SadList) {\r
a3bcde70
HT
377\r
378 SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);\r
9166f840 379\r
a3bcde70 380 //\r
9166f840 381 // Find the right SAD entry which contain the appointed spi and dest addr.\r
a3bcde70 382 //\r
9166f840 383 if (SadEntry->Id->Spi == Spi) {\r
384 if (SadEntry->Data->Mode == EfiIPsecTunnel) {\r
385 if (CompareMem (\r
6cf9230f 386 &DestAddress,\r
9166f840 387 &SadEntry->Data->TunnelDestAddress,\r
388 sizeof (EFI_IP_ADDRESS)\r
389 )) {\r
390 return SadEntry;\r
391 }\r
392 } else {\r
393 if (SadEntry->Data->SpdSelector != NULL &&\r
394 IpSecMatchIpAddress (\r
6cf9230f 395 IpVersion,\r
396 DestAddress,\r
9166f840 397 SadEntry->Data->SpdSelector->RemoteAddress,\r
398 SadEntry->Data->SpdSelector->RemoteAddressCount\r
399 )\r
400 ) {\r
401 return SadEntry;\r
6cf9230f 402 }\r
9166f840 403 }\r
a3bcde70
HT
404 }\r
405 }\r
a3bcde70
HT
406 return NULL;\r
407}\r
408\r
409/**\r
410 Look up if there is existing SAD entry for specified IP packet sending.\r
411\r
412 This function is called by the IPsecProcess when there is some IP packet needed to\r
413 send out. This function checks if there is an existing SAD entry that can be serviced\r
414 to this IP packet sending. If no existing SAD entry could be used, this\r
415 function will invoke an IPsec Key Exchange Negotiation.\r
416\r
417 @param[in] Private Points to private data.\r
418 @param[in] NicHandle Points to a NIC handle.\r
419 @param[in] IpVersion The version of IP.\r
420 @param[in] IpHead The IP Header of packet to be sent out.\r
421 @param[in] IpPayload The IP Payload to be sent out.\r
422 @param[in] OldLastHead The Last protocol of the IP packet.\r
423 @param[in] SpdEntry Points to a related SPD entry.\r
424 @param[out] SadEntry Contains the Point of a related SAD entry.\r
425\r
426 @retval EFI_DEVICE_ERROR One of following conditions is TRUE:\r
427 - If don't find related UDP service.\r
428 - Sequence Number is used up.\r
429 - Extension Sequence Number is used up.\r
a3bcde70
HT
430 @retval EFI_NOT_READY No existing SAD entry could be used.\r
431 @retval EFI_SUCCESS Find the related SAD entry.\r
432\r
433**/\r
434EFI_STATUS\r
435IpSecLookupSadEntry (\r
436 IN IPSEC_PRIVATE_DATA *Private,\r
437 IN EFI_HANDLE NicHandle,\r
438 IN UINT8 IpVersion,\r
439 IN VOID *IpHead,\r
440 IN UINT8 *IpPayload,\r
441 IN UINT8 OldLastHead,\r
442 IN IPSEC_SPD_ENTRY *SpdEntry,\r
443 OUT IPSEC_SAD_ENTRY **SadEntry\r
444 )\r
445{\r
9166f840 446 IKE_UDP_SERVICE *UdpService;\r
a3bcde70
HT
447 IPSEC_SAD_ENTRY *Entry;\r
448 IPSEC_SAD_DATA *Data;\r
449 EFI_IP_ADDRESS DestIp;\r
450 UINT32 SeqNum32;\r
451\r
452 *SadEntry = NULL;\r
9166f840 453 UdpService = IkeLookupUdp (Private, NicHandle, IpVersion);\r
454\r
455 if (UdpService == NULL) {\r
456 return EFI_DEVICE_ERROR;\r
457 }\r
a3bcde70
HT
458 //\r
459 // Parse the destination address from ip header.\r
460 //\r
461 ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));\r
462 if (IpVersion == IP_VERSION_4) {\r
463 CopyMem (\r
464 &DestIp,\r
465 &((IP4_HEAD *) IpHead)->Dst,\r
466 sizeof (IP4_ADDR)\r
467 );\r
468 } else {\r
469 CopyMem (\r
470 &DestIp,\r
471 &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,\r
472 sizeof (EFI_IP_ADDRESS)\r
473 );\r
474 }\r
6cf9230f 475\r
a3bcde70 476 //\r
9166f840 477 // Find the SAD entry in the spd.sas list according to the dest address.\r
a3bcde70 478 //\r
9166f840 479 Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp, IpVersion);\r
a3bcde70
HT
480\r
481 if (Entry == NULL) {\r
a3bcde70
HT
482 if (OldLastHead != IP6_ICMP ||\r
483 (OldLastHead == IP6_ICMP && *IpPayload == ICMP_V6_ECHO_REQUEST)\r
484 ) {\r
485 //\r
9166f840 486 // Start ike negotiation process except the request packet of ping.\r
a3bcde70 487 //\r
9166f840 488 if (SpdEntry->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {\r
489 IkeNegotiate (\r
490 UdpService,\r
491 SpdEntry,\r
492 &SpdEntry->Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress\r
493 );\r
494 } else {\r
495 IkeNegotiate (\r
496 UdpService,\r
497 SpdEntry,\r
498 &DestIp\r
499 );\r
500 }\r
6cf9230f 501\r
a3bcde70
HT
502 }\r
503\r
504 return EFI_NOT_READY;\r
505 }\r
506\r
507 Data = Entry->Data;\r
508\r
509 if (!Data->ManualSet) {\r
510 if (Data->ESNEnabled) {\r
511 //\r
512 // Validate the 64bit sn number if 64bit sn enabled.\r
513 //\r
9166f840 514 if ((UINT64) (Data->SequenceNumber + 1) == 0) {\r
a3bcde70
HT
515 //\r
516 // TODO: Re-negotiate SA\r
517 //\r
518 return EFI_DEVICE_ERROR;\r
519 }\r
520 } else {\r
521 //\r
522 // Validate the 32bit sn number if 64bit sn disabled.\r
523 //\r
524 SeqNum32 = (UINT32) Data->SequenceNumber;\r
9166f840 525 if ((UINT32) (SeqNum32 + 1) == 0) {\r
a3bcde70
HT
526 //\r
527 // TODO: Re-negotiate SA\r
528 //\r
529 return EFI_DEVICE_ERROR;\r
530 }\r
531 }\r
532 }\r
533\r
534 *SadEntry = Entry;\r
535\r
536 return EFI_SUCCESS;\r
537}\r
538\r
539/**\r
540 Find a PAD entry according to a remote IP address.\r
541\r
542 @param[in] IpVersion The version of IP.\r
543 @param[in] IpAddr Points to remote IP address.\r
544\r
545 @return the pointer of related PAD entry.\r
546\r
547**/\r
548IPSEC_PAD_ENTRY *\r
549IpSecLookupPadEntry (\r
550 IN UINT8 IpVersion,\r
551 IN EFI_IP_ADDRESS *IpAddr\r
552 )\r
553{\r
554 LIST_ENTRY *PadList;\r
555 LIST_ENTRY *Entry;\r
556 EFI_IP_ADDRESS_INFO *IpAddrInfo;\r
557 IPSEC_PAD_ENTRY *PadEntry;\r
558\r
559 PadList = &mConfigData[IPsecConfigDataTypePad];\r
560\r
561 for (Entry = PadList->ForwardLink; Entry != PadList; Entry = Entry->ForwardLink) {\r
562\r
563 PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry);\r
564 IpAddrInfo = &PadEntry->Id->Id.IpAddress;\r
565 //\r
566 // Find the right pad entry which contain the appointed dest addr.\r
567 //\r
568 if (IpSecMatchIpAddress (IpVersion, IpAddr, IpAddrInfo, 1)) {\r
569 return PadEntry;\r
570 }\r
571 }\r
572\r
573 return NULL;\r
574}\r
575\r
576/**\r
577 Check if the specified IP packet can be serviced by this SPD entry.\r
578\r
579 @param[in] SpdEntry Point to SPD entry.\r
580 @param[in] IpVersion Version of IP.\r
581 @param[in] IpHead Point to IP header.\r
582 @param[in] IpPayload Point to IP payload.\r
583 @param[in] Protocol The Last protocol of IP packet.\r
584 @param[in] IsOutbound Traffic direction.\r
9166f840 585 @param[out] Action The support action of SPD entry.\r
a3bcde70 586\r
9166f840 587 @retval EFI_SUCCESS Find the related SPD.\r
588 @retval EFI_NOT_FOUND Not find the related SPD entry;\r
a3bcde70
HT
589\r
590**/\r
9166f840 591EFI_STATUS\r
a3bcde70 592IpSecLookupSpdEntry (\r
9166f840 593 IN IPSEC_SPD_ENTRY *SpdEntry,\r
594 IN UINT8 IpVersion,\r
595 IN VOID *IpHead,\r
596 IN UINT8 *IpPayload,\r
597 IN UINT8 Protocol,\r
6cf9230f 598 IN BOOLEAN IsOutbound,\r
9166f840 599 OUT EFI_IPSEC_ACTION *Action\r
a3bcde70
HT
600 )\r
601{\r
602 EFI_IPSEC_SPD_SELECTOR *SpdSel;\r
603 IP4_HEAD *Ip4;\r
604 EFI_IP6_HEADER *Ip6;\r
605 EFI_IP_ADDRESS SrcAddr;\r
606 EFI_IP_ADDRESS DstAddr;\r
607 BOOLEAN SpdMatch;\r
608\r
609 ASSERT (SpdEntry != NULL);\r
610 SpdSel = SpdEntry->Selector;\r
611 Ip4 = (IP4_HEAD *) IpHead;\r
612 Ip6 = (EFI_IP6_HEADER *) IpHead;\r
613\r
614 ZeroMem (&SrcAddr, sizeof (EFI_IP_ADDRESS));\r
615 ZeroMem (&DstAddr, sizeof (EFI_IP_ADDRESS));\r
616\r
617 //\r
618 // Parse the source and destination address from ip header.\r
619 //\r
620 if (IpVersion == IP_VERSION_4) {\r
621 CopyMem (&SrcAddr, &Ip4->Src, sizeof (IP4_ADDR));\r
622 CopyMem (&DstAddr, &Ip4->Dst, sizeof (IP4_ADDR));\r
623 } else {\r
624 CopyMem (&SrcAddr, &Ip6->SourceAddress, sizeof (EFI_IPv6_ADDRESS));\r
625 CopyMem (&DstAddr, &Ip6->DestinationAddress, sizeof (EFI_IPv6_ADDRESS));\r
626 }\r
627 //\r
628 // Check the local and remote addresses for outbound traffic\r
629 //\r
630 SpdMatch = (BOOLEAN)(IsOutbound &&\r
631 IpSecMatchIpAddress (\r
632 IpVersion,\r
633 &SrcAddr,\r
634 SpdSel->LocalAddress,\r
635 SpdSel->LocalAddressCount\r
636 ) &&\r
637 IpSecMatchIpAddress (\r
638 IpVersion,\r
639 &DstAddr,\r
640 SpdSel->RemoteAddress,\r
641 SpdSel->RemoteAddressCount\r
642 )\r
643 );\r
644\r
645 //\r
646 // Check the local and remote addresses for inbound traffic\r
647 //\r
648 SpdMatch = (BOOLEAN) (SpdMatch ||\r
649 (!IsOutbound &&\r
650 IpSecMatchIpAddress (\r
651 IpVersion,\r
652 &DstAddr,\r
653 SpdSel->LocalAddress,\r
654 SpdSel->LocalAddressCount\r
655 ) &&\r
656 IpSecMatchIpAddress (\r
657 IpVersion,\r
658 &SrcAddr,\r
659 SpdSel->RemoteAddress,\r
660 SpdSel->RemoteAddressCount\r
661 )\r
662 ));\r
663\r
664 //\r
665 // Check the next layer protocol and local and remote ports.\r
666 //\r
667 SpdMatch = (BOOLEAN) (SpdMatch &&\r
668 IpSecMatchNextLayerProtocol (\r
669 Protocol,\r
670 IpPayload,\r
671 SpdSel->NextLayerProtocol,\r
672 SpdSel->LocalPort,\r
673 SpdSel->RemotePort,\r
674 IsOutbound\r
675 )\r
676 );\r
677\r
678 if (SpdMatch) {\r
679 //\r
9166f840 680 // Find the right SPD entry if match the 5 key elements.\r
a3bcde70 681 //\r
9166f840 682 *Action = SpdEntry->Data->Action;\r
683 return EFI_SUCCESS;\r
a3bcde70
HT
684 }\r
685\r
9166f840 686 return EFI_NOT_FOUND;\r
a3bcde70
HT
687}\r
688\r
689/**\r
9166f840 690 The call back function of NetbufFromExt.\r
691\r
692 @param[in] Arg The argument passed from the caller.\r
a3bcde70
HT
693\r
694**/\r
9166f840 695VOID\r
a3bcde70 696EFIAPI\r
9166f840 697IpSecOnRecyclePacket (\r
698 IN VOID *Arg\r
a3bcde70
HT
699 )\r
700{\r
9166f840 701}\r
702\r
703/**\r
704 This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP\r
705 is released.\r
706\r
707 @param[in] Event The related event.\r
708 @param[in] Context The data passed by the caller.\r
709\r
710**/\r
711VOID\r
712EFIAPI\r
713IpSecRecycleCallback (\r
714 IN EFI_EVENT Event,\r
715 IN VOID *Context\r
716 )\r
717{\r
718 IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
719\r
720 RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context;\r
721\r
722 if (RecycleContext->FragmentTable != NULL) {\r
723 FreePool (RecycleContext->FragmentTable);\r
724 }\r
725\r
726 if (RecycleContext->PayloadBuffer != NULL) {\r
727 FreePool (RecycleContext->PayloadBuffer);\r
728 }\r
729\r
730 FreePool (RecycleContext);\r
731 gBS->CloseEvent (Event);\r
732\r
733}\r
734\r
735/**\r
6cf9230f 736 Calculate the extension hader of IP. The return length only doesn't contain\r
9166f840 737 the fixed IP header length.\r
738\r
739 @param[in] IpHead Points to an IP head to be calculated.\r
740 @param[in] LastHead Points to the last header of the IP header.\r
741\r
742 @return The length of the extension header.\r
743\r
744**/\r
745UINT16\r
746IpSecGetPlainExtHeadSize (\r
747 IN VOID *IpHead,\r
748 IN UINT8 *LastHead\r
749 )\r
750{\r
751 UINT16 Size;\r
752\r
753 Size = (UINT16) (LastHead - (UINT8 *) IpHead);\r
754\r
755 if (Size > sizeof (EFI_IP6_HEADER)) {\r
756 //\r
757 // * (LastHead+1) point the last header's length but not include the first\r
758 // 8 octers, so this formluation add 8 at the end.\r
759 //\r
760 Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8);\r
761 } else {\r
762 Size = 0;\r
763 }\r
764\r
765 return Size;\r
766}\r
767\r
768/**\r
769 Verify if the Authentication payload is correct.\r
770\r
771 @param[in] EspBuffer Points to the ESP wrapped buffer.\r
772 @param[in] EspSize The size of the ESP wrapped buffer.\r
773 @param[in] SadEntry The related SAD entry to store the authentication\r
774 algorithm key.\r
775 @param[in] IcvSize The length of ICV.\r
776\r
777 @retval EFI_SUCCESS The authentication data is correct.\r
778 @retval EFI_ACCESS_DENIED The authentication data is not correct.\r
779\r
780**/\r
781EFI_STATUS\r
782IpSecEspAuthVerifyPayload (\r
783 IN UINT8 *EspBuffer,\r
784 IN UINTN EspSize,\r
785 IN IPSEC_SAD_ENTRY *SadEntry,\r
4cb0548d 786 IN UINTN IcvSize\r
9166f840 787 )\r
788{\r
789 EFI_STATUS Status;\r
790 UINTN AuthSize;\r
791 UINT8 IcvBuffer[12];\r
792 HASH_DATA_FRAGMENT HashFragment[1];\r
793\r
794 //\r
795 // Calculate the size of authentication payload.\r
796 //\r
4cb0548d 797 AuthSize = EspSize - IcvSize;\r
9166f840 798\r
799 //\r
800 // Calculate the icv buffer and size of the payload.\r
801 //\r
802 HashFragment[0].Data = EspBuffer;\r
803 HashFragment[0].DataSize = AuthSize;\r
6cf9230f 804\r
9166f840 805 Status = IpSecCryptoIoHmac (\r
806 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId,\r
807 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
808 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength,\r
809 HashFragment,\r
810 1,\r
811 IcvBuffer,\r
4cb0548d 812 IcvSize\r
9166f840 813 );\r
814 if (EFI_ERROR (Status)) {\r
815 return Status;\r
816 }\r
6cf9230f 817\r
9166f840 818 //\r
819 // Compare the calculated icv and the appended original icv.\r
820 //\r
4cb0548d 821 if (CompareMem (EspBuffer + AuthSize, IcvBuffer, IcvSize) == 0) {\r
9166f840 822 return EFI_SUCCESS;\r
823 }\r
824\r
825 DEBUG ((DEBUG_ERROR, "Error auth verify payload\n"));\r
826 return EFI_ACCESS_DENIED;\r
827}\r
828\r
829/**\r
830 Search the related SAD entry by the input .\r
831\r
832 @param[in] IpHead The pointer to IP header.\r
833 @param[in] IpVersion The version of IP (IP4 or IP6).\r
834 @param[in] Spi The SPI used to search the related SAD entry.\r
6cf9230f 835\r
9166f840 836\r
837 @retval NULL Not find the related SAD entry.\r
6cf9230f 838 @retval IPSEC_SAD_ENTRY Return the related SAD entry.\r
9166f840 839\r
840**/\r
841IPSEC_SAD_ENTRY *\r
842IpSecFoundSadFromInboundPacket (\r
843 UINT8 *IpHead,\r
844 UINT8 IpVersion,\r
845 UINT32 Spi\r
6cf9230f 846 )\r
9166f840 847{\r
848 EFI_IP_ADDRESS DestIp;\r
6cf9230f 849\r
9166f840 850 //\r
851 // Parse destination address from ip header.\r
852 //\r
853 ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));\r
854 if (IpVersion == IP_VERSION_4) {\r
855 CopyMem (\r
856 &DestIp,\r
857 &((IP4_HEAD *) IpHead)->Dst,\r
858 sizeof (IP4_ADDR)\r
859 );\r
860 } else {\r
861 CopyMem (\r
862 &DestIp,\r
863 &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,\r
864 sizeof (EFI_IPv6_ADDRESS)\r
865 );\r
866 }\r
6cf9230f 867\r
9166f840 868 //\r
869 // Lookup SAD entry according to the spi and dest address.\r
6cf9230f 870 //\r
9166f840 871 return IpSecLookupSadBySpi (Spi, &DestIp, IpVersion);\r
872}\r
873\r
874/**\r
875 Validate the IP6 extension header format for both the packets we received\r
876 and that we will transmit.\r
877\r
878 @param[in] NextHeader The next header field in IPv6 basic header.\r
879 @param[in] ExtHdrs The first bye of the option.\r
880 @param[in] ExtHdrsLen The length of the whole option.\r
881 @param[out] LastHeader The pointer of NextHeader of the last extension\r
882 header processed by IP6.\r
883 @param[out] RealExtsLen The length of extension headers processed by IP6 layer.\r
884 This is an optional parameter that may be NULL.\r
885\r
886 @retval TRUE The option is properly formated.\r
887 @retval FALSE The option is malformated.\r
888\r
889**/\r
890BOOLEAN\r
891IpSecIsIp6ExtsValid (\r
892 IN UINT8 *NextHeader,\r
893 IN UINT8 *ExtHdrs,\r
894 IN UINT32 ExtHdrsLen,\r
895 OUT UINT8 **LastHeader,\r
896 OUT UINT32 *RealExtsLen OPTIONAL\r
897 )\r
898{\r
899 UINT32 Pointer;\r
900 UINT8 *Option;\r
901 UINT8 OptionLen;\r
9166f840 902 UINT8 CountD;\r
903 UINT8 CountF;\r
904 UINT8 CountA;\r
905\r
906 if (RealExtsLen != NULL) {\r
907 *RealExtsLen = 0;\r
908 }\r
909\r
910 *LastHeader = NextHeader;\r
a3bcde70 911\r
9166f840 912 if (ExtHdrs == NULL && ExtHdrsLen == 0) {\r
913 return TRUE;\r
914 }\r
915\r
916 if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) {\r
917 return FALSE;\r
918 }\r
919\r
920 Pointer = 0;\r
9166f840 921 CountD = 0;\r
922 CountF = 0;\r
923 CountA = 0;\r
924\r
925 while (Pointer <= ExtHdrsLen) {\r
926\r
927 switch (*NextHeader) {\r
928 case IP6_HOP_BY_HOP:\r
929 if (Pointer != 0) {\r
930 return FALSE;\r
931 }\r
932\r
9166f840 933 //\r
934 // Fall through\r
935 //\r
936 case IP6_DESTINATION:\r
937 if (*NextHeader == IP6_DESTINATION) {\r
938 CountD++;\r
939 }\r
940\r
941 if (CountD > 2) {\r
942 return FALSE;\r
943 }\r
944\r
945 NextHeader = ExtHdrs + Pointer;\r
946\r
947 Pointer++;\r
948 Option = ExtHdrs + Pointer;\r
949 OptionLen = (UINT8) ((*Option + 1) * 8 - 2);\r
950 Option++;\r
951 Pointer++;\r
952\r
953 Pointer = Pointer + OptionLen;\r
954 break;\r
955\r
956 case IP6_FRAGMENT:\r
957 if (++CountF > 1) {\r
958 return FALSE;\r
959 }\r
a3bcde70 960 //\r
9166f840 961 // RFC2402, AH header should after fragment header.\r
a3bcde70 962 //\r
9166f840 963 if (CountA > 1) {\r
964 return FALSE;\r
965 }\r
966\r
967 NextHeader = ExtHdrs + Pointer;\r
968 Pointer = Pointer + 8;\r
969 break;\r
a3bcde70 970\r
9166f840 971 case IP6_AH:\r
972 if (++CountA > 1) {\r
973 return FALSE;\r
974 }\r
975\r
976 Option = ExtHdrs + Pointer;\r
977 NextHeader = Option;\r
978 Option++;\r
a3bcde70 979 //\r
9166f840 980 // RFC2402, Payload length is specified in 32-bit words, minus "2".\r
a3bcde70 981 //\r
9166f840 982 OptionLen = (UINT8) ((*Option + 2) * 4);\r
983 Pointer = Pointer + OptionLen;\r
984 break;\r
985\r
986 default:\r
987 *LastHeader = NextHeader;\r
988 if (RealExtsLen != NULL) {\r
989 *RealExtsLen = Pointer;\r
990 }\r
991\r
992 return TRUE;\r
6cf9230f 993 }\r
9166f840 994 }\r
995\r
996 *LastHeader = NextHeader;\r
997\r
998 if (RealExtsLen != NULL) {\r
999 *RealExtsLen = Pointer;\r
1000 }\r
1001\r
1002 return TRUE;\r
1003}\r
1004\r
1005/**\r
6cf9230f 1006 The actual entry to process the tunnel header and inner header for tunnel mode\r
9166f840 1007 outbound traffic.\r
1008\r
6cf9230f 1009 This function is the subfunction of IpSecEspInboundPacket(). It change the destination\r
9166f840 1010 Ip address to the station address and recalculate the uplayyer's checksum.\r
9166f840 1011\r
6cf9230f 1012\r
1013 @param[in, out] IpHead Points to the IP header containing the ESP header\r
9166f840 1014 to be trimed on input, and without ESP header\r
1015 on return.\r
1016 @param[in] IpPayload The decrypted Ip payload. It start from the inner\r
1017 header.\r
1018 @param[in] IpVersion The version of IP.\r
1019 @param[in] SadData Pointer of the relevant SAD.\r
1020 @param[in, out] LastHead The Last Header in IP header on return.\r
1021\r
1022**/\r
1023VOID\r
1024IpSecTunnelInboundPacket (\r
1025 IN OUT UINT8 *IpHead,\r
1026 IN UINT8 *IpPayload,\r
1027 IN UINT8 IpVersion,\r
1028 IN IPSEC_SAD_DATA *SadData,\r
1029 IN OUT UINT8 *LastHead\r
1030 )\r
1031{\r
1032 EFI_UDP_HEADER *UdpHeader;\r
1033 TCP_HEAD *TcpHeader;\r
1034 UINT16 *Checksum;\r
1035 UINT16 PseudoChecksum;\r
1036 UINT16 PacketChecksum;\r
1037 UINT32 OptionLen;\r
1038 IP6_ICMP_HEAD *Icmp6Head;\r
1039\r
1040 Checksum = NULL;\r
6cf9230f 1041\r
9166f840 1042 if (IpVersion == IP_VERSION_4) {\r
1043 //\r
6cf9230f 1044 // Zero OutIP header use this to indicate the input packet is under\r
9166f840 1045 // IPsec Tunnel protected.\r
1046 //\r
1047 ZeroMem (\r
1048 (IP4_HEAD *)IpHead,\r
1049 sizeof (IP4_HEAD)\r
1050 );\r
1051 CopyMem (\r
1052 &((IP4_HEAD *)IpPayload)->Dst,\r
1053 &SadData->TunnelDestAddress.v4,\r
1054 sizeof (EFI_IPv4_ADDRESS)\r
1055 );\r
6cf9230f 1056\r
9166f840 1057 //\r
1058 // Recalculate IpHeader Checksum\r
1059 //\r
1060 if (((IP4_HEAD *)(IpPayload))->Checksum != 0 ) {\r
1061 ((IP4_HEAD *)(IpPayload))->Checksum = 0;\r
1062 ((IP4_HEAD *)(IpPayload))->Checksum = (UINT16) (~NetblockChecksum (\r
6cf9230f 1063 (UINT8 *)IpPayload,\r
9166f840 1064 ((IP4_HEAD *)IpPayload)->HeadLen << 2\r
1065 ));\r
1066\r
1067\r
1068 }\r
6cf9230f 1069\r
9166f840 1070 //\r
1071 // Recalcualte PseudoChecksum\r
1072 //\r
1073 switch (((IP4_HEAD *)IpPayload)->Protocol) {\r
1074 case EFI_IP_PROTO_UDP :\r
1075 UdpHeader = (EFI_UDP_HEADER *)((UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2));\r
1076 Checksum = & UdpHeader->Checksum;\r
1077 *Checksum = 0;\r
1078 break;\r
1079\r
1080 case EFI_IP_PROTO_TCP:\r
1081 TcpHeader = (TCP_HEAD *) ((UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2));\r
1082 Checksum = &TcpHeader->Checksum;\r
1083 *Checksum = 0;\r
1084 break;\r
1085\r
1086 default:\r
1087 break;\r
a3bcde70 1088 }\r
9166f840 1089 PacketChecksum = NetblockChecksum (\r
6cf9230f 1090 (UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2),\r
9166f840 1091 NTOHS (((IP4_HEAD *)IpPayload)->TotalLen) - (((IP4_HEAD *)IpPayload)->HeadLen << 2)\r
1092 );\r
1093 PseudoChecksum = NetPseudoHeadChecksum (\r
1094 ((IP4_HEAD *)IpPayload)->Src,\r
1095 ((IP4_HEAD *)IpPayload)->Dst,\r
1096 ((IP4_HEAD *)IpPayload)->Protocol,\r
1097 0\r
1098 );\r
6cf9230f 1099\r
9166f840 1100 if (Checksum != NULL) {\r
1101 *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);\r
1102 *Checksum = (UINT16) ~(NetAddChecksum (*Checksum, HTONS((UINT16)(NTOHS (((IP4_HEAD *)IpPayload)->TotalLen) - (((IP4_HEAD *)IpPayload)->HeadLen << 2)))));\r
a3bcde70 1103 }\r
9166f840 1104 }else {\r
1105 //\r
6cf9230f 1106 // Zero OutIP header use this to indicate the input packet is under\r
9166f840 1107 // IPsec Tunnel protected.\r
1108 //\r
1109 ZeroMem (\r
1110 IpHead,\r
1111 sizeof (EFI_IP6_HEADER)\r
1112 );\r
1113 CopyMem (\r
1114 &((EFI_IP6_HEADER*)IpPayload)->DestinationAddress,\r
1115 &SadData->TunnelDestAddress.v6,\r
1116 sizeof (EFI_IPv6_ADDRESS)\r
1117 );\r
6cf9230f 1118\r
9166f840 1119 //\r
1120 // Get the Extension Header and Header length.\r
1121 //\r
1122 IpSecIsIp6ExtsValid (\r
1123 &((EFI_IP6_HEADER *)IpPayload)->NextHeader,\r
1124 IpPayload + sizeof (EFI_IP6_HEADER),\r
1125 ((EFI_IP6_HEADER *)IpPayload)->PayloadLength,\r
1126 &LastHead,\r
1127 &OptionLen\r
1128 );\r
6cf9230f 1129\r
9166f840 1130 //\r
1131 // Recalcualte PseudoChecksum\r
1132 //\r
1133 switch (*LastHead) {\r
1134 case EFI_IP_PROTO_UDP:\r
1135 UdpHeader = (EFI_UDP_HEADER *)((UINT8 *)IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);\r
1136 Checksum = &UdpHeader->Checksum;\r
1137 *Checksum = 0;\r
1138 break;\r
a3bcde70 1139\r
9166f840 1140 case EFI_IP_PROTO_TCP:\r
1141 TcpHeader = (TCP_HEAD *)(IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);\r
1142 Checksum = &TcpHeader->Checksum;\r
1143 *Checksum = 0;\r
1144 break;\r
1145\r
1146 case IP6_ICMP:\r
1147 Icmp6Head = (IP6_ICMP_HEAD *) (IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);\r
1148 Checksum = &Icmp6Head->Checksum;\r
1149 *Checksum = 0;\r
1150 break;\r
1151 }\r
1152 PacketChecksum = NetblockChecksum (\r
6cf9230f 1153 IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen,\r
9166f840 1154 NTOHS(((EFI_IP6_HEADER *)IpPayload)->PayloadLength) - OptionLen\r
1155 );\r
1156 PseudoChecksum = NetIp6PseudoHeadChecksum (\r
1157 &((EFI_IP6_HEADER *)IpPayload)->SourceAddress,\r
1158 &((EFI_IP6_HEADER *)IpPayload)->DestinationAddress,\r
1159 *LastHead,\r
1160 0\r
1161 );\r
6cf9230f 1162\r
9166f840 1163 if (Checksum != NULL) {\r
1164 *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);\r
1165 *Checksum = (UINT16) ~(NetAddChecksum (\r
1166 *Checksum,\r
1167 HTONS ((UINT16)((NTOHS (((EFI_IP6_HEADER *)(IpPayload))->PayloadLength)) - OptionLen))\r
1168 ));\r
a3bcde70 1169 }\r
6cf9230f 1170 }\r
9166f840 1171}\r
1172\r
1173/**\r
1174 The actual entry to create inner header for tunnel mode inbound traffic.\r
1175\r
6cf9230f 1176 This function is the subfunction of IpSecEspOutboundPacket(). It create\r
1177 the sending packet by encrypting its payload and inserting ESP header in the orginal\r
9166f840 1178 IP header, then return the IpHeader and IPsec protected Fragmentable.\r
6cf9230f 1179\r
1180 @param[in, out] IpHead Points to IP header containing the orginal IP header\r
9166f840 1181 to be processed on input, and inserted ESP header\r
1182 on return.\r
1183 @param[in] IpVersion The version of IP.\r
1184 @param[in] SadData The related SAD data.\r
6cf9230f 1185 @param[in, out] LastHead The Last Header in IP header.\r
47b27101 1186 @param[in] OptionsBuffer Pointer to the options buffer.\r
1187 @param[in] OptionsLength Length of the options buffer.\r
9166f840 1188 @param[in, out] FragmentTable Pointer to a list of fragments to be protected by\r
1189 IPsec on input, and with IPsec protected\r
1190 on return.\r
1191 @param[in] FragmentCount The number of fragments.\r
1192\r
1193 @retval EFI_SUCCESS The operation was successful.\r
1194 @retval EFI_OUT_OF_RESOURCES The required system resources can't be allocated.\r
1195\r
1196**/\r
1197UINT8 *\r
1198IpSecTunnelOutboundPacket (\r
1199 IN OUT UINT8 *IpHead,\r
1200 IN UINT8 IpVersion,\r
1201 IN IPSEC_SAD_DATA *SadData,\r
1202 IN OUT UINT8 *LastHead,\r
1203 IN VOID **OptionsBuffer,\r
1204 IN UINT32 *OptionsLength,\r
1205 IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
1206 IN UINT32 *FragmentCount\r
1207 )\r
1208{\r
1209 UINT8 *InnerHead;\r
1210 NET_BUF *Packet;\r
1211 UINT16 PacketChecksum;\r
1212 UINT16 *Checksum;\r
1213 UINT16 PseudoChecksum;\r
1214 IP6_ICMP_HEAD *IcmpHead;\r
1215\r
1216 Checksum = NULL;\r
1217 if (OptionsLength == NULL) {\r
1218 return NULL;\r
a3bcde70 1219 }\r
6cf9230f 1220\r
9166f840 1221 if (IpVersion == IP_VERSION_4) {\r
1222 InnerHead = AllocateZeroPool (sizeof (IP4_HEAD) + *OptionsLength);\r
1223 ASSERT (InnerHead != NULL);\r
1224 CopyMem (\r
1225 InnerHead,\r
1226 IpHead,\r
1227 sizeof (IP4_HEAD)\r
1228 );\r
1229 CopyMem (\r
1230 InnerHead + sizeof (IP4_HEAD),\r
1231 *OptionsBuffer,\r
1232 *OptionsLength\r
1233 );\r
1234 } else {\r
1235 InnerHead = AllocateZeroPool (sizeof (EFI_IP6_HEADER) + *OptionsLength);\r
02a758cb 1236 ASSERT (InnerHead != NULL);\r
9166f840 1237 CopyMem (\r
1238 InnerHead,\r
1239 IpHead,\r
1240 sizeof (EFI_IP6_HEADER)\r
1241 );\r
1242 CopyMem (\r
1243 InnerHead + sizeof (EFI_IP6_HEADER),\r
1244 *OptionsBuffer,\r
1245 *OptionsLength\r
1246 );\r
1247 }\r
1248 if (OptionsBuffer != NULL) {\r
1249 if (*OptionsLength != 0) {\r
a3bcde70 1250\r
9166f840 1251 *OptionsBuffer = NULL;\r
1252 *OptionsLength = 0;\r
1253 }\r
1254 }\r
6cf9230f 1255\r
9166f840 1256 //\r
1257 // 2. Reassamlbe Fragment into Packet\r
1258 //\r
1259 Packet = NetbufFromExt (\r
1260 (NET_FRAGMENT *)(*FragmentTable),\r
1261 *FragmentCount,\r
1262 0,\r
1263 0,\r
1264 IpSecOnRecyclePacket,\r
1265 NULL\r
1266 );\r
1267 ASSERT (Packet != NULL);\r
1268 //\r
1269 // 3. Check the Last Header, if it is TCP, UDP or ICMP recalcualate its pesudo\r
1270 // CheckSum.\r
1271 //\r
1272 switch (*LastHead) {\r
1273 case EFI_IP_PROTO_UDP:\r
1274 Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, 0);\r
1275 ASSERT (Packet->Udp != NULL);\r
1276 Checksum = &Packet->Udp->Checksum;\r
1277 *Checksum = 0;\r
1278 break;\r
1279\r
1280 case EFI_IP_PROTO_TCP:\r
1281 Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, 0);\r
1282 ASSERT (Packet->Tcp != NULL);\r
1283 Checksum = &Packet->Tcp->Checksum;\r
1284 *Checksum = 0;\r
1285 break;\r
1286\r
1287 case IP6_ICMP:\r
1288 IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);\r
1289 ASSERT (IcmpHead != NULL);\r
1290 Checksum = &IcmpHead->Checksum;\r
1291 *Checksum = 0;\r
1292 break;\r
6cf9230f 1293\r
1294 default:\r
9166f840 1295 break;\r
1296 }\r
a3bcde70 1297\r
9166f840 1298 PacketChecksum = NetbufChecksum (Packet);\r
6cf9230f 1299\r
9166f840 1300 if (IpVersion == IP_VERSION_4) {\r
a3bcde70 1301 //\r
9166f840 1302 // Replace the source address of Inner Header.\r
a3bcde70 1303 //\r
9166f840 1304 CopyMem (\r
1305 &((IP4_HEAD *)InnerHead)->Src,\r
1306 &SadData->SpdSelector->LocalAddress[0].Address.v4,\r
1307 sizeof (EFI_IPv4_ADDRESS)\r
1308 );\r
1309\r
1310 PacketChecksum = NetbufChecksum (Packet);\r
1311 PseudoChecksum = NetPseudoHeadChecksum (\r
1312 ((IP4_HEAD *)InnerHead)->Src,\r
1313 ((IP4_HEAD *)InnerHead)->Dst,\r
1314 *LastHead,\r
1315 0\r
1316 );\r
6cf9230f 1317\r
9166f840 1318 } else {\r
1319 //\r
1320 // Replace the source address of Inner Header.\r
1321 //\r
1322 CopyMem (\r
1323 &((EFI_IP6_HEADER *)InnerHead)->SourceAddress,\r
1324 &(SadData->SpdSelector->LocalAddress[0].Address.v6),\r
1325 sizeof (EFI_IPv6_ADDRESS)\r
1326 );\r
1327 PacketChecksum = NetbufChecksum (Packet);\r
1328 PseudoChecksum = NetIp6PseudoHeadChecksum (\r
1329 &((EFI_IP6_HEADER *)InnerHead)->SourceAddress,\r
1330 &((EFI_IP6_HEADER *)InnerHead)->DestinationAddress,\r
1331 *LastHead,\r
1332 0\r
1333 );\r
6cf9230f 1334\r
9166f840 1335 }\r
1336 if (Checksum != NULL) {\r
1337 *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);\r
1338 *Checksum = (UINT16) ~(NetAddChecksum ((UINT16)*Checksum, HTONS ((UINT16) Packet->TotalSize)));\r
1339 }\r
1340\r
1341 if (Packet != NULL) {\r
1342 NetbufFree (Packet);\r
1343 }\r
1344 return InnerHead;\r
1345}\r
1346\r
1347/**\r
1348 The actual entry to relative function processes the inbound traffic of ESP header.\r
1349\r
6cf9230f 1350 This function is the subfunction of IpSecProtectInboundPacket(). It checks the\r
9166f840 1351 received packet security property and trim the ESP header and then returns without\r
1352 an IPsec protected IP Header and FramgmentTable.\r
6cf9230f 1353\r
9166f840 1354 @param[in] IpVersion The version of IP.\r
6cf9230f 1355 @param[in, out] IpHead Points to the IP header containing the ESP header\r
9166f840 1356 to be trimed on input, and without ESP header\r
1357 on return.\r
1358 @param[out] LastHead The Last Header in IP header on return.\r
47b27101 1359 @param[in, out] OptionsBuffer Pointer to the options buffer.\r
1360 @param[in, out] OptionsLength Length of the options buffer.\r
9166f840 1361 @param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec\r
1362 protected on input, and without IPsec protected\r
1363 on return.\r
1364 @param[in, out] FragmentCount The number of fragments.\r
1365 @param[out] SpdSelector Pointer to contain the address of SPD selector on return.\r
1366 @param[out] RecycleEvent The event for recycling of resources.\r
1367\r
1368 @retval EFI_SUCCESS The operation was successful.\r
1369 @retval EFI_ACCESS_DENIED One or more following conditions is TRUE:\r
4cb0548d 1370 - ESP header was not found or mal-format.\r
9166f840 1371 - The related SAD entry was not found.\r
1372 - The related SAD entry does not support the ESP protocol.\r
1373 @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.\r
1374\r
1375**/\r
1376EFI_STATUS\r
1377IpSecEspInboundPacket (\r
1378 IN UINT8 IpVersion,\r
1379 IN OUT VOID *IpHead,\r
1380 OUT UINT8 *LastHead,\r
47b27101 1381 IN OUT VOID **OptionsBuffer,\r
1382 IN OUT UINT32 *OptionsLength,\r
9166f840 1383 IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
1384 IN OUT UINT32 *FragmentCount,\r
1385 OUT EFI_IPSEC_SPD_SELECTOR **SpdSelector,\r
1386 OUT EFI_EVENT *RecycleEvent\r
1387 )\r
1388{\r
1389 EFI_STATUS Status;\r
1390 NET_BUF *Payload;\r
1391 UINTN EspSize;\r
1392 UINTN IvSize;\r
4cb0548d 1393 UINTN BlockSize;\r
1394 UINTN MiscSize;\r
9166f840 1395 UINTN PlainPayloadSize;\r
1396 UINTN PaddingSize;\r
1397 UINTN IcvSize;\r
1398 UINT8 *ProcessBuffer;\r
1399 EFI_ESP_HEADER *EspHeader;\r
1400 EFI_ESP_TAIL *EspTail;\r
1401 EFI_IPSEC_SA_ID *SaId;\r
1402 IPSEC_SAD_DATA *SadData;\r
1403 IPSEC_SAD_ENTRY *SadEntry;\r
1404 IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
1405 UINT8 NextHeader;\r
1406 UINT16 IpSecHeadSize;\r
1407 UINT8 *InnerHead;\r
1408\r
1409 Status = EFI_SUCCESS;\r
1410 Payload = NULL;\r
1411 ProcessBuffer = NULL;\r
1412 RecycleContext = NULL;\r
1413 *RecycleEvent = NULL;\r
1414 PlainPayloadSize = 0;\r
1415 NextHeader = 0;\r
6cf9230f 1416\r
9166f840 1417 //\r
1418 // Build netbuf from fragment table first.\r
1419 //\r
1420 Payload = NetbufFromExt (\r
1421 (NET_FRAGMENT *) *FragmentTable,\r
1422 *FragmentCount,\r
1423 0,\r
1424 sizeof (EFI_ESP_HEADER),\r
1425 IpSecOnRecyclePacket,\r
1426 NULL\r
a3bcde70 1427 );\r
9166f840 1428 if (Payload == NULL) {\r
1429 Status = EFI_OUT_OF_RESOURCES;\r
1430 goto ON_EXIT;\r
1431 }\r
6cf9230f 1432\r
9166f840 1433 //\r
1434 // Get the esp size and esp header from netbuf.\r
1435 //\r
1436 EspSize = Payload->TotalSize;\r
1437 EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL);\r
6cf9230f 1438\r
9166f840 1439 if (EspHeader == NULL) {\r
1440 Status = EFI_ACCESS_DENIED;\r
1441 goto ON_EXIT;\r
1442 }\r
6cf9230f 1443\r
9166f840 1444 //\r
1445 // Parse destination address from ip header and found the related SAD Entry.\r
1446 //\r
1447 SadEntry = IpSecFoundSadFromInboundPacket (\r
6cf9230f 1448 IpHead,\r
9166f840 1449 IpVersion,\r
1450 NTOHL (EspHeader->Spi)\r
1451 );\r
6cf9230f 1452\r
9166f840 1453 if (SadEntry == NULL) {\r
1454 Status = EFI_ACCESS_DENIED;\r
1455 goto ON_EXIT;\r
1456 }\r
a3bcde70 1457\r
9166f840 1458 SaId = SadEntry->Id;\r
1459 SadData = SadEntry->Data;\r
a3bcde70 1460\r
9166f840 1461 //\r
1462 // Only support esp protocol currently.\r
1463 //\r
1464 if (SaId->Proto != EfiIPsecESP) {\r
1465 Status = EFI_ACCESS_DENIED;\r
1466 goto ON_EXIT;\r
1467 }\r
a3bcde70 1468\r
9166f840 1469 if (!SadData->ManualSet) {\r
1470 //\r
1471 // TODO: Check SA lifetime and sequence number\r
1472 //\r
1473 }\r
6cf9230f 1474\r
9166f840 1475 //\r
1476 // Allocate buffer for decryption and authentication.\r
1477 //\r
1478 ProcessBuffer = AllocateZeroPool (EspSize);\r
1479 if (ProcessBuffer == NULL) {\r
1480 Status = EFI_OUT_OF_RESOURCES;\r
1481 goto ON_EXIT;\r
1482 }\r
1483\r
1484 NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer);\r
1485\r
1486 //\r
4cb0548d 1487 // Get the IcvSize for authentication and BlockSize/IvSize for Decryption.\r
1488 //\r
1489 IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);\r
1490 IvSize = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
1491 BlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
6cf9230f 1492\r
4cb0548d 1493 //\r
1494 // Make sure the ESP packet is not mal-formt.\r
1495 // 1. Check whether the Espsize is larger than ESP header + IvSize + EspTail + IcvSize.\r
1496 // 2. Check whether the left payload size is multiple of IvSize.\r
1497 //\r
1498 MiscSize = sizeof (EFI_ESP_HEADER) + IvSize + IcvSize;\r
1499 if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL))) {\r
1500 Status = EFI_ACCESS_DENIED;\r
1501 goto ON_EXIT;\r
1502 }\r
1503 if ((EspSize - MiscSize) % BlockSize != 0) {\r
1504 Status = EFI_ACCESS_DENIED;\r
1505 goto ON_EXIT;\r
1506 }\r
1507\r
1508 //\r
1509 // Authenticate the ESP packet.\r
9166f840 1510 //\r
9166f840 1511 if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
1512 Status = IpSecEspAuthVerifyPayload (\r
1513 ProcessBuffer,\r
1514 EspSize,\r
1515 SadEntry,\r
4cb0548d 1516 IcvSize\r
9166f840 1517 );\r
1518 if (EFI_ERROR (Status)) {\r
1519 goto ON_EXIT;\r
1520 }\r
1521 }\r
1522 //\r
1523 // Decrypt the payload by the SAD entry if it has decrypt key.\r
1524 //\r
9166f840 1525 if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
1526 Status = IpSecCryptoIoDecrypt (\r
1527 SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId,\r
1528 SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,\r
1529 SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3,\r
1530 ProcessBuffer + sizeof (EFI_ESP_HEADER),\r
1531 ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize,\r
1532 EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize,\r
1533 ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize\r
1534 );\r
1535 if (EFI_ERROR (Status)) {\r
1536 goto ON_EXIT;\r
1537 }\r
1538 }\r
6cf9230f 1539\r
9166f840 1540 //\r
1541 // Parse EspTail and compute the plain payload size.\r
1542 //\r
1543 EspTail = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL));\r
1544 PaddingSize = EspTail->PaddingLength;\r
1545 NextHeader = EspTail->NextHeader;\r
6cf9230f 1546\r
4cb0548d 1547 if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL) + PaddingSize)) {\r
1548 Status = EFI_ACCESS_DENIED;\r
1549 goto ON_EXIT;\r
1550 }\r
1551 PlainPayloadSize = EspSize - MiscSize - sizeof (EFI_ESP_TAIL) - PaddingSize;\r
6cf9230f 1552\r
9166f840 1553 //\r
1554 // TODO: handle anti-replay window\r
1555 //\r
1556 //\r
1557 // Decryption and authentication with esp has been done, so it's time to\r
1558 // reload the new packet, create recycle event and fixup ip header.\r
1559 //\r
1560 RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));\r
1561 if (RecycleContext == NULL) {\r
1562 Status = EFI_OUT_OF_RESOURCES;\r
1563 goto ON_EXIT;\r
1564 }\r
1565\r
1566 Status = gBS->CreateEvent (\r
1567 EVT_NOTIFY_SIGNAL,\r
1568 TPL_NOTIFY,\r
1569 IpSecRecycleCallback,\r
1570 RecycleContext,\r
1571 RecycleEvent\r
1572 );\r
1573 if (EFI_ERROR (Status)) {\r
1574 goto ON_EXIT;\r
1575 }\r
6cf9230f 1576\r
9166f840 1577 //\r
1578 // The caller will take responsible to handle the original fragment table\r
1579 //\r
1580 *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));\r
1581 if (*FragmentTable == NULL) {\r
1582 Status = EFI_OUT_OF_RESOURCES;\r
1583 goto ON_EXIT;\r
1584 }\r
1585\r
1586 RecycleContext->PayloadBuffer = ProcessBuffer;\r
1587 RecycleContext->FragmentTable = *FragmentTable;\r
6cf9230f 1588\r
9166f840 1589 //\r
1590 // If Tunnel, recalculate upper-layyer PesudoCheckSum and trim the out\r
1591 //\r
1592 if (SadData->Mode == EfiIPsecTunnel) {\r
1593 InnerHead = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;\r
1594 IpSecTunnelInboundPacket (\r
1595 IpHead,\r
1596 InnerHead,\r
1597 IpVersion,\r
1598 SadData,\r
1599 LastHead\r
1600 );\r
6cf9230f 1601\r
9166f840 1602 if (IpVersion == IP_VERSION_4) {\r
1603 (*FragmentTable)[0].FragmentBuffer = InnerHead ;\r
1604 (*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize;\r
6cf9230f 1605\r
1606 }else {\r
9166f840 1607 (*FragmentTable)[0].FragmentBuffer = InnerHead;\r
1608 (*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize;\r
6cf9230f 1609 }\r
9166f840 1610 } else {\r
1611 (*FragmentTable)[0].FragmentBuffer = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;\r
1612 (*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize;\r
1613 }\r
6cf9230f 1614\r
9166f840 1615 *FragmentCount = 1;\r
1616\r
1617 //\r
1618 // Update the total length field in ip header since processed by esp.\r
1619 //\r
0231883b 1620 if (SadData->Mode != EfiIPsecTunnel) {\r
9166f840 1621 if (IpVersion == IP_VERSION_4) {\r
94b928ca 1622 ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + PlainPayloadSize));\r
9166f840 1623 } else {\r
1624 IpSecHeadSize = IpSecGetPlainExtHeadSize (IpHead, LastHead);\r
1625 ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize));\r
1626 }\r
1627 //\r
1628 // Update the next layer field in ip header since esp header inserted.\r
1629 //\r
1630 *LastHead = NextHeader;\r
1631 }\r
6cf9230f 1632\r
9166f840 1633\r
1634 //\r
1635 // Update the SPD association of the SAD entry.\r
1636 //\r
1637 *SpdSelector = SadData->SpdSelector;\r
1638\r
1639ON_EXIT:\r
1640 if (Payload != NULL) {\r
1641 NetbufFree (Payload);\r
1642 }\r
1643\r
1644 if (EFI_ERROR (Status)) {\r
1645 if (ProcessBuffer != NULL) {\r
1646 FreePool (ProcessBuffer);\r
1647 }\r
1648\r
1649 if (RecycleContext != NULL) {\r
1650 FreePool (RecycleContext);\r
1651 }\r
1652\r
1653 if (*RecycleEvent != NULL) {\r
1654 gBS->CloseEvent (*RecycleEvent);\r
1655 }\r
1656 }\r
1657\r
1658 return Status;\r
1659}\r
1660\r
1661/**\r
1662 The actual entry to the relative function processes the output traffic using the ESP protocol.\r
1663\r
1664 This function is the subfunction of IpSecProtectOutboundPacket(). It protected\r
1665 the sending packet by encrypting its payload and inserting ESP header in the orginal\r
1666 IP header, then return the IpHeader and IPsec protected Fragmentable.\r
1667\r
1668 @param[in] IpVersion The version of IP.\r
1669 @param[in, out] IpHead Points to IP header containing the orginal IP header\r
1670 to be processed on input, and inserted ESP header\r
1671 on return.\r
1672 @param[in, out] LastHead The Last Header in IP header.\r
47b27101 1673 @param[in, out] OptionsBuffer Pointer to the options buffer.\r
1674 @param[in, out] OptionsLength Length of the options buffer.\r
9166f840 1675 @param[in, out] FragmentTable Pointer to a list of fragments to be protected by\r
1676 IPsec on input, and with IPsec protected\r
1677 on return.\r
1678 @param[in, out] FragmentCount The number of fragments.\r
1679 @param[in] SadEntry The related SAD entry.\r
1680 @param[out] RecycleEvent The event for recycling of resources.\r
1681\r
1682 @retval EFI_SUCCESS The operation was successful.\r
1683 @retval EFI_OUT_OF_RESOURCES The required system resources can't be allocated.\r
1684\r
1685**/\r
1686EFI_STATUS\r
1687IpSecEspOutboundPacket (\r
1688 IN UINT8 IpVersion,\r
1689 IN OUT VOID *IpHead,\r
1690 IN OUT UINT8 *LastHead,\r
47b27101 1691 IN OUT VOID **OptionsBuffer,\r
1692 IN OUT UINT32 *OptionsLength,\r
9166f840 1693 IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
1694 IN OUT UINT32 *FragmentCount,\r
1695 IN IPSEC_SAD_ENTRY *SadEntry,\r
1696 OUT EFI_EVENT *RecycleEvent\r
1697 )\r
1698{\r
1699 EFI_STATUS Status;\r
1700 UINTN Index;\r
1701 EFI_IPSEC_SA_ID *SaId;\r
1702 IPSEC_SAD_DATA *SadData;\r
1703 IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
1704 UINT8 *ProcessBuffer;\r
1705 UINTN BytesCopied;\r
1706 INTN EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4\r
1707 UINTN EspSize; // Total size of esp wrapped ip payload\r
1708 UINTN IvSize; // Size of IV, optional, might be 0\r
1709 UINTN PlainPayloadSize;// Original IP payload size\r
1710 UINTN PaddingSize; // Size of padding\r
1711 UINTN EncryptSize; // Size of data to be encrypted, start after IV and\r
1712 // stop before ICV\r
1713 UINTN IcvSize; // Size of ICV, optional, might be 0\r
1714 UINT8 *RestOfPayload; // Start of Payload after IV\r
1715 UINT8 *Padding; // Start address of padding\r
1716 EFI_ESP_HEADER *EspHeader; // Start address of ESP frame\r
1717 EFI_ESP_TAIL *EspTail; // Address behind padding\r
1718 UINT8 *InnerHead;\r
1719 HASH_DATA_FRAGMENT HashFragment[1];\r
6cf9230f 1720\r
9166f840 1721 Status = EFI_ACCESS_DENIED;\r
1722 SaId = SadEntry->Id;\r
1723 SadData = SadEntry->Data;\r
1724 ProcessBuffer = NULL;\r
1725 RecycleContext = NULL;\r
1726 *RecycleEvent = NULL;\r
1727 InnerHead = NULL;\r
1728\r
1729 if (!SadData->ManualSet &&\r
1730 SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL &&\r
1731 SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL\r
1732 ) {\r
1733 //\r
1734 // Invalid manual SAD entry configuration.\r
1735 //\r
1736 goto ON_EXIT;\r
1737 }\r
1738\r
1739 //\r
1740 // Create OutHeader according to Inner Header\r
1741 //\r
1742 if (SadData->Mode == EfiIPsecTunnel) {\r
1743 InnerHead = IpSecTunnelOutboundPacket (\r
a3bcde70 1744 IpHead,\r
9166f840 1745 IpVersion,\r
1746 SadData,\r
1747 LastHead,\r
1748 OptionsBuffer,\r
1749 OptionsLength,\r
1750 FragmentTable,\r
1751 FragmentCount\r
a3bcde70 1752 );\r
6cf9230f 1753\r
9166f840 1754 if (InnerHead == NULL) {\r
1755 return EFI_INVALID_PARAMETER;\r
1756 }\r
a3bcde70 1757\r
9166f840 1758 }\r
a3bcde70 1759\r
9166f840 1760 //\r
1761 // Calculate enctrypt block size, need iv by default and 4 bytes alignment.\r
1762 //\r
1763 EncryptBlockSize = 4;\r
1764\r
1765 if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
1766 EncryptBlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
a3bcde70 1767\r
9166f840 1768 if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) {\r
a3bcde70 1769 goto ON_EXIT;\r
9166f840 1770 }\r
1771 }\r
1772\r
1773 //\r
1774 // Calculate the plain payload size accroding to the fragment table.\r
1775 //\r
1776 PlainPayloadSize = 0;\r
1777 for (Index = 0; Index < *FragmentCount; Index++) {\r
1778 PlainPayloadSize += (*FragmentTable)[Index].FragmentLength;\r
1779 }\r
1780\r
1781 //\r
1782 // Add IPHeader size for Tunnel Mode\r
1783 //\r
1784 if (SadData->Mode == EfiIPsecTunnel) {\r
1785 if (IpVersion == IP_VERSION_4) {\r
1786 PlainPayloadSize += sizeof (IP4_HEAD);\r
1787 } else {\r
1788 PlainPayloadSize += sizeof (EFI_IP6_HEADER);\r
1789 }\r
1790 //\r
1791 // OPtions should be encryption into it\r
1792 //\r
6cf9230f 1793 PlainPayloadSize += *OptionsLength;\r
9166f840 1794 }\r
1795\r
a3bcde70 1796\r
9166f840 1797 //\r
1798 // Calculate icv size, optional by default and 4 bytes alignment.\r
1799 //\r
1800 IcvSize = 0;\r
1801 if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
1802 IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);\r
1803 if (IcvSize % 4 != 0) {\r
a3bcde70 1804 goto ON_EXIT;\r
9166f840 1805 }\r
1806 }\r
a3bcde70 1807\r
9166f840 1808 //\r
1809 // Calcuate the total size of esp wrapped ip payload.\r
1810 //\r
1811 IvSize = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
1812 EncryptSize = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize;\r
1813 PaddingSize = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL);\r
1814 EspSize = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize;\r
1815\r
1816 ProcessBuffer = AllocateZeroPool (EspSize);\r
1817 if (ProcessBuffer == NULL) {\r
1818 Status = EFI_OUT_OF_RESOURCES;\r
1819 goto ON_EXIT;\r
1820 }\r
1821\r
1822 //\r
1823 // Calculate esp header and esp tail including header, payload and padding.\r
1824 //\r
1825 EspHeader = (EFI_ESP_HEADER *) ProcessBuffer;\r
1826 RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize;\r
1827 Padding = RestOfPayload + PlainPayloadSize;\r
1828 EspTail = (EFI_ESP_TAIL *) (Padding + PaddingSize);\r
1829\r
1830 //\r
1831 // Fill the sn and spi fields in esp header.\r
1832 //\r
1833 EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1);\r
1834 //EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber);\r
1835 EspHeader->Spi = HTONL (SaId->Spi);\r
1836\r
1837 //\r
1838 // Copy the rest of payload (after iv) from the original fragment buffer.\r
1839 //\r
1840 BytesCopied = 0;\r
1841\r
1842 //\r
1843 // For Tunnel Mode\r
1844 //\r
1845 if (SadData->Mode == EfiIPsecTunnel) {\r
1846 if (IpVersion == IP_VERSION_4) {\r
a3bcde70 1847 //\r
9166f840 1848 // HeadLen, Total Length\r
a3bcde70 1849 //\r
9166f840 1850 ((IP4_HEAD *)InnerHead)->HeadLen = (UINT8) ((sizeof (IP4_HEAD) + *OptionsLength) >> 2);\r
6cf9230f 1851 ((IP4_HEAD *)InnerHead)->TotalLen = HTONS ((UINT16) PlainPayloadSize);\r
9166f840 1852 ((IP4_HEAD *)InnerHead)->Checksum = 0;\r
1853 ((IP4_HEAD *)InnerHead)->Checksum = (UINT16) (~NetblockChecksum (\r
1854 (UINT8 *)InnerHead,\r
1855 sizeof(IP4_HEAD)\r
1856 ));\r
1857 CopyMem (\r
1858 RestOfPayload + BytesCopied,\r
1859 InnerHead,\r
1860 sizeof (IP4_HEAD) + *OptionsLength\r
1861 );\r
1862 BytesCopied += sizeof (IP4_HEAD) + *OptionsLength;\r
1863\r
1864 } else {\r
1865 ((EFI_IP6_HEADER *)InnerHead)->PayloadLength = HTONS ((UINT16) (PlainPayloadSize - sizeof (EFI_IP6_HEADER)));\r
1866 CopyMem (\r
1867 RestOfPayload + BytesCopied,\r
1868 InnerHead,\r
1869 sizeof (EFI_IP6_HEADER) + *OptionsLength\r
1870 );\r
1871 BytesCopied += sizeof (EFI_IP6_HEADER) + *OptionsLength;\r
a3bcde70
HT
1872 }\r
1873 }\r
1874\r
9166f840 1875 for (Index = 0; Index < *FragmentCount; Index++) {\r
1876 CopyMem (\r
1877 (RestOfPayload + BytesCopied),\r
1878 (*FragmentTable)[Index].FragmentBuffer,\r
1879 (*FragmentTable)[Index].FragmentLength\r
1880 );\r
1881 BytesCopied += (*FragmentTable)[Index].FragmentLength;\r
1882 }\r
1883 //\r
1884 // Fill the padding buffer by natural number sequence.\r
1885 //\r
1886 for (Index = 0; Index < PaddingSize; Index++) {\r
1887 Padding[Index] = (UINT8) (Index + 1);\r
1888 }\r
1889 //\r
1890 // Fill the padding length and next header fields in esp tail.\r
1891 //\r
1892 EspTail->PaddingLength = (UINT8) PaddingSize;\r
1893 EspTail->NextHeader = *LastHead;\r
1894\r
1895 //\r
1896 // Fill the next header for Tunnel mode.\r
1897 //\r
1898 if (SadData->Mode == EfiIPsecTunnel) {\r
1899 if (IpVersion == IP_VERSION_4) {\r
1900 EspTail->NextHeader = 4;\r
1901 } else {\r
1902 EspTail->NextHeader = 41;\r
6cf9230f 1903 }\r
9166f840 1904 }\r
1905\r
1906 //\r
1907 // Generate iv at random by crypt library.\r
1908 //\r
1909 Status = IpSecGenerateIv (\r
1910 (UINT8 *) (EspHeader + 1),\r
1911 IvSize\r
1912 );\r
6cf9230f 1913\r
1914\r
9166f840 1915 if (EFI_ERROR (Status)) {\r
1916 goto ON_EXIT;\r
1917 }\r
1918\r
1919 //\r
1920 // Encryption the payload (after iv) by the SAD entry if has encrypt key.\r
1921 //\r
1922 if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
1923 Status = IpSecCryptoIoEncrypt (\r
1924 SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId,\r
1925 SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,\r
1926 SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3,\r
1927 (UINT8 *)(EspHeader + 1),\r
1928 RestOfPayload,\r
1929 EncryptSize,\r
1930 RestOfPayload\r
1931 );\r
1932\r
1933 if (EFI_ERROR (Status)) {\r
1934 goto ON_EXIT;\r
1935 }\r
1936 }\r
1937\r
1938 //\r
1939 // Authenticate the esp wrapped buffer by the SAD entry if it has auth key.\r
1940 //\r
1941 if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
1942\r
1943 HashFragment[0].Data = ProcessBuffer;\r
1944 HashFragment[0].DataSize = EspSize - IcvSize;\r
1945 Status = IpSecCryptoIoHmac (\r
1946 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId,\r
1947 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
1948 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength,\r
1949 HashFragment,\r
1950 1,\r
1951 ProcessBuffer + EspSize - IcvSize,\r
1952 IcvSize\r
1953 );\r
1954 if (EFI_ERROR (Status)) {\r
1955 goto ON_EXIT;\r
1956 }\r
1957 }\r
1958\r
1959 //\r
1960 // Encryption and authentication with esp has been done, so it's time to\r
1961 // reload the new packet, create recycle event and fixup ip header.\r
1962 //\r
1963 RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));\r
1964 if (RecycleContext == NULL) {\r
1965 Status = EFI_OUT_OF_RESOURCES;\r
1966 goto ON_EXIT;\r
1967 }\r
1968\r
1969 Status = gBS->CreateEvent (\r
1970 EVT_NOTIFY_SIGNAL,\r
1971 TPL_NOTIFY,\r
1972 IpSecRecycleCallback,\r
1973 RecycleContext,\r
1974 RecycleEvent\r
1975 );\r
1976 if (EFI_ERROR (Status)) {\r
1977 goto ON_EXIT;\r
1978 }\r
1979 //\r
1980 // Caller take responsible to handle the original fragment table.\r
1981 //\r
1982 *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));\r
1983 if (*FragmentTable == NULL) {\r
1984 Status = EFI_OUT_OF_RESOURCES;\r
1985 goto ON_EXIT;\r
1986 }\r
1987\r
1988 RecycleContext->FragmentTable = *FragmentTable;\r
1989 RecycleContext->PayloadBuffer = ProcessBuffer;\r
1990 (*FragmentTable)[0].FragmentBuffer = ProcessBuffer;\r
1991 (*FragmentTable)[0].FragmentLength = (UINT32) EspSize;\r
1992 *FragmentCount = 1;\r
1993\r
1994 //\r
1995 // Update the total length field in ip header since processed by esp.\r
1996 //\r
1997 if (IpVersion == IP_VERSION_4) {\r
1998 ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + EspSize));\r
1999 } else {\r
2000 ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize);\r
2001 }\r
2002\r
2003 //\r
2004 // If tunnel mode, it should change the outer Ip header with tunnel source address\r
2005 // and destination tunnel address.\r
2006 //\r
2007 if (SadData->Mode == EfiIPsecTunnel) {\r
2008 if (IpVersion == IP_VERSION_4) {\r
2009 CopyMem (\r
6cf9230f 2010 &((IP4_HEAD *) IpHead)->Src,\r
9166f840 2011 &SadData->TunnelSourceAddress.v4,\r
2012 sizeof (EFI_IPv4_ADDRESS)\r
6cf9230f 2013 );\r
9166f840 2014 CopyMem (\r
2015 &((IP4_HEAD *) IpHead)->Dst,\r
2016 &SadData->TunnelDestAddress.v4,\r
2017 sizeof (EFI_IPv4_ADDRESS)\r
2018 );\r
2019 } else {\r
2020 CopyMem (\r
2021 &((EFI_IP6_HEADER *) IpHead)->SourceAddress,\r
2022 &SadData->TunnelSourceAddress.v6,\r
2023 sizeof (EFI_IPv6_ADDRESS)\r
2024 );\r
2025 CopyMem (\r
2026 &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,\r
2027 &SadData->TunnelDestAddress.v6,\r
2028 sizeof (EFI_IPv6_ADDRESS)\r
2029 );\r
2030 }\r
2031 }\r
2032\r
2033 //\r
2034 // Update the next layer field in ip header since esp header inserted.\r
2035 //\r
2036 *LastHead = IPSEC_ESP_PROTOCOL;\r
2037\r
2038 //\r
2039 // Increase the sn number in SAD entry according to rfc4303.\r
2040 //\r
2041 SadData->SequenceNumber++;\r
2042\r
a3bcde70 2043ON_EXIT:\r
9166f840 2044 if (EFI_ERROR (Status)) {\r
2045 if (ProcessBuffer != NULL) {\r
2046 FreePool (ProcessBuffer);\r
2047 }\r
2048\r
2049 if (RecycleContext != NULL) {\r
2050 FreePool (RecycleContext);\r
2051 }\r
2052\r
2053 if (*RecycleEvent != NULL) {\r
2054 gBS->CloseEvent (*RecycleEvent);\r
2055 }\r
2056 }\r
2057\r
a3bcde70
HT
2058 return Status;\r
2059}\r
2060\r
9166f840 2061/**\r
2062 This function processes the inbound traffic with IPsec.\r
2063\r
6cf9230f 2064 It checks the received packet security property, trims the ESP/AH header, and then\r
9166f840 2065 returns without an IPsec protected IP Header and FragmentTable.\r
6cf9230f 2066\r
9166f840 2067 @param[in] IpVersion The version of IP.\r
6cf9230f 2068 @param[in, out] IpHead Points to IP header containing the ESP/AH header\r
9166f840 2069 to be trimed on input, and without ESP/AH header\r
2070 on return.\r
2071 @param[in, out] LastHead The Last Header in IP header on return.\r
47b27101 2072 @param[in, out] OptionsBuffer Pointer to the options buffer.\r
2073 @param[in, out] OptionsLength Length of the options buffer.\r
9166f840 2074 @param[in, out] FragmentTable Pointer to a list of fragments in form of IPsec\r
2075 protected on input, and without IPsec protected\r
2076 on return.\r
2077 @param[in, out] FragmentCount The number of fragments.\r
2078 @param[out] SpdEntry Pointer to contain the address of SPD entry on return.\r
2079 @param[out] RecycleEvent The event for recycling of resources.\r
2080\r
2081 @retval EFI_SUCCESS The operation was successful.\r
2082 @retval EFI_UNSUPPORTED The IPSEC protocol is not supported.\r
2083\r
2084**/\r
2085EFI_STATUS\r
2086IpSecProtectInboundPacket (\r
2087 IN UINT8 IpVersion,\r
2088 IN OUT VOID *IpHead,\r
2089 IN OUT UINT8 *LastHead,\r
47b27101 2090 IN OUT VOID **OptionsBuffer,\r
2091 IN OUT UINT32 *OptionsLength,\r
9166f840 2092 IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
2093 IN OUT UINT32 *FragmentCount,\r
2094 OUT EFI_IPSEC_SPD_SELECTOR **SpdEntry,\r
2095 OUT EFI_EVENT *RecycleEvent\r
2096 )\r
2097{\r
2098 if (*LastHead == IPSEC_ESP_PROTOCOL) {\r
2099 //\r
2100 // Process the esp ipsec header of the inbound traffic.\r
2101 //\r
2102 return IpSecEspInboundPacket (\r
2103 IpVersion,\r
2104 IpHead,\r
2105 LastHead,\r
2106 OptionsBuffer,\r
2107 OptionsLength,\r
2108 FragmentTable,\r
2109 FragmentCount,\r
2110 SpdEntry,\r
2111 RecycleEvent\r
2112 );\r
2113 }\r
2114 //\r
2115 // The other protocols are not supported.\r
2116 //\r
2117 return EFI_UNSUPPORTED;\r
2118}\r
2119\r
2120/**\r
2121 This fucntion processes the output traffic with IPsec.\r
2122\r
2123 It protected the sending packet by encrypting it payload and inserting ESP/AH header\r
2124 in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable.\r
2125\r
2126 @param[in] IpVersion The version of IP.\r
2127 @param[in, out] IpHead Point to IP header containing the orginal IP header\r
2128 to be processed on input, and inserted ESP/AH header\r
2129 on return.\r
2130 @param[in, out] LastHead The Last Header in IP header.\r
47b27101 2131 @param[in, out] OptionsBuffer Pointer to the options buffer.\r
2132 @param[in, out] OptionsLength Length of the options buffer.\r
9166f840 2133 @param[in, out] FragmentTable Pointer to a list of fragments to be protected by\r
2134 IPsec on input, and with IPsec protected\r
2135 on return.\r
2136 @param[in, out] FragmentCount Number of fragments.\r
2137 @param[in] SadEntry Related SAD entry.\r
2138 @param[out] RecycleEvent Event for recycling of resources.\r
2139\r
2140 @retval EFI_SUCCESS The operation is successful.\r
2141 @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported.\r
2142\r
2143**/\r
2144EFI_STATUS\r
2145IpSecProtectOutboundPacket (\r
2146 IN UINT8 IpVersion,\r
2147 IN OUT VOID *IpHead,\r
2148 IN OUT UINT8 *LastHead,\r
47b27101 2149 IN OUT VOID **OptionsBuffer,\r
2150 IN OUT UINT32 *OptionsLength,\r
9166f840 2151 IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
2152 IN OUT UINT32 *FragmentCount,\r
2153 IN IPSEC_SAD_ENTRY *SadEntry,\r
2154 OUT EFI_EVENT *RecycleEvent\r
2155 )\r
2156{\r
2157 if (SadEntry->Id->Proto == EfiIPsecESP) {\r
2158 //\r
2159 // Process the esp ipsec header of the outbound traffic.\r
2160 //\r
2161 return IpSecEspOutboundPacket (\r
2162 IpVersion,\r
2163 IpHead,\r
2164 LastHead,\r
2165 OptionsBuffer,\r
2166 OptionsLength,\r
2167 FragmentTable,\r
2168 FragmentCount,\r
2169 SadEntry,\r
2170 RecycleEvent\r
2171 );\r
2172 }\r
2173 //\r
2174 // The other protocols are not supported.\r
2175 //\r
2176 return EFI_UNSUPPORTED;\r
2177}\r