mirror of https://github.com/acidanthera/audk.git
2542 lines
84 KiB
C
2542 lines
84 KiB
C
/** @file
|
|
Implement authentication services for the authenticated variables.
|
|
|
|
Caution: This module requires additional review when modified.
|
|
This driver will have external input - variable data. It may be input in SMM mode.
|
|
This external input must be validated carefully to avoid security issue like
|
|
buffer overflow, integer overflow.
|
|
Variable attribute should also be checked to avoid authentication bypass.
|
|
The whole SMM authentication variable design relies on the integrity of flash part and SMM.
|
|
which is assumed to be protected by platform. All variable code and metadata in flash/SMM Memory
|
|
may not be modified without authorization. If platform fails to protect these resources,
|
|
the authentication service provided in this driver will be broken, and the behavior is undefined.
|
|
|
|
ProcessVarWithPk(), ProcessVarWithKek() and ProcessVariable() are the function to do
|
|
variable authentication.
|
|
|
|
VerifyTimeBasedPayloadAndUpdate() and VerifyCounterBasedPayload() are sub function to do verification.
|
|
They will do basic validation for authentication data structure, then call crypto library
|
|
to verify the signature.
|
|
|
|
Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
|
|
Copyright (c) Microsoft Corporation.
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "AuthServiceInternal.h"
|
|
|
|
#include <Protocol/VariablePolicy.h>
|
|
#include <Library/VariablePolicyLib.h>
|
|
|
|
#define SHA_DIGEST_SIZE_MAX SHA512_DIGEST_SIZE
|
|
|
|
/**
|
|
Retrieves the size, in bytes, of the context buffer required for hash operations.
|
|
|
|
If this interface is not supported, then return zero.
|
|
|
|
@return The size, in bytes, of the context buffer required for hash operations.
|
|
@retval 0 This interface is not supported.
|
|
|
|
**/
|
|
typedef
|
|
UINTN
|
|
(EFIAPI *EFI_HASH_GET_CONTEXT_SIZE)(
|
|
VOID
|
|
);
|
|
|
|
/**
|
|
Initializes user-supplied memory pointed by Sha1Context as hash context for
|
|
subsequent use.
|
|
|
|
If HashContext is NULL, then return FALSE.
|
|
If this interface is not supported, then return FALSE.
|
|
|
|
@param[out] HashContext Pointer to Hashcontext being initialized.
|
|
|
|
@retval TRUE Hash context initialization succeeded.
|
|
@retval FALSE Hash context initialization failed.
|
|
@retval FALSE This interface is not supported.
|
|
|
|
**/
|
|
typedef
|
|
BOOLEAN
|
|
(EFIAPI *EFI_HASH_INIT)(
|
|
OUT VOID *HashContext
|
|
);
|
|
|
|
/**
|
|
Digests the input data and updates Hash context.
|
|
|
|
This function performs Hash digest on a data buffer of the specified size.
|
|
It can be called multiple times to compute the digest of long or discontinuous data streams.
|
|
Hash context should be already correctly initialized by HashInit(), and should not be finalized
|
|
by HashFinal(). Behavior with invalid context is undefined.
|
|
|
|
If HashContext is NULL, then return FALSE.
|
|
If this interface is not supported, then return FALSE.
|
|
|
|
@param[in, out] HashContext Pointer to the Hash context.
|
|
@param[in] Data Pointer to the buffer containing the data to be hashed.
|
|
@param[in] DataSize Size of Data buffer in bytes.
|
|
|
|
@retval TRUE SHA-1 data digest succeeded.
|
|
@retval FALSE SHA-1 data digest failed.
|
|
@retval FALSE This interface is not supported.
|
|
|
|
**/
|
|
typedef
|
|
BOOLEAN
|
|
(EFIAPI *EFI_HASH_UPDATE)(
|
|
IN OUT VOID *HashContext,
|
|
IN CONST VOID *Data,
|
|
IN UINTN DataSize
|
|
);
|
|
|
|
/**
|
|
Completes computation of the Hash digest value.
|
|
|
|
This function completes hash computation and retrieves the digest value into
|
|
the specified memory. After this function has been called, the Hash context cannot
|
|
be used again.
|
|
Hash context should be already correctly initialized by HashInit(), and should not be
|
|
finalized by HashFinal(). Behavior with invalid Hash context is undefined.
|
|
|
|
If HashContext is NULL, then return FALSE.
|
|
If HashValue is NULL, then return FALSE.
|
|
If this interface is not supported, then return FALSE.
|
|
|
|
@param[in, out] HashContext Pointer to the Hash context.
|
|
@param[out] HashValue Pointer to a buffer that receives the Hash digest
|
|
value.
|
|
|
|
@retval TRUE Hash digest computation succeeded.
|
|
@retval FALSE Hash digest computation failed.
|
|
@retval FALSE This interface is not supported.
|
|
|
|
**/
|
|
typedef
|
|
BOOLEAN
|
|
(EFIAPI *EFI_HASH_FINAL)(
|
|
IN OUT VOID *HashContext,
|
|
OUT UINT8 *HashValue
|
|
);
|
|
|
|
typedef struct {
|
|
UINT32 HashSize;
|
|
EFI_HASH_GET_CONTEXT_SIZE GetContextSize;
|
|
EFI_HASH_INIT Init;
|
|
EFI_HASH_UPDATE Update;
|
|
EFI_HASH_FINAL Final;
|
|
VOID **HashShaCtx;
|
|
UINT8 *OidValue;
|
|
UINTN OidLength;
|
|
} EFI_HASH_INFO;
|
|
|
|
//
|
|
// Public Exponent of RSA Key.
|
|
//
|
|
CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 };
|
|
|
|
UINT8 mSha256OidValue[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 };
|
|
UINT8 mSha384OidValue[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 };
|
|
UINT8 mSha512OidValue[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 };
|
|
|
|
EFI_HASH_INFO mHashInfo[] = {
|
|
{ SHA256_DIGEST_SIZE, Sha256GetContextSize, Sha256Init, Sha256Update, Sha256Final, &mHashSha256Ctx, mSha256OidValue, 9 },
|
|
{ SHA384_DIGEST_SIZE, Sha384GetContextSize, Sha384Init, Sha384Update, Sha384Final, &mHashSha384Ctx, mSha384OidValue, 9 },
|
|
{ SHA512_DIGEST_SIZE, Sha512GetContextSize, Sha512Init, Sha512Update, Sha512Final, &mHashSha512Ctx, mSha512OidValue, 9 },
|
|
};
|
|
|
|
//
|
|
// Requirement for different signature type which have been defined in UEFI spec.
|
|
// These data are used to perform SignatureList format check while setting PK/KEK variable.
|
|
//
|
|
EFI_SIGNATURE_ITEM mSupportSigItem[] = {
|
|
// {SigType, SigHeaderSize, SigDataSize }
|
|
{ EFI_CERT_SHA256_GUID, 0, 32 },
|
|
{ EFI_CERT_RSA2048_GUID, 0, 256 },
|
|
{ EFI_CERT_RSA2048_SHA256_GUID, 0, 256 },
|
|
{ EFI_CERT_SHA1_GUID, 0, 20 },
|
|
{ EFI_CERT_RSA2048_SHA1_GUID, 0, 256 },
|
|
{ EFI_CERT_X509_GUID, 0, ((UINT32) ~0) },
|
|
{ EFI_CERT_SHA224_GUID, 0, 28 },
|
|
{ EFI_CERT_SHA384_GUID, 0, 48 },
|
|
{ EFI_CERT_SHA512_GUID, 0, 64 },
|
|
{ EFI_CERT_X509_SHA256_GUID, 0, 48 },
|
|
{ EFI_CERT_X509_SHA384_GUID, 0, 64 },
|
|
{ EFI_CERT_X509_SHA512_GUID, 0, 80 }
|
|
};
|
|
|
|
/**
|
|
Finds variable in storage blocks of volatile and non-volatile storage areas.
|
|
|
|
This code finds variable in storage blocks of volatile and non-volatile storage areas.
|
|
If VariableName is an empty string, then we just return the first
|
|
qualified variable without comparing VariableName and VendorGuid.
|
|
|
|
@param[in] VariableName Name of the variable to be found.
|
|
@param[in] VendorGuid Variable vendor GUID to be found.
|
|
@param[out] Data Pointer to data address.
|
|
@param[out] DataSize Pointer to data size.
|
|
|
|
@retval EFI_INVALID_PARAMETER If VariableName is not an empty string,
|
|
while VendorGuid is NULL.
|
|
@retval EFI_SUCCESS Variable successfully found.
|
|
@retval EFI_NOT_FOUND Variable not found
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AuthServiceInternalFindVariable (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
OUT VOID **Data,
|
|
OUT UINTN *DataSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
AUTH_VARIABLE_INFO AuthVariableInfo;
|
|
|
|
ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));
|
|
Status = mAuthVarLibContextIn->FindVariable (
|
|
VariableName,
|
|
VendorGuid,
|
|
&AuthVariableInfo
|
|
);
|
|
*Data = AuthVariableInfo.Data;
|
|
*DataSize = AuthVariableInfo.DataSize;
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Update the variable region with Variable information.
|
|
|
|
@param[in] VariableName Name of variable.
|
|
@param[in] VendorGuid Guid of variable.
|
|
@param[in] Data Data pointer.
|
|
@param[in] DataSize Size of Data.
|
|
@param[in] Attributes Attribute value of the variable.
|
|
|
|
@retval EFI_SUCCESS The update operation is success.
|
|
@retval EFI_INVALID_PARAMETER Invalid parameter.
|
|
@retval EFI_WRITE_PROTECTED Variable is write-protected.
|
|
@retval EFI_OUT_OF_RESOURCES There is not enough resource.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AuthServiceInternalUpdateVariable (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN VOID *Data,
|
|
IN UINTN DataSize,
|
|
IN UINT32 Attributes
|
|
)
|
|
{
|
|
AUTH_VARIABLE_INFO AuthVariableInfo;
|
|
|
|
ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));
|
|
AuthVariableInfo.VariableName = VariableName;
|
|
AuthVariableInfo.VendorGuid = VendorGuid;
|
|
AuthVariableInfo.Data = Data;
|
|
AuthVariableInfo.DataSize = DataSize;
|
|
AuthVariableInfo.Attributes = Attributes;
|
|
|
|
return mAuthVarLibContextIn->UpdateVariable (
|
|
&AuthVariableInfo
|
|
);
|
|
}
|
|
|
|
/**
|
|
Update the variable region with Variable information.
|
|
|
|
@param[in] VariableName Name of variable.
|
|
@param[in] VendorGuid Guid of variable.
|
|
@param[in] Data Data pointer.
|
|
@param[in] DataSize Size of Data.
|
|
@param[in] Attributes Attribute value of the variable.
|
|
@param[in] TimeStamp Value of associated TimeStamp.
|
|
|
|
@retval EFI_SUCCESS The update operation is success.
|
|
@retval EFI_INVALID_PARAMETER Invalid parameter.
|
|
@retval EFI_WRITE_PROTECTED Variable is write-protected.
|
|
@retval EFI_OUT_OF_RESOURCES There is not enough resource.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AuthServiceInternalUpdateVariableWithTimeStamp (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN VOID *Data,
|
|
IN UINTN DataSize,
|
|
IN UINT32 Attributes,
|
|
IN EFI_TIME *TimeStamp
|
|
)
|
|
{
|
|
EFI_STATUS FindStatus;
|
|
VOID *OrgData;
|
|
UINTN OrgDataSize;
|
|
AUTH_VARIABLE_INFO AuthVariableInfo;
|
|
|
|
FindStatus = AuthServiceInternalFindVariable (
|
|
VariableName,
|
|
VendorGuid,
|
|
&OrgData,
|
|
&OrgDataSize
|
|
);
|
|
|
|
//
|
|
// EFI_VARIABLE_APPEND_WRITE attribute only effects for existing variable
|
|
//
|
|
if (!EFI_ERROR (FindStatus) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0)) {
|
|
if ((CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) &&
|
|
((StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) || (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0) ||
|
|
(StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE2) == 0))) ||
|
|
(CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0)))
|
|
{
|
|
//
|
|
// For variables with formatted as EFI_SIGNATURE_LIST, the driver shall not perform an append of
|
|
// EFI_SIGNATURE_DATA values that are already part of the existing variable value.
|
|
//
|
|
FilterSignatureList (
|
|
OrgData,
|
|
OrgDataSize,
|
|
Data,
|
|
&DataSize
|
|
);
|
|
}
|
|
}
|
|
|
|
ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));
|
|
AuthVariableInfo.VariableName = VariableName;
|
|
AuthVariableInfo.VendorGuid = VendorGuid;
|
|
AuthVariableInfo.Data = Data;
|
|
AuthVariableInfo.DataSize = DataSize;
|
|
AuthVariableInfo.Attributes = Attributes;
|
|
AuthVariableInfo.TimeStamp = TimeStamp;
|
|
return mAuthVarLibContextIn->UpdateVariable (
|
|
&AuthVariableInfo
|
|
);
|
|
}
|
|
|
|
/**
|
|
Determine whether this operation needs a physical present user.
|
|
|
|
@param[in] VariableName Name of the Variable.
|
|
@param[in] VendorGuid GUID of the Variable.
|
|
|
|
@retval TRUE This variable is protected, only a physical present user could set this variable.
|
|
@retval FALSE This variable is not protected.
|
|
|
|
**/
|
|
BOOLEAN
|
|
NeedPhysicallyPresent (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid
|
|
)
|
|
{
|
|
// If the VariablePolicy engine is disabled, allow deletion of any authenticated variables.
|
|
if (IsVariablePolicyEnabled ()) {
|
|
if ( (CompareGuid (VendorGuid, &gEfiSecureBootEnableDisableGuid) && (StrCmp (VariableName, EFI_SECURE_BOOT_ENABLE_NAME) == 0))
|
|
|| (CompareGuid (VendorGuid, &gEfiCustomModeEnableGuid) && (StrCmp (VariableName, EFI_CUSTOM_MODE_NAME) == 0)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Determine whether the platform is operating in Custom Secure Boot mode.
|
|
|
|
@retval TRUE The platform is operating in Custom mode.
|
|
@retval FALSE The platform is operating in Standard mode.
|
|
|
|
**/
|
|
BOOLEAN
|
|
InCustomMode (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *Data;
|
|
UINTN DataSize;
|
|
|
|
Status = AuthServiceInternalFindVariable (EFI_CUSTOM_MODE_NAME, &gEfiCustomModeEnableGuid, &Data, &DataSize);
|
|
if (!EFI_ERROR (Status) && (*(UINT8 *)Data == CUSTOM_SECURE_BOOT_MODE)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Update platform mode.
|
|
|
|
@param[in] Mode SETUP_MODE or USER_MODE.
|
|
|
|
@return EFI_INVALID_PARAMETER Invalid parameter.
|
|
@return EFI_SUCCESS Update platform mode successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UpdatePlatformMode (
|
|
IN UINT32 Mode
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *Data;
|
|
UINTN DataSize;
|
|
UINT8 SecureBootMode;
|
|
UINT8 SecureBootEnable;
|
|
UINTN VariableDataSize;
|
|
|
|
Status = AuthServiceInternalFindVariable (
|
|
EFI_SETUP_MODE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
&Data,
|
|
&DataSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Update the value of SetupMode variable by a simple mem copy, this could avoid possible
|
|
// variable storage reclaim at runtime.
|
|
//
|
|
mPlatformMode = (UINT8)Mode;
|
|
CopyMem (Data, &mPlatformMode, sizeof (UINT8));
|
|
|
|
if (mAuthVarLibContextIn->AtRuntime ()) {
|
|
//
|
|
// SecureBoot Variable indicates whether the platform firmware is operating
|
|
// in Secure boot mode (1) or not (0), so we should not change SecureBoot
|
|
// Variable in runtime.
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Check "SecureBoot" variable's existence.
|
|
// If it doesn't exist, firmware has no capability to perform driver signing verification,
|
|
// then set "SecureBoot" to 0.
|
|
//
|
|
Status = AuthServiceInternalFindVariable (
|
|
EFI_SECURE_BOOT_MODE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
&Data,
|
|
&DataSize
|
|
);
|
|
//
|
|
// If "SecureBoot" variable exists, then check "SetupMode" variable update.
|
|
// If "SetupMode" variable is USER_MODE, "SecureBoot" variable is set to 1.
|
|
// If "SetupMode" variable is SETUP_MODE, "SecureBoot" variable is set to 0.
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
SecureBootMode = SECURE_BOOT_MODE_DISABLE;
|
|
} else {
|
|
if (mPlatformMode == USER_MODE) {
|
|
SecureBootMode = SECURE_BOOT_MODE_ENABLE;
|
|
} else if (mPlatformMode == SETUP_MODE) {
|
|
SecureBootMode = SECURE_BOOT_MODE_DISABLE;
|
|
} else {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
Status = AuthServiceInternalUpdateVariable (
|
|
EFI_SECURE_BOOT_MODE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
&SecureBootMode,
|
|
sizeof (UINT8),
|
|
EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Check "SecureBootEnable" variable's existence. It can enable/disable secure boot feature.
|
|
//
|
|
Status = AuthServiceInternalFindVariable (
|
|
EFI_SECURE_BOOT_ENABLE_NAME,
|
|
&gEfiSecureBootEnableDisableGuid,
|
|
&Data,
|
|
&DataSize
|
|
);
|
|
|
|
if (SecureBootMode == SECURE_BOOT_MODE_ENABLE) {
|
|
//
|
|
// Create the "SecureBootEnable" variable as secure boot is enabled.
|
|
//
|
|
SecureBootEnable = SECURE_BOOT_ENABLE;
|
|
VariableDataSize = sizeof (SecureBootEnable);
|
|
} else {
|
|
//
|
|
// Delete the "SecureBootEnable" variable if this variable exist as "SecureBoot"
|
|
// variable is not in secure boot state.
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
SecureBootEnable = SECURE_BOOT_DISABLE;
|
|
VariableDataSize = 0;
|
|
}
|
|
|
|
Status = AuthServiceInternalUpdateVariable (
|
|
EFI_SECURE_BOOT_ENABLE_NAME,
|
|
&gEfiSecureBootEnableDisableGuid,
|
|
&SecureBootEnable,
|
|
VariableDataSize,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Check input data form to make sure it is a valid EFI_SIGNATURE_LIST for PK/KEK/db/dbx/dbt variable.
|
|
|
|
@param[in] VariableName Name of Variable to be check.
|
|
@param[in] VendorGuid Variable vendor GUID.
|
|
@param[in] Data Point to the variable data to be checked.
|
|
@param[in] DataSize Size of Data.
|
|
|
|
@return EFI_INVALID_PARAMETER Invalid signature list format.
|
|
@return EFI_SUCCESS Passed signature list format check successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
CheckSignatureListFormat (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN VOID *Data,
|
|
IN UINTN DataSize
|
|
)
|
|
{
|
|
EFI_SIGNATURE_LIST *SigList;
|
|
UINTN SigDataSize;
|
|
UINT32 Index;
|
|
UINT32 SigCount;
|
|
BOOLEAN IsPk;
|
|
VOID *RsaContext;
|
|
EFI_SIGNATURE_DATA *CertData;
|
|
UINTN CertLen;
|
|
|
|
if (DataSize == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
ASSERT (VariableName != NULL && VendorGuid != NULL && Data != NULL);
|
|
|
|
if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_PLATFORM_KEY_NAME) == 0)) {
|
|
IsPk = TRUE;
|
|
} else if ((CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0)) ||
|
|
(CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) &&
|
|
((StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) || (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0) ||
|
|
(StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE2) == 0))))
|
|
{
|
|
IsPk = FALSE;
|
|
} else {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
SigCount = 0;
|
|
SigList = (EFI_SIGNATURE_LIST *)Data;
|
|
SigDataSize = DataSize;
|
|
RsaContext = NULL;
|
|
|
|
//
|
|
// Walk through the input signature list and check the data format.
|
|
// If any signature is incorrectly formed, the whole check will fail.
|
|
//
|
|
while ((SigDataSize > 0) && (SigDataSize >= SigList->SignatureListSize)) {
|
|
for (Index = 0; Index < (sizeof (mSupportSigItem) / sizeof (EFI_SIGNATURE_ITEM)); Index++ ) {
|
|
if (CompareGuid (&SigList->SignatureType, &mSupportSigItem[Index].SigType)) {
|
|
//
|
|
// The value of SignatureSize should always be 16 (size of SignatureOwner
|
|
// component) add the data length according to signature type.
|
|
//
|
|
if ((mSupportSigItem[Index].SigDataSize != ((UINT32) ~0)) &&
|
|
((SigList->SignatureSize - sizeof (EFI_GUID)) != mSupportSigItem[Index].SigDataSize))
|
|
{
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((mSupportSigItem[Index].SigHeaderSize != ((UINT32) ~0)) &&
|
|
(SigList->SignatureHeaderSize != mSupportSigItem[Index].SigHeaderSize))
|
|
{
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index == (sizeof (mSupportSigItem) / sizeof (EFI_SIGNATURE_ITEM))) {
|
|
//
|
|
// Undefined signature type.
|
|
//
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {
|
|
//
|
|
// Try to retrieve the RSA public key from the X.509 certificate.
|
|
// If this operation fails, it's not a valid certificate.
|
|
//
|
|
RsaContext = RsaNew ();
|
|
if (RsaContext == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
CertData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigList + sizeof (EFI_SIGNATURE_LIST) + SigList->SignatureHeaderSize);
|
|
CertLen = SigList->SignatureSize - sizeof (EFI_GUID);
|
|
if (!RsaGetPublicKeyFromX509 (CertData->SignatureData, CertLen, &RsaContext)) {
|
|
RsaFree (RsaContext);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
RsaFree (RsaContext);
|
|
}
|
|
|
|
if ((SigList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - SigList->SignatureHeaderSize) % SigList->SignatureSize != 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
SigCount += (SigList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - SigList->SignatureHeaderSize) / SigList->SignatureSize;
|
|
|
|
SigDataSize -= SigList->SignatureListSize;
|
|
SigList = (EFI_SIGNATURE_LIST *)((UINT8 *)SigList + SigList->SignatureListSize);
|
|
}
|
|
|
|
if (((UINTN)SigList - (UINTN)Data) != DataSize) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (IsPk && (SigCount > 1)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Update "VendorKeys" variable to record the out of band secure boot key modification.
|
|
|
|
@return EFI_SUCCESS Variable is updated successfully.
|
|
@return Others Failed to update variable.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
VendorKeyIsModified (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
if (mVendorKeyState == VENDOR_KEYS_MODIFIED) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
mVendorKeyState = VENDOR_KEYS_MODIFIED;
|
|
|
|
Status = AuthServiceInternalUpdateVariable (
|
|
EFI_VENDOR_KEYS_NV_VARIABLE_NAME,
|
|
&gEfiVendorKeysNvGuid,
|
|
&mVendorKeyState,
|
|
sizeof (UINT8),
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
return AuthServiceInternalUpdateVariable (
|
|
EFI_VENDOR_KEYS_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
&mVendorKeyState,
|
|
sizeof (UINT8),
|
|
EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS
|
|
);
|
|
}
|
|
|
|
/**
|
|
Process variable with platform key for verification.
|
|
|
|
Caution: This function may receive untrusted input.
|
|
This function may be invoked in SMM mode, and datasize and data are external input.
|
|
This function will do basic validation, before parse the data.
|
|
This function will parse the authentication carefully to avoid security issues, like
|
|
buffer overflow, integer overflow.
|
|
This function will check attribute carefully to avoid authentication bypass.
|
|
|
|
@param[in] VariableName Name of Variable to be found.
|
|
@param[in] VendorGuid Variable vendor GUID.
|
|
@param[in] Data Data pointer.
|
|
@param[in] DataSize Size of Data found. If size is less than the
|
|
data, this value contains the required size.
|
|
@param[in] Attributes Attribute value of the variable
|
|
@param[in] IsPk Indicate whether it is to process pk.
|
|
|
|
@return EFI_INVALID_PARAMETER Invalid parameter.
|
|
@return EFI_SECURITY_VIOLATION The variable does NOT pass the validation.
|
|
check carried out by the firmware.
|
|
@return EFI_SUCCESS Variable passed validation successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ProcessVarWithPk (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN VOID *Data,
|
|
IN UINTN DataSize,
|
|
IN UINT32 Attributes OPTIONAL,
|
|
IN BOOLEAN IsPk
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOLEAN Del;
|
|
UINT8 *Payload;
|
|
UINTN PayloadSize;
|
|
|
|
if (((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) ||
|
|
((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0))
|
|
{
|
|
//
|
|
// PK, KEK and db/dbx/dbt should set EFI_VARIABLE_NON_VOLATILE attribute and should be a time-based
|
|
// authenticated variable.
|
|
//
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Init state of Del. State may change due to secure check
|
|
//
|
|
Del = FALSE;
|
|
if ( (InCustomMode () && UserPhysicalPresent ())
|
|
|| ( (mPlatformMode == SETUP_MODE)
|
|
&& !(FeaturePcdGet (PcdRequireSelfSignedPk) && IsPk)))
|
|
{
|
|
Payload = (UINT8 *)Data + AUTHINFO2_SIZE (Data);
|
|
PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
|
|
if (PayloadSize == 0) {
|
|
Del = TRUE;
|
|
}
|
|
|
|
Status = CheckSignatureListFormat (VariableName, VendorGuid, Payload, PayloadSize);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = AuthServiceInternalUpdateVariableWithTimeStamp (
|
|
VariableName,
|
|
VendorGuid,
|
|
Payload,
|
|
PayloadSize,
|
|
Attributes,
|
|
&((EFI_VARIABLE_AUTHENTICATION_2 *)Data)->TimeStamp
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if ( (mPlatformMode != SETUP_MODE)
|
|
|| (FeaturePcdGet (PcdRequireSelfSignedPk) && IsPk))
|
|
{
|
|
Status = VendorKeyIsModified ();
|
|
}
|
|
} else if (mPlatformMode == USER_MODE) {
|
|
//
|
|
// Verify against X509 Cert in PK database.
|
|
//
|
|
Status = VerifyTimeBasedPayloadAndUpdate (
|
|
VariableName,
|
|
VendorGuid,
|
|
Data,
|
|
DataSize,
|
|
Attributes,
|
|
AuthVarTypePk,
|
|
&Del
|
|
);
|
|
} else {
|
|
//
|
|
// Verify against the certificate in data payload.
|
|
//
|
|
Status = VerifyTimeBasedPayloadAndUpdate (
|
|
VariableName,
|
|
VendorGuid,
|
|
Data,
|
|
DataSize,
|
|
Attributes,
|
|
AuthVarTypePayload,
|
|
&Del
|
|
);
|
|
}
|
|
|
|
if (!EFI_ERROR (Status) && IsPk) {
|
|
if ((mPlatformMode == SETUP_MODE) && !Del) {
|
|
//
|
|
// If enroll PK in setup mode, need change to user mode.
|
|
//
|
|
Status = UpdatePlatformMode (USER_MODE);
|
|
} else if ((mPlatformMode == USER_MODE) && Del) {
|
|
//
|
|
// If delete PK in user mode, need change to setup mode.
|
|
//
|
|
Status = UpdatePlatformMode (SETUP_MODE);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Process variable with key exchange key for verification.
|
|
|
|
Caution: This function may receive untrusted input.
|
|
This function may be invoked in SMM mode, and datasize and data are external input.
|
|
This function will do basic validation, before parse the data.
|
|
This function will parse the authentication carefully to avoid security issues, like
|
|
buffer overflow, integer overflow.
|
|
This function will check attribute carefully to avoid authentication bypass.
|
|
|
|
@param[in] VariableName Name of Variable to be found.
|
|
@param[in] VendorGuid Variable vendor GUID.
|
|
@param[in] Data Data pointer.
|
|
@param[in] DataSize Size of Data found. If size is less than the
|
|
data, this value contains the required size.
|
|
@param[in] Attributes Attribute value of the variable.
|
|
|
|
@return EFI_INVALID_PARAMETER Invalid parameter.
|
|
@return EFI_SECURITY_VIOLATION The variable does NOT pass the validation
|
|
check carried out by the firmware.
|
|
@return EFI_SUCCESS Variable pass validation successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ProcessVarWithKek (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN VOID *Data,
|
|
IN UINTN DataSize,
|
|
IN UINT32 Attributes OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *Payload;
|
|
UINTN PayloadSize;
|
|
|
|
if (((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) ||
|
|
((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0))
|
|
{
|
|
//
|
|
// DB, DBX and DBT should set EFI_VARIABLE_NON_VOLATILE attribute and should be a time-based
|
|
// authenticated variable.
|
|
//
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
if ((mPlatformMode == USER_MODE) && !(InCustomMode () && UserPhysicalPresent ())) {
|
|
//
|
|
// Time-based, verify against X509 Cert KEK.
|
|
//
|
|
return VerifyTimeBasedPayloadAndUpdate (
|
|
VariableName,
|
|
VendorGuid,
|
|
Data,
|
|
DataSize,
|
|
Attributes,
|
|
AuthVarTypeKek,
|
|
NULL
|
|
);
|
|
} else {
|
|
//
|
|
// If in setup mode or custom secure boot mode, no authentication needed.
|
|
//
|
|
Payload = (UINT8 *)Data + AUTHINFO2_SIZE (Data);
|
|
PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
|
|
|
|
Status = CheckSignatureListFormat (VariableName, VendorGuid, Payload, PayloadSize);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = AuthServiceInternalUpdateVariableWithTimeStamp (
|
|
VariableName,
|
|
VendorGuid,
|
|
Payload,
|
|
PayloadSize,
|
|
Attributes,
|
|
&((EFI_VARIABLE_AUTHENTICATION_2 *)Data)->TimeStamp
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (mPlatformMode != SETUP_MODE) {
|
|
Status = VendorKeyIsModified ();
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Check if it is to delete auth variable.
|
|
|
|
@param[in] OrgAttributes Original attribute value of the variable.
|
|
@param[in] Data Data pointer.
|
|
@param[in] DataSize Size of Data.
|
|
@param[in] Attributes Attribute value of the variable.
|
|
|
|
@retval TRUE It is to delete auth variable.
|
|
@retval FALSE It is not to delete auth variable.
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsDeleteAuthVariable (
|
|
IN UINT32 OrgAttributes,
|
|
IN VOID *Data,
|
|
IN UINTN DataSize,
|
|
IN UINT32 Attributes
|
|
)
|
|
{
|
|
BOOLEAN Del;
|
|
UINTN PayloadSize;
|
|
|
|
Del = FALSE;
|
|
|
|
//
|
|
// To delete a variable created with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
|
|
// or the EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute,
|
|
// SetVariable must be used with attributes matching the existing variable
|
|
// and the DataSize set to the size of the AuthInfo descriptor.
|
|
//
|
|
if ((Attributes == OrgAttributes) &&
|
|
((Attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) != 0))
|
|
{
|
|
if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
|
PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
|
|
if (PayloadSize == 0) {
|
|
Del = TRUE;
|
|
}
|
|
} else {
|
|
PayloadSize = DataSize - AUTHINFO_SIZE;
|
|
if (PayloadSize == 0) {
|
|
Del = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Del;
|
|
}
|
|
|
|
/**
|
|
Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set
|
|
|
|
Caution: This function may receive untrusted input.
|
|
This function may be invoked in SMM mode, and datasize and data are external input.
|
|
This function will do basic validation, before parse the data.
|
|
This function will parse the authentication carefully to avoid security issues, like
|
|
buffer overflow, integer overflow.
|
|
This function will check attribute carefully to avoid authentication bypass.
|
|
|
|
@param[in] VariableName Name of the variable.
|
|
@param[in] VendorGuid Variable vendor GUID.
|
|
@param[in] Data Data pointer.
|
|
@param[in] DataSize Size of Data.
|
|
@param[in] Attributes Attribute value of the variable.
|
|
|
|
@return EFI_INVALID_PARAMETER Invalid parameter.
|
|
@return EFI_WRITE_PROTECTED Variable is write-protected and needs authentication with
|
|
EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set.
|
|
@return EFI_OUT_OF_RESOURCES The Database to save the public key is full.
|
|
@return EFI_SECURITY_VIOLATION The variable is with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
|
|
set, but the AuthInfo does NOT pass the validation
|
|
check carried out by the firmware.
|
|
@return EFI_SUCCESS Variable is not write-protected or pass validation successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ProcessVariable (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN VOID *Data,
|
|
IN UINTN DataSize,
|
|
IN UINT32 Attributes
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
AUTH_VARIABLE_INFO OrgVariableInfo;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
ZeroMem (&OrgVariableInfo, sizeof (OrgVariableInfo));
|
|
Status = mAuthVarLibContextIn->FindVariable (
|
|
VariableName,
|
|
VendorGuid,
|
|
&OrgVariableInfo
|
|
);
|
|
|
|
// If the VariablePolicy engine is disabled, allow deletion of any authenticated variables.
|
|
if ((!EFI_ERROR (Status)) && IsDeleteAuthVariable (OrgVariableInfo.Attributes, Data, DataSize, Attributes) && (UserPhysicalPresent () || !IsVariablePolicyEnabled ())) {
|
|
//
|
|
// Allow the delete operation of common authenticated variable(AT or AW) at user physical presence.
|
|
//
|
|
Status = AuthServiceInternalUpdateVariable (
|
|
VariableName,
|
|
VendorGuid,
|
|
NULL,
|
|
0,
|
|
0
|
|
);
|
|
if (!EFI_ERROR (Status) && ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0)) {
|
|
Status = DeleteCertsFromDb (VariableName, VendorGuid, Attributes);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (NeedPhysicallyPresent (VariableName, VendorGuid) && !UserPhysicalPresent ()) {
|
|
//
|
|
// This variable is protected, only physical present user could modify its value.
|
|
//
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
//
|
|
if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
|
//
|
|
// Reject Counter Based Auth Variable processing request.
|
|
//
|
|
return EFI_UNSUPPORTED;
|
|
} else if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
|
//
|
|
// Process Time-based Authenticated variable.
|
|
//
|
|
return VerifyTimeBasedPayloadAndUpdate (
|
|
VariableName,
|
|
VendorGuid,
|
|
Data,
|
|
DataSize,
|
|
Attributes,
|
|
AuthVarTypePriv,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
if ((OrgVariableInfo.Data != NULL) &&
|
|
((OrgVariableInfo.Attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) != 0))
|
|
{
|
|
//
|
|
// If the variable is already write-protected, it always needs authentication before update.
|
|
//
|
|
return EFI_WRITE_PROTECTED;
|
|
}
|
|
|
|
//
|
|
// Not authenticated variable, just update variable as usual.
|
|
//
|
|
Status = AuthServiceInternalUpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Filter out the duplicated EFI_SIGNATURE_DATA from the new data by comparing to the original data.
|
|
|
|
@param[in] Data Pointer to original EFI_SIGNATURE_LIST.
|
|
@param[in] DataSize Size of Data buffer.
|
|
@param[in, out] NewData Pointer to new EFI_SIGNATURE_LIST.
|
|
@param[in, out] NewDataSize Size of NewData buffer.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
FilterSignatureList (
|
|
IN VOID *Data,
|
|
IN UINTN DataSize,
|
|
IN OUT VOID *NewData,
|
|
IN OUT UINTN *NewDataSize
|
|
)
|
|
{
|
|
EFI_SIGNATURE_LIST *CertList;
|
|
EFI_SIGNATURE_DATA *Cert;
|
|
UINTN CertCount;
|
|
EFI_SIGNATURE_LIST *NewCertList;
|
|
EFI_SIGNATURE_DATA *NewCert;
|
|
UINTN NewCertCount;
|
|
UINTN Index;
|
|
UINTN Index2;
|
|
UINTN Size;
|
|
UINT8 *Tail;
|
|
UINTN CopiedCount;
|
|
UINTN SignatureListSize;
|
|
BOOLEAN IsNewCert;
|
|
UINT8 *TempData;
|
|
UINTN TempDataSize;
|
|
EFI_STATUS Status;
|
|
|
|
if (*NewDataSize == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
TempDataSize = *NewDataSize;
|
|
Status = mAuthVarLibContextIn->GetScratchBuffer (&TempDataSize, (VOID **)&TempData);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Tail = TempData;
|
|
|
|
NewCertList = (EFI_SIGNATURE_LIST *)NewData;
|
|
while ((*NewDataSize > 0) && (*NewDataSize >= NewCertList->SignatureListSize)) {
|
|
NewCert = (EFI_SIGNATURE_DATA *)((UINT8 *)NewCertList + sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize);
|
|
NewCertCount = (NewCertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - NewCertList->SignatureHeaderSize) / NewCertList->SignatureSize;
|
|
|
|
CopiedCount = 0;
|
|
for (Index = 0; Index < NewCertCount; Index++) {
|
|
IsNewCert = TRUE;
|
|
|
|
Size = DataSize;
|
|
CertList = (EFI_SIGNATURE_LIST *)Data;
|
|
while ((Size > 0) && (Size >= CertList->SignatureListSize)) {
|
|
if (CompareGuid (&CertList->SignatureType, &NewCertList->SignatureType) &&
|
|
(CertList->SignatureSize == NewCertList->SignatureSize))
|
|
{
|
|
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
|
|
CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
|
|
for (Index2 = 0; Index2 < CertCount; Index2++) {
|
|
//
|
|
// Iterate each Signature Data in this Signature List.
|
|
//
|
|
if (CompareMem (NewCert, Cert, CertList->SignatureSize) == 0) {
|
|
IsNewCert = FALSE;
|
|
break;
|
|
}
|
|
|
|
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)Cert + CertList->SignatureSize);
|
|
}
|
|
}
|
|
|
|
if (!IsNewCert) {
|
|
break;
|
|
}
|
|
|
|
Size -= CertList->SignatureListSize;
|
|
CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize);
|
|
}
|
|
|
|
if (IsNewCert) {
|
|
//
|
|
// New EFI_SIGNATURE_DATA, keep it.
|
|
//
|
|
if (CopiedCount == 0) {
|
|
//
|
|
// Copy EFI_SIGNATURE_LIST header for only once.
|
|
//
|
|
CopyMem (Tail, NewCertList, sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize);
|
|
Tail = Tail + sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize;
|
|
}
|
|
|
|
CopyMem (Tail, NewCert, NewCertList->SignatureSize);
|
|
Tail += NewCertList->SignatureSize;
|
|
CopiedCount++;
|
|
}
|
|
|
|
NewCert = (EFI_SIGNATURE_DATA *)((UINT8 *)NewCert + NewCertList->SignatureSize);
|
|
}
|
|
|
|
//
|
|
// Update SignatureListSize in the kept EFI_SIGNATURE_LIST.
|
|
//
|
|
if (CopiedCount != 0) {
|
|
SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize + (CopiedCount * NewCertList->SignatureSize);
|
|
CertList = (EFI_SIGNATURE_LIST *)(Tail - SignatureListSize);
|
|
CertList->SignatureListSize = (UINT32)SignatureListSize;
|
|
}
|
|
|
|
*NewDataSize -= NewCertList->SignatureListSize;
|
|
NewCertList = (EFI_SIGNATURE_LIST *)((UINT8 *)NewCertList + NewCertList->SignatureListSize);
|
|
}
|
|
|
|
TempDataSize = (Tail - (UINT8 *)TempData);
|
|
|
|
CopyMem (NewData, TempData, TempDataSize);
|
|
*NewDataSize = TempDataSize;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Compare two EFI_TIME data.
|
|
|
|
|
|
@param FirstTime A pointer to the first EFI_TIME data.
|
|
@param SecondTime A pointer to the second EFI_TIME data.
|
|
|
|
@retval TRUE The FirstTime is not later than the SecondTime.
|
|
@retval FALSE The FirstTime is later than the SecondTime.
|
|
|
|
**/
|
|
BOOLEAN
|
|
AuthServiceInternalCompareTimeStamp (
|
|
IN EFI_TIME *FirstTime,
|
|
IN EFI_TIME *SecondTime
|
|
)
|
|
{
|
|
if (FirstTime->Year != SecondTime->Year) {
|
|
return (BOOLEAN)(FirstTime->Year < SecondTime->Year);
|
|
} else if (FirstTime->Month != SecondTime->Month) {
|
|
return (BOOLEAN)(FirstTime->Month < SecondTime->Month);
|
|
} else if (FirstTime->Day != SecondTime->Day) {
|
|
return (BOOLEAN)(FirstTime->Day < SecondTime->Day);
|
|
} else if (FirstTime->Hour != SecondTime->Hour) {
|
|
return (BOOLEAN)(FirstTime->Hour < SecondTime->Hour);
|
|
} else if (FirstTime->Minute != SecondTime->Minute) {
|
|
return (BOOLEAN)(FirstTime->Minute < SecondTime->Minute);
|
|
}
|
|
|
|
return (BOOLEAN)(FirstTime->Second <= SecondTime->Second);
|
|
}
|
|
|
|
/**
|
|
Calculate SHA digest of SignerCert CommonName + ToplevelCert tbsCertificate.
|
|
SignerCert and ToplevelCert are inside the signer certificate chain.
|
|
|
|
@param[in] HashAlgId Hash algorithm index.
|
|
@param[in] SignerCert A pointer to SignerCert data.
|
|
@param[in] SignerCertSize Length of SignerCert data.
|
|
@param[in] TopLevelCert A pointer to TopLevelCert data.
|
|
@param[in] TopLevelCertSize Length of TopLevelCert data.
|
|
@param[out] ShaDigest Sha digest calculated.
|
|
|
|
@return EFI_ABORTED Digest process failed.
|
|
@return EFI_SUCCESS SHA Digest is successfully calculated.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
CalculatePrivAuthVarSignChainSHADigest (
|
|
IN UINT8 HashAlgId,
|
|
IN UINT8 *SignerCert,
|
|
IN UINTN SignerCertSize,
|
|
IN UINT8 *TopLevelCert,
|
|
IN UINTN TopLevelCertSize,
|
|
OUT UINT8 *ShaDigest
|
|
)
|
|
{
|
|
UINT8 *TbsCert;
|
|
UINTN TbsCertSize;
|
|
CHAR8 CertCommonName[128];
|
|
UINTN CertCommonNameSize;
|
|
BOOLEAN CryptoStatus;
|
|
EFI_STATUS Status;
|
|
|
|
if (HashAlgId >= (sizeof (mHashInfo) / sizeof (EFI_HASH_INFO))) {
|
|
DEBUG ((DEBUG_INFO, "%a Unsupported Hash Algorithm %d\n", __func__, HashAlgId));
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
CertCommonNameSize = sizeof (CertCommonName);
|
|
|
|
//
|
|
// Get SignerCert CommonName
|
|
//
|
|
Status = X509GetCommonName (SignerCert, SignerCertSize, CertCommonName, &CertCommonNameSize);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "%a Get SignerCert CommonName failed with status %x\n", __func__, Status));
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
//
|
|
// Get TopLevelCert tbsCertificate
|
|
//
|
|
if (!X509GetTBSCert (TopLevelCert, TopLevelCertSize, &TbsCert, &TbsCertSize)) {
|
|
DEBUG ((DEBUG_INFO, "%a Get Top-level Cert tbsCertificate failed!\n", __func__));
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
//
|
|
// Digest SignerCert CN + TopLevelCert tbsCertificate
|
|
//
|
|
ZeroMem (ShaDigest, mHashInfo[HashAlgId].HashSize);
|
|
CryptoStatus = mHashInfo[HashAlgId].Init (*(mHashInfo[HashAlgId].HashShaCtx));
|
|
if (!CryptoStatus) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
//
|
|
// '\0' is forced in CertCommonName. No overflow issue
|
|
//
|
|
CryptoStatus = mHashInfo[HashAlgId].Update (
|
|
*(mHashInfo[HashAlgId].HashShaCtx),
|
|
CertCommonName,
|
|
AsciiStrLen (CertCommonName)
|
|
);
|
|
if (!CryptoStatus) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
CryptoStatus = mHashInfo[HashAlgId].Update (*(mHashInfo[HashAlgId].HashShaCtx), TbsCert, TbsCertSize);
|
|
if (!CryptoStatus) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
CryptoStatus = mHashInfo[HashAlgId].Final (*(mHashInfo[HashAlgId].HashShaCtx), ShaDigest);
|
|
if (!CryptoStatus) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Find matching signer's certificates for common authenticated variable
|
|
by corresponding VariableName and VendorGuid from "certdb" or "certdbv".
|
|
|
|
The data format of "certdb" or "certdbv":
|
|
//
|
|
// UINT32 CertDbListSize;
|
|
// /// AUTH_CERT_DB_DATA Certs1[];
|
|
// /// AUTH_CERT_DB_DATA Certs2[];
|
|
// /// ...
|
|
// /// AUTH_CERT_DB_DATA Certsn[];
|
|
//
|
|
|
|
@param[in] VariableName Name of authenticated Variable.
|
|
@param[in] VendorGuid Vendor GUID of authenticated Variable.
|
|
@param[in] Data Pointer to variable "certdb" or "certdbv".
|
|
@param[in] DataSize Size of variable "certdb" or "certdbv".
|
|
@param[out] CertOffset Offset of matching CertData, from starting of Data.
|
|
@param[out] CertDataSize Length of CertData in bytes.
|
|
@param[out] CertNodeOffset Offset of matching AUTH_CERT_DB_DATA , from
|
|
starting of Data.
|
|
@param[out] CertNodeSize Length of AUTH_CERT_DB_DATA in bytes.
|
|
|
|
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
|
|
@retval EFI_NOT_FOUND Fail to find matching certs.
|
|
@retval EFI_SUCCESS Find matching certs and output parameters.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
FindCertsFromDb (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN UINT8 *Data,
|
|
IN UINTN DataSize,
|
|
OUT UINT32 *CertOffset OPTIONAL,
|
|
OUT UINT32 *CertDataSize OPTIONAL,
|
|
OUT UINT32 *CertNodeOffset OPTIONAL,
|
|
OUT UINT32 *CertNodeSize OPTIONAL
|
|
)
|
|
{
|
|
UINT32 Offset;
|
|
AUTH_CERT_DB_DATA *Ptr;
|
|
UINT32 CertSize;
|
|
UINT32 NameSize;
|
|
UINT32 NodeSize;
|
|
UINT32 CertDbListSize;
|
|
|
|
if ((VariableName == NULL) || (VendorGuid == NULL) || (Data == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check whether DataSize matches recorded CertDbListSize.
|
|
//
|
|
if (DataSize < sizeof (UINT32)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
CertDbListSize = ReadUnaligned32 ((UINT32 *)Data);
|
|
|
|
if (CertDbListSize != (UINT32)DataSize) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Offset = sizeof (UINT32);
|
|
|
|
//
|
|
// Get corresponding certificates by VendorGuid and VariableName.
|
|
//
|
|
while (Offset < (UINT32)DataSize) {
|
|
Ptr = (AUTH_CERT_DB_DATA *)(Data + Offset);
|
|
//
|
|
// Check whether VendorGuid matches.
|
|
//
|
|
if (CompareGuid (&Ptr->VendorGuid, VendorGuid)) {
|
|
NodeSize = ReadUnaligned32 (&Ptr->CertNodeSize);
|
|
NameSize = ReadUnaligned32 (&Ptr->NameSize);
|
|
CertSize = ReadUnaligned32 (&Ptr->CertDataSize);
|
|
|
|
if (NodeSize != sizeof (EFI_GUID) + sizeof (UINT32) * 3 + CertSize +
|
|
sizeof (CHAR16) * NameSize)
|
|
{
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Offset = Offset + sizeof (EFI_GUID) + sizeof (UINT32) * 3;
|
|
//
|
|
// Check whether VariableName matches.
|
|
//
|
|
if ((NameSize == StrLen (VariableName)) &&
|
|
(CompareMem (Data + Offset, VariableName, NameSize * sizeof (CHAR16)) == 0))
|
|
{
|
|
Offset = Offset + NameSize * sizeof (CHAR16);
|
|
|
|
if (CertOffset != NULL) {
|
|
*CertOffset = Offset;
|
|
}
|
|
|
|
if (CertDataSize != NULL) {
|
|
*CertDataSize = CertSize;
|
|
}
|
|
|
|
if (CertNodeOffset != NULL) {
|
|
*CertNodeOffset = (UINT32)((UINT8 *)Ptr - Data);
|
|
}
|
|
|
|
if (CertNodeSize != NULL) {
|
|
*CertNodeSize = NodeSize;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
Offset = Offset + NameSize * sizeof (CHAR16) + CertSize;
|
|
}
|
|
} else {
|
|
NodeSize = ReadUnaligned32 (&Ptr->CertNodeSize);
|
|
Offset = Offset + NodeSize;
|
|
}
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
/**
|
|
Retrieve signer's certificates for common authenticated variable
|
|
by corresponding VariableName and VendorGuid from "certdb"
|
|
or "certdbv" according to authenticated variable attributes.
|
|
|
|
@param[in] VariableName Name of authenticated Variable.
|
|
@param[in] VendorGuid Vendor GUID of authenticated Variable.
|
|
@param[in] Attributes Attributes of authenticated variable.
|
|
@param[out] CertData Pointer to signer's certificates.
|
|
@param[out] CertDataSize Length of CertData in bytes.
|
|
|
|
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
|
|
@retval EFI_NOT_FOUND Fail to find "certdb"/"certdbv" or matching certs.
|
|
@retval EFI_SUCCESS Get signer's certificates successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetCertsFromDb (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN UINT32 Attributes,
|
|
OUT UINT8 **CertData,
|
|
OUT UINT32 *CertDataSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *Data;
|
|
UINTN DataSize;
|
|
UINT32 CertOffset;
|
|
CHAR16 *DbName;
|
|
|
|
if ((VariableName == NULL) || (VendorGuid == NULL) || (CertData == NULL) || (CertDataSize == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
|
|
//
|
|
// Get variable "certdb".
|
|
//
|
|
DbName = EFI_CERT_DB_NAME;
|
|
} else {
|
|
//
|
|
// Get variable "certdbv".
|
|
//
|
|
DbName = EFI_CERT_DB_VOLATILE_NAME;
|
|
}
|
|
|
|
//
|
|
// Get variable "certdb" or "certdbv".
|
|
//
|
|
Status = AuthServiceInternalFindVariable (
|
|
DbName,
|
|
&gEfiCertDbGuid,
|
|
(VOID **)&Data,
|
|
&DataSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if ((DataSize == 0) || (Data == NULL)) {
|
|
ASSERT (FALSE);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = FindCertsFromDb (
|
|
VariableName,
|
|
VendorGuid,
|
|
Data,
|
|
DataSize,
|
|
&CertOffset,
|
|
CertDataSize,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
*CertData = Data + CertOffset;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Delete matching signer's certificates when deleting common authenticated
|
|
variable by corresponding VariableName and VendorGuid from "certdb" or
|
|
"certdbv" according to authenticated variable attributes.
|
|
|
|
@param[in] VariableName Name of authenticated Variable.
|
|
@param[in] VendorGuid Vendor GUID of authenticated Variable.
|
|
@param[in] Attributes Attributes of authenticated variable.
|
|
|
|
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
|
|
@retval EFI_NOT_FOUND Fail to find "certdb"/"certdbv" or matching certs.
|
|
@retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources.
|
|
@retval EFI_SUCCESS The operation is completed successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
DeleteCertsFromDb (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN UINT32 Attributes
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *Data;
|
|
UINTN DataSize;
|
|
UINT32 VarAttr;
|
|
UINT32 CertNodeOffset;
|
|
UINT32 CertNodeSize;
|
|
UINT8 *NewCertDb;
|
|
UINT32 NewCertDbSize;
|
|
CHAR16 *DbName;
|
|
|
|
if ((VariableName == NULL) || (VendorGuid == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
|
|
//
|
|
// Get variable "certdb".
|
|
//
|
|
DbName = EFI_CERT_DB_NAME;
|
|
VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
|
} else {
|
|
//
|
|
// Get variable "certdbv".
|
|
//
|
|
DbName = EFI_CERT_DB_VOLATILE_NAME;
|
|
VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
|
}
|
|
|
|
Status = AuthServiceInternalFindVariable (
|
|
DbName,
|
|
&gEfiCertDbGuid,
|
|
(VOID **)&Data,
|
|
&DataSize
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if ((DataSize == 0) || (Data == NULL)) {
|
|
ASSERT (FALSE);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
if (DataSize == sizeof (UINT32)) {
|
|
//
|
|
// There is no certs in "certdb" or "certdbv".
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Get corresponding cert node from "certdb" or "certdbv".
|
|
//
|
|
Status = FindCertsFromDb (
|
|
VariableName,
|
|
VendorGuid,
|
|
Data,
|
|
DataSize,
|
|
NULL,
|
|
NULL,
|
|
&CertNodeOffset,
|
|
&CertNodeSize
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (DataSize < (CertNodeOffset + CertNodeSize)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Construct new data content of variable "certdb" or "certdbv".
|
|
//
|
|
NewCertDbSize = (UINT32)DataSize - CertNodeSize;
|
|
NewCertDb = (UINT8 *)mCertDbStore;
|
|
|
|
//
|
|
// Copy the DB entries before deleting node.
|
|
//
|
|
CopyMem (NewCertDb, Data, CertNodeOffset);
|
|
//
|
|
// Update CertDbListSize.
|
|
//
|
|
CopyMem (NewCertDb, &NewCertDbSize, sizeof (UINT32));
|
|
//
|
|
// Copy the DB entries after deleting node.
|
|
//
|
|
if (DataSize > (CertNodeOffset + CertNodeSize)) {
|
|
CopyMem (
|
|
NewCertDb + CertNodeOffset,
|
|
Data + CertNodeOffset + CertNodeSize,
|
|
DataSize - CertNodeOffset - CertNodeSize
|
|
);
|
|
}
|
|
|
|
//
|
|
// Set "certdb" or "certdbv".
|
|
//
|
|
Status = AuthServiceInternalUpdateVariable (
|
|
DbName,
|
|
&gEfiCertDbGuid,
|
|
NewCertDb,
|
|
NewCertDbSize,
|
|
VarAttr
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Insert signer's certificates for common authenticated variable with VariableName
|
|
and VendorGuid in AUTH_CERT_DB_DATA to "certdb" or "certdbv" according to
|
|
time based authenticated variable attributes. CertData is the SHA digest of
|
|
SignerCert CommonName + TopLevelCert tbsCertificate.
|
|
|
|
@param[in] HashAlgId Hash algorithm index.
|
|
@param[in] VariableName Name of authenticated Variable.
|
|
@param[in] VendorGuid Vendor GUID of authenticated Variable.
|
|
@param[in] Attributes Attributes of authenticated variable.
|
|
@param[in] SignerCert Signer certificate data.
|
|
@param[in] SignerCertSize Length of signer certificate.
|
|
@param[in] TopLevelCert Top-level certificate data.
|
|
@param[in] TopLevelCertSize Length of top-level certificate.
|
|
|
|
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
|
|
@retval EFI_ACCESS_DENIED An AUTH_CERT_DB_DATA entry with same VariableName
|
|
and VendorGuid already exists.
|
|
@retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources.
|
|
@retval EFI_SUCCESS Insert an AUTH_CERT_DB_DATA entry to "certdb" or "certdbv"
|
|
|
|
**/
|
|
EFI_STATUS
|
|
InsertCertsToDb (
|
|
IN UINT8 HashAlgId,
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN UINT32 Attributes,
|
|
IN UINT8 *SignerCert,
|
|
IN UINTN SignerCertSize,
|
|
IN UINT8 *TopLevelCert,
|
|
IN UINTN TopLevelCertSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *Data;
|
|
UINTN DataSize;
|
|
UINT32 VarAttr;
|
|
UINT8 *NewCertDb;
|
|
UINT32 NewCertDbSize;
|
|
UINT32 CertNodeSize;
|
|
UINT32 NameSize;
|
|
UINT32 CertDataSize;
|
|
AUTH_CERT_DB_DATA *Ptr;
|
|
CHAR16 *DbName;
|
|
UINT8 ShaDigest[SHA_DIGEST_SIZE_MAX];
|
|
|
|
if ((VariableName == NULL) || (VendorGuid == NULL) || (SignerCert == NULL) || (TopLevelCert == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (HashAlgId >= (sizeof (mHashInfo) / sizeof (EFI_HASH_INFO))) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
|
|
//
|
|
// Get variable "certdb".
|
|
//
|
|
DbName = EFI_CERT_DB_NAME;
|
|
VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
|
} else {
|
|
//
|
|
// Get variable "certdbv".
|
|
//
|
|
DbName = EFI_CERT_DB_VOLATILE_NAME;
|
|
VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
|
}
|
|
|
|
//
|
|
// Get variable "certdb" or "certdbv".
|
|
//
|
|
Status = AuthServiceInternalFindVariable (
|
|
DbName,
|
|
&gEfiCertDbGuid,
|
|
(VOID **)&Data,
|
|
&DataSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if ((DataSize == 0) || (Data == NULL)) {
|
|
ASSERT (FALSE);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Find whether matching cert node already exists in "certdb" or "certdbv".
|
|
// If yes return error.
|
|
//
|
|
Status = FindCertsFromDb (
|
|
VariableName,
|
|
VendorGuid,
|
|
Data,
|
|
DataSize,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
ASSERT (FALSE);
|
|
return EFI_ACCESS_DENIED;
|
|
}
|
|
|
|
//
|
|
// Construct new data content of variable "certdb" or "certdbv".
|
|
//
|
|
NameSize = (UINT32)StrLen (VariableName);
|
|
CertDataSize = mHashInfo[HashAlgId].HashSize;
|
|
CertNodeSize = sizeof (AUTH_CERT_DB_DATA) + (UINT32)CertDataSize + NameSize * sizeof (CHAR16);
|
|
NewCertDbSize = (UINT32)DataSize + CertNodeSize;
|
|
if (NewCertDbSize > mMaxCertDbSize) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = CalculatePrivAuthVarSignChainSHADigest (
|
|
HashAlgId,
|
|
SignerCert,
|
|
SignerCertSize,
|
|
TopLevelCert,
|
|
TopLevelCertSize,
|
|
ShaDigest
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
NewCertDb = (UINT8 *)mCertDbStore;
|
|
|
|
//
|
|
// Copy the DB entries before inserting node.
|
|
//
|
|
CopyMem (NewCertDb, Data, DataSize);
|
|
//
|
|
// Update CertDbListSize.
|
|
//
|
|
CopyMem (NewCertDb, &NewCertDbSize, sizeof (UINT32));
|
|
//
|
|
// Construct new cert node.
|
|
//
|
|
Ptr = (AUTH_CERT_DB_DATA *)(NewCertDb + DataSize);
|
|
CopyGuid (&Ptr->VendorGuid, VendorGuid);
|
|
CopyMem (&Ptr->CertNodeSize, &CertNodeSize, sizeof (UINT32));
|
|
CopyMem (&Ptr->NameSize, &NameSize, sizeof (UINT32));
|
|
CopyMem (&Ptr->CertDataSize, &CertDataSize, sizeof (UINT32));
|
|
|
|
CopyMem (
|
|
(UINT8 *)Ptr + sizeof (AUTH_CERT_DB_DATA),
|
|
VariableName,
|
|
NameSize * sizeof (CHAR16)
|
|
);
|
|
|
|
CopyMem (
|
|
(UINT8 *)Ptr + sizeof (AUTH_CERT_DB_DATA) + NameSize * sizeof (CHAR16),
|
|
ShaDigest,
|
|
CertDataSize
|
|
);
|
|
|
|
//
|
|
// Set "certdb" or "certdbv".
|
|
//
|
|
Status = AuthServiceInternalUpdateVariable (
|
|
DbName,
|
|
&gEfiCertDbGuid,
|
|
NewCertDb,
|
|
NewCertDbSize,
|
|
VarAttr
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Clean up signer's certificates for common authenticated variable
|
|
by corresponding VariableName and VendorGuid from "certdb".
|
|
System may break down during Timebased Variable update & certdb update,
|
|
make them inconsistent, this function is called in AuthVariable Init
|
|
to ensure consistency.
|
|
|
|
@retval EFI_NOT_FOUND Fail to find variable "certdb".
|
|
@retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources.
|
|
@retval EFI_SUCCESS The operation is completed successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
CleanCertsFromDb (
|
|
VOID
|
|
)
|
|
{
|
|
UINT32 Offset;
|
|
AUTH_CERT_DB_DATA *Ptr;
|
|
UINT32 NameSize;
|
|
UINT32 NodeSize;
|
|
CHAR16 *VariableName;
|
|
EFI_STATUS Status;
|
|
BOOLEAN CertCleaned;
|
|
UINT8 *Data;
|
|
UINTN DataSize;
|
|
EFI_GUID AuthVarGuid;
|
|
AUTH_VARIABLE_INFO AuthVariableInfo;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
//
|
|
// Get corresponding certificates by VendorGuid and VariableName.
|
|
//
|
|
do {
|
|
CertCleaned = FALSE;
|
|
|
|
//
|
|
// Get latest variable "certdb"
|
|
//
|
|
Status = AuthServiceInternalFindVariable (
|
|
EFI_CERT_DB_NAME,
|
|
&gEfiCertDbGuid,
|
|
(VOID **)&Data,
|
|
&DataSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if ((DataSize == 0) || (Data == NULL)) {
|
|
ASSERT (FALSE);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Offset = sizeof (UINT32);
|
|
|
|
while (Offset < (UINT32)DataSize) {
|
|
Ptr = (AUTH_CERT_DB_DATA *)(Data + Offset);
|
|
NodeSize = ReadUnaligned32 (&Ptr->CertNodeSize);
|
|
NameSize = ReadUnaligned32 (&Ptr->NameSize);
|
|
|
|
//
|
|
// Get VarName tailed with '\0'
|
|
//
|
|
VariableName = AllocateZeroPool ((NameSize + 1) * sizeof (CHAR16));
|
|
if (VariableName == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
CopyMem (VariableName, (UINT8 *)Ptr + sizeof (AUTH_CERT_DB_DATA), NameSize * sizeof (CHAR16));
|
|
//
|
|
// Keep VarGuid aligned
|
|
//
|
|
CopyMem (&AuthVarGuid, &Ptr->VendorGuid, sizeof (EFI_GUID));
|
|
|
|
//
|
|
// Find corresponding time auth variable
|
|
//
|
|
ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));
|
|
Status = mAuthVarLibContextIn->FindVariable (
|
|
VariableName,
|
|
&AuthVarGuid,
|
|
&AuthVariableInfo
|
|
);
|
|
|
|
if (EFI_ERROR (Status) || ((AuthVariableInfo.Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0)) {
|
|
//
|
|
// While cleaning certdb, always delete the variable in certdb regardless of it attributes.
|
|
//
|
|
Status = DeleteCertsFromDb (
|
|
VariableName,
|
|
&AuthVarGuid,
|
|
AuthVariableInfo.Attributes | EFI_VARIABLE_NON_VOLATILE
|
|
);
|
|
CertCleaned = TRUE;
|
|
DEBUG ((DEBUG_INFO, "Recovery!! Cert for Auth Variable %s Guid %g is removed for consistency\n", VariableName, &AuthVarGuid));
|
|
FreePool (VariableName);
|
|
break;
|
|
}
|
|
|
|
FreePool (VariableName);
|
|
Offset = Offset + NodeSize;
|
|
}
|
|
} while (CertCleaned);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Find hash algorithm index.
|
|
|
|
@param[in] SigData Pointer to the PKCS#7 message.
|
|
@param[in] SigDataSize Length of the PKCS#7 message.
|
|
|
|
@retval UINT8 Hash Algorithm Index.
|
|
**/
|
|
UINT8
|
|
FindHashAlgorithmIndex (
|
|
IN UINT8 *SigData,
|
|
IN UINT32 SigDataSize
|
|
)
|
|
{
|
|
UINT8 i;
|
|
|
|
for (i = 0; i < (sizeof (mHashInfo) / sizeof (EFI_HASH_INFO)); i++) {
|
|
if ( ( (SigDataSize >= (13 + mHashInfo[i].OidLength))
|
|
&& ( ((*(SigData + 1) & TWO_BYTE_ENCODE) == TWO_BYTE_ENCODE)
|
|
&& (CompareMem (SigData + 13, mHashInfo[i].OidValue, mHashInfo[i].OidLength) == 0)))
|
|
|| ( ((SigDataSize >= (32 + mHashInfo[i].OidLength)))
|
|
&& ( ((*(SigData + 20) & TWO_BYTE_ENCODE) == TWO_BYTE_ENCODE)
|
|
&& (CompareMem (SigData + 32, mHashInfo[i].OidValue, mHashInfo[i].OidLength) == 0))))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set
|
|
|
|
Caution: This function may receive untrusted input.
|
|
This function may be invoked in SMM mode, and datasize and data are external input.
|
|
This function will do basic validation, before parse the data.
|
|
This function will parse the authentication carefully to avoid security issues, like
|
|
buffer overflow, integer overflow.
|
|
|
|
@param[in] VariableName Name of Variable to be found.
|
|
@param[in] VendorGuid Variable vendor GUID.
|
|
@param[in] Data Data pointer.
|
|
@param[in] DataSize Size of Data found. If size is less than the
|
|
data, this value contains the required size.
|
|
@param[in] Attributes Attribute value of the variable.
|
|
@param[in] AuthVarType Verify against PK, KEK database, private database or certificate in data payload.
|
|
@param[in] OrgTimeStamp Pointer to original time stamp,
|
|
original variable is not found if NULL.
|
|
@param[out] VarPayloadPtr Pointer to variable payload address.
|
|
@param[out] VarPayloadSize Pointer to variable payload size.
|
|
|
|
@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_OUT_OF_RESOURCES Failed to process variable due to lack
|
|
of resources.
|
|
@retval EFI_SUCCESS Variable pass validation successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
VerifyTimeBasedPayload (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN VOID *Data,
|
|
IN UINTN DataSize,
|
|
IN UINT32 Attributes,
|
|
IN AUTHVAR_TYPE AuthVarType,
|
|
IN EFI_TIME *OrgTimeStamp,
|
|
OUT UINT8 **VarPayloadPtr,
|
|
OUT UINTN *VarPayloadSize
|
|
)
|
|
{
|
|
EFI_VARIABLE_AUTHENTICATION_2 *CertData;
|
|
UINT8 *SigData;
|
|
UINT32 SigDataSize;
|
|
UINT8 *PayloadPtr;
|
|
UINTN PayloadSize;
|
|
UINT32 Attr;
|
|
BOOLEAN VerifyStatus;
|
|
EFI_STATUS Status;
|
|
EFI_SIGNATURE_LIST *CertList;
|
|
EFI_SIGNATURE_DATA *Cert;
|
|
UINTN Index;
|
|
UINTN CertCount;
|
|
UINT32 KekDataSize;
|
|
UINT8 *NewData;
|
|
UINTN NewDataSize;
|
|
UINT8 *Buffer;
|
|
UINTN Length;
|
|
UINT8 *TopLevelCert;
|
|
UINTN TopLevelCertSize;
|
|
UINT8 *TrustedCert;
|
|
UINTN TrustedCertSize;
|
|
UINT8 *SignerCerts;
|
|
UINTN CertStackSize;
|
|
UINT8 *CertsInCertDb;
|
|
UINT32 CertsSizeinDb;
|
|
UINT8 ShaDigest[SHA_DIGEST_SIZE_MAX];
|
|
EFI_CERT_DATA *CertDataPtr;
|
|
UINT8 HashAlgId;
|
|
|
|
//
|
|
// 1. TopLevelCert is the top-level issuer certificate in signature Signer Cert Chain
|
|
// 2. TrustedCert is the certificate which firmware trusts. It could be saved in protected
|
|
// storage or PK payload on PK init
|
|
//
|
|
VerifyStatus = FALSE;
|
|
CertData = NULL;
|
|
NewData = NULL;
|
|
Attr = Attributes;
|
|
SignerCerts = NULL;
|
|
TopLevelCert = NULL;
|
|
CertsInCertDb = NULL;
|
|
CertDataPtr = NULL;
|
|
|
|
//
|
|
// When the attribute EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS is
|
|
// set, then the Data buffer shall begin with an instance of a complete (and serialized)
|
|
// EFI_VARIABLE_AUTHENTICATION_2 descriptor. The descriptor shall be followed by the new
|
|
// variable value and DataSize shall reflect the combined size of the descriptor and the new
|
|
// variable value. The authentication descriptor is not part of the variable data and is not
|
|
// returned by subsequent calls to GetVariable().
|
|
//
|
|
CertData = (EFI_VARIABLE_AUTHENTICATION_2 *)Data;
|
|
|
|
//
|
|
// Verify that Pad1, Nanosecond, TimeZone, Daylight and Pad2 components of the
|
|
// TimeStamp value are set to zero.
|
|
//
|
|
if ((CertData->TimeStamp.Pad1 != 0) ||
|
|
(CertData->TimeStamp.Nanosecond != 0) ||
|
|
(CertData->TimeStamp.TimeZone != 0) ||
|
|
(CertData->TimeStamp.Daylight != 0) ||
|
|
(CertData->TimeStamp.Pad2 != 0))
|
|
{
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
if ((OrgTimeStamp != NULL) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {
|
|
if (AuthServiceInternalCompareTimeStamp (&CertData->TimeStamp, OrgTimeStamp)) {
|
|
//
|
|
// TimeStamp check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.
|
|
//
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// wCertificateType should be WIN_CERT_TYPE_EFI_GUID.
|
|
// Cert type should be EFI_CERT_TYPE_PKCS7_GUID.
|
|
//
|
|
if ((CertData->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) ||
|
|
!CompareGuid (&CertData->AuthInfo.CertType, &gEfiCertPkcs7Guid))
|
|
{
|
|
//
|
|
// Invalid AuthInfo type, return EFI_SECURITY_VIOLATION.
|
|
//
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
//
|
|
// Find out Pkcs7 SignedData which follows the EFI_VARIABLE_AUTHENTICATION_2 descriptor.
|
|
// AuthInfo.Hdr.dwLength is the length of the entire certificate, including the length of the header.
|
|
//
|
|
SigData = CertData->AuthInfo.CertData;
|
|
SigDataSize = CertData->AuthInfo.Hdr.dwLength - (UINT32)(OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData));
|
|
|
|
//
|
|
// SignedData.digestAlgorithms shall contain the digest algorithm used when preparing the
|
|
// signature. Only a digest algorithm of SHA-256, SHA-384 or SHA-512 is accepted.
|
|
//
|
|
// According to PKCS#7 Definition (https://www.rfc-editor.org/rfc/rfc2315):
|
|
// SignedData ::= SEQUENCE {
|
|
// version Version,
|
|
// digestAlgorithms DigestAlgorithmIdentifiers,
|
|
// contentInfo ContentInfo,
|
|
// .... }
|
|
// The DigestAlgorithmIdentifiers can be used to determine the hash algorithm
|
|
// in VARIABLE_AUTHENTICATION_2 descriptor.
|
|
// This field has the fixed offset (+13) or (+32) based on whether the DER-encoded
|
|
// ContentInfo structure is present or not, and can be calculated based on two
|
|
// bytes of length encoding.
|
|
//
|
|
// Both condition can be handled in WrapPkcs7Data() in CryptPkcs7VerifyCommon.c.
|
|
//
|
|
// See below examples:
|
|
//
|
|
// 1. Without ContentInfo
|
|
// 30 82 0c da // SEQUENCE (5 element) (3294 BYTES) -- SignedData
|
|
// 02 01 01 // INTEGER 1 -- Version
|
|
// 31 0f // SET (1 element) (15 BYTES) -- DigestAlgorithmIdentifiers
|
|
// 30 0d // SEQUENCE (2 element) (13 BYTES) -- AlgorithmIdentifier
|
|
// 06 09 // OBJECT-IDENTIFIER (9 BYTES) -- algorithm
|
|
// 60 86 48 01 65 03 04 02 01 // sha256 [2.16.840.1.101.3.4.2.1]
|
|
// 05 00 // NULL (0 BYTES) -- parameters
|
|
//
|
|
// Example from: https://uefi.org/revocationlistfile
|
|
//
|
|
// 2. With ContentInfo
|
|
// 30 82 05 90 // SEQUENCE (1424 BYTES) -- ContentInfo
|
|
// 06 09 // OBJECT-IDENTIFIER (9 BYTES) -- ContentType
|
|
// 2a 86 48 86 f7 0d 01 07 02 // signedData [1.2.840.113549.1.7.2]
|
|
// a0 82 05 81 // CONTEXT-SPECIFIC CONSTRUCTED TAG 0 (1409 BYTES) -- content
|
|
// 30 82 05 7d // SEQUENCE (1405 BYTES) -- SignedData
|
|
// 02 01 01 // INTEGER 1 -- Version
|
|
// 31 0f // SET (1 element) (15 BYTES) -- DigestAlgorithmIdentifiers
|
|
// 30 0d // SEQUENCE (13 BYTES) -- AlgorithmIdentifier
|
|
// 06 09 // OBJECT-IDENTIFIER (9 BYTES) -- algorithm
|
|
// 60 86 48 01 65 03 04 02 01 // sha256 [2.16.840.1.101.3.4.2.1]
|
|
// 05 00 // NULL (0 BYTES) -- parameters
|
|
//
|
|
// Example generated with: https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot#Manual_process
|
|
//
|
|
HashAlgId = FindHashAlgorithmIndex (SigData, SigDataSize);
|
|
if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
|
|
if (HashAlgId >= (sizeof (mHashInfo) / sizeof (EFI_HASH_INFO))) {
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find out the new data payload which follows Pkcs7 SignedData directly.
|
|
//
|
|
PayloadPtr = SigData + SigDataSize;
|
|
PayloadSize = DataSize - OFFSET_OF_AUTHINFO2_CERT_DATA - (UINTN)SigDataSize;
|
|
|
|
// If the VariablePolicy engine is disabled, allow deletion of any authenticated variables.
|
|
if ((PayloadSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && !IsVariablePolicyEnabled ()) {
|
|
VerifyStatus = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Construct a serialization buffer of the values of the VariableName, VendorGuid and Attributes
|
|
// parameters of the SetVariable() call and the TimeStamp component of the
|
|
// EFI_VARIABLE_AUTHENTICATION_2 descriptor followed by the variable's new value
|
|
// i.e. (VariableName, VendorGuid, Attributes, TimeStamp, Data)
|
|
//
|
|
NewDataSize = PayloadSize + sizeof (EFI_TIME) + sizeof (UINT32) +
|
|
sizeof (EFI_GUID) + StrSize (VariableName) - sizeof (CHAR16);
|
|
|
|
//
|
|
// Here is to reuse scratch data area(at the end of volatile variable store)
|
|
// to reduce SMRAM consumption for SMM variable driver.
|
|
// The scratch buffer is enough to hold the serialized data and safe to use,
|
|
// because it is only used at here to do verification temporarily first
|
|
// and then used in UpdateVariable() for a time based auth variable set.
|
|
//
|
|
Status = mAuthVarLibContextIn->GetScratchBuffer (&NewDataSize, (VOID **)&NewData);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Buffer = NewData;
|
|
Length = StrLen (VariableName) * sizeof (CHAR16);
|
|
CopyMem (Buffer, VariableName, Length);
|
|
Buffer += Length;
|
|
|
|
Length = sizeof (EFI_GUID);
|
|
CopyMem (Buffer, VendorGuid, Length);
|
|
Buffer += Length;
|
|
|
|
Length = sizeof (UINT32);
|
|
CopyMem (Buffer, &Attr, Length);
|
|
Buffer += Length;
|
|
|
|
Length = sizeof (EFI_TIME);
|
|
CopyMem (Buffer, &CertData->TimeStamp, Length);
|
|
Buffer += Length;
|
|
|
|
CopyMem (Buffer, PayloadPtr, PayloadSize);
|
|
|
|
if (AuthVarType == AuthVarTypePk) {
|
|
//
|
|
// Verify that the signature has been made with the current Platform Key (no chaining for PK).
|
|
// First, get signer's certificates from SignedData.
|
|
//
|
|
VerifyStatus = Pkcs7GetSigners (
|
|
SigData,
|
|
SigDataSize,
|
|
&SignerCerts,
|
|
&CertStackSize,
|
|
&TopLevelCert,
|
|
&TopLevelCertSize
|
|
);
|
|
if (!VerifyStatus) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Second, get the current platform key from variable. Check whether it's identical with signer's certificates
|
|
// in SignedData. If not, return error immediately.
|
|
//
|
|
Status = AuthServiceInternalFindVariable (
|
|
EFI_PLATFORM_KEY_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
&Data,
|
|
&DataSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
VerifyStatus = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
CertList = (EFI_SIGNATURE_LIST *)Data;
|
|
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
|
|
if ((TopLevelCertSize != (CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1))) ||
|
|
(CompareMem (Cert->SignatureData, TopLevelCert, TopLevelCertSize) != 0))
|
|
{
|
|
VerifyStatus = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Verify Pkcs7 SignedData via Pkcs7Verify library.
|
|
//
|
|
VerifyStatus = Pkcs7Verify (
|
|
SigData,
|
|
SigDataSize,
|
|
TopLevelCert,
|
|
TopLevelCertSize,
|
|
NewData,
|
|
NewDataSize
|
|
);
|
|
} else if (AuthVarType == AuthVarTypeKek) {
|
|
//
|
|
// Get KEK database from variable.
|
|
//
|
|
Status = AuthServiceInternalFindVariable (
|
|
EFI_KEY_EXCHANGE_KEY_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
&Data,
|
|
&DataSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Ready to verify Pkcs7 SignedData. Go through KEK Signature Database to find out X.509 CertList.
|
|
//
|
|
KekDataSize = (UINT32)DataSize;
|
|
CertList = (EFI_SIGNATURE_LIST *)Data;
|
|
while ((KekDataSize > 0) && (KekDataSize >= CertList->SignatureListSize)) {
|
|
if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
|
|
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
|
|
CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
|
|
for (Index = 0; Index < CertCount; Index++) {
|
|
//
|
|
// Iterate each Signature Data Node within this CertList for a verify
|
|
//
|
|
TrustedCert = Cert->SignatureData;
|
|
TrustedCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1);
|
|
|
|
//
|
|
// Verify Pkcs7 SignedData via Pkcs7Verify library.
|
|
//
|
|
VerifyStatus = Pkcs7Verify (
|
|
SigData,
|
|
SigDataSize,
|
|
TrustedCert,
|
|
TrustedCertSize,
|
|
NewData,
|
|
NewDataSize
|
|
);
|
|
if (VerifyStatus) {
|
|
goto Exit;
|
|
}
|
|
|
|
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)Cert + CertList->SignatureSize);
|
|
}
|
|
}
|
|
|
|
KekDataSize -= CertList->SignatureListSize;
|
|
CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize);
|
|
}
|
|
} else if (AuthVarType == AuthVarTypePriv) {
|
|
//
|
|
// Process common authenticated variable except PK/KEK/DB/DBX/DBT.
|
|
// Get signer's certificates from SignedData.
|
|
//
|
|
VerifyStatus = Pkcs7GetSigners (
|
|
SigData,
|
|
SigDataSize,
|
|
&SignerCerts,
|
|
&CertStackSize,
|
|
&TopLevelCert,
|
|
&TopLevelCertSize
|
|
);
|
|
if (!VerifyStatus) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Get previously stored signer's certificates from certdb or certdbv for existing
|
|
// variable. Check whether they are identical with signer's certificates
|
|
// in SignedData. If not, return error immediately.
|
|
//
|
|
if (OrgTimeStamp != NULL) {
|
|
VerifyStatus = FALSE;
|
|
|
|
Status = GetCertsFromDb (VariableName, VendorGuid, Attributes, &CertsInCertDb, &CertsSizeinDb);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if ((HashAlgId < (sizeof (mHashInfo) / sizeof (EFI_HASH_INFO))) && (CertsSizeinDb == mHashInfo[HashAlgId].HashSize)) {
|
|
//
|
|
// Check hash of signer cert CommonName + Top-level issuer tbsCertificate against data in CertDb
|
|
//
|
|
CertDataPtr = (EFI_CERT_DATA *)(SignerCerts + 1);
|
|
Status = CalculatePrivAuthVarSignChainSHADigest (
|
|
HashAlgId,
|
|
CertDataPtr->CertDataBuffer,
|
|
ReadUnaligned32 ((UINT32 *)&(CertDataPtr->CertDataLength)),
|
|
TopLevelCert,
|
|
TopLevelCertSize,
|
|
ShaDigest
|
|
);
|
|
if (EFI_ERROR (Status) || (CompareMem (ShaDigest, CertsInCertDb, CertsSizeinDb) != 0)) {
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
//
|
|
// Keep backward compatible with previous solution which saves whole signer certs stack in CertDb
|
|
//
|
|
if ((CertStackSize != CertsSizeinDb) ||
|
|
(CompareMem (SignerCerts, CertsInCertDb, CertsSizeinDb) != 0))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
VerifyStatus = Pkcs7Verify (
|
|
SigData,
|
|
SigDataSize,
|
|
TopLevelCert,
|
|
TopLevelCertSize,
|
|
NewData,
|
|
NewDataSize
|
|
);
|
|
if (!VerifyStatus) {
|
|
goto Exit;
|
|
}
|
|
|
|
if ((OrgTimeStamp == NULL) && (PayloadSize != 0)) {
|
|
//
|
|
// When adding a new common authenticated variable, always save Hash of cn of signer cert + tbsCertificate of Top-level issuer
|
|
//
|
|
CertDataPtr = (EFI_CERT_DATA *)(SignerCerts + 1);
|
|
Status = InsertCertsToDb (
|
|
HashAlgId,
|
|
VariableName,
|
|
VendorGuid,
|
|
Attributes,
|
|
CertDataPtr->CertDataBuffer,
|
|
ReadUnaligned32 ((UINT32 *)&(CertDataPtr->CertDataLength)),
|
|
TopLevelCert,
|
|
TopLevelCertSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
VerifyStatus = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
} else if (AuthVarType == AuthVarTypePayload) {
|
|
CertList = (EFI_SIGNATURE_LIST *)PayloadPtr;
|
|
Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
|
|
TrustedCert = Cert->SignatureData;
|
|
TrustedCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1);
|
|
//
|
|
// Verify Pkcs7 SignedData via Pkcs7Verify library.
|
|
//
|
|
VerifyStatus = Pkcs7Verify (
|
|
SigData,
|
|
SigDataSize,
|
|
TrustedCert,
|
|
TrustedCertSize,
|
|
NewData,
|
|
NewDataSize
|
|
);
|
|
} else {
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if ((AuthVarType == AuthVarTypePk) || (AuthVarType == AuthVarTypePriv)) {
|
|
if (TopLevelCert != NULL) {
|
|
Pkcs7FreeSigners (TopLevelCert);
|
|
}
|
|
|
|
if (SignerCerts != NULL) {
|
|
Pkcs7FreeSigners (SignerCerts);
|
|
}
|
|
}
|
|
|
|
if (!VerifyStatus) {
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
Status = CheckSignatureListFormat (VariableName, VendorGuid, PayloadPtr, PayloadSize);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
*VarPayloadPtr = PayloadPtr;
|
|
*VarPayloadSize = PayloadSize;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set
|
|
|
|
Caution: This function may receive untrusted input.
|
|
This function may be invoked in SMM mode, and datasize and data are external input.
|
|
This function will do basic validation, before parse the data.
|
|
This function will parse the authentication carefully to avoid security issues, like
|
|
buffer overflow, integer overflow.
|
|
|
|
@param[in] VariableName Name of Variable to be found.
|
|
@param[in] VendorGuid Variable vendor GUID.
|
|
@param[in] Data Data pointer.
|
|
@param[in] DataSize Size of Data found. If size is less than the
|
|
data, this value contains the required size.
|
|
@param[in] Attributes Attribute value of the variable.
|
|
@param[in] AuthVarType Verify against PK, KEK database, private database or certificate in data payload.
|
|
@param[out] VarDel Delete the variable or not.
|
|
|
|
@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_OUT_OF_RESOURCES Failed to process variable due to lack
|
|
of resources.
|
|
@retval EFI_SUCCESS Variable pass validation successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
VerifyTimeBasedPayloadAndUpdate (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN VOID *Data,
|
|
IN UINTN DataSize,
|
|
IN UINT32 Attributes,
|
|
IN AUTHVAR_TYPE AuthVarType,
|
|
OUT BOOLEAN *VarDel
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS FindStatus;
|
|
UINT8 *PayloadPtr;
|
|
UINTN PayloadSize;
|
|
EFI_VARIABLE_AUTHENTICATION_2 *CertData;
|
|
AUTH_VARIABLE_INFO OrgVariableInfo;
|
|
BOOLEAN IsDel;
|
|
|
|
ZeroMem (&OrgVariableInfo, sizeof (OrgVariableInfo));
|
|
FindStatus = mAuthVarLibContextIn->FindVariable (
|
|
VariableName,
|
|
VendorGuid,
|
|
&OrgVariableInfo
|
|
);
|
|
|
|
Status = VerifyTimeBasedPayload (
|
|
VariableName,
|
|
VendorGuid,
|
|
Data,
|
|
DataSize,
|
|
Attributes,
|
|
AuthVarType,
|
|
(!EFI_ERROR (FindStatus)) ? OrgVariableInfo.TimeStamp : NULL,
|
|
&PayloadPtr,
|
|
&PayloadSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if ( !EFI_ERROR (FindStatus)
|
|
&& (PayloadSize == 0)
|
|
&& ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0))
|
|
{
|
|
IsDel = TRUE;
|
|
} else {
|
|
IsDel = FALSE;
|
|
}
|
|
|
|
CertData = (EFI_VARIABLE_AUTHENTICATION_2 *)Data;
|
|
|
|
//
|
|
// Final step: Update/Append Variable if it pass Pkcs7Verify
|
|
//
|
|
Status = AuthServiceInternalUpdateVariableWithTimeStamp (
|
|
VariableName,
|
|
VendorGuid,
|
|
PayloadPtr,
|
|
PayloadSize,
|
|
Attributes,
|
|
&CertData->TimeStamp
|
|
);
|
|
|
|
//
|
|
// Delete signer's certificates when delete the common authenticated variable.
|
|
//
|
|
if (IsDel && (AuthVarType == AuthVarTypePriv) && !EFI_ERROR (Status)) {
|
|
Status = DeleteCertsFromDb (VariableName, VendorGuid, Attributes);
|
|
}
|
|
|
|
if (VarDel != NULL) {
|
|
if (IsDel && !EFI_ERROR (Status)) {
|
|
*VarDel = TRUE;
|
|
} else {
|
|
*VarDel = FALSE;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|