/** @file | |
The Common operations used by IKE Exchange Process. | |
(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR> | |
Copyright (c) 2010 - 2016, 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 "Utility.h" | |
#include "IpSecDebug.h" | |
#include "IkeService.h" | |
#include "IpSecConfigImpl.h" | |
UINT16 mIkev2EncryptAlgorithmList[IKEV2_SUPPORT_ENCRYPT_ALGORITHM_NUM] = { | |
IKEV2_TRANSFORM_ID_ENCR_3DES, | |
IKEV2_TRANSFORM_ID_ENCR_AES_CBC, | |
}; | |
UINT16 mIkev2PrfAlgorithmList[IKEV2_SUPPORT_PRF_ALGORITHM_NUM] = { | |
IKEV2_TRANSFORM_ID_PRF_HMAC_SHA1, | |
}; | |
UINT16 mIkev2DhGroupAlgorithmList[IKEV2_SUPPORT_DH_ALGORITHM_NUM] = { | |
IKEV2_TRANSFORM_ID_DH_1024MODP, | |
IKEV2_TRANSFORM_ID_DH_2048MODP, | |
}; | |
UINT16 mIkev2AuthAlgorithmList[IKEV2_SUPPORT_AUTH_ALGORITHM_NUM] = { | |
IKEV2_TRANSFORM_ID_AUTH_HMAC_SHA1_96, | |
}; | |
/** | |
Allocate buffer for IKEV2_SA_SESSION and initialize it. | |
@param[in] Private Pointer to IPSEC_PRIVATE_DATA. | |
@param[in] UdpService Pointer to IKE_UDP_SERVICE related to this IKE SA Session. | |
@return Pointer to IKEV2_SA_SESSION or NULL. | |
**/ | |
IKEV2_SA_SESSION * | |
Ikev2SaSessionAlloc ( | |
IN IPSEC_PRIVATE_DATA *Private, | |
IN IKE_UDP_SERVICE *UdpService | |
) | |
{ | |
EFI_STATUS Status; | |
IKEV2_SESSION_COMMON *SessionCommon; | |
IKEV2_SA_SESSION *IkeSaSession; | |
IkeSaSession = AllocateZeroPool (sizeof (IKEV2_SA_SESSION)); | |
if (IkeSaSession == NULL) { | |
return NULL; | |
} | |
// | |
// Initialize the fields of IkeSaSession and its SessionCommon. | |
// | |
IkeSaSession->NCookie = NULL; | |
IkeSaSession->Signature = IKEV2_SA_SESSION_SIGNATURE; | |
IkeSaSession->InitiatorCookie = IkeGenerateCookie (); | |
IkeSaSession->ResponderCookie = 0; | |
// | |
// BUGBUG: Message ID starts from 2 is to match the OpenSwan requirement, but it | |
// might not match the IPv6 Logo. In its test specification, it mentions that | |
// the Message ID should start from zero after the IKE_SA_INIT exchange. | |
// | |
IkeSaSession->MessageId = 2; | |
SessionCommon = &IkeSaSession->SessionCommon; | |
SessionCommon->UdpService = UdpService; | |
SessionCommon->Private = Private; | |
SessionCommon->IkeSessionType = IkeSessionTypeIkeSa; | |
SessionCommon->IkeVer = 2; | |
SessionCommon->AfterEncodePayload = NULL; | |
SessionCommon->BeforeDecodePayload = NULL; | |
// | |
// Create a resend notfiy event for retry. | |
// | |
Status = gBS->CreateEvent ( | |
EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
Ikev2ResendNotify, | |
SessionCommon, | |
&SessionCommon->TimeoutEvent | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (IkeSaSession); | |
return NULL; | |
} | |
// | |
// Initialize the lists in IkeSaSession. | |
// | |
InitializeListHead (&IkeSaSession->ChildSaSessionList); | |
InitializeListHead (&IkeSaSession->ChildSaEstablishSessionList); | |
InitializeListHead (&IkeSaSession->InfoMIDList); | |
InitializeListHead (&IkeSaSession->DeleteSaList); | |
return IkeSaSession; | |
} | |
/** | |
Register the established IKEv2 SA into Private->Ikev2EstablishedList. If there is | |
IKEV2_SA_SESSION with same remote peer IP, remove the old one then register the | |
new one. | |
@param[in] IkeSaSession Pointer to IKEV2_SA_SESSION to be registered. | |
@param[in] Private Pointer to IPSEC_PRAVATE_DATA. | |
**/ | |
VOID | |
Ikev2SaSessionReg ( | |
IN IKEV2_SA_SESSION *IkeSaSession, | |
IN IPSEC_PRIVATE_DATA *Private | |
) | |
{ | |
IKEV2_SESSION_COMMON *SessionCommon; | |
IKEV2_SA_SESSION *OldIkeSaSession; | |
EFI_STATUS Status; | |
UINT64 Lifetime; | |
// | |
// Keep IKE SA exclusive to remote ip address. | |
// | |
SessionCommon = &IkeSaSession->SessionCommon; | |
OldIkeSaSession = Ikev2SaSessionRemove (&Private->Ikev2EstablishedList, &SessionCommon->RemotePeerIp); | |
if (OldIkeSaSession != NULL) { | |
// | |
// TODO: It should delete all child SAs if rekey the IKE SA. | |
// | |
Ikev2SaSessionFree (OldIkeSaSession); | |
} | |
// | |
// Cleanup the fields of SessionCommon for processing. | |
// | |
Ikev2SessionCommonRefresh (SessionCommon); | |
// | |
// Insert the ready IKE SA session into established list. | |
// | |
Ikev2SaSessionInsert (&Private->Ikev2EstablishedList, IkeSaSession, &SessionCommon->RemotePeerIp); | |
// | |
// Create a notfiy event for the IKE SA life time counting. | |
// | |
Status = gBS->CreateEvent ( | |
EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
Ikev2LifetimeNotify, | |
SessionCommon, | |
&SessionCommon->TimeoutEvent | |
); | |
if (EFI_ERROR(Status)){ | |
// | |
// If TimerEvent creation failed, the SA will be alive untill user disable it or | |
// receiving a Delete Payload from peer. | |
// | |
return; | |
} | |
// | |
// Start to count the lifetime of the IKE SA. | |
// | |
if (IkeSaSession->Spd->Data->ProcessingPolicy->SaLifetime.HardLifetime == 0) { | |
Lifetime = IKE_SA_DEFAULT_LIFETIME; | |
} else { | |
Lifetime = IkeSaSession->Spd->Data->ProcessingPolicy->SaLifetime.HardLifetime; | |
} | |
Status = gBS->SetTimer ( | |
SessionCommon->TimeoutEvent, | |
TimerRelative, | |
MultU64x32(Lifetime, 10000000) // ms->100ns | |
); | |
if (EFI_ERROR(Status)){ | |
// | |
// If SetTimer failed, the SA will be alive untill user disable it or | |
// receiving a Delete Payload from peer. | |
// | |
return ; | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
"\n------IkeSa established and start to count down %d seconds lifetime\n", | |
Lifetime | |
)); | |
return ; | |
} | |
/** | |
Find a IKEV2_SA_SESSION by the remote peer IP. | |
@param[in] SaSessionList SaSession List to be searched. | |
@param[in] RemotePeerIp Pointer to specified IP address. | |
@return Pointer to IKEV2_SA_SESSION if find one or NULL. | |
**/ | |
IKEV2_SA_SESSION * | |
Ikev2SaSessionLookup ( | |
IN LIST_ENTRY *SaSessionList, | |
IN EFI_IP_ADDRESS *RemotePeerIp | |
) | |
{ | |
LIST_ENTRY *Entry; | |
IKEV2_SA_SESSION *IkeSaSession; | |
NET_LIST_FOR_EACH (Entry, SaSessionList) { | |
IkeSaSession = IKEV2_SA_SESSION_BY_SESSION (Entry); | |
if (CompareMem ( | |
&IkeSaSession->SessionCommon.RemotePeerIp, | |
RemotePeerIp, | |
sizeof (EFI_IP_ADDRESS) | |
) == 0) { | |
return IkeSaSession; | |
} | |
} | |
return NULL; | |
} | |
/** | |
Insert a IKE_SA_SESSION into IkeSaSession list. The IkeSaSession list is either | |
Private->Ikev2SaSession list or Private->Ikev2EstablishedList list. | |
@param[in] SaSessionList Pointer to list to be inserted into. | |
@param[in] IkeSaSession Pointer to IKEV2_SA_SESSION to be inserted. | |
@param[in] RemotePeerIp Pointer to EFI_IP_ADDRESSS to indicate the | |
unique IKEV2_SA_SESSION. | |
**/ | |
VOID | |
Ikev2SaSessionInsert ( | |
IN LIST_ENTRY *SaSessionList, | |
IN IKEV2_SA_SESSION *IkeSaSession, | |
IN EFI_IP_ADDRESS *RemotePeerIp | |
) | |
{ | |
Ikev2SaSessionRemove (SaSessionList, RemotePeerIp); | |
InsertTailList (SaSessionList, &IkeSaSession->BySessionTable); | |
} | |
/** | |
Remove the SA Session by Remote Peer IP. | |
@param[in] SaSessionList Pointer to list to be searched. | |
@param[in] RemotePeerIp Pointer to EFI_IP_ADDRESS to use for SA Session search. | |
@retval Pointer to IKEV2_SA_SESSION with the specified remote IP address or NULL. | |
**/ | |
IKEV2_SA_SESSION * | |
Ikev2SaSessionRemove ( | |
IN LIST_ENTRY *SaSessionList, | |
IN EFI_IP_ADDRESS *RemotePeerIp | |
) | |
{ | |
LIST_ENTRY *Entry; | |
IKEV2_SA_SESSION *IkeSaSession; | |
NET_LIST_FOR_EACH (Entry, SaSessionList) { | |
IkeSaSession = IKEV2_SA_SESSION_BY_SESSION (Entry); | |
if (CompareMem ( | |
&IkeSaSession->SessionCommon.RemotePeerIp, | |
RemotePeerIp, | |
sizeof (EFI_IP_ADDRESS) | |
) == 0) { | |
RemoveEntryList (Entry); | |
return IkeSaSession; | |
} | |
} | |
return NULL; | |
} | |
/** | |
Marking a SA session as on deleting. | |
@param[in] IkeSaSession Pointer to IKEV2_SA_SESSION. | |
@retval EFI_SUCCESS Find the related SA session and marked it. | |
**/ | |
EFI_STATUS | |
Ikev2SaSessionOnDeleting ( | |
IN IKEV2_SA_SESSION *IkeSaSession | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
Free specified Seession Common. The session common would belong to a IKE SA or | |
a Child SA. | |
@param[in] SessionCommon Pointer to a Session Common. | |
**/ | |
VOID | |
Ikev2SaSessionCommonFree ( | |
IN IKEV2_SESSION_COMMON *SessionCommon | |
) | |
{ | |
ASSERT (SessionCommon != NULL); | |
if (SessionCommon->LastSentPacket != NULL) { | |
IkePacketFree (SessionCommon->LastSentPacket); | |
} | |
if (SessionCommon->SaParams != NULL) { | |
FreePool (SessionCommon->SaParams); | |
} | |
if (SessionCommon->TimeoutEvent != NULL) { | |
gBS->CloseEvent (SessionCommon->TimeoutEvent); | |
} | |
} | |
/** | |
After IKE/Child SA is estiblished, close the time event and free sent packet. | |
@param[in] SessionCommon Pointer to a Session Common. | |
**/ | |
VOID | |
Ikev2SessionCommonRefresh ( | |
IN IKEV2_SESSION_COMMON *SessionCommon | |
) | |
{ | |
ASSERT (SessionCommon != NULL); | |
gBS->CloseEvent (SessionCommon->TimeoutEvent); | |
SessionCommon->TimeoutEvent = NULL; | |
SessionCommon->TimeoutInterval = 0; | |
SessionCommon->RetryCount = 0; | |
if (SessionCommon->LastSentPacket != NULL) { | |
IkePacketFree (SessionCommon->LastSentPacket); | |
SessionCommon->LastSentPacket = NULL; | |
} | |
return ; | |
} | |
/** | |
Free specified IKEV2 SA Session. | |
@param[in] IkeSaSession Pointer to IKEV2_SA_SESSION to be freed. | |
**/ | |
VOID | |
Ikev2SaSessionFree ( | |
IN IKEV2_SA_SESSION *IkeSaSession | |
) | |
{ | |
IKEV2_SESSION_KEYS *IkeKeys; | |
LIST_ENTRY *Entry; | |
IKEV2_CHILD_SA_SESSION *ChildSa; | |
IKEV2_DH_BUFFER *DhBuffer; | |
ASSERT (IkeSaSession != NULL); | |
// | |
// Delete Common Session | |
// | |
Ikev2SaSessionCommonFree (&IkeSaSession->SessionCommon); | |
// | |
// Delete ChildSaEstablish List and SAD | |
// | |
for (Entry = IkeSaSession->ChildSaEstablishSessionList.ForwardLink; | |
Entry != &IkeSaSession->ChildSaEstablishSessionList; | |
) { | |
ChildSa = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry); | |
Entry = Entry->ForwardLink; | |
Ikev2ChildSaSilentDelete (ChildSa->IkeSaSession, ChildSa->LocalPeerSpi); | |
} | |
// | |
// Delete ChildSaSessionList | |
// | |
for ( Entry = IkeSaSession->ChildSaSessionList.ForwardLink; | |
Entry != &IkeSaSession->ChildSaSessionList; | |
){ | |
ChildSa = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry); | |
Entry = Entry->ForwardLink; | |
RemoveEntryList (Entry->BackLink); | |
Ikev2ChildSaSessionFree (ChildSa); | |
} | |
// | |
// Delete DhBuffer and Keys | |
// | |
if (IkeSaSession->IkeKeys != NULL) { | |
IkeKeys = IkeSaSession->IkeKeys; | |
DhBuffer = IkeKeys->DhBuffer; | |
// | |
// Delete DhBuffer | |
// | |
Ikev2DhBufferFree (DhBuffer); | |
// | |
// Delete Keys | |
// | |
if (IkeKeys->SkAiKey != NULL) { | |
FreePool (IkeKeys->SkAiKey); | |
} | |
if (IkeKeys->SkArKey != NULL) { | |
FreePool (IkeKeys->SkArKey); | |
} | |
if (IkeKeys->SkdKey != NULL) { | |
FreePool (IkeKeys->SkdKey); | |
} | |
if (IkeKeys->SkEiKey != NULL) { | |
FreePool (IkeKeys->SkEiKey); | |
} | |
if (IkeKeys->SkErKey != NULL) { | |
FreePool (IkeKeys->SkErKey); | |
} | |
if (IkeKeys->SkPiKey != NULL) { | |
FreePool (IkeKeys->SkPiKey); | |
} | |
if (IkeKeys->SkPrKey != NULL) { | |
FreePool (IkeKeys->SkPrKey); | |
} | |
FreePool (IkeKeys); | |
} | |
if (IkeSaSession->SaData != NULL) { | |
FreePool (IkeSaSession->SaData); | |
} | |
if (IkeSaSession->NiBlock != NULL) { | |
FreePool (IkeSaSession->NiBlock); | |
} | |
if (IkeSaSession->NrBlock != NULL) { | |
FreePool (IkeSaSession->NrBlock); | |
} | |
if (IkeSaSession->NCookie != NULL) { | |
FreePool (IkeSaSession->NCookie); | |
} | |
if (IkeSaSession->InitPacket != NULL) { | |
FreePool (IkeSaSession->InitPacket); | |
} | |
if (IkeSaSession->RespPacket != NULL) { | |
FreePool (IkeSaSession->RespPacket); | |
} | |
FreePool (IkeSaSession); | |
return ; | |
} | |
/** | |
Increase the MessageID in IkeSaSession. | |
@param[in] IkeSaSession Pointer to a specified IKEV2_SA_SESSION. | |
**/ | |
VOID | |
Ikev2SaSessionIncreaseMessageId ( | |
IN IKEV2_SA_SESSION *IkeSaSession | |
) | |
{ | |
if (IkeSaSession->MessageId < 0xffffffff) { | |
IkeSaSession->MessageId ++; | |
} else { | |
// | |
// TODO: Trigger Rekey process. | |
// | |
} | |
} | |
/** | |
Allocate memory for IKEV2 Child SA Session. | |
@param[in] UdpService Pointer to IKE_UDP_SERVICE. | |
@param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to this Child SA | |
Session. | |
@retval Pointer of a new created IKEV2 Child SA Session or NULL. | |
**/ | |
IKEV2_CHILD_SA_SESSION * | |
Ikev2ChildSaSessionAlloc ( | |
IN IKE_UDP_SERVICE *UdpService, | |
IN IKEV2_SA_SESSION *IkeSaSession | |
) | |
{ | |
EFI_STATUS Status; | |
IKEV2_CHILD_SA_SESSION *ChildSaSession; | |
IKEV2_SESSION_COMMON *ChildSaCommon; | |
IKEV2_SESSION_COMMON *SaCommon; | |
ChildSaSession = AllocateZeroPool (sizeof (IKEV2_CHILD_SA_SESSION)); | |
if (ChildSaSession == NULL) { | |
return NULL; | |
} | |
// | |
// Initialize the fields of ChildSaSession and its SessionCommon. | |
// | |
ChildSaSession->Signature = IKEV2_CHILD_SA_SESSION_SIGNATURE; | |
ChildSaSession->IkeSaSession = IkeSaSession; | |
ChildSaSession->MessageId = IkeSaSession->MessageId; | |
// | |
// Generate an new SPI. | |
// | |
Status = IkeGenerateSpi (IkeSaSession, &(ChildSaSession->LocalPeerSpi)); | |
if (EFI_ERROR (Status)) { | |
FreePool (ChildSaSession); | |
return NULL; | |
} | |
ChildSaCommon = &ChildSaSession->SessionCommon; | |
ChildSaCommon->UdpService = UdpService; | |
ChildSaCommon->Private = IkeSaSession->SessionCommon.Private; | |
ChildSaCommon->IkeSessionType = IkeSessionTypeChildSa; | |
ChildSaCommon->IkeVer = 2; | |
ChildSaCommon->AfterEncodePayload = Ikev2ChildSaAfterEncodePayload; | |
ChildSaCommon->BeforeDecodePayload = Ikev2ChildSaBeforeDecodePayload; | |
SaCommon = &ChildSaSession->IkeSaSession->SessionCommon; | |
// | |
// Create a resend notfiy event for retry. | |
// | |
Status = gBS->CreateEvent ( | |
EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
Ikev2ResendNotify, | |
ChildSaCommon, | |
&ChildSaCommon->TimeoutEvent | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (ChildSaSession); | |
return NULL; | |
} | |
CopyMem (&ChildSaCommon->LocalPeerIp, &SaCommon->LocalPeerIp, sizeof (EFI_IP_ADDRESS)); | |
CopyMem (&ChildSaCommon->RemotePeerIp, &SaCommon->RemotePeerIp, sizeof (EFI_IP_ADDRESS)); | |
return ChildSaSession; | |
} | |
/** | |
Register a established IKEv2 Child SA into IkeSaSession->ChildSaEstablishSessionList. | |
If the there is IKEV2_CHILD_SA_SESSION with same remote peer IP, remove the old one | |
then register the new one. | |
@param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION to be registered. | |
@param[in] Private Pointer to IPSEC_PRAVATE_DATA. | |
**/ | |
VOID | |
Ikev2ChildSaSessionReg ( | |
IN IKEV2_CHILD_SA_SESSION *ChildSaSession, | |
IN IPSEC_PRIVATE_DATA *Private | |
) | |
{ | |
IKEV2_SESSION_COMMON *SessionCommon; | |
IKEV2_CHILD_SA_SESSION *OldChildSaSession; | |
IKEV2_SA_SESSION *IkeSaSession; | |
EFI_STATUS Status; | |
UINT64 Lifetime; | |
// | |
// Keep the IKE SA exclusive. | |
// | |
SessionCommon = &ChildSaSession->SessionCommon; | |
IkeSaSession = ChildSaSession->IkeSaSession; | |
OldChildSaSession = Ikev2ChildSaSessionRemove ( | |
&IkeSaSession->ChildSaEstablishSessionList, | |
ChildSaSession->LocalPeerSpi, | |
IKEV2_ESTABLISHED_CHILDSA_LIST | |
); | |
if (OldChildSaSession != NULL) { | |
// | |
// Free the old one. | |
// | |
Ikev2ChildSaSessionFree (OldChildSaSession); | |
} | |
// | |
// Store the ready child SA into SAD. | |
// | |
Ikev2StoreSaData (ChildSaSession); | |
// | |
// Cleanup the fields of SessionCommon for processing. | |
// | |
Ikev2SessionCommonRefresh (SessionCommon); | |
// | |
// Insert the ready child SA session into established list. | |
// | |
Ikev2ChildSaSessionInsert (&IkeSaSession->ChildSaEstablishSessionList, ChildSaSession); | |
// | |
// Create a Notify event for the IKE SA life time counting. | |
// | |
Status = gBS->CreateEvent ( | |
EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
Ikev2LifetimeNotify, | |
SessionCommon, | |
&SessionCommon->TimeoutEvent | |
); | |
if (EFI_ERROR(Status)){ | |
return ; | |
} | |
// | |
// Start to count the lifetime of the IKE SA. | |
// | |
if (ChildSaSession->Spd->Data->ProcessingPolicy->SaLifetime.HardLifetime != 0){ | |
Lifetime = ChildSaSession->Spd->Data->ProcessingPolicy->SaLifetime.HardLifetime; | |
} else { | |
Lifetime = CHILD_SA_DEFAULT_LIFETIME; | |
} | |
Status = gBS->SetTimer ( | |
SessionCommon->TimeoutEvent, | |
TimerRelative, | |
MultU64x32(Lifetime, 10000000) // ms->100ns | |
); | |
if (EFI_ERROR(Status)){ | |
return ; | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
"\n------ChildSa established and start to count down %d seconds lifetime\n", | |
Lifetime | |
)); | |
return ; | |
} | |
/** | |
Find the ChildSaSession by it's MessagId. | |
@param[in] SaSessionList Pointer to a ChildSaSession List. | |
@param[in] Mid The messageId used to search ChildSaSession. | |
@return Pointer to IKEV2_CHILD_SA_SESSION or NULL. | |
**/ | |
IKEV2_CHILD_SA_SESSION * | |
Ikev2ChildSaSessionLookupByMid ( | |
IN LIST_ENTRY *SaSessionList, | |
IN UINT32 Mid | |
) | |
{ | |
LIST_ENTRY *Entry; | |
IKEV2_CHILD_SA_SESSION *ChildSaSession; | |
NET_LIST_FOR_EACH (Entry, SaSessionList) { | |
ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry); | |
if (ChildSaSession->MessageId == Mid) { | |
return ChildSaSession; | |
} | |
} | |
return NULL; | |
} | |
/** | |
This function find the Child SA by the specified SPI. | |
This functin find a ChildSA session by searching the ChildSaSessionlist of | |
the input IKEV2_SA_SESSION by specified MessageID. | |
@param[in] SaSessionList Pointer to List to be searched. | |
@param[in] Spi Specified SPI. | |
@return Pointer to IKEV2_CHILD_SA_SESSION or NULL. | |
**/ | |
IKEV2_CHILD_SA_SESSION * | |
Ikev2ChildSaSessionLookupBySpi ( | |
IN LIST_ENTRY *SaSessionList, | |
IN UINT32 Spi | |
) | |
{ | |
LIST_ENTRY *Entry; | |
IKEV2_CHILD_SA_SESSION *ChildSaSession; | |
NET_LIST_FOR_EACH (Entry, SaSessionList) { | |
ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry); | |
if (ChildSaSession->RemotePeerSpi == Spi || ChildSaSession->LocalPeerSpi == Spi) { | |
return ChildSaSession; | |
} | |
} | |
return NULL; | |
} | |
/** | |
Insert a Child SA Session into the specified ChildSa list. | |
@param[in] SaSessionList Pointer to list to be inserted in. | |
@param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION to be inserted. | |
**/ | |
VOID | |
Ikev2ChildSaSessionInsert ( | |
IN LIST_ENTRY *SaSessionList, | |
IN IKEV2_CHILD_SA_SESSION *ChildSaSession | |
) | |
{ | |
InsertTailList (SaSessionList, &ChildSaSession->ByIkeSa); | |
} | |
/** | |
Remove the IKEV2_CHILD_SA_SESSION from IkeSaSessionList. | |
@param[in] SaSessionList The SA Session List to be iterated. | |
@param[in] Spi Spi used to identified the IKEV2_CHILD_SA_SESSION. | |
@param[in] ListType The type of the List to indicate whether it is a | |
Established. | |
@return The point to IKEV2_CHILD_SA_SESSION or NULL. | |
**/ | |
IKEV2_CHILD_SA_SESSION * | |
Ikev2ChildSaSessionRemove ( | |
IN LIST_ENTRY *SaSessionList, | |
IN UINT32 Spi, | |
IN UINT8 ListType | |
) | |
{ | |
LIST_ENTRY *Entry; | |
LIST_ENTRY *NextEntry; | |
IKEV2_CHILD_SA_SESSION *ChildSaSession; | |
NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SaSessionList) { | |
if (ListType == IKEV2_ESTABLISHED_CHILDSA_LIST || ListType == IKEV2_ESTABLISHING_CHILDSA_LIST) { | |
ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry); | |
} else if (ListType == IKEV2_DELET_CHILDSA_LIST) { | |
ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_DEL_SA (Entry); | |
} else { | |
return NULL; | |
} | |
if (ChildSaSession->RemotePeerSpi == Spi || ChildSaSession->LocalPeerSpi == Spi) { | |
RemoveEntryList (Entry); | |
return ChildSaSession; | |
} | |
} | |
return NULL; | |
} | |
/** | |
Mark a specified Child SA Session as on deleting. | |
@param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION. | |
@retval EFI_SUCCESS Operation is successful. | |
**/ | |
EFI_STATUS | |
Ikev2ChildSaSessionOnDeleting ( | |
IN IKEV2_CHILD_SA_SESSION *ChildSaSession | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
Free the memory located for the specified IKEV2_CHILD_SA_SESSION. | |
@param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION. | |
**/ | |
VOID | |
Ikev2ChildSaSessionFree ( | |
IN IKEV2_CHILD_SA_SESSION *ChildSaSession | |
) | |
{ | |
IKEV2_SESSION_COMMON *SessionCommon; | |
SessionCommon = &ChildSaSession->SessionCommon; | |
if (ChildSaSession->SaData != NULL) { | |
FreePool (ChildSaSession->SaData); | |
} | |
if (ChildSaSession->NiBlock != NULL) { | |
FreePool (ChildSaSession->NiBlock); | |
} | |
if (ChildSaSession->NrBlock != NULL) { | |
FreePool (ChildSaSession->NrBlock); | |
} | |
if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey != NULL) { | |
FreePool (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey); | |
} | |
if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey != NULL) { | |
FreePool (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey); | |
} | |
if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey != NULL) { | |
FreePool (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey); | |
} | |
if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey != NULL) { | |
FreePool (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey); | |
} | |
// | |
// Delete DhBuffer | |
// | |
Ikev2DhBufferFree (ChildSaSession->DhBuffer); | |
// | |
// Delete SpdSelector | |
// | |
if (ChildSaSession->SpdSelector != NULL) { | |
if (ChildSaSession->SpdSelector->LocalAddress != NULL) { | |
FreePool (ChildSaSession->SpdSelector->LocalAddress); | |
} | |
if (ChildSaSession->SpdSelector->RemoteAddress != NULL) { | |
FreePool (ChildSaSession->SpdSelector->RemoteAddress); | |
} | |
FreePool (ChildSaSession->SpdSelector); | |
} | |
Ikev2SaSessionCommonFree (SessionCommon); | |
FreePool (ChildSaSession); | |
return ; | |
} | |
/** | |
Delete the specified established Child SA. | |
This function delete the Child SA directly and don't send the Information Packet to | |
remote peer. | |
@param[in] IkeSaSession Pointer to a IKE SA Session used to be searched for. | |
@param[in] Spi SPI used to find the Child SA. | |
@retval EFI_NOT_FOUND Pointer of IKE SA Session is NULL. | |
@retval EFI_NOT_FOUND There is no specified Child SA related with the input | |
SPI under this IKE SA Session. | |
@retval EFI_SUCCESS Delete the Child SA successfully. | |
**/ | |
EFI_STATUS | |
Ikev2ChildSaSilentDelete ( | |
IN IKEV2_SA_SESSION *IkeSaSession, | |
IN UINT32 Spi | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_IPSEC_CONFIG_SELECTOR *Selector; | |
UINTN SelectorSize; | |
BOOLEAN IsLocalFound; | |
BOOLEAN IsRemoteFound; | |
UINT32 LocalSpi; | |
UINT32 RemoteSpi; | |
IKEV2_CHILD_SA_SESSION *ChildSession; | |
EFI_IPSEC_CONFIG_SELECTOR *LocalSelector; | |
EFI_IPSEC_CONFIG_SELECTOR *RemoteSelector; | |
IPSEC_PRIVATE_DATA *Private; | |
if (IkeSaSession == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
IsLocalFound = FALSE; | |
IsRemoteFound = FALSE; | |
ChildSession = NULL; | |
LocalSelector = NULL; | |
RemoteSelector = NULL; | |
Private = IkeSaSession->SessionCommon.Private; | |
// | |
// Remove the Established SA from ChildSaEstablishlist. | |
// | |
ChildSession = Ikev2ChildSaSessionRemove( | |
&(IkeSaSession->ChildSaEstablishSessionList), | |
Spi, | |
IKEV2_ESTABLISHED_CHILDSA_LIST | |
); | |
if (ChildSession == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
LocalSpi = ChildSession->LocalPeerSpi; | |
RemoteSpi = ChildSession->RemotePeerSpi; | |
SelectorSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR); | |
Selector = AllocateZeroPool (SelectorSize); | |
if (Selector == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
while (1) { | |
Status = EfiIpSecConfigGetNextSelector ( | |
&Private->IpSecConfig, | |
IPsecConfigDataTypeSad, | |
&SelectorSize, | |
Selector | |
); | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
FreePool (Selector); | |
Selector = AllocateZeroPool (SelectorSize); | |
if (Selector == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
break; | |
} | |
Status = EfiIpSecConfigGetNextSelector ( | |
&Private->IpSecConfig, | |
IPsecConfigDataTypeSad, | |
&SelectorSize, | |
Selector | |
); | |
} | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
if (Selector->SaId.Spi == RemoteSpi) { | |
// | |
// SPI is unique. There is only one SAD whose SPI is | |
// same with RemoteSpi. | |
// | |
IsRemoteFound = TRUE; | |
RemoteSelector = AllocateZeroPool (SelectorSize); | |
if (RemoteSelector == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
break; | |
} | |
CopyMem (RemoteSelector, Selector, SelectorSize); | |
} | |
if (Selector->SaId.Spi == LocalSpi) { | |
// | |
// SPI is unique. There is only one SAD whose SPI is | |
// same with LocalSpi. | |
// | |
IsLocalFound = TRUE; | |
LocalSelector = AllocateZeroPool (SelectorSize); | |
if (LocalSelector == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
break; | |
} | |
CopyMem (LocalSelector, Selector, SelectorSize); | |
} | |
} | |
// | |
// Delete SA from the Variable. | |
// | |
if (IsLocalFound) { | |
Status = EfiIpSecConfigSetData ( | |
&Private->IpSecConfig, | |
IPsecConfigDataTypeSad, | |
LocalSelector, | |
NULL, | |
NULL | |
); | |
} | |
if (IsRemoteFound) { | |
Status = EfiIpSecConfigSetData ( | |
&Private->IpSecConfig, | |
IPsecConfigDataTypeSad, | |
RemoteSelector, | |
NULL, | |
NULL | |
); | |
} | |
DEBUG ( | |
(DEBUG_INFO, | |
"\n------IKEV2 deleted ChildSa(local spi, remote spi):(0x%x, 0x%x)------\n", | |
LocalSpi, | |
RemoteSpi) | |
); | |
Ikev2ChildSaSessionFree (ChildSession); | |
if (RemoteSelector != NULL) { | |
FreePool (RemoteSelector); | |
} | |
if (LocalSelector != NULL) { | |
FreePool (LocalSelector); | |
} | |
if (Selector != NULL) { | |
FreePool (Selector); | |
} | |
return Status; | |
} | |
/** | |
Free the specified DhBuffer. | |
@param[in] DhBuffer Pointer to IKEV2_DH_BUFFER to be freed. | |
**/ | |
VOID | |
Ikev2DhBufferFree ( | |
IKEV2_DH_BUFFER *DhBuffer | |
) | |
{ | |
if (DhBuffer != NULL) { | |
if (DhBuffer->GxBuffer != NULL) { | |
FreePool (DhBuffer->GxBuffer); | |
} | |
if (DhBuffer->GyBuffer != NULL) { | |
FreePool (DhBuffer->GyBuffer); | |
} | |
if (DhBuffer->GxyBuffer != NULL) { | |
FreePool (DhBuffer->GxyBuffer); | |
} | |
if (DhBuffer->DhContext != NULL) { | |
IpSecCryptoIoFreeDh (&DhBuffer->DhContext); | |
} | |
FreePool (DhBuffer); | |
} | |
} | |
/** | |
This function is to parse a request IKE packet and return its request type. | |
The request type is one of IKE CHILD SA creation, IKE SA rekeying and | |
IKE CHILD SA rekeying. | |
@param[in] IkePacket IKE packet to be prased. | |
return the type of the IKE packet. | |
**/ | |
IKEV2_CREATE_CHILD_REQUEST_TYPE | |
Ikev2ChildExchangeRequestType( | |
IN IKE_PACKET *IkePacket | |
) | |
{ | |
BOOLEAN Flag; | |
LIST_ENTRY *Entry; | |
IKE_PAYLOAD *IkePayload; | |
Flag = FALSE; | |
NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { | |
IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); | |
if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_INIT) { | |
// | |
// Packet with Ts Payload means it is for either CHILD_SA_CREATE or CHILD_SA_REKEY. | |
// | |
Flag = TRUE; | |
} | |
if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_NOTIFY) { | |
if (((IKEV2_NOTIFY*)IkePayload)->MessageType == IKEV2_NOTIFICATION_REKEY_SA) { | |
// | |
// If notify payload with REKEY_SA message type, the IkePacket is for | |
// rekeying Child SA. | |
// | |
return IkeRequestTypeRekeyChildSa; | |
} | |
} | |
}; | |
if (!Flag){ | |
// | |
// The Create Child Exchange is for IKE SA rekeying. | |
// | |
return IkeRequestTypeRekeyIkeSa; | |
} else { | |
// | |
// If the Notify payloaad with transport mode message type, the IkePacket is | |
// for create Child SA. | |
// | |
return IkeRequestTypeCreateChildSa; | |
} | |
} | |
/** | |
Associate a SPD selector to the Child SA Session. | |
This function is called when the Child SA is not the first child SA of its | |
IKE SA. It associate a SPD to this Child SA. | |
@param[in, out] ChildSaSession Pointer to the Child SA Session to be associated to | |
a SPD selector. | |
@retval EFI_SUCCESS Associate one SPD selector to this Child SA Session successfully. | |
@retval EFI_NOT_FOUND Can't find the related SPD selector. | |
**/ | |
EFI_STATUS | |
Ikev2ChildSaAssociateSpdEntry ( | |
IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession | |
) | |
{ | |
IpSecVisitConfigData (IPsecConfigDataTypeSpd, Ikev2MatchSpdEntry, ChildSaSession); | |
if (ChildSaSession->Spd != NULL) { | |
return EFI_SUCCESS; | |
} else { | |
return EFI_NOT_FOUND; | |
} | |
} | |
/** | |
This function finds the SPI from Create Child SA Exchange Packet. | |
@param[in] IkePacket Pointer to IKE_PACKET to be searched. | |
@retval SPI number or 0 if it is not supported. | |
**/ | |
UINT32 | |
Ikev2ChildExchangeRekeySpi ( | |
IN IKE_PACKET *IkePacket | |
) | |
{ | |
// | |
// Not support yet. | |
// | |
return 0; | |
} | |
/** | |
Validate the IKE header of received IKE packet. | |
@param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to this IKE packet. | |
@param[in] IkeHdr Pointer to IKE header of received IKE packet. | |
@retval TRUE If the IKE header is valid. | |
@retval FALSE If the IKE header is invalid. | |
**/ | |
BOOLEAN | |
Ikev2ValidateHeader ( | |
IN IKEV2_SA_SESSION *IkeSaSession, | |
IN IKE_HEADER *IkeHdr | |
) | |
{ | |
IKEV2_SESSION_STATE State; | |
State = IkeSaSession->SessionCommon.State; | |
if (State == IkeStateInit) { | |
// | |
// For the IKE Initial Exchange, the MessagId should be zero. | |
// | |
if (IkeHdr->MessageId != 0) { | |
return FALSE; | |
} | |
} else { | |
if (State == IkeStateAuth) { | |
if (IkeHdr->MessageId != 1) { | |
return FALSE; | |
} | |
} | |
if (IkeHdr->InitiatorCookie != IkeSaSession->InitiatorCookie || | |
IkeHdr->ResponderCookie != IkeSaSession->ResponderCookie | |
) { | |
// | |
// TODO: send notification INVALID-COOKIE | |
// | |
return FALSE; | |
} | |
} | |
// | |
// Information Exchagne and Create Child Exchange can be started from each part. | |
// | |
if (IkeHdr->ExchangeType != IKEV2_EXCHANGE_TYPE_INFO && | |
IkeHdr->ExchangeType != IKEV2_EXCHANGE_TYPE_CREATE_CHILD | |
) { | |
if (IkeSaSession->SessionCommon.IsInitiator) { | |
if (IkeHdr->InitiatorCookie != IkeSaSession->InitiatorCookie) { | |
// | |
// TODO: send notification INVALID-COOKIE | |
// | |
return FALSE; | |
} | |
if (IkeHdr->Flags != IKE_HEADER_FLAGS_RESPOND) { | |
return FALSE; | |
} | |
} else { | |
if (IkeHdr->Flags != IKE_HEADER_FLAGS_INIT) { | |
return FALSE; | |
} | |
} | |
} | |
return TRUE; | |
} | |
/** | |
Create and intialize IKEV2_SA_DATA for speicifed IKEV2_SESSION_COMMON. | |
This function will be only called by the initiator. The responder's IKEV2_SA_DATA | |
will be generated during parsed the initiator packet. | |
@param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to. | |
@retval a Pointer to a new IKEV2_SA_DATA or NULL. | |
**/ | |
IKEV2_SA_DATA * | |
Ikev2InitializeSaData ( | |
IN IKEV2_SESSION_COMMON *SessionCommon | |
) | |
{ | |
IKEV2_CHILD_SA_SESSION *ChildSaSession; | |
IKEV2_SA_DATA *SaData; | |
IKEV2_PROPOSAL_DATA *ProposalData; | |
IKEV2_TRANSFORM_DATA *TransformData; | |
IKE_SA_ATTRIBUTE *Attribute; | |
ASSERT (SessionCommon != NULL); | |
// | |
// TODO: Remove the hard code of the support Alogrithm. Those data should be | |
// get from the SPD/PAD data. | |
// | |
if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { | |
SaData = AllocateZeroPool ( | |
sizeof (IKEV2_SA_DATA) + | |
sizeof (IKEV2_PROPOSAL_DATA) * 2 + | |
sizeof (IKEV2_TRANSFORM_DATA) * 4 * 2 | |
); | |
} else { | |
SaData = AllocateZeroPool ( | |
sizeof (IKEV2_SA_DATA) + | |
sizeof (IKEV2_PROPOSAL_DATA) * 2 + | |
sizeof (IKEV2_TRANSFORM_DATA) * 3 * 2 | |
); | |
} | |
if (SaData == NULL) { | |
return NULL; | |
} | |
// | |
// First proposal payload: 3DES + SHA1 + DH | |
// | |
SaData->NumProposals = 2; | |
ProposalData = (IKEV2_PROPOSAL_DATA *) (SaData + 1); | |
ProposalData->ProposalIndex = 1; | |
// | |
// If SA data for IKE_SA_INIT exchage, contains 4 transforms. If SA data for | |
// IKE_AUTH exchange contains 3 transforms. | |
// | |
if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { | |
ProposalData->NumTransforms = 4; | |
} else { | |
ProposalData->NumTransforms = 3; | |
} | |
if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { | |
ProposalData->ProtocolId = IPSEC_PROTO_ISAKMP; | |
} else { | |
ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); | |
ProposalData->ProtocolId = IPSEC_PROTO_IPSEC_ESP; | |
ProposalData->Spi = AllocateZeroPool (sizeof (ChildSaSession->LocalPeerSpi)); | |
if (ProposalData->Spi == NULL) { | |
FreePool (SaData); | |
return NULL; | |
} | |
CopyMem ( | |
ProposalData->Spi, | |
&ChildSaSession->LocalPeerSpi, | |
sizeof(ChildSaSession->LocalPeerSpi) | |
); | |
} | |
// | |
// Set transform attribute for Encryption Algorithm - 3DES | |
// | |
TransformData = (IKEV2_TRANSFORM_DATA *) (ProposalData + 1); | |
TransformData->TransformIndex = 0; | |
TransformData->TransformType = IKEV2_TRANSFORM_TYPE_ENCR; | |
TransformData->TransformId = IKEV2_TRANSFORM_ID_ENCR_3DES; | |
// | |
// Set transform attribute for Integrity Algorithm - SHA1_96 | |
// | |
TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); | |
TransformData->TransformIndex = 1; | |
TransformData->TransformType = IKEV2_TRANSFORM_TYPE_INTEG; | |
TransformData->TransformId = IKEV2_TRANSFORM_ID_AUTH_HMAC_SHA1_96; | |
if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { | |
// | |
// Set transform attribute for Pseduo-Random Function - HAMC_SHA1 | |
// | |
TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); | |
TransformData->TransformIndex = 2; | |
TransformData->TransformType = IKEV2_TRANSFORM_TYPE_PRF; | |
TransformData->TransformId = IKEV2_TRANSFORM_ID_PRF_HMAC_SHA1; | |
} | |
if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { | |
// | |
// Set transform attribute for DH Group - DH 1024 | |
// | |
TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); | |
TransformData->TransformIndex = 3; | |
TransformData->TransformType = IKEV2_TRANSFORM_TYPE_DH; | |
TransformData->TransformId = IKEV2_TRANSFORM_ID_DH_1024MODP; | |
} else { | |
// | |
// Transform type for Extended Sequence Numbers. Currently not support Extended | |
// Sequence Number. | |
// | |
TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); | |
TransformData->TransformIndex = 2; | |
TransformData->TransformType = IKEV2_TRANSFORM_TYPE_ESN; | |
TransformData->TransformId = 0; | |
} | |
// | |
// Second proposal payload: 3DES + SHA1 + DH | |
// | |
ProposalData = (IKEV2_PROPOSAL_DATA *) (TransformData + 1); | |
ProposalData->ProposalIndex = 2; | |
if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { | |
ProposalData->ProtocolId = IPSEC_PROTO_ISAKMP; | |
ProposalData->NumTransforms = 4; | |
} else { | |
ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); | |
ProposalData->ProtocolId = IPSEC_PROTO_IPSEC_ESP; | |
ProposalData->NumTransforms = 3; | |
ProposalData->Spi = AllocateZeroPool (sizeof (ChildSaSession->LocalPeerSpi)); | |
if (ProposalData->Spi == NULL) { | |
FreePool (((IKEV2_PROPOSAL_DATA *) (SaData + 1))->Spi); | |
FreePool (SaData); | |
return NULL; | |
} | |
CopyMem ( | |
ProposalData->Spi, | |
&ChildSaSession->LocalPeerSpi, | |
sizeof(ChildSaSession->LocalPeerSpi) | |
); | |
} | |
// | |
// Set transform attribute for Encryption Algorithm - AES-CBC | |
// | |
TransformData = (IKEV2_TRANSFORM_DATA *) (ProposalData + 1); | |
TransformData->TransformIndex = 0; | |
TransformData->TransformType = IKEV2_TRANSFORM_TYPE_ENCR; | |
TransformData->TransformId = IKEV2_TRANSFORM_ID_ENCR_AES_CBC; | |
Attribute = &TransformData->Attribute; | |
Attribute->AttrType = IKEV2_ATTRIBUTE_TYPE_KEYLEN; | |
Attribute->Attr.AttrLength = (UINT16) (8 * IpSecGetEncryptKeyLength (IKEV2_TRANSFORM_ID_ENCR_AES_CBC)); | |
// | |
// Set transform attribute for Integrity Algorithm - SHA1_96 | |
// | |
TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); | |
TransformData->TransformIndex = 1; | |
TransformData->TransformType = IKEV2_TRANSFORM_TYPE_INTEG; | |
TransformData->TransformId = IKEV2_TRANSFORM_ID_AUTH_HMAC_SHA1_96; | |
if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { | |
// | |
// Set transform attribute for Pseduo-Random Function - HAMC_SHA1 | |
// | |
TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); | |
TransformData->TransformIndex = 2; | |
TransformData->TransformType = IKEV2_TRANSFORM_TYPE_PRF; | |
TransformData->TransformId = IKEV2_TRANSFORM_ID_PRF_HMAC_SHA1; | |
} | |
if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { | |
// | |
// Set transform attrbiute for DH Group - DH-1024 | |
// | |
TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); | |
TransformData->TransformIndex = 3; | |
TransformData->TransformType = IKEV2_TRANSFORM_TYPE_DH; | |
TransformData->TransformId = IKEV2_TRANSFORM_ID_DH_1024MODP; | |
} else { | |
// | |
// Transform type for Extended Sequence Numbers. Currently not support Extended | |
// Sequence Number. | |
// | |
TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); | |
TransformData->TransformIndex = 2; | |
TransformData->TransformType = IKEV2_TRANSFORM_TYPE_ESN; | |
TransformData->TransformId = 0; | |
} | |
return SaData; | |
} | |
/** | |
Store the SA into SAD. | |
@param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION. | |
**/ | |
VOID | |
Ikev2StoreSaData ( | |
IN IKEV2_CHILD_SA_SESSION *ChildSaSession | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_IPSEC_SA_ID SaId; | |
EFI_IPSEC_SA_DATA2 SaData; | |
IKEV2_SESSION_COMMON *SessionCommon; | |
IPSEC_PRIVATE_DATA *Private; | |
UINT32 TempAddressCount; | |
EFI_IP_ADDRESS_INFO *TempAddressInfo; | |
SessionCommon = &ChildSaSession->SessionCommon; | |
Private = SessionCommon->Private; | |
ZeroMem (&SaId, sizeof (EFI_IPSEC_SA_ID)); | |
ZeroMem (&SaData, sizeof (EFI_IPSEC_SA_DATA2)); | |
// | |
// Create a SpdSelector. In this implementation, one SPD represents | |
// 2 direction traffic, so in here, there needs to reverse the local address | |
// and remote address for Remote Peer's SA, then reverse again for the locate | |
// SA. | |
// | |
TempAddressCount = ChildSaSession->SpdSelector->LocalAddressCount; | |
TempAddressInfo = ChildSaSession->SpdSelector->LocalAddress; | |
ChildSaSession->SpdSelector->LocalAddressCount = ChildSaSession->SpdSelector->RemoteAddressCount; | |
ChildSaSession->SpdSelector->LocalAddress = ChildSaSession->SpdSelector->RemoteAddress; | |
ChildSaSession->SpdSelector->RemoteAddress = TempAddressInfo; | |
ChildSaSession->SpdSelector->RemoteAddressCount= TempAddressCount; | |
// | |
// Set the SaId and SaData. | |
// | |
SaId.Spi = ChildSaSession->LocalPeerSpi; | |
SaId.Proto = EfiIPsecESP; | |
SaData.AntiReplayWindows = 16; | |
SaData.SNCount = 0; | |
SaData.Mode = ChildSaSession->Spd->Data->ProcessingPolicy->Mode; | |
// | |
// If it is tunnel mode, should add the TunnelDest and TunnelSource for SaData. | |
// | |
if (SaData.Mode == EfiIPsecTunnel) { | |
CopyMem ( | |
&SaData.TunnelSourceAddress, | |
&ChildSaSession->Spd->Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress, | |
sizeof (EFI_IP_ADDRESS) | |
); | |
CopyMem ( | |
&SaData.TunnelDestinationAddress, | |
&ChildSaSession->Spd->Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress, | |
sizeof (EFI_IP_ADDRESS) | |
); | |
} | |
CopyMem (&SaId.DestAddress, &ChildSaSession->SessionCommon.LocalPeerIp, sizeof (EFI_IP_ADDRESS)); | |
CopyMem (&SaData.AlgoInfo, &ChildSaSession->ChildKeymats.LocalPeerInfo, sizeof (EFI_IPSEC_ALGO_INFO)); | |
SaData.SpdSelector = ChildSaSession->SpdSelector; | |
// | |
// Store the remote SA into SAD. | |
// | |
Status = EfiIpSecConfigSetData ( | |
&Private->IpSecConfig, | |
IPsecConfigDataTypeSad, | |
(EFI_IPSEC_CONFIG_SELECTOR *) &SaId, | |
&SaData, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Store the local SA into SAD. | |
// | |
ChildSaSession->SpdSelector->RemoteAddressCount = ChildSaSession->SpdSelector->LocalAddressCount; | |
ChildSaSession->SpdSelector->RemoteAddress = ChildSaSession->SpdSelector->LocalAddress; | |
ChildSaSession->SpdSelector->LocalAddress = TempAddressInfo; | |
ChildSaSession->SpdSelector->LocalAddressCount = TempAddressCount; | |
SaId.Spi = ChildSaSession->RemotePeerSpi; | |
CopyMem (&SaId.DestAddress, &ChildSaSession->SessionCommon.RemotePeerIp, sizeof (EFI_IP_ADDRESS)); | |
CopyMem (&SaData.AlgoInfo, &ChildSaSession->ChildKeymats.RemotePeerInfo, sizeof (EFI_IPSEC_ALGO_INFO)); | |
SaData.SpdSelector = ChildSaSession->SpdSelector; | |
// | |
// If it is tunnel mode, should add the TunnelDest and TunnelSource for SaData. | |
// | |
if (SaData.Mode == EfiIPsecTunnel) { | |
CopyMem ( | |
&SaData.TunnelSourceAddress, | |
&ChildSaSession->Spd->Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress, | |
sizeof (EFI_IP_ADDRESS) | |
); | |
CopyMem ( | |
&SaData.TunnelDestinationAddress, | |
&ChildSaSession->Spd->Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress, | |
sizeof (EFI_IP_ADDRESS) | |
); | |
} | |
Status = EfiIpSecConfigSetData ( | |
&Private->IpSecConfig, | |
IPsecConfigDataTypeSad, | |
(EFI_IPSEC_CONFIG_SELECTOR *) &SaId, | |
&SaData, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
} | |
/** | |
Call back function of the IKE life time is over. | |
This function will mark the related IKE SA Session as deleting and trigger a | |
Information negotiation. | |
@param[in] Event The signaled Event. | |
@param[in] Context Pointer to data passed by caller. | |
**/ | |
VOID | |
EFIAPI | |
Ikev2LifetimeNotify ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
IKEV2_SA_SESSION *IkeSaSession; | |
IKEV2_CHILD_SA_SESSION *ChildSaSession; | |
IKEV2_SESSION_COMMON *SessionCommon; | |
ASSERT (Context != NULL); | |
SessionCommon = (IKEV2_SESSION_COMMON *) Context; | |
if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { | |
IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); | |
DEBUG (( | |
DEBUG_INFO, | |
"\n---IkeSa Lifetime is out(cookie_i, cookie_r):(0x%lx, 0x%lx)---\n", | |
IkeSaSession->InitiatorCookie, | |
IkeSaSession->ResponderCookie | |
)); | |
// | |
// Change the IKE SA Session's State to IKE_STATE_SA_DELETING. | |
// | |
IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateSaDeleting); | |
IkeSaSession->SessionCommon.State = IkeStateSaDeleting; | |
} else { | |
ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); | |
IkeSaSession = ChildSaSession->IkeSaSession; | |
// | |
// Link the timeout child SA to the DeleteSaList. | |
// | |
InsertTailList (&IkeSaSession->DeleteSaList, &ChildSaSession->ByDelete); | |
// | |
// Change the Child SA Session's State to IKE_STATE_SA_DELETING. | |
// | |
DEBUG (( | |
DEBUG_INFO, | |
"\n------ChildSa Lifetime is out(SPI):(0x%x)------\n", | |
ChildSaSession->LocalPeerSpi | |
)); | |
} | |
// | |
// TODO: Send the delete info packet or delete silently | |
// | |
mIkev2Exchange.NegotiateInfo ((UINT8 *) IkeSaSession, NULL); | |
} | |
/** | |
This function will be called if the TimeOut Event is signaled. | |
@param[in] Event The signaled Event. | |
@param[in] Context The data passed by caller. | |
**/ | |
VOID | |
EFIAPI | |
Ikev2ResendNotify ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
IPSEC_PRIVATE_DATA *Private; | |
IKEV2_SA_SESSION *IkeSaSession; | |
IKEV2_CHILD_SA_SESSION *ChildSaSession; | |
IKEV2_SESSION_COMMON *SessionCommon; | |
LIST_ENTRY *ChildSaEntry; | |
UINT8 Value; | |
EFI_STATUS Status; | |
ASSERT (Context != NULL); | |
IkeSaSession = NULL; | |
ChildSaSession = NULL; | |
SessionCommon = (IKEV2_SESSION_COMMON *) Context; | |
Private = SessionCommon->Private; | |
// | |
// Remove the SA session from the processing list if exceed the max retry. | |
// | |
if (SessionCommon->RetryCount > IKE_MAX_RETRY) { | |
if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { | |
IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); | |
if (IkeSaSession->SessionCommon.State == IkeStateSaDeleting) { | |
// | |
// If the IkeSaSession is initiator, delete all its Child SAs before removing IKE SA. | |
// If the IkesaSession is responder, all ChildSa has been remove in Ikev2HandleInfo(); | |
// | |
for (ChildSaEntry = IkeSaSession->ChildSaEstablishSessionList.ForwardLink; | |
ChildSaEntry != &IkeSaSession->ChildSaEstablishSessionList; | |
) { | |
ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (ChildSaEntry); | |
// | |
// Move to next ChildSa Entry. | |
// | |
ChildSaEntry = ChildSaEntry->ForwardLink; | |
// | |
// Delete LocalSpi & RemoteSpi and remove the ChildSaSession from the | |
// EstablishedChildSaList. | |
// | |
Ikev2ChildSaSilentDelete (IkeSaSession, ChildSaSession->LocalPeerSpi); | |
} | |
// | |
// If the IKE SA Delete Payload wasn't sent out successfully, Delete it from the EstablishedList. | |
// | |
Ikev2SaSessionRemove (&Private->Ikev2EstablishedList, &SessionCommon->RemotePeerIp); | |
if (Private != NULL && Private->IsIPsecDisabling) { | |
// | |
// After all IKE SAs were deleted, set the IPSEC_STATUS_DISABLED value in | |
// IPsec status variable. | |
// | |
if (IsListEmpty (&Private->Ikev1EstablishedList) && IsListEmpty (&Private->Ikev2EstablishedList)) { | |
Value = IPSEC_STATUS_DISABLED; | |
Status = gRT->SetVariable ( | |
IPSECCONFIG_STATUS_NAME, | |
&gEfiIpSecConfigProtocolGuid, | |
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
sizeof (Value), | |
&Value | |
); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Set the Disabled Flag in Private data. | |
// | |
Private->IpSec.DisabledFlag = TRUE; | |
Private->IsIPsecDisabling = FALSE; | |
} | |
} | |
} | |
} else { | |
Ikev2SaSessionRemove (&Private->Ikev2SessionList, &SessionCommon->RemotePeerIp); | |
} | |
Ikev2SaSessionFree (IkeSaSession); | |
} else { | |
// | |
// If the packet sent by Child SA. | |
// | |
ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); | |
IkeSaSession = ChildSaSession->IkeSaSession; | |
if (ChildSaSession->SessionCommon.State == IkeStateSaDeleting) { | |
// | |
// Established Child SA should be remove from the SAD entry and | |
// DeleteList. The function of Ikev2DeleteChildSaSilent() will remove | |
// the childSA from the IkeSaSession->ChildSaEstablishedList. So there | |
// is no need to remove it here. | |
// | |
Ikev2ChildSaSilentDelete (IkeSaSession, ChildSaSession->LocalPeerSpi); | |
Ikev2ChildSaSessionRemove ( | |
&IkeSaSession->DeleteSaList, | |
ChildSaSession->LocalPeerSpi, | |
IKEV2_DELET_CHILDSA_LIST | |
); | |
} else { | |
Ikev2ChildSaSessionRemove ( | |
&IkeSaSession->ChildSaSessionList, | |
ChildSaSession->LocalPeerSpi, | |
IKEV2_ESTABLISHING_CHILDSA_LIST | |
); | |
} | |
Ikev2ChildSaSessionFree (ChildSaSession); | |
} | |
return ; | |
} | |
// | |
// Increase the retry count. | |
// | |
SessionCommon->RetryCount++; | |
DEBUG ((DEBUG_INFO, ">>>Resending the last packet ...\n")); | |
// | |
// Resend the last packet. | |
// | |
Ikev2SendIkePacket ( | |
SessionCommon->UdpService, | |
(UINT8*)SessionCommon, | |
SessionCommon->LastSentPacket, | |
0 | |
); | |
} | |
/** | |
Copy ChildSaSession->Spd->Selector to ChildSaSession->SpdSelector. | |
ChildSaSession->SpdSelector stores the real Spdselector for its SA. Sometime, | |
the SpdSelector in ChildSaSession is more accurated or the scope is smaller | |
than the one in ChildSaSession->Spd, especially for the tunnel mode. | |
@param[in, out] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION related to. | |
@retval EFI_SUCCESS The operation complete successfully. | |
@retval EFI_OUT_OF_RESOURCES If the required resource can't be allocated. | |
**/ | |
EFI_STATUS | |
Ikev2ChildSaSessionSpdSelectorCreate ( | |
IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession | |
) | |
{ | |
EFI_STATUS Status; | |
Status = EFI_SUCCESS; | |
if (ChildSaSession->Spd != NULL && ChildSaSession->Spd->Selector != NULL) { | |
if (ChildSaSession->SpdSelector == NULL) { | |
ChildSaSession->SpdSelector = AllocateZeroPool (sizeof (EFI_IPSEC_SPD_SELECTOR)); | |
if (ChildSaSession->SpdSelector == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
return Status; | |
} | |
} | |
CopyMem ( | |
ChildSaSession->SpdSelector, | |
ChildSaSession->Spd->Selector, | |
sizeof (EFI_IPSEC_SPD_SELECTOR) | |
); | |
ChildSaSession->SpdSelector->RemoteAddress = AllocateCopyPool ( | |
ChildSaSession->Spd->Selector->RemoteAddressCount * | |
sizeof (EFI_IP_ADDRESS_INFO), | |
ChildSaSession->Spd->Selector->RemoteAddress | |
); | |
if (ChildSaSession->SpdSelector->RemoteAddress == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
FreePool (ChildSaSession->SpdSelector); | |
return Status; | |
} | |
ChildSaSession->SpdSelector->LocalAddress = AllocateCopyPool ( | |
ChildSaSession->Spd->Selector->LocalAddressCount * | |
sizeof (EFI_IP_ADDRESS_INFO), | |
ChildSaSession->Spd->Selector->LocalAddress | |
); | |
if (ChildSaSession->SpdSelector->LocalAddress == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
FreePool (ChildSaSession->SpdSelector->RemoteAddress); | |
FreePool (ChildSaSession->SpdSelector); | |
return Status; | |
} | |
ChildSaSession->SpdSelector->RemoteAddressCount = ChildSaSession->Spd->Selector->RemoteAddressCount; | |
ChildSaSession->SpdSelector->LocalAddressCount = ChildSaSession->Spd->Selector->LocalAddressCount; | |
} | |
return Status; | |
} | |
/** | |
Generate a ChildSa Session and insert it into related IkeSaSession. | |
@param[in] IkeSaSession Pointer to related IKEV2_SA_SESSION. | |
@param[in] UdpService Pointer to related IKE_UDP_SERVICE. | |
@return pointer of IKEV2_CHILD_SA_SESSION. | |
**/ | |
IKEV2_CHILD_SA_SESSION * | |
Ikev2ChildSaSessionCreate ( | |
IN IKEV2_SA_SESSION *IkeSaSession, | |
IN IKE_UDP_SERVICE *UdpService | |
) | |
{ | |
IKEV2_CHILD_SA_SESSION *ChildSaSession; | |
IKEV2_SESSION_COMMON *ChildSaCommon; | |
// | |
// Create a new ChildSaSession.Insert it into processing list and initiate the common parameters. | |
// | |
ChildSaSession = Ikev2ChildSaSessionAlloc (UdpService, IkeSaSession); | |
if (ChildSaSession == NULL) { | |
return NULL; | |
} | |
// | |
// Set the specific parameters. | |
// | |
ChildSaSession->Spd = IkeSaSession->Spd; | |
ChildSaCommon = &ChildSaSession->SessionCommon; | |
ChildSaCommon->IsInitiator = IkeSaSession->SessionCommon.IsInitiator; | |
if (IkeSaSession->SessionCommon.State == IkeStateAuth) { | |
ChildSaCommon->State = IkeStateAuth; | |
IKEV2_DUMP_STATE (ChildSaCommon->State, IkeStateAuth); | |
} else { | |
ChildSaCommon->State = IkeStateCreateChild; | |
IKEV2_DUMP_STATE (ChildSaCommon->State, IkeStateCreateChild); | |
} | |
// | |
// If SPD->Selector is not NULL, copy it to the ChildSaSession->SpdSelector. | |
// The ChildSaSession->SpdSelector might be changed after the traffic selector | |
// negoniation and it will be copied into the SAData after ChildSA established. | |
// | |
if (EFI_ERROR (Ikev2ChildSaSessionSpdSelectorCreate (ChildSaSession))) { | |
Ikev2ChildSaSessionFree (ChildSaSession); | |
return NULL; | |
} | |
// | |
// Copy first NiBlock and NrBlock to ChildSa Session | |
// | |
ChildSaSession->NiBlock = AllocateZeroPool (IkeSaSession->NiBlkSize); | |
if (ChildSaSession->NiBlock == NULL) { | |
Ikev2ChildSaSessionFree (ChildSaSession); | |
return NULL; | |
} | |
ChildSaSession->NiBlkSize = IkeSaSession->NiBlkSize; | |
CopyMem (ChildSaSession->NiBlock, IkeSaSession->NiBlock, IkeSaSession->NiBlkSize); | |
ChildSaSession->NrBlock = AllocateZeroPool (IkeSaSession->NrBlkSize); | |
if (ChildSaSession->NrBlock == NULL) { | |
Ikev2ChildSaSessionFree (ChildSaSession); | |
return NULL; | |
} | |
ChildSaSession->NrBlkSize = IkeSaSession->NrBlkSize; | |
CopyMem (ChildSaSession->NrBlock, IkeSaSession->NrBlock, IkeSaSession->NrBlkSize); | |
// | |
// Only if the Create Child SA is called for the IKE_INIT Exchange and | |
// IkeSaSession is initiator (Only Initiator's SPD is not NULL), Set the | |
// Traffic Selectors related information here. | |
// | |
if (IkeSaSession->SessionCommon.State == IkeStateAuth && IkeSaSession->Spd != NULL) { | |
ChildSaSession->ProtoId = IkeSaSession->Spd->Selector->NextLayerProtocol; | |
ChildSaSession->LocalPort = IkeSaSession->Spd->Selector->LocalPort; | |
ChildSaSession->RemotePort = IkeSaSession->Spd->Selector->RemotePort; | |
} | |
// | |
// Insert the new ChildSaSession into processing child SA list. | |
// | |
Ikev2ChildSaSessionInsert (&IkeSaSession->ChildSaSessionList, ChildSaSession); | |
return ChildSaSession; | |
} | |
/** | |
Check if the SPD is related to the input Child SA Session. | |
This function is the subfunction of Ikev1AssociateSpdEntry(). It is the call | |
back function of IpSecVisitConfigData(). | |
@param[in] Type Type of the input Config Selector. | |
@param[in] Selector Pointer to the Configure Selector to be checked. | |
@param[in] Data Pointer to the Configure Selector's Data passed | |
from the caller. | |
@param[in] SelectorSize The buffer size of Selector. | |
@param[in] DataSize The buffer size of the Data. | |
@param[in] Context The data passed from the caller. It is a Child | |
SA Session in this context. | |
@retval EFI_SUCCESS The SPD Selector is not related to the Child SA Session. | |
@retval EFI_ABORTED The SPD Selector is related to the Child SA session and | |
set the ChildSaSession->Spd to point to this SPD Selector. | |
**/ | |
EFI_STATUS | |
Ikev2MatchSpdEntry ( | |
IN EFI_IPSEC_CONFIG_DATA_TYPE Type, | |
IN EFI_IPSEC_CONFIG_SELECTOR *Selector, | |
IN VOID *Data, | |
IN UINTN SelectorSize, | |
IN UINTN DataSize, | |
IN VOID *Context | |
) | |
{ | |
IKEV2_CHILD_SA_SESSION *ChildSaSession; | |
EFI_IPSEC_SPD_SELECTOR *SpdSelector; | |
EFI_IPSEC_SPD_DATA *SpdData; | |
BOOLEAN IsMatch; | |
UINT8 IpVersion; | |
ASSERT (Type == IPsecConfigDataTypeSpd); | |
SpdData = (EFI_IPSEC_SPD_DATA *) Data; | |
// | |
// Bypass all non-protect SPD entry first | |
// | |
if (SpdData->Action != EfiIPsecActionProtect) { | |
return EFI_SUCCESS; | |
} | |
ChildSaSession = (IKEV2_CHILD_SA_SESSION *) Context; | |
IpVersion = ChildSaSession->SessionCommon.UdpService->IpVersion; | |
SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) Selector; | |
IsMatch = TRUE; | |
if (SpdSelector->NextLayerProtocol == EFI_IP_PROTO_UDP && | |
SpdSelector->LocalPort == IKE_DEFAULT_PORT && | |
SpdSelector->LocalPortRange == 0 && | |
SpdSelector->RemotePort == IKE_DEFAULT_PORT && | |
SpdSelector->RemotePortRange == 0 | |
) { | |
// | |
// TODO: Skip IKE Policy here or set a SPD entry? | |
// | |
return EFI_SUCCESS; | |
} | |
if (SpdSelector->NextLayerProtocol != EFI_IPSEC_ANY_PROTOCOL && | |
SpdSelector->NextLayerProtocol != ChildSaSession->ProtoId | |
) { | |
IsMatch = FALSE; | |
} | |
if (SpdSelector->LocalPort != EFI_IPSEC_ANY_PORT && SpdSelector->LocalPort != ChildSaSession->LocalPort) { | |
IsMatch = FALSE; | |
} | |
if (SpdSelector->RemotePort != EFI_IPSEC_ANY_PORT && SpdSelector->RemotePort != ChildSaSession->RemotePort) { | |
IsMatch = FALSE; | |
} | |
IsMatch = (BOOLEAN) (IsMatch && | |
IpSecMatchIpAddress ( | |
IpVersion, | |
&ChildSaSession->SessionCommon.LocalPeerIp, | |
SpdSelector->LocalAddress, | |
SpdSelector->LocalAddressCount | |
)); | |
IsMatch = (BOOLEAN) (IsMatch && | |
IpSecMatchIpAddress ( | |
IpVersion, | |
&ChildSaSession->SessionCommon.RemotePeerIp, | |
SpdSelector->RemoteAddress, | |
SpdSelector->RemoteAddressCount | |
)); | |
if (IsMatch) { | |
ChildSaSession->Spd = IkeSearchSpdEntry (SpdSelector); | |
return EFI_ABORTED; | |
} else { | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
Check if the Algorithm ID is supported. | |
@param[in] AlgorithmId The specified Algorithm ID. | |
@param[in] Type The type used to indicate the Algorithm is for Encrypt or | |
Authentication. | |
@retval TRUE If the Algorithm ID is supported. | |
@retval FALSE If the Algorithm ID is not supported. | |
**/ | |
BOOLEAN | |
Ikev2IsSupportAlg ( | |
IN UINT16 AlgorithmId, | |
IN UINT8 Type | |
) | |
{ | |
UINT8 Index; | |
switch (Type) { | |
case IKE_ENCRYPT_TYPE : | |
for (Index = 0; Index < IKEV2_SUPPORT_ENCRYPT_ALGORITHM_NUM; Index++) { | |
if (mIkev2EncryptAlgorithmList[Index] == AlgorithmId) { | |
return TRUE; | |
} | |
} | |
break; | |
case IKE_AUTH_TYPE : | |
for (Index = 0; Index < IKEV2_SUPPORT_AUTH_ALGORITHM_NUM; Index++) { | |
if (mIkev2AuthAlgorithmList[Index] == AlgorithmId) { | |
return TRUE; | |
} | |
} | |
break; | |
case IKE_DH_TYPE : | |
for (Index = 0; Index < IKEV2_SUPPORT_DH_ALGORITHM_NUM; Index++) { | |
if (mIkev2DhGroupAlgorithmList[Index] == AlgorithmId) { | |
return TRUE; | |
} | |
} | |
break; | |
case IKE_PRF_TYPE : | |
for (Index = 0; Index < IKEV2_SUPPORT_PRF_ALGORITHM_NUM; Index++) { | |
if (mIkev2PrfAlgorithmList[Index] == AlgorithmId) { | |
return TRUE; | |
} | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Get the preferred algorithm types from ProposalData. | |
@param[in] ProposalData Pointer to related IKEV2_PROPOSAL_DATA. | |
@param[out] PreferEncryptAlgorithm Output of preferred encrypt algorithm. | |
@param[out] PreferIntegrityAlgorithm Output of preferred integrity algorithm. | |
@param[out] PreferPrfAlgorithm Output of preferred PRF algorithm. Only | |
for IKE SA. | |
@param[out] PreferDhGroup Output of preferred DH group. Only for | |
IKE SA. | |
@param[out] PreferEncryptKeylength Output of preferred encrypt key length | |
in bytes. | |
@param[out] IsSupportEsn Output of value about the Extented Sequence | |
Number is support or not. Only for Child SA. | |
@param[in] IsChildSa If it is ture, the ProposalData is for IKE | |
SA. Otherwise the proposalData is for Child SA. | |
**/ | |
VOID | |
Ikev2ParseProposalData ( | |
IN IKEV2_PROPOSAL_DATA *ProposalData, | |
OUT UINT16 *PreferEncryptAlgorithm, | |
OUT UINT16 *PreferIntegrityAlgorithm, | |
OUT UINT16 *PreferPrfAlgorithm, | |
OUT UINT16 *PreferDhGroup, | |
OUT UINTN *PreferEncryptKeylength, | |
OUT BOOLEAN *IsSupportEsn, | |
IN BOOLEAN IsChildSa | |
) | |
{ | |
IKEV2_TRANSFORM_DATA *TransformData; | |
UINT8 TransformIndex; | |
// | |
// Check input parameters. | |
// | |
if (ProposalData == NULL || | |
PreferEncryptAlgorithm == NULL || | |
PreferIntegrityAlgorithm == NULL || | |
PreferEncryptKeylength == NULL | |
) { | |
return; | |
} | |
if (IsChildSa) { | |
if (IsSupportEsn == NULL) { | |
return; | |
} | |
} else { | |
if (PreferPrfAlgorithm == NULL || PreferDhGroup == NULL) { | |
return; | |
} | |
} | |
TransformData = (IKEV2_TRANSFORM_DATA *)(ProposalData + 1); | |
for (TransformIndex = 0; TransformIndex < ProposalData->NumTransforms; TransformIndex++) { | |
switch (TransformData->TransformType) { | |
// | |
// For IKE SA there are four algorithm types. Encryption Algorithm, Pseudo-random Function, | |
// Integrity Algorithm, Diffie-Hellman Group. For Child SA, there are three algorithm types. | |
// Encryption Algorithm, Integrity Algorithm, Extended Sequence Number. | |
// | |
case IKEV2_TRANSFORM_TYPE_ENCR: | |
if (*PreferEncryptAlgorithm == 0 && Ikev2IsSupportAlg (TransformData->TransformId, IKE_ENCRYPT_TYPE)) { | |
// | |
// Check the attribute value. According to RFC, only Keylength is support. | |
// | |
if (TransformData->Attribute.AttrType == IKEV2_ATTRIBUTE_TYPE_KEYLEN) { | |
// | |
// If the Keylength is not support, continue to check the next one. | |
// | |
if (IpSecGetEncryptKeyLength ((UINT8)TransformData->TransformId) != (UINTN)(TransformData->Attribute.Attr.AttrValue >> 3)){ | |
break; | |
} else { | |
*PreferEncryptKeylength = TransformData->Attribute.Attr.AttrValue; | |
} | |
} | |
*PreferEncryptAlgorithm = TransformData->TransformId; | |
} | |
break; | |
case IKEV2_TRANSFORM_TYPE_PRF : | |
if (!IsChildSa) { | |
if (*PreferPrfAlgorithm == 0 && Ikev2IsSupportAlg (TransformData->TransformId, IKE_PRF_TYPE)) { | |
*PreferPrfAlgorithm = TransformData->TransformId; | |
} | |
} | |
break; | |
case IKEV2_TRANSFORM_TYPE_INTEG : | |
if (*PreferIntegrityAlgorithm == 0 && Ikev2IsSupportAlg (TransformData->TransformId, IKE_AUTH_TYPE)) { | |
*PreferIntegrityAlgorithm = TransformData->TransformId; | |
} | |
break; | |
case IKEV2_TRANSFORM_TYPE_DH : | |
if (!IsChildSa) { | |
if (*PreferDhGroup == 0 && Ikev2IsSupportAlg (TransformData->TransformId, IKE_DH_TYPE)) { | |
*PreferDhGroup = TransformData->TransformId; | |
} | |
} | |
break; | |
case IKEV2_TRANSFORM_TYPE_ESN : | |
if (IsChildSa) { | |
if (TransformData->TransformId != 0) { | |
*IsSupportEsn = TRUE; | |
} | |
} | |
break; | |
default: | |
break; | |
} | |
TransformData = (IKEV2_TRANSFORM_DATA *)(TransformData + 1); | |
} | |
} | |
/** | |
Parse the received Initial Exchange Packet. | |
This function parse the SA Payload and Key Payload to find out the cryptographic | |
suite for the further IKE negotiation and fill it into the IKE SA Session's | |
CommonSession->SaParams. | |
@param[in, out] IkeSaSession Pointer to related IKEV2_SA_SESSION. | |
@param[in] SaPayload The received packet. | |
@param[in] Type The received packet IKE header flag. | |
@retval TRUE If the SA proposal in Packet is acceptable. | |
@retval FALSE If the SA proposal in Packet is not acceptable. | |
**/ | |
BOOLEAN | |
Ikev2SaParseSaPayload ( | |
IN OUT IKEV2_SA_SESSION *IkeSaSession, | |
IN IKE_PAYLOAD *SaPayload, | |
IN UINT8 Type | |
) | |
{ | |
IKEV2_PROPOSAL_DATA *ProposalData; | |
UINT8 ProposalIndex; | |
UINT16 PreferEncryptAlgorithm; | |
UINT16 PreferIntegrityAlgorithm; | |
UINT16 PreferPrfAlgorithm; | |
UINT16 PreferDhGroup; | |
UINTN PreferEncryptKeylength; | |
UINT16 EncryptAlgorithm; | |
UINT16 IntegrityAlgorithm; | |
UINT16 PrfAlgorithm; | |
UINT16 DhGroup; | |
UINTN EncryptKeylength; | |
BOOLEAN IsMatch; | |
UINTN SaDataSize; | |
PreferPrfAlgorithm = 0; | |
PreferIntegrityAlgorithm = 0; | |
PreferDhGroup = 0; | |
PreferEncryptAlgorithm = 0; | |
PreferEncryptKeylength = 0; | |
PrfAlgorithm = 0; | |
IntegrityAlgorithm = 0; | |
DhGroup = 0; | |
EncryptAlgorithm = 0; | |
EncryptKeylength = 0; | |
IsMatch = FALSE; | |
if (Type == IKE_HEADER_FLAGS_INIT) { | |
ProposalData = (IKEV2_PROPOSAL_DATA *)((IKEV2_SA_DATA *)SaPayload->PayloadBuf + 1); | |
for (ProposalIndex = 0; ProposalIndex < ((IKEV2_SA_DATA *)SaPayload->PayloadBuf)->NumProposals; ProposalIndex++) { | |
// | |
// Iterate each proposal to find the perfered one. | |
// | |
if (ProposalData->ProtocolId == IPSEC_PROTO_ISAKMP && ProposalData->NumTransforms >= 4) { | |
// | |
// Get the preferred algorithms. | |
// | |
Ikev2ParseProposalData ( | |
ProposalData, | |
&PreferEncryptAlgorithm, | |
&PreferIntegrityAlgorithm, | |
&PreferPrfAlgorithm, | |
&PreferDhGroup, | |
&PreferEncryptKeylength, | |
NULL, | |
FALSE | |
); | |
if (PreferEncryptAlgorithm != 0 && | |
PreferIntegrityAlgorithm != 0 && | |
PreferPrfAlgorithm != 0 && | |
PreferDhGroup != 0 | |
) { | |
// | |
// Find the matched one. | |
// | |
IkeSaSession->SessionCommon.SaParams = AllocateZeroPool (sizeof (IKEV2_SA_PARAMS)); | |
if (IkeSaSession->SessionCommon.SaParams == NULL) { | |
return FALSE; | |
} | |
IkeSaSession->SessionCommon.SaParams->EncAlgId = PreferEncryptAlgorithm; | |
IkeSaSession->SessionCommon.SaParams->EnckeyLen = PreferEncryptKeylength; | |
IkeSaSession->SessionCommon.SaParams->DhGroup = PreferDhGroup; | |
IkeSaSession->SessionCommon.SaParams->Prf = PreferPrfAlgorithm; | |
IkeSaSession->SessionCommon.SaParams->IntegAlgId = PreferIntegrityAlgorithm; | |
IkeSaSession->SessionCommon.PreferDhGroup = PreferDhGroup; | |
// | |
// Save the matched one in IKEV2_SA_DATA for furthure calculation. | |
// | |
SaDataSize = sizeof (IKEV2_SA_DATA) + | |
sizeof (IKEV2_PROPOSAL_DATA) + | |
sizeof (IKEV2_TRANSFORM_DATA) * 4; | |
IkeSaSession->SaData = AllocateZeroPool (SaDataSize); | |
if (IkeSaSession->SaData == NULL) { | |
FreePool (IkeSaSession->SessionCommon.SaParams); | |
return FALSE; | |
} | |
IkeSaSession->SaData->NumProposals = 1; | |
// | |
// BUGBUG: Suppose the matched proposal only has 4 transforms. If | |
// The matched Proposal has more than 4 transforms means it contains | |
// one than one transform with same type. | |
// | |
CopyMem ( | |
(IKEV2_PROPOSAL_DATA *) (IkeSaSession->SaData + 1), | |
ProposalData, | |
SaDataSize - sizeof (IKEV2_SA_DATA) | |
); | |
((IKEV2_PROPOSAL_DATA *) (IkeSaSession->SaData + 1))->ProposalIndex = 1; | |
return TRUE; | |
} else { | |
PreferEncryptAlgorithm = 0; | |
PreferIntegrityAlgorithm = 0; | |
PreferPrfAlgorithm = 0; | |
PreferDhGroup = 0; | |
PreferEncryptKeylength = 0; | |
} | |
} | |
// | |
// Point to next Proposal. | |
// | |
ProposalData = (IKEV2_PROPOSAL_DATA*)((UINT8*)(ProposalData + 1) + | |
ProposalData->NumTransforms * sizeof (IKEV2_TRANSFORM_DATA)); | |
} | |
} else if (Type == IKE_HEADER_FLAGS_RESPOND) { | |
// | |
// First check the SA proposal's ProtoctolID and Transform Numbers. Since it is | |
// the responded SA proposal, suppose it only has one proposal and the transform Numbers | |
// is 4. | |
// | |
ProposalData = (IKEV2_PROPOSAL_DATA *)((IKEV2_SA_DATA *) SaPayload->PayloadBuf + 1); | |
if (ProposalData->ProtocolId != IPSEC_PROTO_ISAKMP || ProposalData->NumTransforms != 4) { | |
return FALSE; | |
} | |
// | |
// Get the preferred algorithms. | |
// | |
Ikev2ParseProposalData ( | |
ProposalData, | |
&PreferEncryptAlgorithm, | |
&PreferIntegrityAlgorithm, | |
&PreferPrfAlgorithm, | |
&PreferDhGroup, | |
&PreferEncryptKeylength, | |
NULL, | |
FALSE | |
); | |
// | |
// Check if the Sa proposal data from received packet is in the IkeSaSession->SaData. | |
// | |
ProposalData = (IKEV2_PROPOSAL_DATA *) (IkeSaSession->SaData + 1); | |
for (ProposalIndex = 0; ProposalIndex < IkeSaSession->SaData->NumProposals && (!IsMatch); ProposalIndex++) { | |
Ikev2ParseProposalData ( | |
ProposalData, | |
&EncryptAlgorithm, | |
&IntegrityAlgorithm, | |
&PrfAlgorithm, | |
&DhGroup, | |
&EncryptKeylength, | |
NULL, | |
FALSE | |
); | |
if (EncryptAlgorithm == PreferEncryptAlgorithm && | |
EncryptKeylength == PreferEncryptKeylength && | |
IntegrityAlgorithm == PreferIntegrityAlgorithm && | |
PrfAlgorithm == PreferPrfAlgorithm && | |
DhGroup == PreferDhGroup | |
) { | |
IsMatch = TRUE; | |
} else { | |
EncryptAlgorithm = 0; | |
IntegrityAlgorithm = 0; | |
PrfAlgorithm = 0; | |
DhGroup = 0; | |
EncryptKeylength = 0; | |
} | |
ProposalData = (IKEV2_PROPOSAL_DATA*)((UINT8*)(ProposalData + 1) + | |
ProposalData->NumTransforms * sizeof (IKEV2_TRANSFORM_DATA)); | |
} | |
if (IsMatch) { | |
IkeSaSession->SessionCommon.SaParams = AllocateZeroPool (sizeof (IKEV2_SA_PARAMS)); | |
if (IkeSaSession->SessionCommon.SaParams == NULL) { | |
return FALSE; | |
} | |
IkeSaSession->SessionCommon.SaParams->EncAlgId = PreferEncryptAlgorithm; | |
IkeSaSession->SessionCommon.SaParams->EnckeyLen = PreferEncryptKeylength; | |
IkeSaSession->SessionCommon.SaParams->DhGroup = PreferDhGroup; | |
IkeSaSession->SessionCommon.SaParams->Prf = PreferPrfAlgorithm; | |
IkeSaSession->SessionCommon.SaParams->IntegAlgId = PreferIntegrityAlgorithm; | |
IkeSaSession->SessionCommon.PreferDhGroup = PreferDhGroup; | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Parse the received Authentication Exchange Packet. | |
This function parse the SA Payload and Key Payload to find out the cryptographic | |
suite for the ESP and fill it into the Child SA Session's CommonSession->SaParams. | |
@param[in, out] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION related to | |
this Authentication Exchange. | |
@param[in] SaPayload The received packet. | |
@param[in] Type The IKE header's flag of received packet . | |
@retval TRUE If the SA proposal in Packet is acceptable. | |
@retval FALSE If the SA proposal in Packet is not acceptable. | |
**/ | |
BOOLEAN | |
Ikev2ChildSaParseSaPayload ( | |
IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession, | |
IN IKE_PAYLOAD *SaPayload, | |
IN UINT8 Type | |
) | |
{ | |
IKEV2_PROPOSAL_DATA *ProposalData; | |
UINT8 ProposalIndex; | |
UINT16 PreferEncryptAlgorithm; | |
UINT16 PreferIntegrityAlgorithm; | |
UINTN PreferEncryptKeylength; | |
BOOLEAN PreferIsSupportEsn; | |
UINT16 EncryptAlgorithm; | |
UINT16 IntegrityAlgorithm; | |
UINTN EncryptKeylength; | |
BOOLEAN IsSupportEsn; | |
BOOLEAN IsMatch; | |
UINTN SaDataSize; | |
PreferIntegrityAlgorithm = 0; | |
PreferEncryptAlgorithm = 0; | |
PreferEncryptKeylength = 0; | |
IntegrityAlgorithm = 0; | |
EncryptAlgorithm = 0; | |
EncryptKeylength = 0; | |
IsMatch = TRUE; | |
IsSupportEsn = FALSE; | |
PreferIsSupportEsn = FALSE; | |
if (Type == IKE_HEADER_FLAGS_INIT) { | |
ProposalData = (IKEV2_PROPOSAL_DATA *)((IKEV2_SA_DATA *) SaPayload->PayloadBuf + 1); | |
for (ProposalIndex = 0; ProposalIndex < ((IKEV2_SA_DATA *) SaPayload->PayloadBuf)->NumProposals; ProposalIndex++) { | |
// | |
// Iterate each proposal to find the preferred one. | |
// | |
if (ProposalData->ProtocolId == IPSEC_PROTO_IPSEC_ESP && ProposalData->NumTransforms >= 3) { | |
// | |
// Get the preferred algorithm. | |
// | |
Ikev2ParseProposalData ( | |
ProposalData, | |
&PreferEncryptAlgorithm, | |
&PreferIntegrityAlgorithm, | |
NULL, | |
NULL, | |
&PreferEncryptKeylength, | |
&IsSupportEsn, | |
TRUE | |
); | |
// | |
// Don't support the ESN now. | |
// | |
if (PreferEncryptAlgorithm != 0 && | |
PreferIntegrityAlgorithm != 0 && | |
!IsSupportEsn | |
) { | |
// | |
// Find the matched one. | |
// | |
ChildSaSession->SessionCommon.SaParams = AllocateZeroPool (sizeof (IKEV2_SA_PARAMS)); | |
if (ChildSaSession->SessionCommon.SaParams == NULL) { | |
return FALSE; | |
} | |
ChildSaSession->SessionCommon.SaParams->EncAlgId = PreferEncryptAlgorithm; | |
ChildSaSession->SessionCommon.SaParams->EnckeyLen = PreferEncryptKeylength; | |
ChildSaSession->SessionCommon.SaParams->IntegAlgId = PreferIntegrityAlgorithm; | |
CopyMem (&ChildSaSession->RemotePeerSpi, ProposalData->Spi, sizeof (ChildSaSession->RemotePeerSpi)); | |
// | |
// Save the matched one in IKEV2_SA_DATA for furthure calculation. | |
// | |
SaDataSize = sizeof (IKEV2_SA_DATA) + | |
sizeof (IKEV2_PROPOSAL_DATA) + | |
sizeof (IKEV2_TRANSFORM_DATA) * 4; | |
ChildSaSession->SaData = AllocateZeroPool (SaDataSize); | |
if (ChildSaSession->SaData == NULL) { | |
FreePool (ChildSaSession->SessionCommon.SaParams); | |
return FALSE; | |
} | |
ChildSaSession->SaData->NumProposals = 1; | |
// | |
// BUGBUG: Suppose there are 4 transforms in the matched proposal. If | |
// the matched Proposal has more than 4 transforms that means there | |
// are more than one transform with same type. | |
// | |
CopyMem ( | |
(IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1), | |
ProposalData, | |
SaDataSize - sizeof (IKEV2_SA_DATA) | |
); | |
((IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1))->ProposalIndex = 1; | |
((IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1))->Spi = AllocateCopyPool ( | |
sizeof (ChildSaSession->LocalPeerSpi), | |
&ChildSaSession->LocalPeerSpi | |
); | |
if (((IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1))->Spi == NULL) { | |
FreePool (ChildSaSession->SessionCommon.SaParams); | |
FreePool (ChildSaSession->SaData ); | |
return FALSE; | |
} | |
return TRUE; | |
} else { | |
PreferEncryptAlgorithm = 0; | |
PreferIntegrityAlgorithm = 0; | |
IsSupportEsn = TRUE; | |
} | |
} | |
// | |
// Point to next Proposal | |
// | |
ProposalData = (IKEV2_PROPOSAL_DATA *)((UINT8 *)(ProposalData + 1) + | |
ProposalData->NumTransforms * sizeof (IKEV2_TRANSFORM_DATA)); | |
} | |
} else if (Type == IKE_HEADER_FLAGS_RESPOND) { | |
// | |
// First check the SA proposal's ProtoctolID and Transform Numbers. Since it is | |
// the responded SA proposal, suppose it only has one proposal and the transform Numbers | |
// is 3. | |
// | |
ProposalData = (IKEV2_PROPOSAL_DATA *)((IKEV2_SA_DATA *)SaPayload->PayloadBuf + 1); | |
if (ProposalData->ProtocolId != IPSEC_PROTO_IPSEC_ESP || ProposalData->NumTransforms != 3) { | |
return FALSE; | |
} | |
// | |
// Get the preferred algorithms. | |
// | |
Ikev2ParseProposalData ( | |
ProposalData, | |
&PreferEncryptAlgorithm, | |
&PreferIntegrityAlgorithm, | |
NULL, | |
NULL, | |
&PreferEncryptKeylength, | |
&PreferIsSupportEsn, | |
TRUE | |
); | |
ProposalData = (IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1); | |
for (ProposalIndex = 0; ProposalIndex < ChildSaSession->SaData->NumProposals && (!IsMatch); ProposalIndex++) { | |
Ikev2ParseProposalData ( | |
ProposalData, | |
&EncryptAlgorithm, | |
&IntegrityAlgorithm, | |
NULL, | |
NULL, | |
&EncryptKeylength, | |
&IsSupportEsn, | |
TRUE | |
); | |
if (EncryptAlgorithm == PreferEncryptAlgorithm && | |
EncryptKeylength == PreferEncryptKeylength && | |
IntegrityAlgorithm == PreferIntegrityAlgorithm && | |
IsSupportEsn == PreferIsSupportEsn | |
) { | |
IsMatch = TRUE; | |
} else { | |
PreferEncryptAlgorithm = 0; | |
PreferIntegrityAlgorithm = 0; | |
IsSupportEsn = TRUE; | |
} | |
ProposalData = (IKEV2_PROPOSAL_DATA*)((UINT8*)(ProposalData + 1) + | |
ProposalData->NumTransforms * sizeof (IKEV2_TRANSFORM_DATA)); | |
} | |
ProposalData = (IKEV2_PROPOSAL_DATA *)((IKEV2_SA_DATA *)SaPayload->PayloadBuf + 1); | |
if (IsMatch) { | |
ChildSaSession->SessionCommon.SaParams = AllocateZeroPool (sizeof (IKEV2_SA_PARAMS)); | |
if (ChildSaSession->SessionCommon.SaParams == NULL) { | |
return FALSE; | |
} | |
ChildSaSession->SessionCommon.SaParams->EncAlgId = PreferEncryptAlgorithm; | |
ChildSaSession->SessionCommon.SaParams->EnckeyLen = PreferEncryptKeylength; | |
ChildSaSession->SessionCommon.SaParams->IntegAlgId = PreferIntegrityAlgorithm; | |
CopyMem (&ChildSaSession->RemotePeerSpi, ProposalData->Spi, sizeof (ChildSaSession->RemotePeerSpi)); | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Generate Key buffer from fragments. | |
If the digest length of specified HashAlgId is larger than or equal with the | |
required output key length, derive the key directly. Otherwise, Key Material | |
needs to be PRF-based concatenation according to 2.13 of RFC 4306: | |
prf+ (K,S) = T1 | T2 | T3 | T4 | ..., T1 = prf (K, S | 0x01), | |
T2 = prf (K, T1 | S | 0x02), T3 = prf (K, T2 | S | 0x03),T4 = prf (K, T3 | S | 0x04) | |
then derive the key from this key material. | |
@param[in] HashAlgId The Hash Algorithm ID used to generate key. | |
@param[in] HashKey Pointer to a key buffer which contains hash key. | |
@param[in] HashKeyLength The length of HashKey in bytes. | |
@param[in, out] OutputKey Pointer to buffer which is used to receive the | |
output key. | |
@param[in] OutputKeyLength The length of OutPutKey buffer. | |
@param[in] Fragments Pointer to the data to be used to generate key. | |
@param[in] NumFragments The numbers of the Fragement. | |
@retval EFI_SUCCESS The operation complete successfully. | |
@retval EFI_INVALID_PARAMETER If NumFragments is zero. | |
@retval EFI_OUT_OF_RESOURCES If the required resource can't be allocated. | |
@retval Others The operation is failed. | |
**/ | |
EFI_STATUS | |
Ikev2SaGenerateKey ( | |
IN UINT8 HashAlgId, | |
IN UINT8 *HashKey, | |
IN UINTN HashKeyLength, | |
IN OUT UINT8 *OutputKey, | |
IN UINTN OutputKeyLength, | |
IN PRF_DATA_FRAGMENT *Fragments, | |
IN UINTN NumFragments | |
) | |
{ | |
EFI_STATUS Status; | |
PRF_DATA_FRAGMENT LocalFragments[3]; | |
UINT8 *Digest; | |
UINTN DigestSize; | |
UINTN Round; | |
UINTN Index; | |
UINTN AuthKeyLength; | |
UINTN FragmentsSize; | |
UINT8 TailData; | |
Status = EFI_SUCCESS; | |
if (NumFragments == 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
LocalFragments[0].Data = NULL; | |
LocalFragments[1].Data = NULL; | |
LocalFragments[2].Data = NULL; | |
AuthKeyLength = IpSecGetHmacDigestLength (HashAlgId); | |
DigestSize = AuthKeyLength; | |
Digest = AllocateZeroPool (AuthKeyLength); | |
if (Digest == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// If the required output key length is less than the digest size, | |
// copy the digest into OutputKey. | |
// | |
if (OutputKeyLength <= DigestSize) { | |
Status = IpSecCryptoIoHmac ( | |
HashAlgId, | |
HashKey, | |
HashKeyLength, | |
(HASH_DATA_FRAGMENT *) Fragments, | |
NumFragments, | |
Digest, | |
DigestSize | |
); | |
if (EFI_ERROR (Status)) { | |
goto Exit; | |
} | |
CopyMem (OutputKey, Digest, OutputKeyLength); | |
goto Exit; | |
} | |
// | |
//Otherwise, Key Material need to be PRF-based concatenation according to 2.13 | |
//of RFC 4306: prf+ (K,S) = T1 | T2 | T3 | T4 | ..., T1 = prf (K, S | 0x01), | |
//T2 = prf (K, T1 | S | 0x02), T3 = prf (K, T2 | S | 0x03),T4 = prf (K, T3 | S | 0x04) | |
//then derive the key from this key material. | |
// | |
FragmentsSize = 0; | |
for (Index = 0; Index < NumFragments; Index++) { | |
FragmentsSize = FragmentsSize + Fragments[Index].DataSize; | |
} | |
LocalFragments[1].Data = AllocateZeroPool (FragmentsSize); | |
if (LocalFragments[1].Data == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Exit; | |
} | |
LocalFragments[1].DataSize = FragmentsSize; | |
// | |
// Copy all input fragments into LocalFragments[1]; | |
// | |
FragmentsSize = 0; | |
for (Index = 0; Index < NumFragments; Index++) { | |
CopyMem ( | |
LocalFragments[1].Data + FragmentsSize, | |
Fragments[Index].Data, | |
Fragments[Index].DataSize | |
); | |
FragmentsSize = FragmentsSize + Fragments[Index].DataSize; | |
} | |
// | |
// Prepare 0x01 as the first tail data. | |
// | |
TailData = 0x01; | |
LocalFragments[2].Data = &TailData; | |
LocalFragments[2].DataSize = sizeof (TailData); | |
// | |
// Allocate buffer for the first fragment | |
// | |
LocalFragments[0].Data = AllocateZeroPool (AuthKeyLength); | |
if (LocalFragments[0].Data == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Exit; | |
} | |
LocalFragments[0].DataSize = AuthKeyLength; | |
Round = (OutputKeyLength - 1) / AuthKeyLength + 1; | |
for (Index = 0; Index < Round; Index++) { | |
Status = IpSecCryptoIoHmac ( | |
HashAlgId, | |
HashKey, | |
HashKeyLength, | |
(HASH_DATA_FRAGMENT *)(Index == 0 ? &LocalFragments[1] : LocalFragments), | |
Index == 0 ? 2 : 3, | |
Digest, | |
DigestSize | |
); | |
if (EFI_ERROR(Status)) { | |
goto Exit; | |
} | |
CopyMem ( | |
LocalFragments[0].Data, | |
Digest, | |
DigestSize | |
); | |
if (OutputKeyLength > DigestSize * (Index + 1)) { | |
CopyMem ( | |
OutputKey + Index * DigestSize, | |
Digest, | |
DigestSize | |
); | |
LocalFragments[0].DataSize = DigestSize; | |
TailData ++; | |
} else { | |
// | |
// The last round | |
// | |
CopyMem ( | |
OutputKey + Index * DigestSize, | |
Digest, | |
OutputKeyLength - Index * DigestSize | |
); | |
} | |
} | |
Exit: | |
// | |
// Only First and second Framgement Data need to be freed. | |
// | |
for (Index = 0 ; Index < 2; Index++) { | |
if (LocalFragments[Index].Data != NULL) { | |
FreePool (LocalFragments[Index].Data); | |
} | |
} | |
if (Digest != NULL) { | |
FreePool (Digest); | |
} | |
return Status; | |
} | |