From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 1E21281C62 for ; Tue, 13 Dec 2016 23:34:43 -0800 (PST) Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga104.fm.intel.com with ESMTP; 13 Dec 2016 23:34:42 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.33,345,1477983600"; d="scan'208";a="1098918557" Received: from jiaxinwu-mobl2.ccr.corp.intel.com ([10.239.196.26]) by fmsmga002.fm.intel.com with ESMTP; 13 Dec 2016 23:34:40 -0800 From: Jiaxin Wu To: edk2-devel@lists.01.org Cc: Ye Ting , Fu Siyuan , Zhang Lubo , Long Qin , Thomas Palmer , Wu Jiaxin Date: Wed, 14 Dec 2016 15:34:16 +0800 Message-Id: <1481700859-76060-8-git-send-email-jiaxin.wu@intel.com> X-Mailer: git-send-email 1.9.5.msysgit.1 In-Reply-To: <1481700859-76060-1-git-send-email-jiaxin.wu@intel.com> References: <1481700859-76060-1-git-send-email-jiaxin.wu@intel.com> MIME-Version: 1.0 Subject: [Patch 07/10] NetworkPkg/HttpDxe: HTTPS support over IPv4 and IPv6 X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 14 Dec 2016 07:34:43 -0000 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is used to enable HTTPS feature. HttpDxe driver will consume TlsDxe driver. It can both support http and https feature, that’s depended on the information of URL, the HTTP instance can be able to determine whether to use http or https. Cc: Ye Ting Cc: Fu Siyuan Cc: Zhang Lubo Cc: Long Qin Cc: Thomas Palmer Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Wu Jiaxin --- NetworkPkg/HttpDxe/HttpDriver.h | 10 +- NetworkPkg/HttpDxe/HttpDxe.inf | 12 +- NetworkPkg/HttpDxe/HttpImpl.c | 252 +++++- NetworkPkg/HttpDxe/HttpProto.c | 464 +++++++--- NetworkPkg/HttpDxe/HttpProto.h | 65 +- NetworkPkg/HttpDxe/HttpsSupport.c | 1692 +++++++++++++++++++++++++++++++++++++ NetworkPkg/HttpDxe/HttpsSupport.h | 260 ++++++ 7 files changed, 2601 insertions(+), 154 deletions(-) create mode 100644 NetworkPkg/HttpDxe/HttpsSupport.c create mode 100644 NetworkPkg/HttpDxe/HttpsSupport.h diff --git a/NetworkPkg/HttpDxe/HttpDriver.h b/NetworkPkg/HttpDxe/HttpDriver.h index fa2372c..93a412a 100644 --- a/NetworkPkg/HttpDxe/HttpDriver.h +++ b/NetworkPkg/HttpDxe/HttpDriver.h @@ -22,10 +22,11 @@ // // Libraries // #include +#include #include #include #include #include #include @@ -48,17 +49,23 @@ #include #include #include #include #include +#include +#include - +#include // // Produced Protocols // #include +#include + +#include + // // Driver Version // #define HTTP_DRIVER_VERSION 0xa @@ -77,10 +84,11 @@ extern EFI_HTTP_UTILITIES_PROTOCOL *mHttpUtilities; // Include files with function prototypes // #include "ComponentName.h" #include "HttpImpl.h" #include "HttpProto.h" +#include "HttpsSupport.h" #include "HttpDns.h" typedef struct { EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; UINTN NumberOfChildren; diff --git a/NetworkPkg/HttpDxe/HttpDxe.inf b/NetworkPkg/HttpDxe/HttpDxe.inf index bf2cbee..1118181 100644 --- a/NetworkPkg/HttpDxe/HttpDxe.inf +++ b/NetworkPkg/HttpDxe/HttpDxe.inf @@ -1,9 +1,9 @@ ## @file # Implementation of EFI HTTP protocol interfaces. # -# Copyright (c) 2015, Intel Corporation. All rights reserved.
+# Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
# # This program and the accompanying materials # are licensed and made available under the terms and conditions of the BSD License # which accompanies this distribution. The full text of the license may be found at # http://opensource.org/licenses/bsd-license.php. @@ -24,10 +24,11 @@ MODULE_UNI_FILE = HttpDxe.uni [Packages] MdePkg/MdePkg.dec MdeModulePkg/MdeModulePkg.dec + NetworkPkg/NetworkPkg.dec [Sources] ComponentName.h ComponentName.c HttpDns.h @@ -36,14 +37,17 @@ HttpDriver.c HttpImpl.h HttpImpl.c HttpProto.h HttpProto.c + HttpsSupport.h + HttpsSupport.c [LibraryClasses] UefiDriverEntryPoint UefiBootServicesTableLib + UefiRuntimeServicesTableLib MemoryAllocationLib BaseLib UefiLib DebugLib NetLib @@ -62,8 +66,14 @@ gEfiDns4ProtocolGuid ## SOMETIMES_CONSUMES gEfiDns6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES gEfiDns6ProtocolGuid ## SOMETIMES_CONSUMES gEfiIp4Config2ProtocolGuid ## SOMETIMES_CONSUMES gEfiIp6ConfigProtocolGuid ## SOMETIMES_CONSUMES + gEfiTlsServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiTlsProtocolGuid ## SOMETIMES_CONSUMES + gEfiTlsConfigurationProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + gEfiTlsCaCertificateGuid ## CONSUMES ## GUID [UserExtensions.TianoCore."ExtraFiles"] HttpDxeExtra.uni \ No newline at end of file diff --git a/NetworkPkg/HttpDxe/HttpImpl.c b/NetworkPkg/HttpDxe/HttpImpl.c index 6fcb0b7..77aa64a 100644 --- a/NetworkPkg/HttpDxe/HttpImpl.c +++ b/NetworkPkg/HttpDxe/HttpImpl.c @@ -239,10 +239,11 @@ EfiHttpRequest ( UINTN HostNameSize; UINT16 RemotePort; HTTP_PROTOCOL *HttpInstance; BOOLEAN Configure; BOOLEAN ReConfigure; + BOOLEAN TlsConfigure; CHAR8 *RequestMsg; CHAR8 *Url; UINTN UrlLen; CHAR16 *HostNameStr; HTTP_TOKEN_WRAP *Wrap; @@ -258,10 +259,11 @@ EfiHttpRequest ( HostName = NULL; RequestMsg = NULL; HostNameStr = NULL; Wrap = NULL; FileUrl = NULL; + TlsConfigure = FALSE; if ((This == NULL) || (Token == NULL)) { return EFI_INVALID_PARAMETER; } @@ -343,10 +345,36 @@ EfiHttpRequest ( HttpInstance->Url = Url; } UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen); + + // + // From the information in Url, the HTTP instance will + // be able to determine whether to use http or https. + // + HttpInstance->UseHttps = IsHttpsUrl (Url); + + // + // Check whether we need to create Tls child and open the TLS protocol. + // + if (HttpInstance->UseHttps && HttpInstance->TlsChildHandle == NULL) { + // + // Use TlsSb to create Tls child and open the TLS protocol. + // + HttpInstance->TlsChildHandle = TlsCreateChild ( + HttpInstance->Service->ImageHandle, + &(HttpInstance->Tls), + &(HttpInstance->TlsConfiguration) + ); + if (HttpInstance->TlsChildHandle == NULL) { + return EFI_DEVICE_ERROR; + } + + TlsConfigure = TRUE; + } + UrlParser = NULL; Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser); if (EFI_ERROR (Status)) { goto Error1; } @@ -357,11 +385,15 @@ EfiHttpRequest ( goto Error1; } Status = HttpUrlGetPort (Url, UrlParser, &RemotePort); if (EFI_ERROR (Status)) { - RemotePort = HTTP_DEFAULT_PORT; + if (HttpInstance->UseHttps) { + RemotePort = HTTPS_DEFAULT_PORT; + } else { + RemotePort = HTTP_DEFAULT_PORT; + } } // // If Configure is TRUE, it indicates the first time to call Request(); // If ReConfigure is TRUE, it indicates the request URL is not same // with the previous call to Request(); @@ -374,13 +406,17 @@ EfiHttpRequest ( // Request() is called the first time. // ReConfigure = FALSE; } else { if ((HttpInstance->RemotePort == RemotePort) && - (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) { + (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) && + (!HttpInstance->UseHttps || (HttpInstance->UseHttps && + !TlsConfigure && + HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))) { // // Host Name and port number of the request URL are the same with previous call to Request(). + // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring. // Check whether previous TCP packet sent out. // if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) { // @@ -480,10 +516,20 @@ EfiHttpRequest ( if (!HttpInstance->LocalAddressIsIPv6) { ASSERT (HttpInstance->Tcp4 != NULL); } else { ASSERT (HttpInstance->Tcp6 != NULL); } + + if (HttpInstance->UseHttps && !TlsConfigure) { + Status = TlsCloseSession (HttpInstance); + if (EFI_ERROR (Status)) { + goto Error1; + } + + TlsCloseTxRxEvent (HttpInstance); + } + HttpCloseConnection (HttpInstance); EfiHttpCancel (This, NULL); } // @@ -498,17 +544,22 @@ EfiHttpRequest ( Wrap->HttpToken = Token; Wrap->HttpInstance = HttpInstance; if (Request != NULL) { Wrap->TcpWrap.Method = Request->Method; } - - Status = HttpInitTcp (HttpInstance, Wrap, Configure); + + Status = HttpInitSession ( + HttpInstance, + Wrap, + Configure || ReConfigure, + TlsConfigure + ); if (EFI_ERROR (Status)) { goto Error2; - } + } - if (!Configure) { + if (!Configure && !ReConfigure && !TlsConfigure) { // // For the new HTTP token, create TX TCP token events. // Status = HttpCreateTcpTxEvent (Wrap); if (EFI_ERROR (Status)) { @@ -591,13 +642,18 @@ Error4: if (RequestMsg != NULL) { FreePool (RequestMsg); } Error3: - HttpCloseConnection (HttpInstance); + if (HttpInstance->UseHttps) { + TlsCloseSession (HttpInstance); + TlsCloseTxRxEvent (HttpInstance); + } Error2: + HttpCloseConnection (HttpInstance); + HttpCloseTcpConnCloseEvent (HttpInstance); if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) { gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL; } @@ -729,26 +785,34 @@ HttpCancel ( } else { return Status; } } - // - // Then check the tokens queued by EfiHttpResponse(). - // - Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token); - if (EFI_ERROR (Status)) { - if (Token != NULL) { - if (Status == EFI_ABORTED) { - return EFI_SUCCESS; + if (!HttpInstance->UseHttps) { + // + // Then check the tokens queued by EfiHttpResponse(), except for Https. + // + Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token); + if (EFI_ERROR (Status)) { + if (Token != NULL) { + if (Status == EFI_ABORTED) { + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } } else { - return EFI_NOT_FOUND; + return Status; } + } + } else { + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken); } else { - return Status; + HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken); } } - + return EFI_SUCCESS; } /** @@ -880,10 +944,11 @@ HttpResponseWorker ( HTTP_PROTOCOL *HttpInstance; EFI_HTTP_TOKEN *Token; NET_MAP_ITEM *Item; HTTP_TOKEN_WRAP *ValueInItem; UINTN HdrLen; + NET_FRAGMENT Fragment; if (Wrap == NULL || Wrap->HttpInstance == NULL) { return EFI_INVALID_PARAMETER; } @@ -897,21 +962,15 @@ HttpResponseWorker ( HttpHeaders = NULL; SizeofHeaders = 0; BufferSize = 0; EndofHeader = NULL; ValueInItem = NULL; + Fragment.Len = 0; + Fragment.Bulk = NULL; if (HttpMsg->Data.Response != NULL) { // - // Need receive the HTTP headers, prepare buffer. - // - Status = HttpCreateTcpRxEventForHeader (HttpInstance); - if (EFI_ERROR (Status)) { - goto Error; - } - - // // Check whether we have cached header from previous call. // if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) { // // The data is stored at [NextMsg, CacheBody + CacheLen]. @@ -1198,13 +1257,120 @@ HttpResponseWorker ( ASSERT (HttpInstance->MsgParser != NULL); // // We still need receive more data when there is no cache data and MsgParser is not NULL; // - Status = HttpTcpReceiveBody (Wrap, HttpMsg); - if (EFI_ERROR (Status)) { - goto Error2; + if (!HttpInstance->UseHttps) { + Status = HttpTcpReceiveBody (Wrap, HttpMsg); + + if (EFI_ERROR (Status)) { + goto Error2; + } + + } else { + if (HttpInstance->TimeoutEvent == NULL) { + // + // Create TimeoutEvent for response + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &HttpInstance->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + goto Error2; + } + } + + // + // Start the timer, and wait Timeout seconds to receive the body packet. + // + Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto Error2; + } + + Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent); + + gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); + + if (EFI_ERROR (Status)) { + goto Error2; + } + + // + // Check whether we receive a complete HTTP message. + // + Status = HttpParseMessageBody ( + HttpInstance->MsgParser, + (UINTN) Fragment.Len, + (CHAR8 *) Fragment.Bulk + ); + if (EFI_ERROR (Status)) { + goto Error2; + } + + if (HttpIsMessageComplete (HttpInstance->MsgParser)) { + // + // Free the MsgParse since we already have a full HTTP message. + // + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + + // + // We receive part of header of next HTTP msg. + // + if (HttpInstance->NextMsg != NULL) { + HttpMsg->BodyLength = MIN ((UINTN) (HttpInstance->NextMsg - (CHAR8 *) Fragment.Bulk), HttpMsg->BodyLength); + CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength); + + HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength; + if (HttpInstance->CacheLen != 0) { + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + } + + HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen); + if (HttpInstance->CacheBody == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error2; + } + + CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen); + HttpInstance->CacheOffset = 0; + + HttpInstance->NextMsg = HttpInstance->CacheBody + (UINTN) (HttpInstance->NextMsg - (CHAR8 *) (Fragment.Bulk + HttpMsg->BodyLength)); + } + } else { + HttpMsg->BodyLength = MIN (Fragment.Len, (UINT32) HttpMsg->BodyLength); + CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength); + HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength; + if (HttpInstance->CacheLen != 0) { + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + } + + HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen); + if (HttpInstance->CacheBody == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error2; + } + + CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen); + HttpInstance->CacheOffset = 0; + } + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + + goto Exit; } return Status; Exit: @@ -1232,19 +1398,30 @@ Error2: Error: Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); if (Item != NULL) { NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); } - - HttpTcpTokenCleanup (Wrap); + + if (!HttpInstance->UseHttps) { + HttpTcpTokenCleanup (Wrap); + } else { + FreePool (Wrap); + } if (HttpHeaders != NULL) { FreePool (HttpHeaders); + HttpHeaders = NULL; + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; } if (HttpMsg->Headers != NULL) { FreePool (HttpMsg->Headers); + HttpMsg->Headers = NULL; } if (HttpInstance->CacheBody != NULL) { FreePool (HttpInstance->CacheBody); HttpInstance->CacheBody = NULL; @@ -1351,13 +1528,20 @@ EfiHttpResponse ( } Wrap->HttpInstance = HttpInstance; Wrap->HttpToken = Token; - Status = HttpCreateTcpRxEvent (Wrap); - if (EFI_ERROR (Status)) { - goto Error; + // + // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to + // receive the https response. A special TlsRxToken is used for receiving TLS + // related messages. It should be a blocking response. + // + if (!HttpInstance->UseHttps) { + Status = HttpCreateTcpRxEvent (Wrap); + if (EFI_ERROR (Status)) { + goto Error; + } } Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap); if (EFI_ERROR (Status)) { goto Error; diff --git a/NetworkPkg/HttpDxe/HttpProto.c b/NetworkPkg/HttpDxe/HttpProto.c index 6373f07..77a3ee3 100644 --- a/NetworkPkg/HttpDxe/HttpProto.c +++ b/NetworkPkg/HttpDxe/HttpProto.c @@ -926,10 +926,26 @@ HttpCleanProtocol ( HttpInstance->Service->ImageHandle, HttpInstance->Handle ); } + if (HttpInstance->TlsConfigData.CACert != NULL) { + FreePool (HttpInstance->TlsConfigData.CACert); + HttpInstance->TlsConfigData.CACert = NULL; + } + + if (HttpInstance->TlsConfigData.ClientCert != NULL) { + FreePool (HttpInstance->TlsConfigData.ClientCert); + HttpInstance->TlsConfigData.ClientCert = NULL; + } + + if (HttpInstance->TlsConfigData.ClientPrivateKey != NULL) { + FreePool (HttpInstance->TlsConfigData.ClientPrivateKey); + HttpInstance->TlsConfigData.ClientPrivateKey = NULL; + } + + TlsCloseTxRxEvent (HttpInstance); } /** Establish TCP connection with HTTP server. @@ -1183,11 +1199,12 @@ HttpConfigureTcp6 ( return EFI_SUCCESS; } /** - Check existing TCP connection, if in error state, recover TCP4 connection. + Check existing TCP connection, if in error state, recover TCP4 connection. Then, + connect one TLS session if required. @param[in] HttpInstance The HTTP instance private data. @retval EFI_SUCCESS The TCP connection is established. @retval EFI_NOT_READY TCP4 protocol child is not created or configured. @@ -1224,15 +1241,62 @@ HttpConnectTcp4 ( return EFI_SUCCESS; } else if (Tcp4State > Tcp4StateEstablished ) { HttpCloseConnection(HttpInstance); } - return HttpCreateConnection (HttpInstance); + Status = HttpCreateConnection (HttpInstance); + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp4 Connection fail - %x\n", Status)); + return Status; + } + + // + // Tls session connection. + // + if (HttpInstance->UseHttps) { + if (HttpInstance->TimeoutEvent == NULL) { + // + // Create TimeoutEvent for TLS connection. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &HttpInstance->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + // + // Start the timer, and wait Timeout seconds for connection. + // + Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_CONNECTION_TIMEOUT * TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + + Status = TlsConnectSession (HttpInstance, HttpInstance->TimeoutEvent); + + gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); + + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + return Status; } /** - Check existing TCP connection, if in error state, recover TCP6 connection. + Check existing TCP connection, if in error state, recover TCP6 connection. Then, + connect one TLS session if required. @param[in] HttpInstance The HTTP instance private data. @retval EFI_SUCCESS The TCP connection is established. @retval EFI_NOT_READY TCP6 protocol child is not created or configured. @@ -1269,34 +1333,92 @@ HttpConnectTcp6 ( return EFI_SUCCESS; } else if (Tcp6State > Tcp6StateEstablished ) { HttpCloseConnection(HttpInstance); } - return HttpCreateConnection (HttpInstance); + Status = HttpCreateConnection (HttpInstance); + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp6 Connection fail - %x\n", Status)); + return Status; + } + + // + // Tls session connection. + // + if (HttpInstance->UseHttps) { + if (HttpInstance->TimeoutEvent == NULL) { + // + // Create TimeoutEvent for TLS connection. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &HttpInstance->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + // + // Start the timer, and wait Timeout seconds for connection. + // + Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_CONNECTION_TIMEOUT * TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + + Status = TlsConnectSession (HttpInstance, HttpInstance->TimeoutEvent); + + gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); + + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + return Status; } /** - Initialize TCP related data. + Initialize Http session. @param[in] HttpInstance The HTTP instance private data. @param[in] Wrap The HTTP token's wrap data. - @param[in] Configure The Flag indicates whether the first time to initialize Tcp. + @param[in] Configure The Flag indicates whether need to initialize session. + @param[in] TlsConfigure The Flag indicates whether it's the new Tls session. - @retval EFI_SUCCESS The initialization of TCP instance is done. + @retval EFI_SUCCESS The initialization of session is done. @retval Others Other error as indicated. **/ EFI_STATUS -HttpInitTcp ( +HttpInitSession ( IN HTTP_PROTOCOL *HttpInstance, IN HTTP_TOKEN_WRAP *Wrap, - IN BOOLEAN Configure + IN BOOLEAN Configure, + IN BOOLEAN TlsConfigure ) { EFI_STATUS Status; ASSERT (HttpInstance != NULL); + // + // Configure Tls session. + // + if (TlsConfigure) { + Status = TlsConfigureSession (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } + if (!HttpInstance->LocalAddressIsIPv6) { // // Configure TCP instance. // if (Configure) { @@ -1336,11 +1458,11 @@ HttpInitTcp ( return EFI_SUCCESS; } /** - Send the HTTP message through TCP4 or TCP6. + Send the HTTP or HTTPS message through TCP4 or TCP6. @param[in] HttpInstance The HTTP instance private data. @param[in] Wrap The HTTP token's wrap data. @param[in] TxString Buffer containing the HTTP message string. @param[in] TxStringLen Length of the HTTP message string in bytes. @@ -1360,18 +1482,68 @@ HttpTransmitTcp ( EFI_STATUS Status; EFI_TCP4_IO_TOKEN *Tx4Token; EFI_TCP4_PROTOCOL *Tcp4; EFI_TCP6_IO_TOKEN *Tx6Token; EFI_TCP6_PROTOCOL *Tcp6; + UINT8 *Buffer; + UINTN BufferSize; + NET_FRAGMENT TempFragment; + + Status = EFI_SUCCESS; + Buffer = NULL; + + // + // Need to encrypt data. + // + if (HttpInstance->UseHttps) { + // + // Build BufferOut data + // + BufferSize = sizeof (TLS_RECORD_HEADER) + TxStringLen; + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + ((TLS_RECORD_HEADER *) Buffer)->ContentType = TLS_CONTENT_TYPE_APPLICATION_DATA; + ((TLS_RECORD_HEADER *) Buffer)->Version.Major = HttpInstance->TlsConfigData.Version.Major; + ((TLS_RECORD_HEADER *) Buffer)->Version.Minor = HttpInstance->TlsConfigData.Version.Minor; + ((TLS_RECORD_HEADER *) Buffer)->Length = (UINT16) (TxStringLen); + CopyMem (Buffer + sizeof (TLS_RECORD_HEADER), TxString, TxStringLen); + + // + // Encrypt Packet. + // + Status = TlsProcessMessage ( + HttpInstance, + Buffer, + BufferSize, + EfiTlsEncrypt, + &TempFragment + ); + + FreePool (Buffer); + + if (EFI_ERROR (Status)) { + return Status; + } + } - if (!HttpInstance->LocalAddressIsIPv6) { + if (!HttpInstance->LocalAddressIsIPv6) { Tcp4 = HttpInstance->Tcp4; Tx4Token = &Wrap->TcpWrap.Tx4Token; + + if (HttpInstance->UseHttps) { + Tx4Token->Packet.TxData->DataLength = TempFragment.Len; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = TempFragment.Len; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TempFragment.Bulk; + } else { + Tx4Token->Packet.TxData->DataLength = (UINT32) TxStringLen; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + } - Tx4Token->Packet.TxData->DataLength = (UINT32) TxStringLen; - Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; - Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; Tx4Token->CompletionToken.Status = EFI_NOT_READY; Wrap->TcpWrap.IsTxDone = FALSE; Status = Tcp4->Transmit (Tcp4, Tx4Token); if (EFI_ERROR (Status)) { @@ -1380,25 +1552,31 @@ HttpTransmitTcp ( } } else { Tcp6 = HttpInstance->Tcp6; Tx6Token = &Wrap->TcpWrap.Tx6Token; - - Tx6Token->Packet.TxData->DataLength = (UINT32) TxStringLen; - Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; - Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + + if (HttpInstance->UseHttps) { + Tx6Token->Packet.TxData->DataLength = TempFragment.Len; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = TempFragment.Len; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TempFragment.Bulk; + } else { + Tx6Token->Packet.TxData->DataLength = (UINT32) TxStringLen; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + } + Tx6Token->CompletionToken.Status = EFI_NOT_READY; Wrap->TcpWrap.IsTxDone = FALSE; Status = Tcp6->Transmit (Tcp6, Tx6Token); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); return Status; } } - return Status; } /** Check whether the user's token or event has already @@ -1464,11 +1642,11 @@ HttpTcpNotReady ( return EFI_SUCCESS; } /** - Transmit the HTTP mssage by processing the associated HTTP token. + Transmit the HTTP or HTTPS mssage by processing the associated HTTP token. @param[in] Map The container of Tx4Token or Tx6Token. @param[in] Item Current item to check against. @param[in] Context The Token to check againist. @@ -1588,167 +1766,235 @@ HttpTcpReceiveHeader ( EFI_TCP6_IO_TOKEN *Rx6Token; EFI_TCP6_PROTOCOL *Tcp6; CHAR8 **EndofHeader; CHAR8 **HttpHeaders; CHAR8 *Buffer; + NET_FRAGMENT Fragment; ASSERT (HttpInstance != NULL); EndofHeader = HttpInstance->EndofHeader; HttpHeaders = HttpInstance->HttpHeaders; Tcp4 = HttpInstance->Tcp4; Tcp6 = HttpInstance->Tcp6; Buffer = NULL; Rx4Token = NULL; Rx6Token = NULL; + Fragment.Len = 0; + Fragment.Bulk = NULL; if (HttpInstance->LocalAddressIsIPv6) { ASSERT (Tcp6 != NULL); } else { ASSERT (Tcp4 != NULL); } - if (!HttpInstance->LocalAddressIsIPv6) { - Rx4Token = &HttpInstance->Rx4Token; - Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); - if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { - Status = EFI_OUT_OF_RESOURCES; + if (!HttpInstance->UseHttps) { + Status = HttpCreateTcpRxEventForHeader (HttpInstance); + if (EFI_ERROR (Status)) { return Status; } + } + + if (!HttpInstance->LocalAddressIsIPv6) { + if (!HttpInstance->UseHttps) { + Rx4Token = &HttpInstance->Rx4Token; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + } // // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. // - while (*EndofHeader == NULL) { - HttpInstance->IsRxDone = FALSE; - Rx4Token->Packet.RxData->DataLength = DEF_BUF_LEN; - Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; - Status = Tcp4->Receive (Tcp4, Rx4Token); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); - return Status; - } - - while (!HttpInstance->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { - Tcp4->Poll (Tcp4); + while (*EndofHeader == NULL) { + if (!HttpInstance->UseHttps) { + HttpInstance->IsRxDone = FALSE; + Rx4Token->Packet.RxData->DataLength = DEF_BUF_LEN; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp4->Receive (Tcp4, Rx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { + Tcp4->Poll (Tcp4); + } + + if (!HttpInstance->IsRxDone) { + // + // Cancle the Token before close its Event. + // + Tcp4->Cancel (HttpInstance->Tcp4, &Rx4Token->CompletionToken); + gBS->CloseEvent (Rx4Token->CompletionToken.Event); + Rx4Token->CompletionToken.Status = EFI_TIMEOUT; + } + + Status = Rx4Token->CompletionToken.Status; + if (EFI_ERROR (Status)) { + return Status; + } + + Fragment.Len = Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength; + Fragment.Bulk = (UINT8 *) Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer; + } else { + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + + Status = HttpsReceive (HttpInstance, &Fragment, Timeout); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } } - if (!HttpInstance->IsRxDone) { - // - // Cancle the Token before close its Event. - // - Tcp4->Cancel (HttpInstance->Tcp4, &Rx4Token->CompletionToken); - gBS->CloseEvent (Rx4Token->CompletionToken.Event); - Rx4Token->CompletionToken.Status = EFI_TIMEOUT; - } - - Status = Rx4Token->CompletionToken.Status; - if (EFI_ERROR (Status)) { - return Status; - } - // // Append the response string. // - *BufferSize = (*SizeofHeaders) + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength; + *BufferSize = *SizeofHeaders + Fragment.Len; Buffer = AllocateZeroPool (*BufferSize); if (Buffer == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } - + if (*HttpHeaders != NULL) { - CopyMem (Buffer, *HttpHeaders, (*SizeofHeaders)); + CopyMem (Buffer, *HttpHeaders, *SizeofHeaders); FreePool (*HttpHeaders); } - + CopyMem ( - Buffer + (*SizeofHeaders), - Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer, - Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength + Buffer + *SizeofHeaders, + Fragment.Bulk, + Fragment.Len ); - *HttpHeaders = Buffer; - *SizeofHeaders = *BufferSize; - + *HttpHeaders = Buffer; + *SizeofHeaders = *BufferSize; + // // Check whether we received end of HTTP headers. // *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); - } - FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); - Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + }; + // + // Free the buffer. + // + if (Rx4Token != NULL && Rx4Token->Packet.RxData != NULL && Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + Fragment.Bulk = NULL; + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } } else { - Rx6Token = &HttpInstance->Rx6Token; - Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); - if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { - Status = EFI_OUT_OF_RESOURCES; - return Status; + if (!HttpInstance->UseHttps) { + Rx6Token = &HttpInstance->Rx6Token; + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } } // // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. // - while (*EndofHeader == NULL) { - HttpInstance->IsRxDone = FALSE; - Rx6Token->Packet.RxData->DataLength = DEF_BUF_LEN; - Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; - Status = Tcp6->Receive (Tcp6, Rx6Token); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); - return Status; - } - - while (!HttpInstance->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { - Tcp6->Poll (Tcp6); + while (*EndofHeader == NULL) { + if (!HttpInstance->UseHttps) { + HttpInstance->IsRxDone = FALSE; + Rx6Token->Packet.RxData->DataLength = DEF_BUF_LEN; + Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp6->Receive (Tcp6, Rx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { + Tcp6->Poll (Tcp6); + } + + if (!HttpInstance->IsRxDone) { + // + // Cancle the Token before close its Event. + // + Tcp6->Cancel (HttpInstance->Tcp6, &Rx6Token->CompletionToken); + gBS->CloseEvent (Rx6Token->CompletionToken.Event); + Rx6Token->CompletionToken.Status = EFI_TIMEOUT; + } + + Status = Rx6Token->CompletionToken.Status; + if (EFI_ERROR (Status)) { + return Status; + } + + Fragment.Len = Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength; + Fragment.Bulk = (UINT8 *) Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer; + } else { + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + + Status = HttpsReceive (HttpInstance, &Fragment, Timeout); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } } - if (!HttpInstance->IsRxDone) { - // - // Cancle the Token before close its Event. - // - Tcp6->Cancel (HttpInstance->Tcp6, &Rx6Token->CompletionToken); - gBS->CloseEvent (Rx6Token->CompletionToken.Event); - Rx6Token->CompletionToken.Status = EFI_TIMEOUT; - } - - Status = Rx6Token->CompletionToken.Status; - if (EFI_ERROR (Status)) { - return Status; - } - // // Append the response string. // - *BufferSize = (*SizeofHeaders) + Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength; + *BufferSize = *SizeofHeaders + Fragment.Len; Buffer = AllocateZeroPool (*BufferSize); if (Buffer == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } - + if (*HttpHeaders != NULL) { - CopyMem (Buffer, *HttpHeaders, (*SizeofHeaders)); + CopyMem (Buffer, *HttpHeaders, *SizeofHeaders); FreePool (*HttpHeaders); } - + CopyMem ( - Buffer + (*SizeofHeaders), - Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer, - Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength + Buffer + *SizeofHeaders, + Fragment.Bulk, + Fragment.Len ); - *HttpHeaders = Buffer; - *SizeofHeaders = *BufferSize; - + *HttpHeaders = Buffer; + *SizeofHeaders = *BufferSize; + // // Check whether we received end of HTTP headers. // - *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); - + *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); + }; + + // + // Free the buffer. + // + if (Rx6Token != NULL && Rx6Token->Packet.RxData != NULL && Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + Fragment.Bulk = NULL; + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; } - FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); - Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; } // // Skip the CRLF after the HTTP headers. // diff --git a/NetworkPkg/HttpDxe/HttpProto.h b/NetworkPkg/HttpDxe/HttpProto.h index e1fd785..e3b3275 100644 --- a/NetworkPkg/HttpDxe/HttpProto.h +++ b/NetworkPkg/HttpDxe/HttpProto.h @@ -81,10 +81,24 @@ typedef struct { BOOLEAN IsRxDone; UINTN BodyLen; EFI_HTTP_METHOD Method; } HTTP_TCP_TOKEN_WRAP; +typedef struct { + EFI_TLS_VERSION Version; + EFI_TLS_CONNECTION_END ConnectionEnd; + EFI_TLS_VERIFY VerifyMethod; + EFI_TLS_SESSION_STATE SessionState; + + VOID *CACert; + UINTN CACertSize; + VOID *ClientCert; + UINTN ClientCertSize; + VOID *ClientPrivateKey; + UINTN ClientPrivateKeySize; +} TLS_CONFIG_DATA; + typedef struct _HTTP_PROTOCOL { UINT32 Signature; EFI_HTTP_PROTOCOL Http; EFI_HANDLE Handle; HTTP_SERVICE *Service; @@ -151,10 +165,39 @@ typedef struct _HTTP_PROTOCOL { NET_MAP TxTokens; NET_MAP RxTokens; CHAR8 *Url; + + // + // Https Support + // + BOOLEAN UseHttps; + + EFI_HANDLE TlsChildHandle; /// Tls ChildHandle + TLS_CONFIG_DATA TlsConfigData; + EFI_TLS_PROTOCOL *Tls; + EFI_TLS_CONFIGURATION_PROTOCOL *TlsConfiguration; + EFI_TLS_SESSION_STATE TlsSessionState; + + // + // TlsTxData used for transmitting TLS related messages. + // + EFI_TCP4_IO_TOKEN Tcp4TlsTxToken; + EFI_TCP4_TRANSMIT_DATA Tcp4TlsTxData; + EFI_TCP6_IO_TOKEN Tcp6TlsTxToken; + EFI_TCP6_TRANSMIT_DATA Tcp6TlsTxData; + BOOLEAN TlsIsTxDone; + + // + // TlsRxData used for receiving TLS related messages. + // + EFI_TCP4_IO_TOKEN Tcp4TlsRxToken; + EFI_TCP4_RECEIVE_DATA Tcp4TlsRxData; + EFI_TCP6_IO_TOKEN Tcp6TlsRxToken; + EFI_TCP6_RECEIVE_DATA Tcp6TlsRxData; + BOOLEAN TlsIsRxDone; } HTTP_PROTOCOL; typedef struct { EFI_HTTP_TOKEN *HttpToken; HTTP_PROTOCOL *HttpInstance; @@ -350,11 +393,12 @@ HttpConfigureTcp6 ( IN HTTP_PROTOCOL *HttpInstance, IN HTTP_TOKEN_WRAP *Wrap ); /** - Check existing TCP connection, if in error state, receover TCP4 connection. + Check existing TCP connection, if in error state, recover TCP4 connection. Then, + connect one TLS session if required. @param[in] HttpInstance The HTTP instance private data. @retval EFI_SUCCESS The TCP connection is established. @retval EFI_NOT_READY TCP4 protocol child is not created or configured. @@ -365,11 +409,12 @@ EFI_STATUS HttpConnectTcp4 ( IN HTTP_PROTOCOL *HttpInstance ); /** - Check existing TCP connection, if in error state, recover TCP6 connection. + Check existing TCP connection, if in error state, recover TCP6 connection. Then, + connect one TLS session if required. @param[in] HttpInstance The HTTP instance private data. @retval EFI_SUCCESS The TCP connection is established. @retval EFI_NOT_READY TCP6 protocol child is not created or configured. @@ -380,11 +425,11 @@ EFI_STATUS HttpConnectTcp6 ( IN HTTP_PROTOCOL *HttpInstance ); /** - Send the HTTP message through TCP4 or TCP6. + Send the HTTP or HTTPS message through TCP4 or TCP6. @param[in] HttpInstance The HTTP instance private data. @param[in] Wrap The HTTP token's wrap data. @param[in] TxString Buffer containing the HTTP message string. @param[in] TxStringLen Length of the HTTP message string in bytes. @@ -441,29 +486,31 @@ HttpTcpNotReady ( IN NET_MAP_ITEM *Item, IN VOID *Context ); /** - Initialize TCP related data. + Initialize Http session. @param[in] HttpInstance The HTTP instance private data. @param[in] Wrap The HTTP token's wrap data. - @param[in] Configure The Flag indicates whether the first time to initialize Tcp. + @param[in] Configure The Flag indicates whether need to initialize session. + @param[in] TlsConfigure The Flag indicates whether it's the new Tls session. - @retval EFI_SUCCESS The initialization of TCP instance is done. + @retval EFI_SUCCESS The initialization of session is done. @retval Others Other error as indicated. **/ EFI_STATUS -HttpInitTcp ( +HttpInitSession ( IN HTTP_PROTOCOL *HttpInstance, IN HTTP_TOKEN_WRAP *Wrap, - IN BOOLEAN Configure + IN BOOLEAN Configure, + IN BOOLEAN TlsConfigure ); /** - Transmit the HTTP mssage by processing the associated HTTP token. + Transmit the HTTP or HTTPS mssage by processing the associated HTTP token. @param[in] Map The container of TxToken or Tx6Token. @param[in] Item Current item to check against. @param[in] Context The Token to check againist. diff --git a/NetworkPkg/HttpDxe/HttpsSupport.c b/NetworkPkg/HttpDxe/HttpsSupport.c new file mode 100644 index 0000000..177b9a8 --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpsSupport.c @@ -0,0 +1,1692 @@ +/** @file + Miscellaneous routines specific to Https for HttpDxe driver. + +Copyright (c) 2016, 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 = (UINT8 *) HttpInstance->TlsConfigData.CACert; + CACertSize = HttpInstance->TlsConfigData.CACertSize; + + // + // Try to read the TlsCaCertificate variable. + // + CACertSize = 0; + Status = gRT->GetVariable ( + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + NULL, + &CACertSize, + NULL + ); + + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // 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; + } + } + + // + // 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)) { + return Status; + } + + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); + } + + ItemDataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); + } + + 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)) { + goto ERROR; + } + + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsVerifyMethod, + &HttpInstance->TlsConfigData.VerifyMethod, + sizeof (EFI_TLS_VERIFY) + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + Status = HttpInstance->Tls->SetSessionData ( + HttpInstance->Tls, + EfiTlsSessionState, + &(HttpInstance->TlsConfigData.SessionState), + sizeof (EFI_TLS_SESSION_STATE) + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Tls Config Certificate + // + Status = TlsConfigCertificate (HttpInstance); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TLS Certificate Config Error!\n")); + goto ERROR; + } + + // + // 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. This 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); + 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)) { + return Status; + } + + if (BufferOutSize != 0) { + // + // Transmit the response packet. + // + PacketOut = NetbufAlloc ((UINT32) BufferOutSize); + DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); + 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); + 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); + 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); + 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; +} diff --git a/NetworkPkg/HttpDxe/HttpsSupport.h b/NetworkPkg/HttpDxe/HttpsSupport.h new file mode 100644 index 0000000..d846b76 --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpsSupport.h @@ -0,0 +1,260 @@ +/** @file + The header files of miscellaneous routines specific to Https for HttpDxe driver. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTPS_SUPPORT_H__ +#define __EFI_HTTPS_SUPPORT_H__ + +#define HTTPS_DEFAULT_PORT 443 + +#define HTTPS_FLAG "https" + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Close events in the TlsTxToken and TlsRxToken. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +EFIAPI +TlsCloseTxRxEvent ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Receive one TLS PDU. An TLS PDU contains an TLS record header and it's + corresponding record data. The 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 + ); + +/** + 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 + ); + +/** + 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 or Packet 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +#endif -- 1.9.5.msysgit.1