git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12261 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			883 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			883 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Implement authentication services for the authenticated variable
 | 
						|
  service in UEFI2.2.
 | 
						|
 | 
						|
Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
 | 
						|
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 "Variable.h"
 | 
						|
#include "AuthService.h"
 | 
						|
 | 
						|
///
 | 
						|
/// Global database array for scratch
 | 
						|
///
 | 
						|
UINT32   mPubKeyNumber;
 | 
						|
UINT32   mPlatformMode;
 | 
						|
EFI_GUID mSignatureSupport[SIGSUPPORT_NUM] = {EFI_CERT_RSA2048_SHA256_GUID, EFI_CERT_RSA2048_SHA1_GUID};
 | 
						|
//
 | 
						|
// Public Exponent of RSA Key.
 | 
						|
//
 | 
						|
CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 };
 | 
						|
 | 
						|
/**
 | 
						|
  Initializes for authenticated varibale service.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The function successfully executed.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate enough memory resources.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
AutenticatedVariableServiceInitialize (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS              Status;
 | 
						|
  VARIABLE_POINTER_TRACK  Variable;
 | 
						|
  UINT8                   VarValue;
 | 
						|
  UINT32                  VarAttr;
 | 
						|
  UINTN                   DataSize;
 | 
						|
  UINTN                   CtxSize;
 | 
						|
  VARIABLE_HEADER         VariableHeader;
 | 
						|
  BOOLEAN                 Valid;
 | 
						|
 | 
						|
  mVariableModuleGlobal->AuthenticatedVariableGuid[Physical] = &gEfiAuthenticatedVariableGuid;
 | 
						|
  mVariableModuleGlobal->CertRsa2048Sha256Guid[Physical]     = &gEfiCertRsa2048Sha256Guid;
 | 
						|
  mVariableModuleGlobal->ImageSecurityDatabaseGuid[Physical] = &gEfiImageSecurityDatabaseGuid;
 | 
						|
 | 
						|
  //
 | 
						|
  // Initialize hash context.
 | 
						|
  //
 | 
						|
  CtxSize   = Sha256GetContextSize ();
 | 
						|
  mVariableModuleGlobal->HashContext[Physical] = AllocateRuntimePool (CtxSize);
 | 
						|
  ASSERT (mVariableModuleGlobal->HashContext[Physical] != NULL);
 | 
						|
  //
 | 
						|
  // Check "AuthVarKeyDatabase" variable's existence. 
 | 
						|
  // If it doesn't exist, create a new one with initial value of 0 and EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. 
 | 
						|
  //
 | 
						|
  Status = FindVariable (
 | 
						|
             mVariableModuleGlobal->VariableName[Physical][VAR_AUTH_KEY_DB], 
 | 
						|
             &gEfiAuthenticatedVariableGuid, 
 | 
						|
             &Variable, 
 | 
						|
             &mVariableModuleGlobal->VariableGlobal[Physical],
 | 
						|
             mVariableModuleGlobal->FvbInstance
 | 
						|
             );
 | 
						|
 | 
						|
  if (Variable.CurrPtr == 0x0) {
 | 
						|
    VarAttr       = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;
 | 
						|
    VarValue      = 0;
 | 
						|
    mPubKeyNumber = 0;
 | 
						|
    Status        = UpdateVariable (
 | 
						|
                      mVariableModuleGlobal->VariableName[Physical][VAR_AUTH_KEY_DB],
 | 
						|
                      &gEfiAuthenticatedVariableGuid,
 | 
						|
                      &VarValue,
 | 
						|
                      sizeof(UINT8),
 | 
						|
                      VarAttr,
 | 
						|
                      0,
 | 
						|
                      0,
 | 
						|
                      FALSE,
 | 
						|
                      mVariableModuleGlobal,
 | 
						|
                      &Variable
 | 
						|
                      );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // Load database in global variable for cache.
 | 
						|
    //
 | 
						|
    Valid = IsValidVariableHeader (
 | 
						|
              Variable.CurrPtr, 
 | 
						|
              Variable.Volatile, 
 | 
						|
              &mVariableModuleGlobal->VariableGlobal[Physical], 
 | 
						|
              mVariableModuleGlobal->FvbInstance, 
 | 
						|
              &VariableHeader
 | 
						|
              );
 | 
						|
    ASSERT (Valid);
 | 
						|
 | 
						|
    DataSize  = DataSizeOfVariable (&VariableHeader);
 | 
						|
    ASSERT (DataSize <= MAX_KEYDB_SIZE);
 | 
						|
    GetVariableDataPtr (
 | 
						|
      Variable.CurrPtr,
 | 
						|
      Variable.Volatile,
 | 
						|
      &mVariableModuleGlobal->VariableGlobal[Physical],
 | 
						|
      mVariableModuleGlobal->FvbInstance,
 | 
						|
      (CHAR16 *) mVariableModuleGlobal->PubKeyStore
 | 
						|
      );
 | 
						|
 | 
						|
    mPubKeyNumber = (UINT32) (DataSize / EFI_CERT_TYPE_RSA2048_SIZE);
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Check "SetupMode" variable's existence. 
 | 
						|
  // If it doesn't exist, check PK database's existence to determine the value.
 | 
						|
  // Then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. 
 | 
						|
  //
 | 
						|
  Status = FindVariable (
 | 
						|
             mVariableModuleGlobal->VariableName[Physical][VAR_SETUP_MODE], 
 | 
						|
             &gEfiGlobalVariableGuid, 
 | 
						|
             &Variable, 
 | 
						|
             &mVariableModuleGlobal->VariableGlobal[Physical],
 | 
						|
             mVariableModuleGlobal->FvbInstance
 | 
						|
             );
 | 
						|
 | 
						|
  if (Variable.CurrPtr == 0x0) {
 | 
						|
    Status = FindVariable (
 | 
						|
               mVariableModuleGlobal->VariableName[Physical][VAR_PLATFORM_KEY], 
 | 
						|
               &gEfiGlobalVariableGuid, 
 | 
						|
               &Variable, 
 | 
						|
               &mVariableModuleGlobal->VariableGlobal[Physical],
 | 
						|
               mVariableModuleGlobal->FvbInstance
 | 
						|
               );
 | 
						|
    if (Variable.CurrPtr == 0x0) {
 | 
						|
      mPlatformMode = SETUP_MODE;
 | 
						|
    } else {
 | 
						|
      mPlatformMode = USER_MODE;
 | 
						|
    }
 | 
						|
 | 
						|
    VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;
 | 
						|
    Status  = UpdateVariable (
 | 
						|
                mVariableModuleGlobal->VariableName[Physical][VAR_SETUP_MODE],
 | 
						|
                &gEfiGlobalVariableGuid,
 | 
						|
                &mPlatformMode,
 | 
						|
                sizeof(UINT8),
 | 
						|
                VarAttr,
 | 
						|
                0,
 | 
						|
                0,
 | 
						|
                FALSE,
 | 
						|
                mVariableModuleGlobal,
 | 
						|
                &Variable
 | 
						|
                );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    GetVariableDataPtr (
 | 
						|
      Variable.CurrPtr,
 | 
						|
      Variable.Volatile,
 | 
						|
      &mVariableModuleGlobal->VariableGlobal[Physical],
 | 
						|
      mVariableModuleGlobal->FvbInstance,
 | 
						|
      (CHAR16 *) &mPlatformMode
 | 
						|
      );
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Check "SignatureSupport" variable's existence. 
 | 
						|
  // If it doesn't exist, then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. 
 | 
						|
  //
 | 
						|
  Status = FindVariable (
 | 
						|
             EFI_SIGNATURE_SUPPORT_NAME, 
 | 
						|
             &gEfiGlobalVariableGuid, 
 | 
						|
             &Variable, 
 | 
						|
             &mVariableModuleGlobal->VariableGlobal[Physical],
 | 
						|
             mVariableModuleGlobal->FvbInstance
 | 
						|
             );
 | 
						|
 | 
						|
  if (Variable.CurrPtr == 0x0) {
 | 
						|
    VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;
 | 
						|
    Status  = UpdateVariable (
 | 
						|
                EFI_SIGNATURE_SUPPORT_NAME,
 | 
						|
                &gEfiGlobalVariableGuid,
 | 
						|
                mSignatureSupport,
 | 
						|
                SIGSUPPORT_NUM * sizeof(EFI_GUID),
 | 
						|
                VarAttr,
 | 
						|
                0,
 | 
						|
                0,
 | 
						|
                FALSE,
 | 
						|
                mVariableModuleGlobal,
 | 
						|
                &Variable
 | 
						|
                );
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Add public key in store and return its index.
 | 
						|
 | 
						|
  @param[in]  VirtualMode             The current calling mode for this function.
 | 
						|
  @param[in]  Global                  The context of this Extended SAL Variable Services Class call.
 | 
						|
  @param[in]  PubKey                  The input pointer to Public Key data.
 | 
						|
 | 
						|
  @return                             The index of new added item.
 | 
						|
 | 
						|
**/
 | 
						|
UINT32
 | 
						|
AddPubKeyInStore (
 | 
						|
  IN  BOOLEAN                   VirtualMode,
 | 
						|
  IN  ESAL_VARIABLE_GLOBAL      *Global,
 | 
						|
  IN  UINT8                     *PubKey
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS              Status;
 | 
						|
  BOOLEAN                 IsFound;
 | 
						|
  UINT32                  Index;
 | 
						|
  VARIABLE_POINTER_TRACK  Variable;
 | 
						|
  UINT8                   *Ptr;
 | 
						|
 | 
						|
  if (PubKey == NULL) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = FindVariable (
 | 
						|
             Global->VariableName[VirtualMode][VAR_AUTH_KEY_DB],
 | 
						|
             Global->AuthenticatedVariableGuid[VirtualMode],
 | 
						|
             &Variable,
 | 
						|
             &Global->VariableGlobal[VirtualMode],
 | 
						|
             Global->FvbInstance
 | 
						|
             );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
  //
 | 
						|
  // Check whether the public key entry does exist.
 | 
						|
  //
 | 
						|
  IsFound = FALSE;
 | 
						|
  for (Ptr = Global->PubKeyStore, Index = 1; Index <= mPubKeyNumber; Index++) {
 | 
						|
    if (CompareMem (Ptr, PubKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {
 | 
						|
      IsFound = TRUE;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    Ptr += EFI_CERT_TYPE_RSA2048_SIZE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!IsFound) {
 | 
						|
    //
 | 
						|
    // Add public key in database.
 | 
						|
    //
 | 
						|
    if (mPubKeyNumber == MAX_KEY_NUM) {
 | 
						|
      //
 | 
						|
      // Notes: Database is full, need enhancement here, currently just return 0.
 | 
						|
      //
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    CopyMem (Global->PubKeyStore + mPubKeyNumber * EFI_CERT_TYPE_RSA2048_SIZE, PubKey, EFI_CERT_TYPE_RSA2048_SIZE);
 | 
						|
    Index = ++mPubKeyNumber;
 | 
						|
    //
 | 
						|
    // Update public key database variable.
 | 
						|
    //
 | 
						|
    Status = UpdateVariable (
 | 
						|
               Global->VariableName[VirtualMode][VAR_AUTH_KEY_DB],
 | 
						|
               Global->AuthenticatedVariableGuid[VirtualMode],
 | 
						|
               Global->PubKeyStore,
 | 
						|
               mPubKeyNumber * EFI_CERT_TYPE_RSA2048_SIZE,
 | 
						|
               EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS,
 | 
						|
               0,
 | 
						|
               0,
 | 
						|
               VirtualMode,
 | 
						|
               Global,
 | 
						|
               &Variable
 | 
						|
               );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  return Index;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Verify data payload with AuthInfo in EFI_CERT_TYPE_RSA2048_SHA256 type.
 | 
						|
  Follow the steps in UEFI2.2.
 | 
						|
 | 
						|
  @param[in]  VirtualMode             The current calling mode for this function.
 | 
						|
  @param[in]  Global                  The context of this Extended SAL Variable Services Class call.
 | 
						|
  @param[in]  Data                    The pointer to data with AuthInfo.
 | 
						|
  @param[in]  DataSize                The size of Data.
 | 
						|
  @param[in]  PubKey                  The public key used for verification.
 | 
						|
 | 
						|
  @retval EFI_INVALID_PARAMETER       Invalid parameter.
 | 
						|
  @retval EFI_SECURITY_VIOLATION      Authentication failed.
 | 
						|
  @retval EFI_SUCCESS                 Authentication successful.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
VerifyDataPayload (
 | 
						|
  IN  BOOLEAN                   VirtualMode,
 | 
						|
  IN  ESAL_VARIABLE_GLOBAL      *Global,
 | 
						|
  IN  UINT8                     *Data,
 | 
						|
  IN  UINTN                     DataSize,
 | 
						|
  IN  UINT8                     *PubKey
 | 
						|
  )
 | 
						|
{
 | 
						|
  BOOLEAN                         Status;
 | 
						|
  EFI_VARIABLE_AUTHENTICATION     *CertData;
 | 
						|
  EFI_CERT_BLOCK_RSA_2048_SHA256  *CertBlock;
 | 
						|
  UINT8                           Digest[SHA256_DIGEST_SIZE];
 | 
						|
  VOID                            *Rsa;
 | 
						|
  VOID                            *HashContext;
 | 
						|
 | 
						|
  Rsa         = NULL;
 | 
						|
  CertData    = NULL;
 | 
						|
  CertBlock   = NULL;
 | 
						|
 | 
						|
  if (Data == NULL || PubKey == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  CertData  = (EFI_VARIABLE_AUTHENTICATION *) Data;
 | 
						|
  CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData);
 | 
						|
 | 
						|
  //
 | 
						|
  // wCertificateType should be WIN_CERT_TYPE_EFI_GUID.
 | 
						|
  // Cert type should be EFI_CERT_TYPE_RSA2048_SHA256.
 | 
						|
  //
 | 
						|
  if ((CertData->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) ||
 | 
						|
      !CompareGuid (&CertData->AuthInfo.CertType, Global->CertRsa2048Sha256Guid[VirtualMode])
 | 
						|
        ) {
 | 
						|
    //
 | 
						|
    // Invalid AuthInfo type, return EFI_SECURITY_VIOLATION.
 | 
						|
    //
 | 
						|
    return EFI_SECURITY_VIOLATION;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Hash data payload with SHA256.
 | 
						|
  //
 | 
						|
  ZeroMem (Digest, SHA256_DIGEST_SIZE);
 | 
						|
  HashContext = Global->HashContext[VirtualMode];
 | 
						|
  Status  = Sha256Init (HashContext);
 | 
						|
  if (!Status) {
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
  Status  = Sha256Update (HashContext, Data + AUTHINFO_SIZE, (UINTN) (DataSize - AUTHINFO_SIZE));
 | 
						|
  if (!Status) {
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Hash Monotonic Count.
 | 
						|
  //
 | 
						|
  Status  = Sha256Update (HashContext, &CertData->MonotonicCount, sizeof (UINT64));
 | 
						|
  if (!Status) {
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
  Status  = Sha256Final (HashContext, Digest);
 | 
						|
  if (!Status) {
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Generate & Initialize RSA Context.
 | 
						|
  //
 | 
						|
  Rsa = RsaNew ();
 | 
						|
  ASSERT (Rsa != NULL);
 | 
						|
  // 
 | 
						|
  // Set RSA Key Components.
 | 
						|
  // NOTE: Only N and E are needed to be set as RSA public key for signature verification.
 | 
						|
  //
 | 
						|
  Status = RsaSetKey (Rsa, RsaKeyN, PubKey, EFI_CERT_TYPE_RSA2048_SIZE);
 | 
						|
  if (!Status) {
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
  Status = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE));
 | 
						|
  if (!Status) {
 | 
						|
    goto Done;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Verify the signature.
 | 
						|
  //
 | 
						|
  Status = RsaPkcs1Verify (
 | 
						|
             Rsa, 
 | 
						|
             Digest, 
 | 
						|
             SHA256_DIGEST_SIZE, 
 | 
						|
             CertBlock->Signature, 
 | 
						|
             EFI_CERT_TYPE_RSA2048_SHA256_SIZE
 | 
						|
             );
 | 
						|
 | 
						|
Done:
 | 
						|
  if (Rsa != NULL) {
 | 
						|
    RsaFree (Rsa);
 | 
						|
  }
 | 
						|
  if (Status) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  } else {
 | 
						|
    return EFI_SECURITY_VIOLATION;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Update platform mode.
 | 
						|
 | 
						|
  @param[in]  VirtualMode             The current calling mode for this function.
 | 
						|
  @param[in]  Global                  The context of this Extended SAL Variable Services Class call.
 | 
						|
  @param[in]  Mode                    SETUP_MODE or USER_MODE.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
UpdatePlatformMode (
 | 
						|
  IN  BOOLEAN                   VirtualMode,
 | 
						|
  IN  ESAL_VARIABLE_GLOBAL      *Global,
 | 
						|
  IN  UINT32                    Mode
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS              Status;
 | 
						|
  VARIABLE_POINTER_TRACK  Variable;
 | 
						|
  UINT32                  VarAttr;
 | 
						|
 | 
						|
  Status = FindVariable (
 | 
						|
             Global->VariableName[VirtualMode][VAR_SETUP_MODE], 
 | 
						|
             Global->GlobalVariableGuid[VirtualMode], 
 | 
						|
             &Variable, 
 | 
						|
             &Global->VariableGlobal[VirtualMode],
 | 
						|
             Global->FvbInstance
 | 
						|
             );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  mPlatformMode  = Mode;
 | 
						|
  VarAttr        = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;
 | 
						|
  Status         = UpdateVariable (
 | 
						|
                     Global->VariableName[VirtualMode][VAR_SETUP_MODE],
 | 
						|
                     Global->GlobalVariableGuid[VirtualMode],
 | 
						|
                     &mPlatformMode,
 | 
						|
                     sizeof(UINT8),
 | 
						|
                     VarAttr,
 | 
						|
                     0,
 | 
						|
                     0,
 | 
						|
                     VirtualMode,
 | 
						|
                     Global,
 | 
						|
                     &Variable
 | 
						|
                     );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Process variable with platform key for verification.
 | 
						|
 | 
						|
  @param[in]  VariableName                The name of Variable to be found.
 | 
						|
  @param[in]  VendorGuid                  The variable vendor GUID.
 | 
						|
  @param[in]  Data                        The data pointer.
 | 
						|
  @param[in]  DataSize                    The size of Data found. If size is less than the
 | 
						|
                                          data, this value contains the required size.
 | 
						|
  @param[in]  VirtualMode                 The current calling mode for this function.
 | 
						|
  @param[in]  Global                      The context of this Extended SAL Variable Services Class call.
 | 
						|
  @param[in]  Variable                    The variable information which is used to keep track of variable usage.
 | 
						|
  @param[in]  Attributes                  The attribute value of the variable.
 | 
						|
  @param[in]  IsPk                        Indicates whether to process pk.
 | 
						|
 | 
						|
  @retval EFI_INVALID_PARAMETER           Invalid parameter.
 | 
						|
  @retval EFI_SECURITY_VIOLATION          The variable does NOT pass the validation 
 | 
						|
                                          check carried out by the firmware. 
 | 
						|
  @retval EFI_SUCCESS                     The variable passed validation successfully.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ProcessVarWithPk (
 | 
						|
  IN  CHAR16                    *VariableName,
 | 
						|
  IN  EFI_GUID                  *VendorGuid,
 | 
						|
  IN  VOID                      *Data,
 | 
						|
  IN  UINTN                     DataSize,
 | 
						|
  IN  BOOLEAN                   VirtualMode,
 | 
						|
  IN  ESAL_VARIABLE_GLOBAL      *Global,
 | 
						|
  IN  VARIABLE_POINTER_TRACK    *Variable,
 | 
						|
  IN  UINT32                    Attributes OPTIONAL,
 | 
						|
  IN  BOOLEAN                   IsPk
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                  Status;
 | 
						|
  VARIABLE_POINTER_TRACK      PkVariable;
 | 
						|
  EFI_SIGNATURE_LIST          *OldPkList;
 | 
						|
  EFI_SIGNATURE_DATA          *OldPkData;
 | 
						|
  EFI_VARIABLE_AUTHENTICATION *CertData;
 | 
						|
  VARIABLE_HEADER             VariableHeader;
 | 
						|
  BOOLEAN                     Valid;
 | 
						|
 | 
						|
  OldPkList = NULL;
 | 
						|
 | 
						|
  if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
 | 
						|
    //
 | 
						|
    // PK and KEK should set EFI_VARIABLE_NON_VOLATILE attribute.
 | 
						|
    //
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mPlatformMode == USER_MODE) {
 | 
						|
    if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) {
 | 
						|
      //
 | 
						|
      // In user mode, PK and KEK should set EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute.
 | 
						|
      //
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
    }
 | 
						|
 | 
						|
    CertData = (EFI_VARIABLE_AUTHENTICATION *) Data;
 | 
						|
 | 
						|
    if (Variable->CurrPtr != 0x0) {
 | 
						|
      Valid = IsValidVariableHeader (
 | 
						|
                Variable->CurrPtr, 
 | 
						|
                Variable->Volatile, 
 | 
						|
                &Global->VariableGlobal[VirtualMode], 
 | 
						|
                Global->FvbInstance, 
 | 
						|
                &VariableHeader
 | 
						|
                );
 | 
						|
      ASSERT (Valid);
 | 
						|
 | 
						|
      if (CertData->MonotonicCount <= VariableHeader.MonotonicCount) {
 | 
						|
        //
 | 
						|
        // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.
 | 
						|
        //
 | 
						|
        return EFI_SECURITY_VIOLATION;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // Get platform key from variable.
 | 
						|
    //
 | 
						|
    Status = FindVariable (
 | 
						|
               Global->VariableName[VirtualMode][VAR_PLATFORM_KEY], 
 | 
						|
               Global->GlobalVariableGuid[VirtualMode], 
 | 
						|
               &PkVariable, 
 | 
						|
               &Global->VariableGlobal[VirtualMode],
 | 
						|
               Global->FvbInstance
 | 
						|
               );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    ZeroMem (Global->KeyList, MAX_KEYDB_SIZE);
 | 
						|
    GetVariableDataPtr (
 | 
						|
      PkVariable.CurrPtr,
 | 
						|
      PkVariable.Volatile,
 | 
						|
      &Global->VariableGlobal[VirtualMode],
 | 
						|
      Global->FvbInstance,
 | 
						|
      (CHAR16 *) Global->KeyList
 | 
						|
      );
 | 
						|
 | 
						|
    OldPkList = (EFI_SIGNATURE_LIST *) Global->KeyList;
 | 
						|
    OldPkData = (EFI_SIGNATURE_DATA *) ((UINT8 *) OldPkList + sizeof (EFI_SIGNATURE_LIST) + OldPkList->SignatureHeaderSize);
 | 
						|
    Status    = VerifyDataPayload (VirtualMode, Global, Data, DataSize, OldPkData->SignatureData);
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      Status = UpdateVariable (
 | 
						|
                 VariableName, 
 | 
						|
                 VendorGuid, 
 | 
						|
                 (UINT8*)Data + AUTHINFO_SIZE, 
 | 
						|
                 DataSize - AUTHINFO_SIZE, 
 | 
						|
                 Attributes, 
 | 
						|
                 0, 
 | 
						|
                 CertData->MonotonicCount, 
 | 
						|
                 VirtualMode, 
 | 
						|
                 Global,
 | 
						|
                 Variable
 | 
						|
                 );
 | 
						|
 | 
						|
      if (!EFI_ERROR (Status)) {
 | 
						|
        //
 | 
						|
        // If delete PK in user mode, need change to setup mode.
 | 
						|
        //
 | 
						|
        if ((DataSize == AUTHINFO_SIZE) && IsPk) {
 | 
						|
          UpdatePlatformMode (VirtualMode, Global, SETUP_MODE);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, VirtualMode, Global, Variable);
 | 
						|
    //
 | 
						|
    // If enroll PK in setup mode, need change to user mode.
 | 
						|
    //
 | 
						|
    if ((DataSize != 0) && IsPk) {
 | 
						|
      UpdatePlatformMode (VirtualMode, Global, USER_MODE);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Process variable with key exchange key for verification.
 | 
						|
 | 
						|
  @param[in]  VariableName                The name of Variable to be found.
 | 
						|
  @param[in]  VendorGuid                  The variable vendor GUID.
 | 
						|
  @param[in]  Data                        The data pointer.
 | 
						|
  @param[in]  DataSize                    The size of Data found. If size is less than the
 | 
						|
                                          data, this value contains the required size.
 | 
						|
  @param[in]  VirtualMode                 The current calling mode for this function.
 | 
						|
  @param[in]  Global                      The context of this Extended SAL Variable Services Class call.
 | 
						|
  @param[in]  Variable                    The variable information which is used to keep track of variable usage.
 | 
						|
  @param[in]  Attributes                  The attribute value of the variable.
 | 
						|
 | 
						|
  @retval EFI_INVALID_PARAMETER           Invalid parameter.
 | 
						|
  @retval EFI_SECURITY_VIOLATION          The variable did NOT pass the validation 
 | 
						|
                                          check carried out by the firmware. 
 | 
						|
  @retval EFI_SUCCESS                     The variable passed validation successfully.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ProcessVarWithKek (
 | 
						|
  IN  CHAR16                               *VariableName,
 | 
						|
  IN  EFI_GUID                             *VendorGuid,
 | 
						|
  IN  VOID                                 *Data,
 | 
						|
  IN  UINTN                                DataSize,
 | 
						|
  IN  BOOLEAN                              VirtualMode,
 | 
						|
  IN  ESAL_VARIABLE_GLOBAL                 *Global,
 | 
						|
  IN  VARIABLE_POINTER_TRACK               *Variable,
 | 
						|
  IN  UINT32                               Attributes OPTIONAL
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                      Status;
 | 
						|
  VARIABLE_POINTER_TRACK          KekVariable;
 | 
						|
  EFI_SIGNATURE_LIST              *KekList;
 | 
						|
  EFI_SIGNATURE_DATA              *KekItem;
 | 
						|
  UINT32                          KekCount;
 | 
						|
  EFI_VARIABLE_AUTHENTICATION     *CertData;
 | 
						|
  EFI_CERT_BLOCK_RSA_2048_SHA256  *CertBlock;
 | 
						|
  BOOLEAN                         IsFound;
 | 
						|
  UINT32                          Index;
 | 
						|
  VARIABLE_HEADER                 VariableHeader;
 | 
						|
  BOOLEAN                         Valid;
 | 
						|
 | 
						|
  KekList = NULL;
 | 
						|
 | 
						|
  if (mPlatformMode == USER_MODE) {
 | 
						|
    if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) {
 | 
						|
      //
 | 
						|
      // In user mode, should set EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute.
 | 
						|
      //
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
    }
 | 
						|
 | 
						|
    CertData  = (EFI_VARIABLE_AUTHENTICATION *) Data;
 | 
						|
    CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData);
 | 
						|
    if (Variable->CurrPtr != 0x0) {
 | 
						|
      Valid = IsValidVariableHeader (
 | 
						|
                Variable->CurrPtr, 
 | 
						|
                Variable->Volatile, 
 | 
						|
                &Global->VariableGlobal[VirtualMode], 
 | 
						|
                Global->FvbInstance, 
 | 
						|
                &VariableHeader
 | 
						|
                );
 | 
						|
      ASSERT (Valid);
 | 
						|
 | 
						|
      if (CertData->MonotonicCount <= VariableHeader.MonotonicCount) {
 | 
						|
        //
 | 
						|
        // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.
 | 
						|
        //
 | 
						|
        return EFI_SECURITY_VIOLATION;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // Get KEK database from variable.
 | 
						|
    //
 | 
						|
    Status = FindVariable (
 | 
						|
               Global->VariableName[VirtualMode][VAR_KEY_EXCHANGE_KEY], 
 | 
						|
               Global->GlobalVariableGuid[VirtualMode], 
 | 
						|
               &KekVariable, 
 | 
						|
               &Global->VariableGlobal[VirtualMode],
 | 
						|
               Global->FvbInstance
 | 
						|
               );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    ZeroMem (Global->KeyList, MAX_KEYDB_SIZE);
 | 
						|
    GetVariableDataPtr (
 | 
						|
      KekVariable.CurrPtr,
 | 
						|
      KekVariable.Volatile,
 | 
						|
      &Global->VariableGlobal[VirtualMode],
 | 
						|
      Global->FvbInstance,
 | 
						|
      (CHAR16 *) Global->KeyList
 | 
						|
      );
 | 
						|
    //
 | 
						|
    // Enumerate all Kek items in this list to verify the variable certificate data.
 | 
						|
    // If anyone is authenticated successfully, it means the variable is correct!
 | 
						|
    //
 | 
						|
    KekList   = (EFI_SIGNATURE_LIST *) Global->KeyList;
 | 
						|
    IsFound   = FALSE;
 | 
						|
    KekCount  = (KekList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - KekList->SignatureHeaderSize) / KekList->SignatureSize;
 | 
						|
    KekItem   = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekList + sizeof (EFI_SIGNATURE_LIST) + KekList->SignatureHeaderSize);
 | 
						|
    for (Index = 0; Index < KekCount; Index++) {
 | 
						|
      if (CompareMem (KekItem->SignatureData, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {
 | 
						|
        IsFound = TRUE;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekItem + KekList->SignatureSize);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!IsFound) {
 | 
						|
      return EFI_SECURITY_VIOLATION;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = VerifyDataPayload (VirtualMode, Global, Data, DataSize, CertBlock->PublicKey);
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      Status = UpdateVariable (
 | 
						|
                 VariableName, 
 | 
						|
                 VendorGuid, 
 | 
						|
                 (UINT8*)Data + AUTHINFO_SIZE, 
 | 
						|
                 DataSize - AUTHINFO_SIZE, 
 | 
						|
                 Attributes, 
 | 
						|
                 0, 
 | 
						|
                 CertData->MonotonicCount, 
 | 
						|
                 VirtualMode,
 | 
						|
                 Global,
 | 
						|
                 Variable
 | 
						|
                 );
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // If in setup mode, no authentication needed.
 | 
						|
    //
 | 
						|
    Status = UpdateVariable (
 | 
						|
               VariableName, 
 | 
						|
               VendorGuid, 
 | 
						|
               Data, 
 | 
						|
               DataSize, 
 | 
						|
               Attributes, 
 | 
						|
               0, 
 | 
						|
               0, 
 | 
						|
               VirtualMode,
 | 
						|
               Global,
 | 
						|
               Variable
 | 
						|
               );
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Process variable with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set, and return the index of associated public key.
 | 
						|
 | 
						|
  @param[in]  Data                        The data pointer.
 | 
						|
  @param[in]  DataSize                    The size of Data found. If size is less than the
 | 
						|
                                          data, this value contains the required size.
 | 
						|
  @param[in]  VirtualMode                 The current calling mode for this function.
 | 
						|
  @param[in]  Global                      The context of this Extended SAL Variable Services Class call.
 | 
						|
  @param[in]  Variable                    The variable information which is used to keep track of variable usage.
 | 
						|
  @param[in]  Attributes                  The attribute value of the variable.
 | 
						|
  @param[out] KeyIndex                    The output index of corresponding public key in database.
 | 
						|
  @param[out] MonotonicCount              The output value of corresponding Monotonic Count.
 | 
						|
 | 
						|
  @retval EFI_INVALID_PARAMETER           Invalid parameter.
 | 
						|
  @retval EFI_WRITE_PROTECTED             The variable is write-protected and needs authentication with
 | 
						|
                                          EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.
 | 
						|
  @retval EFI_SECURITY_VIOLATION          The variable is with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
 | 
						|
                                          set, but the AuthInfo does NOT pass the validation 
 | 
						|
                                          check carried out by the firmware. 
 | 
						|
  @retval EFI_SUCCESS                     The variable is not write-protected, or passed validation successfully.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
VerifyVariable (
 | 
						|
  IN  VOID                      *Data,
 | 
						|
  IN  UINTN                     DataSize,
 | 
						|
  IN  BOOLEAN                   VirtualMode,
 | 
						|
  IN  ESAL_VARIABLE_GLOBAL      *Global,
 | 
						|
  IN  VARIABLE_POINTER_TRACK    *Variable,
 | 
						|
  IN  UINT32                    Attributes OPTIONAL,
 | 
						|
  OUT UINT32                    *KeyIndex OPTIONAL,
 | 
						|
  OUT UINT64                    *MonotonicCount OPTIONAL
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                      Status;
 | 
						|
  BOOLEAN                         IsDeletion;
 | 
						|
  BOOLEAN                         IsFirstTime;
 | 
						|
  UINT8                           *PubKey;
 | 
						|
  EFI_VARIABLE_AUTHENTICATION     *CertData;
 | 
						|
  EFI_CERT_BLOCK_RSA_2048_SHA256  *CertBlock;
 | 
						|
  VARIABLE_HEADER                 VariableHeader;
 | 
						|
  BOOLEAN                         Valid;
 | 
						|
 | 
						|
  CertData    = NULL;
 | 
						|
  CertBlock   = NULL;
 | 
						|
  PubKey      = NULL;
 | 
						|
  IsDeletion  = FALSE;
 | 
						|
  Valid       = FALSE;
 | 
						|
 | 
						|
  if (KeyIndex != NULL) {
 | 
						|
    *KeyIndex = 0;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Determine if first time SetVariable with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS.
 | 
						|
  //
 | 
						|
  ZeroMem (&VariableHeader, sizeof (VARIABLE_HEADER));
 | 
						|
  if (Variable->CurrPtr != 0x0) {
 | 
						|
    Valid = IsValidVariableHeader (
 | 
						|
              Variable->CurrPtr, 
 | 
						|
              Variable->Volatile, 
 | 
						|
              &Global->VariableGlobal[VirtualMode], 
 | 
						|
              Global->FvbInstance, 
 | 
						|
              &VariableHeader
 | 
						|
              );
 | 
						|
    ASSERT (Valid);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
 | 
						|
    if (KeyIndex == NULL) {
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Determine current operation type.
 | 
						|
    //
 | 
						|
    if (DataSize == AUTHINFO_SIZE) {
 | 
						|
      IsDeletion = TRUE;
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // Determine whether this is the first time with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.
 | 
						|
    //
 | 
						|
    if (Variable->CurrPtr == 0x0) {
 | 
						|
      IsFirstTime = TRUE;
 | 
						|
    } else if (Valid &&(VariableHeader.Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) {
 | 
						|
      IsFirstTime = TRUE;
 | 
						|
    } else {
 | 
						|
      *KeyIndex   = VariableHeader.PubKeyIndex;
 | 
						|
      IsFirstTime = FALSE;
 | 
						|
    }
 | 
						|
  } else if (Valid && (VariableHeader.Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) { 
 | 
						|
      //
 | 
						|
      // If the variable is already write-protected, it always needs authentication before update.
 | 
						|
      //
 | 
						|
      return EFI_WRITE_PROTECTED;
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // If without EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, set and attributes collision.
 | 
						|
    // That means it is not authenticated variable, just return EFI_SUCCESS.
 | 
						|
    //
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Get PubKey and check Monotonic Count value corresponding to the variable.
 | 
						|
  //
 | 
						|
  CertData  = (EFI_VARIABLE_AUTHENTICATION *) Data;
 | 
						|
  CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData);
 | 
						|
  PubKey    = CertBlock->PublicKey;
 | 
						|
 | 
						|
  if (MonotonicCount != NULL) {
 | 
						|
    //
 | 
						|
    // Update Monotonic Count value.
 | 
						|
    //
 | 
						|
    *MonotonicCount = CertData->MonotonicCount;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!IsFirstTime) {
 | 
						|
    //
 | 
						|
    // Check input PubKey.
 | 
						|
    //
 | 
						|
    if (CompareMem (PubKey, Global->PubKeyStore + (*KeyIndex - 1) * EFI_CERT_TYPE_RSA2048_SIZE, EFI_CERT_TYPE_RSA2048_SIZE) != 0) {
 | 
						|
      return EFI_SECURITY_VIOLATION;
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // Compare the current monotonic count and ensure that it is greater than the last SetVariable
 | 
						|
    // operation with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute set.
 | 
						|
    //
 | 
						|
    if (CertData->MonotonicCount <= VariableHeader.MonotonicCount) {
 | 
						|
      //
 | 
						|
      // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.
 | 
						|
      //
 | 
						|
      return EFI_SECURITY_VIOLATION;
 | 
						|
    }
 | 
						|
  } 
 | 
						|
  //
 | 
						|
  // Verify the certificate in Data payload.
 | 
						|
  //
 | 
						|
  Status = VerifyDataPayload (VirtualMode, Global, Data, DataSize, PubKey);
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    //
 | 
						|
    // Now, the signature has been verified!
 | 
						|
    //
 | 
						|
    if (IsFirstTime && !IsDeletion) {
 | 
						|
      //
 | 
						|
      // Update public key database variable if need and return the index.
 | 
						|
      //
 | 
						|
      *KeyIndex = AddPubKeyInStore (VirtualMode, Global, PubKey);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 |