audk/NetworkPkg/IpSecDxe/Ikev2/Info.c

404 lines
13 KiB
C

/** @file
The Implementations for Information Exchange.
(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#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
};