]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/IpSecDxe/IpSecSaEngine.c
Add NetworkPkg (P.UDK2010.UP3.Network.P1)
[mirror_edk2.git] / NetworkPkg / IpSecDxe / IpSecSaEngine.c
CommitLineData
a3bcde70
HT
1/** @file\r
2 IPsec inbound and outbound traffic processing.\r
3\r
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
5\r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php.\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "IpSecImpl.h"\r
17#include "IpSecDebug.h"\r
18#include "IpSecCryptIo.h"\r
19\r
20extern LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum];\r
21\r
22/**\r
23 The call back function of NetbufFromExt.\r
24\r
25 @param[in] Arg The argument passed from the caller.\r
26\r
27**/\r
28VOID\r
29EFIAPI\r
30IpSecOnRecyclePacket (\r
31 IN VOID *Arg\r
32 )\r
33{\r
34}\r
35\r
36/**\r
37 This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP\r
38 is released.\r
39\r
40 @param[in] Event The related event.\r
41 @param[in] Context The data passed by the caller.\r
42\r
43**/\r
44VOID\r
45EFIAPI\r
46IpSecRecycleCallback (\r
47 IN EFI_EVENT Event,\r
48 IN VOID *Context\r
49 )\r
50{\r
51 IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
52\r
53 RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context;\r
54\r
55 if (RecycleContext->FragmentTable != NULL) {\r
56 FreePool (RecycleContext->FragmentTable);\r
57 }\r
58\r
59 if (RecycleContext->PayloadBuffer != NULL) {\r
60 FreePool (RecycleContext->PayloadBuffer);\r
61 }\r
62\r
63 FreePool (RecycleContext);\r
64 gBS->CloseEvent (Event);\r
65\r
66}\r
67\r
68/**\r
69 Calculate the extension header of IP. The return length only doesn't contain\r
70 the fixed IP header length.\r
71\r
72 @param[in] IpHead Points to an IP head to be calculated.\r
73 @param[in] LastHead Points to the last header of the IP header.\r
74\r
75 @return The length of the extension header.\r
76\r
77**/\r
78UINT16\r
79IpSecGetPlainExtHeadSize (\r
80 IN VOID *IpHead,\r
81 IN UINT8 *LastHead\r
82 )\r
83{\r
84 UINT16 Size;\r
85\r
86 Size = (UINT16) (LastHead - (UINT8 *) IpHead);\r
87\r
88 if (Size > sizeof (EFI_IP6_HEADER)) {\r
89 //\r
90 // * (LastHead+1) point the last header's length but not include the first\r
91 // 8 octers, so this formluation add 8 at the end.\r
92 //\r
93 Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8);\r
94 } else {\r
95 Size = 0;\r
96 }\r
97\r
98 return Size;\r
99}\r
100\r
101/**\r
102 Authenticate the IpSec Payload and store the result in the IcvBuffer.\r
103\r
104 @param[in] BufferToAuth The buffer to be Authenticated.\r
105 @param[in] AuthSize The size of the buffer to be Authenticated.\r
106 @param[in, out] IcvBuffer The buffer to store the ICV.\r
107 @param[in] IcvSize The size of ICV.\r
108 @param[in] Key The Key passed to the CryptLib to generate a\r
109 CRYPT_HANDLE.\r
110 @param[in] AuthAlgId The Authentication Algorithm ID.\r
111\r
112 @retval EFI_UNSUPPORTED If the AuthAlg is not in the support list.\r
113 @retval EFI_SUCCESS Authenticated the payload successfully.\r
114 @retval otherwise Authentication of the payload failed.\r
115**/\r
116EFI_STATUS\r
117IpSecAuthPayload (\r
118 IN UINT8 *BufferToAuth,\r
119 IN UINTN AuthSize,\r
120 IN OUT UINT8 *IcvBuffer,\r
121 IN UINTN IcvSize,\r
122 IN VOID *Key,\r
123 IN UINT8 AuthAlgId\r
124 )\r
125{\r
126 switch (AuthAlgId) {\r
127 case EFI_IPSEC_AALG_NONE :\r
128 case EFI_IPSEC_AALG_NULL :\r
129 return EFI_SUCCESS;\r
130\r
131 default:\r
132 return EFI_UNSUPPORTED;\r
133 }\r
134}\r
135\r
136/**\r
137 Verify if the Authentication payload is correct.\r
138\r
139 @param[in] EspBuffer Points to the ESP wrapped buffer.\r
140 @param[in] EspSize The size of the ESP wrapped buffer.\r
141 @param[in] SadEntry The related SAD entry to store the authentication\r
142 algorithm key.\r
143 @param[in] IcvSize The length of ICV.\r
144\r
145 @retval EFI_SUCCESS The authentication data is correct.\r
146 @retval EFI_ACCESS_DENIED The authentication data is not correct.\r
147\r
148**/\r
149EFI_STATUS\r
150IpSecEspAuthVerifyPayload (\r
151 IN UINT8 *EspBuffer,\r
152 IN UINTN EspSize,\r
153 IN IPSEC_SAD_ENTRY *SadEntry,\r
154 IN UINTN *IcvSize\r
155 )\r
156{\r
157 EFI_STATUS Status;\r
158 UINTN AuthSize;\r
159 UINT8 IcvBuffer[12];\r
160\r
161 //\r
162 // Calculate the size of authentication payload.\r
163 //\r
164 *IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);\r
165 AuthSize = EspSize - *IcvSize;\r
166\r
167 //\r
168 // Calculate the icv buffer and size of the payload.\r
169 //\r
170 Status = IpSecAuthPayload (\r
171 EspBuffer,\r
172 AuthSize,\r
173 IcvBuffer,\r
174 *IcvSize,\r
175 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
176 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId\r
177 );\r
178 if (EFI_ERROR (Status)) {\r
179 return Status;\r
180 }\r
181 //\r
182 // Compare the calculated icv and the appended original icv.\r
183 //\r
184 if (CompareMem (EspBuffer + AuthSize, IcvBuffer, *IcvSize) == 0) {\r
185 return EFI_SUCCESS;\r
186 }\r
187\r
188 DEBUG ((DEBUG_ERROR, "Error auth verify payload\n"));\r
189 return EFI_ACCESS_DENIED;\r
190}\r
191\r
192/**\r
193 ESP Decrypt the payload.\r
194\r
195 @param[in, out] PayloadBuffer Pointer to the buffer containing the ESP wrapped;\r
196 to be decrypted on input, and plaintext on return. The\r
197 number of bytes of data to be decrypted is\r
198 specified by EncryptSize.\r
199 @param[in] EncryptSize The size of the PayloadBuffer as input.\r
200 @param[in] SadEntry The related SAD entry.\r
201 @param[in] IvSize The size of IV.\r
202 @param[out] PlainPayloadSize Contains the return value of decrypted size.\r
203 @param[out] PaddingSize Contains the return value of Padding size.\r
204 @param[out] NextHeader Contains the return value of the last protocol header\r
205 of the IP packet.\r
206\r
207 @retval EFI_UNSUPPORTED The Algorithm pointed to by the SAD entry is not supported.\r
208 @retval EFI_SUCCESS The operation completed successfully.\r
209\r
210**/\r
211EFI_STATUS\r
212IpSecEspDecryptPayload (\r
213 IN OUT UINT8 *PayloadBuffer,\r
214 IN UINTN EncryptSize,\r
215 IN IPSEC_SAD_ENTRY *SadEntry,\r
216 IN UINTN *IvSize,\r
217 OUT UINTN *PlainPayloadSize,\r
218 OUT UINTN *PaddingSize,\r
219 OUT UINT8 *NextHeader\r
220 )\r
221{\r
222 EFI_ESP_TAIL *EspTail;\r
223\r
224 switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) {\r
225 case EFI_IPSEC_EALG_NULL:\r
226 EspTail = (EFI_ESP_TAIL *) (PayloadBuffer + EncryptSize - sizeof (EFI_ESP_TAIL));\r
227 *PaddingSize = EspTail->PaddingLength;\r
228 *NextHeader = EspTail->NextHeader;\r
229 *PlainPayloadSize = EncryptSize - EspTail->PaddingLength - sizeof (EFI_ESP_TAIL);\r
230 break;\r
231\r
232 case EFI_IPSEC_EALG_3DESCBC:\r
233 case EFI_IPSEC_EALG_AESCBC:\r
234 //\r
235 // TODO: support these algorithm\r
236 //\r
237 return EFI_UNSUPPORTED;\r
238 default :\r
239 return EFI_UNSUPPORTED;\r
240 }\r
241\r
242 return EFI_SUCCESS;\r
243}\r
244\r
245/**\r
246 ESP Encrypt the payload.\r
247\r
248 @param[in, out] BufferToEncrypt Pointer to the buffer containing plaintext to be\r
249 encrypted on input, and ciphertext on return. The\r
250 number of bytes of data to be encrypted is\r
251 specified by EncryptSize.\r
252 @param[in, out] EncryptSize The size of the plaintext on input, and the size of the\r
253 ciphertext on return.\r
254 @param[in] IvBuffer Points to IV data.\r
255 @param[in] IvSize Size of IV.\r
256 @param[in] SadEntry Related SAD entry.\r
257\r
258 @retval EFI_UNSUPPORTED The Algorithm pointed by SAD entry is not supported.\r
259 @retval EFI_SUCCESS The operation completed successfully.\r
260\r
261**/\r
262EFI_STATUS\r
263IpSecEspEncryptPayload (\r
264 IN OUT UINT8 *BufferToEncrypt,\r
265 IN OUT UINTN EncryptSize,\r
266 IN UINT8 *IvBuffer,\r
267 IN UINTN IvSize,\r
268 IN IPSEC_SAD_ENTRY *SadEntry\r
269 )\r
270{\r
271 switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) {\r
272 case EFI_IPSEC_EALG_NULL:\r
273 return EFI_SUCCESS;\r
274\r
275 case EFI_IPSEC_EALG_3DESCBC:\r
276 case EFI_IPSEC_EALG_AESCBC:\r
277 //\r
278 // TODO: support these algorithms\r
279 //\r
280 return EFI_UNSUPPORTED;\r
281 default :\r
282 return EFI_UNSUPPORTED;\r
283\r
284 }\r
285}\r
286\r
287/**\r
288 The actual entry to relative function processes the inbound traffic of ESP header.\r
289\r
290 This function is the subfunction of IpSecProtectInboundPacket(). It checks the\r
291 received packet security property and trim the ESP header and then returns without\r
292 an IPsec protected IP Header and FramgmentTable.\r
293\r
294 @param[in] IpVersion The version of IP.\r
295 @param[in, out] IpHead Points to the IP header containing the ESP header\r
296 to be trimed on input, and without ESP header\r
297 on return.\r
298 @param[out] LastHead The Last Header in IP header on return.\r
299 @param[in] OptionsBuffer Pointer to the options buffer. It is optional.\r
300 @param[in] OptionsLength Length of the options buffer. It is optional.\r
301 @param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec\r
302 protected on input, and without IPsec protected\r
303 on return.\r
304 @param[in] FragmentCount The number of fragments.\r
305 @param[out] SpdEntry Pointer to contain the address of SPD entry on return.\r
306 @param[out] RecycleEvent The event for recycling of resources.\r
307\r
308 @retval EFI_SUCCESS The operation was successful.\r
309 @retval EFI_ACCESS_DENIED One or more following conditions is TRUE:\r
310 - ESP header was not found.\r
311 - The related SAD entry was not found.\r
312 - The related SAD entry does not support the ESP protocol.\r
313 @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated.\r
314\r
315**/\r
316EFI_STATUS\r
317IpSecEspInboundPacket (\r
318 IN UINT8 IpVersion,\r
319 IN OUT VOID *IpHead,\r
320 OUT UINT8 *LastHead,\r
321 IN VOID *OptionsBuffer, OPTIONAL\r
322 IN UINT32 OptionsLength, OPTIONAL\r
323 IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
324 IN UINT32 *FragmentCount,\r
325 OUT IPSEC_SPD_ENTRY **SpdEntry,\r
326 OUT EFI_EVENT *RecycleEvent\r
327 )\r
328{\r
329 EFI_STATUS Status;\r
330 NET_BUF *Payload;\r
331 UINTN EspSize;\r
332 UINTN IvSize;\r
333 UINTN PlainPayloadSize;\r
334 UINTN PaddingSize;\r
335 UINTN IcvSize;\r
336 UINT8 *ProcessBuffer;\r
337 EFI_IP_ADDRESS DestIp;\r
338 EFI_ESP_HEADER *EspHeader;\r
339 EFI_ESP_TAIL *EspTail;\r
340 EFI_IPSEC_SA_ID *SaId;\r
341 IPSEC_SAD_DATA *SadData;\r
342 IPSEC_SAD_ENTRY *SadEntry;\r
343 IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
344 UINT32 Spi;\r
345 UINT8 NextHeader;\r
346 UINT16 IpSecHeadSize;\r
347\r
348 Status = EFI_SUCCESS;\r
349 Payload = NULL;\r
350 ProcessBuffer = NULL;\r
351 RecycleContext = NULL;\r
352 *RecycleEvent = NULL;\r
353 PlainPayloadSize = 0;\r
354 NextHeader = 0;\r
355 //\r
356 // Build netbuf from fragment table first.\r
357 //\r
358 Payload = NetbufFromExt (\r
359 (NET_FRAGMENT *) *FragmentTable,\r
360 *FragmentCount,\r
361 0,\r
362 sizeof (EFI_ESP_HEADER),\r
363 IpSecOnRecyclePacket,\r
364 NULL\r
365 );\r
366 if (Payload == NULL) {\r
367 Status = EFI_OUT_OF_RESOURCES;\r
368 goto ON_EXIT;\r
369 }\r
370 //\r
371 // Get the esp size and eso header from netbuf.\r
372 //\r
373 EspSize = Payload->TotalSize;\r
374 EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL);\r
375 if (EspHeader == NULL) {\r
376 Status = EFI_ACCESS_DENIED;\r
377 goto ON_EXIT;\r
378 }\r
379 //\r
380 // Parse destination address from ip header.\r
381 //\r
382 ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));\r
383 if (IpVersion == IP_VERSION_4) {\r
384 CopyMem (\r
385 &DestIp,\r
386 &((IP4_HEAD *) IpHead)->Dst,\r
387 sizeof (IP4_ADDR)\r
388 );\r
389 } else {\r
390 CopyMem (\r
391 &DestIp,\r
392 &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,\r
393 sizeof (EFI_IPv6_ADDRESS)\r
394 );\r
395 }\r
396 //\r
397 // Lookup sad entry according to the spi and dest address.\r
398 //\r
399 Spi = NTOHL (EspHeader->Spi);\r
400 SadEntry = IpSecLookupSadBySpi (Spi, &DestIp);\r
401 if (SadEntry == NULL) {\r
402 Status = EFI_ACCESS_DENIED;\r
403 goto ON_EXIT;\r
404 }\r
405\r
406 SaId = SadEntry->Id;\r
407 SadData = SadEntry->Data;\r
408\r
409 //\r
410 // Only support esp protocol currently.\r
411 //\r
412 if (SaId->Proto != EfiIPsecESP) {\r
413 Status = EFI_ACCESS_DENIED;\r
414 goto ON_EXIT;\r
415 }\r
416\r
417 if (!SadData->ManualSet) {\r
418 //\r
419 // TODO: Check sa lifetime and sequence number\r
420 //\r
421 }\r
422 //\r
423 // Allocate buffer for decryption and authentication by esp.\r
424 //\r
425 ProcessBuffer = AllocateZeroPool (EspSize);\r
426 if (ProcessBuffer == NULL) {\r
427 Status = EFI_OUT_OF_RESOURCES;\r
428 goto ON_EXIT;\r
429 }\r
430\r
431 NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer);\r
432\r
433 //\r
434 // Authenticate the esp wrapped buffer by the sad entry if has auth key.\r
435 //\r
436 IcvSize = 0;\r
437 if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
438 Status = IpSecEspAuthVerifyPayload (\r
439 ProcessBuffer,\r
440 EspSize,\r
441 SadEntry,\r
442 &IcvSize\r
443 );\r
444 if (EFI_ERROR (Status)) {\r
445 goto ON_EXIT;\r
446 }\r
447 }\r
448 //\r
449 // Decrypt the payload by the sad entry if has decrypt key.\r
450 //\r
451 IvSize = 0;\r
452 if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
453 Status = IpSecEspDecryptPayload (\r
454 ProcessBuffer + sizeof (EFI_ESP_HEADER),\r
455 EspSize - sizeof (EFI_ESP_HEADER) - IcvSize,\r
456 SadEntry,\r
457 &IvSize,\r
458 &PlainPayloadSize,\r
459 &PaddingSize,\r
460 &NextHeader\r
461 );\r
462 if (EFI_ERROR (Status)) {\r
463 goto ON_EXIT;\r
464 }\r
465 } else {\r
466 EspTail = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL));\r
467 PaddingSize = EspTail->PaddingLength;\r
468 NextHeader = EspTail->NextHeader;\r
469 PlainPayloadSize = EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize - sizeof (EFI_ESP_TAIL) - PaddingSize;\r
470 }\r
471 //\r
472 // TODO: handle anti-replay window\r
473 //\r
474 //\r
475 // Decryption and authentication with esp has been done, so it's time to\r
476 // reload the new packet, create recycle event and fixup ip header.\r
477 //\r
478 RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));\r
479 if (RecycleContext == NULL) {\r
480 Status = EFI_OUT_OF_RESOURCES;\r
481 goto ON_EXIT;\r
482 }\r
483\r
484 Status = gBS->CreateEvent (\r
485 EVT_NOTIFY_SIGNAL,\r
486 TPL_NOTIFY,\r
487 IpSecRecycleCallback,\r
488 RecycleContext,\r
489 RecycleEvent\r
490 );\r
491 if (EFI_ERROR (Status)) {\r
492 goto ON_EXIT;\r
493 }\r
494 //\r
495 // TODO: Who take responsible to handle the original fragment table?\r
496 //\r
497 *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));\r
498 if (*FragmentTable == NULL) {\r
499 Status = EFI_OUT_OF_RESOURCES;\r
500 goto ON_EXIT;\r
501 }\r
502\r
503 RecycleContext->PayloadBuffer = ProcessBuffer;\r
504 RecycleContext->FragmentTable = *FragmentTable;\r
505 (*FragmentTable)[0].FragmentBuffer = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;\r
506 (*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize;\r
507 *FragmentCount = 1;\r
508\r
509 //\r
510 // Update the total length field in ip header since processed by esp.\r
511 //\r
512 if (IpVersion == IP_VERSION_4) {\r
513 ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + PlainPayloadSize));\r
514 } else {\r
515 IpSecHeadSize = IpSecGetPlainExtHeadSize (IpHead, LastHead);\r
516 ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize));\r
517 }\r
518 //\r
519 // Update the next layer field in ip header since esp header inserted.\r
520 //\r
521 *LastHead = NextHeader;\r
522\r
523 //\r
524 // Update the spd association of the sad entry.\r
525 //\r
526 *SpdEntry = SadData->SpdEntry;\r
527\r
528ON_EXIT:\r
529 if (Payload != NULL) {\r
530 NetbufFree (Payload);\r
531 }\r
532\r
533 if (EFI_ERROR (Status)) {\r
534 if (ProcessBuffer != NULL) {\r
535 FreePool (ProcessBuffer);\r
536 }\r
537\r
538 if (RecycleContext != NULL) {\r
539 FreePool (RecycleContext);\r
540 }\r
541\r
542 if (*RecycleEvent != NULL) {\r
543 gBS->CloseEvent (*RecycleEvent);\r
544 }\r
545 }\r
546\r
547 return Status;\r
548}\r
549\r
550/**\r
551 The actual entry to the relative function processes the output traffic using the ESP protocol.\r
552\r
553 This function is the subfunction of IpSecProtectOutboundPacket(). It protected\r
554 the sending packet by encrypting its payload and inserting ESP header in the orginal\r
555 IP header, then return the IpHeader and IPsec protected Fragmentable.\r
556\r
557 @param[in] IpVersion The version of IP.\r
558 @param[in, out] IpHead Points to IP header containing the orginal IP header\r
559 to be processed on input, and inserted ESP header\r
560 on return.\r
561 @param[in] LastHead The Last Header in IP header.\r
562 @param[in] OptionsBuffer Pointer to the options buffer. It is optional.\r
563 @param[in] OptionsLength Length of the options buffer. It is optional.\r
564 @param[in, out] FragmentTable Pointer to a list of fragments to be protected by\r
565 IPsec on input, and with IPsec protected\r
566 on return.\r
567 @param[in] FragmentCount The number of fragments.\r
568 @param[in] SadEntry The related SAD entry.\r
569 @param[out] RecycleEvent The event for recycling of resources.\r
570\r
571 @retval EFI_SUCCESS The operation was successful.\r
572 @retval EFI_OUT_OF_RESOURCES The required system resources can't be allocated.\r
573\r
574**/\r
575EFI_STATUS\r
576IpSecEspOutboundPacket (\r
577 IN UINT8 IpVersion,\r
578 IN OUT VOID *IpHead,\r
579 IN UINT8 *LastHead,\r
580 IN VOID *OptionsBuffer, OPTIONAL\r
581 IN UINT32 OptionsLength, OPTIONAL\r
582 IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
583 IN UINT32 *FragmentCount,\r
584 IN IPSEC_SAD_ENTRY *SadEntry,\r
585 OUT EFI_EVENT *RecycleEvent\r
586 )\r
587{\r
588 EFI_STATUS Status;\r
589 UINTN Index;\r
590 EFI_IPSEC_SA_ID *SaId;\r
591 IPSEC_SAD_DATA *SadData;\r
592 IPSEC_RECYCLE_CONTEXT *RecycleContext;\r
593 UINT8 *ProcessBuffer;\r
594 UINTN BytesCopied;\r
595 INTN EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4\r
596 UINTN EspSize; // Total size of esp wrapped ip payload\r
597 UINTN IvSize; // Size of IV, optional, might be 0\r
598 UINTN PlainPayloadSize;// Original IP payload size\r
599 UINTN PaddingSize; // Size of padding\r
600 UINTN EncryptSize; // Size of data to be encrypted, start after IV and\r
601 // stop before ICV\r
602 UINTN IcvSize; // Size of ICV, optional, might be 0\r
603 UINT8 *RestOfPayload; // Start of Payload after IV\r
604 UINT8 *Padding; // Start address of padding\r
605 EFI_ESP_HEADER *EspHeader; // Start address of ESP frame\r
606 EFI_ESP_TAIL *EspTail; // Address behind padding\r
607\r
608 Status = EFI_ACCESS_DENIED;\r
609 SaId = SadEntry->Id;\r
610 SadData = SadEntry->Data;\r
611 ProcessBuffer = NULL;\r
612 RecycleContext = NULL;\r
613 *RecycleEvent = NULL;\r
614\r
615 if (!SadData->ManualSet &&\r
616 SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL &&\r
617 SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL\r
618 ) {\r
619 //\r
620 // Invalid manual sad entry configuration.\r
621 //\r
622 goto ON_EXIT;\r
623 }\r
624 //\r
625 // Calculate enctrypt block size, need iv by default and 4 bytes alignment.\r
626 //\r
627 EncryptBlockSize = 4;\r
628\r
629 if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
630 EncryptBlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
631\r
632 if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) {\r
633 goto ON_EXIT;\r
634 }\r
635 }\r
636 //\r
637 // Calculate the plain payload size accroding to the fragment table.\r
638 //\r
639 PlainPayloadSize = 0;\r
640 for (Index = 0; Index < *FragmentCount; Index++) {\r
641 PlainPayloadSize += (*FragmentTable)[Index].FragmentLength;\r
642 }\r
643 //\r
644 // Calculate icv size, optional by default and 4 bytes alignment.\r
645 //\r
646 IcvSize = 0;\r
647 if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
648 IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);\r
649 if (IcvSize % 4 != 0) {\r
650 goto ON_EXIT;\r
651 }\r
652 }\r
653 //\r
654 // Calcuate the total size of esp wrapped ip payload.\r
655 //\r
656 IvSize = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);\r
657 EncryptSize = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize;\r
658 PaddingSize = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL);\r
659 EspSize = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize;\r
660\r
661 ProcessBuffer = AllocateZeroPool (EspSize);\r
662 if (ProcessBuffer == NULL) {\r
663 Status = EFI_OUT_OF_RESOURCES;\r
664 goto ON_EXIT;\r
665 }\r
666 //\r
667 // Calculate esp header and esp tail including header, payload and padding.\r
668 //\r
669 EspHeader = (EFI_ESP_HEADER *) ProcessBuffer;\r
670 RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize;\r
671 Padding = RestOfPayload + PlainPayloadSize;\r
672 EspTail = (EFI_ESP_TAIL *) (Padding + PaddingSize);\r
673\r
674 //\r
675 // Fill the sn and spi fields in esp header.\r
676 //\r
677 EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1);\r
678 EspHeader->Spi = HTONL (SaId->Spi);\r
679\r
680 //\r
681 // Copy the rest of payload (after iv) from the original fragment buffer.\r
682 //\r
683 BytesCopied = 0;\r
684 for (Index = 0; Index < *FragmentCount; Index++) {\r
685 CopyMem (\r
686 (RestOfPayload + BytesCopied),\r
687 (*FragmentTable)[Index].FragmentBuffer,\r
688 (*FragmentTable)[Index].FragmentLength\r
689 );\r
690 BytesCopied += (*FragmentTable)[Index].FragmentLength;\r
691 }\r
692 //\r
693 // Fill the padding buffer by natural number sequence.\r
694 //\r
695 for (Index = 0; Index < PaddingSize; Index++) {\r
696 Padding[Index] = (UINT8) (Index + 1);\r
697 }\r
698 //\r
699 // Fill the padding length and next header fields in esp tail.\r
700 //\r
701 EspTail->PaddingLength = (UINT8) PaddingSize;\r
702 EspTail->NextHeader = *LastHead;\r
703\r
704 //\r
705 // Generate iv at random by crypt library.\r
706 //\r
707 Status = IpSecGenerateIv (\r
708 (UINT8 *) (EspHeader + 1),\r
709 IvSize\r
710 );\r
711\r
712\r
713 if (EFI_ERROR (Status)) {\r
714 goto ON_EXIT;\r
715 }\r
716 //\r
717 // Encrypt the payload (after iv) by the sad entry if has encrypt key.\r
718 //\r
719 if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {\r
720 Status = IpSecEspEncryptPayload (\r
721 RestOfPayload,\r
722 EncryptSize,\r
723 (UINT8 *) (EspHeader + 1),\r
724 IvSize,\r
725 SadEntry\r
726 );\r
727 if (EFI_ERROR (Status)) {\r
728 goto ON_EXIT;\r
729 }\r
730 }\r
731 //\r
732 // Authenticate the esp wrapped buffer by the sad entry if has auth key.\r
733 //\r
734 if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {\r
735 Status = IpSecAuthPayload (\r
736 ProcessBuffer,\r
737 EspSize - IcvSize,\r
738 ProcessBuffer + EspSize - IcvSize,\r
739 IcvSize,\r
740 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,\r
741 SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId\r
742 );\r
743 if (EFI_ERROR (Status)) {\r
744 goto ON_EXIT;\r
745 }\r
746 }\r
747 //\r
748 // Encryption and authentication with esp has been done, so it's time to\r
749 // reload the new packet, create recycle event and fixup ip header.\r
750 //\r
751 RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));\r
752 if (RecycleContext == NULL) {\r
753 Status = EFI_OUT_OF_RESOURCES;\r
754 goto ON_EXIT;\r
755 }\r
756\r
757 Status = gBS->CreateEvent (\r
758 EVT_NOTIFY_SIGNAL,\r
759 TPL_NOTIFY,\r
760 IpSecRecycleCallback,\r
761 RecycleContext,\r
762 RecycleEvent\r
763 );\r
764 if (EFI_ERROR (Status)) {\r
765 goto ON_EXIT;\r
766 }\r
767 //\r
768 // TODO: Who take responsible to handle the original fragment table?\r
769 //\r
770 *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));\r
771 if (*FragmentTable == NULL) {\r
772 Status = EFI_OUT_OF_RESOURCES;\r
773 goto ON_EXIT;\r
774 }\r
775\r
776 RecycleContext->FragmentTable = *FragmentTable;\r
777 RecycleContext->PayloadBuffer = ProcessBuffer;\r
778 (*FragmentTable)[0].FragmentBuffer = ProcessBuffer;\r
779 (*FragmentTable)[0].FragmentLength = (UINT32) EspSize;\r
780 *FragmentCount = 1;\r
781\r
782 //\r
783 // Update the total length field in ip header since processed by esp.\r
784 //\r
785 if (IpVersion == IP_VERSION_4) {\r
786 ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + EspSize));\r
787 } else {\r
788 ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize);\r
789 }\r
790 //\r
791 // Update the next layer field in ip header since esp header inserted.\r
792 //\r
793 *LastHead = IPSEC_ESP_PROTOCOL;\r
794\r
795 //\r
796 // Increase the sn number in sad entry according to rfc4303.\r
797 //\r
798 SadData->SequenceNumber++;\r
799\r
800ON_EXIT:\r
801 if (EFI_ERROR (Status)) {\r
802 if (ProcessBuffer != NULL) {\r
803 FreePool (ProcessBuffer);\r
804 }\r
805\r
806 if (RecycleContext != NULL) {\r
807 FreePool (RecycleContext);\r
808 }\r
809\r
810 if (*RecycleEvent != NULL) {\r
811 gBS->CloseEvent (*RecycleEvent);\r
812 }\r
813 }\r
814\r
815 return Status;\r
816}\r
817\r
818/**\r
819 This function processes the inbound traffic with IPsec.\r
820\r
821 It checks the received packet security property, trims the ESP/AH header, and then\r
822 returns without an IPsec protected IP Header and FragmentTable.\r
823\r
824 @param[in] IpVersion The version of IP.\r
825 @param[in, out] IpHead Points to IP header containing the ESP/AH header\r
826 to be trimed on input, and without ESP/AH header\r
827 on return.\r
828 @param[in] LastHead The Last Header in IP header on return.\r
829 @param[in] OptionsBuffer Pointer to the options buffer. It is optional.\r
830 @param[in] OptionsLength Length of the options buffer. It is optional.\r
831 @param[in, out] FragmentTable Pointer to a list of fragments in form of IPsec\r
832 protected on input, and without IPsec protected\r
833 on return.\r
834 @param[in] FragmentCount The number of fragments.\r
835 @param[out] SpdEntry Pointer to contain the address of SPD entry on return.\r
836 @param[out] RecycleEvent The event for recycling of resources.\r
837\r
838 @retval EFI_SUCCESS The operation was successful.\r
839 @retval EFI_UNSUPPORTED The IPSEC protocol is not supported.\r
840\r
841**/\r
842EFI_STATUS\r
843IpSecProtectInboundPacket (\r
844 IN UINT8 IpVersion,\r
845 IN OUT VOID *IpHead,\r
846 IN UINT8 *LastHead,\r
847 IN VOID *OptionsBuffer, OPTIONAL\r
848 IN UINT32 OptionsLength, OPTIONAL\r
849 IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
850 IN UINT32 *FragmentCount,\r
851 OUT IPSEC_SPD_ENTRY **SpdEntry,\r
852 OUT EFI_EVENT *RecycleEvent\r
853 )\r
854{\r
855 if (*LastHead == IPSEC_ESP_PROTOCOL) {\r
856 //\r
857 // Process the esp ipsec header of the inbound traffic.\r
858 //\r
859 return IpSecEspInboundPacket (\r
860 IpVersion,\r
861 IpHead,\r
862 LastHead,\r
863 OptionsBuffer,\r
864 OptionsLength,\r
865 FragmentTable,\r
866 FragmentCount,\r
867 SpdEntry,\r
868 RecycleEvent\r
869 );\r
870 }\r
871 //\r
872 // The other protocols are not supported.\r
873 //\r
874 return EFI_UNSUPPORTED;\r
875}\r
876\r
877/**\r
878 This function processes the output traffic with IPsec.\r
879\r
880 It protected the sending packet by encrypting it payload and inserting ESP/AH header\r
881 in the orginal IP header, then returns the IpHeader and IPsec protected Fragmentable.\r
882\r
883 @param[in] IpVersion The version of IP.\r
884 @param[in, out] IpHead Points to IP header containing the orginal IP header\r
885 to be processed on input, and inserted ESP/AH header\r
886 on return.\r
887 @param[in] LastHead The Last Header in the IP header.\r
888 @param[in] OptionsBuffer Pointer to the options buffer. It is optional.\r
889 @param[in] OptionsLength Length of the options buffer. It is optional.\r
890 @param[in, out] FragmentTable Pointer to a list of fragments to be protected by\r
891 IPsec on input, and with IPsec protected\r
892 on return.\r
893 @param[in] FragmentCount The number of fragments.\r
894 @param[in] SadEntry The related SAD entry.\r
895 @param[out] RecycleEvent The event for recycling of resources.\r
896\r
897 @retval EFI_SUCCESS The operation was successful.\r
898 @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported.\r
899\r
900**/\r
901EFI_STATUS\r
902IpSecProtectOutboundPacket (\r
903 IN UINT8 IpVersion,\r
904 IN OUT VOID *IpHead,\r
905 IN UINT8 *LastHead,\r
906 IN VOID *OptionsBuffer, OPTIONAL\r
907 IN UINT32 OptionsLength, OPTIONAL\r
908 IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,\r
909 IN UINT32 *FragmentCount,\r
910 IN IPSEC_SAD_ENTRY *SadEntry,\r
911 OUT EFI_EVENT *RecycleEvent\r
912 )\r
913{\r
914 if (SadEntry->Id->Proto == EfiIPsecESP) {\r
915 //\r
916 // Process the esp ipsec header of the outbound traffic.\r
917 //\r
918 return IpSecEspOutboundPacket (\r
919 IpVersion,\r
920 IpHead,\r
921 LastHead,\r
922 OptionsBuffer,\r
923 OptionsLength,\r
924 FragmentTable,\r
925 FragmentCount,\r
926 SadEntry,\r
927 RecycleEvent\r
928 );\r
929 }\r
930 //\r
931 // The other protocols are not supported.\r
932 //\r
933 return EFI_UNSUPPORTED;\r
934}\r