X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=NetworkPkg%2FHttpDxe%2FHttpsSupport.c;h=e4d9a37bee2db1528e16c242aba0ac256313f7b2;hp=f0077dd4b8cbef6675ca475f63ac3491b28f4207;hb=7618784b85c5df91fae4c0d7a910bfb07b248caf;hpb=973f8862f2b44a16b028f5afabbc8a0042029a29;ds=sidebyside diff --git a/NetworkPkg/HttpDxe/HttpsSupport.c b/NetworkPkg/HttpDxe/HttpsSupport.c index f0077dd4b8..e4d9a37bee 100644 --- a/NetworkPkg/HttpDxe/HttpsSupport.c +++ b/NetworkPkg/HttpDxe/HttpsSupport.c @@ -1,1719 +1,1720 @@ -/** @file - Miscellaneous routines specific to Https for HttpDxe driver. - -Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.
-(C) Copyright 2016 Hewlett Packard Enterprise Development LP
-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 "HttpDriver.h" - -/** - Returns the first occurrence of a Null-terminated ASCII sub-string in a Null-terminated - ASCII string and ignore case during the search process. - - This function scans the contents of the ASCII string specified by String - and returns the first occurrence of SearchString and ignore case during the search process. - If SearchString is not found in String, then NULL is returned. If the length of SearchString - is zero, then String is returned. - - If String is NULL, then ASSERT(). - If SearchString is NULL, then ASSERT(). - - @param[in] String A pointer to a Null-terminated ASCII string. - @param[in] SearchString A pointer to a Null-terminated ASCII string to search for. - - @retval NULL If the SearchString does not appear in String. - @retval others If there is a match return the first occurrence of SearchingString. - If the length of SearchString is zero,return String. - -**/ -CHAR8 * -AsciiStrCaseStr ( - IN CONST CHAR8 *String, - IN CONST CHAR8 *SearchString - ) -{ - CONST CHAR8 *FirstMatch; - CONST CHAR8 *SearchStringTmp; - - CHAR8 Src; - CHAR8 Dst; - - // - // ASSERT both strings are less long than PcdMaximumAsciiStringLength - // - ASSERT (AsciiStrSize (String) != 0); - ASSERT (AsciiStrSize (SearchString) != 0); - - if (*SearchString == '\0') { - return (CHAR8 *) String; - } - - while (*String != '\0') { - SearchStringTmp = SearchString; - FirstMatch = String; - - while ((*SearchStringTmp != '\0') - && (*String != '\0')) { - Src = *String; - Dst = *SearchStringTmp; - - if ((Src >= 'A') && (Src <= 'Z')) { - Src -= ('A' - 'a'); - } - - if ((Dst >= 'A') && (Dst <= 'Z')) { - Dst -= ('A' - 'a'); - } - - if (Src != Dst) { - break; - } - - String++; - SearchStringTmp++; - } - - if (*SearchStringTmp == '\0') { - return (CHAR8 *) FirstMatch; - } - - String = FirstMatch + 1; - } - - return NULL; -} - -/** - The callback function to free the net buffer list. - - @param[in] Arg The opaque parameter. - -**/ -VOID -EFIAPI -FreeNbufList ( - IN VOID *Arg - ) -{ - ASSERT (Arg != NULL); - - NetbufFreeList ((LIST_ENTRY *) Arg); - FreePool (Arg); -} - -/** - Check whether the Url is from Https. - - @param[in] Url The pointer to a HTTP or HTTPS URL string. - - @retval TRUE The Url is from HTTPS. - @retval FALSE The Url is from HTTP. - -**/ -BOOLEAN -IsHttpsUrl ( - IN CHAR8 *Url - ) -{ - CHAR8 *Tmp; - - Tmp = NULL; - - Tmp = AsciiStrCaseStr (Url, HTTPS_FLAG); - if (Tmp != NULL && Tmp == Url) { - return TRUE; - } - - return FALSE; -} - -/** - Creates a Tls child handle, open EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL. - - @param[in] ImageHandle The firmware allocated handle for the UEFI image. - @param[out] TlsProto Pointer to the EFI_TLS_PROTOCOL instance. - @param[out] TlsConfiguration Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance. - - @return The child handle with opened EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL. - -**/ -EFI_HANDLE -EFIAPI -TlsCreateChild ( - IN EFI_HANDLE ImageHandle, - OUT EFI_TLS_PROTOCOL **TlsProto, - OUT EFI_TLS_CONFIGURATION_PROTOCOL **TlsConfiguration - ) -{ - EFI_STATUS Status; - EFI_SERVICE_BINDING_PROTOCOL *TlsSb; - EFI_HANDLE TlsChildHandle; - - TlsSb = NULL; - TlsChildHandle = 0; - - // - // Locate TlsServiceBinding protocol. - // - gBS->LocateProtocol ( - &gEfiTlsServiceBindingProtocolGuid, - NULL, - (VOID **) &TlsSb - ); - if (TlsSb == NULL) { - return NULL; - } - - Status = TlsSb->CreateChild (TlsSb, &TlsChildHandle); - if (EFI_ERROR (Status)) { - return NULL; - } - - Status = gBS->OpenProtocol ( - TlsChildHandle, - &gEfiTlsProtocolGuid, - (VOID **) TlsProto, - ImageHandle, - TlsChildHandle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); - if (EFI_ERROR (Status)) { - TlsSb->DestroyChild (TlsSb, TlsChildHandle); - return NULL; - } - - Status = gBS->OpenProtocol ( - TlsChildHandle, - &gEfiTlsConfigurationProtocolGuid, - (VOID **) TlsConfiguration, - ImageHandle, - TlsChildHandle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); - if (EFI_ERROR (Status)) { - TlsSb->DestroyChild (TlsSb, TlsChildHandle); - return NULL; - } - - return TlsChildHandle; -} - -/** - Create event for the TLS receive and transmit tokens which are used to receive and - transmit TLS related messages. - - @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. - - @retval EFI_SUCCESS The events are created successfully. - @retval others Other error as indicated. - -**/ -EFI_STATUS -EFIAPI -TlsCreateTxRxEvent ( - IN OUT HTTP_PROTOCOL *HttpInstance - ) -{ - EFI_STATUS Status; - - if (!HttpInstance->LocalAddressIsIPv6) { - // - // For Tcp4TlsTxToken. - // - Status = gBS->CreateEvent ( - EVT_NOTIFY_SIGNAL, - TPL_NOTIFY, - HttpCommonNotify, - &HttpInstance->TlsIsTxDone, - &HttpInstance->Tcp4TlsTxToken.CompletionToken.Event - ); - if (EFI_ERROR (Status)) { - goto ERROR; - } - - HttpInstance->Tcp4TlsTxData.Push = TRUE; - HttpInstance->Tcp4TlsTxData.Urgent = FALSE; - HttpInstance->Tcp4TlsTxData.DataLength = 0; - HttpInstance->Tcp4TlsTxData.FragmentCount = 1; - HttpInstance->Tcp4TlsTxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp4TlsTxData.DataLength; - HttpInstance->Tcp4TlsTxData.FragmentTable[0].FragmentBuffer = NULL; - HttpInstance->Tcp4TlsTxToken.Packet.TxData = &HttpInstance->Tcp4TlsTxData; - HttpInstance->Tcp4TlsTxToken.CompletionToken.Status = EFI_NOT_READY; - - // - // For Tcp4TlsRxToken. - // - Status = gBS->CreateEvent ( - EVT_NOTIFY_SIGNAL, - TPL_NOTIFY, - HttpCommonNotify, - &HttpInstance->TlsIsRxDone, - &HttpInstance->Tcp4TlsRxToken.CompletionToken.Event - ); - if (EFI_ERROR (Status)) { - goto ERROR; - } - - HttpInstance->Tcp4TlsRxData.DataLength = 0; - HttpInstance->Tcp4TlsRxData.FragmentCount = 1; - HttpInstance->Tcp4TlsRxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp4TlsRxData.DataLength ; - HttpInstance->Tcp4TlsRxData.FragmentTable[0].FragmentBuffer = NULL; - HttpInstance->Tcp4TlsRxToken.Packet.RxData = &HttpInstance->Tcp4TlsRxData; - HttpInstance->Tcp4TlsRxToken.CompletionToken.Status = EFI_NOT_READY; - } else { - // - // For Tcp6TlsTxToken. - // - Status = gBS->CreateEvent ( - EVT_NOTIFY_SIGNAL, - TPL_NOTIFY, - HttpCommonNotify, - &HttpInstance->TlsIsTxDone, - &HttpInstance->Tcp6TlsTxToken.CompletionToken.Event - ); - if (EFI_ERROR (Status)) { - goto ERROR; - } - - HttpInstance->Tcp6TlsTxData.Push = TRUE; - HttpInstance->Tcp6TlsTxData.Urgent = FALSE; - HttpInstance->Tcp6TlsTxData.DataLength = 0; - HttpInstance->Tcp6TlsTxData.FragmentCount = 1; - HttpInstance->Tcp6TlsTxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp6TlsTxData.DataLength; - HttpInstance->Tcp6TlsTxData.FragmentTable[0].FragmentBuffer = NULL; - HttpInstance->Tcp6TlsTxToken.Packet.TxData = &HttpInstance->Tcp6TlsTxData; - HttpInstance->Tcp6TlsTxToken.CompletionToken.Status = EFI_NOT_READY; - - // - // For Tcp6TlsRxToken. - // - Status = gBS->CreateEvent ( - EVT_NOTIFY_SIGNAL, - TPL_NOTIFY, - HttpCommonNotify, - &HttpInstance->TlsIsRxDone, - &HttpInstance->Tcp6TlsRxToken.CompletionToken.Event - ); - if (EFI_ERROR (Status)) { - goto ERROR; - } - - HttpInstance->Tcp6TlsRxData.DataLength = 0; - HttpInstance->Tcp6TlsRxData.FragmentCount = 1; - HttpInstance->Tcp6TlsRxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp6TlsRxData.DataLength ; - HttpInstance->Tcp6TlsRxData.FragmentTable[0].FragmentBuffer = NULL; - HttpInstance->Tcp6TlsRxToken.Packet.RxData = &HttpInstance->Tcp6TlsRxData; - HttpInstance->Tcp6TlsRxToken.CompletionToken.Status = EFI_NOT_READY; - } - - return Status; - -ERROR: - // - // Error handling - // - TlsCloseTxRxEvent (HttpInstance); - - return Status; -} - -/** - Close events in the TlsTxToken and TlsRxToken. - - @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. - -**/ -VOID -EFIAPI -TlsCloseTxRxEvent ( - IN HTTP_PROTOCOL *HttpInstance - ) -{ - ASSERT (HttpInstance != NULL); - if (!HttpInstance->LocalAddressIsIPv6) { - if (NULL != HttpInstance->Tcp4TlsTxToken.CompletionToken.Event) { - gBS->CloseEvent(HttpInstance->Tcp4TlsTxToken.CompletionToken.Event); - HttpInstance->Tcp4TlsTxToken.CompletionToken.Event = NULL; - } - - if (NULL != HttpInstance->Tcp4TlsRxToken.CompletionToken.Event) { - gBS->CloseEvent (HttpInstance->Tcp4TlsRxToken.CompletionToken.Event); - HttpInstance->Tcp4TlsRxToken.CompletionToken.Event = NULL; - } - } else { - if (NULL != HttpInstance->Tcp6TlsTxToken.CompletionToken.Event) { - gBS->CloseEvent(HttpInstance->Tcp6TlsTxToken.CompletionToken.Event); - HttpInstance->Tcp6TlsTxToken.CompletionToken.Event = NULL; - } - - if (NULL != HttpInstance->Tcp6TlsRxToken.CompletionToken.Event) { - gBS->CloseEvent (HttpInstance->Tcp6TlsRxToken.CompletionToken.Event); - HttpInstance->Tcp6TlsRxToken.CompletionToken.Event = NULL; - } - } -} - -/** - Read the TlsCaCertificate variable and configure it. - - @param[in, out] HttpInstance The HTTP instance private data. - - @retval EFI_SUCCESS TlsCaCertificate is configured. - @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. - @retval EFI_NOT_FOUND Fail to get 'TlsCaCertificate' variable. - @retval Others Other error as indicated. - -**/ -EFI_STATUS -TlsConfigCertificate ( - IN OUT HTTP_PROTOCOL *HttpInstance - ) -{ - EFI_STATUS Status; - UINT8 *CACert; - UINTN CACertSize; - UINT32 Index; - EFI_SIGNATURE_LIST *CertList; - EFI_SIGNATURE_DATA *Cert; - UINTN CertCount; - UINT32 ItemDataSize; - - CACert = NULL; - CACertSize = 0; - - // - // Try to read the TlsCaCertificate variable. - // - Status = gRT->GetVariable ( - EFI_TLS_CA_CERTIFICATE_VARIABLE, - &gEfiTlsCaCertificateGuid, - NULL, - &CACertSize, - NULL - ); - - if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { - return Status; - } - - // - // Allocate buffer and read the config variable. - // - CACert = AllocatePool (CACertSize); - if (CACert == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - Status = gRT->GetVariable ( - EFI_TLS_CA_CERTIFICATE_VARIABLE, - &gEfiTlsCaCertificateGuid, - NULL, - &CACertSize, - CACert - ); - if (EFI_ERROR (Status)) { - // - // GetVariable still error or the variable is corrupted. - // Fall back to the default value. - // - FreePool (CACert); - - return EFI_NOT_FOUND; - } - - ASSERT (CACert != NULL); - - // - // Enumerate all data and erasing the target item. - // - ItemDataSize = (UINT32) CACertSize; - CertList = (EFI_SIGNATURE_LIST *) CACert; - while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) { - Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); - CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; - for (Index = 0; Index < CertCount; Index++) { - // - // EfiTlsConfigDataTypeCACertificate - // - Status = HttpInstance->TlsConfiguration->SetData ( - HttpInstance->TlsConfiguration, - EfiTlsConfigDataTypeCACertificate, - Cert->SignatureData, - CertList->SignatureSize - sizeof (Cert->SignatureOwner) - ); - if (EFI_ERROR (Status)) { - FreePool (CACert); - return Status; - } - - Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); - } - - ItemDataSize -= CertList->SignatureListSize; - CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); - } - - FreePool (CACert); - return Status; -} - -/** - Configure TLS session data. - - @param[in, out] HttpInstance The HTTP instance private data. - - @retval EFI_SUCCESS TLS session data is configured. - @retval Others Other error as indicated. - -**/ -EFI_STATUS -EFIAPI -TlsConfigureSession ( - IN OUT HTTP_PROTOCOL *HttpInstance - ) -{ - EFI_STATUS Status; - - // - // TlsConfigData initialization - // - HttpInstance->TlsConfigData.ConnectionEnd = EfiTlsClient; - HttpInstance->TlsConfigData.VerifyMethod = EFI_TLS_VERIFY_PEER; - HttpInstance->TlsConfigData.SessionState = EfiTlsSessionNotStarted; - - // - // EfiTlsConnectionEnd, - // EfiTlsVerifyMethod - // EfiTlsSessionState - // - Status = HttpInstance->Tls->SetSessionData ( - HttpInstance->Tls, - EfiTlsConnectionEnd, - &(HttpInstance->TlsConfigData.ConnectionEnd), - sizeof (EFI_TLS_CONNECTION_END) - ); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = HttpInstance->Tls->SetSessionData ( - HttpInstance->Tls, - EfiTlsVerifyMethod, - &HttpInstance->TlsConfigData.VerifyMethod, - sizeof (EFI_TLS_VERIFY) - ); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = HttpInstance->Tls->SetSessionData ( - HttpInstance->Tls, - EfiTlsSessionState, - &(HttpInstance->TlsConfigData.SessionState), - sizeof (EFI_TLS_SESSION_STATE) - ); - if (EFI_ERROR (Status)) { - return Status; - } - - // - // Tls Config Certificate - // - Status = TlsConfigCertificate (HttpInstance); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "TLS Certificate Config Error!\n")); - return Status; - } - - // - // TlsCreateTxRxEvent - // - Status = TlsCreateTxRxEvent (HttpInstance); - if (EFI_ERROR (Status)) { - goto ERROR; - } - - return Status; - -ERROR: - TlsCloseTxRxEvent (HttpInstance); - - return Status; -} - -/** - Transmit the Packet by processing the associated HTTPS token. - - @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. - @param[in] Packet The packet to transmit. - - @retval EFI_SUCCESS The packet is transmitted. - @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL. - @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. - @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. - @retval Others Other errors as indicated. - -**/ -EFI_STATUS -EFIAPI -TlsCommonTransmit ( - IN OUT HTTP_PROTOCOL *HttpInstance, - IN NET_BUF *Packet - ) -{ - EFI_STATUS Status; - VOID *Data; - UINTN Size; - - if ((HttpInstance == NULL) || (Packet == NULL)) { - return EFI_INVALID_PARAMETER; - } - - if (!HttpInstance->LocalAddressIsIPv6) { - Size = sizeof (EFI_TCP4_TRANSMIT_DATA) + - (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA); - } else { - Size = sizeof (EFI_TCP6_TRANSMIT_DATA) + - (Packet->BlockOpNum - 1) * sizeof (EFI_TCP6_FRAGMENT_DATA); - } - - Data = AllocatePool (Size); - if (Data == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - if (!HttpInstance->LocalAddressIsIPv6) { - ((EFI_TCP4_TRANSMIT_DATA *) Data)->Push = TRUE; - ((EFI_TCP4_TRANSMIT_DATA *) Data)->Urgent = FALSE; - ((EFI_TCP4_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize; - - // - // Build the fragment table. - // - ((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum; - - NetbufBuildExt ( - Packet, - (NET_FRAGMENT *) &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentTable[0], - &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount - ); - - HttpInstance->Tcp4TlsTxToken.Packet.TxData = (EFI_TCP4_TRANSMIT_DATA *) Data; - - Status = EFI_DEVICE_ERROR; - - // - // Transmit the packet. - // - Status = HttpInstance->Tcp4->Transmit (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsTxToken); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - while (!HttpInstance->TlsIsTxDone) { - HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); - } - - HttpInstance->TlsIsTxDone = FALSE; - Status = HttpInstance->Tcp4TlsTxToken.CompletionToken.Status; - } else { - ((EFI_TCP6_TRANSMIT_DATA *) Data)->Push = TRUE; - ((EFI_TCP6_TRANSMIT_DATA *) Data)->Urgent = FALSE; - ((EFI_TCP6_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize; - - // - // Build the fragment table. - // - ((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum; - - NetbufBuildExt ( - Packet, - (NET_FRAGMENT *) &((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentTable[0], - &((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentCount - ); - - HttpInstance->Tcp6TlsTxToken.Packet.TxData = (EFI_TCP6_TRANSMIT_DATA *) Data; - - Status = EFI_DEVICE_ERROR; - - // - // Transmit the packet. - // - Status = HttpInstance->Tcp6->Transmit (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsTxToken); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - while (!HttpInstance->TlsIsTxDone) { - HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); - } - - HttpInstance->TlsIsTxDone = FALSE; - Status = HttpInstance->Tcp6TlsTxToken.CompletionToken.Status; - } - -ON_EXIT: - FreePool (Data); - - return Status; -} - -/** - Receive the Packet by processing the associated HTTPS token. - - @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. - @param[in] Packet The packet to transmit. - @param[in] Timeout The time to wait for connection done. - - @retval EFI_SUCCESS The Packet is received. - @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL. - @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. - @retval EFI_TIMEOUT The operation is time out. - @retval Others Other error as indicated. - -**/ -EFI_STATUS -EFIAPI -TlsCommonReceive ( - IN OUT HTTP_PROTOCOL *HttpInstance, - IN NET_BUF *Packet, - IN EFI_EVENT Timeout - ) -{ - EFI_TCP4_RECEIVE_DATA *Tcp4RxData; - EFI_TCP6_RECEIVE_DATA *Tcp6RxData; - EFI_STATUS Status; - NET_FRAGMENT *Fragment; - UINT32 FragmentCount; - UINT32 CurrentFragment; - - Tcp4RxData = NULL; - Tcp6RxData = NULL; - - if ((HttpInstance == NULL) || (Packet == NULL)) { - return EFI_INVALID_PARAMETER; - } - - FragmentCount = Packet->BlockOpNum; - Fragment = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT)); - if (Fragment == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; - } - - // - // Build the fragment table. - // - NetbufBuildExt (Packet, Fragment, &FragmentCount); - - if (!HttpInstance->LocalAddressIsIPv6) { - Tcp4RxData = HttpInstance->Tcp4TlsRxToken.Packet.RxData; - if (Tcp4RxData == NULL) { - return EFI_INVALID_PARAMETER; - } - Tcp4RxData->FragmentCount = 1; - } else { - Tcp6RxData = HttpInstance->Tcp6TlsRxToken.Packet.RxData; - if (Tcp6RxData == NULL) { - return EFI_INVALID_PARAMETER; - } - Tcp6RxData->FragmentCount = 1; - } - - CurrentFragment = 0; - Status = EFI_SUCCESS; - - while (CurrentFragment < FragmentCount) { - if (!HttpInstance->LocalAddressIsIPv6) { - Tcp4RxData->DataLength = Fragment[CurrentFragment].Len; - Tcp4RxData->FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len; - Tcp4RxData->FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk; - Status = HttpInstance->Tcp4->Receive (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken); - } else { - Tcp6RxData->DataLength = Fragment[CurrentFragment].Len; - Tcp6RxData->FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len; - Tcp6RxData->FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk; - Status = HttpInstance->Tcp6->Receive (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken); - } - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - while (!HttpInstance->TlsIsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { - // - // Poll until some data is received or an error occurs. - // - if (!HttpInstance->LocalAddressIsIPv6) { - HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); - } else { - HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); - } - } - - if (!HttpInstance->TlsIsRxDone) { - // - // Timeout occurs, cancel the receive request. - // - if (!HttpInstance->LocalAddressIsIPv6) { - HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken); - } else { - HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken); - } - - Status = EFI_TIMEOUT; - goto ON_EXIT; - } else { - HttpInstance->TlsIsRxDone = FALSE; - } - - if (!HttpInstance->LocalAddressIsIPv6) { - Status = HttpInstance->Tcp4TlsRxToken.CompletionToken.Status; - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - Fragment[CurrentFragment].Len -= Tcp4RxData->FragmentTable[0].FragmentLength; - if (Fragment[CurrentFragment].Len == 0) { - CurrentFragment++; - } else { - Fragment[CurrentFragment].Bulk += Tcp4RxData->FragmentTable[0].FragmentLength; - } - } else { - Status = HttpInstance->Tcp6TlsRxToken.CompletionToken.Status; - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - Fragment[CurrentFragment].Len -= Tcp6RxData->FragmentTable[0].FragmentLength; - if (Fragment[CurrentFragment].Len == 0) { - CurrentFragment++; - } else { - Fragment[CurrentFragment].Bulk += Tcp6RxData->FragmentTable[0].FragmentLength; - } - } - } - -ON_EXIT: - - if (Fragment != NULL) { - FreePool (Fragment); - } - - return Status; -} - -/** - Receive one TLS PDU. An TLS PDU contains an TLS record header and it's - corresponding record data. These two parts will be put into two blocks of buffers in the - net buffer. - - @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. - @param[out] Pdu The received TLS PDU. - @param[in] Timeout The time to wait for connection done. - - @retval EFI_SUCCESS An TLS PDU is received. - @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. - @retval EFI_PROTOCOL_ERROR An unexpected TLS packet was received. - @retval Others Other errors as indicated. - -**/ -EFI_STATUS -EFIAPI -TlsReceiveOnePdu ( - IN OUT HTTP_PROTOCOL *HttpInstance, - OUT NET_BUF **Pdu, - IN EFI_EVENT Timeout - ) -{ - EFI_STATUS Status; - - LIST_ENTRY *NbufList; - - UINT32 Len; - - NET_BUF *PduHdr; - UINT8 *Header; - TLS_RECORD_HEADER RecordHeader; - - NET_BUF *DataSeg; - - NbufList = NULL; - PduHdr = NULL; - Header = NULL; - DataSeg = NULL; - - NbufList = AllocatePool (sizeof (LIST_ENTRY)); - if (NbufList == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - InitializeListHead (NbufList); - - // - // Allocate buffer to receive one TLS header. - // - Len = sizeof (TLS_RECORD_HEADER); - PduHdr = NetbufAlloc (Len); - if (PduHdr == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; - } - - Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL); - if (Header == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; - } - - // - // First step, receive one TLS header. - // - Status = TlsCommonReceive (HttpInstance, PduHdr, Timeout); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - RecordHeader = *(TLS_RECORD_HEADER *) Header; - if ((RecordHeader.ContentType == TlsContentTypeHandshake || - RecordHeader.ContentType == TlsContentTypeAlert || - RecordHeader.ContentType == TlsContentTypeChangeCipherSpec || - RecordHeader.ContentType == TlsContentTypeApplicationData) && - (RecordHeader.Version.Major == 0x03) && /// Major versions are same. - (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || - RecordHeader.Version.Minor ==TLS11_PROTOCOL_VERSION_MINOR || - RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) - ) { - InsertTailList (NbufList, &PduHdr->List); - } else { - Status = EFI_PROTOCOL_ERROR; - goto ON_EXIT; - } - - Len = SwapBytes16(RecordHeader.Length); - if (Len == 0) { - // - // No TLS payload. - // - goto FORM_PDU; - } - - // - // Allocate buffer to receive one TLS payload. - // - DataSeg = NetbufAlloc (Len); - if (DataSeg == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; - } - - NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL); - - // - // Second step, receive one TLS payload. - // - Status = TlsCommonReceive (HttpInstance, DataSeg, Timeout); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - InsertTailList (NbufList, &DataSeg->List); - -FORM_PDU: - // - // Form the PDU from a list of PDU. - // - *Pdu = NetbufFromBufList (NbufList, 0, 0, FreeNbufList, NbufList); - if (*Pdu == NULL) { - Status = EFI_OUT_OF_RESOURCES; - } - -ON_EXIT: - - if (EFI_ERROR (Status)) { - // - // Free the Nbufs in this NbufList and the NbufList itself. - // - FreeNbufList (NbufList); - } - - return Status; -} - -/** - Connect one TLS session by finishing the TLS handshake process. - - @param[in] HttpInstance The HTTP instance private data. - @param[in] Timeout The time to wait for connection done. - - @retval EFI_SUCCESS The TLS session is established. - @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. - @retval EFI_ABORTED TLS session state is incorrect. - @retval Others Other error as indicated. - -**/ -EFI_STATUS -EFIAPI -TlsConnectSession ( - IN HTTP_PROTOCOL *HttpInstance, - IN EFI_EVENT Timeout - ) -{ - EFI_STATUS Status; - UINT8 *BufferOut; - UINTN BufferOutSize; - NET_BUF *PacketOut; - UINT8 *DataOut; - NET_BUF *Pdu; - UINT8 *BufferIn; - UINTN BufferInSize; - UINT8 *GetSessionDataBuffer; - UINTN GetSessionDataBufferSize; - - BufferOut = NULL; - PacketOut = NULL; - DataOut = NULL; - Pdu = NULL; - BufferIn = NULL; - - // - // Initialize TLS state. - // - HttpInstance->TlsSessionState = EfiTlsSessionNotStarted; - Status = HttpInstance->Tls->SetSessionData ( - HttpInstance->Tls, - EfiTlsSessionState, - &(HttpInstance->TlsSessionState), - sizeof (EFI_TLS_SESSION_STATE) - ); - if (EFI_ERROR (Status)) { - return Status; - } - - // - // Create ClientHello - // - BufferOutSize = DEF_BUF_LEN; - BufferOut = AllocateZeroPool (BufferOutSize); - if (BufferOut == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->BuildResponsePacket ( - HttpInstance->Tls, - NULL, - 0, - BufferOut, - &BufferOutSize - ); - if (Status == EFI_BUFFER_TOO_SMALL) { - FreePool (BufferOut); - BufferOut = AllocateZeroPool (BufferOutSize); - if (BufferOut == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->BuildResponsePacket ( - HttpInstance->Tls, - NULL, - 0, - BufferOut, - &BufferOutSize - ); - } - if (EFI_ERROR (Status)) { - FreePool (BufferOut); - return Status; - } - - // - // Transmit ClientHello - // - PacketOut = NetbufAlloc ((UINT32) BufferOutSize); - DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); - if (DataOut == NULL) { - FreePool (BufferOut); - return EFI_OUT_OF_RESOURCES; - } - - CopyMem (DataOut, BufferOut, BufferOutSize); - Status = TlsCommonTransmit (HttpInstance, PacketOut); - - FreePool (BufferOut); - NetbufFree (PacketOut); - - if (EFI_ERROR (Status)) { - return Status; - } - - while(HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring && \ - ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { - // - // Receive one TLS record. - // - Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout); - if (EFI_ERROR (Status)) { - return Status; - } - - BufferInSize = Pdu->TotalSize; - BufferIn = AllocateZeroPool (BufferInSize); - if (BufferIn == NULL) { - NetbufFree (Pdu); - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - NetbufCopy (Pdu, 0, (UINT32)BufferInSize, BufferIn); - - NetbufFree (Pdu); - - // - // Handle Receive data. - // - BufferOutSize = DEF_BUF_LEN; - BufferOut = AllocateZeroPool (BufferOutSize); - if (BufferOut == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->BuildResponsePacket ( - HttpInstance->Tls, - BufferIn, - BufferInSize, - BufferOut, - &BufferOutSize - ); - if (Status == EFI_BUFFER_TOO_SMALL) { - FreePool (BufferOut); - BufferOut = AllocateZeroPool (BufferOutSize); - if (BufferOut == NULL) { - FreePool (BufferIn); - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->BuildResponsePacket ( - HttpInstance->Tls, - BufferIn, - BufferInSize, - BufferOut, - &BufferOutSize - ); - } - - FreePool (BufferIn); - - if (EFI_ERROR (Status)) { - FreePool (BufferOut); - return Status; - } - - if (BufferOutSize != 0) { - // - // Transmit the response packet. - // - PacketOut = NetbufAlloc ((UINT32) BufferOutSize); - DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); - if (DataOut == NULL) { - FreePool (BufferOut); - return EFI_OUT_OF_RESOURCES; - } - - CopyMem (DataOut, BufferOut, BufferOutSize); - - Status = TlsCommonTransmit (HttpInstance, PacketOut); - - NetbufFree (PacketOut); - - if (EFI_ERROR (Status)) { - FreePool (BufferOut); - return Status; - } - } - - FreePool (BufferOut); - - // - // Get the session state, then decide whether need to continue handle received packet. - // - GetSessionDataBufferSize = DEF_BUF_LEN; - GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); - if (GetSessionDataBuffer == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->GetSessionData ( - HttpInstance->Tls, - EfiTlsSessionState, - GetSessionDataBuffer, - &GetSessionDataBufferSize - ); - if (Status == EFI_BUFFER_TOO_SMALL) { - FreePool (GetSessionDataBuffer); - GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); - if (GetSessionDataBuffer == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->GetSessionData ( - HttpInstance->Tls, - EfiTlsSessionState, - GetSessionDataBuffer, - &GetSessionDataBufferSize - ); - } - if (EFI_ERROR (Status)) { - FreePool(GetSessionDataBuffer); - return Status; - } - - ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE)); - HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer; - - FreePool (GetSessionDataBuffer); - - if(HttpInstance->TlsSessionState == EfiTlsSessionError) { - return EFI_ABORTED; - } - } - - if (HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring) { - Status = EFI_ABORTED; - } - - return Status; -} - -/** - Close the TLS session and send out the close notification message. - - @param[in] HttpInstance The HTTP instance private data. - - @retval EFI_SUCCESS The TLS session is closed. - @retval EFI_INVALID_PARAMETER HttpInstance is NULL. - @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. - @retval Others Other error as indicated. - -**/ -EFI_STATUS -EFIAPI -TlsCloseSession ( - IN HTTP_PROTOCOL *HttpInstance - ) -{ - EFI_STATUS Status; - - UINT8 *BufferOut; - UINTN BufferOutSize; - - NET_BUF *PacketOut; - UINT8 *DataOut; - - Status = EFI_SUCCESS; - BufferOut = NULL; - PacketOut = NULL; - DataOut = NULL; - - if (HttpInstance == NULL) { - return EFI_INVALID_PARAMETER; - } - - HttpInstance->TlsSessionState = EfiTlsSessionClosing; - - Status = HttpInstance->Tls->SetSessionData ( - HttpInstance->Tls, - EfiTlsSessionState, - &(HttpInstance->TlsSessionState), - sizeof (EFI_TLS_SESSION_STATE) - ); - if (EFI_ERROR (Status)) { - return Status; - } - - BufferOutSize = DEF_BUF_LEN; - BufferOut = AllocateZeroPool (BufferOutSize); - if (BufferOut == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->BuildResponsePacket ( - HttpInstance->Tls, - NULL, - 0, - BufferOut, - &BufferOutSize - ); - if (Status == EFI_BUFFER_TOO_SMALL) { - FreePool (BufferOut); - BufferOut = AllocateZeroPool (BufferOutSize); - if (BufferOut == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->BuildResponsePacket ( - HttpInstance->Tls, - NULL, - 0, - BufferOut, - &BufferOutSize - ); - } - - if (EFI_ERROR (Status)) { - FreePool (BufferOut); - return Status; - } - - PacketOut = NetbufAlloc ((UINT32) BufferOutSize); - DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); - if (DataOut == NULL) { - FreePool (BufferOut); - return EFI_OUT_OF_RESOURCES; - } - - CopyMem (DataOut, BufferOut, BufferOutSize); - - Status = TlsCommonTransmit (HttpInstance, PacketOut); - - FreePool (BufferOut); - NetbufFree (PacketOut); - - return Status; -} - -/** - Process one message according to the CryptMode. - - @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. - @param[in] Message Pointer to the message buffer needed to processed. - @param[in] MessageSize Pointer to the message buffer size. - @param[in] ProcessMode Process mode. - @param[in, out] Fragment Only one Fragment returned after the Message is - processed successfully. - - @retval EFI_SUCCESS Message is processed successfully. - @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. - @retval Others Other errors as indicated. - -**/ -EFI_STATUS -EFIAPI -TlsProcessMessage ( - IN HTTP_PROTOCOL *HttpInstance, - IN UINT8 *Message, - IN UINTN MessageSize, - IN EFI_TLS_CRYPT_MODE ProcessMode, - IN OUT NET_FRAGMENT *Fragment - ) -{ - EFI_STATUS Status; - UINT8 *Buffer; - UINT32 BufferSize; - UINT32 BytesCopied; - EFI_TLS_FRAGMENT_DATA *FragmentTable; - UINT32 FragmentCount; - EFI_TLS_FRAGMENT_DATA *OriginalFragmentTable; - UINTN Index; - - Status = EFI_SUCCESS; - Buffer = NULL; - BufferSize = 0; - BytesCopied = 0; - FragmentTable = NULL; - OriginalFragmentTable = NULL; - - // - // Rebuild fragment table from BufferIn. - // - FragmentCount = 1; - FragmentTable = AllocateZeroPool (FragmentCount * sizeof (EFI_TLS_FRAGMENT_DATA)); - if (FragmentTable == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; - } - - FragmentTable->FragmentLength = (UINT32) MessageSize; - FragmentTable->FragmentBuffer = Message; - - // - // Record the original FragmentTable. - // - OriginalFragmentTable = FragmentTable; - - // - // Process the Message. - // - Status = HttpInstance->Tls->ProcessPacket ( - HttpInstance->Tls, - &FragmentTable, - &FragmentCount, - ProcessMode - ); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - // - // Calculate the size according to FragmentTable. - // - for (Index = 0; Index < FragmentCount; Index++) { - BufferSize += FragmentTable[Index].FragmentLength; - } - - // - // Allocate buffer for processed data. - // - Buffer = AllocateZeroPool (BufferSize); - if (Buffer == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; - } - - // - // Copy the new FragmentTable buffer into Buffer. - // - for (Index = 0; Index < FragmentCount; Index++) { - CopyMem ( - (Buffer + BytesCopied), - FragmentTable[Index].FragmentBuffer, - FragmentTable[Index].FragmentLength - ); - BytesCopied += FragmentTable[Index].FragmentLength; - - // - // Free the FragmentBuffer since it has been copied. - // - FreePool (FragmentTable[Index].FragmentBuffer); - } - - Fragment->Len = BufferSize; - Fragment->Bulk = Buffer; - -ON_EXIT: - - if (OriginalFragmentTable != NULL) { - FreePool (OriginalFragmentTable); - OriginalFragmentTable = NULL; - } - - // - // Caller has the responsibility to free the FragmentTable. - // - if (FragmentTable != NULL) { - FreePool (FragmentTable); - FragmentTable = NULL; - } - - return Status; -} - -/** - Receive one fragment decrypted from one TLS record. - - @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. - @param[in, out] Fragment The received Fragment. - @param[in] Timeout The time to wait for connection done. - - @retval EFI_SUCCESS One fragment is received. - @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. - @retval EFI_ABORTED Something wrong decryption the message. - @retval Others Other errors as indicated. - -**/ -EFI_STATUS -EFIAPI -HttpsReceive ( - IN HTTP_PROTOCOL *HttpInstance, - IN OUT NET_FRAGMENT *Fragment, - IN EFI_EVENT Timeout - ) -{ - EFI_STATUS Status; - NET_BUF *Pdu; - TLS_RECORD_HEADER RecordHeader; - UINT8 *BufferIn; - UINTN BufferInSize; - NET_FRAGMENT TempFragment; - UINT8 *BufferOut; - UINTN BufferOutSize; - NET_BUF *PacketOut; - UINT8 *DataOut; - UINT8 *GetSessionDataBuffer; - UINTN GetSessionDataBufferSize; - - Status = EFI_SUCCESS; - Pdu = NULL; - BufferIn = NULL; - BufferInSize = 0; - BufferOut = NULL; - BufferOutSize = 0; - PacketOut = NULL; - DataOut = NULL; - GetSessionDataBuffer = NULL; - GetSessionDataBufferSize = 0; - - // - // Receive only one TLS record - // - Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout); - if (EFI_ERROR (Status)) { - return Status; - } - - BufferInSize = Pdu->TotalSize; - BufferIn = AllocateZeroPool (BufferInSize); - if (BufferIn == NULL) { - Status = EFI_OUT_OF_RESOURCES; - NetbufFree (Pdu); - return Status; - } - - NetbufCopy (Pdu, 0, (UINT32) BufferInSize, BufferIn); - - NetbufFree (Pdu); - - // - // Handle Receive data. - // - RecordHeader = *(TLS_RECORD_HEADER *) BufferIn; - - if ((RecordHeader.ContentType == TlsContentTypeApplicationData) && - (RecordHeader.Version.Major == 0x03) && - (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || - RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR || - RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) - ) { - // - // Decrypt Packet. - // - Status = TlsProcessMessage ( - HttpInstance, - BufferIn, - BufferInSize, - EfiTlsDecrypt, - &TempFragment - ); - - FreePool (BufferIn); - - if (EFI_ERROR (Status)) { - if (Status == EFI_ABORTED) { - // - // Something wrong decryption the message. - // BuildResponsePacket() will be called to generate Error Alert message and send it out. - // - BufferOutSize = DEF_BUF_LEN; - BufferOut = AllocateZeroPool (BufferOutSize); - if (BufferOut == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->BuildResponsePacket ( - HttpInstance->Tls, - NULL, - 0, - BufferOut, - &BufferOutSize - ); - if (Status == EFI_BUFFER_TOO_SMALL) { - FreePool (BufferOut); - BufferOut = AllocateZeroPool (BufferOutSize); - if (BufferOut == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->BuildResponsePacket ( - HttpInstance->Tls, - NULL, - 0, - BufferOut, - &BufferOutSize - ); - } - if (EFI_ERROR (Status)) { - FreePool(BufferOut); - return Status; - } - - if (BufferOutSize != 0) { - PacketOut = NetbufAlloc ((UINT32)BufferOutSize); - DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); - if (DataOut == NULL) { - FreePool (BufferOut); - return EFI_OUT_OF_RESOURCES; - } - - CopyMem (DataOut, BufferOut, BufferOutSize); - - Status = TlsCommonTransmit (HttpInstance, PacketOut); - - NetbufFree (PacketOut); - } - - FreePool(BufferOut); - - if (EFI_ERROR (Status)) { - return Status; - } - - return EFI_ABORTED; - } - - return Status; - } - - // - // Parsing buffer. - // - ASSERT (((TLS_RECORD_HEADER *) (TempFragment.Bulk))->ContentType == TlsContentTypeApplicationData); - - BufferInSize = ((TLS_RECORD_HEADER *) (TempFragment.Bulk))->Length; - BufferIn = AllocateZeroPool (BufferInSize); - if (BufferIn == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - CopyMem (BufferIn, TempFragment.Bulk + sizeof (TLS_RECORD_HEADER), BufferInSize); - - // - // Free the buffer in TempFragment. - // - FreePool (TempFragment.Bulk); - - } else if ((RecordHeader.ContentType == TlsContentTypeAlert) && - (RecordHeader.Version.Major == 0x03) && - (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || - RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR || - RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) - ) { - BufferOutSize = DEF_BUF_LEN; - BufferOut = AllocateZeroPool (BufferOutSize); - if (BufferOut == NULL) { - FreePool (BufferIn); - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->BuildResponsePacket ( - HttpInstance->Tls, - BufferIn, - BufferInSize, - BufferOut, - &BufferOutSize - ); - if (Status == EFI_BUFFER_TOO_SMALL) { - FreePool (BufferOut); - BufferOut = AllocateZeroPool (BufferOutSize); - if (BufferOut == NULL) { - FreePool (BufferIn); - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->BuildResponsePacket ( - HttpInstance->Tls, - BufferIn, - BufferInSize, - BufferOut, - &BufferOutSize - ); - } - - FreePool (BufferIn); - - if (EFI_ERROR (Status)) { - FreePool (BufferOut); - return Status; - } - - if (BufferOutSize != 0) { - PacketOut = NetbufAlloc ((UINT32) BufferOutSize); - DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); - if (DataOut == NULL) { - FreePool (BufferOut); - return EFI_OUT_OF_RESOURCES; - } - - CopyMem (DataOut, BufferOut, BufferOutSize); - - Status = TlsCommonTransmit (HttpInstance, PacketOut); - - NetbufFree (PacketOut); - } - - FreePool (BufferOut); - - // - // Get the session state. - // - GetSessionDataBufferSize = DEF_BUF_LEN; - GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); - if (GetSessionDataBuffer == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->GetSessionData ( - HttpInstance->Tls, - EfiTlsSessionState, - GetSessionDataBuffer, - &GetSessionDataBufferSize - ); - if (Status == EFI_BUFFER_TOO_SMALL) { - FreePool (GetSessionDataBuffer); - GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); - if (GetSessionDataBuffer == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; - } - - Status = HttpInstance->Tls->GetSessionData ( - HttpInstance->Tls, - EfiTlsSessionState, - GetSessionDataBuffer, - &GetSessionDataBufferSize - ); - } - if (EFI_ERROR (Status)) { - FreePool (GetSessionDataBuffer); - return Status; - } - - ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE)); - HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer; - - FreePool (GetSessionDataBuffer); - - if(HttpInstance->TlsSessionState == EfiTlsSessionError) { - DEBUG ((EFI_D_ERROR, "TLS Session State Error!\n")); - return EFI_ABORTED; - } - - BufferIn = NULL; - BufferInSize = 0; - } - - Fragment->Bulk = BufferIn; - Fragment->Len = (UINT32) BufferInSize; - - return Status; -} +/** @file + Miscellaneous routines specific to Https for HttpDxe driver. + +Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
+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 "HttpDriver.h" + +/** + Returns the first occurrence of a Null-terminated ASCII sub-string in a Null-terminated + ASCII string and ignore case during the search process. + + This function scans the contents of the ASCII string specified by String + and returns the first occurrence of SearchString and ignore case during the search process. + If SearchString is not found in String, then NULL is returned. If the length of SearchString + is zero, then String is returned. + + If String is NULL, then ASSERT(). + If SearchString is NULL, then ASSERT(). + + @param[in] String A pointer to a Null-terminated ASCII string. + @param[in] SearchString A pointer to a Null-terminated ASCII string to search for. + + @retval NULL If the SearchString does not appear in String. + @retval others If there is a match return the first occurrence of SearchingString. + If the length of SearchString is zero,return String. + +**/ +CHAR8 * +AsciiStrCaseStr ( + IN CONST CHAR8 *String, + IN CONST CHAR8 *SearchString + ) +{ + CONST CHAR8 *FirstMatch; + CONST CHAR8 *SearchStringTmp; + + CHAR8 Src; + CHAR8 Dst; + + // + // ASSERT both strings are less long than PcdMaximumAsciiStringLength + // + ASSERT (AsciiStrSize (String) != 0); + ASSERT (AsciiStrSize (SearchString) != 0); + + if (*SearchString == '\0') { + return (CHAR8 *) String; + } + + while (*String != '\0') { + SearchStringTmp = SearchString; + FirstMatch = String; + + while ((*SearchStringTmp != '\0') + && (*String != '\0')) { + Src = *String; + Dst = *SearchStringTmp; + + if ((Src >= 'A') && (Src <= 'Z')) { + Src -= ('A' - 'a'); + } + + if ((Dst >= 'A') && (Dst <= 'Z')) { + Dst -= ('A' - 'a'); + } + + if (Src != Dst) { + break; + } + + String++; + SearchStringTmp++; + } + + if (*SearchStringTmp == '\0') { + return (CHAR8 *) FirstMatch; + } + + String = FirstMatch + 1; + } + + return NULL; +} + +/** + The callback function to free the net buffer list. + + @param[in] Arg The opaque parameter. + +**/ +VOID +EFIAPI +FreeNbufList ( + IN VOID *Arg + ) +{ + ASSERT (Arg != NULL); + + NetbufFreeList ((LIST_ENTRY *) Arg); + FreePool (Arg); +} + +/** + Check whether the Url is from Https. + + @param[in] Url The pointer to a HTTP or HTTPS URL string. + + @retval TRUE The Url is from HTTPS. + @retval FALSE The Url is from HTTP. + +**/ +BOOLEAN +IsHttpsUrl ( + IN CHAR8 *Url + ) +{ + CHAR8 *Tmp; + + Tmp = NULL; + + Tmp = AsciiStrCaseStr (Url, HTTPS_FLAG); + if (Tmp != NULL && Tmp == Url) { + return TRUE; + } + + return FALSE; +} + +/** + Creates a Tls child handle, open EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[out] TlsProto Pointer to the EFI_TLS_PROTOCOL instance. + @param[out] TlsConfiguration Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance. + + @return The child handle with opened EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL. + +**/ +EFI_HANDLE +EFIAPI +TlsCreateChild ( + IN EFI_HANDLE ImageHandle, + OUT EFI_TLS_PROTOCOL **TlsProto, + OUT EFI_TLS_CONFIGURATION_PROTOCOL **TlsConfiguration + ) +{ + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *TlsSb; + EFI_HANDLE TlsChildHandle; + + TlsSb = NULL; + TlsChildHandle = 0; + + // + // Locate TlsServiceBinding protocol. + // + gBS->LocateProtocol ( + &gEfiTlsServiceBindingProtocolGuid, + NULL, + (VOID **) &TlsSb + ); + if (TlsSb == NULL) { + return NULL; + } + + Status = TlsSb->CreateChild (TlsSb, &TlsChildHandle); + if (EFI_ERROR (Status)) { + return NULL; + } + + Status = gBS->OpenProtocol ( + TlsChildHandle, + &gEfiTlsProtocolGuid, + (VOID **) TlsProto, + ImageHandle, + TlsChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + TlsSb->DestroyChild (TlsSb, TlsChildHandle); + return NULL; + } + + Status = gBS->OpenProtocol ( + TlsChildHandle, + &gEfiTlsConfigurationProtocolGuid, + (VOID **) TlsConfiguration, + ImageHandle, + TlsChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + TlsSb->DestroyChild (TlsSb, TlsChildHandle); + return NULL; + } + + return TlsChildHandle; +} + +/** + Create event for the TLS receive and transmit tokens which are used to receive and + transmit TLS related messages. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events are created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCreateTxRxEvent ( + IN OUT HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (!HttpInstance->LocalAddressIsIPv6) { + // + // For Tcp4TlsTxToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->TlsIsTxDone, + &HttpInstance->Tcp4TlsTxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + HttpInstance->Tcp4TlsTxData.Push = TRUE; + HttpInstance->Tcp4TlsTxData.Urgent = FALSE; + HttpInstance->Tcp4TlsTxData.DataLength = 0; + HttpInstance->Tcp4TlsTxData.FragmentCount = 1; + HttpInstance->Tcp4TlsTxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp4TlsTxData.DataLength; + HttpInstance->Tcp4TlsTxData.FragmentTable[0].FragmentBuffer = NULL; + HttpInstance->Tcp4TlsTxToken.Packet.TxData = &HttpInstance->Tcp4TlsTxData; + HttpInstance->Tcp4TlsTxToken.CompletionToken.Status = EFI_NOT_READY; + + // + // For Tcp4TlsRxToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->TlsIsRxDone, + &HttpInstance->Tcp4TlsRxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + HttpInstance->Tcp4TlsRxData.DataLength = 0; + HttpInstance->Tcp4TlsRxData.FragmentCount = 1; + HttpInstance->Tcp4TlsRxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp4TlsRxData.DataLength ; + HttpInstance->Tcp4TlsRxData.FragmentTable[0].FragmentBuffer = NULL; + HttpInstance->Tcp4TlsRxToken.Packet.RxData = &HttpInstance->Tcp4TlsRxData; + HttpInstance->Tcp4TlsRxToken.CompletionToken.Status = EFI_NOT_READY; + } else { + // + // For Tcp6TlsTxToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->TlsIsTxDone, + &HttpInstance->Tcp6TlsTxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + HttpInstance->Tcp6TlsTxData.Push = TRUE; + HttpInstance->Tcp6TlsTxData.Urgent = FALSE; + HttpInstance->Tcp6TlsTxData.DataLength = 0; + HttpInstance->Tcp6TlsTxData.FragmentCount = 1; + HttpInstance->Tcp6TlsTxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp6TlsTxData.DataLength; + HttpInstance->Tcp6TlsTxData.FragmentTable[0].FragmentBuffer = NULL; + HttpInstance->Tcp6TlsTxToken.Packet.TxData = &HttpInstance->Tcp6TlsTxData; + HttpInstance->Tcp6TlsTxToken.CompletionToken.Status = EFI_NOT_READY; + + // + // For Tcp6TlsRxToken. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->TlsIsRxDone, + &HttpInstance->Tcp6TlsRxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + HttpInstance->Tcp6TlsRxData.DataLength = 0; + HttpInstance->Tcp6TlsRxData.FragmentCount = 1; + HttpInstance->Tcp6TlsRxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp6TlsRxData.DataLength ; + HttpInstance->Tcp6TlsRxData.FragmentTable[0].FragmentBuffer = NULL; + HttpInstance->Tcp6TlsRxToken.Packet.RxData = &HttpInstance->Tcp6TlsRxData; + HttpInstance->Tcp6TlsRxToken.CompletionToken.Status = EFI_NOT_READY; + } + + return Status; + +ERROR: + // + // Error handling + // + TlsCloseTxRxEvent (HttpInstance); + + return Status; +} + +/** + Close events in the TlsTxToken and TlsRxToken. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +EFIAPI +TlsCloseTxRxEvent ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + ASSERT (HttpInstance != NULL); + if (!HttpInstance->LocalAddressIsIPv6) { + if (NULL != HttpInstance->Tcp4TlsTxToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp4TlsTxToken.CompletionToken.Event); + HttpInstance->Tcp4TlsTxToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp4TlsRxToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp4TlsRxToken.CompletionToken.Event); + HttpInstance->Tcp4TlsRxToken.CompletionToken.Event = NULL; + } + } else { + if (NULL != HttpInstance->Tcp6TlsTxToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp6TlsTxToken.CompletionToken.Event); + HttpInstance->Tcp6TlsTxToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp6TlsRxToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp6TlsRxToken.CompletionToken.Event); + HttpInstance->Tcp6TlsRxToken.CompletionToken.Event = NULL; + } + } +} + +/** + Read the TlsCaCertificate variable and configure it. + + @param[in, out] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS TlsCaCertificate is configured. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_NOT_FOUND Fail to get 'TlsCaCertificate' variable. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +TlsConfigCertificate ( + IN OUT HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + UINT8 *CACert; + UINTN CACertSize; + UINT32 Index; + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_DATA *Cert; + UINTN CertCount; + UINT32 ItemDataSize; + + CACert = NULL; + CACertSize = 0; + + // + // Try to read the TlsCaCertificate variable. + // + Status = gRT->GetVariable ( + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + NULL, + &CACertSize, + NULL + ); + + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + // + // Allocate buffer and read the config variable. + // + CACert = AllocatePool (CACertSize); + if (CACert == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gRT->GetVariable ( + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + NULL, + &CACertSize, + CACert + ); + if (EFI_ERROR (Status)) { + // + // GetVariable still error or the variable is corrupted. + // Fall back to the default value. + // + FreePool (CACert); + + return EFI_NOT_FOUND; + } + + ASSERT (CACert != NULL); + + // + // Enumerate all data and erasing the target item. + // + ItemDataSize = (UINT32) CACertSize; + CertList = (EFI_SIGNATURE_LIST *) CACert; + while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) { + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + for (Index = 0; Index < CertCount; Index++) { + // + // EfiTlsConfigDataTypeCACertificate + // + Status = HttpInstance->TlsConfiguration->SetData ( + HttpInstance->TlsConfiguration, + EfiTlsConfigDataTypeCACertificate, + Cert->SignatureData, + CertList->SignatureSize - sizeof (Cert->SignatureOwner) + ); + if (EFI_ERROR (Status)) { + FreePool (CACert); + return Status; + } + + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); + } + + ItemDataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); + } + + FreePool (CACert); + return Status; +} + +/** + Configure TLS session data. + + @param[in, out] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS TLS session data is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsConfigureSession ( + IN OUT HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + // + // TlsConfigData initialization + // + HttpInstance->TlsConfigData.ConnectionEnd = EfiTlsClient; + HttpInstance->TlsConfigData.VerifyMethod = EFI_TLS_VERIFY_PEER; + HttpInstance->TlsConfigData.SessionState = EfiTlsSessionNotStarted; + + // + // EfiTlsConnectionEnd, + // EfiTlsVerifyMethod + // EfiTlsSessionState + // + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsConnectionEnd, + &(HttpInstance->TlsConfigData.ConnectionEnd), + sizeof (EFI_TLS_CONNECTION_END) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsVerifyMethod, + &HttpInstance->TlsConfigData.VerifyMethod, + sizeof (EFI_TLS_VERIFY) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + &(HttpInstance->TlsConfigData.SessionState), + sizeof (EFI_TLS_SESSION_STATE) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Tls Config Certificate + // + Status = TlsConfigCertificate (HttpInstance); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TLS Certificate Config Error!\n")); + return Status; + } + + // + // TlsCreateTxRxEvent + // + Status = TlsCreateTxRxEvent (HttpInstance); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + return Status; + +ERROR: + TlsCloseTxRxEvent (HttpInstance); + + return Status; +} + +/** + Transmit the Packet by processing the associated HTTPS token. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Packet The packet to transmit. + + @retval EFI_SUCCESS The packet is transmitted. + @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCommonTransmit ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN NET_BUF *Packet + ) +{ + EFI_STATUS Status; + VOID *Data; + UINTN Size; + + if ((HttpInstance == NULL) || (Packet == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!HttpInstance->LocalAddressIsIPv6) { + Size = sizeof (EFI_TCP4_TRANSMIT_DATA) + + (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA); + } else { + Size = sizeof (EFI_TCP6_TRANSMIT_DATA) + + (Packet->BlockOpNum - 1) * sizeof (EFI_TCP6_FRAGMENT_DATA); + } + + Data = AllocatePool (Size); + if (Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (!HttpInstance->LocalAddressIsIPv6) { + ((EFI_TCP4_TRANSMIT_DATA *) Data)->Push = TRUE; + ((EFI_TCP4_TRANSMIT_DATA *) Data)->Urgent = FALSE; + ((EFI_TCP4_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize; + + // + // Build the fragment table. + // + ((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum; + + NetbufBuildExt ( + Packet, + (NET_FRAGMENT *) &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentTable[0], + &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount + ); + + HttpInstance->Tcp4TlsTxToken.Packet.TxData = (EFI_TCP4_TRANSMIT_DATA *) Data; + + Status = EFI_DEVICE_ERROR; + + // + // Transmit the packet. + // + Status = HttpInstance->Tcp4->Transmit (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsTxToken); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + while (!HttpInstance->TlsIsTxDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + + HttpInstance->TlsIsTxDone = FALSE; + Status = HttpInstance->Tcp4TlsTxToken.CompletionToken.Status; + } else { + ((EFI_TCP6_TRANSMIT_DATA *) Data)->Push = TRUE; + ((EFI_TCP6_TRANSMIT_DATA *) Data)->Urgent = FALSE; + ((EFI_TCP6_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize; + + // + // Build the fragment table. + // + ((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum; + + NetbufBuildExt ( + Packet, + (NET_FRAGMENT *) &((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentTable[0], + &((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentCount + ); + + HttpInstance->Tcp6TlsTxToken.Packet.TxData = (EFI_TCP6_TRANSMIT_DATA *) Data; + + Status = EFI_DEVICE_ERROR; + + // + // Transmit the packet. + // + Status = HttpInstance->Tcp6->Transmit (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsTxToken); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + while (!HttpInstance->TlsIsTxDone) { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + + HttpInstance->TlsIsTxDone = FALSE; + Status = HttpInstance->Tcp6TlsTxToken.CompletionToken.Status; + } + +ON_EXIT: + FreePool (Data); + + return Status; +} + +/** + Receive the Packet by processing the associated HTTPS token. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Packet The packet to transmit. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS The Packet is received. + @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_TIMEOUT The operation is time out. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCommonReceive ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN NET_BUF *Packet, + IN EFI_EVENT Timeout + ) +{ + EFI_TCP4_RECEIVE_DATA *Tcp4RxData; + EFI_TCP6_RECEIVE_DATA *Tcp6RxData; + EFI_STATUS Status; + NET_FRAGMENT *Fragment; + UINT32 FragmentCount; + UINT32 CurrentFragment; + + Tcp4RxData = NULL; + Tcp6RxData = NULL; + + if ((HttpInstance == NULL) || (Packet == NULL)) { + return EFI_INVALID_PARAMETER; + } + + FragmentCount = Packet->BlockOpNum; + Fragment = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT)); + if (Fragment == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Build the fragment table. + // + NetbufBuildExt (Packet, Fragment, &FragmentCount); + + if (!HttpInstance->LocalAddressIsIPv6) { + Tcp4RxData = HttpInstance->Tcp4TlsRxToken.Packet.RxData; + if (Tcp4RxData == NULL) { + return EFI_INVALID_PARAMETER; + } + Tcp4RxData->FragmentCount = 1; + } else { + Tcp6RxData = HttpInstance->Tcp6TlsRxToken.Packet.RxData; + if (Tcp6RxData == NULL) { + return EFI_INVALID_PARAMETER; + } + Tcp6RxData->FragmentCount = 1; + } + + CurrentFragment = 0; + Status = EFI_SUCCESS; + + while (CurrentFragment < FragmentCount) { + if (!HttpInstance->LocalAddressIsIPv6) { + Tcp4RxData->DataLength = Fragment[CurrentFragment].Len; + Tcp4RxData->FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len; + Tcp4RxData->FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk; + Status = HttpInstance->Tcp4->Receive (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken); + } else { + Tcp6RxData->DataLength = Fragment[CurrentFragment].Len; + Tcp6RxData->FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len; + Tcp6RxData->FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk; + Status = HttpInstance->Tcp6->Receive (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken); + } + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + while (!HttpInstance->TlsIsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { + // + // Poll until some data is received or an error occurs. + // + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } else { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + } + + if (!HttpInstance->TlsIsRxDone) { + // + // Timeout occurs, cancel the receive request. + // + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken); + } else { + HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken); + } + + Status = EFI_TIMEOUT; + goto ON_EXIT; + } else { + HttpInstance->TlsIsRxDone = FALSE; + } + + if (!HttpInstance->LocalAddressIsIPv6) { + Status = HttpInstance->Tcp4TlsRxToken.CompletionToken.Status; + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Fragment[CurrentFragment].Len -= Tcp4RxData->FragmentTable[0].FragmentLength; + if (Fragment[CurrentFragment].Len == 0) { + CurrentFragment++; + } else { + Fragment[CurrentFragment].Bulk += Tcp4RxData->FragmentTable[0].FragmentLength; + } + } else { + Status = HttpInstance->Tcp6TlsRxToken.CompletionToken.Status; + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Fragment[CurrentFragment].Len -= Tcp6RxData->FragmentTable[0].FragmentLength; + if (Fragment[CurrentFragment].Len == 0) { + CurrentFragment++; + } else { + Fragment[CurrentFragment].Bulk += Tcp6RxData->FragmentTable[0].FragmentLength; + } + } + } + +ON_EXIT: + + if (Fragment != NULL) { + FreePool (Fragment); + } + + return Status; +} + +/** + Receive one TLS PDU. An TLS PDU contains an TLS record header and it's + corresponding record data. These two parts will be put into two blocks of buffers in the + net buffer. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[out] Pdu The received TLS PDU. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS An TLS PDU is received. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_PROTOCOL_ERROR An unexpected TLS packet was received. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsReceiveOnePdu ( + IN OUT HTTP_PROTOCOL *HttpInstance, + OUT NET_BUF **Pdu, + IN EFI_EVENT Timeout + ) +{ + EFI_STATUS Status; + + LIST_ENTRY *NbufList; + + UINT32 Len; + + NET_BUF *PduHdr; + UINT8 *Header; + TLS_RECORD_HEADER RecordHeader; + + NET_BUF *DataSeg; + + NbufList = NULL; + PduHdr = NULL; + Header = NULL; + DataSeg = NULL; + + NbufList = AllocatePool (sizeof (LIST_ENTRY)); + if (NbufList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (NbufList); + + // + // Allocate buffer to receive one TLS header. + // + Len = sizeof (TLS_RECORD_HEADER); + PduHdr = NetbufAlloc (Len); + if (PduHdr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL); + if (Header == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // First step, receive one TLS header. + // + Status = TlsCommonReceive (HttpInstance, PduHdr, Timeout); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + RecordHeader = *(TLS_RECORD_HEADER *) Header; + if ((RecordHeader.ContentType == TlsContentTypeHandshake || + RecordHeader.ContentType == TlsContentTypeAlert || + RecordHeader.ContentType == TlsContentTypeChangeCipherSpec || + RecordHeader.ContentType == TlsContentTypeApplicationData) && + (RecordHeader.Version.Major == 0x03) && /// Major versions are same. + (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor ==TLS11_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) + ) { + InsertTailList (NbufList, &PduHdr->List); + } else { + Status = EFI_PROTOCOL_ERROR; + goto ON_EXIT; + } + + Len = SwapBytes16(RecordHeader.Length); + if (Len == 0) { + // + // No TLS payload. + // + goto FORM_PDU; + } + + // + // Allocate buffer to receive one TLS payload. + // + DataSeg = NetbufAlloc (Len); + if (DataSeg == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL); + + // + // Second step, receive one TLS payload. + // + Status = TlsCommonReceive (HttpInstance, DataSeg, Timeout); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + InsertTailList (NbufList, &DataSeg->List); + +FORM_PDU: + // + // Form the PDU from a list of PDU. + // + *Pdu = NetbufFromBufList (NbufList, 0, 0, FreeNbufList, NbufList); + if (*Pdu == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } + +ON_EXIT: + + if (EFI_ERROR (Status)) { + // + // Free the Nbufs in this NbufList and the NbufList itself. + // + FreeNbufList (NbufList); + } + + return Status; +} + +/** + Connect one TLS session by finishing the TLS handshake process. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS The TLS session is established. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_ABORTED TLS session state is incorrect. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsConnectSession ( + IN HTTP_PROTOCOL *HttpInstance, + IN EFI_EVENT Timeout + ) +{ + EFI_STATUS Status; + UINT8 *BufferOut; + UINTN BufferOutSize; + NET_BUF *PacketOut; + UINT8 *DataOut; + NET_BUF *Pdu; + UINT8 *BufferIn; + UINTN BufferInSize; + UINT8 *GetSessionDataBuffer; + UINTN GetSessionDataBufferSize; + + BufferOut = NULL; + PacketOut = NULL; + DataOut = NULL; + Pdu = NULL; + BufferIn = NULL; + + // + // Initialize TLS state. + // + HttpInstance->TlsSessionState = EfiTlsSessionNotStarted; + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + &(HttpInstance->TlsSessionState), + sizeof (EFI_TLS_SESSION_STATE) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create ClientHello + // + BufferOutSize = DEF_BUF_LEN; + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (BufferOut); + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + } + if (EFI_ERROR (Status)) { + FreePool (BufferOut); + return Status; + } + + // + // Transmit ClientHello + // + PacketOut = NetbufAlloc ((UINT32) BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + if (DataOut == NULL) { + FreePool (BufferOut); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (DataOut, BufferOut, BufferOutSize); + Status = TlsCommonTransmit (HttpInstance, PacketOut); + + FreePool (BufferOut); + NetbufFree (PacketOut); + + if (EFI_ERROR (Status)) { + return Status; + } + + while(HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring && \ + ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { + // + // Receive one TLS record. + // + Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout); + if (EFI_ERROR (Status)) { + return Status; + } + + BufferInSize = Pdu->TotalSize; + BufferIn = AllocateZeroPool (BufferInSize); + if (BufferIn == NULL) { + NetbufFree (Pdu); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + NetbufCopy (Pdu, 0, (UINT32)BufferInSize, BufferIn); + + NetbufFree (Pdu); + + // + // Handle Receive data. + // + BufferOutSize = DEF_BUF_LEN; + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + BufferIn, + BufferInSize, + BufferOut, + &BufferOutSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (BufferOut); + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + FreePool (BufferIn); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + BufferIn, + BufferInSize, + BufferOut, + &BufferOutSize + ); + } + + FreePool (BufferIn); + + if (EFI_ERROR (Status)) { + FreePool (BufferOut); + return Status; + } + + if (BufferOutSize != 0) { + // + // Transmit the response packet. + // + PacketOut = NetbufAlloc ((UINT32) BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + if (DataOut == NULL) { + FreePool (BufferOut); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (DataOut, BufferOut, BufferOutSize); + + Status = TlsCommonTransmit (HttpInstance, PacketOut); + + NetbufFree (PacketOut); + + if (EFI_ERROR (Status)) { + FreePool (BufferOut); + return Status; + } + } + + FreePool (BufferOut); + + // + // Get the session state, then decide whether need to continue handle received packet. + // + GetSessionDataBufferSize = DEF_BUF_LEN; + GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); + if (GetSessionDataBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->GetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + GetSessionDataBuffer, + &GetSessionDataBufferSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (GetSessionDataBuffer); + GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); + if (GetSessionDataBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->GetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + GetSessionDataBuffer, + &GetSessionDataBufferSize + ); + } + if (EFI_ERROR (Status)) { + FreePool(GetSessionDataBuffer); + return Status; + } + + ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE)); + HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer; + + FreePool (GetSessionDataBuffer); + + if(HttpInstance->TlsSessionState == EfiTlsSessionError) { + return EFI_ABORTED; + } + } + + if (HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring) { + Status = EFI_ABORTED; + } + + return Status; +} + +/** + Close the TLS session and send out the close notification message. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TLS session is closed. + @retval EFI_INVALID_PARAMETER HttpInstance is NULL. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsCloseSession ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + UINT8 *BufferOut; + UINTN BufferOutSize; + + NET_BUF *PacketOut; + UINT8 *DataOut; + + Status = EFI_SUCCESS; + BufferOut = NULL; + PacketOut = NULL; + DataOut = NULL; + + if (HttpInstance == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance->TlsSessionState = EfiTlsSessionClosing; + + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + &(HttpInstance->TlsSessionState), + sizeof (EFI_TLS_SESSION_STATE) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + BufferOutSize = DEF_BUF_LEN; + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (BufferOut); + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + } + + if (EFI_ERROR (Status)) { + FreePool (BufferOut); + return Status; + } + + PacketOut = NetbufAlloc ((UINT32) BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + if (DataOut == NULL) { + FreePool (BufferOut); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (DataOut, BufferOut, BufferOutSize); + + Status = TlsCommonTransmit (HttpInstance, PacketOut); + + FreePool (BufferOut); + NetbufFree (PacketOut); + + return Status; +} + +/** + Process one message according to the CryptMode. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Message Pointer to the message buffer needed to processed. + @param[in] MessageSize Pointer to the message buffer size. + @param[in] ProcessMode Process mode. + @param[in, out] Fragment Only one Fragment returned after the Message is + processed successfully. + + @retval EFI_SUCCESS Message is processed successfully. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +TlsProcessMessage ( + IN HTTP_PROTOCOL *HttpInstance, + IN UINT8 *Message, + IN UINTN MessageSize, + IN EFI_TLS_CRYPT_MODE ProcessMode, + IN OUT NET_FRAGMENT *Fragment + ) +{ + EFI_STATUS Status; + UINT8 *Buffer; + UINT32 BufferSize; + UINT32 BytesCopied; + EFI_TLS_FRAGMENT_DATA *FragmentTable; + UINT32 FragmentCount; + EFI_TLS_FRAGMENT_DATA *OriginalFragmentTable; + UINTN Index; + + Status = EFI_SUCCESS; + Buffer = NULL; + BufferSize = 0; + BytesCopied = 0; + FragmentTable = NULL; + OriginalFragmentTable = NULL; + + // + // Rebuild fragment table from BufferIn. + // + FragmentCount = 1; + FragmentTable = AllocateZeroPool (FragmentCount * sizeof (EFI_TLS_FRAGMENT_DATA)); + if (FragmentTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + FragmentTable->FragmentLength = (UINT32) MessageSize; + FragmentTable->FragmentBuffer = Message; + + // + // Record the original FragmentTable. + // + OriginalFragmentTable = FragmentTable; + + // + // Process the Message. + // + Status = HttpInstance->Tls->ProcessPacket ( + HttpInstance->Tls, + &FragmentTable, + &FragmentCount, + ProcessMode + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Calculate the size according to FragmentTable. + // + for (Index = 0; Index < FragmentCount; Index++) { + BufferSize += FragmentTable[Index].FragmentLength; + } + + // + // Allocate buffer for processed data. + // + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Copy the new FragmentTable buffer into Buffer. + // + for (Index = 0; Index < FragmentCount; Index++) { + CopyMem ( + (Buffer + BytesCopied), + FragmentTable[Index].FragmentBuffer, + FragmentTable[Index].FragmentLength + ); + BytesCopied += FragmentTable[Index].FragmentLength; + + // + // Free the FragmentBuffer since it has been copied. + // + FreePool (FragmentTable[Index].FragmentBuffer); + } + + Fragment->Len = BufferSize; + Fragment->Bulk = Buffer; + +ON_EXIT: + + if (OriginalFragmentTable != NULL) { + FreePool (OriginalFragmentTable); + OriginalFragmentTable = NULL; + } + + // + // Caller has the responsibility to free the FragmentTable. + // + if (FragmentTable != NULL) { + FreePool (FragmentTable); + FragmentTable = NULL; + } + + return Status; +} + +/** + Receive one fragment decrypted from one TLS record. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in, out] Fragment The received Fragment. + @param[in] Timeout The time to wait for connection done. + + @retval EFI_SUCCESS One fragment is received. + @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. + @retval EFI_ABORTED Something wrong decryption the message. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +HttpsReceive ( + IN HTTP_PROTOCOL *HttpInstance, + IN OUT NET_FRAGMENT *Fragment, + IN EFI_EVENT Timeout + ) +{ + EFI_STATUS Status; + NET_BUF *Pdu; + TLS_RECORD_HEADER RecordHeader; + UINT8 *BufferIn; + UINTN BufferInSize; + NET_FRAGMENT TempFragment; + UINT8 *BufferOut; + UINTN BufferOutSize; + NET_BUF *PacketOut; + UINT8 *DataOut; + UINT8 *GetSessionDataBuffer; + UINTN GetSessionDataBufferSize; + + Status = EFI_SUCCESS; + Pdu = NULL; + BufferIn = NULL; + BufferInSize = 0; + BufferOut = NULL; + BufferOutSize = 0; + PacketOut = NULL; + DataOut = NULL; + GetSessionDataBuffer = NULL; + GetSessionDataBufferSize = 0; + + // + // Receive only one TLS record + // + Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout); + if (EFI_ERROR (Status)) { + return Status; + } + + BufferInSize = Pdu->TotalSize; + BufferIn = AllocateZeroPool (BufferInSize); + if (BufferIn == NULL) { + Status = EFI_OUT_OF_RESOURCES; + NetbufFree (Pdu); + return Status; + } + + NetbufCopy (Pdu, 0, (UINT32) BufferInSize, BufferIn); + + NetbufFree (Pdu); + + // + // Handle Receive data. + // + RecordHeader = *(TLS_RECORD_HEADER *) BufferIn; + + if ((RecordHeader.ContentType == TlsContentTypeApplicationData) && + (RecordHeader.Version.Major == 0x03) && + (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) + ) { + // + // Decrypt Packet. + // + Status = TlsProcessMessage ( + HttpInstance, + BufferIn, + BufferInSize, + EfiTlsDecrypt, + &TempFragment + ); + + FreePool (BufferIn); + + if (EFI_ERROR (Status)) { + if (Status == EFI_ABORTED) { + // + // Something wrong decryption the message. + // BuildResponsePacket() will be called to generate Error Alert message and send it out. + // + BufferOutSize = DEF_BUF_LEN; + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (BufferOut); + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + NULL, + 0, + BufferOut, + &BufferOutSize + ); + } + if (EFI_ERROR (Status)) { + FreePool(BufferOut); + return Status; + } + + if (BufferOutSize != 0) { + PacketOut = NetbufAlloc ((UINT32)BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + if (DataOut == NULL) { + FreePool (BufferOut); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (DataOut, BufferOut, BufferOutSize); + + Status = TlsCommonTransmit (HttpInstance, PacketOut); + + NetbufFree (PacketOut); + } + + FreePool(BufferOut); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_ABORTED; + } + + return Status; + } + + // + // Parsing buffer. + // + ASSERT (((TLS_RECORD_HEADER *) (TempFragment.Bulk))->ContentType == TlsContentTypeApplicationData); + + BufferInSize = ((TLS_RECORD_HEADER *) (TempFragment.Bulk))->Length; + BufferIn = AllocateZeroPool (BufferInSize); + if (BufferIn == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + CopyMem (BufferIn, TempFragment.Bulk + sizeof (TLS_RECORD_HEADER), BufferInSize); + + // + // Free the buffer in TempFragment. + // + FreePool (TempFragment.Bulk); + + } else if ((RecordHeader.ContentType == TlsContentTypeAlert) && + (RecordHeader.Version.Major == 0x03) && + (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR || + RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) + ) { + BufferOutSize = DEF_BUF_LEN; + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + FreePool (BufferIn); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + BufferIn, + BufferInSize, + BufferOut, + &BufferOutSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (BufferOut); + BufferOut = AllocateZeroPool (BufferOutSize); + if (BufferOut == NULL) { + FreePool (BufferIn); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->BuildResponsePacket ( + HttpInstance->Tls, + BufferIn, + BufferInSize, + BufferOut, + &BufferOutSize + ); + } + + FreePool (BufferIn); + + if (EFI_ERROR (Status)) { + FreePool (BufferOut); + return Status; + } + + if (BufferOutSize != 0) { + PacketOut = NetbufAlloc ((UINT32) BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + if (DataOut == NULL) { + FreePool (BufferOut); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (DataOut, BufferOut, BufferOutSize); + + Status = TlsCommonTransmit (HttpInstance, PacketOut); + + NetbufFree (PacketOut); + } + + FreePool (BufferOut); + + // + // Get the session state. + // + GetSessionDataBufferSize = DEF_BUF_LEN; + GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); + if (GetSessionDataBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->GetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + GetSessionDataBuffer, + &GetSessionDataBufferSize + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (GetSessionDataBuffer); + GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); + if (GetSessionDataBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = HttpInstance->Tls->GetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + GetSessionDataBuffer, + &GetSessionDataBufferSize + ); + } + if (EFI_ERROR (Status)) { + FreePool (GetSessionDataBuffer); + return Status; + } + + ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE)); + HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer; + + FreePool (GetSessionDataBuffer); + + if(HttpInstance->TlsSessionState == EfiTlsSessionError) { + DEBUG ((EFI_D_ERROR, "TLS Session State Error!\n")); + return EFI_ABORTED; + } + + BufferIn = NULL; + BufferInSize = 0; + } + + Fragment->Bulk = BufferIn; + Fragment->Len = (UINT32) BufferInSize; + + return Status; +} +