/** @file | |
IPsec inbound and outbound traffic processing. | |
Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php. | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "IpSecImpl.h" | |
#include "IpSecDebug.h" | |
#include "IpSecCryptIo.h" | |
extern LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum]; | |
/** | |
The call back function of NetbufFromExt. | |
@param[in] Arg The argument passed from the caller. | |
**/ | |
VOID | |
EFIAPI | |
IpSecOnRecyclePacket ( | |
IN VOID *Arg | |
) | |
{ | |
} | |
/** | |
This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP | |
is released. | |
@param[in] Event The related event. | |
@param[in] Context The data passed by the caller. | |
**/ | |
VOID | |
EFIAPI | |
IpSecRecycleCallback ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
IPSEC_RECYCLE_CONTEXT *RecycleContext; | |
RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context; | |
if (RecycleContext->FragmentTable != NULL) { | |
FreePool (RecycleContext->FragmentTable); | |
} | |
if (RecycleContext->PayloadBuffer != NULL) { | |
FreePool (RecycleContext->PayloadBuffer); | |
} | |
FreePool (RecycleContext); | |
gBS->CloseEvent (Event); | |
} | |
/** | |
Calculate the extension header of IP. The return length only doesn't contain | |
the fixed IP header length. | |
@param[in] IpHead Points to an IP head to be calculated. | |
@param[in] LastHead Points to the last header of the IP header. | |
@return The length of the extension header. | |
**/ | |
UINT16 | |
IpSecGetPlainExtHeadSize ( | |
IN VOID *IpHead, | |
IN UINT8 *LastHead | |
) | |
{ | |
UINT16 Size; | |
Size = (UINT16) (LastHead - (UINT8 *) IpHead); | |
if (Size > sizeof (EFI_IP6_HEADER)) { | |
// | |
// * (LastHead+1) point the last header's length but not include the first | |
// 8 octers, so this formluation add 8 at the end. | |
// | |
Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8); | |
} else { | |
Size = 0; | |
} | |
return Size; | |
} | |
/** | |
Authenticate the IpSec Payload and store the result in the IcvBuffer. | |
@param[in] BufferToAuth The buffer to be Authenticated. | |
@param[in] AuthSize The size of the buffer to be Authenticated. | |
@param[in, out] IcvBuffer The buffer to store the ICV. | |
@param[in] IcvSize The size of ICV. | |
@param[in] Key The Key passed to the CryptLib to generate a | |
CRYPT_HANDLE. | |
@param[in] AuthAlgId The Authentication Algorithm ID. | |
@retval EFI_UNSUPPORTED If the AuthAlg is not in the support list. | |
@retval EFI_SUCCESS Authenticated the payload successfully. | |
@retval otherwise Authentication of the payload failed. | |
**/ | |
EFI_STATUS | |
IpSecAuthPayload ( | |
IN UINT8 *BufferToAuth, | |
IN UINTN AuthSize, | |
IN OUT UINT8 *IcvBuffer, | |
IN UINTN IcvSize, | |
IN VOID *Key, | |
IN UINT8 AuthAlgId | |
) | |
{ | |
switch (AuthAlgId) { | |
case IKE_AALG_NONE : | |
case IKE_AALG_NULL : | |
return EFI_SUCCESS; | |
default: | |
return EFI_UNSUPPORTED; | |
} | |
} | |
/** | |
Verify if the Authentication payload is correct. | |
@param[in] EspBuffer Points to the ESP wrapped buffer. | |
@param[in] EspSize The size of the ESP wrapped buffer. | |
@param[in] SadEntry The related SAD entry to store the authentication | |
algorithm key. | |
@param[in] IcvSize The length of ICV. | |
@retval EFI_SUCCESS The authentication data is correct. | |
@retval EFI_ACCESS_DENIED The authentication data is not correct. | |
**/ | |
EFI_STATUS | |
IpSecEspAuthVerifyPayload ( | |
IN UINT8 *EspBuffer, | |
IN UINTN EspSize, | |
IN IPSEC_SAD_ENTRY *SadEntry, | |
IN UINTN *IcvSize | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN AuthSize; | |
UINT8 IcvBuffer[12]; | |
// | |
// Calculate the size of authentication payload. | |
// | |
*IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId); | |
AuthSize = EspSize - *IcvSize; | |
// | |
// Calculate the icv buffer and size of the payload. | |
// | |
Status = IpSecAuthPayload ( | |
EspBuffer, | |
AuthSize, | |
IcvBuffer, | |
*IcvSize, | |
SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey, | |
SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Compare the calculated icv and the appended original icv. | |
// | |
if (CompareMem (EspBuffer + AuthSize, IcvBuffer, *IcvSize) == 0) { | |
return EFI_SUCCESS; | |
} | |
DEBUG ((DEBUG_ERROR, "Error auth verify payload\n")); | |
return EFI_ACCESS_DENIED; | |
} | |
/** | |
ESP Decrypt the payload. | |
@param[in, out] PayloadBuffer Pointer to the buffer containing the ESP wrapped; | |
to be decrypted on input, and plaintext on return. The | |
number of bytes of data to be decrypted is | |
specified by EncryptSize. | |
@param[in] EncryptSize The size of the PayloadBuffer as input. | |
@param[in] SadEntry The related SAD entry. | |
@param[in] IvSize The size of IV. | |
@param[out] PlainPayloadSize Contains the return value of decrypted size. | |
@param[out] PaddingSize Contains the return value of Padding size. | |
@param[out] NextHeader Contains the return value of the last protocol header | |
of the IP packet. | |
@retval EFI_UNSUPPORTED The Algorithm pointed to by the SAD entry is not supported. | |
@retval EFI_SUCCESS The operation completed successfully. | |
**/ | |
EFI_STATUS | |
IpSecEspDecryptPayload ( | |
IN OUT UINT8 *PayloadBuffer, | |
IN UINTN EncryptSize, | |
IN IPSEC_SAD_ENTRY *SadEntry, | |
IN UINTN *IvSize, | |
OUT UINTN *PlainPayloadSize, | |
OUT UINTN *PaddingSize, | |
OUT UINT8 *NextHeader | |
) | |
{ | |
EFI_ESP_TAIL *EspTail; | |
switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) { | |
case IKE_EALG_NULL: | |
EspTail = (EFI_ESP_TAIL *) (PayloadBuffer + EncryptSize - sizeof (EFI_ESP_TAIL)); | |
*PaddingSize = EspTail->PaddingLength; | |
*NextHeader = EspTail->NextHeader; | |
*PlainPayloadSize = EncryptSize - EspTail->PaddingLength - sizeof (EFI_ESP_TAIL); | |
break; | |
case IKE_EALG_3DESCBC: | |
case IKE_EALG_AESCBC: | |
// | |
// TODO: support these algorithm | |
// | |
return EFI_UNSUPPORTED; | |
default : | |
return EFI_UNSUPPORTED; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
ESP Encrypt the payload. | |
@param[in, out] BufferToEncrypt Pointer to the buffer containing plaintext to be | |
encrypted on input, and ciphertext on return. The | |
number of bytes of data to be encrypted is | |
specified by EncryptSize. | |
@param[in, out] EncryptSize The size of the plaintext on input, and the size of the | |
ciphertext on return. | |
@param[in] IvBuffer Points to IV data. | |
@param[in] IvSize Size of IV. | |
@param[in] SadEntry Related SAD entry. | |
@retval EFI_UNSUPPORTED The Algorithm pointed by SAD entry is not supported. | |
@retval EFI_SUCCESS The operation completed successfully. | |
**/ | |
EFI_STATUS | |
IpSecEspEncryptPayload ( | |
IN OUT UINT8 *BufferToEncrypt, | |
IN OUT UINTN EncryptSize, | |
IN UINT8 *IvBuffer, | |
IN UINTN IvSize, | |
IN IPSEC_SAD_ENTRY *SadEntry | |
) | |
{ | |
switch (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId) { | |
case IKE_EALG_NULL: | |
return EFI_SUCCESS; | |
case IKE_EALG_3DESCBC: | |
case IKE_EALG_AESCBC: | |
// | |
// TODO: support these algorithms | |
// | |
return EFI_UNSUPPORTED; | |
default : | |
return EFI_UNSUPPORTED; | |
} | |
} | |
/** | |
The actual entry to relative function processes the inbound traffic of ESP header. | |
This function is the subfunction of IpSecProtectInboundPacket(). It checks the | |
received packet security property and trim the ESP header and then returns without | |
an IPsec protected IP Header and FramgmentTable. | |
@param[in] IpVersion The version of IP. | |
@param[in, out] IpHead Points to the IP header containing the ESP header | |
to be trimed on input, and without ESP header | |
on return. | |
@param[out] LastHead The Last Header in IP header on return. | |
@param[in, out] OptionsBuffer Pointer to the options buffer. It is optional. | |
@param[in, out] OptionsLength Length of the options buffer. It is optional. | |
@param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec | |
protected on input, and without IPsec protected | |
on return. | |
@param[in, out] FragmentCount The number of fragments. | |
@param[out] SpdEntry Pointer to contain the address of SPD entry on return. | |
@param[out] RecycleEvent The event for recycling of resources. | |
@retval EFI_SUCCESS The operation was successful. | |
@retval EFI_ACCESS_DENIED One or more following conditions is TRUE: | |
- ESP header was not found. | |
- The related SAD entry was not found. | |
- The related SAD entry does not support the ESP protocol. | |
@retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. | |
**/ | |
EFI_STATUS | |
IpSecEspInboundPacket ( | |
IN UINT8 IpVersion, | |
IN OUT VOID *IpHead, | |
OUT UINT8 *LastHead, | |
IN OUT VOID **OptionsBuffer, OPTIONAL | |
IN OUT UINT32 *OptionsLength, OPTIONAL | |
IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, | |
IN OUT UINT32 *FragmentCount, | |
OUT IPSEC_SPD_ENTRY **SpdEntry, | |
OUT EFI_EVENT *RecycleEvent | |
) | |
{ | |
EFI_STATUS Status; | |
NET_BUF *Payload; | |
UINTN EspSize; | |
UINTN IvSize; | |
UINTN PlainPayloadSize; | |
UINTN PaddingSize; | |
UINTN IcvSize; | |
UINT8 *ProcessBuffer; | |
EFI_IP_ADDRESS DestIp; | |
EFI_ESP_HEADER *EspHeader; | |
EFI_ESP_TAIL *EspTail; | |
EFI_IPSEC_SA_ID *SaId; | |
IPSEC_SAD_DATA *SadData; | |
IPSEC_SAD_ENTRY *SadEntry; | |
IPSEC_RECYCLE_CONTEXT *RecycleContext; | |
UINT32 Spi; | |
UINT8 NextHeader; | |
UINT16 IpSecHeadSize; | |
Status = EFI_SUCCESS; | |
Payload = NULL; | |
ProcessBuffer = NULL; | |
RecycleContext = NULL; | |
*RecycleEvent = NULL; | |
PlainPayloadSize = 0; | |
NextHeader = 0; | |
// | |
// Build netbuf from fragment table first. | |
// | |
Payload = NetbufFromExt ( | |
(NET_FRAGMENT *) *FragmentTable, | |
*FragmentCount, | |
0, | |
sizeof (EFI_ESP_HEADER), | |
IpSecOnRecyclePacket, | |
NULL | |
); | |
if (Payload == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
// | |
// Get the esp size and eso header from netbuf. | |
// | |
EspSize = Payload->TotalSize; | |
EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL); | |
if (EspHeader == NULL) { | |
Status = EFI_ACCESS_DENIED; | |
goto ON_EXIT; | |
} | |
// | |
// Parse destination address from ip header. | |
// | |
ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS)); | |
if (IpVersion == IP_VERSION_4) { | |
CopyMem ( | |
&DestIp, | |
&((IP4_HEAD *) IpHead)->Dst, | |
sizeof (IP4_ADDR) | |
); | |
} else { | |
CopyMem ( | |
&DestIp, | |
&((EFI_IP6_HEADER *) IpHead)->DestinationAddress, | |
sizeof (EFI_IPv6_ADDRESS) | |
); | |
} | |
// | |
// Lookup sad entry according to the spi and dest address. | |
// | |
Spi = NTOHL (EspHeader->Spi); | |
SadEntry = IpSecLookupSadBySpi (Spi, &DestIp); | |
if (SadEntry == NULL) { | |
Status = EFI_ACCESS_DENIED; | |
goto ON_EXIT; | |
} | |
SaId = SadEntry->Id; | |
SadData = SadEntry->Data; | |
// | |
// Only support esp protocol currently. | |
// | |
if (SaId->Proto != EfiIPsecESP) { | |
Status = EFI_ACCESS_DENIED; | |
goto ON_EXIT; | |
} | |
if (!SadData->ManualSet) { | |
// | |
// TODO: Check sa lifetime and sequence number | |
// | |
} | |
// | |
// Allocate buffer for decryption and authentication by esp. | |
// | |
ProcessBuffer = AllocateZeroPool (EspSize); | |
if (ProcessBuffer == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer); | |
// | |
// Authenticate the esp wrapped buffer by the sad entry if has auth key. | |
// | |
IcvSize = 0; | |
if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { | |
Status = IpSecEspAuthVerifyPayload ( | |
ProcessBuffer, | |
EspSize, | |
SadEntry, | |
&IcvSize | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
} | |
// | |
// Decrypt the payload by the sad entry if has decrypt key. | |
// | |
IvSize = 0; | |
if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) { | |
Status = IpSecEspDecryptPayload ( | |
ProcessBuffer + sizeof (EFI_ESP_HEADER), | |
EspSize - sizeof (EFI_ESP_HEADER) - IcvSize, | |
SadEntry, | |
&IvSize, | |
&PlainPayloadSize, | |
&PaddingSize, | |
&NextHeader | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
} else { | |
EspTail = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL)); | |
PaddingSize = EspTail->PaddingLength; | |
NextHeader = EspTail->NextHeader; | |
PlainPayloadSize = EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize - sizeof (EFI_ESP_TAIL) - PaddingSize; | |
} | |
// | |
// TODO: handle anti-replay window | |
// | |
// | |
// Decryption and authentication with esp has been done, so it's time to | |
// reload the new packet, create recycle event and fixup ip header. | |
// | |
RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT)); | |
if (RecycleContext == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
IpSecRecycleCallback, | |
RecycleContext, | |
RecycleEvent | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
// | |
// TODO: Who take responsible to handle the original fragment table? | |
// | |
*FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA)); | |
if (*FragmentTable == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
RecycleContext->PayloadBuffer = ProcessBuffer; | |
RecycleContext->FragmentTable = *FragmentTable; | |
(*FragmentTable)[0].FragmentBuffer = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize; | |
(*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize; | |
*FragmentCount = 1; | |
// | |
// Update the total length field in ip header since processed by esp. | |
// | |
if (IpVersion == IP_VERSION_4) { | |
((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + PlainPayloadSize)); | |
} else { | |
IpSecHeadSize = IpSecGetPlainExtHeadSize (IpHead, LastHead); | |
((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize)); | |
} | |
// | |
// Update the next layer field in ip header since esp header inserted. | |
// | |
*LastHead = NextHeader; | |
// | |
// Update the spd association of the sad entry. | |
// | |
*SpdEntry = SadData->SpdEntry; | |
ON_EXIT: | |
if (Payload != NULL) { | |
NetbufFree (Payload); | |
} | |
if (EFI_ERROR (Status)) { | |
if (ProcessBuffer != NULL) { | |
FreePool (ProcessBuffer); | |
} | |
if (RecycleContext != NULL) { | |
FreePool (RecycleContext); | |
} | |
if (*RecycleEvent != NULL) { | |
gBS->CloseEvent (*RecycleEvent); | |
} | |
} | |
return Status; | |
} | |
/** | |
The actual entry to the relative function processes the output traffic using the ESP protocol. | |
This function is the subfunction of IpSecProtectOutboundPacket(). It protected | |
the sending packet by encrypting its payload and inserting ESP header in the orginal | |
IP header, then return the IpHeader and IPsec protected Fragmentable. | |
@param[in] IpVersion The version of IP. | |
@param[in, out] IpHead Points to IP header containing the orginal IP header | |
to be processed on input, and inserted ESP header | |
on return. | |
@param[in, out] LastHead The Last Header in IP header. | |
@param[in, out] OptionsBuffer Pointer to the options buffer. It is optional. | |
@param[in, out] OptionsLength Length of the options buffer. It is optional. | |
@param[in, out] FragmentTable Pointer to a list of fragments to be protected by | |
IPsec on input, and with IPsec protected | |
on return. | |
@param[in, out] FragmentCount The number of fragments. | |
@param[in] SadEntry The related SAD entry. | |
@param[out] RecycleEvent The event for recycling of resources. | |
@retval EFI_SUCCESS The operation was successful. | |
@retval EFI_OUT_OF_RESOURCES The required system resources can't be allocated. | |
**/ | |
EFI_STATUS | |
IpSecEspOutboundPacket ( | |
IN UINT8 IpVersion, | |
IN OUT VOID *IpHead, | |
IN OUT UINT8 *LastHead, | |
IN OUT VOID **OptionsBuffer, OPTIONAL | |
IN OUT UINT32 *OptionsLength, OPTIONAL | |
IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, | |
IN OUT UINT32 *FragmentCount, | |
IN IPSEC_SAD_ENTRY *SadEntry, | |
OUT EFI_EVENT *RecycleEvent | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
EFI_IPSEC_SA_ID *SaId; | |
IPSEC_SAD_DATA *SadData; | |
IPSEC_RECYCLE_CONTEXT *RecycleContext; | |
UINT8 *ProcessBuffer; | |
UINTN BytesCopied; | |
INTN EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4 | |
UINTN EspSize; // Total size of esp wrapped ip payload | |
UINTN IvSize; // Size of IV, optional, might be 0 | |
UINTN PlainPayloadSize;// Original IP payload size | |
UINTN PaddingSize; // Size of padding | |
UINTN EncryptSize; // Size of data to be encrypted, start after IV and | |
// stop before ICV | |
UINTN IcvSize; // Size of ICV, optional, might be 0 | |
UINT8 *RestOfPayload; // Start of Payload after IV | |
UINT8 *Padding; // Start address of padding | |
EFI_ESP_HEADER *EspHeader; // Start address of ESP frame | |
EFI_ESP_TAIL *EspTail; // Address behind padding | |
Status = EFI_ACCESS_DENIED; | |
SaId = SadEntry->Id; | |
SadData = SadEntry->Data; | |
ProcessBuffer = NULL; | |
RecycleContext = NULL; | |
*RecycleEvent = NULL; | |
if (!SadData->ManualSet && | |
SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL && | |
SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL | |
) { | |
// | |
// Invalid manual sad entry configuration. | |
// | |
goto ON_EXIT; | |
} | |
// | |
// Calculate enctrypt block size, need iv by default and 4 bytes alignment. | |
// | |
EncryptBlockSize = 4; | |
if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) { | |
EncryptBlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId); | |
if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) { | |
goto ON_EXIT; | |
} | |
} | |
// | |
// Calculate the plain payload size accroding to the fragment table. | |
// | |
PlainPayloadSize = 0; | |
for (Index = 0; Index < *FragmentCount; Index++) { | |
PlainPayloadSize += (*FragmentTable)[Index].FragmentLength; | |
} | |
// | |
// Calculate icv size, optional by default and 4 bytes alignment. | |
// | |
IcvSize = 0; | |
if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { | |
IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId); | |
if (IcvSize % 4 != 0) { | |
goto ON_EXIT; | |
} | |
} | |
// | |
// Calcuate the total size of esp wrapped ip payload. | |
// | |
IvSize = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId); | |
EncryptSize = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize; | |
PaddingSize = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL); | |
EspSize = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize; | |
ProcessBuffer = AllocateZeroPool (EspSize); | |
if (ProcessBuffer == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
// | |
// Calculate esp header and esp tail including header, payload and padding. | |
// | |
EspHeader = (EFI_ESP_HEADER *) ProcessBuffer; | |
RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize; | |
Padding = RestOfPayload + PlainPayloadSize; | |
EspTail = (EFI_ESP_TAIL *) (Padding + PaddingSize); | |
// | |
// Fill the sn and spi fields in esp header. | |
// | |
EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1); | |
EspHeader->Spi = HTONL (SaId->Spi); | |
// | |
// Copy the rest of payload (after iv) from the original fragment buffer. | |
// | |
BytesCopied = 0; | |
for (Index = 0; Index < *FragmentCount; Index++) { | |
CopyMem ( | |
(RestOfPayload + BytesCopied), | |
(*FragmentTable)[Index].FragmentBuffer, | |
(*FragmentTable)[Index].FragmentLength | |
); | |
BytesCopied += (*FragmentTable)[Index].FragmentLength; | |
} | |
// | |
// Fill the padding buffer by natural number sequence. | |
// | |
for (Index = 0; Index < PaddingSize; Index++) { | |
Padding[Index] = (UINT8) (Index + 1); | |
} | |
// | |
// Fill the padding length and next header fields in esp tail. | |
// | |
EspTail->PaddingLength = (UINT8) PaddingSize; | |
EspTail->NextHeader = *LastHead; | |
// | |
// Generate iv at random by crypt library. | |
// | |
Status = IpSecGenerateIv ( | |
(UINT8 *) (EspHeader + 1), | |
IvSize | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
// | |
// Encrypt the payload (after iv) by the sad entry if has encrypt key. | |
// | |
if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) { | |
Status = IpSecEspEncryptPayload ( | |
RestOfPayload, | |
EncryptSize, | |
(UINT8 *) (EspHeader + 1), | |
IvSize, | |
SadEntry | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
} | |
// | |
// Authenticate the esp wrapped buffer by the sad entry if has auth key. | |
// | |
if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { | |
Status = IpSecAuthPayload ( | |
ProcessBuffer, | |
EspSize - IcvSize, | |
ProcessBuffer + EspSize - IcvSize, | |
IcvSize, | |
SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey, | |
SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
} | |
// | |
// Encryption and authentication with esp has been done, so it's time to | |
// reload the new packet, create recycle event and fixup ip header. | |
// | |
RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT)); | |
if (RecycleContext == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
IpSecRecycleCallback, | |
RecycleContext, | |
RecycleEvent | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
// | |
// TODO: Who take responsible to handle the original fragment table? | |
// | |
*FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA)); | |
if (*FragmentTable == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_EXIT; | |
} | |
RecycleContext->FragmentTable = *FragmentTable; | |
RecycleContext->PayloadBuffer = ProcessBuffer; | |
(*FragmentTable)[0].FragmentBuffer = ProcessBuffer; | |
(*FragmentTable)[0].FragmentLength = (UINT32) EspSize; | |
*FragmentCount = 1; | |
// | |
// Update the total length field in ip header since processed by esp. | |
// | |
if (IpVersion == IP_VERSION_4) { | |
((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) (((IP4_HEAD *) IpHead)->HeadLen + EspSize)); | |
} else { | |
((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize); | |
} | |
// | |
// Update the next layer field in ip header since esp header inserted. | |
// | |
*LastHead = IPSEC_ESP_PROTOCOL; | |
// | |
// Increase the sn number in sad entry according to rfc4303. | |
// | |
SadData->SequenceNumber++; | |
ON_EXIT: | |
if (EFI_ERROR (Status)) { | |
if (ProcessBuffer != NULL) { | |
FreePool (ProcessBuffer); | |
} | |
if (RecycleContext != NULL) { | |
FreePool (RecycleContext); | |
} | |
if (*RecycleEvent != NULL) { | |
gBS->CloseEvent (*RecycleEvent); | |
} | |
} | |
return Status; | |
} | |
/** | |
This function processes the inbound traffic with IPsec. | |
It checks the received packet security property, trims the ESP/AH header, and then | |
returns without an IPsec protected IP Header and FragmentTable. | |
@param[in] IpVersion The version of IP. | |
@param[in, out] IpHead Points to IP header containing the ESP/AH header | |
to be trimed on input, and without ESP/AH header | |
on return. | |
@param[out] LastHead The Last Header in IP header on return. | |
@param[in, out] OptionsBuffer Pointer to the options buffer. It is optional. | |
@param[in, out] OptionsLength Length of the options buffer. It is optional. | |
@param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec | |
protected on input, and without IPsec protected | |
on return. | |
@param[in, out] FragmentCount Number of fragments. | |
@param[out] SpdEntry Pointer to contain the address of SPD entry on return. | |
@param[out] RecycleEvent Event for recycling of resources. | |
@retval EFI_SUCCESS The operation is successful. | |
@retval EFI_UNSUPPORTED If the IPSEC protocol is not supported. | |
**/ | |
EFI_STATUS | |
IpSecProtectInboundPacket ( | |
IN UINT8 IpVersion, | |
IN OUT VOID *IpHead, | |
OUT UINT8 *LastHead, | |
IN OUT VOID **OptionsBuffer, OPTIONAL | |
IN OUT UINT32 *OptionsLength, OPTIONAL | |
IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, | |
IN OUT UINT32 *FragmentCount, | |
OUT IPSEC_SPD_ENTRY **SpdEntry, | |
OUT EFI_EVENT *RecycleEvent | |
) | |
{ | |
if (*LastHead == IPSEC_ESP_PROTOCOL) { | |
// | |
// Process the esp ipsec header of the inbound traffic. | |
// | |
return IpSecEspInboundPacket ( | |
IpVersion, | |
IpHead, | |
LastHead, | |
OptionsBuffer, | |
OptionsLength, | |
FragmentTable, | |
FragmentCount, | |
SpdEntry, | |
RecycleEvent | |
); | |
} | |
// | |
// The other protocols are not supported. | |
// | |
return EFI_UNSUPPORTED; | |
} | |
/** | |
This fucntion processes the output traffic with IPsec. | |
It protected the sending packet by encrypting it payload and inserting ESP/AH header | |
in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable. | |
@param[in] IpVersion The version of IP. | |
@param[in, out] IpHead Point to IP header containing the orginal IP header | |
to be processed on input, and inserted ESP/AH header | |
on return. | |
@param[in, out] LastHead The Last Header in IP header. | |
@param[in, out] OptionsBuffer Pointer to the options buffer. It is optional. | |
@param[in, out] OptionsLength Length of the options buffer. It is optional. | |
@param[in, out] FragmentTable Pointer to a list of fragments to be protected by | |
IPsec on input, and with IPsec protected | |
on return. | |
@param[in, out] FragmentCount Number of fragments. | |
@param[in] SadEntry Related SAD entry. | |
@param[out] RecycleEvent Event for recycling of resources. | |
@retval EFI_SUCCESS The operation is successful. | |
@retval EFI_UNSUPPORTED If the IPSEC protocol is not supported. | |
**/ | |
EFI_STATUS | |
IpSecProtectOutboundPacket ( | |
IN UINT8 IpVersion, | |
IN OUT VOID *IpHead, | |
IN OUT UINT8 *LastHead, | |
IN OUT VOID **OptionsBuffer, OPTIONAL | |
IN OUT UINT32 *OptionsLength, OPTIONAL | |
IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, | |
IN OUT UINT32 *FragmentCount, | |
IN IPSEC_SAD_ENTRY *SadEntry, | |
OUT EFI_EVENT *RecycleEvent | |
) | |
{ | |
if (SadEntry->Id->Proto == EfiIPsecESP) { | |
// | |
// Process the esp ipsec header of the outbound traffic. | |
// | |
return IpSecEspOutboundPacket ( | |
IpVersion, | |
IpHead, | |
LastHead, | |
OptionsBuffer, | |
OptionsLength, | |
FragmentTable, | |
FragmentCount, | |
SadEntry, | |
RecycleEvent | |
); | |
} | |
// | |
// The other protocols are not supported. | |
// | |
return EFI_UNSUPPORTED; | |
} |