*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
 | 
						|
};
 |