From 751bea5a3e3f204f64fd0126a06474eeed792201 Mon Sep 17 00:00:00 2001 From: Mike Beaton Date: Sun, 11 Aug 2024 16:51:17 +0100 Subject: [PATCH] OpenNetworkBoot: Support loading .dmg and .chunklist without requiring fake MIME type --- OpenCorePkg.dsc | 2 + Platform/OpenNetworkBoot/HttpBootCallback.c | 133 ++++++++++++++----- Platform/OpenNetworkBoot/OpenNetworkBoot.inf | 1 + Platform/OpenNetworkBoot/README.md | 45 +++---- 4 files changed, 121 insertions(+), 60 deletions(-) diff --git a/OpenCorePkg.dsc b/OpenCorePkg.dsc index 28f5af2b7cb..6f76d9866f1 100755 --- a/OpenCorePkg.dsc +++ b/OpenCorePkg.dsc @@ -182,6 +182,8 @@ !include NetworkPkg/NetworkLibs.dsc.inc + HttpLib|NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf + !include Ext4Pkg/Ext4Defines.dsc.inc !include Ext4Pkg/Ext4Libs.dsc.inc diff --git a/Platform/OpenNetworkBoot/HttpBootCallback.c b/Platform/OpenNetworkBoot/HttpBootCallback.c index 75e869be2a9..ed91e2a311c 100644 --- a/Platform/OpenNetworkBoot/HttpBootCallback.c +++ b/Platform/OpenNetworkBoot/HttpBootCallback.c @@ -7,8 +7,12 @@ #include "NetworkBootInternal.h" +#include +#include #include +#define HTTP_CONTENT_TYPE_APP_EFI "application/efi" + OC_DMG_LOADING_SUPPORT gDmgLoading; STATIC EFI_HTTP_BOOT_CALLBACK mOriginalHttpBootCallback; @@ -22,13 +26,13 @@ STATIC EFI_HTTP_BOOT_CALLBACK mOriginalHttpBootCallback; EFI_STATUS ValidateDmgAndHttps ( CHAR16 *Uri, - BOOLEAN ShowLog + BOOLEAN ShowLog, + BOOLEAN *HasDmgExtension ) { CHAR8 *Match; CHAR8 *Uri8; UINTN UriSize; - BOOLEAN HasDmgExtension; if (gRequireHttpsUri && !HasHttpsUri (Uri)) { // @@ -41,24 +45,25 @@ ValidateDmgAndHttps ( return EFI_UNSUPPORTED; } - if (gDmgLoading == OcDmgLoadingDisabled) { - UriSize = StrSize (Uri); - Uri8 = AllocatePool (UriSize); - if (Uri8 == NULL) { - return EFI_OUT_OF_RESOURCES; - } + UriSize = StrSize (Uri); + Uri8 = AllocatePool (UriSize); + if (Uri8 == NULL) { + return EFI_OUT_OF_RESOURCES; + } - UnicodeStrToAsciiStrS (Uri, Uri8, UriSize); + UnicodeStrToAsciiStrS (Uri, Uri8, UriSize); - Match = ".dmg"; - HasDmgExtension = UriFileHasExtension (Uri8, Match); - if (!HasDmgExtension) { - Match = ".chunklist"; - HasDmgExtension = UriFileHasExtension (Uri8, Match); - } + Match = ".dmg"; + *HasDmgExtension = UriFileHasExtension (Uri8, Match); + if (!*HasDmgExtension) { + Match = ".chunklist"; + *HasDmgExtension = UriFileHasExtension (Uri8, Match); + } - FreePool (Uri8); - if (HasDmgExtension) { + FreePool (Uri8); + + if (gDmgLoading == OcDmgLoadingDisabled) { + if (*HasDmgExtension) { if (ShowLog) { DEBUG ((DEBUG_INFO, "NTBT: %a file is requested while DMG loading is disabled\n", Match)); } @@ -83,24 +88,86 @@ OpenNetworkBootHttpBootCallback ( { EFI_STATUS Status; EFI_HTTP_MESSAGE *HttpMessage; + EFI_HTTP_HEADER *Header; + STATIC BOOLEAN HasDmgExtension = FALSE; - if ((DataType == HttpBootHttpRequest) && (Data != NULL)) { - HttpMessage = (EFI_HTTP_MESSAGE *)Data; - if (HttpMessage->Data.Request->Url != NULL) { - // - // Print log messages once on initial access with HTTP HEAD, don't - // log on subsequent GET which is an attempt to get file size by - // pre-loading entire file for case of chunked encoding (where file - // size is not known until it has been transferred). - // - Status = ValidateDmgAndHttps ( - HttpMessage->Data.Request->Url, - HttpMessage->Data.Request->Method == HttpMethodHead - ); - if (EFI_ERROR (Status)) { - return Status; + switch (DataType) { + // + // Abort early if .dmg or .chunklist is found when DmgLoading is disabled. + // This is a convenience, we could allow these to load and they would be + // rejected eventually anyway. + // + case HttpBootHttpRequest: + if (Data != NULL) { + HttpMessage = (EFI_HTTP_MESSAGE *)Data; + if (HttpMessage->Data.Request->Url != NULL) { + // + // Print log messages once on initial access with HTTP HEAD, don't + // log on subsequent GET which is an attempt to get file size by + // pre-loading entire file for case of chunked encoding (where file + // size is not known until it has been transferred). + // + Status = ValidateDmgAndHttps ( + HttpMessage->Data.Request->Url, + HttpMessage->Data.Request->Method == HttpMethodHead, + &HasDmgExtension + ); + if (EFI_ERROR (Status)) { + return Status; + } + } } - } + break; + + // + // Provide fake MIME type of application/efi for .dmg and .chunklist. + // This is also a convenience of sorts, in that as long as the user + // sets application/efi MIME type for these files on their web server, + // this will work anyway. + // + case HttpBootHttpResponse: + if ((Data != NULL) && HasDmgExtension) { + // + // We do not need to keep modifying Content-Type for subsequent packets. + // + HasDmgExtension = FALSE; + + HttpMessage = (EFI_HTTP_MESSAGE *)Data; + Header = HttpFindHeader (HttpMessage->HeaderCount, HttpMessage->Headers, HTTP_HEADER_CONTENT_TYPE); + + if (Header == NULL) { + Header = HttpMessage->Headers; + ++HttpMessage->HeaderCount; + HttpMessage->Headers = AllocatePool (HttpMessage->HeaderCount * sizeof (HttpMessage->Headers[0])); + if (HttpMessage->Headers == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (HttpMessage->Headers, Header, HttpMessage->HeaderCount * sizeof (HttpMessage->Headers[0])); + Header = &HttpMessage->Headers[HttpMessage->HeaderCount - 1]; + Header->FieldValue = NULL; + Header->FieldName = AllocateCopyPool (L_STR_SIZE (HTTP_HEADER_CONTENT_TYPE), HTTP_HEADER_CONTENT_TYPE); + if (Header->FieldName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + ASSERT (Header->FieldValue != NULL); + if (AsciiStrCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) != 0) { + FreePool (Header->FieldValue); + Header->FieldValue = NULL; + } + } + + if (Header->FieldValue == NULL) { + Header->FieldValue = AllocateCopyPool (L_STR_SIZE (HTTP_CONTENT_TYPE_APP_EFI), HTTP_CONTENT_TYPE_APP_EFI); + if (Header->FieldValue == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + } + break; + + default: + break; } return mOriginalHttpBootCallback ( diff --git a/Platform/OpenNetworkBoot/OpenNetworkBoot.inf b/Platform/OpenNetworkBoot/OpenNetworkBoot.inf index 8c9c7c7ee3a..00c2f996d58 100644 --- a/Platform/OpenNetworkBoot/OpenNetworkBoot.inf +++ b/Platform/OpenNetworkBoot/OpenNetworkBoot.inf @@ -21,6 +21,7 @@ [LibraryClasses] DebugLib DevicePathLib + HttpLib OcBootManagementLib OcFlexArrayLib OcVirtualFsLib diff --git a/Platform/OpenNetworkBoot/README.md b/Platform/OpenNetworkBoot/README.md index 18c5459723c..44adfe73fb1 100644 --- a/Platform/OpenNetworkBoot/README.md +++ b/Platform/OpenNetworkBoot/README.md @@ -45,7 +45,6 @@ be configured on the network boot client. This can be done using OpenNetworkBoot's certificate configuration options, as documented in the [OpenCore documentation](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/Configuration.pdf). - > **Note 2**: In some firmware the existing `HttpBootDxe` driver may produce options which do not work correctly (e.g. blank screen when selected, because they are designed to work with a firmware UI which is not present @@ -54,14 +53,22 @@ If so, in order to get working HTTP Boot options it may be necessary to use the `UEFI/Unload` config setting to unload the existing `HttpBootDxe` driver before loading the `HttpBootDxe` driver provided with OpenCore. -> **Note 3**: In some firmware the existing `HttpDxe` driver may be locked -down to `https://` URIs only (even in some cases where the machine has no BIOS UI -for HTTP Boot; e.g. Dell OptiPlex 3070). This means that while `HttpBootDxe` can -work with the native `HttpDxe`, it will only boot from `https://` URIs (giving a -failure message otherwise). If `http://` URIs are required, this limitation can +> **Note 3**: In some firmware the existing `HttpDxe` (and `HttpBootDxe`) drivers +may be locked down to `https://` URIs (even if the machine +has no BIOS UI for HTTP Boot; e.g. Dell OptiPlex 3070). +This means that while the `HttpBootDxe` from OpenCore can +work with the native `HttpDxe`, it will only boot from `https://` URIs, giving a +failure message otherwise. If `http://` URIs are required, this limitation can be worked around by using the `UEFI/Unload` config setting to unload the existing `HttpDxe` driver before loading the `HttpDxe` driver provided with OpenCore. +> **Note 4**: During HTTP Boot '*Error: Could not retrieve NBP file size from HTTP server*' +is a very generic error message for 'something went wrong'. +It could be that `http://` URIs are not allowed by `HttpDxe` or `HttpBootDxe`, +or that a file does not exist at the specified URI on the server, or that the certificates +(if any) stored in NVRAM could not be used to validate an `https://` URI, or any one of a number +of other similar problems. + ## Identifying missing network boot drivers The `dh` command in UEFI Shell (e.g. `OpenShell` provided with @@ -249,9 +256,12 @@ If the MIME type is none of the above, then the corresponding file extensions (`.iso`, `.img` and `.efi`) are used instead to identify the NBP file type. +Additionally, for boot options generated by `OpenNetworkBoot`, `.dmg` +and `.chunklist` files will be recognised by extension and loaded, +regardless of MIME type. + > **Note**: Files which cannot be recognised by any of the above MIME types or -file extensions will _not_ be loaded by standard HTTP boot drivers (including -the `HttpBootDxe` driver currently shipped with OpenCore). +file extensions will _not_ be loaded by HTTP boot drivers. ## Booting DMG files @@ -261,25 +271,6 @@ filename is `{filename}.dmg` or `{filename}.chunklist` then the other file of this pair will be automatically loaded, in order to allow DMG chunklist verification, and both files will be used for OpenCore DMG booting. -### MIME types - -In order for `.dmg` and `.chunklist` files to be loaded by standard HTTP boot -drivers (including the `HttpBootDxe` driver currently shipped with OpenCore), -they must be served with the MIME type `application/efi`, otherwise they will -be treated as having an unknown file type and will not be loaded. - -> **Note**: `.dmg` files should not use the MIME types for ISO or IMG files mentioned -above, or else the HTTP boot drivers will try to load, mount and boot from -them as if they were the corresponding type of ramdisk, which will fail. - -For example, to set up the required MIME types when using Apache installed via -Homebrew on macOS, the following line should be added to -`/opt/homebrew/etc/httpd/mime.types`: - -``` -application/efi efi dmg chunklist -``` - ### `DmgLoading` configuration setting The behaviour of `OpenNetworkBoot`'s DMG support depends on the OpenCore