*v2: update the commit log and refine the code comments. There are three kinds of IKE Exchange process: #1. Initial Exchange #2. CREATE_CHILD_SA_Exchange #3. Information Exchange The IKE header "FLAG" update is incorrect in #2 and #3 exchange, which may cause the continue session failure. This patch is used to correct the updates of IKE header "FLAG" according the RFC4306 section 3.1. Cc: Ye Ting <ting.ye@intel.com> Cc: Fu Siyuan <siyuan.fu@intel.com> Cc: Zhang Lubo <lubo.zhang@intel.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jiaxin Wu <jiaxin.wu@intel.com> Reviewed-by: Ye Ting <ting.ye@intel.com>
		
			
				
	
	
		
			410 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			410 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   The Implementations for Information Exchange.
 | |
| 
 | |
|   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
 | |
|   Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
 | |
| 
 | |
|   This program and the accompanying materials
 | |
|   are licensed and made available under the terms and conditions of the BSD License
 | |
|   which accompanies this distribution.  The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php.
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
|   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
|   
 | |
| **/
 | |
| 
 | |
| #include "Utility.h"
 | |
| #include "IpSecDebug.h"
 | |
| #include "IpSecConfigImpl.h"
 | |
| 
 | |
| /**
 | |
|   Generate Information Packet.
 | |
| 
 | |
|   The information Packet may contain one Delete Payload, or Notify Payload, which 
 | |
|   dependes on the Context's parameters.
 | |
| 
 | |
|   @param[in]  SaSession   Pointer to IKE SA Session or Child SA Session which is 
 | |
|                           related to the information Exchange.
 | |
|   @param[in]  Context     The Data passed from the caller. If the Context is not NULL
 | |
|                           it should contain the information for Notification Data.
 | |
|                           
 | |
|   @retval     Pointer of IKE_PACKET generated.
 | |
| 
 | |
| **/
 | |
| IKE_PACKET *
 | |
| Ikev2InfoGenerator (
 | |
|   IN UINT8                         *SaSession,
 | |
|   IN VOID                          *Context
 | |
|   )
 | |
| {
 | |
|   IKEV2_SA_SESSION            *IkeSaSession;
 | |
|   IKEV2_CHILD_SA_SESSION      *ChildSaSession;
 | |
|   IKE_PACKET                  *IkePacket;
 | |
|   IKE_PAYLOAD                 *IkePayload;
 | |
|   IKEV2_INFO_EXCHANGE_CONTEXT *InfoContext;
 | |
| 
 | |
|   InfoContext  = NULL;
 | |
|   IkeSaSession = (IKEV2_SA_SESSION *) SaSession;
 | |
|   IkePacket    = IkePacketAlloc ();
 | |
|   if (IkePacket == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Fill IkePacket Header.
 | |
|   //
 | |
|   IkePacket->Header->ExchangeType    = IKEV2_EXCHANGE_TYPE_INFO;
 | |
|   IkePacket->Header->Version         = (UINT8) (2 << 4); 
 | |
| 
 | |
|   if (Context != NULL) {
 | |
|     InfoContext = (IKEV2_INFO_EXCHANGE_CONTEXT *) Context;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // For Liveness Check
 | |
|   //
 | |
|   if (InfoContext != NULL && 
 | |
|       (InfoContext->InfoType == Ikev2InfoLiveCheck || InfoContext->InfoType == Ikev2InfoNotify) 
 | |
|     ) {
 | |
|     IkePacket->Header->MessageId       = InfoContext->MessageId;
 | |
|     IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie;
 | |
|     IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie;
 | |
|     IkePacket->Header->NextPayload     = IKEV2_PAYLOAD_TYPE_NONE;
 | |
|     IkePacket->Header->Flags           = IKE_HEADER_FLAGS_RESPOND;
 | |
|     //
 | |
|     // TODO: add Notify Payload for Notification Information.
 | |
|     //
 | |
|     return IkePacket;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // For delete SAs
 | |
|   //  
 | |
|   if (IkeSaSession->SessionCommon.IkeSessionType == IkeSessionTypeIkeSa) {
 | |
| 
 | |
|     IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie;
 | |
|     IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie;
 | |
| 
 | |
|     //
 | |
|     // If the information message is response message,the MessageId should
 | |
|     // be same as the request MessageId which passed through the Context.
 | |
|     //
 | |
|     if (InfoContext != NULL) {
 | |
|       IkePacket->Header->MessageId     = InfoContext->MessageId;
 | |
|     } else {
 | |
|       IkePacket->Header->MessageId     = IkeSaSession->MessageId;
 | |
|       Ikev2SaSessionIncreaseMessageId (IkeSaSession);
 | |
|     }
 | |
|     //
 | |
|     // If the state is on deleting generate a Delete Payload for it.
 | |
|     //
 | |
|     if (IkeSaSession->SessionCommon.State == IkeStateSaDeleting ) {
 | |
|       IkePayload = Ikev2GenerateDeletePayload (
 | |
|                      IkeSaSession, 
 | |
|                      IKEV2_PAYLOAD_TYPE_NONE, 
 | |
|                      0, 
 | |
|                      0, 
 | |
|                      NULL
 | |
|                      );  
 | |
|       if (IkePayload == NULL) {
 | |
|         goto ERROR_EXIT;
 | |
|       }
 | |
|       //
 | |
|       // Fill the next payload in IkePacket's Header.
 | |
|       //
 | |
|       IkePacket->Header->NextPayload     = IKEV2_PAYLOAD_TYPE_DELETE;
 | |
|       IKE_PACKET_APPEND_PAYLOAD (IkePacket, IkePayload);
 | |
|       IkePacket->Private           = IkeSaSession->SessionCommon.Private;
 | |
|       IkePacket->Spi               = 0;
 | |
|       IkePacket->IsDeleteInfo      = TRUE;
 | |
|             
 | |
|     } else if (Context != NULL) {
 | |
|       //
 | |
|       // TODO: If contest is not NULL Generate a Notify Payload.
 | |
|       //
 | |
|     } else {
 | |
|       //
 | |
|       // The input parameter is not correct.
 | |
|       //
 | |
|       goto ERROR_EXIT;
 | |
|     }
 | |
| 
 | |
|     if (IkeSaSession->SessionCommon.IsInitiator) {
 | |
|       IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT ;
 | |
|     }  
 | |
|   } else {
 | |
|     //
 | |
|     // Delete the Child SA Information Exchagne
 | |
|     //
 | |
|     ChildSaSession                     = (IKEV2_CHILD_SA_SESSION *) SaSession;
 | |
|     IkeSaSession                       = ChildSaSession->IkeSaSession;
 | |
|     IkePacket->Header->InitiatorCookie = ChildSaSession->IkeSaSession->InitiatorCookie;
 | |
|     IkePacket->Header->ResponderCookie = ChildSaSession->IkeSaSession->ResponderCookie;
 | |
| 
 | |
|     //
 | |
|     // If the information message is response message,the MessageId should
 | |
|     // be same as the request MessageId which passed through the Context.
 | |
|     //
 | |
|     if (InfoContext != NULL && InfoContext->MessageId != 0) {
 | |
|       IkePacket->Header->MessageId     = InfoContext->MessageId;
 | |
|     } else {
 | |
|       IkePacket->Header->MessageId     = ChildSaSession->IkeSaSession->MessageId;
 | |
|       Ikev2SaSessionIncreaseMessageId (IkeSaSession);
 | |
|     }
 | |
|     
 | |
|     IkePayload     = Ikev2GenerateDeletePayload (
 | |
|                        ChildSaSession->IkeSaSession,
 | |
|                        IKEV2_PAYLOAD_TYPE_DELETE,
 | |
|                        4,
 | |
|                        1,
 | |
|                        (UINT8 *)&ChildSaSession->LocalPeerSpi
 | |
|                        );
 | |
|     if (IkePayload == NULL) {
 | |
|       goto ERROR_EXIT;
 | |
|     }
 | |
|     //
 | |
|     // Fill the Next Payload in IkePacket's Header.
 | |
|     //
 | |
|     IkePacket->Header->NextPayload     = IKEV2_PAYLOAD_TYPE_DELETE;
 | |
|     IKE_PACKET_APPEND_PAYLOAD (IkePacket, IkePayload);
 | |
| 
 | |
|     IkePacket->Private      = IkeSaSession->SessionCommon.Private;
 | |
|     IkePacket->Spi          = ChildSaSession->LocalPeerSpi;
 | |
|     IkePacket->IsDeleteInfo = TRUE;
 | |
| 
 | |
|     if (!ChildSaSession->SessionCommon.IsInitiator) {
 | |
|       //
 | |
|       // If responder, use the MessageId fromt the initiator.
 | |
|       //
 | |
|       IkePacket->Header->MessageId = ChildSaSession->MessageId;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Change the IsOnDeleting Flag
 | |
|     //
 | |
|     ChildSaSession->SessionCommon.IsOnDeleting = TRUE;
 | |
| 
 | |
|     if (ChildSaSession->SessionCommon.IsInitiator) {
 | |
|       IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT ;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (InfoContext != NULL) {
 | |
|     IkePacket->Header->Flags |= IKE_HEADER_FLAGS_RESPOND;
 | |
|   }
 | |
|   
 | |
|   return IkePacket;
 | |
| 
 | |
| ERROR_EXIT:
 | |
|    if (IkePacket != NULL) {
 | |
|      FreePool (IkePacket);
 | |
|    }
 | |
|    return NULL;
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Parse the Info Exchange.
 | |
| 
 | |
|   @param[in]  SaSession   Pointer to IKEV2_SA_SESSION.
 | |
|   @param[in]  IkePacket   Pointer to IkePacket related to the Information Exchange.
 | |
| 
 | |
|   @retval  EFI_SUCCESS    The operation finised successed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ikev2InfoParser (
 | |
|   IN UINT8                         *SaSession,
 | |
|   IN IKE_PACKET                    *IkePacket
 | |
|   )
 | |
| {
 | |
|   IKEV2_CHILD_SA_SESSION *ChildSaSession;
 | |
|   IKEV2_SA_SESSION       *IkeSaSession;
 | |
|   IKE_PAYLOAD            *DeletePayload;
 | |
|   IKE_PAYLOAD            *IkePayload;
 | |
|   IKEV2_DELETE           *Delete;
 | |
|   LIST_ENTRY             *Entry;
 | |
|   LIST_ENTRY             *ListEntry;
 | |
|   UINT8                  Index;
 | |
|   UINT32                 Spi;
 | |
|   UINT8                  *SpiBuffer;
 | |
|   IPSEC_PRIVATE_DATA     *Private;
 | |
|   UINT8                  Value;
 | |
|   EFI_STATUS             Status;
 | |
|   IKE_PACKET             *RespondPacket;
 | |
|   
 | |
|   IKEV2_INFO_EXCHANGE_CONTEXT Context;
 | |
|   
 | |
|   IkeSaSession   = (IKEV2_SA_SESSION *) SaSession;
 | |
| 
 | |
|   DeletePayload  = NULL;
 | |
|   Private        = NULL;
 | |
|   RespondPacket  = NULL;
 | |
|   Status         = EFI_SUCCESS;
 | |
|   
 | |
|   //
 | |
|   // For Liveness Check
 | |
|   //
 | |
|   if (IkePacket->Header->NextPayload == IKEV2_PAYLOAD_TYPE_NONE &&
 | |
|       (IkePacket->PayloadTotalSize == 0)
 | |
|       ) {
 | |
|     if (IkePacket->Header->Flags == IKE_HEADER_FLAGS_INIT) {
 | |
|       //
 | |
|       // If it is Liveness check request, reply it.
 | |
|       //
 | |
|       Context.InfoType  = Ikev2InfoLiveCheck;
 | |
|       Context.MessageId = IkePacket->Header->MessageId;
 | |
|       RespondPacket     = Ikev2InfoGenerator ((UINT8 *)IkeSaSession, &Context);
 | |
| 
 | |
|       if (RespondPacket == NULL) {
 | |
|         Status = EFI_INVALID_PARAMETER;
 | |
|         return Status;
 | |
|       }
 | |
|       Status = Ikev2SendIkePacket (
 | |
|                  IkeSaSession->SessionCommon.UdpService,
 | |
|                  (UINT8 *)(&IkeSaSession->SessionCommon),
 | |
|                  RespondPacket,
 | |
|                  0
 | |
|                  );
 | |
| 
 | |
|     } else {
 | |
|       //
 | |
|       // Todo: verify the liveness check response packet.
 | |
|       //
 | |
|     }
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // For SA Delete
 | |
|   //
 | |
|   NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) {   
 | |
| 
 | |
|   //
 | |
|   // Iterate payloads to find the Delete/Notify Payload.
 | |
|   //
 | |
|     IkePayload  = IKE_PAYLOAD_BY_PACKET (Entry);
 | |
|     
 | |
|     if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_DELETE) {
 | |
|       DeletePayload = IkePayload;
 | |
|       Delete = (IKEV2_DELETE *)DeletePayload->PayloadBuf;
 | |
| 
 | |
|       if (Delete->SpiSize == 0) {
 | |
|         //
 | |
|         // Delete IKE SA.
 | |
|         //
 | |
|         if (IkeSaSession->SessionCommon.State == IkeStateSaDeleting) {
 | |
|           RemoveEntryList (&IkeSaSession->BySessionTable);
 | |
|           Ikev2SaSessionFree (IkeSaSession);
 | |
|           //
 | |
|           // Checking the Private status.
 | |
|           //
 | |
|           //
 | |
|           // when all IKE SAs were disabled by calling "IPsecConfig -disable", the IPsec
 | |
|           // status should be changed.
 | |
|           //
 | |
|           Private = IkeSaSession->SessionCommon.Private;
 | |
|           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 DisabledFlag in Private data.
 | |
|                 //
 | |
|                 Private->IpSec.DisabledFlag = TRUE;
 | |
|                 Private->IsIPsecDisabling   = FALSE;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         } else {
 | |
|           IkeSaSession->SessionCommon.State = IkeStateSaDeleting;
 | |
|           Context.InfoType                  = Ikev2InfoDelete;
 | |
|           Context.MessageId                 = IkePacket->Header->MessageId;
 | |
| 
 | |
|           RespondPacket = Ikev2InfoGenerator ((UINT8 *)IkeSaSession, &Context);
 | |
|           if (RespondPacket == NULL) {
 | |
|             Status = EFI_INVALID_PARAMETER;
 | |
|             return Status;
 | |
|           }
 | |
|           Status = Ikev2SendIkePacket (
 | |
|                      IkeSaSession->SessionCommon.UdpService, 
 | |
|                      (UINT8 *)(&IkeSaSession->SessionCommon), 
 | |
|                      RespondPacket, 
 | |
|                      0
 | |
|                      );
 | |
|         }
 | |
|       } else if (Delete->SpiSize == 4) {
 | |
|         //
 | |
|         // Move the Child SAs to DeleteList
 | |
|         //
 | |
|         SpiBuffer = (UINT8 *)(Delete + 1);
 | |
|         for (Index = 0; Index < Delete->NumSpis; Index++) {
 | |
|           Spi = ReadUnaligned32 ((UINT32 *)SpiBuffer);
 | |
|           for (ListEntry = IkeSaSession->ChildSaEstablishSessionList.ForwardLink;
 | |
|                ListEntry != &IkeSaSession->ChildSaEstablishSessionList;
 | |
|           ) {
 | |
|             ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (ListEntry);
 | |
|             ListEntry = ListEntry->ForwardLink;
 | |
| 
 | |
|             if (ChildSaSession->RemotePeerSpi == HTONL(Spi)) {
 | |
|               if (ChildSaSession->SessionCommon.State != IkeStateSaDeleting) {
 | |
| 
 | |
|                 //
 | |
|                 // Insert the ChildSa Session into Delete List.
 | |
|                 //
 | |
|                 InsertTailList (&IkeSaSession->DeleteSaList, &ChildSaSession->ByDelete);
 | |
|                 ChildSaSession->SessionCommon.State       = IkeStateSaDeleting;
 | |
|                 ChildSaSession->SessionCommon.IsInitiator = FALSE;
 | |
|                 ChildSaSession->MessageId                 = IkePacket->Header->MessageId;
 | |
| 
 | |
|                 Context.InfoType = Ikev2InfoDelete;
 | |
|                 Context.MessageId = IkePacket->Header->MessageId;
 | |
|           
 | |
|                 RespondPacket = Ikev2InfoGenerator ((UINT8 *)ChildSaSession, &Context);
 | |
|                 if (RespondPacket == NULL) {
 | |
|                   Status = EFI_INVALID_PARAMETER;
 | |
|                   return Status;
 | |
|                 }
 | |
|                 Status = Ikev2SendIkePacket (
 | |
|                            ChildSaSession->SessionCommon.UdpService,
 | |
|                            (UINT8 *)(&ChildSaSession->SessionCommon),
 | |
|                            RespondPacket, 
 | |
|                            0
 | |
|                            );
 | |
|               } else {
 | |
|                 //
 | |
|                 // Delete the Child SA.
 | |
|                 //
 | |
|                 Ikev2ChildSaSilentDelete (IkeSaSession, Spi);
 | |
|                 RemoveEntryList (&ChildSaSession->ByDelete);
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|           SpiBuffer = SpiBuffer + sizeof (Spi);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| GLOBAL_REMOVE_IF_UNREFERENCED IKEV2_PACKET_HANDLER  mIkev2Info = {
 | |
|   Ikev2InfoParser,
 | |
|   Ikev2InfoGenerator
 | |
| };
 |