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;
 | |
| }
 | |
| 
 |