| /** @file | |
| The Common operations used by IKE Exchange Process. | |
| (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR> | |
| Copyright (c) 2010 - 2015, 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)); | |
| ASSERT (IkeSaSession != 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; | |
| ChildSaSession->LocalPeerSpi = IkeGenerateSpi (); | |
| 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; | |
| IKE_UDP_SERVICE *UdpService; | |
| IPSEC_PRIVATE_DATA *Private; | |
| if (IkeSaSession == NULL) { | |
| return EFI_NOT_FOUND; | |
| } | |
| IsLocalFound = FALSE; | |
| IsRemoteFound = FALSE; | |
| ChildSession = NULL; | |
| LocalSelector = NULL; | |
| RemoteSelector = NULL; | |
| UdpService = IkeSaSession->SessionCommon.UdpService; | |
| 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); | |
| ASSERT (Selector != NULL); | |
| while (1) { | |
| Status = EfiIpSecConfigGetNextSelector ( | |
| &Private->IpSecConfig, | |
| IPsecConfigDataTypeSad, | |
| &SelectorSize, | |
| Selector | |
| ); | |
| if (Status == EFI_BUFFER_TOO_SMALL) { | |
| FreePool (Selector); | |
| Selector = AllocateZeroPool (SelectorSize); | |
| ASSERT (Selector != NULL); | |
| 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); | |
| ASSERT (RemoteSelector != NULL); | |
| 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); | |
| ASSERT (LocalSelector != NULL); | |
| 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)); | |
| ASSERT (ProposalData->Spi != 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)); | |
| ASSERT (ProposalData->Spi != 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. | |
| **/ | |
| VOID | |
| Ikev2ChildSaSessionSpdSelectorCreate ( | |
| IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession | |
| ) | |
| { | |
| if (ChildSaSession->Spd != NULL && ChildSaSession->Spd->Selector != NULL) { | |
| if (ChildSaSession->SpdSelector == NULL) { | |
| ChildSaSession->SpdSelector = AllocateZeroPool (sizeof (EFI_IPSEC_SPD_SELECTOR)); | |
| ASSERT (ChildSaSession->SpdSelector != NULL); | |
| } | |
| 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 | |
| ); | |
| ChildSaSession->SpdSelector->LocalAddress = AllocateCopyPool ( | |
| ChildSaSession->Spd->Selector->LocalAddressCount * | |
| sizeof (EFI_IP_ADDRESS_INFO), | |
| ChildSaSession->Spd->Selector->LocalAddress | |
| ); | |
| ASSERT (ChildSaSession->SpdSelector->LocalAddress != NULL); | |
| ASSERT (ChildSaSession->SpdSelector->RemoteAddress != NULL); | |
| ChildSaSession->SpdSelector->RemoteAddressCount = ChildSaSession->Spd->Selector->RemoteAddressCount; | |
| ChildSaSession->SpdSelector->LocalAddressCount = ChildSaSession->Spd->Selector->LocalAddressCount; | |
| } | |
| } | |
| /** | |
| 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); | |
| ASSERT (ChildSaSession != 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. | |
| // | |
| Ikev2ChildSaSessionSpdSelectorCreate (ChildSaSession); | |
| // | |
| // Copy first NiBlock and NrBlock to ChildSa Session | |
| // | |
| ChildSaSession->NiBlock = AllocateZeroPool (IkeSaSession->NiBlkSize); | |
| ASSERT (ChildSaSession->NiBlock != NULL); | |
| ChildSaSession->NiBlkSize = IkeSaSession->NiBlkSize; | |
| CopyMem (ChildSaSession->NiBlock, IkeSaSession->NiBlock, IkeSaSession->NiBlkSize); | |
| ChildSaSession->NrBlock = AllocateZeroPool (IkeSaSession->NrBlkSize); | |
| ASSERT (ChildSaSession->NrBlock != 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)); | |
| ASSERT (IkeSaSession->SessionCommon.SaParams != NULL); | |
| 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); | |
| ASSERT (IkeSaSession->SaData != NULL); | |
| 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)); | |
| ASSERT (IkeSaSession->SessionCommon.SaParams != NULL); | |
| 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)); | |
| ASSERT (ChildSaSession->SessionCommon.SaParams != NULL); | |
| 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); | |
| ASSERT (ChildSaSession->SaData != NULL); | |
| 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 | |
| ); | |
| ASSERT (((IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1))->Spi != NULL); | |
| 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)); | |
| ASSERT (ChildSaSession->SessionCommon.SaParams != NULL); | |
| 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); | |
| ASSERT (LocalFragments[1].Data != NULL); | |
| 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); | |
| ASSERT (LocalFragments[0].Data != NULL); | |
| 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; | |
| } | |