NetworkPkg/HttpBootDxe: Add Support for HTTP Boot Basic Authentication

REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2504

Add support for TLS Client Authentication using Basic Authentication
for HTTP Boot

Cc: Maciej Rabeda <maciej.rabeda@linux.intel.com>
Cc: Wu Jiaxin <jiaxin.wu@intel.com>
Cc: Siyuan Fu <siyuan.fu@intel.com>
Signed-off-by: Saloni Kasbekar <saloni.kasbekar@intel.com>
Reviewed-by: Maciej Rabeda <maciej.rabeda@linux.intel.com>
This commit is contained in:
Saloni Kasbekar
2022-07-19 06:54:22 -07:00
committed by mergify[bot]
parent 19a8768365
commit 671b0cea51
6 changed files with 143 additions and 5 deletions

View File

@ -204,6 +204,14 @@
/// ///
#define HTTP_HEADER_IF_NONE_MATCH "If-None-Match" #define HTTP_HEADER_IF_NONE_MATCH "If-None-Match"
///
/// The WWW-Authenticate Response Header
/// If a server receives a request for an access-protected object, and an
/// acceptable Authorization header is not sent, the server responds with
/// a "401 Unauthorized" status code, and a WWW-Authenticate header.
///
#define HTTP_HEADER_WWW_AUTHENTICATE "WWW-Authenticate"
/// ///
/// Authorization Request Header /// Authorization Request Header
/// The Authorization field value consists of credentials /// The Authorization field value consists of credentials

View File

@ -32,7 +32,7 @@ typedef enum {
/// ///
HttpBootDhcp6, HttpBootDhcp6,
/// ///
/// Data points to an EFI_HTTP_MESSAGE structure, whichcontians a HTTP request message /// Data points to an EFI_HTTP_MESSAGE structure, which contains a HTTP request message
/// to be transmitted. /// to be transmitted.
/// ///
HttpBootHttpRequest, HttpBootHttpRequest,
@ -46,6 +46,10 @@ typedef enum {
/// buffer of the entity body data. /// buffer of the entity body data.
/// ///
HttpBootHttpEntityBody, HttpBootHttpEntityBody,
///
/// Data points to the authentication information to provide to the HTTP server.
///
HttpBootHttpAuthInfo,
HttpBootTypeMax HttpBootTypeMax
} EFI_HTTP_BOOT_CALLBACK_DATA_TYPE; } EFI_HTTP_BOOT_CALLBACK_DATA_TYPE;

View File

@ -922,6 +922,7 @@ HttpBootGetBootFileCallback (
@retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
BufferSize has been updated with the size needed to complete BufferSize has been updated with the size needed to complete
the request. the request.
@retval EFI_ACCESS_DENIED The server needs to authenticate the client.
@retval Others Unexpected error happened. @retval Others Unexpected error happened.
**/ **/
@ -951,6 +952,9 @@ HttpBootGetBootFile (
CHAR16 *Url; CHAR16 *Url;
BOOLEAN IdentityMode; BOOLEAN IdentityMode;
UINTN ReceivedSize; UINTN ReceivedSize;
CHAR8 BaseAuthValue[80];
EFI_HTTP_HEADER *HttpHeader;
CHAR8 *Data;
ASSERT (Private != NULL); ASSERT (Private != NULL);
ASSERT (Private->HttpCreated); ASSERT (Private->HttpCreated);
@ -1009,8 +1013,9 @@ HttpBootGetBootFile (
// Host // Host
// Accept // Accept
// User-Agent // User-Agent
// [Authorization]
// //
HttpIoHeader = HttpIoCreateHeader (3); HttpIoHeader = HttpIoCreateHeader ((Private->AuthData != NULL) ? 4 : 3);
if (HttpIoHeader == NULL) { if (HttpIoHeader == NULL) {
Status = EFI_OUT_OF_RESOURCES; Status = EFI_OUT_OF_RESOURCES;
goto ERROR_2; goto ERROR_2;
@ -1063,6 +1068,35 @@ HttpBootGetBootFile (
goto ERROR_3; goto ERROR_3;
} }
//
// Add HTTP header field 4: Authorization
//
if (Private->AuthData != NULL) {
ASSERT (HttpIoHeader->MaxHeaderCount == 4);
if ((Private->AuthScheme != NULL) && (CompareMem (Private->AuthScheme, "Basic", 5) != 0)) {
Status = EFI_UNSUPPORTED;
goto ERROR_3;
}
AsciiSPrint (
BaseAuthValue,
sizeof (BaseAuthValue),
"%a %a",
"Basic",
Private->AuthData
);
Status = HttpIoSetHeader (
HttpIoHeader,
HTTP_HEADER_AUTHORIZATION,
BaseAuthValue
);
if (EFI_ERROR (Status)) {
goto ERROR_3;
}
}
// //
// 2.2 Build the rest of HTTP request info. // 2.2 Build the rest of HTTP request info.
// //
@ -1111,6 +1145,7 @@ HttpBootGetBootFile (
goto ERROR_4; goto ERROR_4;
} }
Data = NULL;
Status = HttpIoRecvResponse ( Status = HttpIoRecvResponse (
&Private->HttpIo, &Private->HttpIo,
TRUE, TRUE,
@ -1121,6 +1156,68 @@ HttpBootGetBootFile (
StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode; StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;
HttpBootPrintErrorMessage (StatusCode); HttpBootPrintErrorMessage (StatusCode);
Status = ResponseData->Status; Status = ResponseData->Status;
if ((StatusCode == HTTP_STATUS_401_UNAUTHORIZED) || \
(StatusCode == HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED))
{
if ((Private->AuthData != NULL) || (Private->AuthScheme != NULL)) {
if (Private->AuthData != NULL) {
FreePool (Private->AuthData);
Private->AuthData = NULL;
}
if (Private->AuthScheme != NULL) {
FreePool (Private->AuthScheme);
Private->AuthScheme = NULL;
}
Status = EFI_ACCESS_DENIED;
goto ERROR_4;
}
//
// Server indicates the user has to provide a user-id and password as a means of identification.
//
if (Private->HttpBootCallback != NULL) {
Data = AllocateZeroPool (sizeof (CHAR8) * HTTP_BOOT_AUTHENTICATION_INFO_MAX_LEN);
if (Data == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ERROR_4;
}
Status = Private->HttpBootCallback->Callback (
Private->HttpBootCallback,
HttpBootHttpAuthInfo,
TRUE,
HTTP_BOOT_AUTHENTICATION_INFO_MAX_LEN,
Data
);
if (EFI_ERROR (Status)) {
if (Data != NULL) {
FreePool (Data);
}
goto ERROR_5;
}
Private->AuthData = (CHAR8 *)Data;
}
HttpHeader = HttpFindHeader (
ResponseData->HeaderCount,
ResponseData->Headers,
HTTP_HEADER_WWW_AUTHENTICATE
);
if (HttpHeader != NULL) {
Private->AuthScheme = AllocateZeroPool (AsciiStrLen (HttpHeader->FieldValue) + 1);
if (Private->AuthScheme == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (Private->AuthScheme, HttpHeader->FieldValue, AsciiStrLen (HttpHeader->FieldValue));
}
Status = EFI_ACCESS_DENIED;
}
} }
goto ERROR_5; goto ERROR_5;

View File

@ -10,8 +10,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#ifndef __EFI_HTTP_BOOT_HTTP_H__ #ifndef __EFI_HTTP_BOOT_HTTP_H__
#define __EFI_HTTP_BOOT_HTTP_H__ #define __EFI_HTTP_BOOT_HTTP_H__
#define HTTP_BOOT_BLOCK_SIZE 1500 #define HTTP_BOOT_BLOCK_SIZE 1500
#define HTTP_USER_AGENT_EFI_HTTP_BOOT "UefiHttpBoot/1.0" #define HTTP_USER_AGENT_EFI_HTTP_BOOT "UefiHttpBoot/1.0"
#define HTTP_BOOT_AUTHENTICATION_INFO_MAX_LEN 255
// //
// Record the data length and start address of a data block. // Record the data length and start address of a data block.
@ -106,6 +107,7 @@ HttpBootCreateHttpIo (
@retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
BufferSize has been updated with the size needed to complete BufferSize has been updated with the size needed to complete
the request. the request.
@retval EFI_ACCESS_DENIED The server needs to authenticate the client.
@retval Others Unexpected error happened. @retval Others Unexpected error happened.
**/ **/

View File

@ -183,6 +183,12 @@ struct _HTTP_BOOT_PRIVATE_DATA {
UINT64 ReceivedSize; UINT64 ReceivedSize;
UINT32 Percentage; UINT32 Percentage;
//
// Data for the server to authenticate the client
//
CHAR8 *AuthData;
CHAR8 *AuthScheme;
// //
// HII callback info block // HII callback info block
// //

View File

@ -360,7 +360,18 @@ HttpBootLoadFile (
NULL, NULL,
&Private->ImageType &Private->ImageType
); );
if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) { if ((Private->AuthData != NULL) && (Status == EFI_ACCESS_DENIED)) {
//
// Try to use HTTP HEAD method again since the Authentication information is provided.
//
Status = HttpBootGetBootFile (
Private,
TRUE,
&Private->BootFileSize,
NULL,
&Private->ImageType
);
} else if ((EFI_ERROR (Status)) && (Status != EFI_BUFFER_TOO_SMALL)) {
// //
// Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method. // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method.
// //
@ -489,6 +500,16 @@ HttpBootStop (
} }
} }
if (Private->AuthData != NULL) {
FreePool (Private->AuthData);
Private->AuthData = NULL;
}
if (Private->AuthScheme != NULL) {
FreePool (Private->AuthScheme);
Private->AuthScheme = NULL;
}
if (Private->DnsServerIp != NULL) { if (Private->DnsServerIp != NULL) {
FreePool (Private->DnsServerIp); FreePool (Private->DnsServerIp);
Private->DnsServerIp = NULL; Private->DnsServerIp = NULL;