| /** @file | |
| A hook-in library for NetworkPkg/TlsAuthConfigDxe, in order to set volatile | |
| variables related to TLS configuration, before TlsAuthConfigDxe or HttpDxe | |
| (which is a UEFI_DRIVER) consume them. | |
| Copyright (C) 2013, 2015, 2018, Red Hat, Inc. | |
| Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Uefi/UefiBaseType.h> | |
| #include <Uefi/UefiSpec.h> | |
| #include <Guid/HttpTlsCipherList.h> | |
| #include <Guid/TlsAuthentication.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/QemuFwCfgLib.h> | |
| #include <Library/UefiRuntimeServicesTableLib.h> | |
| /** | |
| Read the list of trusted CA certificates from the fw_cfg file | |
| "etc/edk2/https/cacerts", and store it to | |
| gEfiTlsCaCertificateGuid:EFI_TLS_CA_CERTIFICATE_VARIABLE. | |
| The contents are validated (for well-formedness) by NetworkPkg/HttpDxe. | |
| **/ | |
| STATIC | |
| VOID | |
| SetCaCerts ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| FIRMWARE_CONFIG_ITEM HttpsCaCertsItem; | |
| UINTN HttpsCaCertsSize; | |
| VOID *HttpsCaCerts; | |
| Status = QemuFwCfgFindFile ( | |
| "etc/edk2/https/cacerts", | |
| &HttpsCaCertsItem, | |
| &HttpsCaCertsSize | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_VERBOSE, | |
| "%a:%a: not touching CA cert list\n", | |
| gEfiCallerBaseName, | |
| __func__ | |
| )); | |
| return; | |
| } | |
| // | |
| // Delete the current EFI_TLS_CA_CERTIFICATE_VARIABLE if it exists. This | |
| // serves two purposes: | |
| // | |
| // (a) If the variable exists with EFI_VARIABLE_NON_VOLATILE attribute, we | |
| // cannot make it volatile without deleting it first. | |
| // | |
| // (b) If we fail to recreate the variable later, deleting the current one is | |
| // still justified if the fw_cfg file exists. Emptying the set of trusted | |
| // CA certificates will fail HTTPS boot, which is better than trusting | |
| // any certificate that's possibly missing from the fw_cfg file. | |
| // | |
| Status = gRT->SetVariable ( | |
| EFI_TLS_CA_CERTIFICATE_VARIABLE, // VariableName | |
| &gEfiTlsCaCertificateGuid, // VendorGuid | |
| 0, // Attributes | |
| 0, // DataSize | |
| NULL // Data | |
| ); | |
| if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { | |
| // | |
| // This is fatal. | |
| // | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a:%a: failed to delete %g:\"%s\"\n", | |
| gEfiCallerBaseName, | |
| __func__, | |
| &gEfiTlsCaCertificateGuid, | |
| EFI_TLS_CA_CERTIFICATE_VARIABLE | |
| )); | |
| ASSERT_EFI_ERROR (Status); | |
| CpuDeadLoop (); | |
| } | |
| if (HttpsCaCertsSize == 0) { | |
| DEBUG (( | |
| DEBUG_VERBOSE, | |
| "%a:%a: applied empty CA cert list\n", | |
| gEfiCallerBaseName, | |
| __func__ | |
| )); | |
| return; | |
| } | |
| HttpsCaCerts = AllocatePool (HttpsCaCertsSize); | |
| if (HttpsCaCerts == NULL) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a:%a: failed to allocate HttpsCaCerts\n", | |
| gEfiCallerBaseName, | |
| __func__ | |
| )); | |
| return; | |
| } | |
| QemuFwCfgSelectItem (HttpsCaCertsItem); | |
| QemuFwCfgReadBytes (HttpsCaCertsSize, HttpsCaCerts); | |
| Status = gRT->SetVariable ( | |
| EFI_TLS_CA_CERTIFICATE_VARIABLE, // VariableName | |
| &gEfiTlsCaCertificateGuid, // VendorGuid | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS, // Attributes | |
| HttpsCaCertsSize, // DataSize | |
| HttpsCaCerts // Data | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a:%a: failed to set %g:\"%s\": %r\n", | |
| gEfiCallerBaseName, | |
| __func__, | |
| &gEfiTlsCaCertificateGuid, | |
| EFI_TLS_CA_CERTIFICATE_VARIABLE, | |
| Status | |
| )); | |
| goto FreeHttpsCaCerts; | |
| } | |
| DEBUG (( | |
| DEBUG_VERBOSE, | |
| "%a:%a: stored CA cert list (%Lu byte(s))\n", | |
| gEfiCallerBaseName, | |
| __func__, | |
| (UINT64)HttpsCaCertsSize | |
| )); | |
| FreeHttpsCaCerts: | |
| FreePool (HttpsCaCerts); | |
| } | |
| /** | |
| Read the list of trusted cipher suites from the fw_cfg file | |
| "etc/edk2/https/ciphers", and store it to | |
| gEdkiiHttpTlsCipherListGuid:EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE. | |
| The contents are propagated by NetworkPkg/HttpDxe to NetworkPkg/TlsDxe; the | |
| list is processed by the latter. | |
| **/ | |
| STATIC | |
| VOID | |
| SetCipherSuites ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| FIRMWARE_CONFIG_ITEM HttpsCiphersItem; | |
| UINTN HttpsCiphersSize; | |
| VOID *HttpsCiphers; | |
| Status = QemuFwCfgFindFile ( | |
| "etc/edk2/https/ciphers", | |
| &HttpsCiphersItem, | |
| &HttpsCiphersSize | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_VERBOSE, | |
| "%a:%a: not touching cipher suites\n", | |
| gEfiCallerBaseName, | |
| __func__ | |
| )); | |
| return; | |
| } | |
| // | |
| // From this point on, any failure is fatal. An ordered cipher preference | |
| // list is available from QEMU, thus we cannot let the firmware attempt HTTPS | |
| // boot with either pre-existent or non-existent preferences. An empty set of | |
| // cipher suites does not fail HTTPS boot automatically; the default cipher | |
| // suite preferences would take effect, and we must prevent that. | |
| // | |
| // Delete the current EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE if it exists. If | |
| // the variable exists with EFI_VARIABLE_NON_VOLATILE attribute, we cannot | |
| // make it volatile without deleting it first. | |
| // | |
| Status = gRT->SetVariable ( | |
| EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE, // VariableName | |
| &gEdkiiHttpTlsCipherListGuid, // VendorGuid | |
| 0, // Attributes | |
| 0, // DataSize | |
| NULL // Data | |
| ); | |
| if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a:%a: failed to delete %g:\"%s\"\n", | |
| gEfiCallerBaseName, | |
| __func__, | |
| &gEdkiiHttpTlsCipherListGuid, | |
| EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE | |
| )); | |
| goto Done; | |
| } | |
| if (HttpsCiphersSize == 0) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a:%a: list of cipher suites must not be empty\n", | |
| gEfiCallerBaseName, | |
| __func__ | |
| )); | |
| Status = EFI_INVALID_PARAMETER; | |
| goto Done; | |
| } | |
| HttpsCiphers = AllocatePool (HttpsCiphersSize); | |
| if (HttpsCiphers == NULL) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a:%a: failed to allocate HttpsCiphers\n", | |
| gEfiCallerBaseName, | |
| __func__ | |
| )); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Done; | |
| } | |
| QemuFwCfgSelectItem (HttpsCiphersItem); | |
| QemuFwCfgReadBytes (HttpsCiphersSize, HttpsCiphers); | |
| Status = gRT->SetVariable ( | |
| EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE, // VariableName | |
| &gEdkiiHttpTlsCipherListGuid, // VendorGuid | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS, // Attributes | |
| HttpsCiphersSize, // DataSize | |
| HttpsCiphers // Data | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a:%a: failed to set %g:\"%s\"\n", | |
| gEfiCallerBaseName, | |
| __func__, | |
| &gEdkiiHttpTlsCipherListGuid, | |
| EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE | |
| )); | |
| goto FreeHttpsCiphers; | |
| } | |
| DEBUG (( | |
| DEBUG_VERBOSE, | |
| "%a:%a: stored list of cipher suites (%Lu byte(s))\n", | |
| gEfiCallerBaseName, | |
| __func__, | |
| (UINT64)HttpsCiphersSize | |
| )); | |
| FreeHttpsCiphers: | |
| FreePool (HttpsCiphers); | |
| Done: | |
| if (EFI_ERROR (Status)) { | |
| ASSERT_EFI_ERROR (Status); | |
| CpuDeadLoop (); | |
| } | |
| } | |
| RETURN_STATUS | |
| EFIAPI | |
| TlsAuthConfigInit ( | |
| VOID | |
| ) | |
| { | |
| SetCaCerts (); | |
| SetCipherSuites (); | |
| return RETURN_SUCCESS; | |
| } |