/** @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.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
/**
  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,
      __FUNCTION__
      ));
    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,
      __FUNCTION__,
      &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,
      __FUNCTION__
      ));
    return;
  }
  HttpsCaCerts = AllocatePool (HttpsCaCertsSize);
  if (HttpsCaCerts == NULL) {
    DEBUG ((
      DEBUG_ERROR,
      "%a:%a: failed to allocate HttpsCaCerts\n",
      gEfiCallerBaseName,
      __FUNCTION__
      ));
    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,
      __FUNCTION__,
      &gEfiTlsCaCertificateGuid,
      EFI_TLS_CA_CERTIFICATE_VARIABLE,
      Status
      ));
    goto FreeHttpsCaCerts;
  }
  DEBUG ((
    DEBUG_VERBOSE,
    "%a:%a: stored CA cert list (%Lu byte(s))\n",
    gEfiCallerBaseName,
    __FUNCTION__,
    (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,
      __FUNCTION__
      ));
    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,
      __FUNCTION__,
      &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,
      __FUNCTION__
      ));
    Status = EFI_INVALID_PARAMETER;
    goto Done;
  }
  HttpsCiphers = AllocatePool (HttpsCiphersSize);
  if (HttpsCiphers == NULL) {
    DEBUG ((
      DEBUG_ERROR,
      "%a:%a: failed to allocate HttpsCiphers\n",
      gEfiCallerBaseName,
      __FUNCTION__
      ));
    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,
      __FUNCTION__,
      &gEdkiiHttpTlsCipherListGuid,
      EDKII_HTTP_TLS_CIPHER_LIST_VARIABLE
      ));
    goto FreeHttpsCiphers;
  }
  DEBUG ((
    DEBUG_VERBOSE,
    "%a:%a: stored list of cipher suites (%Lu byte(s))\n",
    gEfiCallerBaseName,
    __FUNCTION__,
    (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;
}