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