/** @file
The Implementations for Information Exchange.
(C) Copyright 2015 Hewlett-Packard Development Company, L.P.
Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
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
};