| /** @file |
| Miscellaneous routines specific to Https for HttpDxe driver. |
| |
| Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> |
| (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> |
| This program and the accompanying materials |
| are licensed and made available under the terms and conditions of the BSD License |
| which accompanies this distribution. The full text of the license may be found at |
| http://opensource.org/licenses/bsd-license.php |
| |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. |
| |
| **/ |
| |
| #include "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 == TLS_CONTENT_TYPE_HANDSHAKE || |
| RecordHeader.ContentType == TLS_CONTENT_TYPE_ALERT || |
| RecordHeader.ContentType == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC || |
| RecordHeader.ContentType == TLS_CONTENT_TYPE_APPLICATION_DATA) && |
| (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); |
| |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| 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 == TLS_CONTENT_TYPE_APPLICATION_DATA) && |
| (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 == TLS_CONTENT_TYPE_APPLICATION_DATA); |
| |
| 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 == TLS_CONTENT_TYPE_ALERT) && |
| (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; |
| } |