From 098f8621634f1cbdd1253c9957eed09a505223f5 Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 27 Apr 2017 11:16:34 +0800 Subject: NetWorkPkg: Move to new location Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c | 199 ++ Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c | 809 +++++++ Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h | 258 +++ Core/NetworkPkg/IpSecDxe/Ikev2/Info.c | 409 ++++ Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c | 3353 +++++++++++++++++++++++++++++ Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h | 443 ++++ Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c | 2261 +++++++++++++++++++ Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c | 2802 ++++++++++++++++++++++++ Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h | 1134 ++++++++++ 9 files changed, 11668 insertions(+) create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Info.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h (limited to 'Core/NetworkPkg/IpSecDxe/Ikev2') diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c b/Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c new file mode 100644 index 0000000000..eaccad2086 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c @@ -0,0 +1,199 @@ +/** @file + The operations for Child SA. + + 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" + +/** + Generate IKE Packet for CREATE_CHILD_SA exchange. + + This IKE Packet would be the packet for creating new CHILD SA, or the packet for + rekeying existing IKE SA, or the packet for existing CHILD SA. + + @param[in] SaSession Pointer to related SA session. + @param[in] Context The data passed by the caller. + + return a pointer of IKE packet. + +**/ +IKE_PACKET * +Ikev2CreateChildGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PACKET *IkePacket; + IKE_PAYLOAD *NotifyPayload; + UINT32 *MessageId; + + NotifyPayload = NULL; + MessageId = NULL; + + ChildSaSession = (IKEV2_CHILD_SA_SESSION *) SaSession; + if (ChildSaSession == NULL) { + return NULL; + } + + IkePacket = IkePacketAlloc(); + if (IkePacket == NULL) { + return NULL; + } + + + if (Context != NULL) { + MessageId = (UINT32 *) Context; + } + + IkePacket->Header->Version = (UINT8) (2 << 4); + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_NOTIFY; + IkePacket->Header->ExchangeType = IKE_XCG_TYPE_CREATE_CHILD_SA; + + if (ChildSaSession->SessionCommon.IkeSessionType == IkeSessionTypeChildSa) { + // + // 1.a Fill the IkePacket->Hdr + // + IkePacket->Header->InitiatorCookie = ChildSaSession->IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = ChildSaSession->IkeSaSession->ResponderCookie; + + if (MessageId != NULL) { + IkePacket->Header->MessageId = *MessageId; + } else { + IkePacket->Header->MessageId = ChildSaSession->MessageId; + } + + if (ChildSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT; + } + + } else { + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + // + // 1.a Fill the IkePacket->Hdr + // + IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie; + + if (MessageId != NULL) { + IkePacket->Header->MessageId = *MessageId; + } else { + IkePacket->Header->MessageId = IkeSaSession->MessageId; + } + + if (IkeSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT; + } + } + + if (MessageId != NULL) { + IkePacket->Header->Flags |= IKE_HEADER_FLAGS_RESPOND; + } + + // + // According to RFC4306, Chapter 4. + // A minimal implementation may support the CREATE_CHILD_SA exchange only to + // recognize requests and reject them with a Notify payload of type NO_ADDITIONAL_SAS. + // + NotifyPayload = Ikev2GenerateNotifyPayload ( + 0, + IKEV2_PAYLOAD_TYPE_NONE, + 0, + IKEV2_NOTIFICATION_NO_ADDITIONAL_SAS, + NULL, + NULL, + 0 + ); + if (NotifyPayload == NULL) { + IkePacketFree (IkePacket); + return NULL; + } + + IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload); + // + // TODO: Support the CREATE_CHILD_SA exchange. + // + return IkePacket; +} + +/** + Parse the IKE packet of CREATE_CHILD_SA exchange. + + This function parse the IKE packet and save the related information to further + calculation. + + @param[in] SaSession Pointer to IKEv2_CHILD_SA_SESSION related to this Exchange. + @param[in] IkePacket Received packet to be parsed. + + + @retval EFI_SUCCESS The IKE Packet is acceptable. + @retval EFI_UNSUPPORTED Not support the CREATE_CHILD_SA request. + +**/ +EFI_STATUS +Ikev2CreateChildParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Routine process before the payload decoding. + + @param[in] SessionCommon Pointer to ChildSa SessionCommon. + @param[in] PayloadBuf Pointer to the payload. + @param[in] PayloadSize Size of PayloadBuf in byte. + @param[in] PayloadType Type of Payload. + +**/ +VOID +Ikev2ChildSaBeforeDecodePayload ( + IN UINT8 *SessionCommon, + IN UINT8 *PayloadBuf, + IN UINTN PayloadSize, + IN UINT8 PayloadType + ) +{ + +} + +/** + Routine Process after the payload encoding. + + @param[in] SessionCommon Pointer to ChildSa SessionCommon. + @param[in] PayloadBuf Pointer to the payload. + @param[in] PayloadSize Size of PayloadBuf in byte. + @param[in] PayloadType Type of Payload. + +**/ +VOID +Ikev2ChildSaAfterEncodePayload ( + IN UINT8 *SessionCommon, + IN UINT8 *PayloadBuf, + IN UINTN PayloadSize, + IN UINT8 PayloadType + ) +{ +} + +IKEV2_PACKET_HANDLER mIkev2CreateChild = { + // + // Create Child + // + Ikev2CreateChildParser, + Ikev2CreateChildGenerator +}; diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c b/Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c new file mode 100644 index 0000000000..5609964fa4 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c @@ -0,0 +1,809 @@ +/** @file + The general interfaces of the IKEv2. + + 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 "IkeService.h" +#include "IpSecConfigImpl.h" + +/** + General interface to intialize a IKEv2 negotiation. + + @param[in] UdpService Point to Udp Servcie used for the IKE packet sending. + @param[in] SpdEntry Point to SPD entry related to this IKE negotiation. + @param[in] PadEntry Point to PAD entry related to this IKE negotiation. + @param[in] RemoteIp Point to IP Address which the remote peer to negnotiate. + + @retval EFI_SUCCESS The operation is successful. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + @retval EFI_INVALID_PARAMETER If UdpService or RemoteIp is NULL. + @return Others The operation is failed. + +**/ +EFI_STATUS +Ikev2NegotiateSa ( + IN IKE_UDP_SERVICE *UdpService, + IN IPSEC_SPD_ENTRY *SpdEntry, + IN IPSEC_PAD_ENTRY *PadEntry, + IN EFI_IP_ADDRESS *RemoteIp + ) +{ + IPSEC_PRIVATE_DATA *Private; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_SESSION_COMMON *SessionCommon; + IKEV2_PACKET_HANDLER Handler; + IKE_PACKET *IkePacket; + EFI_STATUS Status; + + if (UdpService == NULL || RemoteIp == NULL) { + return EFI_INVALID_PARAMETER; + } + + IkePacket = NULL; + Private = (UdpService->IpVersion == IP_VERSION_4) ? + IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : + IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead); + + // + // Lookup the remote ip address in the processing IKE SA session list. + // + IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2SessionList, RemoteIp); + if (IkeSaSession != NULL) { + // + // Drop the packet if already in process. + // + return EFI_SUCCESS; + } + + // + // Create a new IkeSaSession and initiate the common parameters. + // + IkeSaSession = Ikev2SaSessionAlloc (Private, UdpService); + if (IkeSaSession == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the specific parameters and state(IKE_STATE_INIT). + // + IkeSaSession->Spd = SpdEntry; + IkeSaSession->Pad = PadEntry; + SessionCommon = &IkeSaSession->SessionCommon; + SessionCommon->IsInitiator = TRUE; + SessionCommon->State = IkeStateInit; + // + // TODO: Get the prefer DH Group from the IPsec Configuration, after the IPsecconfig application update + // to support it. + // + SessionCommon->PreferDhGroup = IKEV2_TRANSFORM_ID_DH_1024MODP; + + CopyMem ( + &SessionCommon->RemotePeerIp, + RemoteIp, + sizeof (EFI_IP_ADDRESS) + ); + + CopyMem ( + &SessionCommon->LocalPeerIp, + &UdpService->DefaultAddress, + sizeof (EFI_IP_ADDRESS) + ); + + IKEV2_DUMP_STATE (SessionCommon->State, IkeStateInit); + + // + // Initiate the SAD data of the IkeSaSession. + // + IkeSaSession->SaData = Ikev2InitializeSaData (SessionCommon); + if (IkeSaSession->SaData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Generate an IKE request packet and send it out. + // + Handler = mIkev2Initial[IkeSaSession->Pad->Data->AuthMethod][SessionCommon->State]; + IkePacket = Handler.Generator ((UINT8 *) IkeSaSession, NULL); + if (IkePacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) SessionCommon, IkePacket, 0); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Insert the current IkeSaSession into the processing IKE SA list. + // + Ikev2SaSessionInsert (&Private->Ikev2SessionList, IkeSaSession, RemoteIp); + + return EFI_SUCCESS; + +ON_ERROR: + + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + Ikev2SaSessionFree (IkeSaSession); + return Status; +} + +/** + It is general interface to negotiate the Child SA. + + There are three situations which will invoke this function. First, create a CHILD + SA if the input Context is NULL. Second, rekeying the existing IKE SA if the Context + is a IKEv2_SA_SESSION. Third, rekeying the existing CHILD SA if the context is a + IKEv2_CHILD_SA_SESSION. + + @param[in] IkeSaSession Pointer to IKEv2_SA_SESSION related to this operation. + @param[in] SpdEntry Pointer to IPSEC_SPD_ENTRY related to this operation. + @param[in] Context The data pass from the caller. + + @retval EFI_SUCCESS The operation is successful. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + @retval EFI_UNSUPPORTED The condition is not support yet. + @return Others The operation is failed. + +**/ +EFI_STATUS +Ikev2NegotiateChildSa ( + IN UINT8 *IkeSaSession, + IN IPSEC_SPD_ENTRY *SpdEntry, + IN UINT8 *Context + ) +{ + EFI_STATUS Status; + IKEV2_SA_SESSION *SaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SESSION_COMMON *ChildSaCommon; + IKE_PACKET *IkePacket; + IKE_UDP_SERVICE *UdpService; + + SaSession = (IKEV2_SA_SESSION*) IkeSaSession; + UdpService = SaSession->SessionCommon.UdpService; + IkePacket = NULL; + + // + // 1. Create another child SA session if context is null. + // 2. Rekeying the IKE SA session if the context is IKE SA session. + // 3. Rekeying the child SA session if the context is child SA session. + // + if (Context == NULL) { + // + // Create a new ChildSaSession and initiate the common parameters. + // + ChildSaSession = Ikev2ChildSaSessionAlloc (UdpService, SaSession); + + if (ChildSaSession == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the specific parameters and state as IKE_STATE_CREATE_CHILD. + // + ChildSaSession->Spd = SpdEntry; + ChildSaCommon = &ChildSaSession->SessionCommon; + ChildSaCommon->IsInitiator = TRUE; + ChildSaCommon->State = IkeStateCreateChild; + + IKEV2_DUMP_STATE (ChildSaCommon->State, IkeStateCreateChild); + + if (SpdEntry->Selector->NextLayerProtocol != EFI_IPSEC_ANY_PROTOCOL) { + ChildSaSession->ProtoId = SpdEntry->Selector->NextLayerProtocol; + } + + if (SpdEntry->Selector->LocalPort != EFI_IPSEC_ANY_PORT) { + ChildSaSession->LocalPort = SpdEntry->Selector->LocalPort; + } + + if (SpdEntry->Selector->RemotePort != EFI_IPSEC_ANY_PORT) { + ChildSaSession->RemotePort = SpdEntry->Selector->RemotePort; + } + // + // Initiate the SAD data parameters of the ChildSaSession. + // + ChildSaSession->SaData = Ikev2InitializeSaData (ChildSaCommon); + if (ChildSaSession->SaData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + // + // Generate an IKE request packet and send it out. + // + IkePacket = mIkev2CreateChild.Generator ((UINT8 *) ChildSaSession, NULL); + + if (IkePacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) ChildSaCommon, IkePacket, 0); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Insert the ChildSaSession into processing child SA list. + // + Ikev2ChildSaSessionInsert (&SaSession->ChildSaSessionList, ChildSaSession); + } else { + // + // TODO: Rekeying IkeSaSession or ChildSaSession, NOT support yet. + // + // Rekey IkeSa, set IkeSaSession->State and pass over IkeSaSession + // Rekey ChildSa, set ChildSaSession->State and pass over ChildSaSession + // + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (ChildSaSession->SaData != NULL) { + FreePool (ChildSaSession->SaData); + } + + if (ChildSaSession->SessionCommon.TimeoutEvent != NULL) { + gBS->CloseEvent (ChildSaSession->SessionCommon.TimeoutEvent); + } + + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + + Ikev2ChildSaSessionFree (ChildSaSession); + return Status; +} + +/** + It is general interface to start the Information Exchange. + + There are three situations which will invoke this function. First, deliver a Delete Information + to delete the IKE SA if the input Context is NULL and the state of related IkeSaSeesion's is on + deleting.Second, deliver a Notify Information without the contents if the input Context is NULL. + Third, deliver a Notify Information if the input Context is not NULL. + + @param[in] IkeSaSession Pointer to IKEv2_SA_SESSION related to this operation. + @param[in] Context Data passed by caller. + + @retval EFI_SUCCESS The operation is successful. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + @retval EFI_UNSUPPORTED The condition is not support yet. + @return Otherwise The operation is failed. + +**/ +EFI_STATUS +Ikev2NegotiateInfo ( + IN UINT8 *IkeSaSession, + IN UINT8 *Context + ) +{ + + EFI_STATUS Status; + IKEV2_SA_SESSION *Ikev2SaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SESSION_COMMON *SaCommon; + IKE_PACKET *IkePacket; + IKE_UDP_SERVICE *UdpService; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + Ikev2SaSession = (IKEV2_SA_SESSION *) IkeSaSession; + UdpService = Ikev2SaSession->SessionCommon.UdpService; + SaCommon = &Ikev2SaSession->SessionCommon; + IkePacket = NULL; + Status = EFI_SUCCESS; + + // + // Delete the IKE SA. + // + if (Ikev2SaSession->SessionCommon.State == IkeStateSaDeleting && Context == NULL) { + + // + // Generate Information Packet which contains the Delete Payload. + // + IkePacket = mIkev2Info.Generator ((UINT8 *) Ikev2SaSession, NULL); + if (IkePacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Send out the Packet + // + if (UdpService != NULL && UdpService->Output != NULL) { + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) SaCommon, IkePacket, 0); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + } else if (!IsListEmpty (&Ikev2SaSession->DeleteSaList)) { + // + // Iterate all Deleting Child SAs. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Ikev2SaSession->DeleteSaList) { + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_DEL_SA (Entry); + ChildSaSession->SessionCommon.State = IkeStateSaDeleting; + + // + // Generate Information Packet which contains the Child SA Delete Payload. + // + IkePacket = mIkev2Info.Generator ((UINT8 *) ChildSaSession, NULL); + if (IkePacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Send out the Packet + // + if (UdpService != NULL && UdpService->Output != NULL) { + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) &ChildSaSession->SessionCommon, IkePacket, 0); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + } + } else if (Context == NULL) { + // + // TODO: Deliver null notification message. + // + } else if (Context != NULL) { + // + // TODO: Send out the Information Exchange which contains the Notify Payload. + // + } +ON_ERROR: + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + return Status; + +} + +/** + The general interface when received a IKEv2 packet for the IKE SA establishing. + + This function first find the related IKE SA Session according to the IKE packet's + remote IP. Then call the corresponding function to handle this IKE packet according + to the related IKE SA Session's State. + + @param[in] UdpService Pointer of related UDP Service. + @param[in] IkePacket Data passed by caller. + +**/ +VOID +Ikev2HandleSa ( + IN IKE_UDP_SERVICE *UdpService, + IN IKE_PACKET *IkePacket + ) +{ + EFI_STATUS Status; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SESSION_COMMON *IkeSaCommon; + IKEV2_SESSION_COMMON *ChildSaCommon; + IKEV2_PACKET_HANDLER Handler; + IKE_PACKET *Reply; + IPSEC_PAD_ENTRY *PadEntry; + IPSEC_PRIVATE_DATA *Private; + BOOLEAN IsNewSession; + + Private = (UdpService->IpVersion == IP_VERSION_4) ? + IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : + IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead); + + ChildSaSession = NULL; + ChildSaCommon = NULL; + + // + // Lookup the remote ip address in the processing IKE SA session list. + // + IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2SessionList, &IkePacket->RemotePeerIp); + IsNewSession = FALSE; + + if (IkeSaSession == NULL) { + // + // Lookup the remote ip address in the pad. + // + PadEntry = IpSecLookupPadEntry (UdpService->IpVersion, &IkePacket->RemotePeerIp); + if (PadEntry == NULL) { + // + // Drop the packet if no pad entry matched, this is the request from RFC 4301. + // + return ; + } + + // + // Create a new IkeSaSession and initiate the common parameters. + // + IkeSaSession = Ikev2SaSessionAlloc (Private, UdpService); + if (IkeSaSession == NULL) { + return; + } + IkeSaSession->Pad = PadEntry; + IkeSaCommon = &IkeSaSession->SessionCommon; + IkeSaCommon->IsInitiator = FALSE; + IkeSaCommon->State = IkeStateInit; + + IKEV2_DUMP_STATE (IkeSaCommon->State, IkeStateInit); + + CopyMem ( + &IkeSaCommon->RemotePeerIp, + &IkePacket->RemotePeerIp, + sizeof (EFI_IP_ADDRESS) + ); + + CopyMem ( + &IkeSaCommon->LocalPeerIp, + &UdpService->DefaultAddress, + sizeof (EFI_IP_ADDRESS) + ); + + IsNewSession = TRUE; + } + + // + // Validate the IKE packet header. + // + if (!Ikev2ValidateHeader (IkeSaSession, IkePacket->Header)) { + // + // Drop the packet if invalid IKE header. + // + goto ON_ERROR; + } + + // + // Decode all the payloads in the IKE packet. + // + IkeSaCommon = &IkeSaSession->SessionCommon; + Status = Ikev2DecodePacket (IkeSaCommon, IkePacket, IkeSessionTypeIkeSa); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Try to reate the first ChildSa Session of that IkeSaSession. + // If the IkeSaSession is responder, here will create the first ChildSaSession. + // + if (IkeSaCommon->State == IkeStateAuth && IsListEmpty(&IkeSaSession->ChildSaSessionList)) { + // + // Generate a piggyback child SA in IKE_STATE_AUTH state. + // + ASSERT (IsListEmpty (&IkeSaSession->ChildSaSessionList) && + IsListEmpty (&IkeSaSession->ChildSaEstablishSessionList)); + + ChildSaSession = Ikev2ChildSaSessionCreate (IkeSaSession, UdpService); + if (ChildSaSession == NULL) { + goto ON_ERROR; + } + + ChildSaCommon = &ChildSaSession->SessionCommon; + } + + // + // Parse the IKE request packet according to the auth method and current state. + // + Handler = mIkev2Initial[IkeSaSession->Pad->Data->AuthMethod][IkeSaCommon->State]; + Status = Handler.Parser ((UINT8 *)IkeSaSession, IkePacket); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Try to reate the first ChildSa Session of that IkeSaSession. + // If the IkeSaSession is initiator, here will create the first ChildSaSession. + // + if (IkeSaCommon->State == IkeStateAuth && IsListEmpty(&IkeSaSession->ChildSaSessionList)) { + // + // Generate a piggyback child SA in IKE_STATE_AUTH state. + // + ASSERT (IsListEmpty (&IkeSaSession->ChildSaSessionList) && + IsListEmpty (&IkeSaSession->ChildSaEstablishSessionList)); + + ChildSaSession = Ikev2ChildSaSessionCreate (IkeSaSession, UdpService); + if (ChildSaSession == NULL) { + goto ON_ERROR; + } + + ChildSaCommon = &ChildSaSession->SessionCommon; + + // + // Initialize the SA data for Child SA. + // + ChildSaSession->SaData = Ikev2InitializeSaData (ChildSaCommon); + } + + // + // Generate the IKE response packet and send it out if not established. + // + if (IkeSaCommon->State != IkeStateIkeSaEstablished) { + Handler = mIkev2Initial[IkeSaSession->Pad->Data->AuthMethod][IkeSaCommon->State]; + Reply = Handler.Generator ((UINT8 *) IkeSaSession, NULL); + if (Reply == NULL) { + goto ON_ERROR; + } + + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) IkeSaCommon, Reply, 0); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + if (!IkeSaCommon->IsInitiator) { + IkeSaCommon->State ++; + IKEV2_DUMP_STATE (IkeSaCommon->State - 1, IkeSaCommon->State); + } + } + + // + // Insert the new IkeSaSession into the Private processing IkeSaSession List. + // + if (IsNewSession) { + Ikev2SaSessionInsert (&Private->Ikev2SessionList, IkeSaSession, &IkePacket->RemotePeerIp); + } + + // + // Register the IkeSaSession and remove it from processing list. + // + if (IkeSaCommon->State == IkeStateIkeSaEstablished) { + + // + // Remove the Established IKE SA Session from the IKE SA Session Negotiating list + // and insert it into IKE SA Session Established list. + // + Ikev2SaSessionRemove (&Private->Ikev2SessionList, &IkePacket->RemotePeerIp); + Ikev2SaSessionReg (IkeSaSession, Private); + + // + // Remove the Established Child SA Session from the IkeSaSession->ChildSaSessionList + // ,insert it into IkeSaSession->ChildSaEstablishSessionList and save this Child SA + // into SAD. + // + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (IkeSaSession->ChildSaSessionList.BackLink); + Ikev2ChildSaSessionRemove ( + &IkeSaSession->ChildSaSessionList, + ChildSaSession->LocalPeerSpi, + IKEV2_ESTABLISHING_CHILDSA_LIST + ); + Ikev2ChildSaSessionReg (ChildSaSession, Private); + } + + return ; + +ON_ERROR: + if (ChildSaSession != NULL) { + // + // Remove the ChildSa from the list (Established list or Negotiating list). + // + RemoveEntryList (&ChildSaSession->ByIkeSa); + Ikev2ChildSaSessionFree (ChildSaSession); + } + + if (IsNewSession && IkeSaSession != NULL) { + // + // Remove the IkeSa from the list (Established list or Negotiating list). + // + if ((&IkeSaSession->BySessionTable)->ForwardLink != NULL && + !IsListEmpty (&IkeSaSession->BySessionTable + )){ + RemoveEntryList (&IkeSaSession->BySessionTable); + } + Ikev2SaSessionFree (IkeSaSession); + } + + return ; +} + +/** + + The general interface when received a IKEv2 packet for the IKE Child SA establishing + or IKE SA/CHILD SA rekeying. + + This function first find the related IKE SA Session according to the IKE packet's + remote IP. Then call the corresponding function to handle this IKE packet according + to the related IKE Child Session's State. + + @param[in] UdpService Pointer of related UDP Service. + @param[in] IkePacket Data passed by caller. + +**/ +VOID +Ikev2HandleChildSa ( + IN IKE_UDP_SERVICE *UdpService, + IN IKE_PACKET *IkePacket + ) +{ + EFI_STATUS Status; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CREATE_CHILD_REQUEST_TYPE RequestType; + IKE_PACKET *Reply; + IPSEC_PRIVATE_DATA *Private; + + Private = (UdpService->IpVersion == IP_VERSION_4) ? + IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : + IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead); + + Reply = NULL; + + // + // Lookup the remote ip address in the processing IKE SA session list. + // + IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2EstablishedList, &IkePacket->RemotePeerIp); + + if (IkeSaSession == NULL) { + // + // Drop the packet if no IKE SA associated. + // + return ; + } + + // + // Validate the IKE packet header. + // + if (!Ikev2ValidateHeader (IkeSaSession, IkePacket->Header)) { + // + // Drop the packet if invalid IKE header. + // + return; + } + + // + // Decode all the payloads in the IKE packet. + // + Status = Ikev2DecodePacket (&IkeSaSession->SessionCommon, IkePacket, IkeSessionTypeIkeSa); + if (EFI_ERROR (Status)) { + return; + } + + // + // Get the request type: CreateChildSa/RekeyChildSa/RekeyIkeSa. + // + RequestType = Ikev2ChildExchangeRequestType (IkePacket); + + switch (RequestType) { + case IkeRequestTypeCreateChildSa: + case IkeRequestTypeRekeyChildSa: + case IkeRequestTypeRekeyIkeSa: + // + // Parse the IKE request packet. Not support CREATE_CHILD_SA exchange yet, so + // only EFI_UNSUPPORTED will be returned and that will trigger a reply with a + // Notify payload of type NO_ADDITIONAL_SAS. + // + Status = mIkev2CreateChild.Parser ((UINT8 *) IkeSaSession, IkePacket); + if (EFI_ERROR (Status)) { + goto ON_REPLY; + } + + default: + // + // No support. + // + return ; + } + +ON_REPLY: + // + // Generate the reply packet if needed and send it out. + // + if (!(IkePacket->Header->Flags & IKE_HEADER_FLAGS_RESPOND)) { + Reply = mIkev2CreateChild.Generator ((UINT8 *) IkeSaSession, &IkePacket->Header->MessageId); + if (Reply != NULL) { + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) &(IkeSaSession->SessionCommon), Reply, 0); + if (EFI_ERROR (Status)) { + // + // Delete Reply payload. + // + if (Reply != NULL) { + IkePacketFree (Reply); + } + } + } + } + return ; +} + +/** + + It is general interface to handle IKEv2 information Exchange. + + @param[in] UdpService Point to IKE UPD Service related to this information exchange. + @param[in] IkePacket The IKE packet to be parsed. + +**/ +VOID +Ikev2HandleInfo ( + IN IKE_UDP_SERVICE *UdpService, + IN IKE_PACKET *IkePacket + ) +{ + EFI_STATUS Status; + IKEV2_SESSION_COMMON *SessionCommon; + IKEV2_SA_SESSION *IkeSaSession; + IPSEC_PRIVATE_DATA *Private; + + Private = (UdpService->IpVersion == IP_VERSION_4) ? + IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : + IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead); + + // + // Lookup the remote ip address in the processing IKE SA session list. + // + IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2EstablishedList, &IkePacket->RemotePeerIp); + + if (IkeSaSession == NULL) { + // + // Drop the packet if no IKE SA associated. + // + return ; + } + // + // Validate the IKE packet header. + // + if (!Ikev2ValidateHeader (IkeSaSession, IkePacket->Header)) { + + // + // Drop the packet if invalid IKE header. + // + return; + } + + SessionCommon = &IkeSaSession->SessionCommon; + + // + // Decode all the payloads in the IKE packet. + // + Status = Ikev2DecodePacket (SessionCommon, IkePacket, IkeSessionTypeIkeSa); + if (EFI_ERROR (Status)) { + return; + } + + Status = mIkev2Info.Parser ((UINT8 *)IkeSaSession, IkePacket); + + if (EFI_ERROR (Status)) { + // + // Drop the packet if fail to parse. + // + return; + } +} + +IKE_EXCHANGE_INTERFACE mIkev1Exchange = { + 1, + NULL, //Ikev1NegotiateSa + NULL, //Ikev1NegotiateChildSa + NULL, + NULL, //Ikev1HandleSa, + NULL, //Ikev1HandleChildSa + NULL, //Ikev1HandleInfo +}; + +IKE_EXCHANGE_INTERFACE mIkev2Exchange = { + 2, + Ikev2NegotiateSa, + Ikev2NegotiateChildSa, + Ikev2NegotiateInfo, + Ikev2HandleSa, + Ikev2HandleChildSa, + Ikev2HandleInfo +}; + diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h b/Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h new file mode 100644 index 0000000000..a2b733a4d2 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h @@ -0,0 +1,258 @@ +/** @file + IKEv2 related definitions. + + Copyright (c) 2010, 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. + +**/ +#ifndef _IKE_V2_H_ +#define _IKE_V2_H_ + +#include "Ike.h" +#include "Payload.h" + +#define IKEV2_TS_ANY_PORT 0xffff +#define IKEV2_TS_ANY_PROTOCOL 0 + +#define IKEV2_DELET_CHILDSA_LIST 0 +#define IKEV2_ESTABLISHING_CHILDSA_LIST 1 +#define IKEV2_ESTABLISHED_CHILDSA_LIST 2 + +#define IKEV2_SA_SESSION_SIGNATURE SIGNATURE_32 ('I', 'K', 'E', 'I') +#define IKEV2_SA_SESSION_FROM_COMMON(a) CR (a, IKEV2_SA_SESSION, SessionCommon, IKEV2_SA_SESSION_SIGNATURE) +#define IKEV2_SA_SESSION_BY_SESSION(a) CR (a, IKEV2_SA_SESSION, BySessionTable, IKEV2_SA_SESSION_SIGNATURE) +#define IKEV2_SA_SESSION_BY_ESTABLISHED(a) CR (a, IKEV2_SA_SESSION, ByEstablishedTable, IKEV2_SA_SESSION_SIGNATURE) + +#define IKEV2_CHILD_SA_SESSION_SIGNATURE SIGNATURE_32 ('I', 'K', 'E', 'C') +#define IKEV2_CHILD_SA_SESSION_FROM_COMMON(a) CR (a, IKEV2_CHILD_SA_SESSION, SessionCommon, IKEV2_CHILD_SA_SESSION_SIGNATURE) +#define IKEV2_CHILD_SA_SESSION_BY_IKE_SA(a) CR (a, IKEV2_CHILD_SA_SESSION, ByIkeSa, IKEV2_CHILD_SA_SESSION_SIGNATURE) +#define IKEV2_CHILD_SA_SESSION_BY_DEL_SA(a) CR (a, IKEV2_CHILD_SA_SESSION, ByDelete, IKEV2_CHILD_SA_SESSION_SIGNATURE) + +#define IS_IKEV2_SA_SESSION(s) ((s)->Common.IkeSessionType == IkeSessionTypeIkeSa) +#define IKEV2_SA_FIRST_PROPOSAL(Sa) (IKEV2_PROPOSAL *)((IKEV2_SA *)(Sa)+1) +#define IKEV2_NEXT_TRANSFORM_WITH_SIZE(Transform,TransformSize) \ + (IKEV2_TRANSFORM *) ((UINT8 *)(Transform) + (TransformSize)) + +#define IKEV2_NEXT_PROPOSAL_WITH_SIZE(Proposal, ProposalSize) \ + (IKEV2_PROPOSAL *) ((UINT8 *)(Proposal) + (ProposalSize)) + +#define IKEV2_PROPOSAL_FIRST_TRANSFORM(Proposal) \ + (IKEV2_TRANSFORM *)((UINT8 *)((IKEV2_PROPOSAL *)(Proposal)+1) + \ + (((IKEV2_PROPOSAL *)(Proposal))->SpiSize)) +#define IKEV2_PROPOSAL_FIRST_TRANSFORM(Proposal) \ + (IKEV2_TRANSFORM *)((UINT8 *)((IKEV2_PROPOSAL *)(Proposal)+1) + \ + (((IKEV2_PROPOSAL *)(Proposal))->SpiSize)) + +typedef enum { + IkeStateInit, + IkeStateAuth, + IkeStateIkeSaEstablished, + IkeStateCreateChild, + IkeStateSaRekeying, + IkeStateChildSaEstablished, + IkeStateSaDeleting, + IkeStateMaximum +} IKEV2_SESSION_STATE; + +typedef enum { + IkeRequestTypeCreateChildSa, + IkeRequestTypeRekeyChildSa, + IkeRequestTypeRekeyIkeSa, + IkeRequestTypeMaximum +} IKEV2_CREATE_CHILD_REQUEST_TYPE; + +typedef struct { + UINT8 *GxBuffer; + UINTN GxSize; + UINT8 *GyBuffer; + UINTN GySize; + UINT8 *GxyBuffer; + UINTN GxySize; + UINT8 *DhContext; +} IKEV2_DH_BUFFER; + +typedef struct { + IKEV2_DH_BUFFER *DhBuffer; + UINT8 *SkdKey; + UINTN SkdKeySize; + UINT8 *SkAiKey; + UINTN SkAiKeySize; + UINT8 *SkArKey; + UINTN SkArKeySize; + UINT8 *SkEiKey; + UINTN SkEiKeySize; + UINT8 *SkErKey; + UINTN SkErKeySize; + UINT8 *SkPiKey; + UINTN SkPiKeySize; + UINT8 *SkPrKey; + UINTN SkPrKeySize; +} IKEV2_SESSION_KEYS; + +typedef struct { + UINT16 LifeType; + UINT64 LifeDuration; + UINT16 EncAlgId; + UINTN EnckeyLen; + UINT16 Prf; + UINT16 IntegAlgId; + UINTN IntegKeyLen; + UINT16 DhGroup; + UINT8 ExtSeq; +} IKEV2_SA_PARAMS; + +// +// Internal Payload +// +typedef struct { + IKEV2_SA SaHeader; + UINTN NumProposals; + // + // IKE_PROPOSAL_DATA Proposals[1]; + // +} IKEV2_SA_DATA; + +typedef struct { + UINT8 ProposalIndex; + UINT8 ProtocolId; + UINT8 *Spi; + UINT8 NumTransforms; + // + // IKE_TRANSFORM_DATA Transforms[1]; + // +} IKEV2_PROPOSAL_DATA; + +typedef struct { + UINT8 TransformIndex; + UINT8 TransformType; + UINT16 TransformId; + IKE_SA_ATTRIBUTE Attribute; +} IKEV2_TRANSFORM_DATA; + +typedef struct { + UINT8 IkeVer; + IKE_SESSION_TYPE IkeSessionType; + BOOLEAN IsInitiator; + BOOLEAN IsOnDeleting; // Flag to indicate whether the SA is on deleting. + IKEV2_SESSION_STATE State; + EFI_EVENT TimeoutEvent; + UINT64 TimeoutInterval; + UINTN RetryCount; + IKE_PACKET *LastSentPacket; + IKEV2_SA_PARAMS *SaParams; + UINT16 PreferDhGroup; + EFI_IP_ADDRESS RemotePeerIp; + EFI_IP_ADDRESS LocalPeerIp; + IKE_ON_PAYLOAD_FROM_NET BeforeDecodePayload; + IKE_ON_PAYLOAD_FROM_NET AfterEncodePayload; + IKE_UDP_SERVICE *UdpService; + IPSEC_PRIVATE_DATA *Private; +} IKEV2_SESSION_COMMON; + +typedef struct { + UINT32 Signature; + IKEV2_SESSION_COMMON SessionCommon; + UINT64 InitiatorCookie; + UINT64 ResponderCookie; + // + // Initiator: SA proposals to be sent + // Responder: SA proposals to be matched + // + IKEV2_SA_DATA *SaData; // SA Private struct used for SA payload generation + IKEV2_SESSION_KEYS *IkeKeys; + UINT8 *NiBlock; + UINTN NiBlkSize; + UINT8 *NrBlock; + UINTN NrBlkSize; + UINT8 *NCookie; // Buffer Contains the Notify Cookie + UINTN NCookieSize; // Size of NCookie + IPSEC_PAD_ENTRY *Pad; + IPSEC_SPD_ENTRY *Spd; // SPD that requested the negotiation, TODO: better use SPD selector + LIST_ENTRY ChildSaSessionList; + LIST_ENTRY ChildSaEstablishSessionList; // For Establish Child SA. + LIST_ENTRY InfoMIDList; // For Information MID + LIST_ENTRY DeleteSaList; // For deteling Child SA. + UINT8 *InitPacket; + UINTN InitPacketSize; + UINT8 *RespPacket; + UINTN RespPacketSize; + UINT32 MessageId; + LIST_ENTRY BySessionTable; // Use for all IkeSaSession Links +} IKEV2_SA_SESSION; + +typedef struct { + UINT32 Signature; + IKEV2_SESSION_COMMON SessionCommon; + IKEV2_SA_SESSION *IkeSaSession; + UINT32 MessageId; + IKEV2_SA_DATA *SaData; + UINT8 IpsecProtocol; + UINT32 LocalPeerSpi; + UINT32 RemotePeerSpi; + UINT8 *NiBlock; + UINTN NiBlkSize; + UINT8 *NrBlock; + UINTN NrBlkSize; + SA_KEYMATS ChildKeymats; + IKEV2_DH_BUFFER *DhBuffer; //New DH exchnaged by CREATE_CHILD_SA + IPSEC_SPD_ENTRY *Spd; + EFI_IPSEC_SPD_SELECTOR *SpdSelector; + UINT16 ProtoId; + UINT16 RemotePort; + UINT16 LocalPort; + LIST_ENTRY ByIkeSa; + LIST_ENTRY ByDelete; +} IKEV2_CHILD_SA_SESSION; + +typedef enum { + Ikev2InfoNotify, + Ikev2InfoDelete, + Ikev2InfoLiveCheck +} IKEV2_INFO_TYPE; + +// +// This struct is used to pass the detail infromation to the InfoGenerator() for +// the response Information Exchange Message creatation. +// +typedef struct { + UINT32 MessageId; + IKEV2_INFO_TYPE InfoType; +} IKEV2_INFO_EXCHANGE_CONTEXT; + +typedef struct { + UINTN DataSize; + UINT8 *Data; +} PRF_DATA_FRAGMENT; + +typedef +IKE_PACKET * +(*IKEV2_PACKET_GENERATOR) ( + IN UINT8 *SaSession, + IN VOID *Context +); + +typedef +EFI_STATUS +(*IKEV2_PACKET_PARSER) ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket +); + +typedef struct { + IKEV2_PACKET_PARSER Parser; + IKEV2_PACKET_GENERATOR Generator; +} IKEV2_PACKET_HANDLER; + +extern IKEV2_PACKET_HANDLER mIkev2Initial[][2]; +extern IKEV2_PACKET_HANDLER mIkev2CreateChild; +extern IKEV2_PACKET_HANDLER mIkev2Info; + +#endif + diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Info.c b/Core/NetworkPkg/IpSecDxe/Ikev2/Info.c new file mode 100644 index 0000000000..0d2b290817 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Info.c @@ -0,0 +1,409 @@ +/** @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 +}; diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c b/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c new file mode 100644 index 0000000000..237743b1b1 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c @@ -0,0 +1,3353 @@ +/** @file + The implementation of Payloads Creation. + + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2010 - 2017, 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" +#include "IpSecCryptIo.h" + +// +// The Constant String of "Key Pad for IKEv2" for Authentication Payload generation. +// +#define CONSTANT_KEY_SIZE 17 +GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mConstantKey[CONSTANT_KEY_SIZE] = +{ + 'K', 'e', 'y', ' ', 'P', 'a', 'd', ' ', 'f', 'o', 'r', ' ', 'I', 'K', 'E', 'v', '2' +}; + +/** + Generate Ikev2 SA payload according to SessionSaData + + @param[in] SessionSaData The data used in SA payload. + @param[in] NextPayload The payload type presented in NextPayload field of + SA Payload header. + @param[in] Type The SA type. It MUST be neither (1) for IKE_SA or + (2) for CHILD_SA or (3) for INFO. + + @retval a Pointer to SA IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateSaPayload ( + IN IKEV2_SA_DATA *SessionSaData, + IN UINT8 NextPayload, + IN IKE_SESSION_TYPE Type + ) +{ + IKE_PAYLOAD *SaPayload; + IKEV2_SA_DATA *SaData; + UINTN SaDataSize; + + SaPayload = IkePayloadAlloc (); + if (SaPayload == NULL) { + return NULL; + } + + // + // TODO: Get the Proposal Number and Transform Number from IPsec Config, + // after the Ipsecconfig Application is support it. + // + + if (Type == IkeSessionTypeIkeSa) { + SaDataSize = sizeof (IKEV2_SA_DATA) + + SessionSaData->NumProposals * sizeof (IKEV2_PROPOSAL_DATA) + + sizeof (IKEV2_TRANSFORM_DATA) * SessionSaData->NumProposals * 4; + } else { + SaDataSize = sizeof (IKEV2_SA_DATA) + + SessionSaData->NumProposals * sizeof (IKEV2_PROPOSAL_DATA) + + sizeof (IKEV2_TRANSFORM_DATA) * SessionSaData->NumProposals * 3; + + } + + SaData = AllocateZeroPool (SaDataSize); + if (SaData == NULL) { + IkePayloadFree (SaPayload); + return NULL; + } + + CopyMem (SaData, SessionSaData, SaDataSize); + SaData->SaHeader.Header.NextPayload = NextPayload; + SaPayload->PayloadType = IKEV2_PAYLOAD_TYPE_SA; + SaPayload->PayloadBuf = (UINT8 *) SaData; + + return SaPayload; +} + +/** + Generate a Nonce payload containing the input parameter NonceBuf. + + @param[in] NonceBuf The nonce buffer contains the whole Nonce payload block + except the payload header. + @param[in] NonceSize The buffer size of the NonceBuf + @param[in] NextPayload The payload type presented in the NextPayload field + of Nonce Payload header. + + @retval Pointer to Nonce IKE paload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateNoncePayload ( + IN UINT8 *NonceBuf, + IN UINTN NonceSize, + IN UINT8 NextPayload + ) +{ + IKE_PAYLOAD *NoncePayload; + IKEV2_NONCE *Nonce; + UINTN Size; + UINT8 *NonceBlock; + + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Nonce Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + Size = sizeof (IKEV2_NONCE) + NonceSize; + NonceBlock = NonceBuf; + + Nonce = AllocateZeroPool (Size); + if (Nonce == NULL) { + return NULL; + } + + CopyMem (Nonce + 1, NonceBlock, Size - sizeof (IKEV2_NONCE)); + + Nonce->Header.NextPayload = NextPayload; + Nonce->Header.PayloadLength = (UINT16) Size; + NoncePayload = IkePayloadAlloc (); + if (NoncePayload == NULL) { + FreePool (Nonce); + return NULL; + } + + NoncePayload->PayloadType = IKEV2_PAYLOAD_TYPE_NONCE; + NoncePayload->PayloadBuf = (UINT8 *) Nonce; + NoncePayload->PayloadSize = Size; + + return NoncePayload; +} + +/** + Generate a Key Exchange payload according to the DH group type and save the + public Key into IkeSaSession IkeKey field. + + @param[in, out] IkeSaSession Pointer of the IKE_SA_SESSION. + @param[in] NextPayload The payload type presented in the NextPayload field of Key + Exchange Payload header. + + @retval Pointer to Key IKE payload. + +**/ +IKE_PAYLOAD* +Ikev2GenerateKePayload ( + IN OUT IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload + ) +{ + IKE_PAYLOAD *KePayload; + IKEV2_KEY_EXCHANGE *Ke; + UINTN KeSize; + IKEV2_SESSION_KEYS *IkeKeys; + + // + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! DH Group # ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Key Exchange Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + IkeKeys = IkeSaSession->IkeKeys; + + if (IkeSaSession->SessionCommon.IsInitiator) { + KeSize = sizeof (IKEV2_KEY_EXCHANGE) + IkeKeys->DhBuffer->GxSize; + } else { + KeSize = sizeof (IKEV2_KEY_EXCHANGE) + IkeKeys->DhBuffer->GxSize; + } + + // + // Allocate buffer for Key Exchange + // + Ke = AllocateZeroPool (KeSize); + if (Ke == NULL) { + return NULL; + } + + Ke->Header.NextPayload = NextPayload; + Ke->Header.PayloadLength = (UINT16) KeSize; + Ke->DhGroup = IkeSaSession->SessionCommon.PreferDhGroup; + + CopyMem (Ke + 1, IkeKeys->DhBuffer->GxBuffer, IkeKeys->DhBuffer->GxSize); + + // + // Create IKE_PAYLOAD to point to Key Exchange payload + // + KePayload = IkePayloadAlloc (); + if (KePayload == NULL) { + FreePool (Ke); + return NULL; + } + + KePayload->PayloadType = IKEV2_PAYLOAD_TYPE_KE; + KePayload->PayloadBuf = (UINT8 *) Ke; + KePayload->PayloadSize = KeSize; + return KePayload; +} + +/** + Generate a ID payload. + + @param[in] CommonSession Pointer to IKEV2_SESSION_COMMON related to ID payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + + @retval Pointer to ID IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateIdPayload ( + IN IKEV2_SESSION_COMMON *CommonSession, + IN UINT8 NextPayload + ) +{ + IKE_PAYLOAD *IdPayload; + IKEV2_ID *Id; + UINTN IdSize; + UINT8 IpVersion; + UINT8 AddrSize; + + // + // ID payload + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload ! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ID Type ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Identification Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + IpVersion = CommonSession->UdpService->IpVersion; + AddrSize = (UINT8) ((IpVersion == IP_VERSION_4) ? sizeof(EFI_IPv4_ADDRESS) : sizeof(EFI_IPv6_ADDRESS)); + IdSize = sizeof (IKEV2_ID) + AddrSize; + + Id = (IKEV2_ID *) AllocateZeroPool (IdSize); + if (Id == NULL) { + return NULL; + } + + IdPayload = IkePayloadAlloc (); + if (IdPayload == NULL) { + FreePool (Id); + return NULL; + } + + IdPayload->PayloadType = (UINT8) ((CommonSession->IsInitiator) ? IKEV2_PAYLOAD_TYPE_ID_INIT : IKEV2_PAYLOAD_TYPE_ID_RSP); + IdPayload->PayloadBuf = (UINT8 *) Id; + IdPayload->PayloadSize = IdSize; + + // + // Set generic header of identification payload + // + Id->Header.NextPayload = NextPayload; + Id->Header.PayloadLength = (UINT16) IdSize; + Id->IdType = (UINT8) ((IpVersion == IP_VERSION_4) ? IKEV2_ID_TYPE_IPV4_ADDR : IKEV2_ID_TYPE_IPV6_ADDR); + CopyMem (Id + 1, &CommonSession->LocalPeerIp, AddrSize); + + return IdPayload; +} + +/** + Generate a ID payload. + + @param[in] CommonSession Pointer to IKEV2_SESSION_COMMON related to ID payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + @param[in] InCert Pointer to the Certificate which distinguished name + will be added into the Id payload. + @param[in] CertSize Size of the Certificate. + + @retval Pointer to ID IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCertIdPayload ( + IN IKEV2_SESSION_COMMON *CommonSession, + IN UINT8 NextPayload, + IN UINT8 *InCert, + IN UINTN CertSize + ) +{ + IKE_PAYLOAD *IdPayload; + IKEV2_ID *Id; + UINTN IdSize; + UINTN SubjectSize; + UINT8 *CertSubject; + + // + // ID payload + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload ! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ID Type ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Identification Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + SubjectSize = 0; + CertSubject = NULL; + IpSecCryptoIoGetSubjectFromCert ( + InCert, + CertSize, + &CertSubject, + &SubjectSize + ); + if (SubjectSize != 0) { + ASSERT (CertSubject != NULL); + } + + IdSize = sizeof (IKEV2_ID) + SubjectSize; + + Id = (IKEV2_ID *) AllocateZeroPool (IdSize); + if (Id == NULL) { + return NULL; + } + + IdPayload = IkePayloadAlloc (); + if (IdPayload == NULL) { + FreePool (Id); + return NULL; + } + + IdPayload->PayloadType = (UINT8) ((CommonSession->IsInitiator) ? IKEV2_PAYLOAD_TYPE_ID_INIT : IKEV2_PAYLOAD_TYPE_ID_RSP); + IdPayload->PayloadBuf = (UINT8 *) Id; + IdPayload->PayloadSize = IdSize; + + // + // Set generic header of identification payload + // + Id->Header.NextPayload = NextPayload; + Id->Header.PayloadLength = (UINT16) IdSize; + Id->IdType = 9; + CopyMem (Id + 1, CertSubject, SubjectSize); + + if (CertSubject != NULL) { + FreePool (CertSubject); + } + return IdPayload; +} + +/** + Generate a Authentication Payload. + + This function is used for both Authentication generation and verification. When the + IsVerify is TRUE, it create a Auth Data for verification. This function choose the + related IKE_SA_INIT Message for Auth data creation according to the IKE Session's type + and the value of IsVerify parameter. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to. + @param[in] IdPayload Pointer to the ID payload to be used for Authentication + payload generation. + @param[in] NextPayload The type filled into the Authentication Payload next + payload field. + @param[in] IsVerify If it is TURE, the Authentication payload is used for + verification. + + @return pointer to IKE Authentication payload for Pre-shared key method. + +**/ +IKE_PAYLOAD * +Ikev2PskGenerateAuthPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *IdPayload, + IN UINT8 NextPayload, + IN BOOLEAN IsVerify + ) +{ + UINT8 *Digest; + UINTN DigestSize; + PRF_DATA_FRAGMENT Fragments[3]; + UINT8 *KeyBuf; + UINTN KeySize; + IKE_PAYLOAD *AuthPayload; + IKEV2_AUTH *PayloadBuf; + EFI_STATUS Status; + + // + // Auth = Prf(Prf(Secret,"Key Pad for IKEv2),IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) + // + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Auth Method ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Authentication Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + KeyBuf = NULL; + AuthPayload = NULL; + Digest = NULL; + + DigestSize = IpSecGetHmacDigestLength ((UINT8)IkeSaSession->SessionCommon.SaParams->Prf); + Digest = AllocateZeroPool (DigestSize); + if (Digest == NULL) { + return NULL; + } + + if (IdPayload == NULL) { + return NULL; + } + + // + // Calcualte Prf(Seceret, "Key Pad for IKEv2"); + // + Fragments[0].Data = (UINT8 *) mConstantKey; + Fragments[0].DataSize = CONSTANT_KEY_SIZE; + + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + IkeSaSession->Pad->Data->AuthData, + IkeSaSession->Pad->Data->AuthDataSize, + (HASH_DATA_FRAGMENT *)Fragments, + 1, + Digest, + DigestSize + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Store the AuthKey into KeyBuf + // + KeyBuf = AllocateZeroPool (DigestSize); + if (KeyBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + CopyMem (KeyBuf, Digest, DigestSize); + KeySize = DigestSize; + + // + // Calculate Prf(SK_Pi/r, IDi/r) + // + Fragments[0].Data = IdPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER); + Fragments[0].DataSize = IdPayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER); + + if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) || + (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify) + ) { + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + IkeSaSession->IkeKeys->SkPrKey, + IkeSaSession->IkeKeys->SkPrKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + Digest, + DigestSize + ); + } else { + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + IkeSaSession->IkeKeys->SkPiKey, + IkeSaSession->IkeKeys->SkPiKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + Digest, + DigestSize + ); + } + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Copy data to Fragments. + // + if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) || + (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify) + ) { + Fragments[0].Data = IkeSaSession->RespPacket; + Fragments[0].DataSize = IkeSaSession->RespPacketSize; + Fragments[1].Data = IkeSaSession->NiBlock; + Fragments[1].DataSize = IkeSaSession->NiBlkSize; + } else { + Fragments[0].Data = IkeSaSession->InitPacket; + Fragments[0].DataSize = IkeSaSession->InitPacketSize; + Fragments[1].Data = IkeSaSession->NrBlock; + Fragments[1].DataSize = IkeSaSession->NrBlkSize; + } + + // + // Copy the result of Prf(SK_Pr, IDi/r) to Fragments[2]. + // + Fragments[2].Data = AllocateZeroPool (DigestSize); + if (Fragments[2].Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + Fragments[2].DataSize = DigestSize; + CopyMem (Fragments[2].Data, Digest, DigestSize); + + // + // Calculate Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) + // + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + KeyBuf, + KeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 3, + Digest, + DigestSize + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Allocate buffer for Auth Payload + // + AuthPayload = IkePayloadAlloc (); + if (AuthPayload == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + AuthPayload->PayloadSize = sizeof (IKEV2_AUTH) + DigestSize; + PayloadBuf = (IKEV2_AUTH *) AllocateZeroPool (AuthPayload->PayloadSize); + if (PayloadBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + // + // Fill in Auth payload. + // + PayloadBuf->Header.NextPayload = NextPayload; + PayloadBuf->Header.PayloadLength = (UINT16) (AuthPayload->PayloadSize); + if (IkeSaSession->Pad->Data->AuthMethod == EfiIPsecAuthMethodPreSharedSecret) { + // + // Only support Shared Key Message Integrity + // + PayloadBuf->AuthMethod = IKEV2_AUTH_METHOD_SKMI; + } else { + // + // Not support other Auth method. + // + Status = EFI_UNSUPPORTED; + goto EXIT; + } + + // + // Copy the result of Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) to Auth + // payload block. + // + CopyMem ( + PayloadBuf + 1, + Digest, + DigestSize + ); + + // + // Fill in IKE_PACKET + // + AuthPayload->PayloadBuf = (UINT8 *) PayloadBuf; + AuthPayload->PayloadType = IKEV2_PAYLOAD_TYPE_AUTH; + +EXIT: + if (KeyBuf != NULL) { + FreePool (KeyBuf); + } + if (Digest != NULL) { + FreePool (Digest); + } + if (Fragments[2].Data != NULL) { + // + // Free the buffer which contains the result of Prf(SK_Pr, IDi/r) + // + FreePool (Fragments[2].Data); + } + + if (EFI_ERROR (Status)) { + if (AuthPayload != NULL) { + IkePayloadFree (AuthPayload); + } + return NULL; + } else { + return AuthPayload; + } +} + +/** + Generate a Authentication Payload for Certificate Auth method. + + This function has two functions. One is creating a local Authentication + Payload for sending and other is creating the remote Authentication data + for verification when the IsVerify is TURE. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to. + @param[in] IdPayload Pointer to the ID payload to be used for Authentication + payload generation. + @param[in] NextPayload The type filled into the Authentication Payload + next payload field. + @param[in] IsVerify If it is TURE, the Authentication payload is used + for verification. + @param[in] UefiPrivateKey Pointer to the UEFI private key. Ignore it when + verify the authenticate payload. + @param[in] UefiPrivateKeyLen The size of UefiPrivateKey in bytes. Ignore it + when verify the authenticate payload. + @param[in] UefiKeyPwd Pointer to the password of UEFI private key. + Ignore it when verify the authenticate payload. + @param[in] UefiKeyPwdLen The size of UefiKeyPwd in bytes.Ignore it when + verify the authenticate payload. + + @return pointer to IKE Authentication payload for Cerifitcation method. + +**/ +IKE_PAYLOAD * +Ikev2CertGenerateAuthPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *IdPayload, + IN UINT8 NextPayload, + IN BOOLEAN IsVerify, + IN UINT8 *UefiPrivateKey, + IN UINTN UefiPrivateKeyLen, + IN UINT8 *UefiKeyPwd, + IN UINTN UefiKeyPwdLen + ) +{ + UINT8 *Digest; + UINTN DigestSize; + PRF_DATA_FRAGMENT Fragments[3]; + IKE_PAYLOAD *AuthPayload; + IKEV2_AUTH *PayloadBuf; + EFI_STATUS Status; + UINT8 *Signature; + UINTN SigSize; + + // + // Auth = Prf(Scert,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) + // + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Auth Method ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Authentication Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // + // Initial point + // + AuthPayload = NULL; + Digest = NULL; + Signature = NULL; + SigSize = 0; + + if (IdPayload == NULL) { + return NULL; + } + DigestSize = IpSecGetHmacDigestLength ((UINT8)IkeSaSession->SessionCommon.SaParams->Prf); + Digest = AllocateZeroPool (DigestSize); + if (Digest == NULL) { + return NULL; + } + + // + // Calculate Prf(SK_Pi/r, IDi/r) + // + Fragments[0].Data = IdPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER); + Fragments[0].DataSize = IdPayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER); + + IpSecDumpBuf ("RestofIDPayload", Fragments[0].Data, Fragments[0].DataSize); + + if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) || + (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify) + ) { + Status = IpSecCryptoIoHmac( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + IkeSaSession->IkeKeys->SkPrKey, + IkeSaSession->IkeKeys->SkPrKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + Digest, + DigestSize + ); + IpSecDumpBuf ("MACedIDForR", Digest, DigestSize); + } else { + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + IkeSaSession->IkeKeys->SkPiKey, + IkeSaSession->IkeKeys->SkPiKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + Digest, + DigestSize + ); + IpSecDumpBuf ("MACedIDForI", Digest, DigestSize); + } + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Copy data to Fragments. + // + if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) || + (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify) + ) { + Fragments[0].Data = IkeSaSession->RespPacket; + Fragments[0].DataSize = IkeSaSession->RespPacketSize; + Fragments[1].Data = IkeSaSession->NiBlock; + Fragments[1].DataSize = IkeSaSession->NiBlkSize; + IpSecDumpBuf ("RealMessage2", Fragments[0].Data, Fragments[0].DataSize); + IpSecDumpBuf ("NonceIDdata", Fragments[1].Data, Fragments[1].DataSize); + } else { + Fragments[0].Data = IkeSaSession->InitPacket; + Fragments[0].DataSize = IkeSaSession->InitPacketSize; + Fragments[1].Data = IkeSaSession->NrBlock; + Fragments[1].DataSize = IkeSaSession->NrBlkSize; + IpSecDumpBuf ("RealMessage1", Fragments[0].Data, Fragments[0].DataSize); + IpSecDumpBuf ("NonceRDdata", Fragments[1].Data, Fragments[1].DataSize); + } + + // + // Copy the result of Prf(SK_Pr, IDi/r) to Fragments[2]. + // + Fragments[2].Data = AllocateZeroPool (DigestSize); + if (Fragments[2].Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + Fragments[2].DataSize = DigestSize; + CopyMem (Fragments[2].Data, Digest, DigestSize); + + // + // Calculate Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) + // + Status = IpSecCryptoIoHash ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + (HASH_DATA_FRAGMENT *) Fragments, + 3, + Digest, + DigestSize + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + IpSecDumpBuf ("HashSignedOctects", Digest, DigestSize); + // + // Sign the data by the private Key + // + if (!IsVerify) { + IpSecCryptoIoAuthDataWithCertificate ( + Digest, + DigestSize, + UefiPrivateKey, + UefiPrivateKeyLen, + UefiKeyPwd, + UefiKeyPwdLen, + &Signature, + &SigSize + ); + + if (SigSize == 0 || Signature == NULL) { + goto EXIT; + } + } + + // + // Allocate buffer for Auth Payload + // + AuthPayload = IkePayloadAlloc (); + if (AuthPayload == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + if (!IsVerify) { + AuthPayload->PayloadSize = sizeof (IKEV2_AUTH) + SigSize; + } else { + AuthPayload->PayloadSize = sizeof (IKEV2_AUTH) + DigestSize; + } + + PayloadBuf = (IKEV2_AUTH *) AllocateZeroPool (AuthPayload->PayloadSize); + if (PayloadBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + // + // Fill in Auth payload. + // + PayloadBuf->Header.NextPayload = NextPayload; + PayloadBuf->Header.PayloadLength = (UINT16) (AuthPayload->PayloadSize); + if (IkeSaSession->Pad->Data->AuthMethod == EfiIPsecAuthMethodCertificates) { + PayloadBuf->AuthMethod = IKEV2_AUTH_METHOD_RSA; + } else { + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + + // + // Copy the result of Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) to Auth + // payload block. + // + if (!IsVerify) { + CopyMem (PayloadBuf + 1, Signature, SigSize); + } else { + CopyMem (PayloadBuf + 1, Digest, DigestSize); + } + + // + // Fill in IKE_PACKET + // + AuthPayload->PayloadBuf = (UINT8 *) PayloadBuf; + AuthPayload->PayloadType = IKEV2_PAYLOAD_TYPE_AUTH; + +EXIT: + if (Digest != NULL) { + FreePool (Digest); + } + if (Signature != NULL) { + FreePool (Signature); + } + if (Fragments[2].Data != NULL) { + // + // Free the buffer which contains the result of Prf(SK_Pr, IDi/r) + // + FreePool (Fragments[2].Data); + } + + if (EFI_ERROR (Status)) { + if (AuthPayload != NULL) { + IkePayloadFree (AuthPayload); + } + return NULL; + } else { + return AuthPayload; + } +} + +/** + Generate TS payload. + + This function generates TSi or TSr payload according to type of next payload. + If the next payload is Responder TS, gereate TSi Payload. Otherwise, generate + TSr payload. + + @param[in] ChildSa Pointer to IKEV2_CHILD_SA_SESSION related to this TS payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + @param[in] IsTunnel It indicates that if the Ts Payload is after the CP payload. + If yes, it means the Tsi and Tsr payload should be with + Max port range and address range and protocol is marked + as zero. + + @retval Pointer to Ts IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateTsPayload ( + IN IKEV2_CHILD_SA_SESSION *ChildSa, + IN UINT8 NextPayload, + IN BOOLEAN IsTunnel + ) +{ + IKE_PAYLOAD *TsPayload; + IKEV2_TS *TsPayloadBuf; + TRAFFIC_SELECTOR *TsSelector; + UINTN SelectorSize; + UINTN TsPayloadSize; + UINT8 IpVersion; + UINT8 AddrSize; + + // + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Number of TSs ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + TsPayload = IkePayloadAlloc(); + if (TsPayload == NULL) { + return NULL; + } + + IpVersion = ChildSa->SessionCommon.UdpService->IpVersion; + // + // The Starting Address and Ending Address is variable length depends on + // is IPv4 or IPv6 + // + AddrSize = (UINT8)((IpVersion == IP_VERSION_4) ? sizeof (EFI_IPv4_ADDRESS) : sizeof (EFI_IPv6_ADDRESS)); + SelectorSize = sizeof (TRAFFIC_SELECTOR) + 2 * AddrSize; + TsPayloadSize = sizeof (IKEV2_TS) + SelectorSize; + TsPayloadBuf = AllocateZeroPool (TsPayloadSize); + if (TsPayloadBuf == NULL) { + goto ON_ERROR; + } + + TsPayload->PayloadBuf = (UINT8 *) TsPayloadBuf; + TsSelector = (TRAFFIC_SELECTOR*)(TsPayloadBuf + 1); + + TsSelector->TSType = (UINT8)((IpVersion == IP_VERSION_4) ? IKEV2_TS_TYPE_IPV4_ADDR_RANGE : IKEV2_TS_TYPS_IPV6_ADDR_RANGE); + + // + // For tunnel mode + // + if (IsTunnel) { + TsSelector->IpProtocolId = IKEV2_TS_ANY_PROTOCOL; + TsSelector->SelecorLen = (UINT16) SelectorSize; + TsSelector->StartPort = 0; + TsSelector->EndPort = IKEV2_TS_ANY_PORT; + ZeroMem ((UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR), AddrSize); + SetMem ((UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR) + AddrSize, AddrSize, 0xff); + + } else { + // + // TODO: Support port range and address range + // + if (NextPayload == IKEV2_PAYLOAD_TYPE_TS_RSP){ + // + // Create initiator Traffic Selector + // + TsSelector->SelecorLen = (UINT16)SelectorSize; + + // + // Currently only support the port range from 0~0xffff. Don't support other + // port range. + // TODO: support Port range + // + if (ChildSa->SessionCommon.IsInitiator) { + if (ChildSa->Spd->Selector->LocalPort != 0 && + ChildSa->Spd->Selector->LocalPortRange == 0) { + // + // For not port range. + // + TsSelector->StartPort = ChildSa->Spd->Selector->LocalPort; + TsSelector->EndPort = ChildSa->Spd->Selector->LocalPort; + } else if (ChildSa->Spd->Selector->LocalPort == 0){ + // + // For port from 0~0xffff + // + TsSelector->StartPort = 0; + TsSelector->EndPort = IKEV2_TS_ANY_PORT; + } else { + // + // Not support now. + // + goto ON_ERROR; + } + } else { + if (ChildSa->Spd->Selector->RemotePort != 0 && + ChildSa->Spd->Selector->RemotePortRange == 0) { + // + // For not port range. + // + TsSelector->StartPort = ChildSa->Spd->Selector->RemotePort; + TsSelector->EndPort = ChildSa->Spd->Selector->RemotePort; + } else if (ChildSa->Spd->Selector->RemotePort == 0) { + // + // For port from 0~0xffff + // + TsSelector->StartPort = 0; + TsSelector->EndPort = IKEV2_TS_ANY_PORT; + } else { + // + // Not support now. + // + goto ON_ERROR; + } + } + // + // Copy Address.Currently the address range is not supported. + // The Starting address is same as Ending address + // TODO: Support Address Range. + // + CopyMem ( + (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR), + ChildSa->SessionCommon.IsInitiator ? + ChildSa->Spd->Selector->LocalAddress : + ChildSa->Spd->Selector->RemoteAddress, + AddrSize + ); + CopyMem ( + (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR) + AddrSize, + ChildSa->SessionCommon.IsInitiator ? + ChildSa->Spd->Selector->LocalAddress : + ChildSa->Spd->Selector->RemoteAddress, + AddrSize + ); + // + // If the Next Payload is not TS responder, this TS payload type is the TS responder. + // + TsPayload->PayloadType = IKEV2_PAYLOAD_TYPE_TS_INIT; + }else{ + // + // Create responder Traffic Selector + // + TsSelector->SelecorLen = (UINT16)SelectorSize; + + // + // Currently only support the port range from 0~0xffff. Don't support other + // port range. + // TODO: support Port range + // + if (!ChildSa->SessionCommon.IsInitiator) { + if (ChildSa->Spd->Selector->LocalPort != 0 && + ChildSa->Spd->Selector->LocalPortRange == 0) { + // + // For not port range. + // + TsSelector->StartPort = ChildSa->Spd->Selector->LocalPort; + TsSelector->EndPort = ChildSa->Spd->Selector->LocalPort; + } else if (ChildSa->Spd->Selector->LocalPort == 0){ + // + // For port from 0~0xffff + // + TsSelector->StartPort = 0; + TsSelector->EndPort = IKEV2_TS_ANY_PORT; + } else { + // + // Not support now. + // + goto ON_ERROR; + } + } else { + if (ChildSa->Spd->Selector->RemotePort != 0 && + ChildSa->Spd->Selector->RemotePortRange == 0) { + // + // For not port range. + // + TsSelector->StartPort = ChildSa->Spd->Selector->RemotePort; + TsSelector->EndPort = ChildSa->Spd->Selector->RemotePort; + } else if (ChildSa->Spd->Selector->RemotePort == 0){ + // + // For port from 0~0xffff + // + TsSelector->StartPort = 0; + TsSelector->EndPort = IKEV2_TS_ANY_PORT; + } else { + // + // Not support now. + // + goto ON_ERROR; + } + } + // + // Copy Address.Currently the address range is not supported. + // The Starting address is same as Ending address + // TODO: Support Address Range. + // + CopyMem ( + (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR), + ChildSa->SessionCommon.IsInitiator ? + ChildSa->Spd->Selector->RemoteAddress : + ChildSa->Spd->Selector->LocalAddress, + AddrSize + ); + CopyMem ( + (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR) + AddrSize, + ChildSa->SessionCommon.IsInitiator ? + ChildSa->Spd->Selector->RemoteAddress : + ChildSa->Spd->Selector->LocalAddress, + AddrSize + ); + // + // If the Next Payload is not TS responder, this TS payload type is the TS responder. + // + TsPayload->PayloadType = IKEV2_PAYLOAD_TYPE_TS_RSP; + } + } + + if (ChildSa->Spd->Selector->NextLayerProtocol != 0xffff) { + TsSelector->IpProtocolId = (UINT8)ChildSa->Spd->Selector->NextLayerProtocol; + } else { + TsSelector->IpProtocolId = IKEV2_TS_ANY_PROTOCOL; + } + + TsPayloadBuf->Header.NextPayload = NextPayload; + TsPayloadBuf->Header.PayloadLength = (UINT16)TsPayloadSize; + TsPayloadBuf->TSNumbers = 1; + TsPayload->PayloadSize = TsPayloadSize; + goto ON_EXIT; + +ON_ERROR: + if (TsPayload != NULL) { + IkePayloadFree (TsPayload); + TsPayload = NULL; + } +ON_EXIT: + return TsPayload; +} + +/** + Generate the Notify payload. + + Since the structure of Notify payload which defined in RFC 4306 is simple, so + there is no internal data structure for Notify payload. This function generate + Notify payload defined in RFC 4306, but all the fields in this payload are still + in host order and need call Ikev2EncodePayload() to convert those fields from + the host order to network order beforing sending it. + + @param[in] ProtocolId The protocol type ID. For IKE_SA it MUST be one (1). + For IPsec SAs it MUST be neither (2) for AH or (3) + for ESP. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Notify payload. + @param[in] SpiSize Size of the SPI in SPI size field of the Notify Payload. + @param[in] MessageType The message type in NotifyMessageType field of the + Notify Payload. + @param[in] SpiBuf Pointer to buffer contains the SPI value. + @param[in] NotifyData Pointer to buffer contains the notification data. + @param[in] NotifyDataSize The size of NotifyData in bytes. + + + @retval Pointer to IKE Notify Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateNotifyPayload ( + IN UINT8 ProtocolId, + IN UINT8 NextPayload, + IN UINT8 SpiSize, + IN UINT16 MessageType, + IN UINT8 *SpiBuf, + IN UINT8 *NotifyData, + IN UINTN NotifyDataSize + ) +{ + IKE_PAYLOAD *NotifyPayload; + IKEV2_NOTIFY *Notify; + UINT16 NotifyPayloadLen; + UINT8 *MessageData; + + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Protocol ID ! SPI Size ! Notify Message Type ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Security Parameter Index (SPI) ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Notification Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // + NotifyPayloadLen = (UINT16) (sizeof (IKEV2_NOTIFY) + NotifyDataSize + SpiSize); + Notify = (IKEV2_NOTIFY *) AllocateZeroPool (NotifyPayloadLen); + if (Notify == NULL) { + return NULL; + } + + // + // Set Delete Payload's Generic Header + // + Notify->Header.NextPayload = NextPayload; + Notify->Header.PayloadLength = NotifyPayloadLen; + Notify->SpiSize = SpiSize; + Notify->ProtocolId = ProtocolId; + Notify->MessageType = MessageType; + + // + // Copy Spi , for Cookie Notify, there is no SPI. + // + if (SpiBuf != NULL && SpiSize != 0 ) { + CopyMem (Notify + 1, SpiBuf, SpiSize); + } + + MessageData = ((UINT8 *) (Notify + 1)) + SpiSize; + + // + // Copy Notification Data + // + if (NotifyDataSize != 0) { + CopyMem (MessageData, NotifyData, NotifyDataSize); + } + + // + // Create Payload for and set type as IKEV2_PAYLOAD_TYPE_NOTIFY + // + NotifyPayload = IkePayloadAlloc (); + if (NotifyPayload == NULL) { + FreePool (Notify); + return NULL; + } + + NotifyPayload->PayloadType = IKEV2_PAYLOAD_TYPE_NOTIFY; + NotifyPayload->PayloadBuf = (UINT8 *) Notify; + NotifyPayload->PayloadSize = NotifyPayloadLen; + return NotifyPayload; +} + +/** + Generate the Delete payload. + + Since the structure of Delete payload which defined in RFC 4306 is simple, + there is no internal data structure for Delete payload. This function generate + Delete payload defined in RFC 4306, but all the fields in this payload are still + in host order and need call Ikev2EncodePayload() to convert those fields from + the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used of Delete payload generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] SpiSize Size of the SPI in SPI size field of the Delete Payload. + @param[in] SpiNum Number of SPI in NumofSPIs field of the Delete Payload. + @param[in] SpiBuf Pointer to buffer contains the SPI value. + + @retval a Pointer of IKE Delete Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateDeletePayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 SpiSize, + IN UINT16 SpiNum, + IN UINT8 *SpiBuf + + ) +{ + IKE_PAYLOAD *DelPayload; + IKEV2_DELETE *Del; + UINT16 SpiBufSize; + UINT16 DelPayloadLen; + + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Protocol ID ! SPI Size ! # of SPIs ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Security Parameter Index(es) (SPI) ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + SpiBufSize = (UINT16) (SpiSize * SpiNum); + if (SpiBufSize != 0 && SpiBuf == NULL) { + return NULL; + } + + DelPayloadLen = (UINT16) (sizeof (IKEV2_DELETE) + SpiBufSize); + + Del = AllocateZeroPool (DelPayloadLen); + if (Del == NULL) { + return NULL; + } + + // + // Set Delete Payload's Generic Header + // + Del->Header.NextPayload = NextPayload; + Del->Header.PayloadLength = DelPayloadLen; + Del->NumSpis = SpiNum; + Del->SpiSize = SpiSize; + + if (SpiSize == 4) { + // + // TODO: should consider the AH if needs to support. + // + Del->ProtocolId = IPSEC_PROTO_IPSEC_ESP; + } else { + Del->ProtocolId = IPSEC_PROTO_ISAKMP; + } + + // + // Set Del Payload's Idntification Data + // + CopyMem (Del + 1, SpiBuf, SpiBufSize); + DelPayload = IkePayloadAlloc (); + if (DelPayload == NULL) { + FreePool (Del); + return NULL; + } + + DelPayload->PayloadType = IKEV2_PAYLOAD_TYPE_DELETE; + DelPayload->PayloadBuf = (UINT8 *) Del; + DelPayload->PayloadSize = DelPayloadLen; + return DelPayload; +} + +/** + Generate the Configuration payload. + + This function generate configuration payload defined in RFC 4306, but all the + fields in this payload are still in host order and need call Ikev2EncodePayload() + to convert those fields from the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used for Delete payload + generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] CfgType The attribute type in the Configuration attribute. + + @retval Pointer to IKE CP Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCpPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 CfgType + ) +{ + IKE_PAYLOAD *CpPayload; + IKEV2_CFG *Cfg; + UINT16 PayloadLen; + IKEV2_CFG_ATTRIBUTES *CfgAttributes; + + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! CFG Type ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Configuration Attributes ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + PayloadLen = (UINT16) (sizeof (IKEV2_CFG) + sizeof (IKEV2_CFG_ATTRIBUTES)); + Cfg = (IKEV2_CFG *) AllocateZeroPool (PayloadLen); + + if (Cfg == NULL) { + return NULL; + } + + CfgAttributes = (IKEV2_CFG_ATTRIBUTES *)((UINT8 *)Cfg + sizeof (IKEV2_CFG)); + + // + // Only generate the configuration payload with an empty INTERNAL_IP4_ADDRESS + // or INTERNAL_IP6_ADDRESS. + // + + Cfg->Header.NextPayload = NextPayload; + Cfg->Header.PayloadLength = PayloadLen; + Cfg->CfgType = IKEV2_CFG_TYPE_REQUEST; + + CfgAttributes->AttritType = CfgType; + CfgAttributes->ValueLength = 0; + + CpPayload = IkePayloadAlloc (); + if (CpPayload == NULL) { + if (Cfg != NULL) { + FreePool (Cfg); + } + return NULL; + } + + CpPayload->PayloadType = IKEV2_PAYLOAD_TYPE_CP; + CpPayload->PayloadBuf = (UINT8 *) Cfg; + CpPayload->PayloadSize = PayloadLen; + return CpPayload; +} + +/** + Parser the Notify Cookie payload. + + This function parses the Notify Cookie payload.If the Notify ProtocolId is not + IPSEC_PROTO_ISAKMP or if the SpiSize is not zero or if the MessageType is not + the COOKIE, return EFI_INVALID_PARAMETER. + + @param[in] IkeNCookie Pointer to the IKE_PAYLOAD which contians the + Notify Cookie payload. + the Notify payload. + @param[in, out] IkeSaSession Pointer to the relevant IKE SA Session. + + @retval EFI_SUCCESS The Notify Cookie Payload is valid. + @retval EFI_INVALID_PARAMETER The Notify Cookie Payload is invalid. + @retval EFI_OUT_OF_RESOURCE The required resource can't be allocated. + +**/ +EFI_STATUS +Ikev2ParserNotifyCookiePayload ( + IN IKE_PAYLOAD *IkeNCookie, + IN OUT IKEV2_SA_SESSION *IkeSaSession + ) +{ + IKEV2_NOTIFY *NotifyPayload; + UINTN NotifyDataSize; + + NotifyPayload = (IKEV2_NOTIFY *)IkeNCookie->PayloadBuf; + + if ((NotifyPayload->ProtocolId != IPSEC_PROTO_ISAKMP) || + (NotifyPayload->SpiSize != 0) || + (NotifyPayload->MessageType != IKEV2_NOTIFICATION_COOKIE) + ) { + return EFI_INVALID_PARAMETER; + } + + NotifyDataSize = NotifyPayload->Header.PayloadLength - sizeof (IKEV2_NOTIFY); + IkeSaSession->NCookie = AllocateZeroPool (NotifyDataSize); + if (IkeSaSession->NCookie == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IkeSaSession->NCookieSize = NotifyDataSize; + + CopyMem ( + IkeSaSession->NCookie, + (UINT8 *)NotifyPayload + sizeof (IKEV2_NOTIFY), + NotifyDataSize + ); + + return EFI_SUCCESS; +} + + +/** + Generate the Certificate payload or Certificate Request Payload. + + Since the Certificate Payload structure is same with Certificate Request Payload, + the only difference is that one contains the Certificate Data, other contains + the acceptable certificateion CA. This function generate Certificate payload + or Certificate Request Payload defined in RFC 4306, but all the fields + in the payload are still in host order and need call Ikev2EncodePayload() + to convert those fields from the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used of Delete payload + generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] Certificate Pointer of buffer contains the certification data. + @param[in] CertificateLen The length of Certificate in byte. + @param[in] EncodeType Specified the Certificate Encodeing which is defined + in RFC 4306. + @param[in] IsRequest To indicate create Certificate Payload or Certificate + Request Payload. If it is TURE, create Certificate + Request Payload. Otherwise, create Certificate Payload. + + @retval a Pointer to IKE Payload whose payload buffer containing the Certificate + payload or Certificated Request payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCertificatePayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 *Certificate, + IN UINTN CertificateLen, + IN UINT8 EncodeType, + IN BOOLEAN IsRequest + ) +{ + IKE_PAYLOAD *CertPayload; + IKEV2_CERT *Cert; + UINT16 PayloadLen; + UINT8 *PublicKey; + UINTN PublicKeyLen; + HASH_DATA_FRAGMENT Fragment[1]; + UINT8 *HashData; + UINTN HashDataSize; + EFI_STATUS Status; + + // + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Cert Encoding ! ! + // +-+-+-+-+-+-+-+-+ ! + // ~ Certificate Data/Authority ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + Status = EFI_SUCCESS; + PublicKey = NULL; + PublicKeyLen = 0; + + if (!IsRequest) { + PayloadLen = (UINT16) (sizeof (IKEV2_CERT) + CertificateLen); + } else { + // + // SHA1 Hash length is 20. + // + PayloadLen = (UINT16) (sizeof (IKEV2_CERT) + 20); + } + + Cert = AllocateZeroPool (PayloadLen); + if (Cert == NULL) { + return NULL; + } + + // + // Generate Certificate Payload or Certificate Request Payload. + // + Cert->Header.NextPayload = NextPayload; + Cert->Header.PayloadLength = PayloadLen; + Cert->CertEncoding = EncodeType; + if (!IsRequest) { + CopyMem ( + ((UINT8 *)Cert) + sizeof (IKEV2_CERT), + Certificate, + CertificateLen + ); + } else { + Status = IpSecCryptoIoGetPublicKeyFromCert ( + Certificate, + CertificateLen, + &PublicKey, + &PublicKeyLen + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Fragment[0].Data = PublicKey; + Fragment[0].DataSize = PublicKeyLen; + HashDataSize = IpSecGetHmacDigestLength (IKE_AALG_SHA1HMAC); + HashData = AllocateZeroPool (HashDataSize); + if (HashData == NULL) { + goto ON_EXIT; + } + + Status = IpSecCryptoIoHash ( + IKE_AALG_SHA1HMAC, + Fragment, + 1, + HashData, + HashDataSize + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + CopyMem ( + ((UINT8 *)Cert) + sizeof (IKEV2_CERT), + HashData, + HashDataSize + ); + } + + CertPayload = IkePayloadAlloc (); + if (CertPayload == NULL) { + goto ON_EXIT; + } + + if (!IsRequest) { + CertPayload->PayloadType = IKEV2_PAYLOAD_TYPE_CERT; + } else { + CertPayload->PayloadType = IKEV2_PAYLOAD_TYPE_CERTREQ; + } + + CertPayload->PayloadBuf = (UINT8 *) Cert; + CertPayload->PayloadSize = PayloadLen; + return CertPayload; + +ON_EXIT: + if (Cert != NULL) { + FreePool (Cert); + } + if (PublicKey != NULL) { + FreePool (PublicKey); + } + return NULL; +} + +/** + Remove and free all IkePayloads in the specified IkePacket. + + @param[in] IkePacket The pointer of IKE_PACKET. + +**/ +VOID +ClearAllPayloads ( + IN IKE_PACKET *IkePacket + ) +{ + LIST_ENTRY *PayloadEntry; + IKE_PAYLOAD *IkePayload; + // + // remove all payloads from list and free each payload. + // + while (!IsListEmpty (&IkePacket->PayloadList)) { + PayloadEntry = IkePacket->PayloadList.ForwardLink; + IkePayload = IKE_PAYLOAD_BY_PACKET (PayloadEntry); + IKE_PACKET_REMOVE_PAYLOAD (IkePacket, IkePayload); + IkePayloadFree (IkePayload); + } +} + +/** + Transfer the intrnal data structure IKEV2_SA_DATA to IKEV2_SA structure defined in RFC. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the SA Session. + @param[in] SaData Pointer to IKEV2_SA_DATA to be transfered. + + @retval return the pointer of IKEV2_SA. + +**/ +IKEV2_SA* +Ikev2EncodeSa ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN IKEV2_SA_DATA *SaData + ) +{ + IKEV2_SA *Sa; + UINTN SaSize; + IKEV2_PROPOSAL_DATA *ProposalData; + IKEV2_TRANSFORM_DATA *TransformData; + UINTN TotalTransforms; + UINTN SaAttrsSize; + UINTN TransformsSize; + UINTN TransformSize; + UINTN ProposalsSize; + UINTN ProposalSize; + UINTN ProposalIndex; + UINTN TransformIndex; + IKE_SA_ATTRIBUTE *SaAttribute; + IKEV2_PROPOSAL *Proposal; + IKEV2_TRANSFORM *Transform; + + // + // Transform IKE_SA_DATA structure to IKE_SA Payload. + // Header length is host order. + // The returned IKE_SA struct should be freed by caller. + // + TotalTransforms = 0; + // + // Calculate the Proposal numbers and Transform numbers. + // + for (ProposalIndex = 0; ProposalIndex < SaData->NumProposals; ProposalIndex++) { + + ProposalData = (IKEV2_PROPOSAL_DATA *) (SaData + 1) + ProposalIndex; + TotalTransforms += ProposalData->NumTransforms; + + } + SaSize = sizeof (IKEV2_SA) + + SaData->NumProposals * sizeof (IKEV2_PROPOSAL) + + TotalTransforms * (sizeof (IKEV2_TRANSFORM) + MAX_SA_ATTRS_SIZE); + // + // Allocate buffer for IKE_SA. + // + Sa = AllocateZeroPool (SaSize); + if (Sa == NULL) { + return NULL; + } + + CopyMem (Sa, SaData, sizeof (IKEV2_SA)); + Sa->Header.PayloadLength = (UINT16) sizeof (IKEV2_SA); + ProposalsSize = 0; + Proposal = (IKEV2_PROPOSAL *) (Sa + 1); + + // + // Set IKE_PROPOSAL + // + ProposalData = (IKEV2_PROPOSAL_DATA *) (SaData + 1); + for (ProposalIndex = 0; ProposalIndex < SaData->NumProposals; ProposalIndex++) { + Proposal->ProposalIndex = ProposalData->ProposalIndex; + Proposal->ProtocolId = ProposalData->ProtocolId; + Proposal->NumTransforms = ProposalData->NumTransforms; + + if (ProposalData->Spi == 0) { + Proposal->SpiSize = 0; + } else { + Proposal->SpiSize = 4; + *(UINT32 *) (Proposal + 1) = HTONL (*((UINT32*)ProposalData->Spi)); + } + + TransformsSize = 0; + Transform = (IKEV2_TRANSFORM *) ((UINT8 *) (Proposal + 1) + Proposal->SpiSize); + + // + // Set IKE_TRANSFORM + // + for (TransformIndex = 0; TransformIndex < ProposalData->NumTransforms; TransformIndex++) { + TransformData = (IKEV2_TRANSFORM_DATA *) (ProposalData + 1) + TransformIndex; + Transform->TransformType = TransformData->TransformType; + Transform->TransformId = HTONS (TransformData->TransformId); + SaAttrsSize = 0; + + // + // If the Encryption Algorithm is variable key length set the key length in attribute. + // Note that only a single attribute type (Key Length) is defined and it is fixed length. + // + if (Transform->TransformType == IKEV2_TRANSFORM_TYPE_ENCR && TransformData->Attribute.Attr.AttrValue != 0) { + SaAttribute = (IKE_SA_ATTRIBUTE *) (Transform + 1); + SaAttribute->AttrType = HTONS (IKEV2_ATTRIBUTE_TYPE_KEYLEN | SA_ATTR_FORMAT_BIT); + SaAttribute->Attr.AttrValue = HTONS (TransformData->Attribute.Attr.AttrValue); + SaAttrsSize = sizeof (IKE_SA_ATTRIBUTE); + } + + // + // If the Integrity Algorithm is variable key length set the key length in attribute. + // + if (Transform->TransformType == IKEV2_TRANSFORM_TYPE_INTEG && TransformData->Attribute.Attr.AttrValue != 0) { + SaAttribute = (IKE_SA_ATTRIBUTE *) (Transform + 1); + SaAttribute->AttrType = HTONS (IKEV2_ATTRIBUTE_TYPE_KEYLEN | SA_ATTR_FORMAT_BIT); + SaAttribute->Attr.AttrValue = HTONS (TransformData->Attribute.Attr.AttrValue); + SaAttrsSize = sizeof (IKE_SA_ATTRIBUTE); + } + + TransformSize = sizeof (IKEV2_TRANSFORM) + SaAttrsSize; + TransformsSize += TransformSize; + + Transform->Header.NextPayload = IKE_TRANSFORM_NEXT_PAYLOAD_MORE; + Transform->Header.PayloadLength = HTONS ((UINT16)TransformSize); + + if (TransformIndex == ((UINT32)ProposalData->NumTransforms - 1)) { + Transform->Header.NextPayload = IKE_TRANSFORM_NEXT_PAYLOAD_NONE; + } + + Transform = (IKEV2_TRANSFORM *)((UINT8 *) Transform + TransformSize); + } + + // + // Set Proposal's Generic Header. + // + ProposalSize = sizeof (IKEV2_PROPOSAL) + Proposal->SpiSize + TransformsSize; + ProposalsSize += ProposalSize; + Proposal->Header.NextPayload = IKE_PROPOSAL_NEXT_PAYLOAD_MORE; + Proposal->Header.PayloadLength = HTONS ((UINT16)ProposalSize); + + if (ProposalIndex == (UINTN)(SaData->NumProposals - 1)) { + Proposal->Header.NextPayload = IKE_PROPOSAL_NEXT_PAYLOAD_NONE; + } + + // + // Point to next Proposal Payload + // + Proposal = (IKEV2_PROPOSAL *) ((UINT8 *) Proposal + ProposalSize); + ProposalData = (IKEV2_PROPOSAL_DATA *)(((UINT8 *)ProposalData) + sizeof (IKEV2_PROPOSAL_DATA) + (TransformIndex * sizeof (IKEV2_TRANSFORM_DATA))); + } + // + // Set SA's Generic Header. + // + Sa->Header.PayloadLength = (UINT16) (Sa->Header.PayloadLength + ProposalsSize); + return Sa; +} + +/** + Decode SA payload. + + This function converts the received SA payload to internal data structure. + + @param[in] SessionCommon Pointer to IKE Common Session used to decode the SA + Payload. + @param[in] Sa Pointer to SA Payload + + @return a Pointer to internal data structure for SA payload. + +**/ +IKEV2_SA_DATA * +Ikev2DecodeSa ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN IKEV2_SA *Sa + ) +{ + IKEV2_SA_DATA *SaData; + EFI_STATUS Status; + IKEV2_PROPOSAL *Proposal; + IKEV2_TRANSFORM *Transform; + UINTN TotalProposals; + UINTN TotalTransforms; + UINTN ProposalNextPayloadSum; + UINTN ProposalIndex; + UINTN TransformIndex; + UINTN SaRemaining; + UINT16 ProposalSize; + UINTN ProposalRemaining; + UINT16 TransformSize; + UINTN SaAttrRemaining; + IKE_SA_ATTRIBUTE *SaAttribute; + IKEV2_PROPOSAL_DATA *ProposalData; + IKEV2_TRANSFORM_DATA *TransformData; + UINT8 *Spi; + + // + // Transfrom from IKE_SA payload to IKE_SA_DATA structure. + // Header length NTOH is already done + // The returned IKE_SA_DATA should be freed by caller + // + SaData = NULL; + Status = EFI_SUCCESS; + + // + // First round sanity check and size calculae + // + TotalProposals = 0; + TotalTransforms = 0; + ProposalNextPayloadSum = 0; + SaRemaining = Sa->Header.PayloadLength - sizeof (IKEV2_SA);// Point to current position in SA + Proposal = (IKEV2_PROPOSAL *)((IKEV2_SA *)(Sa)+1); + + // + // Calculate the number of Proposal payload and the total numbers of + // Transforms payload (the transforms in all proposal payload). + // + while (SaRemaining > sizeof (IKEV2_PROPOSAL)) { + ProposalSize = NTOHS (Proposal->Header.PayloadLength); + if (SaRemaining < ProposalSize) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + if (Proposal->SpiSize != 0 && Proposal->SpiSize != 4) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + TotalProposals++; + TotalTransforms += Proposal->NumTransforms; + SaRemaining -= ProposalSize; + ProposalNextPayloadSum += Proposal->Header.NextPayload; + Proposal = IKEV2_NEXT_PROPOSAL_WITH_SIZE (Proposal, ProposalSize); + } + + // + // Check the proposal number. + // The proposal Substructure, the NextPayLoad field indicates : 0 (last) or 2 (more) + // which Specifies whether this is the last Proposal Substructure in the SA. + // Here suming all Proposal NextPayLoad field to check the proposal number is correct + // or not. + // + if (TotalProposals == 0 || + (TotalProposals - 1) * IKE_PROPOSAL_NEXT_PAYLOAD_MORE != ProposalNextPayloadSum + ) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Second round sanity check and decode. Transform the SA payload into + // a IKE_SA_DATA structure. + // + SaData = (IKEV2_SA_DATA *) AllocateZeroPool ( + sizeof (IKEV2_SA_DATA) + + TotalProposals * sizeof (IKEV2_PROPOSAL_DATA) + + TotalTransforms * sizeof (IKEV2_TRANSFORM_DATA) + ); + if (SaData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem (SaData, Sa, sizeof (IKEV2_SA)); + SaData->NumProposals = TotalProposals; + ProposalData = (IKEV2_PROPOSAL_DATA *) (SaData + 1); + + // + // Proposal Payload + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload ! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Proposal # ! Protocol-Id ! SPI Size !# of Transforms! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! SPI (variable) ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + for (ProposalIndex = 0, Proposal = IKEV2_SA_FIRST_PROPOSAL (Sa); + ProposalIndex < TotalProposals; + ProposalIndex++ + ) { + + // + // TODO: check ProposalId + // + ProposalData->ProposalIndex = Proposal->ProposalIndex; + ProposalData->ProtocolId = Proposal->ProtocolId; + if (Proposal->SpiSize == 0) { + ProposalData->Spi = 0; + } else { + // + // SpiSize == 4 + // + Spi = AllocateZeroPool (Proposal->SpiSize); + if (Spi == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem (Spi, (UINT32 *) (Proposal + 1), Proposal->SpiSize); + *((UINT32*) Spi) = NTOHL (*((UINT32*) Spi)); + ProposalData->Spi = Spi; + } + + ProposalData->NumTransforms = Proposal->NumTransforms; + ProposalSize = NTOHS (Proposal->Header.PayloadLength); + ProposalRemaining = ProposalSize; + // + // Transform Payload + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload ! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // !Transform Type ! RESERVED ! Transform ID ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ SA Attributes ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + Transform = IKEV2_PROPOSAL_FIRST_TRANSFORM (Proposal); + for (TransformIndex = 0; TransformIndex < Proposal->NumTransforms; TransformIndex++) { + + // + // Transfer the IKEV2_TRANSFORM structure into internal IKEV2_TRANSFORM_DATA struture. + // + TransformData = (IKEV2_TRANSFORM_DATA *) (ProposalData + 1) + TransformIndex; + TransformData->TransformId = NTOHS (Transform->TransformId); + TransformData->TransformType = Transform->TransformType; + TransformSize = NTOHS (Transform->Header.PayloadLength); + // + // Check the Proposal Data is correct. + // + if (ProposalRemaining < TransformSize) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Check if the Transform payload includes Attribution. + // + SaAttrRemaining = TransformSize - sizeof (IKEV2_TRANSFORM); + + // + // According to RFC 4603, currently only the Key length attribute type is + // supported. For each Transform, there is only one attributeion. + // + if (SaAttrRemaining > 0) { + if (SaAttrRemaining != sizeof (IKE_SA_ATTRIBUTE)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + SaAttribute = (IKE_SA_ATTRIBUTE *) ((IKEV2_TRANSFORM *)(Transform) + 1); + TransformData->Attribute.AttrType = (UINT16)((NTOHS (SaAttribute->AttrType)) & ~SA_ATTR_FORMAT_BIT); + TransformData->Attribute.Attr.AttrValue = NTOHS (SaAttribute->Attr.AttrValue); + + // + // Currently, only supports the Key Length Attribution. + // + if (TransformData->Attribute.AttrType != IKEV2_ATTRIBUTE_TYPE_KEYLEN) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + } + + // + // Move to next Transform + // + Transform = IKEV2_NEXT_TRANSFORM_WITH_SIZE (Transform, TransformSize); + } + Proposal = IKEV2_NEXT_PROPOSAL_WITH_SIZE (Proposal, ProposalSize); + ProposalData = (IKEV2_PROPOSAL_DATA *) ((UINT8 *)(ProposalData + 1) + + ProposalData->NumTransforms * + sizeof (IKEV2_TRANSFORM_DATA)); + } + +Exit: + if (EFI_ERROR (Status) && SaData != NULL) { + FreePool (SaData); + SaData = NULL; + } + return SaData; +} + +/** + General interface of payload encoding. + + This function encodes the internal data structure into payload which + is defined in RFC 4306. The IkePayload->PayloadBuf is used to store both the input + payload and converted payload. Only the SA payload use the interal structure + to store the attribute. Other payload use structure which is same with the RFC + defined, for this kind payloads just do host order to network order change of + some fields. + + @param[in] SessionCommon Pointer to IKE Session Common used to encode the payload. + @param[in, out] IkePayload Pointer to IKE payload to be encoded as input, and + store the encoded result as output. + + @retval EFI_INVALID_PARAMETER Meet error when encoding the SA payload. + @retval EFI_SUCCESS Encoded successfully. + +**/ +EFI_STATUS +Ikev2EncodePayload ( + IN UINT8 *SessionCommon, + IN OUT IKE_PAYLOAD *IkePayload + ) +{ + IKEV2_SA_DATA *SaData; + IKEV2_SA *SaPayload; + IKEV2_COMMON_PAYLOAD_HEADER *PayloadHdr; + IKEV2_NOTIFY *NotifyPayload; + IKEV2_DELETE *DeletePayload; + IKEV2_KEY_EXCHANGE *KeyPayload; + IKEV2_TS *TsPayload; + IKEV2_CFG_ATTRIBUTES *CfgAttribute; + UINT8 *TsBuffer; + UINT8 Index; + TRAFFIC_SELECTOR *TrafficSelector; + + // + // Transform the Internal IKE structure to IKE payload. + // Only the SA payload use the interal structure to store the attribute. + // Other payload use structure which same with the RFC defined, so there is + // no need to tranform them to IKE payload. + // + switch (IkePayload->PayloadType) { + case IKEV2_PAYLOAD_TYPE_SA: + // + // Transform IKE_SA_DATA to IK_SA payload + // + SaData = (IKEV2_SA_DATA *) IkePayload->PayloadBuf; + SaPayload = Ikev2EncodeSa ((IKEV2_SESSION_COMMON *) SessionCommon, SaData); + + if (SaPayload == NULL) { + return EFI_INVALID_PARAMETER; + } + if (!IkePayload->IsPayloadBufExt) { + FreePool (IkePayload->PayloadBuf); + } + IkePayload->PayloadBuf = (UINT8 *) SaPayload; + IkePayload->IsPayloadBufExt = FALSE; + break; + + case IKEV2_PAYLOAD_TYPE_NOTIFY: + NotifyPayload = (IKEV2_NOTIFY *) IkePayload->PayloadBuf; + NotifyPayload->MessageType = HTONS (NotifyPayload->MessageType); + break; + + case IKEV2_PAYLOAD_TYPE_DELETE: + DeletePayload = (IKEV2_DELETE *) IkePayload->PayloadBuf; + DeletePayload->NumSpis = HTONS (DeletePayload->NumSpis); + break; + + case IKEV2_PAYLOAD_TYPE_KE: + KeyPayload = (IKEV2_KEY_EXCHANGE *) IkePayload->PayloadBuf; + KeyPayload->DhGroup = HTONS (KeyPayload->DhGroup); + break; + + case IKEV2_PAYLOAD_TYPE_TS_INIT: + case IKEV2_PAYLOAD_TYPE_TS_RSP: + TsPayload = (IKEV2_TS *) IkePayload->PayloadBuf; + TsBuffer = IkePayload->PayloadBuf + sizeof (IKEV2_TS); + + for (Index = 0; Index < TsPayload->TSNumbers; Index++) { + TrafficSelector = (TRAFFIC_SELECTOR *) TsBuffer; + TsBuffer = TsBuffer + TrafficSelector->SelecorLen; + // + // Host order to network order + // + TrafficSelector->SelecorLen = HTONS (TrafficSelector->SelecorLen); + TrafficSelector->StartPort = HTONS (TrafficSelector->StartPort); + TrafficSelector->EndPort = HTONS (TrafficSelector->EndPort); + + } + + break; + + case IKEV2_PAYLOAD_TYPE_CP: + CfgAttribute = (IKEV2_CFG_ATTRIBUTES *)(((IKEV2_CFG *) IkePayload->PayloadBuf) + 1); + CfgAttribute->AttritType = HTONS (CfgAttribute->AttritType); + CfgAttribute->ValueLength = HTONS (CfgAttribute->ValueLength); + + case IKEV2_PAYLOAD_TYPE_ID_INIT: + case IKEV2_PAYLOAD_TYPE_ID_RSP: + case IKEV2_PAYLOAD_TYPE_AUTH: + default: + break; + } + + PayloadHdr = (IKEV2_COMMON_PAYLOAD_HEADER *) IkePayload->PayloadBuf; + IkePayload->PayloadSize = PayloadHdr->PayloadLength; + PayloadHdr->PayloadLength = HTONS (PayloadHdr->PayloadLength); + IKEV2_DUMP_PAYLOAD (IkePayload); + return EFI_SUCCESS; +} + +/** + The general interface for decoding Payload. + + This function converts the received Payload into internal structure. + + @param[in] SessionCommon Pointer to IKE Session Common used for decoding. + @param[in, out] IkePayload Pointer to IKE payload to be decoded as input, and + store the decoded result as output. + + @retval EFI_INVALID_PARAMETER Meet error when decoding the SA payload. + @retval EFI_SUCCESS Decoded successfully. + +**/ +EFI_STATUS +Ikev2DecodePayload ( + IN UINT8 *SessionCommon, + IN OUT IKE_PAYLOAD *IkePayload + ) +{ + IKEV2_COMMON_PAYLOAD_HEADER *PayloadHdr; + UINT16 PayloadSize; + UINT8 PayloadType; + IKEV2_SA_DATA *SaData; + EFI_STATUS Status; + IKEV2_NOTIFY *NotifyPayload; + IKEV2_DELETE *DeletePayload; + UINT16 TsTotalSize; + TRAFFIC_SELECTOR *TsSelector; + IKEV2_TS *TsPayload; + IKEV2_KEY_EXCHANGE *KeyPayload; + IKEV2_CFG_ATTRIBUTES *CfgAttribute; + UINT8 Index; + + // + // Transform the IKE payload to Internal IKE structure. + // Only the SA payload and Hash Payload use the interal + // structure to store the attribute. Other payloads use + // structure which is same with the definitions in RFC, + // so there is no need to tranform them to internal IKE + // structure. + // + Status = EFI_SUCCESS; + PayloadSize = (UINT16) IkePayload->PayloadSize; + PayloadType = IkePayload->PayloadType; + PayloadHdr = (IKEV2_COMMON_PAYLOAD_HEADER *) IkePayload->PayloadBuf; + // + // The PayloadSize is the size of whole payload. + // Replace HTONS operation to assignment statements, since the result is same. + // + PayloadHdr->PayloadLength = PayloadSize; + + IKEV2_DUMP_PAYLOAD (IkePayload); + switch (PayloadType) { + case IKEV2_PAYLOAD_TYPE_SA: + if (PayloadSize < sizeof (IKEV2_SA)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + SaData = Ikev2DecodeSa ((IKEV2_SESSION_COMMON *) SessionCommon, (IKEV2_SA *) PayloadHdr); + if (SaData == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + if (!IkePayload->IsPayloadBufExt) { + FreePool (IkePayload->PayloadBuf); + } + + IkePayload->PayloadBuf = (UINT8 *) SaData; + IkePayload->IsPayloadBufExt = FALSE; + break; + + case IKEV2_PAYLOAD_TYPE_ID_INIT: + case IKEV2_PAYLOAD_TYPE_ID_RSP : + if (PayloadSize < sizeof (IKEV2_ID)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + break; + + case IKEV2_PAYLOAD_TYPE_NOTIFY: + if (PayloadSize < sizeof (IKEV2_NOTIFY)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + NotifyPayload = (IKEV2_NOTIFY *) PayloadHdr; + NotifyPayload->MessageType = NTOHS (NotifyPayload->MessageType); + break; + + case IKEV2_PAYLOAD_TYPE_DELETE: + if (PayloadSize < sizeof (IKEV2_DELETE)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + DeletePayload = (IKEV2_DELETE *) PayloadHdr; + DeletePayload->NumSpis = NTOHS (DeletePayload->NumSpis); + break; + + case IKEV2_PAYLOAD_TYPE_AUTH: + if (PayloadSize < sizeof (IKEV2_AUTH)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + break; + + case IKEV2_PAYLOAD_TYPE_KE: + KeyPayload = (IKEV2_KEY_EXCHANGE *) IkePayload->PayloadBuf; + KeyPayload->DhGroup = HTONS (KeyPayload->DhGroup); + break; + + case IKEV2_PAYLOAD_TYPE_TS_INIT: + case IKEV2_PAYLOAD_TYPE_TS_RSP : + TsTotalSize = 0; + if (PayloadSize < sizeof (IKEV2_TS)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + // + // Parse each traffic selector and transfer network-order to host-order + // + TsPayload = (IKEV2_TS *) IkePayload->PayloadBuf; + TsSelector = (TRAFFIC_SELECTOR *) (IkePayload->PayloadBuf + sizeof (IKEV2_TS)); + + for (Index = 0; Index < TsPayload->TSNumbers; Index++) { + TsSelector->SelecorLen = NTOHS (TsSelector->SelecorLen); + TsSelector->StartPort = NTOHS (TsSelector->StartPort); + TsSelector->EndPort = NTOHS (TsSelector->EndPort); + + TsTotalSize = (UINT16) (TsTotalSize + TsSelector->SelecorLen); + TsSelector = (TRAFFIC_SELECTOR *) ((UINT8 *) TsSelector + TsSelector->SelecorLen); + } + // + // Check if the total size of Traffic Selectors is correct. + // + if (TsTotalSize != PayloadSize - sizeof(IKEV2_TS)) { + Status = EFI_INVALID_PARAMETER; + } + + case IKEV2_PAYLOAD_TYPE_CP: + CfgAttribute = (IKEV2_CFG_ATTRIBUTES *)(((IKEV2_CFG *) IkePayload->PayloadBuf) + 1); + CfgAttribute->AttritType = NTOHS (CfgAttribute->AttritType); + CfgAttribute->ValueLength = NTOHS (CfgAttribute->ValueLength); + + default: + break; + } + + Exit: + return Status; +} + +/** + Decode the IKE packet. + + This function first decrypts the IKE packet if needed , then separates the whole + IKE packet from the IkePacket->PayloadBuf into IkePacket payload list. + + @param[in] SessionCommon Pointer to IKEV1_SESSION_COMMON containing + some parameter used by IKE packet decoding. + @param[in, out] IkePacket The IKE Packet to be decoded on input, and + the decoded result on return. + @param[in] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supported. + + @retval EFI_SUCCESS The IKE packet is decoded successfully. + @retval Otherwise The IKE packet decoding is failed. + +**/ +EFI_STATUS +Ikev2DecodePacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN UINTN IkeType + ) +{ + EFI_STATUS Status; + IKEV2_COMMON_PAYLOAD_HEADER *PayloadHdr; + UINT8 PayloadType; + UINTN RemainBytes; + UINT16 PayloadSize; + IKE_PAYLOAD *IkePayload; + IKE_HEADER *IkeHeader; + IKEV2_SA_SESSION *IkeSaSession; + + IkeHeader = NULL; + + // + // Check if the IkePacket need decrypt. + // + if (SessionCommon->State >= IkeStateAuth) { + Status = Ikev2DecryptPacket (SessionCommon, IkePacket, IkeType); + if (EFI_ERROR (Status)) { + return Status; + } + } + + Status = EFI_SUCCESS; + + // + // If the IkePacket doesn't contain any payload return invalid parameter. + // + if (IkePacket->Header->NextPayload == IKEV2_PAYLOAD_TYPE_NONE) { + if ((SessionCommon->State >= IkeStateAuth) && + (IkePacket->Header->ExchangeType == IKEV2_EXCHANGE_TYPE_INFO) + ) { + // + // If it is Liveness check, there will be no payload load in the encrypt payload. + // + Status = EFI_SUCCESS; + } else { + Status = EFI_INVALID_PARAMETER; + } + } + + // + // If the PayloadTotalSize < Header length, return invalid parameter. + // + RemainBytes = IkePacket->PayloadTotalSize; + if (RemainBytes < sizeof (IKEV2_COMMON_PAYLOAD_HEADER)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // If the packet is first or second message, store whole message in + // IkeSa->InitiPacket or IkeSa->RespPacket for following Auth Payload + // calculate. + // + if (IkePacket->Header->ExchangeType == IKEV2_EXCHANGE_TYPE_INIT) { + IkeHeader = AllocateZeroPool (sizeof (IKE_HEADER)); + if (IkeHeader == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem (IkeHeader, IkePacket->Header, sizeof (IKE_HEADER)); + + // + // Before store the whole packet, roll back the host order to network order, + // since the header order was changed in the IkePacketFromNetbuf. + // + IkeHdrNetToHost (IkeHeader); + IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); + if (SessionCommon->IsInitiator) { + IkeSaSession->RespPacket = AllocateZeroPool (IkePacket->Header->Length); + if (IkeSaSession->RespPacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->RespPacketSize = IkePacket->Header->Length; + CopyMem (IkeSaSession->RespPacket, IkeHeader, sizeof (IKE_HEADER)); + CopyMem ( + IkeSaSession->RespPacket + sizeof (IKE_HEADER), + IkePacket->PayloadsBuf, + IkePacket->Header->Length - sizeof (IKE_HEADER) + ); + } else { + IkeSaSession->InitPacket = AllocateZeroPool (IkePacket->Header->Length); + if (IkeSaSession->InitPacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->InitPacketSize = IkePacket->Header->Length; + CopyMem (IkeSaSession->InitPacket, IkeHeader, sizeof (IKE_HEADER)); + CopyMem ( + IkeSaSession->InitPacket + sizeof (IKE_HEADER), + IkePacket->PayloadsBuf, + IkePacket->Header->Length - sizeof (IKE_HEADER) + ); + } + } + + // + // Point to the first Payload + // + PayloadHdr = (IKEV2_COMMON_PAYLOAD_HEADER *) IkePacket->PayloadsBuf; + PayloadType = IkePacket->Header->NextPayload; + + // + // Parse each payload + // + while (RemainBytes >= sizeof (IKEV2_COMMON_PAYLOAD_HEADER)) { + PayloadSize = NTOHS (PayloadHdr->PayloadLength); + + // + //Check the size of the payload is correct. + // + if (RemainBytes < PayloadSize) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // At certain states, it should save some datas before decoding. + // + if (SessionCommon->BeforeDecodePayload != NULL) { + SessionCommon->BeforeDecodePayload ( + (UINT8 *) SessionCommon, + (UINT8 *) PayloadHdr, + PayloadSize, + PayloadType + ); + } + + // + // Initial IkePayload + // + IkePayload = IkePayloadAlloc (); + if (IkePayload == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + IkePayload->PayloadType = PayloadType; + IkePayload->PayloadBuf = (UINT8 *) PayloadHdr; + IkePayload->PayloadSize = PayloadSize; + IkePayload->IsPayloadBufExt = TRUE; + + Status = Ikev2DecodePayload ((UINT8 *) SessionCommon, IkePayload); + if (EFI_ERROR (Status)) { + goto Exit; + } + + IPSEC_DUMP_BUF ("After Decoding Payload", IkePayload->PayloadBuf, IkePayload->PayloadSize); + // + // Add each payload into packet + // Notice, the IkePacket->Hdr->Lenght still recode the whole IkePacket length + // which is before the decoding. + // + IKE_PACKET_APPEND_PAYLOAD (IkePacket, IkePayload); + + RemainBytes -= PayloadSize; + PayloadType = PayloadHdr->NextPayload; + if (PayloadType == IKEV2_PAYLOAD_TYPE_NONE) { + break; + } + + PayloadHdr = (IKEV2_COMMON_PAYLOAD_HEADER *) ((UINT8 *) PayloadHdr + PayloadSize); + } + + if (PayloadType != IKEV2_PAYLOAD_TYPE_NONE) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + +Exit: + if (EFI_ERROR (Status)) { + ClearAllPayloads (IkePacket); + } + + if (IkeHeader != NULL) { + FreePool (IkeHeader); + } + return Status; +} + +/** + Encode the IKE packet. + + This function puts all Payloads into one payload then encrypt it if needed. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON containing + some parameter used during IKE packet encoding. + @param[in, out] IkePacket Pointer to IKE_PACKET to be encoded as input, + and the encoded result as output. + @param[in] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supportted. + + @retval EFI_SUCCESS Encode IKE packet successfully. + @retval Otherwise Encode IKE packet failed. + +**/ +EFI_STATUS +Ikev2EncodePacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN UINTN IkeType + ) +{ + IKE_PAYLOAD *IkePayload; + UINTN PayloadTotalSize; + LIST_ENTRY *Entry; + EFI_STATUS Status; + IKEV2_SA_SESSION *IkeSaSession; + + PayloadTotalSize = 0; + // + // Encode each payload + // + for (Entry = IkePacket->PayloadList.ForwardLink; Entry != &(IkePacket->PayloadList);) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + Entry = Entry->ForwardLink; + Status = Ikev2EncodePayload ((UINT8 *) SessionCommon, IkePayload); + if (EFI_ERROR (Status)) { + return Status; + } + + if (SessionCommon->AfterEncodePayload != NULL) { + // + // For certain states, save some payload for further calculation + // + SessionCommon->AfterEncodePayload ( + (UINT8 *) SessionCommon, + IkePayload->PayloadBuf, + IkePayload->PayloadSize, + IkePayload->PayloadType + ); + } + + PayloadTotalSize += IkePayload->PayloadSize; + } + IkePacket->PayloadTotalSize = PayloadTotalSize; + + Status = EFI_SUCCESS; + if (SessionCommon->State >= IkeStateAuth) { + // + // Encrypt all payload and transfer IKE packet header from Host order to Network order. + // + Status = Ikev2EncryptPacket (SessionCommon, IkePacket); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // Fill in the lenght into IkePacket header and transfer Host order to Network order. + // + IkePacket->Header->Length = (UINT32) (sizeof (IKE_HEADER) + IkePacket->PayloadTotalSize); + IkeHdrHostToNet (IkePacket->Header); + } + + // + // If the packet is first message, store whole message in IkeSa->InitiPacket + // for following Auth Payload calculation. + // + if (IkePacket->Header->ExchangeType == IKEV2_EXCHANGE_TYPE_INIT) { + IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); + if (SessionCommon->IsInitiator) { + IkeSaSession->InitPacketSize = IkePacket->PayloadTotalSize + sizeof (IKE_HEADER); + IkeSaSession->InitPacket = AllocateZeroPool (IkeSaSession->InitPacketSize); + if (IkeSaSession->InitPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (IkeSaSession->InitPacket, IkePacket->Header, sizeof (IKE_HEADER)); + PayloadTotalSize = 0; + for (Entry = IkePacket->PayloadList.ForwardLink; Entry != &(IkePacket->PayloadList);) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + Entry = Entry->ForwardLink; + CopyMem ( + IkeSaSession->InitPacket + sizeof (IKE_HEADER) + PayloadTotalSize, + IkePayload->PayloadBuf, + IkePayload->PayloadSize + ); + PayloadTotalSize = PayloadTotalSize + IkePayload->PayloadSize; + } + } else { + IkeSaSession->RespPacketSize = IkePacket->PayloadTotalSize + sizeof(IKE_HEADER); + IkeSaSession->RespPacket = AllocateZeroPool (IkeSaSession->RespPacketSize); + if (IkeSaSession->RespPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (IkeSaSession->RespPacket, IkePacket->Header, sizeof (IKE_HEADER)); + PayloadTotalSize = 0; + for (Entry = IkePacket->PayloadList.ForwardLink; Entry != &(IkePacket->PayloadList);) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + Entry = Entry->ForwardLink; + + CopyMem ( + IkeSaSession->RespPacket + sizeof (IKE_HEADER) + PayloadTotalSize, + IkePayload->PayloadBuf, + IkePayload->PayloadSize + ); + PayloadTotalSize = PayloadTotalSize + IkePayload->PayloadSize; + } + } + } + + return Status; +} + +/** + Decrypt IKE packet. + + This function decrypts the Encrypted IKE packet and put the result into IkePacket->PayloadBuf. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON containing + some parameter used during decrypting. + @param[in, out] IkePacket Pointer to IKE_PACKET to be decrypted as input, + and the decrypted result as output. + @param[in, out] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supportted. + + @retval EFI_INVALID_PARAMETER If the IKE packet length is zero or the + IKE packet length is not aligned with Algorithm Block Size + @retval EFI_SUCCESS Decrypt IKE packet successfully. + +**/ +EFI_STATUS +Ikev2DecryptPacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN OUT UINTN IkeType + ) +{ + UINT8 CryptBlockSize; // Encrypt Block Size + UINTN DecryptedSize; // Encrypted IKE Payload Size + UINT8 *DecryptedBuf; // Encrypted IKE Payload buffer + UINTN IntegritySize; + UINT8 *IntegrityBuffer; + UINTN IvSize; // Iv Size + UINT8 CheckSumSize; // Integrity Check Sum Size depends on intergrity Auth + UINT8 *CheckSumData; // Check Sum data + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + EFI_STATUS Status; + UINT8 PadLen; + HASH_DATA_FRAGMENT Fragments[1]; + + IvSize = 0; + IkeSaSession = NULL; + CryptBlockSize = 0; + CheckSumSize = 0; + + // + // Check if the first payload is the Encrypted payload + // + if (IkePacket->Header->NextPayload != IKEV2_PAYLOAD_TYPE_ENCRYPT) { + return EFI_ACCESS_DENIED; + } + CheckSumData = NULL; + DecryptedBuf = NULL; + IntegrityBuffer = NULL; + + // + // Get the Block Size + // + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + + CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) SessionCommon->SaParams->EncAlgId); + + CheckSumSize = (UINT8) IpSecGetIcvLength ((UINT8) SessionCommon->SaParams->IntegAlgId); + IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); + + } else if (SessionCommon->IkeSessionType == IkeSessionTypeChildSa) { + + ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); + IkeSaSession = ChildSaSession->IkeSaSession; + CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId); + CheckSumSize = (UINT8) IpSecGetIcvLength ((UINT8) IkeSaSession->SessionCommon.SaParams->IntegAlgId); + } else { + // + // The type of SA Session would either be IkeSa or ChildSa. + // + return EFI_INVALID_PARAMETER; + } + + CheckSumData = AllocateZeroPool (CheckSumSize); + if (CheckSumData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Fill in the Integrity buffer + // + IntegritySize = IkePacket->PayloadTotalSize + sizeof (IKE_HEADER); + IntegrityBuffer = AllocateZeroPool (IntegritySize); + if (IntegrityBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (IntegrityBuffer, IkePacket->Header, sizeof(IKE_HEADER)); + CopyMem (IntegrityBuffer + sizeof (IKE_HEADER), IkePacket->PayloadsBuf, IkePacket->PayloadTotalSize); + + // + // Change Host order to Network order, since the header order was changed + // in the IkePacketFromNetbuf. + // + IkeHdrHostToNet ((IKE_HEADER *)IntegrityBuffer); + + // + // Calculate the Integrity CheckSum Data + // + Fragments[0].Data = IntegrityBuffer; + Fragments[0].DataSize = IntegritySize - CheckSumSize; + + if (SessionCommon->IsInitiator) { + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId, + IkeSaSession->IkeKeys->SkArKey, + IkeSaSession->IkeKeys->SkArKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + CheckSumData, + CheckSumSize + ); + } else { + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId, + IkeSaSession->IkeKeys->SkAiKey, + IkeSaSession->IkeKeys->SkAiKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + CheckSumData, + CheckSumSize + ); + } + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Compare the Integrity CheckSum Data with the one in IkePacket + // + if (CompareMem ( + IkePacket->PayloadsBuf + IkePacket->PayloadTotalSize - CheckSumSize, + CheckSumData, + CheckSumSize + ) != 0) { + DEBUG ((DEBUG_ERROR, "Error auth verify payload\n")); + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + IvSize = CryptBlockSize; + + // + // Decrypt the payload with the key. + // + DecryptedSize = IkePacket->PayloadTotalSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER) - IvSize - CheckSumSize; + DecryptedBuf = AllocateZeroPool (DecryptedSize); + if (DecryptedBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem ( + DecryptedBuf, + IkePacket->PayloadsBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER) + IvSize, + DecryptedSize + ); + + if (SessionCommon->IsInitiator) { + Status = IpSecCryptoIoDecrypt ( + (UINT8) SessionCommon->SaParams->EncAlgId, + IkeSaSession->IkeKeys->SkErKey, + IkeSaSession->IkeKeys->SkErKeySize << 3, + IkePacket->PayloadsBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER), + DecryptedBuf, + DecryptedSize, + DecryptedBuf + ); + } else { + Status = IpSecCryptoIoDecrypt ( + (UINT8) SessionCommon->SaParams->EncAlgId, + IkeSaSession->IkeKeys->SkEiKey, + IkeSaSession->IkeKeys->SkEiKeySize << 3, + IkePacket->PayloadsBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER), + DecryptedBuf, + DecryptedSize, + DecryptedBuf + ); + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error decrypt buffer with %r\n", Status)); + goto ON_EXIT; + } + + // + // Get the Padding length + // + // + PadLen = (UINT8) (*(DecryptedBuf + DecryptedSize - sizeof (IKEV2_PAD_LEN))); + + // + // Save the next payload of encrypted payload into IkePacket->Hdr->NextPayload + // + IkePacket->Header->NextPayload = ((IKEV2_ENCRYPTED *) IkePacket->PayloadsBuf)->Header.NextPayload; + + // + // Free old IkePacket->PayloadBuf and point it to decrypted paylaod buffer. + // + FreePool (IkePacket->PayloadsBuf); + IkePacket->PayloadsBuf = DecryptedBuf; + IkePacket->PayloadTotalSize = DecryptedSize - PadLen; + + IPSEC_DUMP_BUF ("Decrypted Buffer", DecryptedBuf, DecryptedSize); + + +ON_EXIT: + if (CheckSumData != NULL) { + FreePool (CheckSumData); + } + + if (EFI_ERROR (Status) && DecryptedBuf != NULL) { + FreePool (DecryptedBuf); + } + + if (IntegrityBuffer != NULL) { + FreePool (IntegrityBuffer); + } + + return Status; +} + +/** + Encrypt IKE packet. + + This function encrypt IKE packet before sending it. The Encrypted IKE packet + is put in to IKEV2 Encrypted Payload. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the IKE packet. + @param[in, out] IkePacket Pointer to IKE packet to be encrypted. + + @retval EFI_SUCCESS Operation is successful. + @retval Others Operation is failed. + +**/ +EFI_STATUS +Ikev2EncryptPacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket + ) +{ + UINT8 CryptBlockSize; // Encrypt Block Size + UINT8 CryptBlockSizeMask; // Block Mask + UINTN EncryptedSize; // Encrypted IKE Payload Size + UINT8 *EncryptedBuf; // Encrypted IKE Payload buffer + UINT8 *EncryptPayloadBuf; // Contain whole Encrypted Payload + UINTN EncryptPayloadSize; // Total size of the Encrypted payload + UINT8 *IntegrityBuf; // Buffer to be intergity + UINT8 *IvBuffer; // Initialization Vector + UINT8 IvSize; // Iv Size + UINT8 CheckSumSize; // Integrity Check Sum Size depends on intergrity Auth + UINT8 *CheckSumData; // Check Sum data + UINTN Index; + IKE_PAYLOAD *EncryptPayload; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + EFI_STATUS Status; + LIST_ENTRY *Entry; + IKE_PAYLOAD *IkePayload; + HASH_DATA_FRAGMENT Fragments[1]; + + Status = EFI_SUCCESS; + + // + // Initial all buffers to NULL. + // + EncryptedBuf = NULL; + EncryptPayloadBuf = NULL; + IvBuffer = NULL; + CheckSumData = NULL; + IkeSaSession = NULL; + CryptBlockSize = 0; + CheckSumSize = 0; + IntegrityBuf = NULL; + // + // Get the Block Size + // + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + + CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) SessionCommon->SaParams->EncAlgId); + CheckSumSize = (UINT8) IpSecGetIcvLength ((UINT8) SessionCommon->SaParams->IntegAlgId); + IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); + + } else if (SessionCommon->IkeSessionType == IkeSessionTypeChildSa) { + + ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); + IkeSaSession = ChildSaSession->IkeSaSession; + CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId); + CheckSumSize = (UINT8) IpSecGetIcvLength ((UINT8) IkeSaSession->SessionCommon.SaParams->IntegAlgId); + } + + // + // Calcualte the EncryptPayloadSize and the PAD length + // + CryptBlockSizeMask = (UINT8) (CryptBlockSize - 1); + EncryptedSize = (IkePacket->PayloadTotalSize + sizeof (IKEV2_PAD_LEN) + CryptBlockSizeMask) & ~CryptBlockSizeMask; + EncryptedBuf = (UINT8 *) AllocateZeroPool (EncryptedSize); + if (EncryptedBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Copy all payload into EncryptedIkePayload + // + Index = 0; + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + + CopyMem (EncryptedBuf + Index, IkePayload->PayloadBuf, IkePayload->PayloadSize); + Index += IkePayload->PayloadSize; + + }; + + // + // Fill in the Pading Length + // + *(EncryptedBuf + EncryptedSize - 1) = (UINT8)(EncryptedSize - IkePacket->PayloadTotalSize - 1); + + // + // The IV size is equal with block size + // + IvSize = CryptBlockSize; + IvBuffer = (UINT8 *) AllocateZeroPool (IvSize); + if (IvBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Generate IV + // + IkeGenerateIv (IvBuffer, IvSize); + + // + // Encrypt payload buf + // + if (SessionCommon->IsInitiator) { + Status = IpSecCryptoIoEncrypt ( + (UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId, + IkeSaSession->IkeKeys->SkEiKey, + IkeSaSession->IkeKeys->SkEiKeySize << 3, + IvBuffer, + EncryptedBuf, + EncryptedSize, + EncryptedBuf + ); + } else { + Status = IpSecCryptoIoEncrypt ( + (UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId, + IkeSaSession->IkeKeys->SkErKey, + IkeSaSession->IkeKeys->SkErKeySize << 3, + IvBuffer, + EncryptedBuf, + EncryptedSize, + EncryptedBuf + ); + } + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Allocate the buffer for the whole IKE payload (Encrypted Payload). + // + EncryptPayloadSize = sizeof(IKEV2_ENCRYPTED) + IvSize + EncryptedSize + CheckSumSize; + EncryptPayloadBuf = AllocateZeroPool (EncryptPayloadSize); + if (EncryptPayloadBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Fill in Header of Encrypted Payload + // + ((IKEV2_ENCRYPTED *) EncryptPayloadBuf)->Header.NextPayload = IkePacket->Header->NextPayload; + ((IKEV2_ENCRYPTED *) EncryptPayloadBuf)->Header.PayloadLength = HTONS ((UINT16)EncryptPayloadSize); + + // + // Fill in Iv + // + CopyMem (EncryptPayloadBuf + sizeof (IKEV2_ENCRYPTED), IvBuffer, IvSize); + + // + // Fill in encrypted data + // + CopyMem (EncryptPayloadBuf + sizeof (IKEV2_ENCRYPTED) + IvSize, EncryptedBuf, EncryptedSize); + + // + // Fill in the IKE Packet header + // + IkePacket->PayloadTotalSize = EncryptPayloadSize; + IkePacket->Header->Length = (UINT32) (sizeof (IKE_HEADER) + IkePacket->PayloadTotalSize); + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ENCRYPT; + + IntegrityBuf = AllocateZeroPool (IkePacket->Header->Length); + if (IntegrityBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + IkeHdrHostToNet (IkePacket->Header); + + CopyMem (IntegrityBuf, IkePacket->Header, sizeof (IKE_HEADER)); + CopyMem (IntegrityBuf + sizeof (IKE_HEADER), EncryptPayloadBuf, EncryptPayloadSize); + + // + // Calcualte Integrity CheckSum + // + Fragments[0].Data = IntegrityBuf; + Fragments[0].DataSize = EncryptPayloadSize + sizeof (IKE_HEADER) - CheckSumSize; + + CheckSumData = AllocateZeroPool (CheckSumSize); + if (CheckSumData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + if (SessionCommon->IsInitiator) { + + IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId, + IkeSaSession->IkeKeys->SkAiKey, + IkeSaSession->IkeKeys->SkAiKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + CheckSumData, + CheckSumSize + ); + } else { + + IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId, + IkeSaSession->IkeKeys->SkArKey, + IkeSaSession->IkeKeys->SkArKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + CheckSumData, + CheckSumSize + ); + } + + // + // Copy CheckSum into Encrypted Payload + // + CopyMem (EncryptPayloadBuf + EncryptPayloadSize - CheckSumSize, CheckSumData, CheckSumSize); + + IPSEC_DUMP_BUF ("Encrypted payload buffer", EncryptPayloadBuf, EncryptPayloadSize); + IPSEC_DUMP_BUF ("Integrith CheckSum Data", CheckSumData, CheckSumSize); + + // + // Clean all payload under IkePacket->PayloadList. + // + ClearAllPayloads (IkePacket); + + // + // Create Encrypted Payload and add into IkePacket->PayloadList + // + EncryptPayload = IkePayloadAlloc (); + if (EncryptPayload == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Fill the encrypted payload into the IKE_PAYLOAD structure. + // + EncryptPayload->PayloadBuf = EncryptPayloadBuf; + EncryptPayload->PayloadSize = EncryptPayloadSize; + EncryptPayload->PayloadType = IKEV2_PAYLOAD_TYPE_ENCRYPT; + + IKE_PACKET_APPEND_PAYLOAD (IkePacket, EncryptPayload); + +ON_EXIT: + if (EncryptedBuf != NULL) { + FreePool (EncryptedBuf); + } + + if (EFI_ERROR (Status) && EncryptPayloadBuf != NULL) { + FreePool (EncryptPayloadBuf); + } + + if (IvBuffer != NULL) { + FreePool (IvBuffer); + } + + if (CheckSumData != NULL) { + FreePool (CheckSumData); + } + + if (IntegrityBuf != NULL) { + FreePool (IntegrityBuf); + } + + return Status; +} + +/** + Save some useful payloads after accepting the Packet. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the operation. + @param[in] IkePacket Pointer to received IkePacet. + @param[in] IkeType The type used to indicate it is in IkeSa or ChildSa or Info + exchange. + +**/ +VOID +Ikev2OnPacketAccepted ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN IKE_PACKET *IkePacket, + IN UINT8 IkeType + ) +{ + return; +} + +/** + + The notification function. It will be called when the related UDP_TX_TOKEN's event + is signaled. + + This function frees the Net Buffer pointed to the input Packet. + + @param[in] Packet Pointer to Net buffer containing the sending IKE packet. + @param[in] EndPoint Pointer to UDP_END_POINT containing the remote and local + address information. + @param[in] IoStatus The Status of the related UDP_TX_TOKEN. + @param[in] Context Pointer to data passed from the caller. + +**/ +VOID +EFIAPI +Ikev2OnPacketSent ( + IN NET_BUF *Packet, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + IKE_PACKET *IkePacket; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + UINT8 Value; + IPSEC_PRIVATE_DATA *Private; + EFI_STATUS Status; + + IkePacket = (IKE_PACKET *) Context; + Private = NULL; + + if (EFI_ERROR (IoStatus)) { + DEBUG ((DEBUG_ERROR, "Error send the last packet in IkeSessionTypeIkeSa with %r\n", IoStatus)); + } + + NetbufFree (Packet); + + if (IkePacket->IsDeleteInfo) { + // + // For each RemotePeerIP, there are only one IKESA. + // + IkeSaSession = Ikev2SaSessionLookup ( + &IkePacket->Private->Ikev2EstablishedList, + &IkePacket->RemotePeerIp + ); + if (IkeSaSession == NULL) { + IkePacketFree (IkePacket); + return; + } + + Private = IkePacket->Private; + if (IkePacket->Spi != 0 ) { + // + // At that time, the established Child SA still in eht ChildSaEstablishSessionList. + // And meanwhile, if the Child SA is in the the ChildSa in Delete list, + // remove it from delete list and delete it direclty. + // + ChildSaSession = Ikev2ChildSaSessionLookupBySpi ( + &IkeSaSession->ChildSaEstablishSessionList, + IkePacket->Spi + ); + if (ChildSaSession != NULL) { + Ikev2ChildSaSessionRemove ( + &IkeSaSession->DeleteSaList, + ChildSaSession->LocalPeerSpi, + IKEV2_DELET_CHILDSA_LIST + ); + + // + // Delete the Child SA. + // + Ikev2ChildSaSilentDelete ( + IkeSaSession, + IkePacket->Spi + ); + } + + } else { + // + // Delete the IKE SA + // + DEBUG ( + (DEBUG_INFO, + "\n------ deleted Packet (cookie_i, cookie_r):(0x%lx, 0x%lx)------\n", + IkeSaSession->InitiatorCookie, + IkeSaSession->ResponderCookie) + ); + + RemoveEntryList (&IkeSaSession->BySessionTable); + Ikev2SaSessionFree (IkeSaSession); + } + } + IkePacketFree (IkePacket); + + // + // when all IKE SAs were disabled by calling "IPsecConfig -disable", the IPsec status + // should be changed. + // + 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; + } + } + } +} + +/** + Send out IKEV2 packet. + + @param[in] IkeUdpService Pointer to IKE_UDP_SERVICE used to send the IKE packet. + @param[in] SessionCommon Pointer to IKEV1_SESSION_COMMON related to the IKE packet. + @param[in] IkePacket Pointer to IKE_PACKET to be sent out. + @param[in] IkeType The type of IKE to point what's kind of the IKE + packet is to be sent out. IKE_SA_TYPE, IKE_INFO_TYPE + and IKE_CHILD_TYPE are supportted. + + @retval EFI_SUCCESS The operation complete successfully. + @retval Otherwise The operation is failed. + +**/ +EFI_STATUS +Ikev2SendIkePacket ( + IN IKE_UDP_SERVICE *IkeUdpService, + IN UINT8 *SessionCommon, + IN IKE_PACKET *IkePacket, + IN UINTN IkeType + ) +{ + EFI_STATUS Status; + NET_BUF *IkePacketNetbuf; + UDP_END_POINT EndPoint; + IKEV2_SESSION_COMMON *Common; + + Common = (IKEV2_SESSION_COMMON *) SessionCommon; + + // + // Set the resend interval + // + if (Common->TimeoutInterval == 0) { + Common->TimeoutInterval = IKE_DEFAULT_TIMEOUT_INTERVAL; + } + + // + // Retransfer the packet if it is initial packet. + // + if (IkePacket->Header->Flags == IKE_HEADER_FLAGS_INIT) { + // + // Set timer for next retry, this will cancel previous timer + // + Status = gBS->SetTimer ( + Common->TimeoutEvent, + TimerRelative, + MultU64x32 (Common->TimeoutInterval, 10000) // ms->100ns + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + IKE_PACKET_REF (IkePacket); + // + // If the last sent packet is same with this round packet, the packet is resent packet. + // + if (IkePacket != Common->LastSentPacket && Common->LastSentPacket != NULL) { + IkePacketFree (Common->LastSentPacket); + } + + Common->LastSentPacket = IkePacket; + + // + // Transform IkePacke to NetBuf + // + IkePacketNetbuf = IkeNetbufFromPacket ((UINT8 *) SessionCommon, IkePacket, IkeType); + if (IkePacketNetbuf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (&EndPoint, sizeof (UDP_END_POINT)); + EndPoint.RemotePort = IKE_DEFAULT_PORT; + CopyMem (&IkePacket->RemotePeerIp, &Common->RemotePeerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&EndPoint.RemoteAddr, &Common->RemotePeerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&EndPoint.LocalAddr, &Common->LocalPeerIp, sizeof (EFI_IP_ADDRESS)); + + IPSEC_DUMP_PACKET (IkePacket, EfiIPsecOutBound, IkeUdpService->IpVersion); + + if (IkeUdpService->IpVersion == IP_VERSION_4) { + EndPoint.RemoteAddr.Addr[0] = HTONL (EndPoint.RemoteAddr.Addr[0]); + EndPoint.LocalAddr.Addr[0] = HTONL (EndPoint.LocalAddr.Addr[0]); + } + + // + // Call UDPIO to send out the IKE packet. + // + Status = UdpIoSendDatagram ( + IkeUdpService->Output, + IkePacketNetbuf, + &EndPoint, + NULL, + Ikev2OnPacketSent, + (VOID*)IkePacket + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error send packet with %r\n", Status)); + } + + return Status; +} + diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h b/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h new file mode 100644 index 0000000000..7a85792ed7 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h @@ -0,0 +1,443 @@ +/** @file + The Definitions related to IKEv2 payload. + + 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. + +**/ +#ifndef _IKE_V2_PAYLOAD_H_ +#define _IKE_V2_PAYLOAD_H_ + +// +// Payload Type for IKEv2 +// +#define IKEV2_PAYLOAD_TYPE_NONE 0 +#define IKEV2_PAYLOAD_TYPE_SA 33 +#define IKEV2_PAYLOAD_TYPE_KE 34 +#define IKEV2_PAYLOAD_TYPE_ID_INIT 35 +#define IKEV2_PAYLOAD_TYPE_ID_RSP 36 +#define IKEV2_PAYLOAD_TYPE_CERT 37 +#define IKEV2_PAYLOAD_TYPE_CERTREQ 38 +#define IKEV2_PAYLOAD_TYPE_AUTH 39 +#define IKEV2_PAYLOAD_TYPE_NONCE 40 +#define IKEV2_PAYLOAD_TYPE_NOTIFY 41 +#define IKEV2_PAYLOAD_TYPE_DELETE 42 +#define IKEV2_PAYLOAD_TYPE_VENDOR 43 +#define IKEV2_PAYLOAD_TYPE_TS_INIT 44 +#define IKEV2_PAYLOAD_TYPE_TS_RSP 45 +#define IKEV2_PAYLOAD_TYPE_ENCRYPT 46 +#define IKEV2_PAYLOAD_TYPE_CP 47 +#define IKEV2_PAYLOAD_TYPE_EAP 48 + +// +// IKE header Flag (1 octet) for IKEv2, defined in RFC 4306 section 3.1 +// +// I(nitiator) (bit 3 of Flags, 0x08) - This bit MUST be set in messages sent by the +// original initiator of the IKE_SA +// +// R(esponse) (bit 5 of Flags, 0x20) - This bit indicates that this message is a response to +// a message containing the same message ID. +// +#define IKE_HEADER_FLAGS_INIT 0x08 +#define IKE_HEADER_FLAGS_RESPOND 0x20 + +// +// IKE Header Exchange Type for IKEv2 +// +#define IKEV2_EXCHANGE_TYPE_INIT 34 +#define IKEV2_EXCHANGE_TYPE_AUTH 35 +#define IKEV2_EXCHANGE_TYPE_CREATE_CHILD 36 +#define IKEV2_EXCHANGE_TYPE_INFO 37 + +#pragma pack(1) +typedef struct { + UINT8 NextPayload; + UINT8 Reserved; + UINT16 PayloadLength; +} IKEV2_COMMON_PAYLOAD_HEADER; +#pragma pack() + +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + // + // Proposals + // +} IKEV2_SA; +#pragma pack() + +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 ProposalIndex; + UINT8 ProtocolId; + UINT8 SpiSize; + UINT8 NumTransforms; +} IKEV2_PROPOSAL; +#pragma pack() + +// +// IKEv2 Transform Type Values presented within Transform Payload +// +#define IKEV2_TRANSFORM_TYPE_ENCR 1 // Encryption Algorithm +#define IKEV2_TRANSFORM_TYPE_PRF 2 // Pseduo-Random Func +#define IKEV2_TRANSFORM_TYPE_INTEG 3 // Integrity Algorithm +#define IKEV2_TRANSFORM_TYPE_DH 4 // DH Group +#define IKEV2_TRANSFORM_TYPE_ESN 5 // Extended Sequence Number + +// +// IKEv2 Transform ID for Encrypt Algorithm (ENCR) +// +#define IKEV2_TRANSFORM_ID_ENCR_DES_IV64 1 +#define IKEV2_TRANSFORM_ID_ENCR_DES 2 +#define IKEV2_TRANSFORM_ID_ENCR_3DES 3 +#define IKEV2_TRANSFORM_ID_ENCR_RC5 4 +#define IKEV2_TRANSFORM_ID_ENCR_IDEA 5 +#define IKEV2_TRANSFORM_ID_ENCR_CAST 6 +#define IKEV2_TRANSFORM_ID_ENCR_BLOWFISH 7 +#define IKEV2_TRANSFORM_ID_ENCR_3IDEA 8 +#define IKEV2_TRANSFORM_ID_ENCR_DES_IV32 9 +#define IKEV2_TRANSFORM_ID_ENCR_NULL 11 +#define IKEV2_TRANSFORM_ID_ENCR_AES_CBC 12 +#define IKEV2_TRANSFORM_ID_ENCR_AES_CTR 13 + +// +// IKEv2 Transform ID for Pseudo-Random Function (PRF) +// +#define IKEV2_TRANSFORM_ID_PRF_HMAC_MD5 1 +#define IKEV2_TRANSFORM_ID_PRF_HMAC_SHA1 2 +#define IKEV2_TRANSFORM_ID_PRF_HMAC_TIGER 3 +#define IKEV2_TRANSFORM_ID_PRF_AES128_XCBC 4 + +// +// IKEv2 Transform ID for Integrity Algorithm (INTEG) +// +#define IKEV2_TRANSFORM_ID_AUTH_NONE 0 +#define IKEV2_TRANSFORM_ID_AUTH_HMAC_MD5_96 1 +#define IKEV2_TRANSFORM_ID_AUTH_HMAC_SHA1_96 2 +#define IKEV2_TRANSFORM_ID_AUTH_HMAC_DES_MAC 3 +#define IKEV2_TRANSFORM_ID_AUTH_HMAC_KPDK_MD5 4 +#define IKEV2_TRANSFORM_ID_AUTH_HMAC_AES_XCBC_96 5 + +// +// IKEv2 Transform ID for Diffie-Hellman Group (DH) +// +#define IKEV2_TRANSFORM_ID_DH_768MODP 1 +#define IKEV2_TRANSFORM_ID_DH_1024MODP 2 +#define IKEV2_TRANSFORM_ID_DH_2048MODP 14 + +// +// IKEv2 Attribute Type Values +// +#define IKEV2_ATTRIBUTE_TYPE_KEYLEN 14 + +// +// Transform Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 TransformType; + UINT8 Reserved; + UINT16 TransformId; + // + // SA Attributes + // +} IKEV2_TRANSFORM; +#pragma pack() + +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT16 DhGroup; + UINT16 Reserved; + // + // Remaining part contains the key exchanged + // +} IKEV2_KEY_EXCHANGE; +#pragma pack() + +// +// Identification Type Values presented within Ikev2 ID payload +// +#define IKEV2_ID_TYPE_IPV4_ADDR 1 +#define IKEV2_ID_TYPE_FQDN 2 +#define IKEV2_ID_TYPE_RFC822_ADDR 3 +#define IKEV2_ID_TYPE_IPV6_ADDR 5 +#define IKEV2_ID_TYPE_DER_ASN1_DN 9 +#define IKEV2_ID_TYPE_DER_ASN1_GN 10 +#define IKEV2_ID_TYPE_KEY_ID 11 + +// +// Identification Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 IdType; + UINT8 Reserver1; + UINT16 Reserver2; + // + // Identification Data + // +} IKEV2_ID; +#pragma pack() + +// +// Encoding Type presented in IKEV2 Cert Payload +// +#define IKEV2_CERT_ENCODEING_RESERVED 0 +#define IKEV2_CERT_ENCODEING_X509_CERT_WRAP 1 +#define IKEV2_CERT_ENCODEING_PGP_CERT 2 +#define IKEV2_CERT_ENCODEING_DNS_SIGN_KEY 3 +#define IKEV2_CERT_ENCODEING_X509_CERT_SIGN 4 +#define IKEV2_CERT_ENCODEING_KERBEROS_TOKEN 6 +#define IKEV2_CERT_ENCODEING_REVOCATION_LIST_CERT 7 +#define IKEV2_CERT_ENCODEING_AUTH_REVOCATION_LIST 8 +#define IKEV2_CERT_ENCODEING_SPKI_CERT 9 +#define IKEV2_CERT_ENCODEING_X509_CERT_ATTRIBUTE 10 +#define IKEV2_CERT_ENCODEING_RAW_RSA_KEY 11 +#define IKEV2_CERT_ENCODEING_HASH_AND_URL_OF_X509_CERT 12 + +// +// IKEV2 Certificate Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 CertEncoding; + // + // Cert Data + // +} IKEV2_CERT; +#pragma pack() + +// +// IKEV2 Certificate Request Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 CertEncoding; + // + // Cert Authority + // +} IKEV2_CERT_REQ; +#pragma pack() + +// +// Authentication Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 AuthMethod; + UINT8 Reserved1; + UINT16 Reserved2; + // + // Auth Data + // +} IKEV2_AUTH; +#pragma pack() + +// +// Authmethod in Authentication Payload +// +#define IKEV2_AUTH_METHOD_RSA 1; // RSA Digital Signature +#define IKEV2_AUTH_METHOD_SKMI 2; // Shared Key Message Integrity +#define IKEV2_AUTH_METHOD_DSS 3; // DSS Digital Signature + +// +// IKEv2 Nonce Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + // + // Nonce Data + // +} IKEV2_NONCE; +#pragma pack() + +// +// Notification Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 ProtocolId; + UINT8 SpiSize; + UINT16 MessageType; + // + // SPI and Notification Data + // +} IKEV2_NOTIFY; +#pragma pack() + +// +// Notify Message Types presented within IKEv2 Notify Payload +// +#define IKEV2_NOTIFICATION_UNSUPPORT_CRITICAL_PAYLOAD 1 +#define IKEV2_NOTIFICATION_INVALID_IKE_SPI 4 +#define IKEV2_NOTIFICATION_INVALID_MAJOR_VERSION 5 +#define IKEV2_NOTIFICATION_INVALID_SYNTAX 7 +#define IKEV2_NOTIFICATION_INVALID_MESSAGE_ID 9 +#define IKEV2_NOTIFICATION_INVALID_SPI 11 +#define IKEV2_NOTIFICATION_NO_PROPOSAL_CHOSEN 14 +#define IKEV2_NOTIFICATION_INVALID_KEY_PAYLOAD 17 +#define IKEV2_NOTIFICATION_AUTHENTICATION_FAILED 24 +#define IKEV2_NOTIFICATION_SINGLE_PAIR_REQUIRED 34 +#define IKEV2_NOTIFICATION_NO_ADDITIONAL_SAS 35 +#define IKEV2_NOTIFICATION_INTERNAL_ADDRESS_FAILURE 36 +#define IKEV2_NOTIFICATION_FAILED_CP_REQUIRED 37 +#define IKEV2_NOTIFICATION_TS_UNCCEPTABLE 38 +#define IKEV2_NOTIFICATION_INVALID_SELECTORS 39 +#define IKEV2_NOTIFICATION_COOKIE 16390 +#define IKEV2_NOTIFICATION_USE_TRANSPORT_MODE 16391 +#define IKEV2_NOTIFICATION_REKEY_SA 16393 + +// +// IKEv2 Protocol ID +// +// +// IKEv2 Delete Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 ProtocolId; + UINT8 SpiSize; + UINT16 NumSpis; + // + // SPIs + // +} IKEV2_DELETE; +#pragma pack() + +// +// Traffic Selector Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 TSNumbers; + UINT8 Reserved1; + UINT16 Reserved2; + // + // Traffic Selector + // +} IKEV2_TS; +#pragma pack() + +// +// Traffic Selector +// +#pragma pack(1) +typedef struct { + UINT8 TSType; + UINT8 IpProtocolId; + UINT16 SelecorLen; + UINT16 StartPort; + UINT16 EndPort; + // + // Starting Address && Ending Address + // +} TRAFFIC_SELECTOR; +#pragma pack() + +// +// Ts Type in Traffic Selector +// +#define IKEV2_TS_TYPE_IPV4_ADDR_RANGE 7 +#define IKEV2_TS_TYPS_IPV6_ADDR_RANGE 8 + +// +// Vendor Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + // + // Vendor ID + // +} IKEV2_VENDOR; +#pragma pack() + +// +// Encrypted Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + // + // IV, Encrypted IKE Payloads, Padding, PAD length, Integrity CheckSum + // +} IKEV2_ENCRYPTED; +#pragma pack() + +#pragma pack(1) +typedef struct { + UINT8 PadLength; +} IKEV2_PAD_LEN; +#pragma pack() + +// +// Configuration Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 CfgType; + UINT8 Reserve1; + UINT16 Reserve2; + // + // Configuration Attributes + // +} IKEV2_CFG; +#pragma pack() + +// +// Configuration Payload CPG type +// +#define IKEV2_CFG_TYPE_REQUEST 1 +#define IKEV2_CFG_TYPE_REPLY 2 +#define IKEV2_CFG_TYPE_SET 3 +#define IKEV2_CFG_TYPE_ACK 4 + +// +// Configuration Attributes +// +#pragma pack(1) +typedef struct { + UINT16 AttritType; + UINT16 ValueLength; +} IKEV2_CFG_ATTRIBUTES; +#pragma pack() + +// +// Configuration Attributes +// +#define IKEV2_CFG_ATTR_INTERNAL_IP4_ADDRESS 1 +#define IKEV2_CFG_ATTR_INTERNAL_IP4_NBTMASK 2 +#define IKEV2_CFG_ATTR_INTERNAL_IP4_DNS 3 +#define IKEV2_CFG_ATTR_INTERNAL_IP4_NBNS 4 +#define IKEV2_CFG_ATTR_INTERNA_ADDRESS_BXPIRY 5 +#define IKEV2_CFG_ATTR_INTERNAL_IP4_DHCP 6 +#define IKEV2_CFG_ATTR_APPLICATION_VERSION 7 +#define IKEV2_CFG_ATTR_INTERNAL_IP6_ADDRESS 8 +#define IKEV2_CFG_ATTR_INTERNAL_IP6_DNS 10 +#define IKEV2_CFG_ATTR_INTERNAL_IP6_NBNS 11 +#define IKEV2_CFG_ATTR_INTERNAL_IP6_DHCP 12 +#define IKEV2_CFG_ATTR_INTERNAL_IP4_SUBNET 13 +#define IKEV2_CFG_ATTR_SUPPORTED_ATTRIBUTES 14 +#define IKEV2_CFG_ATTR_IP6_SUBNET 15 + +#endif + diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c b/Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c new file mode 100644 index 0000000000..f9421ed4e8 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c @@ -0,0 +1,2261 @@ +/** @file + The operations for IKEv2 SA. + + (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 "IkeService.h" +#include "Ikev2.h" + +/** + Generates the DH Key. + + This generates the DH local public key and store it in the IKEv2 SA Session's GxBuffer. + + @param[in] IkeSaSession Pointer to related IKE SA Session. + + @retval EFI_SUCCESS The operation succeeded. + @retval Others The operation failed. + +**/ +EFI_STATUS +Ikev2GenerateSaDhPublicKey ( + IN IKEV2_SA_SESSION *IkeSaSession + ); + +/** + Generates the IKEv2 SA key for the furthure IKEv2 exchange. + + @param[in] IkeSaSession Pointer to IKEv2 SA Session. + @param[in] KePayload Pointer to Key payload used to generate the Key. + + @retval EFI_UNSUPPORTED If the Algorithm Id is not supported. + @retval EFI_SUCCESS The operation succeeded. + +**/ +EFI_STATUS +Ikev2GenerateSaKeys ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *KePayload + ); + +/** + Generates the Keys for the furthure IPsec Protocol. + + @param[in] ChildSaSession Pointer to IKE Child SA Session. + @param[in] KePayload Pointer to Key payload used to generate the Key. + + @retval EFI_UNSUPPORTED If one or more Algorithm Id is unsupported. + @retval EFI_SUCCESS The operation succeeded. + +**/ +EFI_STATUS +Ikev2GenerateChildSaKeys ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession, + IN IKE_PAYLOAD *KePayload + ); + +/** + Gernerates IKEv2 packet for IKE_SA_INIT exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION related to the exchange. + @param[in] Context Context Data passed by caller. + + @retval EFI_SUCCESS The IKEv2 packet generation succeeded. + @retval Others The IKEv2 packet generation failed. + +**/ +IKE_PACKET * +Ikev2InitPskGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + IKE_PACKET *IkePacket; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *KePayload; + IKE_PAYLOAD *NoncePayload; + IKE_PAYLOAD *NotifyPayload; + EFI_STATUS Status; + + SaPayload = NULL; + KePayload = NULL; + NoncePayload = NULL; + NotifyPayload = NULL; + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + + // + // 1. Allocate IKE packet + // + IkePacket = IkePacketAlloc (); + if (IkePacket == NULL) { + goto CheckError; + } + + // + // 1.a Fill the IkePacket->Hdr + // + IkePacket->Header->ExchangeType = IKEV2_EXCHANGE_TYPE_INIT; + IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie; + IkePacket->Header->Version = (UINT8) (2 << 4); + IkePacket->Header->MessageId = 0; + + if (IkeSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT; + } else { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND; + } + + // + // If the NCookie is not NULL, this IKE_SA_INIT packet is resent by the NCookie + // and the NCookie payload should be the first payload in this packet. + // + if (IkeSaSession->NCookie != NULL) { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_NOTIFY; + NotifyPayload = Ikev2GenerateNotifyPayload ( + IPSEC_PROTO_ISAKMP, + IKEV2_PAYLOAD_TYPE_SA, + 0, + IKEV2_NOTIFICATION_COOKIE, + NULL, + IkeSaSession->NCookie, + IkeSaSession->NCookieSize + ); + } else { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_SA; + } + + // + // 2. Generate SA Payload according to the SaData & SaParams + // + SaPayload = Ikev2GenerateSaPayload ( + IkeSaSession->SaData, + IKEV2_PAYLOAD_TYPE_KE, + IkeSessionTypeIkeSa + ); + + // + // 3. Generate DH public key. + // The DhPrivate Key has been generated in Ikev2InitPskParser, if the + // IkeSaSession is responder. If resending IKE_SA_INIT with Cookie Notify + // No need to recompute the Public key. + // + if ((IkeSaSession->SessionCommon.IsInitiator) && (IkeSaSession->NCookie == NULL)) { + Status = Ikev2GenerateSaDhPublicKey (IkeSaSession); + if (EFI_ERROR (Status)) { + goto CheckError; + } + } + + // + // 4. Generate KE Payload according to SaParams->DhGroup + // + KePayload = Ikev2GenerateKePayload ( + IkeSaSession, + IKEV2_PAYLOAD_TYPE_NONCE + ); + + // + // 5. Generate Nonce Payload + // If resending IKE_SA_INIT with Cookie Notify paylaod, no need to regenerate + // the Nonce Payload. + // + if ((IkeSaSession->SessionCommon.IsInitiator) && (IkeSaSession->NCookie == NULL)) { + IkeSaSession->NiBlkSize = IKE_NONCE_SIZE; + IkeSaSession->NiBlock = IkeGenerateNonce (IKE_NONCE_SIZE); + if (IkeSaSession->NiBlock == NULL) { + goto CheckError; + } + } + + if (IkeSaSession->SessionCommon.IsInitiator) { + NoncePayload = Ikev2GenerateNoncePayload ( + IkeSaSession->NiBlock, + IkeSaSession->NiBlkSize, + IKEV2_PAYLOAD_TYPE_NONE + ); + } else { + // + // The Nonce Payload has been created in Ikev2PskParser if the IkeSaSession is + // responder. + // + NoncePayload = Ikev2GenerateNoncePayload ( + IkeSaSession->NrBlock, + IkeSaSession->NrBlkSize, + IKEV2_PAYLOAD_TYPE_NONE + ); + } + + if (NotifyPayload != NULL) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload); + } + if (SaPayload != NULL) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, SaPayload); + } + if (KePayload != NULL) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, KePayload); + } + if (NoncePayload != NULL) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, NoncePayload); + } + + return IkePacket; + +CheckError: + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + if (SaPayload != NULL) { + IkePayloadFree (SaPayload); + } + return NULL; +} + +/** + Parses the IKEv2 packet for IKE_SA_INIT exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION related to the exchange. + @param[in] IkePacket The received IKE packet to be parsed. + + @retval EFI_SUCCESS The IKEv2 packet is acceptable and the relative data is + saved for furthure communication. + @retval EFI_INVALID_PARAMETER The IKEv2 packet is malformed or the SA proposal is unacceptable. + +**/ +EFI_STATUS +Ikev2InitPskParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *KeyPayload; + IKE_PAYLOAD *IkePayload; + IKE_PAYLOAD *NoncePayload; + IKE_PAYLOAD *NotifyPayload; + UINT8 *NonceBuffer; + UINTN NonceSize; + LIST_ENTRY *Entry; + EFI_STATUS Status; + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + KeyPayload = NULL; + SaPayload = NULL; + NoncePayload = NULL; + IkePayload = NULL; + NotifyPayload = NULL; + + // + // Iterate payloads to find the SaPayload and KeyPayload. + // + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_SA) { + SaPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_KE) { + KeyPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_NONCE) { + NoncePayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_NOTIFY) { + NotifyPayload = IkePayload; + } + } + + // + // According to RFC 4306 - 2.6. If the responder responds with the COOKIE Notify + // payload with the cookie data, initiator MUST retry the IKE_SA_INIT with a + // Notify payload of type COOKIE containing the responder suppplied cookie data + // as first payload and all other payloads unchanged. + // + if (IkeSaSession->SessionCommon.IsInitiator) { + if (NotifyPayload != NULL && !EFI_ERROR(Ikev2ParserNotifyCookiePayload (NotifyPayload, IkeSaSession))) { + return EFI_SUCCESS; + } + } + + if ((KeyPayload == NULL) || (SaPayload == NULL) || (NoncePayload == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Store NoncePayload for SKEYID computing. + // + NonceSize = NoncePayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER); + NonceBuffer = (UINT8 *) AllocatePool (NonceSize); + if (NonceBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto CheckError; + } + + CopyMem ( + NonceBuffer, + NoncePayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER), + NonceSize + ); + + // + // Check if IkePacket Header matches the state + // + if (IkeSaSession->SessionCommon.IsInitiator) { + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_RESPOND + // + if (IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) { + Status = EFI_INVALID_PARAMETER; + goto CheckError; + } + + // + // 2. Parse the SA Payload and Key Payload to find out the cryptographic + // suite and fill in the Sa paramse into CommonSession->SaParams + // + if (!Ikev2SaParseSaPayload (IkeSaSession, SaPayload, IkePacket->Header->Flags)) { + Status = EFI_INVALID_PARAMETER; + goto CheckError; + } + + // + // 3. If Initiator, the NoncePayload is Nr_b. + // + IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateAuth); + IkeSaSession->NrBlock = NonceBuffer; + IkeSaSession->NrBlkSize = NonceSize; + IkeSaSession->SessionCommon.State = IkeStateAuth; + IkeSaSession->ResponderCookie = IkePacket->Header->ResponderCookie; + + // + // 4. Change the state of IkeSaSession + // + IkeSaSession->SessionCommon.State = IkeStateAuth; + } else { + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_INIT + // + if (IkePacket->Header->Flags != IKE_HEADER_FLAGS_INIT) { + Status = EFI_INVALID_PARAMETER; + goto CheckError; + } + + // + // 2. Parse the SA payload and find out the perfered one + // and fill in the SA parameters into CommonSession->SaParams and SaData into + // IkeSaSession for the responder SA payload generation. + // + if (!Ikev2SaParseSaPayload (IkeSaSession, SaPayload, IkePacket->Header->Flags)) { + Status = EFI_INVALID_PARAMETER; + goto CheckError; + } + + // + // 3. Generat Dh Y parivate Key + // + Status = Ikev2GenerateSaDhPublicKey (IkeSaSession); + if (EFI_ERROR (Status)) { + goto CheckError; + } + + // + // 4. If Responder, the NoncePayload is Ni_b and go to generate Nr_b. + // + IkeSaSession->NiBlock = NonceBuffer; + IkeSaSession->NiBlkSize = NonceSize; + + // + // 5. Generate Nr_b + // + IkeSaSession->NrBlock = IkeGenerateNonce (IKE_NONCE_SIZE); + ASSERT (IkeSaSession->NrBlock != NULL); + IkeSaSession->NrBlkSize = IKE_NONCE_SIZE; + + // + // 6. Save the Cookies + // + IkeSaSession->InitiatorCookie = IkePacket->Header->InitiatorCookie; + IkeSaSession->ResponderCookie = IkeGenerateCookie (); + } + + if (IkeSaSession->SessionCommon.PreferDhGroup != ((IKEV2_KEY_EXCHANGE *)KeyPayload->PayloadBuf)->DhGroup) { + Status = EFI_INVALID_PARAMETER; + goto CheckError; + } + // + // Call Ikev2GenerateSaKeys to create SKEYID, SKEYID_d, SKEYID_a, SKEYID_e. + // + Status = Ikev2GenerateSaKeys (IkeSaSession, KeyPayload); + if (EFI_ERROR(Status)) { + goto CheckError; + } + return EFI_SUCCESS; + +CheckError: + if (NonceBuffer != NULL) { + FreePool (NonceBuffer); + } + + return Status; +} + +/** + Generates the IKEv2 packet for IKE_AUTH exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION. + @param[in] Context Context data passed by caller. + + @retval Pointer to IKE Packet to be sent out. + +**/ +IKE_PACKET * +Ikev2AuthPskGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + IKE_PACKET *IkePacket; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *IdPayload; + IKE_PAYLOAD *AuthPayload; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *TsiPayload; + IKE_PAYLOAD *TsrPayload; + IKE_PAYLOAD *NotifyPayload; + IKE_PAYLOAD *CpPayload; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList)); + + IkePacket = NULL; + IdPayload = NULL; + AuthPayload = NULL; + SaPayload = NULL; + TsiPayload = NULL; + TsrPayload = NULL; + NotifyPayload = NULL; + CpPayload = NULL; + NotifyPayload = NULL; + + // + // 1. Allocate IKE Packet + // + IkePacket= IkePacketAlloc (); + if (IkePacket == NULL) { + return NULL; + } + + // + // 1.a Fill the IkePacket Header. + // + IkePacket->Header->ExchangeType = IKEV2_EXCHANGE_TYPE_AUTH; + IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie; + IkePacket->Header->Version = (UINT8)(2 << 4); + if (ChildSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ID_INIT; + } else { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ID_RSP; + } + + // + // According to RFC4306_2.2, For the IKE_SA_INIT message the MessageID should + // be always number 0 and 1; + // + IkePacket->Header->MessageId = 1; + + if (IkeSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT; + } else { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND; + } + + // + // 2. Generate ID Payload according to IP version and address. + // + IdPayload = Ikev2GenerateIdPayload ( + &IkeSaSession->SessionCommon, + IKEV2_PAYLOAD_TYPE_AUTH + ); + if (IdPayload == NULL) { + goto CheckError; + } + + // + // 3. Generate Auth Payload + // If it is tunnel mode, should create the configuration payload after the + // Auth payload. + // + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + + AuthPayload = Ikev2PskGenerateAuthPayload ( + ChildSaSession->IkeSaSession, + IdPayload, + IKEV2_PAYLOAD_TYPE_SA, + FALSE + ); + } else { + AuthPayload = Ikev2PskGenerateAuthPayload ( + ChildSaSession->IkeSaSession, + IdPayload, + IKEV2_PAYLOAD_TYPE_CP, + FALSE + ); + if (IkeSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) { + CpPayload = Ikev2GenerateCpPayload ( + ChildSaSession->IkeSaSession, + IKEV2_PAYLOAD_TYPE_SA, + IKEV2_CFG_ATTR_INTERNAL_IP4_ADDRESS + ); + } else { + CpPayload = Ikev2GenerateCpPayload ( + ChildSaSession->IkeSaSession, + IKEV2_PAYLOAD_TYPE_SA, + IKEV2_CFG_ATTR_INTERNAL_IP6_ADDRESS + ); + } + + if (CpPayload == NULL) { + goto CheckError; + } + } + + if (AuthPayload == NULL) { + goto CheckError; + } + + // + // 4. Generate SA Payload according to the SA Data in ChildSaSession + // + SaPayload = Ikev2GenerateSaPayload ( + ChildSaSession->SaData, + IKEV2_PAYLOAD_TYPE_TS_INIT, + IkeSessionTypeChildSa + ); + if (SaPayload == NULL) { + goto CheckError; + } + + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + // + // Generate Tsi and Tsr. + // + TsiPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_TS_RSP, + FALSE + ); + + TsrPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_NOTIFY, + FALSE + ); + + // + // Generate Notify Payload. If transport mode, there should have Notify + // payload with TRANSPORT_MODE notification. + // + NotifyPayload = Ikev2GenerateNotifyPayload ( + 0, + IKEV2_PAYLOAD_TYPE_NONE, + 0, + IKEV2_NOTIFICATION_USE_TRANSPORT_MODE, + NULL, + NULL, + 0 + ); + if (NotifyPayload == NULL) { + goto CheckError; + } + } else { + // + // Generate Tsr for Tunnel mode. + // + TsiPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_TS_RSP, + TRUE + ); + TsrPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_NONE, + FALSE + ); + } + + if (TsiPayload == NULL || TsrPayload == NULL) { + goto CheckError; + } + + IKE_PACKET_APPEND_PAYLOAD (IkePacket, IdPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, AuthPayload); + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, CpPayload); + } + IKE_PACKET_APPEND_PAYLOAD (IkePacket, SaPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsiPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsrPayload); + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload); + } + + return IkePacket; + +CheckError: + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + + if (IdPayload != NULL) { + IkePayloadFree (IdPayload); + } + + if (AuthPayload != NULL) { + IkePayloadFree (AuthPayload); + } + + if (CpPayload != NULL) { + IkePayloadFree (CpPayload); + } + + if (SaPayload != NULL) { + IkePayloadFree (SaPayload); + } + + if (TsiPayload != NULL) { + IkePayloadFree (TsiPayload); + } + + if (TsrPayload != NULL) { + IkePayloadFree (TsrPayload); + } + + if (NotifyPayload != NULL) { + IkePayloadFree (NotifyPayload); + } + + return NULL; +} + +/** + Parses IKE_AUTH packet. + + @param[in] SaSession Pointer to the IKE_SA_SESSION related to this packet. + @param[in] IkePacket Pointer to the IKE_AUTH packet to be parsered. + + @retval EFI_INVALID_PARAMETER The IKE packet is malformed or the SA + proposal is unacceptable. + @retval EFI_SUCCESS The IKE packet is acceptable and the + relative data is saved for furthure communication. + +**/ +EFI_STATUS +Ikev2AuthPskParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *IkePayload; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *IdiPayload; + IKE_PAYLOAD *IdrPayload; + IKE_PAYLOAD *AuthPayload; + IKE_PAYLOAD *TsiPayload; + IKE_PAYLOAD *TsrPayload; + IKE_PAYLOAD *VerifiedAuthPayload; + LIST_ENTRY *Entry; + EFI_STATUS Status; + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList)); + + SaPayload = NULL; + IdiPayload = NULL; + IdrPayload = NULL; + AuthPayload = NULL; + TsiPayload = NULL; + TsrPayload = NULL; + + // + // Iterate payloads to find the SaPayload/ID/AUTH/TS Payload. + // + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_INIT) { + IdiPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_RSP) { + IdrPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_SA) { + SaPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_AUTH) { + AuthPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_INIT) { + TsiPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_RSP) { + TsrPayload = IkePayload; + } + } + + if ((SaPayload == NULL) || (AuthPayload == NULL) || (TsiPayload == NULL) || (TsrPayload == NULL)) { + return EFI_INVALID_PARAMETER; + } + if ((IdiPayload == NULL) && (IdrPayload == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check IkePacket Header is match the state + // + if (IkeSaSession->SessionCommon.IsInitiator) { + + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_RESPOND + // + if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) || + (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH) + ) { + return EFI_INVALID_PARAMETER; + } + + } else { + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_INIT + // + if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_INIT) || + (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH) + ) { + return EFI_INVALID_PARAMETER; + } + + // + // 2. Parse the SA payload and Key Payload and find out the perferable one + // and fill in the Sa paramse into CommonSession->SaParams and SaData into + // IkeSaSession for the responder SA payload generation. + // + } + + // + // Verify the Auth Payload. + // + VerifiedAuthPayload = Ikev2PskGenerateAuthPayload ( + IkeSaSession, + IkeSaSession->SessionCommon.IsInitiator ? IdrPayload : IdiPayload, + IKEV2_PAYLOAD_TYPE_SA, + TRUE + ); + if ((VerifiedAuthPayload != NULL) && + (0 != CompareMem ( + VerifiedAuthPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER), + AuthPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER), + VerifiedAuthPayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER) + ))) { + return EFI_INVALID_PARAMETER; + }; + + // + // 3. Parse the SA Payload to find out the cryptographic suite + // and fill in the Sa paramse into CommonSession->SaParams. If no acceptable + // porposal found, return EFI_INVALID_PARAMETER. + // + if (!Ikev2ChildSaParseSaPayload (ChildSaSession, SaPayload, IkePacket->Header->Flags)) { + return EFI_INVALID_PARAMETER; + } + + // + // 4. Parse TSi, TSr payloads. + // + if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != + ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId) && + (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0) && + (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0) + ) { + return EFI_INVALID_PARAMETER; + } + + if (!IkeSaSession->SessionCommon.IsInitiator) { + // + //TODO:check the Port range. Only support any port and one certain port here. + // + ChildSaSession->ProtoId = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId; + ChildSaSession->LocalPort = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort; + ChildSaSession->RemotePort = ((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort; + // + // Association a SPD with this SA. + // + Status = Ikev2ChildSaAssociateSpdEntry (ChildSaSession); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + // + // Associate the IkeSaSession's SPD to the first ChildSaSession's SPD. + // + if (ChildSaSession->IkeSaSession->Spd == NULL) { + ChildSaSession->IkeSaSession->Spd = ChildSaSession->Spd; + Status = Ikev2ChildSaSessionSpdSelectorCreate (ChildSaSession); + if (EFI_ERROR (Status)) { + return Status; + } + } + } else { + // + //TODO:check the Port range. + // + if ((((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) && + (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->RemotePort) + ) { + return EFI_INVALID_PARAMETER; + } + if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) && + (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->LocalPort) + ) { + return EFI_INVALID_PARAMETER; + } + // + // For the tunnel mode, it should add the vitual IP address into the SA's SPD Selector. + // + if (ChildSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + if (!ChildSaSession->IkeSaSession->SessionCommon.IsInitiator) { + // + // If it is tunnel mode, the UEFI part must be the initiator. + // + return EFI_INVALID_PARAMETER; + } + // + // Get the Virtual IP address from the Tsi traffic selector. + // TODO: check the CFG reply payload + // + CopyMem ( + &ChildSaSession->SpdSelector->LocalAddress[0].Address, + TsiPayload->PayloadBuf + sizeof (IKEV2_TS) + sizeof (TRAFFIC_SELECTOR), + (ChildSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) ? + sizeof (EFI_IPv4_ADDRESS) : sizeof (EFI_IPv6_ADDRESS) + ); + } + } + + // + // 5. Generate keymats for IPsec protocol. + // + Status = Ikev2GenerateChildSaKeys (ChildSaSession, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + if (IkeSaSession->SessionCommon.IsInitiator) { + // + // 6. Change the state of IkeSaSession + // + IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateIkeSaEstablished); + IkeSaSession->SessionCommon.State = IkeStateIkeSaEstablished; + } + + return EFI_SUCCESS; +} + +/** + Gernerates IKEv2 packet for IKE_SA_INIT exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION related to the exchange. + @param[in] Context Context Data passed by caller. + + @retval EFI_SUCCESS The IKE packet generation succeeded. + @retval Others The IKE packet generation failed. + +**/ +IKE_PACKET* +Ikev2InitCertGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + IKE_PACKET *IkePacket; + IKE_PAYLOAD *CertReqPayload; + LIST_ENTRY *Node; + IKE_PAYLOAD *NoncePayload; + + if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) { + return NULL; + } + + // + // The first two messages exchange is same between PSK and Cert. + // + IkePacket = Ikev2InitPskGenerator (SaSession, Context); + + if ((IkePacket != NULL) && (!((IKEV2_SA_SESSION *)SaSession)->SessionCommon.IsInitiator)) { + // + // Add the Certification Request Payload + // + CertReqPayload = Ikev2GenerateCertificatePayload ( + (IKEV2_SA_SESSION *)SaSession, + IKEV2_PAYLOAD_TYPE_NONE, + (UINT8*)PcdGetPtr(PcdIpsecUefiCaFile), + PcdGet32(PcdIpsecUefiCaFileSize), + IKEV2_CERT_ENCODEING_HASH_AND_URL_OF_X509_CERT, + TRUE + ); + // + // Change Nonce Payload Next payload type. + // + IKE_PACKET_END_PAYLOAD (IkePacket, Node); + NoncePayload = IKE_PAYLOAD_BY_PACKET (Node); + ((IKEV2_NONCE *)NoncePayload->PayloadBuf)->Header.NextPayload = IKEV2_PAYLOAD_TYPE_CERTREQ; + + // + // Add Certification Request Payload + // + IKE_PACKET_APPEND_PAYLOAD (IkePacket, CertReqPayload); + } + + return IkePacket; +} + +/** + Parses the IKEv2 packet for IKE_SA_INIT exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION related to the exchange. + @param[in] IkePacket The received IKEv2 packet to be parsed. + + @retval EFI_SUCCESS The IKEv2 packet is acceptable and the relative data is + saved for furthure communication. + @retval EFI_INVALID_PARAMETER The IKE packet is malformed or the SA proposal is unacceptable. + @retval EFI_UNSUPPORTED The certificate authentication is not supported. + +**/ +EFI_STATUS +Ikev2InitCertParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) { + return EFI_UNSUPPORTED; + } + + // + // The first two messages exchange is same between PSK and Cert. + // Todo: Parse Certificate Request from responder Initial Exchange. + // + return Ikev2InitPskParser (SaSession, IkePacket); +} + +/** + Generates the IKEv2 packet for IKE_AUTH exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION. + @param[in] Context Context data passed by caller. + + @retval Pointer to IKEv2 Packet to be sent out. + +**/ +IKE_PACKET * +Ikev2AuthCertGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + IKE_PACKET *IkePacket; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *IdPayload; + IKE_PAYLOAD *AuthPayload; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *TsiPayload; + IKE_PAYLOAD *TsrPayload; + IKE_PAYLOAD *NotifyPayload; + IKE_PAYLOAD *CpPayload; + IKE_PAYLOAD *CertPayload; + IKE_PAYLOAD *CertReqPayload; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + + if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) { + return NULL; + } + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList)); + + IkePacket = NULL; + IdPayload = NULL; + AuthPayload = NULL; + CpPayload = NULL; + SaPayload = NULL; + TsiPayload = NULL; + TsrPayload = NULL; + NotifyPayload = NULL; + CertPayload = NULL; + CertReqPayload = NULL; + + // + // 1. Allocate IKE Packet + // + IkePacket= IkePacketAlloc (); + if (IkePacket == NULL) { + return NULL; + } + + // + // 1.a Fill the IkePacket Header. + // + IkePacket->Header->ExchangeType = IKEV2_EXCHANGE_TYPE_AUTH; + IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie; + IkePacket->Header->Version = (UINT8)(2 << 4); + if (ChildSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ID_INIT; + } else { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ID_RSP; + } + + // + // According to RFC4306_2.2, For the IKE_SA_INIT message the MessageID should + // be always number 0 and 1; + // + IkePacket->Header->MessageId = 1; + + if (IkeSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT; + } else { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND; + } + + // + // 2. Generate ID Payload according to IP version and address. + // + IdPayload = Ikev2GenerateCertIdPayload ( + &IkeSaSession->SessionCommon, + IKEV2_PAYLOAD_TYPE_CERT, + (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificate), + PcdGet32 (PcdIpsecUefiCertificateSize) + ); + if (IdPayload == NULL) { + goto CheckError; + } + + // + // 3. Generate Certificate Payload + // + CertPayload = Ikev2GenerateCertificatePayload ( + IkeSaSession, + (UINT8)(IkeSaSession->SessionCommon.IsInitiator ? IKEV2_PAYLOAD_TYPE_CERTREQ : IKEV2_PAYLOAD_TYPE_AUTH), + (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificate), + PcdGet32 (PcdIpsecUefiCertificateSize), + IKEV2_CERT_ENCODEING_X509_CERT_SIGN, + FALSE + ); + if (CertPayload == NULL) { + goto CheckError; + } + + if (IkeSaSession->SessionCommon.IsInitiator) { + CertReqPayload = Ikev2GenerateCertificatePayload ( + IkeSaSession, + IKEV2_PAYLOAD_TYPE_AUTH, + (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificate), + PcdGet32 (PcdIpsecUefiCertificateSize), + IKEV2_CERT_ENCODEING_HASH_AND_URL_OF_X509_CERT, + TRUE + ); + if (CertReqPayload == NULL) { + goto CheckError; + } + } + + // + // 4. Generate Auth Payload + // If it is tunnel mode, should create the configuration payload after the + // Auth payload. + // + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + AuthPayload = Ikev2CertGenerateAuthPayload ( + ChildSaSession->IkeSaSession, + IdPayload, + IKEV2_PAYLOAD_TYPE_SA, + FALSE, + (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificateKey), + PcdGet32 (PcdIpsecUefiCertificateKeySize), + ChildSaSession->IkeSaSession->Pad->Data->AuthData, + ChildSaSession->IkeSaSession->Pad->Data->AuthDataSize + ); + } else { + AuthPayload = Ikev2CertGenerateAuthPayload ( + ChildSaSession->IkeSaSession, + IdPayload, + IKEV2_PAYLOAD_TYPE_CP, + FALSE, + (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificateKey), + PcdGet32 (PcdIpsecUefiCertificateKeySize), + ChildSaSession->IkeSaSession->Pad->Data->AuthData, + ChildSaSession->IkeSaSession->Pad->Data->AuthDataSize + ); + if (IkeSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) { + CpPayload = Ikev2GenerateCpPayload ( + ChildSaSession->IkeSaSession, + IKEV2_PAYLOAD_TYPE_SA, + IKEV2_CFG_ATTR_INTERNAL_IP4_ADDRESS + ); + } else { + CpPayload = Ikev2GenerateCpPayload ( + ChildSaSession->IkeSaSession, + IKEV2_PAYLOAD_TYPE_SA, + IKEV2_CFG_ATTR_INTERNAL_IP6_ADDRESS + ); + } + + if (CpPayload == NULL) { + goto CheckError; + } + } + + if (AuthPayload == NULL) { + goto CheckError; + } + + // + // 5. Generate SA Payload according to the Sa Data in ChildSaSession + // + SaPayload = Ikev2GenerateSaPayload ( + ChildSaSession->SaData, + IKEV2_PAYLOAD_TYPE_TS_INIT, + IkeSessionTypeChildSa + ); + if (SaPayload == NULL) { + goto CheckError; + } + + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + // + // Generate Tsi and Tsr. + // + TsiPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_TS_RSP, + FALSE + ); + + TsrPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_NOTIFY, + FALSE + ); + + // + // Generate Notify Payload. If transport mode, there should have Notify + // payload with TRANSPORT_MODE notification. + // + NotifyPayload = Ikev2GenerateNotifyPayload ( + 0, + IKEV2_PAYLOAD_TYPE_NONE, + 0, + IKEV2_NOTIFICATION_USE_TRANSPORT_MODE, + NULL, + NULL, + 0 + ); + if (NotifyPayload == NULL) { + goto CheckError; + } + } else { + // + // Generate Tsr for Tunnel mode. + // + TsiPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_TS_RSP, + TRUE + ); + TsrPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_NONE, + FALSE + ); + } + + if (TsiPayload == NULL || TsrPayload == NULL) { + goto CheckError; + } + + IKE_PACKET_APPEND_PAYLOAD (IkePacket, IdPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, CertPayload); + if (IkeSaSession->SessionCommon.IsInitiator) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, CertReqPayload); + } + IKE_PACKET_APPEND_PAYLOAD (IkePacket, AuthPayload); + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, CpPayload); + } + IKE_PACKET_APPEND_PAYLOAD (IkePacket, SaPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsiPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsrPayload); + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload); + } + + return IkePacket; + +CheckError: + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + + if (IdPayload != NULL) { + IkePayloadFree (IdPayload); + } + + if (CertPayload != NULL) { + IkePayloadFree (CertPayload); + } + + if (CertReqPayload != NULL) { + IkePayloadFree (CertReqPayload); + } + + if (AuthPayload != NULL) { + IkePayloadFree (AuthPayload); + } + + if (CpPayload != NULL) { + IkePayloadFree (CpPayload); + } + + if (SaPayload != NULL) { + IkePayloadFree (SaPayload); + } + + if (TsiPayload != NULL) { + IkePayloadFree (TsiPayload); + } + + if (TsrPayload != NULL) { + IkePayloadFree (TsrPayload); + } + + if (NotifyPayload != NULL) { + IkePayloadFree (NotifyPayload); + } + + return NULL; +} + +/** + Parses IKE_AUTH packet. + + @param[in] SaSession Pointer to the IKE_SA_SESSION related to this packet. + @param[in] IkePacket Pointer to the IKE_AUTH packet to be parsered. + + @retval EFI_INVALID_PARAMETER The IKEv2 packet is malformed or the SA + proposal is unacceptable. + @retval EFI_SUCCESS The IKE packet is acceptable and the + relative data is saved for furthure communication. + @retval EFI_UNSUPPORTED The certificate authentication is not supported. + +**/ +EFI_STATUS +Ikev2AuthCertParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *IkePayload; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *IdiPayload; + IKE_PAYLOAD *IdrPayload; + IKE_PAYLOAD *AuthPayload; + IKE_PAYLOAD *TsiPayload; + IKE_PAYLOAD *TsrPayload; + IKE_PAYLOAD *CertPayload; + IKE_PAYLOAD *VerifiedAuthPayload; + LIST_ENTRY *Entry; + EFI_STATUS Status; + + if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) { + return EFI_UNSUPPORTED; + } + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList)); + + SaPayload = NULL; + IdiPayload = NULL; + IdrPayload = NULL; + AuthPayload = NULL; + TsiPayload = NULL; + TsrPayload = NULL; + CertPayload = NULL; + VerifiedAuthPayload = NULL; + Status = EFI_INVALID_PARAMETER; + + // + // Iterate payloads to find the SaPayload/ID/AUTH/TS Payload. + // + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_INIT) { + IdiPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_RSP) { + IdrPayload = IkePayload; + } + + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_SA) { + SaPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_AUTH) { + AuthPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_INIT) { + TsiPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_RSP) { + TsrPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_CERT) { + CertPayload = IkePayload; + } + } + + if ((SaPayload == NULL) || (AuthPayload == NULL) || (TsiPayload == NULL) || + (TsrPayload == NULL) || (CertPayload == NULL)) { + goto Exit; + } + if ((IdiPayload == NULL) && (IdrPayload == NULL)) { + goto Exit; + } + + // + // Check IkePacket Header is match the state + // + if (IkeSaSession->SessionCommon.IsInitiator) { + + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_RESPOND + // + if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) || + (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH)) { + goto Exit; + } + } else { + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_INIT + // + if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_INIT) || + (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH)) { + goto Exit; + } + } + + // + // Verify the Auth Payload. + // + VerifiedAuthPayload = Ikev2CertGenerateAuthPayload ( + IkeSaSession, + IkeSaSession->SessionCommon.IsInitiator ? IdrPayload:IdiPayload, + IKEV2_PAYLOAD_TYPE_SA, + TRUE, + NULL, + 0, + NULL, + 0 + ); + + if ((VerifiedAuthPayload != NULL) && + (!IpSecCryptoIoVerifySignDataByCertificate ( + CertPayload->PayloadBuf + sizeof (IKEV2_CERT), + CertPayload->PayloadSize - sizeof (IKEV2_CERT), + (UINT8 *)PcdGetPtr (PcdIpsecUefiCaFile), + PcdGet32 (PcdIpsecUefiCaFileSize), + VerifiedAuthPayload->PayloadBuf + sizeof (IKEV2_AUTH), + VerifiedAuthPayload->PayloadSize - sizeof (IKEV2_AUTH), + AuthPayload->PayloadBuf + sizeof (IKEV2_AUTH), + AuthPayload->PayloadSize - sizeof (IKEV2_AUTH) + ))) { + goto Exit; + } + + // + // 3. Parse the SA Payload to find out the cryptographic suite + // and fill in the SA paramse into CommonSession->SaParams. If no acceptable + // porposal found, return EFI_INVALID_PARAMETER. + // + if (!Ikev2ChildSaParseSaPayload (ChildSaSession, SaPayload, IkePacket->Header->Flags)) { + goto Exit; + } + + // + // 4. Parse TSi, TSr payloads. + // + if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != + ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId) && + (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0) && + (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0) + ) { + goto Exit; + } + + if (!IkeSaSession->SessionCommon.IsInitiator) { + // + //Todo:check the Port range. Only support any port and one certain port here. + // + ChildSaSession->ProtoId = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId; + ChildSaSession->LocalPort = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort; + ChildSaSession->RemotePort = ((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort; + // + // Association a SPD with this SA. + // + if (EFI_ERROR (Ikev2ChildSaAssociateSpdEntry (ChildSaSession))) { + goto Exit; + } + // + // Associate the IkeSaSession's SPD to the first ChildSaSession's SPD. + // + if (ChildSaSession->IkeSaSession->Spd == NULL) { + ChildSaSession->IkeSaSession->Spd = ChildSaSession->Spd; + Status = Ikev2ChildSaSessionSpdSelectorCreate (ChildSaSession); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + } else { + // + // Todo:check the Port range. + // + if ((((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) && + (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->RemotePort) + ) { + goto Exit; + } + if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) && + (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->LocalPort) + ) { + goto Exit; + } + // + // For the tunnel mode, it should add the vitual IP address into the SA's SPD Selector. + // + if (ChildSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + if (!ChildSaSession->IkeSaSession->SessionCommon.IsInitiator) { + // + // If it is tunnel mode, the UEFI part must be the initiator. + // + goto Exit; + } + // + // Get the Virtual IP address from the Tsi traffic selector. + // TODO: check the CFG reply payload + // + CopyMem ( + &ChildSaSession->SpdSelector->LocalAddress[0].Address, + TsiPayload->PayloadBuf + sizeof (IKEV2_TS) + sizeof (TRAFFIC_SELECTOR), + (ChildSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) ? + sizeof (EFI_IPv4_ADDRESS) : sizeof (EFI_IPv6_ADDRESS) + ); + } + } + + // + // 5. Generat keymats for IPsec protocol. + // + Status = Ikev2GenerateChildSaKeys (ChildSaSession, NULL); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (IkeSaSession->SessionCommon.IsInitiator) { + // + // 6. Change the state of IkeSaSession + // + IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateIkeSaEstablished); + IkeSaSession->SessionCommon.State = IkeStateIkeSaEstablished; + } + + Status = EFI_SUCCESS; + +Exit: + if (VerifiedAuthPayload != NULL) { + IkePayloadFree (VerifiedAuthPayload); + } + return Status; +} + +/** + Generates the DH Public Key. + + This generates the DH local public key and store it in the IKE SA Session's GxBuffer. + + @param[in] IkeSaSession Pointer to related IKE SA Session. + + @retval EFI_SUCCESS The operation succeeded. + @retval Others The operation failed. + +**/ +EFI_STATUS +Ikev2GenerateSaDhPublicKey ( + IN IKEV2_SA_SESSION *IkeSaSession + ) +{ + EFI_STATUS Status; + IKEV2_SESSION_KEYS *IkeKeys; + + IkeSaSession->IkeKeys = AllocateZeroPool (sizeof (IKEV2_SESSION_KEYS)); + if (IkeSaSession->IkeKeys == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IkeKeys = IkeSaSession->IkeKeys; + IkeKeys->DhBuffer = AllocateZeroPool (sizeof (IKEV2_DH_BUFFER)); + if (IkeKeys->DhBuffer == NULL) { + FreePool (IkeSaSession->IkeKeys); + return EFI_OUT_OF_RESOURCES; + } + + // + // Init DH with the certain DH Group Description. + // + IkeKeys->DhBuffer->GxSize = OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].Size >> 3; + IkeKeys->DhBuffer->GxBuffer = AllocateZeroPool (IkeKeys->DhBuffer->GxSize); + if (IkeKeys->DhBuffer->GxBuffer == NULL) { + FreePool (IkeKeys->DhBuffer); + FreePool (IkeSaSession->IkeKeys); + return EFI_OUT_OF_RESOURCES; + } + + // + // Get X PublicKey + // + Status = IpSecCryptoIoDhGetPublicKey ( + &IkeKeys->DhBuffer->DhContext, + OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].GroupGenerator, + OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].Size, + OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].Modulus, + IkeKeys->DhBuffer->GxBuffer, + &IkeKeys->DhBuffer->GxSize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error CPLKeyManGetKeyParam X public key error Status = %r\n", Status)); + + FreePool (IkeKeys->DhBuffer->GxBuffer); + + FreePool (IkeKeys->DhBuffer); + + FreePool (IkeSaSession->IkeKeys); + + return Status; + } + + IPSEC_DUMP_BUF ("DH Public Key (g^x) Dump", IkeKeys->DhBuffer->GxBuffer, IkeKeys->DhBuffer->GxSize); + + return EFI_SUCCESS; +} + +/** + Computes the DH Shared/Exchange Key. + + Given peer's public key, this function computes the exchanged common key and + stores it in the IKEv2 SA Session's GxyBuffer. + + @param[in] DhBuffer Pointer to buffer of peer's puliic key. + @param[in] KePayload Pointer to received key payload. + + @retval EFI_SUCCESS The operation succeeded. + @retval Otherwise The operation failed. + +**/ +EFI_STATUS +Ikev2GenerateSaDhComputeKey ( + IN IKEV2_DH_BUFFER *DhBuffer, + IN IKE_PAYLOAD *KePayload + ) +{ + EFI_STATUS Status; + IKEV2_KEY_EXCHANGE *Ke; + UINT8 *PubKey; + UINTN PubKeySize; + + Ke = (IKEV2_KEY_EXCHANGE *) KePayload->PayloadBuf; + PubKey = (UINT8 *) (Ke + 1); + PubKeySize = KePayload->PayloadSize - sizeof (IKEV2_KEY_EXCHANGE); + DhBuffer->GxySize = DhBuffer->GxSize; + DhBuffer->GxyBuffer = AllocateZeroPool (DhBuffer->GxySize); + if (DhBuffer->GxyBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get GxyBuf + // + Status = IpSecCryptoIoDhComputeKey ( + DhBuffer->DhContext, + PubKey, + PubKeySize, + DhBuffer->GxyBuffer, + &DhBuffer->GxySize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error CPLKeyManGetKeyParam Y session key error Status = %r\n", Status)); + + FreePool (DhBuffer->GxyBuffer); + + return Status; + } + + // + // Create GxyBuf. + // + DhBuffer->GySize = PubKeySize; + DhBuffer->GyBuffer = AllocateZeroPool (DhBuffer->GySize); + if (DhBuffer->GyBuffer == NULL) { + FreePool (DhBuffer->GxyBuffer); + + return Status; + } + + CopyMem (DhBuffer->GyBuffer, PubKey, DhBuffer->GySize); + + IPSEC_DUMP_BUF ("DH Public Key (g^y) Dump", DhBuffer->GyBuffer, DhBuffer->GySize); + IPSEC_DUMP_BUF ("DH Shared Key (g^xy) Dump", DhBuffer->GxyBuffer, DhBuffer->GxySize); + + return EFI_SUCCESS; +} + +/** + Generates the IKE SKEYSEED and seven other secrets. SK_d, SK_ai, SK_ar, SK_ei, SK_er, + SK_pi, SK_pr are keys for the furthure IKE exchange. + + @param[in] IkeSaSession Pointer to IKE SA Session. + @param[in] KePayload Pointer to Key payload used to generate the Key. + + @retval EFI_UNSUPPORTED If one or more Algorithm Id is not supported. + @retval EFI_OUT_OF_RESOURCES If there is no enough resource to be allocated to + meet the requirement. + @retval EFI_SUCCESS The operation succeeded. + +**/ +EFI_STATUS +Ikev2GenerateSaKeys ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *KePayload + ) +{ + EFI_STATUS Status; + IKEV2_SA_PARAMS *SaParams; + PRF_DATA_FRAGMENT Fragments[4]; + UINT64 InitiatorCookieNet; + UINT64 ResponderCookieNet; + UINT8 *KeyBuffer; + UINTN KeyBufferSize; + UINTN AuthAlgKeyLen; + UINTN EncryptAlgKeyLen; + UINTN IntegrityAlgKeyLen; + UINTN PrfAlgKeyLen; + UINT8 *OutputKey; + UINTN OutputKeyLength; + UINT8 *Digest; + UINTN DigestSize; + + Digest = NULL; + OutputKey = NULL; + KeyBuffer = NULL; + Status = EFI_SUCCESS; + + // + // Generate Gxy + // + Status = Ikev2GenerateSaDhComputeKey (IkeSaSession->IkeKeys->DhBuffer, KePayload); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Get the key length of Authenticaion, Encryption, PRF, and Integrity. + // + SaParams = IkeSaSession->SessionCommon.SaParams; + AuthAlgKeyLen = IpSecGetHmacDigestLength ((UINT8)SaParams->Prf); + EncryptAlgKeyLen = IpSecGetEncryptKeyLength ((UINT8)SaParams->EncAlgId); + IntegrityAlgKeyLen = IpSecGetHmacDigestLength ((UINT8)SaParams->IntegAlgId); + PrfAlgKeyLen = IpSecGetHmacDigestLength ((UINT8)SaParams->Prf); + + // + // If one or more algorithm is not support, return EFI_UNSUPPORTED. + // + if (AuthAlgKeyLen == 0 || + EncryptAlgKeyLen == 0 || + IntegrityAlgKeyLen == 0 || + PrfAlgKeyLen == 0 + ) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + + // + // Compute SKEYSEED = prf(Ni | Nr, g^ir) + // + KeyBufferSize = IkeSaSession->NiBlkSize + IkeSaSession->NrBlkSize; + KeyBuffer = AllocateZeroPool (KeyBufferSize); + if (KeyBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem (KeyBuffer, IkeSaSession->NiBlock, IkeSaSession->NiBlkSize); + CopyMem (KeyBuffer + IkeSaSession->NiBlkSize, IkeSaSession->NrBlock, IkeSaSession->NrBlkSize); + + Fragments[0].Data = IkeSaSession->IkeKeys->DhBuffer->GxyBuffer; + Fragments[0].DataSize = IkeSaSession->IkeKeys->DhBuffer->GxySize; + + DigestSize = IpSecGetHmacDigestLength ((UINT8)SaParams->Prf); + Digest = AllocateZeroPool (DigestSize); + + if (Digest == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + IpSecCryptoIoHmac ( + (UINT8)SaParams->Prf, + KeyBuffer, + KeyBufferSize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + Digest, + DigestSize + ); + + // + // {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr } = prf+ + // (SKEYSEED, Ni | Nr | SPIi | SPIr ) + // + Fragments[0].Data = IkeSaSession->NiBlock; + Fragments[0].DataSize = IkeSaSession->NiBlkSize; + Fragments[1].Data = IkeSaSession->NrBlock; + Fragments[1].DataSize = IkeSaSession->NrBlkSize; + InitiatorCookieNet = HTONLL (IkeSaSession->InitiatorCookie); + ResponderCookieNet = HTONLL (IkeSaSession->ResponderCookie); + Fragments[2].Data = (UINT8 *)(&InitiatorCookieNet); + Fragments[2].DataSize = sizeof (IkeSaSession->InitiatorCookie); + Fragments[3].Data = (UINT8 *)(&ResponderCookieNet); + Fragments[3].DataSize = sizeof (IkeSaSession->ResponderCookie); + + IPSEC_DUMP_BUF (">>> NiBlock", IkeSaSession->NiBlock, IkeSaSession->NiBlkSize); + IPSEC_DUMP_BUF (">>> NrBlock", IkeSaSession->NrBlock, IkeSaSession->NrBlkSize); + IPSEC_DUMP_BUF (">>> InitiatorCookie", (UINT8 *)&IkeSaSession->InitiatorCookie, sizeof(UINT64)); + IPSEC_DUMP_BUF (">>> ResponderCookie", (UINT8 *)&IkeSaSession->ResponderCookie, sizeof(UINT64)); + + OutputKeyLength = PrfAlgKeyLen + + 2 * EncryptAlgKeyLen + + 2 * AuthAlgKeyLen + + 2 * IntegrityAlgKeyLen; + OutputKey = AllocateZeroPool (OutputKeyLength); + if (OutputKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Generate Seven Keymates. + // + Status = Ikev2SaGenerateKey ( + (UINT8)SaParams->Prf, + Digest, + DigestSize, + OutputKey, + OutputKeyLength, + Fragments, + 4 + ); + if (EFI_ERROR(Status)) { + goto Exit; + } + + // + // Save the seven keys into KeySession. + // First, SK_d + // + IkeSaSession->IkeKeys->SkdKey = AllocateZeroPool (PrfAlgKeyLen); + if (IkeSaSession->IkeKeys->SkdKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkdKeySize = PrfAlgKeyLen; + CopyMem (IkeSaSession->IkeKeys->SkdKey, OutputKey, PrfAlgKeyLen); + + IPSEC_DUMP_BUF (">>> SK_D Key", IkeSaSession->IkeKeys->SkdKey, PrfAlgKeyLen); + + // + // Second, Sk_ai + // + IkeSaSession->IkeKeys->SkAiKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (IkeSaSession->IkeKeys->SkAiKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkAiKeySize = IntegrityAlgKeyLen; + CopyMem (IkeSaSession->IkeKeys->SkAiKey, OutputKey + PrfAlgKeyLen, IntegrityAlgKeyLen); + + IPSEC_DUMP_BUF (">>> SK_Ai Key", IkeSaSession->IkeKeys->SkAiKey, IkeSaSession->IkeKeys->SkAiKeySize); + + // + // Third, Sk_ar + // + IkeSaSession->IkeKeys->SkArKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (IkeSaSession->IkeKeys->SkArKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkArKeySize = IntegrityAlgKeyLen; + CopyMem ( + IkeSaSession->IkeKeys->SkArKey, + OutputKey + PrfAlgKeyLen + IntegrityAlgKeyLen, + IntegrityAlgKeyLen + ); + + IPSEC_DUMP_BUF (">>> SK_Ar Key", IkeSaSession->IkeKeys->SkArKey, IkeSaSession->IkeKeys->SkArKeySize); + + // + // Fourth, Sk_ei + // + IkeSaSession->IkeKeys->SkEiKey = AllocateZeroPool (EncryptAlgKeyLen); + if (IkeSaSession->IkeKeys->SkEiKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkEiKeySize = EncryptAlgKeyLen; + + CopyMem ( + IkeSaSession->IkeKeys->SkEiKey, + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen, + EncryptAlgKeyLen + ); + IPSEC_DUMP_BUF ( + ">>> SK_Ei Key", + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen, + EncryptAlgKeyLen + ); + + // + // Fifth, Sk_er + // + IkeSaSession->IkeKeys->SkErKey = AllocateZeroPool (EncryptAlgKeyLen); + if (IkeSaSession->IkeKeys->SkErKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkErKeySize = EncryptAlgKeyLen; + + CopyMem ( + IkeSaSession->IkeKeys->SkErKey, + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + EncryptAlgKeyLen, + EncryptAlgKeyLen + ); + IPSEC_DUMP_BUF ( + ">>> SK_Er Key", + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + EncryptAlgKeyLen, + EncryptAlgKeyLen + ); + + // + // Sixth, Sk_pi + // + IkeSaSession->IkeKeys->SkPiKey = AllocateZeroPool (AuthAlgKeyLen); + if (IkeSaSession->IkeKeys->SkPiKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkPiKeySize = AuthAlgKeyLen; + + CopyMem ( + IkeSaSession->IkeKeys->SkPiKey, + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + 2 * EncryptAlgKeyLen, + AuthAlgKeyLen + ); + IPSEC_DUMP_BUF ( + ">>> SK_Pi Key", + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + 2 * EncryptAlgKeyLen, + AuthAlgKeyLen + ); + + // + // Seventh, Sk_pr + // + IkeSaSession->IkeKeys->SkPrKey = AllocateZeroPool (AuthAlgKeyLen); + if (IkeSaSession->IkeKeys->SkPrKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkPrKeySize = AuthAlgKeyLen; + + CopyMem ( + IkeSaSession->IkeKeys->SkPrKey, + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + 2 * EncryptAlgKeyLen + AuthAlgKeyLen, + AuthAlgKeyLen + ); + IPSEC_DUMP_BUF ( + ">>> SK_Pr Key", + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + 2 * EncryptAlgKeyLen + AuthAlgKeyLen, + AuthAlgKeyLen + ); + + +Exit: + if (Digest != NULL) { + FreePool (Digest); + } + if (KeyBuffer != NULL) { + FreePool (KeyBuffer); + } + if (OutputKey != NULL) { + FreePool (OutputKey); + } + + if (EFI_ERROR(Status)) { + if (IkeSaSession->IkeKeys->SkdKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkdKey); + } + if (IkeSaSession->IkeKeys->SkAiKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkAiKey); + } + if (IkeSaSession->IkeKeys->SkArKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkArKey); + } + if (IkeSaSession->IkeKeys->SkEiKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkEiKey); + } + if (IkeSaSession->IkeKeys->SkErKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkErKey); + } + if (IkeSaSession->IkeKeys->SkPiKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkPiKey); + } + if (IkeSaSession->IkeKeys->SkPrKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkPrKey); + } + } + + + return Status; +} + +/** + Generates the Keys for the furthure IPsec Protocol. + + @param[in] ChildSaSession Pointer to IKE Child SA Session. + @param[in] KePayload Pointer to Key payload used to generate the Key. + + @retval EFI_UNSUPPORTED If one or more Algorithm Id is not supported. + @retval EFI_SUCCESS The operation succeeded. + +**/ +EFI_STATUS +Ikev2GenerateChildSaKeys ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession, + IN IKE_PAYLOAD *KePayload + ) +{ + EFI_STATUS Status; + IKEV2_SA_PARAMS *SaParams; + PRF_DATA_FRAGMENT Fragments[3]; + UINTN EncryptAlgKeyLen; + UINTN IntegrityAlgKeyLen; + UINT8* OutputKey; + UINTN OutputKeyLength; + + Status = EFI_SUCCESS; + OutputKey = NULL; + + if (KePayload != NULL) { + // + // Generate Gxy + // + Status = Ikev2GenerateSaDhComputeKey (ChildSaSession->DhBuffer, KePayload); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Fragments[0].Data = ChildSaSession->DhBuffer->GxyBuffer; + Fragments[0].DataSize = ChildSaSession->DhBuffer->GxySize; + } + + Fragments[1].Data = ChildSaSession->NiBlock; + Fragments[1].DataSize = ChildSaSession->NiBlkSize; + Fragments[2].Data = ChildSaSession->NrBlock; + Fragments[2].DataSize = ChildSaSession->NrBlkSize; + + // + // Get the key length of Authenticaion, Encryption, PRF, and Integrity. + // + SaParams = ChildSaSession->SessionCommon.SaParams; + EncryptAlgKeyLen = IpSecGetEncryptKeyLength ((UINT8)SaParams->EncAlgId); + IntegrityAlgKeyLen = IpSecGetHmacDigestLength ((UINT8)SaParams->IntegAlgId); + OutputKeyLength = 2 * EncryptAlgKeyLen + 2 * IntegrityAlgKeyLen; + + if ((EncryptAlgKeyLen == 0) || (IntegrityAlgKeyLen == 0)) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + + // + // + // If KePayload is not NULL, calculate KEYMAT = prf+(SK_d, g^ir (new) | Ni | Nr ), + // otherwise, KEYMAT = prf+(SK_d, Ni | Nr ) + // + OutputKey = AllocateZeroPool (OutputKeyLength); + if (OutputKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Derive Key from the SkdKey Buffer. + // + Status = Ikev2SaGenerateKey ( + (UINT8)ChildSaSession->IkeSaSession->SessionCommon.SaParams->Prf, + ChildSaSession->IkeSaSession->IkeKeys->SkdKey, + ChildSaSession->IkeSaSession->IkeKeys->SkdKeySize, + OutputKey, + OutputKeyLength, + KePayload == NULL ? &Fragments[1] : Fragments, + KePayload == NULL ? 2 : 3 + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Copy KEYMATE (SK_ENCRYPT_i | SK_ENCRYPT_r | SK_INTEG_i | SK_INTEG_r) to + // ChildKeyMates. + // + if (!ChildSaSession->SessionCommon.IsInitiator) { + + // + // Initiator Encryption Key + // + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncAlgoId = (UINT8)SaParams->EncAlgId; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey = AllocateZeroPool (EncryptAlgKeyLen); + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey, + OutputKey, + EncryptAlgKeyLen + ); + + // + // Initiator Authentication Key + // + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthAlgoId = (UINT8)SaParams->IntegAlgId; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey, + OutputKey + EncryptAlgKeyLen, + IntegrityAlgKeyLen + ); + + // + // Responder Encrypt Key + // + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncAlgoId = (UINT8)SaParams->EncAlgId; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey = AllocateZeroPool (EncryptAlgKeyLen); + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey, + OutputKey + EncryptAlgKeyLen + IntegrityAlgKeyLen, + EncryptAlgKeyLen + ); + + // + // Responder Authentication Key + // + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthAlgoId = (UINT8)SaParams->IntegAlgId; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey, + OutputKey + 2 * EncryptAlgKeyLen + IntegrityAlgKeyLen, + IntegrityAlgKeyLen + ); + } else { + // + // Initiator Encryption Key + // + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncAlgoId = (UINT8)SaParams->EncAlgId; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey = AllocateZeroPool (EncryptAlgKeyLen); + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey, + OutputKey, + EncryptAlgKeyLen + ); + + // + // Initiator Authentication Key + // + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthAlgoId = (UINT8)SaParams->IntegAlgId; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey, + OutputKey + EncryptAlgKeyLen, + IntegrityAlgKeyLen + ); + + // + // Responder Encryption Key + // + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncAlgoId = (UINT8)SaParams->EncAlgId; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey = AllocateZeroPool (EncryptAlgKeyLen); + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey, + OutputKey + EncryptAlgKeyLen + IntegrityAlgKeyLen, + EncryptAlgKeyLen + ); + + // + // Responder Authentication Key + // + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthAlgoId = (UINT8)SaParams->IntegAlgId; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey, + OutputKey + 2 * EncryptAlgKeyLen + IntegrityAlgKeyLen, + IntegrityAlgKeyLen + ); + } + + IPSEC_DUMP_BUF ( + " >>> Local Encryption Key", + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey, + EncryptAlgKeyLen + ); + IPSEC_DUMP_BUF ( + " >>> Remote Encryption Key", + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey, + EncryptAlgKeyLen + ); + IPSEC_DUMP_BUF ( + " >>> Local Authentication Key", + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey, + IntegrityAlgKeyLen + ); + IPSEC_DUMP_BUF ( + " >>> Remote Authentication Key", + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey, + IntegrityAlgKeyLen + ); + + + +Exit: + if (EFI_ERROR (Status)) { + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey); + } + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey); + } + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey); + } + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey); + } + } + + if (OutputKey != NULL) { + FreePool (OutputKey); + } + + return EFI_SUCCESS; +} + +GLOBAL_REMOVE_IF_UNREFERENCED IKEV2_PACKET_HANDLER mIkev2Initial[][2] = { + { //PSK + { // IKEV2_INIT + Ikev2InitPskParser, + Ikev2InitPskGenerator + }, + { //IKEV2_AUTH + Ikev2AuthPskParser, + Ikev2AuthPskGenerator + } + }, + { // CERT + { // IKEV2_INIT + Ikev2InitCertParser, + Ikev2InitCertGenerator + }, + { // IKEV2_AUTH + Ikev2AuthCertParser, + Ikev2AuthCertGenerator + }, + }, +}; diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c b/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c new file mode 100644 index 0000000000..2ca7f3c63c --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c @@ -0,0 +1,2802 @@ +/** @file + The Common operations used by IKE Exchange Process. + + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2010 - 2017, 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 "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)); + if (IkeSaSession == NULL) { + return 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; + + // + // Generate an new SPI. + // + Status = IkeGenerateSpi (IkeSaSession, &(ChildSaSession->LocalPeerSpi)); + if (EFI_ERROR (Status)) { + FreePool (ChildSaSession); + return NULL; + } + + 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; + 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. + // + 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; + IPSEC_PRIVATE_DATA *Private; + + if (IkeSaSession == NULL) { + return EFI_NOT_FOUND; + } + + IsLocalFound = FALSE; + IsRemoteFound = FALSE; + ChildSession = NULL; + LocalSelector = NULL; + RemoteSelector = NULL; + + Private = IkeSaSession->SessionCommon.Private; + + // + // 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); + if (Selector == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + while (1) { + Status = EfiIpSecConfigGetNextSelector ( + &Private->IpSecConfig, + IPsecConfigDataTypeSad, + &SelectorSize, + Selector + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (Selector); + + Selector = AllocateZeroPool (SelectorSize); + if (Selector == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + 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); + if (RemoteSelector == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + 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); + if (LocalSelector == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + 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)); + if (ProposalData->Spi == NULL) { + FreePool (SaData); + return 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)); + if (ProposalData->Spi == NULL) { + FreePool (((IKEV2_PROPOSAL_DATA *) (SaData + 1))->Spi); + FreePool (SaData); + return 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. + + @retval EFI_SUCCESS The operation complete successfully. + @retval EFI_OUT_OF_RESOURCES If the required resource can't be allocated. + +**/ +EFI_STATUS +Ikev2ChildSaSessionSpdSelectorCreate ( + IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (ChildSaSession->Spd != NULL && ChildSaSession->Spd->Selector != NULL) { + if (ChildSaSession->SpdSelector == NULL) { + ChildSaSession->SpdSelector = AllocateZeroPool (sizeof (EFI_IPSEC_SPD_SELECTOR)); + if (ChildSaSession->SpdSelector == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + } + 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 + ); + if (ChildSaSession->SpdSelector->RemoteAddress == NULL) { + Status = EFI_OUT_OF_RESOURCES; + + FreePool (ChildSaSession->SpdSelector); + + return Status; + } + + ChildSaSession->SpdSelector->LocalAddress = AllocateCopyPool ( + ChildSaSession->Spd->Selector->LocalAddressCount * + sizeof (EFI_IP_ADDRESS_INFO), + ChildSaSession->Spd->Selector->LocalAddress + ); + if (ChildSaSession->SpdSelector->LocalAddress == NULL) { + Status = EFI_OUT_OF_RESOURCES; + + FreePool (ChildSaSession->SpdSelector->RemoteAddress); + + FreePool (ChildSaSession->SpdSelector); + + return Status; + } + + ChildSaSession->SpdSelector->RemoteAddressCount = ChildSaSession->Spd->Selector->RemoteAddressCount; + ChildSaSession->SpdSelector->LocalAddressCount = ChildSaSession->Spd->Selector->LocalAddressCount; + } + + return Status; +} + +/** + 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); + if (ChildSaSession == NULL) { + return 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. + // + if (EFI_ERROR (Ikev2ChildSaSessionSpdSelectorCreate (ChildSaSession))) { + Ikev2ChildSaSessionFree (ChildSaSession); + return NULL; + } + + // + // Copy first NiBlock and NrBlock to ChildSa Session + // + ChildSaSession->NiBlock = AllocateZeroPool (IkeSaSession->NiBlkSize); + if (ChildSaSession->NiBlock == NULL) { + Ikev2ChildSaSessionFree (ChildSaSession); + return NULL; + } + + ChildSaSession->NiBlkSize = IkeSaSession->NiBlkSize; + CopyMem (ChildSaSession->NiBlock, IkeSaSession->NiBlock, IkeSaSession->NiBlkSize); + + ChildSaSession->NrBlock = AllocateZeroPool (IkeSaSession->NrBlkSize); + if (ChildSaSession->NrBlock == NULL) { + Ikev2ChildSaSessionFree (ChildSaSession); + return 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)); + if (IkeSaSession->SessionCommon.SaParams == NULL) { + return FALSE; + } + + 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); + if (IkeSaSession->SaData == NULL) { + FreePool (IkeSaSession->SessionCommon.SaParams); + return FALSE; + } + + 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)); + if (IkeSaSession->SessionCommon.SaParams == NULL) { + return FALSE; + } + + 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)); + if (ChildSaSession->SessionCommon.SaParams == NULL) { + return FALSE; + } + + 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); + if (ChildSaSession->SaData == NULL) { + FreePool (ChildSaSession->SessionCommon.SaParams); + return FALSE; + } + + 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 + ); + if (((IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1))->Spi == NULL) { + FreePool (ChildSaSession->SessionCommon.SaParams); + + FreePool (ChildSaSession->SaData ); + + return FALSE; + } + + 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)); + if (ChildSaSession->SessionCommon.SaParams == NULL) { + return FALSE; + } + + 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. + If the authentication algorithm given by HashAlgId + cannot be found. + @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); + if (AuthKeyLength == 0) { + return EFI_INVALID_PARAMETER; + } + + 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); + if (LocalFragments[1].Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + 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); + if (LocalFragments[0].Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + 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; +} + diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h b/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h new file mode 100644 index 0000000000..319b6cb32c --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h @@ -0,0 +1,1134 @@ +/** @file + The interfaces of IKE/Child session operations and payload related operations + used by IKE Exchange Process. + + 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. + +**/ + +#ifndef _IKE_V2_UTILITY_H_ +#define _IKE_V2_UTILITY_H_ + +#include "Ikev2.h" +#include "IkeCommon.h" +#include "IpSecCryptIo.h" + +#include + +#define IKEV2_SUPPORT_ENCRYPT_ALGORITHM_NUM 2 +#define IKEV2_SUPPORT_PRF_ALGORITHM_NUM 1 +#define IKEV2_SUPPORT_DH_ALGORITHM_NUM 2 +#define IKEV2_SUPPORT_AUTH_ALGORITHM_NUM 1 + +/** + 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. + +**/ +IKEV2_SA_SESSION * +Ikev2SaSessionAlloc ( + IN IPSEC_PRIVATE_DATA *Private, + IN IKE_UDP_SERVICE *UdpService + ); + +/** + Register Establish IKEv2 SA into Private->Ikev2EstablishedList. + + @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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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. + +**/ +IKEV2_SA_SESSION * +Ikev2SaSessionRemove ( + IN LIST_ENTRY *SaSessionList, + IN EFI_IP_ADDRESS *RemotePeerIp + ); + + +/** + 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 + ); + +/** + 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 + ); + +/** + Free specified IKEV2 SA Session. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION to be freed. + +**/ +VOID +Ikev2SaSessionFree ( + IN IKEV2_SA_SESSION *IkeSaSession + ); + +/** + 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 + ); + +/** + Increase the MessageID in IkeSaSession. + + @param[in] IkeSaSession Pointer to a specified IKEV2_SA_SESSION. + +**/ +VOID +Ikev2SaSessionIncreaseMessageId ( + IN IKEV2_SA_SESSION *IkeSaSession + ); + +/** + Allocate Momery 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. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionAlloc ( + IN IKE_UDP_SERVICE *UdpService, + IN IKEV2_SA_SESSION *IkeSaSession + ); + +/** + 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 + ); + +/** + 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. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionLookupBySpi ( + IN LIST_ENTRY *SaSessionList, + IN UINT32 Spi + ); + +/** + 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. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionLookupByMid ( + IN LIST_ENTRY *SaSessionList, + IN UINT32 Mid + ); + +/** + 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 + ); + +/** + Remove the IKEV2_CHILD_SA_SESSION from IkeSaSessionList. + + @param[in] SaSessionList The SA Session List to be iterated. + @param[in] Spi Spi used to identify 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. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionRemove ( + IN LIST_ENTRY *SaSessionList, + IN UINT32 Spi, + IN UINT8 ListType + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Free the specified DhBuffer. + + @param[in] DhBuffer Pointer to IKEV2_DH_BUFFER to be freed. + +**/ +VOID +Ikev2DhBufferFree ( + IN IKEV2_DH_BUFFER *DhBuffer + ); + +/** + Delete the specified established Child SA. + + This function delete the Child SA directly and dont 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 + ); + +/** + 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 + ); + +/** + This function finds the SPI from Create Child Sa Exchange Packet. + + @param[in] IkePacket Pointer to IKE_PACKET to be searched. + + @retval SPI number. + +**/ +UINT32 +Ikev2ChildExchangeRekeySpi( + IN IKE_PACKET *IkePacket + ); + + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Store the SA into SAD. + + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION. + +**/ +VOID +Ikev2StoreSaData ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession + ); + +/** + Routine process before the payload decoding. + + @param[in] SessionCommon Pointer to ChildSa SessionCommon. + @param[in] PayloadBuf Pointer to the payload. + @param[in] PayloadSize Size of PayloadBuf in byte. + @param[in] PayloadType Type of Payload. + +**/ +VOID +Ikev2ChildSaBeforeDecodePayload ( + IN UINT8 *SessionCommon, + IN UINT8 *PayloadBuf, + IN UINTN PayloadSize, + IN UINT8 PayloadType + ); + +/** + Routine Process after the encode payload. + + @param[in] SessionCommon Pointer to ChildSa SessionCommon. + @param[in] PayloadBuf Pointer to the payload. + @param[in] PayloadSize Size of PayloadBuf in byte. + @param[in] PayloadType Type of Payload. + +**/ +VOID +Ikev2ChildSaAfterEncodePayload ( + IN UINT8 *SessionCommon, + IN UINT8 *PayloadBuf, + IN UINTN PayloadSize, + IN UINT8 PayloadType + ); + +/** + Generate Ikev2 SA payload according to SessionSaData + + @param[in] SessionSaData The data used in SA payload. + @param[in] NextPayload The payload type presented in NextPayload field of + SA Payload header. + @param[in] Type The SA type. It MUST be neither (1) for IKE_SA or + (2) for CHILD_SA or (3) for INFO. + + @retval a Pointer to SA IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateSaPayload ( + IN IKEV2_SA_DATA *SessionSaData, + IN UINT8 NextPayload, + IN IKE_SESSION_TYPE Type + ); + +/** + Generate a ID payload. + + @param[in] CommonSession Pointer to IKEV2_SESSION_COMMON related to ID payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + + @retval Pointer to ID IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateIdPayload ( + IN IKEV2_SESSION_COMMON *CommonSession, + IN UINT8 NextPayload + ); + +/** + Generate a ID payload. + + @param[in] CommonSession Pointer to IKEV2_SESSION_COMMON related to ID payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + @param[in] InCert Pointer to the Certificate which distinguished name + will be added into the Id payload. + @param[in] CertSize Size of the Certificate. + + @retval Pointer to ID IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCertIdPayload ( + IN IKEV2_SESSION_COMMON *CommonSession, + IN UINT8 NextPayload, + IN UINT8 *InCert, + IN UINTN CertSize + ); + +/** + Generate a Nonce payload contenting the input parameter NonceBuf. + + @param[in] NonceBuf The nonce buffer content the whole Nonce payload block + except the payload header. + @param[in] NonceSize The buffer size of the NonceBuf + @param[in] NextPayload The payload type presented in the NextPayload field + of Nonce Payload header. + + @retval Pointer to Nonce IKE paload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateNoncePayload ( + IN UINT8 *NonceBuf, + IN UINTN NonceSize, + IN UINT8 NextPayload + ); + +/** + Generate the Notify payload. + + Since the structure of Notify payload which defined in RFC 4306 is simple, so + there is no internal data structure for Notify payload. This function generate + Notify payload defined in RFC 4306, but all the fields in this payload are still + in host order and need call Ikev2EncodePayload() to convert those fields from + the host order to network order beforing sending it. + + @param[in] ProtocolId The protocol type ID. For IKE_SA it MUST be one (1). + For IPsec SAs it MUST be neither (2) for AH or (3) + for ESP. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Notify payload. + @param[in] SpiSize Size of the SPI in SPI size field of the Notify Payload. + @param[in] MessageType The message type in NotifyMessageType field of the + Notify Payload. + @param[in] SpiBuf Pointer to buffer contains the SPI value. + @param[in] NotifyData Pointer to buffer contains the notification data. + @param[in] NotifyDataSize The size of NotifyData in bytes. + + + @retval Pointer to IKE Notify Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateNotifyPayload ( + IN UINT8 ProtocolId, + IN UINT8 NextPayload, + IN UINT8 SpiSize, + IN UINT16 MessageType, + IN UINT8 *SpiBuf, + IN UINT8 *NotifyData, + IN UINTN NotifyDataSize + ); + +/** + Generate the Delete payload. + + Since the structure of Delete payload which defined in RFC 4306 is simple, + there is no internal data structure for Delete payload. This function generate + Delete payload defined in RFC 4306, but all the fields in this payload are still + in host order and need call Ikev2EncodePayload() to convert those fields from + the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used of Delete payload generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] SpiSize Size of the SPI in SPI size field of the Delete Payload. + @param[in] SpiNum Number of SPI in NumofSPIs field of the Delete Payload. + @param[in] SpiBuf Pointer to buffer contains the SPI value. + + @retval Pointer to IKE Delete Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateDeletePayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 SpiSize, + IN UINT16 SpiNum, + IN UINT8 *SpiBuf + ); + +/** + Generate the Configuration payload. + + This function generates a configuration payload defined in RFC 4306, but all the + fields in this payload are still in host order and need call Ikev2EncodePayload() + to convert those fields from the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used for Delete payload + generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] CfgType The attribute type in the Configuration attribute. + + @retval Pointer to IKE CP Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCpPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 CfgType + ); + +/** + Generate a Authentication Payload. + + This function is used for both Authentication generation and verification. When the + IsVerify is TRUE, it create a Auth Data for verification. This function choose the + related IKE_SA_INIT Message for Auth data creation according to the IKE Session's type + and the value of IsVerify parameter. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to. + @param[in] IdPayload Pointer to the ID payload to be used for Authentication + payload generation. + @param[in] NextPayload The type filled into the Authentication Payload next + payload field. + @param[in] IsVerify If it is TURE, the Authentication payload is used for + verification. + + @return pointer to IKE Authentication payload for pre-shard key method. + +**/ +IKE_PAYLOAD * +Ikev2PskGenerateAuthPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *IdPayload, + IN UINT8 NextPayload, + IN BOOLEAN IsVerify + ); + +/** + Generate a Authentication Payload for Certificate Auth method. + + This function has two functions. One is creating a local Authentication + Payload for sending and other is creating the remote Authentication data + for verification when the IsVerify is TURE. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to. + @param[in] IdPayload Pointer to the ID payload to be used for Authentication + payload generation. + @param[in] NextPayload The type filled into the Authentication Payload + next payload field. + @param[in] IsVerify If it is TURE, the Authentication payload is used + for verification. + @param[in] UefiPrivateKey Pointer to the UEFI private key. Ignore it when + verify the authenticate payload. + @param[in] UefiPrivateKeyLen The size of UefiPrivateKey in bytes. Ignore it + when verify the authenticate payload. + @param[in] UefiKeyPwd Pointer to the password of UEFI private key. + Ignore it when verify the authenticate payload. + @param[in] UefiKeyPwdLen The size of UefiKeyPwd in bytes.Ignore it when + verify the authenticate payload. + + @return pointer to IKE Authentication payload for certification method. + +**/ +IKE_PAYLOAD * +Ikev2CertGenerateAuthPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *IdPayload, + IN UINT8 NextPayload, + IN BOOLEAN IsVerify, + IN UINT8 *UefiPrivateKey, + IN UINTN UefiPrivateKeyLen, + IN UINT8 *UefiKeyPwd, + IN UINTN UefiKeyPwdLen + ); + +/** + Generate TS payload. + + This function generates TSi or TSr payload according to type of next payload. + If the next payload is Responder TS, gereate TSi Payload. Otherwise, generate + TSr payload + + @param[in] ChildSa Pointer to IKEV2_CHILD_SA_SESSION related to this TS payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + @param[in] IsTunnel It indicates that if the Ts Payload is after the CP payload. + If yes, it means the Tsi and Tsr payload should be with + Max port range and address range and protocol is marked + as zero. + + @retval Pointer to Ts IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateTsPayload ( + IN IKEV2_CHILD_SA_SESSION *ChildSa, + IN UINT8 NextPayload, + IN BOOLEAN IsTunnel + ); + +/** + Parser the Notify Cookie payload. + + This function parses the Notify Cookie payload.If the Notify ProtocolId is not + IPSEC_PROTO_ISAKMP or if the SpiSize is not zero or if the MessageType is not + the COOKIE, return EFI_INVALID_PARAMETER. + + @param[in] IkeNCookie Pointer to the IKE_PAYLOAD which contians the + Notify Cookie payload. + the Notify payload. + @param[in, out] IkeSaSession Pointer to the relevant IKE SA Session. + + @retval EFI_SUCCESS The Notify Cookie Payload is valid. + @retval EFI_INVALID_PARAMETER The Notify Cookie Payload is invalid. + @retval EFI_OUT_OF_RESOURCE The required resource can't be allocated. + +**/ +EFI_STATUS +Ikev2ParserNotifyCookiePayload ( + IN IKE_PAYLOAD *IkeNCookie, + IN OUT IKEV2_SA_SESSION *IkeSaSession + ); + +/** + Generate the Certificate payload or Certificate Request Payload. + + Since the Certificate Payload structure is same with Certificate Request Payload, + the only difference is that one contains the Certificate Data, other contains + the acceptable certificateion CA. This function generate Certificate payload + or Certificate Request Payload defined in RFC 4306, but all the fields + in the payload are still in host order and need call Ikev2EncodePayload() + to convert those fields from the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used of Delete payload + generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] Certificate Pointer of buffer contains the certification data. + @param[in] CertificateLen The length of Certificate in byte. + @param[in] EncodeType Specified the Certificate Encodeing which is defined + in RFC 4306. + @param[in] IsRequest To indicate create Certificate Payload or Certificate + Request Payload. If it is TURE, create Certificate + Request Payload. Otherwise, create Certificate Payload. + + @retval a Pointer to IKE Payload whose payload buffer containing the Certificate + payload or Certificated Request payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCertificatePayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 *Certificate, + IN UINTN CertificateLen, + IN UINT8 EncodeType, + IN BOOLEAN IsRequest + ); + +/** + General interface of payload encoding. + + This function encode the internal data structure into payload which + is defined in RFC 4306. The IkePayload->PayloadBuf used to store both the input + payload and converted payload. Only the SA payload use the interal structure + to store the attribute. Other payload use structure which is same with the RFC + defined, for this kind payloads just do host order to network order change of + some fields. + + @param[in] SessionCommon Pointer to IKE Session Common used to encode the payload. + @param[in, out] IkePayload Pointer to IKE payload to be encode as input, and + store the encoded result as output. + + @retval EFI_INVALID_PARAMETER Meet error when encode the SA payload. + @retval EFI_SUCCESS Encode successfully. + +**/ +EFI_STATUS +Ikev2EncodePayload ( + IN UINT8 *SessionCommon, + IN OUT IKE_PAYLOAD *IkePayload + ); + +/** + The general interface of decode Payload. + + This function convert the received Payload into internal structure. + + @param[in] SessionCommon Pointer to IKE Session Common to use for decoding. + @param[in, out] IkePayload Pointer to IKE payload to be decode as input, and + store the decoded result as output. + + @retval EFI_INVALID_PARAMETER Meet error when decode the SA payload. + @retval EFI_SUCCESS Decode successfully. + +**/ +EFI_STATUS +Ikev2DecodePayload ( + IN UINT8 *SessionCommon, + IN OUT IKE_PAYLOAD *IkePayload + ); + +/** + Decrypt IKE packet. + + This function decrpt the Encrypted IKE packet and put the result into IkePacket->PayloadBuf. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON containing + some parameter used during decrypting. + @param[in, out] IkePacket Point to IKE_PACKET to be decrypted as input, + and the decrypted reslult as output. + @param[in, out] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supportted. + + @retval EFI_INVALID_PARAMETER If the IKE packet length is zero or the + IKE packet length is not Algorithm Block Size + alignment. + @retval EFI_SUCCESS Decrypt IKE packet successfully. + +**/ +EFI_STATUS +Ikev2DecryptPacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN OUT UINTN IkeType + ); + +/** + Encrypt IKE packet. + + This function encrypt IKE packet before sending it. The Encrypted IKE packet + is put in to IKEV2 Encrypted Payload. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the IKE packet. + @param[in, out] IkePacket Pointer to IKE packet to be encrypted. + + @retval EFI_SUCCESS Operation is successful. + @retval Others OPeration is failed. + +**/ +EFI_STATUS +Ikev2EncryptPacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket + ); + +/** + Encode the IKE packet. + + This function put all Payloads into one payload then encrypt it if needed. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON containing + some parameter used during IKE packet encoding. + @param[in, out] IkePacket Pointer to IKE_PACKET to be encoded as input, + and the encoded reslult as output. + @param[in] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supportted. + + @retval EFI_SUCCESS Encode IKE packet successfully. + @retval Otherwise Encode IKE packet failed. + +**/ +EFI_STATUS +Ikev2EncodePacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN UINTN IkeType + ); + +/** + Decode the IKE packet. + + This function first decrypts the IKE packet if needed , then separats the whole + IKE packet from the IkePacket->PayloadBuf into IkePacket payload list. + + @param[in] SessionCommon Pointer to IKEV1_SESSION_COMMON containing + some parameter used by IKE packet decoding. + @param[in, out] IkePacket The IKE Packet to be decoded on input, and + the decoded result on return. + @param[in] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supportted. + + @retval EFI_SUCCESS The IKE packet is decoded successfull. + @retval Otherwise The IKE packet decoding is failed. + +**/ +EFI_STATUS +Ikev2DecodePacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN UINTN IkeType + ); + +/** + Save some useful payloads after accepting the Packet. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the operation. + @param[in] IkePacket Pointer to received IkePacet. + @param[in] IkeType The type used to indicate it is in IkeSa or ChildSa or Info + exchange. + +**/ +VOID +Ikev2OnPacketAccepted ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN IKE_PACKET *IkePacket, + IN UINT8 IkeType + ); + +/** + Send out IKEV2 packet. + + @param[in] IkeUdpService Pointer to IKE_UDP_SERVICE used to send the IKE packet. + @param[in] SessionCommon Pointer to IKEV1_SESSION_COMMON related to the IKE packet. + @param[in] IkePacket Pointer to IKE_PACKET to be sent out. + @param[in] IkeType The type of IKE to point what's kind of the IKE + packet is to be sent out. IKE_SA_TYPE, IKE_INFO_TYPE + and IKE_CHILD_TYPE are supportted. + + @retval EFI_SUCCESS The operation complete successfully. + @retval Otherwise The operation is failed. + +**/ +EFI_STATUS +Ikev2SendIkePacket ( + IN IKE_UDP_SERVICE *IkeUdpService, + IN UINT8 *SessionCommon, + IN IKE_PACKET *IkePacket, + IN UINTN IkeType + ); + +/** + Callback function for 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 time out event. + @param[in] Context Pointer to data passed by caller. + +**/ +VOID +EFIAPI +Ikev2LifetimeNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + 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 + ); + +/** + Generate a Key Exchange payload according to the DH group type and save the + public Key into IkeSaSession IkeKey field. + + @param[in, out] IkeSaSession Pointer of the IKE_SA_SESSION. + @param[in] NextPayload The payload type presented in the NextPayload field of Key + Exchange Payload header. + + @retval Pointer to Key IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateKePayload ( + IN OUT IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ) ; + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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. + + @retval EFI_SUCCESS The operation complete successfully. + @retval EFI_OUT_OF_RESOURCES If the required resource can't be allocated. + +**/ +EFI_STATUS +Ikev2ChildSaSessionSpdSelectorCreate ( + IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession + ); + +extern IKE_ALG_GUID_INFO mIPsecEncrAlgInfo[]; +#endif + -- cgit v1.2.3