git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11219 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			2693 lines
		
	
	
		
			84 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2693 lines
		
	
	
		
			84 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   The Common operations used by IKE Exchange Process.
 | |
| 
 | |
|   Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
 | |
| 
 | |
|   This program and the accompanying materials
 | |
|   are licensed and made available under the terms and conditions of the BSD License
 | |
|   which accompanies this distribution.  The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php.
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
|   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "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;
 | |
|   IKEV2_SA_PARAMS              *SaParams;
 | |
|   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.
 | |
|   //
 | |
|   SaParams = SessionCommon->SaParams;
 | |
|   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  = (UdpService->IpVersion == IP_VERSION_4) ?
 | |
|              IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) :
 | |
|              IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead);
 | |
| 
 | |
|   //
 | |
|   // 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);
 | |
|       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);
 | |
|       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);
 | |
|       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;
 | |
| }
 | |
| 
 |