mirror of https://github.com/acidanthera/audk.git
935 lines
31 KiB
C
935 lines
31 KiB
C
/** @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;
|
|
}
|