alder_lake_bios/Insyde/InsydeModulePkg/Universal/Variable/VariableRuntimeDxe/AuthService.c

2782 lines
94 KiB
C

/** @file
Provide authentication services for the authenticated variable service
Caution: This module requires additional review when modified.
This driver will have external input - variable data and communicate buffer
in SMM mode. This external input must be validated carefully to avoid
security issues such as buffer overflow or integer overflow.
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.
;******************************************************************************
;* Copyright (c) 2012 - 2019, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include "Variable.h"
#include "AuthService.h"
#include "InsydeSecureVariable.h"
#include "VariableCache.h"
#include "VarCheck.h"
#include <KernelSetupConfig.h>
//
// Global database array for scratch
//
UINT32 mPlatformMode;
UINT32 mPlatformBootMode;
CRYPTO_SERVICES_PROTOCOL *mCryptoService = NULL;
EFI_HASH_PROTOCOL *mHash = NULL;
VOID *mSha256Hash;
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 };
SIGNATURE_SUPPORT_INFO mSupportSigItem[] = {
{EFI_CERT_X509_GUID, 0, FALSE, ALL_SIGNATURE_SUPPORT},
{EFI_CERT_X509_SHA256_GUID, sizeof (EFI_CERT_X509_SHA256) + sizeof (EFI_GUID), TRUE, FORBIDDEN_SIGNATURE_SUPPORT },
{EFI_CERT_X509_SHA384_GUID, sizeof (EFI_CERT_X509_SHA384) + sizeof (EFI_GUID), TRUE, FORBIDDEN_SIGNATURE_SUPPORT },
{EFI_CERT_X509_SHA512_GUID, sizeof (EFI_CERT_X509_SHA512) + sizeof (EFI_GUID), TRUE, FORBIDDEN_SIGNATURE_SUPPORT },
{EFI_CERT_SHA1_GUID, SHA1_DIGEST_SIZE + sizeof (EFI_GUID), TRUE, (AUTHORIZED_SIGNATURE_SUPPORT | FORBIDDEN_SIGNATURE_SUPPORT) },
{EFI_CERT_SHA224_GUID, 28 + sizeof (EFI_GUID), TRUE, (AUTHORIZED_SIGNATURE_SUPPORT | FORBIDDEN_SIGNATURE_SUPPORT) },
{EFI_CERT_SHA256_GUID, SHA256_DIGEST_SIZE + sizeof (EFI_GUID), TRUE, (AUTHORIZED_SIGNATURE_SUPPORT | FORBIDDEN_SIGNATURE_SUPPORT) },
{EFI_CERT_SHA384_GUID, 48 + sizeof (EFI_GUID), TRUE, (AUTHORIZED_SIGNATURE_SUPPORT | FORBIDDEN_SIGNATURE_SUPPORT) },
{EFI_CERT_SHA512_GUID, 64 + sizeof (EFI_GUID), TRUE, (AUTHORIZED_SIGNATURE_SUPPORT | FORBIDDEN_SIGNATURE_SUPPORT) }
};
/**
Internal function to delete a Variable given its name and GUID, no authentication
required.
@param[in] VariableName Name of the Variable.
@param[in] VendorGuid GUID of the Variable.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
@retval EFI_SUCCESS Variable deleted successfully.
@return Others The driver failded to start the device.
**/
STATIC
EFI_STATUS
DeleteVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN VARIABLE_GLOBAL *Global
)
{
EFI_STATUS Status;
VARIABLE_POINTER_TRACK Variable;
UINTN VariableCount;
Status = FindVariableByLifetime (VariableName, VendorGuid, &Variable, &VariableCount, Global);
if (EFI_ERROR (Status)) {
return EFI_SUCCESS;
}
ASSERT (Variable.CurrPtr != NULL);
return UpdateVariable (VariableName, VendorGuid, NULL, 0, 0, 0, 0, &Variable, NULL, Global);
}
/**
Convert all of module authenticated service relative pointers to virtual address.
**/
VOID
AuthVariableClassAddressChange (
VOID
)
{
gRT->ConvertPointer (0x0, (VOID **) &mCertDbList);
gRT->ConvertPointer (0x0, (VOID **) &mStorageArea);
gRT->ConvertPointer (0x0, (VOID **) &mSha256Hash);
gRT->ConvertPointer (0x0, (VOID **) &mCryptoService);
gRT->ConvertPointer (0x0, (VOID **) &mHash);
}
/**
This function uses to clear all of secure settings. These variable
includes PK, KEK, db, dbx.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
@retval EFI_SUCCESS Clear secure settings successful.
--*/
EFI_STATUS
ClearSecureSettings (
IN VARIABLE_GLOBAL *Global
)
{
UINTN VariableCount;
VARIABLE_POINTER_TRACK Variable;
UINT32 VarAttr;
EFI_STATUS Status;
VariableCount = 0;
FindVariableByLifetime (
EFI_SETUP_MODE_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
Global
);
mPlatformMode = SETUP_MODE;
if (Variable.CurrPtr != NULL) {
VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;
Status = UpdateVariable (
EFI_SETUP_MODE_NAME,
&gEfiGlobalVariableGuid,
&mPlatformMode,
0,
VarAttr,
0,
0,
&Variable,
NULL,
Global
);
ASSERT_EFI_ERROR (Status);
}
//
// Clear PK, KEK, db and dbx
//
Status = DeleteVariable (EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid, Global);
ASSERT_EFI_ERROR (Status);
Status = DeleteVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, Global);
ASSERT_EFI_ERROR (Status);
Status = DeleteVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, Global);
ASSERT_EFI_ERROR (Status);
Status = DeleteVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, Global);
ASSERT_EFI_ERROR (Status);
Status = DeleteVariable (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid, Global);
ASSERT_EFI_ERROR (Status);
Status = DeleteVariable (EFI_IMAGE_SECURITY_DATABASE3, &gEfiImageSecurityDatabaseGuid, Global);
ASSERT_EFI_ERROR (Status);
return Status;
}
/**
This function only uses for create dummy dbx data when dbx variable doesn't exist.
@param[in, out] BufferSize On input, this is input buffer size.
On output, this is buffer size of dummy data.
@param[out] Buffer Pointer to buffer to save dummy dbx data.
@retval EFI_SUCCESS Create dummy dbx data successful.
@retval EFI_INVALID_PARAMETER BufferSize or Buffer is NULL.
@retval EFI_BUFFER_TOO_SMALL Input buffer size is too small.
**/
STATIC
EFI_STATUS
CreateDummyDbxData (
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
EFI_SIGNATURE_LIST *SignatureList;
UINT32 RequestBufferSize;
if (BufferSize == NULL || Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
RequestBufferSize = sizeof (EFI_SIGNATURE_LIST) + SHA256_DIGEST_SIZE + sizeof (EFI_GUID);
if (*BufferSize < RequestBufferSize) {
*BufferSize = RequestBufferSize;
return EFI_BUFFER_TOO_SMALL;
}
*BufferSize = RequestBufferSize;
SignatureList = Buffer;
ZeroMem (SignatureList, RequestBufferSize);
SignatureList->SignatureListSize = RequestBufferSize;
SignatureList->SignatureSize = RequestBufferSize - sizeof (EFI_SIGNATURE_LIST);
CopyMem (&SignatureList->SignatureType, &gEfiCertSha256Guid, sizeof (EFI_GUID));
return EFI_SUCCESS;
}
/**
Initializes for authenticated variable service.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
@retval EFI_SUCCESS Function successfully executed.
@retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resources.
**/
EFI_STATUS
AuthenticatedVariableServiceInitialize (
IN VARIABLE_GLOBAL *Global
)
{
EFI_STATUS Status;
VARIABLE_POINTER_TRACK Variable;
UINT32 VarAttr;
UINTN VariableCount;
EFI_TIME TimeStamp;
UINTN VariableSize;
UINT32 ListSize;
if (mSmst == NULL) {
Status = gBS->LocateProtocol (
&gCryptoServicesProtocolGuid,
NULL,
(VOID **)&mCryptoService
);
ASSERT (mCryptoService != NULL);
Status = gBS->LocateProtocol (
&gEfiHashProtocolGuid,
NULL,
(VOID **)&mHash
);
ASSERT (mHash != NULL);
} else {
Status = mSmst->SmmLocateProtocol (
&gCryptoServicesProtocolGuid,
NULL,
(VOID **)&mCryptoService
);
ASSERT (mCryptoService != NULL);
Status = mSmst->SmmLocateProtocol (
&gEfiHashProtocolGuid,
NULL,
(VOID **)&mHash
);
ASSERT (mHash != NULL);
}
SyncAuthData (Global);
mSha256Hash = VariableAllocateZeroBuffer (SHA256_DIGEST_SIZE, TRUE);
if (mSmst == NULL) {
//
// Check "SignatureSupport" variable's existence.
// If it doesn't exist, then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.
//
VariableCount = 0;
Status = FindVariableByLifetime (
EFI_SIGNATURE_SUPPORT_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
Global
);
if (Variable.CurrPtr == NULL) {
VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS;
Status = UpdateVariable (
EFI_SIGNATURE_SUPPORT_NAME,
&gEfiGlobalVariableGuid,
mSignatureSupport,
SIGSUPPORT_NUM * sizeof (EFI_GUID),
VarAttr,
0,
0,
&Variable,
NULL,
Global
);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Check "certdb" variable's existence.
// If it doesn't exist, then create a new one with
// EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set.
//
VariableCount = 0;
Status = FindVariableByLifetime (
EFI_CERT_DB_NAME,
&gEfiGenericVariableGuid,
&Variable,
&VariableCount,
Global
);
if (Variable.CurrPtr == NULL) {
VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
ListSize = 0;
ZeroMem (&TimeStamp, sizeof (TimeStamp));
Status = UpdateVariable (
EFI_CERT_DB_NAME,
&gEfiGenericVariableGuid,
&ListSize,
sizeof (UINT32),
VarAttr,
0,
0,
&Variable,
&TimeStamp,
Global
);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Check "certdbv" variable's existence.
// If it doesn't exist, then create a new one with
// EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set.
//
VariableCount = 0;
Status = FindVariableByLifetime (
EFI_CERT_DB_VOLATILE_NAME,
&gEfiGenericVariableGuid,
&Variable,
&VariableCount,
Global
);
if (Variable.CurrPtr == NULL) {
VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
ListSize = 0;
ZeroMem (&TimeStamp, sizeof (TimeStamp));
Status = UpdateVariable (
EFI_CERT_DB_VOLATILE_NAME,
&gEfiGenericVariableGuid,
&ListSize,
sizeof (UINT32),
VarAttr,
0,
0,
&Variable,
&TimeStamp,
Global
);
if (EFI_ERROR (Status)) {
return Status;
}
}
}
if (!FeaturePcdGet (PcdH2OSecureBootSupported)) {
return EFI_SUCCESS;
}
if (mSmst == NULL) {
//
// Check "dbx" variable's existence.
// If it doesn't exist, then create a new empty one with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set.
//
VariableCount = 0;
Status = FindVariableByLifetimeInAllRegions (
EFI_IMAGE_SECURITY_DATABASE1,
&gEfiImageSecurityDatabaseGuid,
&Variable,
&VariableCount,
Global
);
if (Variable.CurrPtr == NULL) {
VariableSize = APPEND_BUFF_SIZE;
Status = CreateDummyDbxData (&VariableSize, mStorageArea);
ASSERT_EFI_ERROR (Status);
VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
ZeroMem (&TimeStamp, sizeof (TimeStamp));
Status = UpdateVariable (
EFI_IMAGE_SECURITY_DATABASE1,
&gEfiImageSecurityDatabaseGuid,
mStorageArea,
VariableSize,
VarAttr,
0,
0,
&Variable,
&TimeStamp,
Global
);
}
}
Status = InitializeAdministerSecureBootVariables ();
return Status;
}
/**
According to variable name and vendor GUID to check the name and GUID in certificated data
is matched.
@param[in] VariableName Name of Variable to be found.
@param[in] VendorGuid Variable vendor GUID.
@param[in] CertDbData Pointer to AUTH_CERT_DB_DATA instance.
@retval TRUE Variable name and variable vendor GUID is matched.
@retval FALSE Variable name and variable vendor GUID isn't matched.
**/
STATIC
BOOLEAN
IsMatchedCertData (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN AUTH_CERT_DB_DATA *CertDbData
)
{
if (VariableName == NULL || VendorGuid == NULL || CertDbData == NULL) {
return FALSE;
}
if (CompareGuid (&CertDbData->VendorGuid, VendorGuid) &&
StrCmp (VariableName, (CHAR16 *) (CertDbData + 1)) == 0) {
return TRUE;
}
return FALSE;
}
/**
Calculate SHA256 digest of SignerCert CommonName + ToplevelCert tbsCertificate
SignerCert and ToplevelCert are inside the signer certificate chain.
@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] Sha256Digest Sha256 digest calculated.
@return EFI_ABORTED Digest process failed.
@return EFI_SUCCESS SHA256 Digest is succesfully calculated.
**/
EFI_STATUS
CalculatePrivAuthVarSignChainSHA256Digest (
IN UINT8 *SignerCert,
IN UINTN SignerCertSize,
IN UINT8 *TopLevelCert,
IN UINTN TopLevelCertSize,
OUT UINT8 *Sha256Digest
)
{
UINT8 *TbsCert;
UINTN TbsCertSize;
UINT8 CertCommonName[256];
UINTN CertCommonNameSize;
EFI_STATUS Status;
BOOLEAN Success;
EFI_HASH_OUTPUT Digest;
CertCommonNameSize = sizeof(CertCommonName);
//
// Get SignerCert CommonName
//
Success = mCryptoService->X509GetSubjectName (
SignerCert,
SignerCertSize,
CertCommonName,
&CertCommonNameSize
);
if (!Success) {
return EFI_ABORTED;
}
Success = mCryptoService->X509GetTBSCert (
TopLevelCert,
TopLevelCertSize,
&TbsCert,
&TbsCertSize
);
//
// Get TopLevelCert tbsCertificate
//
if (!Success) {
return EFI_ABORTED;
}
Digest.Sha256Hash = (EFI_SHA256_HASH *) Sha256Digest;
Status = mHash->Hash (
mHash,
&gEfiHashAlgorithmSha256Guid,
FALSE,
CertCommonName,
CertCommonNameSize,
(EFI_HASH_OUTPUT *) &Digest
);
if (EFI_ERROR(Status)) {
return EFI_ABORTED;
}
Status = mHash->Hash (
mHash,
&gEfiHashAlgorithmSha256Guid,
TRUE,
TbsCert,
TbsCertSize,
(EFI_HASH_OUTPUT *) &Digest
);
if (EFI_ERROR(Status)) {
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 Variable to be found.
@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] Variable Pointer to AUTH_CERT_DB_DATA instance.
@retval EFI_SUCCESS Find matching certs and output parameters.
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
@retval EFI_NOT_FOUND Fail to find matching certs.
**/
STATIC
EFI_STATUS
FindCertsFromDb (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT8 *Data,
IN UINTN DataSize,
OUT AUTH_CERT_DB_DATA **CertDbData OPTIONAL
)
{
UINT32 Offset;
AUTH_CERT_DB_DATA *WorkingCert;
UINT32 CertDbListSize;
if (VariableName == NULL || VendorGuid == NULL || Data == NULL || DataSize < sizeof (UINT32)) {
return EFI_INVALID_PARAMETER;
}
CertDbListSize = *((UINT32 *) Data);
if (CertDbListSize != (UINT32) DataSize) {
return EFI_INVALID_PARAMETER;
}
Offset = sizeof (UINT32);
//
// Get corresponding certificates by VendorGuid and VariableName.
//
while (Offset < (UINT32) CertDbListSize) {
WorkingCert = (AUTH_CERT_DB_DATA *) (Data + Offset);
if (IsMatchedCertData (VariableName, VendorGuid, WorkingCert)) {
if (CertDbData != NULL) {
*CertDbData = WorkingCert;
}
return EFI_SUCCESS;
}
Offset += WorkingCert->CertNodeSize;
}
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 Variable to be found.
@param[in] VendorGuid Variable vendor GUID.
@param[in] Attributes Attributes of authenticated variable.
@param[out] CertDbData Data pointer.
@retval EFI_SUCCESS Get signer's certificates successfully.
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
@retval EFI_NOT_FOUND Fail to find matching certs.
**/
STATIC
EFI_STATUS
GetCertsFromDb (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
OUT AUTH_CERT_DB_DATA **CertDbData
)
{
VARIABLE_POINTER_TRACK CertDbVariable;
EFI_STATUS Status;
UINTN VariableCount;
CHAR16 *DbName;
if ((VariableName == NULL) || (VendorGuid == NULL) || (CertDbData == 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".
//
Status = FindVariableByLifetime (
DbName,
&gEfiGenericVariableGuid,
&CertDbVariable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = FindCertsFromDb (
VariableName,
VendorGuid,
GetVariableDataPtr (CertDbVariable.CurrPtr),
DataSizeOfVariable (CertDbVariable.CurrPtr),
CertDbData
);
return Status;
}
/**
Get certificate from AUTH_CERT_DB_DATA instance.
@param[in] CertDbData Pointer to AUTH_CERT_DB_DATA instance.
@return NULL Cannot find signature in AUTH_CERT_DB_DATA instance.
@return Other The start address of certificated data.
**/
STATIC
VOID *
GetCertDbData (
IN AUTH_CERT_DB_DATA *CertDbData
)
{
UINT8 *CertData;
if (CertDbData == NULL) {
return NULL;
}
CertData = ((UINT8 *) (CertDbData + 1)) + CertDbData->NameSize;
return (VOID *) CertData;
}
/**
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_SUCCESS IThe operation is completed successfully.
@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.
**/
STATIC
EFI_STATUS
DeleteCertsFromDb (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes
)
{
VARIABLE_POINTER_TRACK CertDbVariable;
EFI_STATUS Status;
UINT32 VarAttr;
UINT32 CertNodeOffset;
UINT8 *NewCertDb;
UINT32 NewCertDbSize;
UINTN VariableCount;
AUTH_CERT_DB_DATA *CertDbData;
EFI_TIME TimeStamp;
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 = FindVariableByLifetime (
DbName,
&gEfiGenericVariableGuid,
&CertDbVariable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
if (EFI_ERROR (Status)) {
return Status;
}
if (DataSizeOfVariable (CertDbVariable.CurrPtr) == sizeof (UINT32)) {
//
// There is no certs in "certdb" or "certdbv".
//
return EFI_NOT_FOUND;
}
//
// Get corresponding cert node from "certdb" or "certdbv".
//
Status = FindCertsFromDb (
VariableName,
VendorGuid,
GetVariableDataPtr (CertDbVariable.CurrPtr),
DataSizeOfVariable (CertDbVariable.CurrPtr),
&CertDbData
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Construct new data content of variable "certdb" or "certdbv".
// 1.Copy the DB entries before deleting node.
// 2.Update CertDbListSize.
// 3.Copy the DB entries after deleting node.
//
NewCertDbSize = (UINT32) DataSizeOfVariable (CertDbVariable.CurrPtr) - CertDbData->CertNodeSize;
NewCertDb = mCertDbList;
CertNodeOffset = (UINT32) (UINTN) ((UINT8 *) CertDbData - GetVariableDataPtr (CertDbVariable.CurrPtr));
CopyMem (NewCertDb, GetVariableDataPtr (CertDbVariable.CurrPtr), CertNodeOffset);
CopyMem (NewCertDb, &NewCertDbSize, sizeof (UINT32));
CopyMem(
NewCertDb + CertNodeOffset,
GetVariableDataPtr (CertDbVariable.CurrPtr) + CertNodeOffset + CertDbData->CertNodeSize,
DataSizeOfVariable (CertDbVariable.CurrPtr) - CertNodeOffset - CertDbData->CertNodeSize
);
ZeroMem (&TimeStamp, sizeof (TimeStamp));
Status = UpdateVariable (
DbName,
&gEfiGenericVariableGuid,
NewCertDb,
NewCertDbSize,
VarAttr,
0,
0,
&CertDbVariable,
&TimeStamp,
&mVariableModuleGlobal->VariableBase
);
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 SHA256 digest of
SignerCert CommonName + TopLevelCert tbsCertificate.
@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_SUCCESS Insert an AUTH_CERT_DB_DATA entry to "certdb".
@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 Certificated database list is large than MAX_VARIABLE_SIZE.
**/
STATIC
EFI_STATUS
InsertCertsToDb (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINT8 *SignerCert,
IN UINTN SignerCertSize,
IN UINT8 *TopLevelCert,
IN UINTN TopLevelCertSize
)
{
VARIABLE_POINTER_TRACK CertDbVariable;
EFI_STATUS Status;
UINT32 VarAttr;
UINT8 *NewCertDb;
UINT32 NewCertDbSize;
UINT32 CertNodeSize;
UINT32 NameSize;
AUTH_CERT_DB_DATA *CurrentCertDbData;
UINTN VariableCount;
EFI_TIME TimeStamp;
CHAR16 *DbName;
UINT32 CertDataSize;
UINT8 Sha256Digest[SHA256_DIGEST_SIZE];
if ((VariableName == NULL) || (VendorGuid == NULL) || (SignerCert == NULL) || (TopLevelCert == 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;
}
//
// Get variable "certdb" or "certdbv".
//
Status = FindVariableByLifetime (
DbName,
&gEfiGenericVariableGuid,
&CertDbVariable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Find whether matching cert node already exists in "certdb" or "certdbv".
// If yes return error.
//
Status = FindCertsFromDb (
VariableName,
VendorGuid,
GetVariableDataPtr (CertDbVariable.CurrPtr),
DataSizeOfVariable (CertDbVariable.CurrPtr),
NULL
);
if (!EFI_ERROR (Status)) {
ASSERT (FALSE);
return EFI_ACCESS_DENIED;
}
//
// Construct new data content of variable "certdb" or "certdbv".
//
NameSize = (UINT32) StrSize (VariableName);
CertDataSize = sizeof(Sha256Digest);
CertNodeSize = sizeof (AUTH_CERT_DB_DATA) + (UINT32) CertDataSize + NameSize;
NewCertDbSize = (UINT32) DataSizeOfVariable (CertDbVariable.CurrPtr) + CertNodeSize;
NewCertDb = mCertDbList;
if (NewCertDbSize > MAX_VARIABLE_SIZE) {
return EFI_OUT_OF_RESOURCES;
}
Status = CalculatePrivAuthVarSignChainSHA256Digest(
SignerCert,
SignerCertSize,
TopLevelCert,
TopLevelCertSize,
Sha256Digest
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// 1.Copy the whole original DB entries .
// 2.Update CertDbListSize.
// 3.Append new certificated database at end of original DB list.
//
CopyMem (NewCertDb, GetVariableDataPtr (CertDbVariable.CurrPtr), DataSizeOfVariable (CertDbVariable.CurrPtr));
CopyMem (NewCertDb, &NewCertDbSize, sizeof (UINT32));
//
// Construct new cert node header.
//
CurrentCertDbData = (AUTH_CERT_DB_DATA *) (NewCertDb + DataSizeOfVariable (CertDbVariable.CurrPtr));
CopyMem (&CurrentCertDbData->VendorGuid, VendorGuid, sizeof (EFI_GUID));
CopyMem (&CurrentCertDbData->CertNodeSize, &CertNodeSize, sizeof (UINT32));
CopyMem (&CurrentCertDbData->NameSize, &NameSize, sizeof (UINT32));
CopyMem (&CurrentCertDbData->CertDataSize, &CertDataSize, sizeof (UINT32));
CopyMem (
CurrentCertDbData + 1,
VariableName,
NameSize
);
CopyMem (
((UINT8 *) (CurrentCertDbData + 1)) + NameSize,
Sha256Digest,
CertDataSize
);
ZeroMem (&TimeStamp, sizeof (TimeStamp));
Status = UpdateVariable (
DbName,
&gEfiGenericVariableGuid,
NewCertDb,
NewCertDbSize,
VarAttr,
0,
0,
&CertDbVariable,
&TimeStamp,
&mVariableModuleGlobal->VariableBase
);
return Status;
}
/**
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] Variable The variable information which is used to keep track of variable usage.
@param[in] Attributes Attribute value of the variable.
@param[in] AuthVarType Verify against PK or KEK database or private database.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
@param[out] VarDel Delete the variable or not.
@retval EFI_SUCCESS Variable pass validation successfully.
@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.
**/
STATIC
EFI_STATUS
WriteTimeBasedPayload (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN VOID *Data,
IN UINTN DataSize,
IN VARIABLE_POINTER_TRACK *Variable,
IN UINT32 Attributes,
IN VARIABLE_GLOBAL *Global,
IN AUTHVAR_TYPE AuthVarType,
OUT BOOLEAN *VarDel
)
{
UINT8 *TopLevelCert;
UINTN TopLevelCertSize;
UINT8 *SigData;
UINT8 *PayLoadPtr;
UINTN Index;
UINTN CertCount;
UINTN PayloadSize;
UINT32 Attr;
UINT32 SigDataSize;
UINT32 SigListDataSize;
BOOLEAN VerifyStatus;
EFI_STATUS Status;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
VARIABLE_POINTER_TRACK PkVariable;
VARIABLE_POINTER_TRACK SigListData;
EFI_VARIABLE_AUTHENTICATION_2 *CertData;
UINT8 *NewData;
UINTN NewDataSize;
UINTN VariableCount;
UINT8 *SignerCerts;
UINTN CertStackSize;
AUTH_CERT_DB_DATA *CertDbData;
UINT8 Sha256Digest[SHA256_DIGEST_SIZE];
EFI_CERT_DATA *CertDataPtr;
if (mCryptoService == NULL) {
return EFI_UNSUPPORTED;
}
VerifyStatus = FALSE;
CertData = NULL;
NewData = NULL;
Attr = Attributes;
TopLevelCert = NULL;
SignerCerts = 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_INVALID_PARAMETER;
}
if ((Variable->CurrPtr != NULL) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {
if (CompareTimeStamp (&CertData->TimeStamp, &Variable->CurrPtr->TimeStamp)) {
//
// 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 = (UINT8*) ((UINTN)Data + (UINTN)(((EFI_VARIABLE_AUTHENTICATION_2 *) 0)->AuthInfo.CertData));
SigDataSize = CertData->AuthInfo.Hdr.dwLength - (UINT32)(UINTN)(((WIN_CERTIFICATE_UEFI_GUID *) 0)->CertData);
//
// Find out the new data payload which follows Pkcs7 SignedData directly.
//
PayLoadPtr = (UINT8*) ((UINTN) SigData + (UINTN) SigDataSize);
PayloadSize = DataSize - (UINTN)(((EFI_VARIABLE_AUTHENTICATION_2 *) 0)->AuthInfo.CertData) - (UINTN) SigDataSize;
//
// Construct a buffer to fill with (VariableName, VendorGuid, Attributes, TimeStamp, Data).
//
NewDataSize = PayloadSize + sizeof (EFI_TIME) + sizeof (UINT32) +
sizeof (EFI_GUID) + StrSize (VariableName) - sizeof (CHAR16);
NewData = (UINT8 *) mStorageArea;
if (NewData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (NewData, VariableName, StrSize (VariableName) - sizeof (CHAR16));
CopyMem (NewData + StrSize (VariableName) - sizeof (CHAR16), VendorGuid, sizeof (EFI_GUID));
CopyMem (
NewData + StrSize (VariableName) - sizeof (CHAR16) + sizeof (EFI_GUID),
&Attr,
sizeof (UINT32)
);
CopyMem (
NewData + StrSize (VariableName) - sizeof (CHAR16) + sizeof (EFI_GUID) + sizeof (UINT32),
&CertData->TimeStamp,
sizeof (EFI_TIME)
);
CopyMem (NewData + (NewDataSize - PayloadSize), PayLoadPtr, PayloadSize);
if (AuthVarType == AuthVarTypePk) {
//
// Get platform key from variable.
//
VariableCount = 0;
Status = FindVariableByLifetimeInAllRegions (
EFI_PLATFORM_KEY_NAME,
&gEfiGlobalVariableGuid,
&PkVariable,
&VariableCount,
Global
);
if (EFI_ERROR (Status)) {
return Status;
}
CertList = (EFI_SIGNATURE_LIST *) GetVariableDataPtr (PkVariable.CurrPtr);
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
TopLevelCert = Cert->SignatureData;
TopLevelCertSize = CertList->SignatureSize - sizeof (EFI_GUID);
if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
//
// Verify Pkcs7 SignedData via Pkcs7Verify library.
// NOTE THAT: Since we have no Pkcs7 SignedData Parser, there is no way to know what exactly
// digestAlgorithms caller uses, here suppose SHA1 is used by caller for hash.
//
VerifyStatus = mCryptoService->Pkcs7Verify (
SigData,
SigDataSize,
TopLevelCert,
TopLevelCertSize,
NewData,
NewDataSize,
FALSE // no need to do authenticode workaround
);
if (VerifyStatus) {
goto Exit;
}
}
} else if (AuthVarType == AuthVarTypeKek || AuthVarType == AuthVarTypeDbr){
//
// Get database from variable.
//
VariableCount = 0;
Status = FindVariableByLifetimeInAllRegions (
AuthVarType == AuthVarTypeKek ? EFI_KEY_EXCHANGE_KEY_NAME : EFI_IMAGE_SECURITY_DATABASE3,
AuthVarType == AuthVarTypeKek ? &gEfiGlobalVariableGuid : &gEfiImageSecurityDatabaseGuid,
&SigListData,
&VariableCount,
Global
);
if (EFI_ERROR (Status)){
return Status;
}
//
// Ready to verify Pkcs7 SignedData. Go through KEK Signature Database to find out X.509 CertList.
//
SigListDataSize = SigListData.CurrPtr->DataSize;
CertList = (EFI_SIGNATURE_LIST *) GetVariableDataPtr (SigListData.CurrPtr);
while ((SigListDataSize > 0) && (SigListDataSize >= CertList->SignatureListSize)) {
if (CertList->SignatureListSize < sizeof (EFI_SIGNATURE_LIST)) {
return EFI_INVALID_PARAMETER;
}
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
//
TopLevelCert = Cert->SignatureData;
//
// SignatureSize includes the owner GUID
//
TopLevelCertSize = CertList->SignatureSize - sizeof(EFI_GUID);
//
// Verify Pkcs7 SignedData via Pkcs7Verify library.
// NOTE THAT: Since we have no Pkcs7 SignedData Parser, there is no way to know what exactly
// digestAlgorithms caller uses, here suppose SHA1 is used by caller for hash.
//
VerifyStatus = mCryptoService->Pkcs7Verify (
SigData,
SigDataSize,
TopLevelCert,
TopLevelCertSize,
NewData,
NewDataSize,
FALSE // no need for authenticode workaround
);
if (VerifyStatus) {
goto Exit;
}
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
}
}
SigListDataSize -= 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 = mCryptoService->Pkcs7GetSigners (
SigData,
SigDataSize,
&SignerCerts,
&CertStackSize,
&TopLevelCert,
&TopLevelCertSize
);
if (!VerifyStatus) {
goto Exit;
}
//
// Get previously stored signer's certificates from certdb for existing
// variable. Check whether they are identical with signer's certificates
// in SignedData. If not, return error immediately.
//
if ((Variable->CurrPtr != NULL)) {
VerifyStatus = FALSE;
Status = GetCertsFromDb (VariableName, VendorGuid, Attributes, &CertDbData);
if (EFI_ERROR (Status)) {
goto Exit;
}
if (CertDbData->CertDataSize == SHA256_DIGEST_SIZE) {
//
// Check hash of signer cert CommonName + Top-level issuer tbsCertificate against data in CertDb
//
CertDataPtr = (EFI_CERT_DATA *)(SignerCerts + 1);
Status = CalculatePrivAuthVarSignChainSHA256Digest(
CertDataPtr->CertDataBuffer,
ReadUnaligned32 ((UINT32 *)&(CertDataPtr->CertDataLength)),
TopLevelCert,
TopLevelCertSize,
Sha256Digest
);
if (EFI_ERROR(Status) || CompareMem (Sha256Digest, GetCertDbData (CertDbData), CertDbData->CertDataSize) != 0){
goto Exit;
}
} else {
if ((CertStackSize != CertDbData->CertDataSize) ||
(CompareMem (SignerCerts, GetCertDbData (CertDbData), CertDbData->CertDataSize) != 0)) {
goto Exit;
}
}
}
VerifyStatus = mCryptoService->Pkcs7Verify (
SigData,
SigDataSize,
TopLevelCert,
TopLevelCertSize,
NewData,
NewDataSize,
FALSE
);
if (!VerifyStatus) {
goto Exit;
}
//
// Delete signer's certificates when delete the common authenticated variable.
//
if ((PayloadSize == 0) && (Variable->CurrPtr != NULL) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {
Status = DeleteCertsFromDb (VariableName, VendorGuid, Attributes);
if (EFI_ERROR (Status)) {
VerifyStatus = FALSE;
goto Exit;
}
} else if (Variable->CurrPtr == 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 (
VariableName,
VendorGuid,
Attributes,
CertDataPtr->CertDataBuffer,
ReadUnaligned32 ((UINT32 *)&(CertDataPtr->CertDataLength)),
TopLevelCert,
TopLevelCertSize
);
if (EFI_ERROR (Status)) {
VerifyStatus = FALSE;
goto Exit;
}
}
} else {
return EFI_SECURITY_VIOLATION;
}
Exit:
if (AuthVarType == AuthVarTypePriv) {
mCryptoService->Pkcs7FreeSigners (TopLevelCert);
mCryptoService->Pkcs7FreeSigners (SignerCerts);
}
if (!VerifyStatus) {
return EFI_SECURITY_VIOLATION;
}
if ((PayloadSize == 0) && (VarDel != NULL)) {
*VarDel = TRUE;
}
//
// Final step: Update/Append Variable if it pass Pkcs7Verify
//
return UpdateVariable (
VariableName,
VendorGuid,
PayLoadPtr,
PayloadSize,
Attributes,
0,
0,
Variable,
&CertData->TimeStamp,
Global
);
}
/**
Update platform mode.
@param[in] Mode SETUP_MODE or USER_MODE.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
**/
VOID
UpdatePlatformMode (
IN UINT32 Mode,
IN VARIABLE_GLOBAL *Global
)
{
EFI_STATUS Status;
VARIABLE_POINTER_TRACK Variable;
UINT32 VarAttr;
UINTN VariableCount;
VariableCount = 0;
Status = FindVariableByLifetime (
EFI_SETUP_MODE_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
Global
);
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 (
EFI_SETUP_MODE_NAME,
&gEfiGlobalVariableGuid,
&mPlatformMode,
sizeof(UINT8),
VarAttr,
0,
0,
&Variable,
NULL,
Global
);
ASSERT_EFI_ERROR (Status);
}
/**
Function to get current DeployedMode variable value.
If this value doesn't exist, this function will return 0.
@return current DeployedMode value
**/
UINT8
DeployedModeValue (
VOID
)
{
VARIABLE_POINTER_TRACK Variable;
EFI_STATUS Status;
UINTN VariableCount;
Status = FindVariableByLifetime (
EFI_DEPLOYED_MODE_VARIABLE_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
if (Status != EFI_SUCCESS) {
return 0;
}
return *(GetVariableDataPtr (Variable.CurrPtr));
}
/**
Function to get current AuditMode variable value.
If this value doesn't exist, this function will return 0.
@return current AuditMode value
**/
UINT8
AuditModeValue (
VOID
)
{
VARIABLE_POINTER_TRACK Variable;
EFI_STATUS Status;
UINTN VariableCount;
Status = FindVariableByLifetime (
EFI_AUDIT_MODE_VARIABLE_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
if (Status != EFI_SUCCESS) {
return 0;
}
return *(GetVariableDataPtr (Variable.CurrPtr));
}
/**
Internal function to update AuditMode variable value.
@param[in] Input value to update AuditMode variable.
@retval EFI_SUCCESS Update AuditMode Variable Successfully.
@retval EFI_INVALID_PARAMETER Input Value to update AuditMode is incorrect.
@return Other Ohter errors occurred while updating AuditMode variable.
**/
EFI_STATUS
UpdateAuditModeValue (
IN CONST UINT8 Value
)
{
VARIABLE_POINTER_TRACK Variable;
UINTN VariableCount;
if (Value != 0 && Value != 1) {
return EFI_INVALID_PARAMETER;
}
FindVariableByLifetime (
EFI_AUDIT_MODE_VARIABLE_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
return UpdateVariable (
EFI_AUDIT_MODE_VARIABLE_NAME,
&gEfiGlobalVariableGuid,
&Value,
sizeof (Value),
VARIABLE_ATTRIBUTE_NV_BS_RT,
0,
0,
&Variable,
NULL,
&mVariableModuleGlobal->VariableBase
);
}
/**
Internal function to update DeployedMode variable value.
@param[in] Input value to update DeployedMode variable.
@retval EFI_SUCCESS Update DeployedMode Variable Successfully.
@retval EFI_INVALID_PARAMETER Input Value to update DeployedMode is incorrect.
@return Other Ohter errors occurred while updating DeployedMode variable.
**/
EFI_STATUS
UpdateDeployedModeValue (
IN CONST UINT8 Value
)
{
VARIABLE_POINTER_TRACK Variable;
UINTN VariableCount;
if (Value != 0 && Value != 1) {
return EFI_INVALID_PARAMETER;
}
FindVariableByLifetime (
EFI_DEPLOYED_MODE_VARIABLE_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
return UpdateVariable (
EFI_DEPLOYED_MODE_VARIABLE_NAME,
&gEfiGlobalVariableGuid,
&Value,
sizeof (Value),
VARIABLE_ATTRIBUTE_NV_BS_RT,
0,
0,
&Variable,
NULL,
&mVariableModuleGlobal->VariableBase
);
}
/**
Function to initialize all of secure boot related mode variables.
**/
VOID
InitializeSecureBootModesValue (
VOID
)
{
UINTN VariableCount;
VARIABLE_POINTER_TRACK Variable;
UINT8 AuditMode;
UINT8 DeployedMode;
UINT32 VarAttr;
EFI_STATUS Status;
if (!FeaturePcdGet (PcdH2OSecureBootSupported)) {
return;
}
SyncAuthData (&mVariableModuleGlobal->VariableBase);
//
// Check PK database's existence to determine the SetupMode value.
// Then create a new "SetupMode" with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.
//
VariableCount = 0;
Status = FindVariableByLifetime (
EFI_SETUP_MODE_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
VarAttr = VARIABLE_ATTRIBUTE_NV_BS_RT_AW;
Status = UpdateVariable (
EFI_SETUP_MODE_NAME,
&gEfiGlobalVariableGuid,
&mPlatformMode,
sizeof(UINT8),
VarAttr,
0,
0,
&Variable,
NULL,
&mVariableModuleGlobal->VariableBase
);
ASSERT (Status == EFI_SUCCESS);
//
// Check "SecureBoot" variable existence. This variable must match the platform booting mode.
// With current code, the setting depends only on the presence of the PK variable.
// NOTE: if there is any setup option to skip secure boot, that must be checked here!!!
//
VariableCount = 0;
Status = FindVariableByLifetime (
EFI_SECURE_BOOT_MODE_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
VarAttr = VARIABLE_ATTRIBUTE_NV_BS_RT_AW;
Status = UpdateVariable (
EFI_SECURE_BOOT_MODE_NAME,
&gEfiGlobalVariableGuid,
&mPlatformBootMode,
sizeof (UINT8),
VarAttr,
0,
0,
&Variable,
NULL,
&mVariableModuleGlobal->VariableBase
);
ASSERT (Status == EFI_SUCCESS);
if (!FeaturePcdGet (PcdH2OCustomizedSecureBootSupported)) {
return;
}
//
// Initialize AuditMode and DeployedMode value
//
AuditMode = AuditModeValue ();
DeployedMode = DeployedModeValue ();
//
// Two steps to synchronize Deployed mode and Audit mode variable.
// 1. synchronize when PK present.
// 2. synchronize when PK doesn't present.
//
//
// System break while translating from Audit mode to Deployed mode.
// 1. Set PK -> system break -> set Deployed mode from 0 to 1 -> Set Audit mode from 1 to 0
// 2. Set PK -> set Deployed mode from 0 to 1 -> system break -> Set Audit mode from 1 to 0
// Need change Deployed mode to 1 and Audit mode to 0.
//
if (((DeployedMode == 0 && AuditMode == 1) || (DeployedMode == 1 && AuditMode == 1)) && DoesPkExist ()) {
DeployedMode = 1;
AuditMode = 0;
}
//
// System break while translating from Deployed mode to Setup mode.
// 1. Delete PK -> system break -> set Deployed mode from 1 to 0
// *2. Invalid setting if deployed mode == 1 and audit mode == 1 and PK not present
// Need change Deployed mode to 0 and Audit mode to 0.
//
if (((DeployedMode == 1 && AuditMode == 0) || (DeployedMode == 1 && AuditMode == 1))&& !DoesPkExist ()) {
DeployedMode = 0;
AuditMode = 0;
}
//
// Set AuditMode and DeployedMode variable.
//
UpdateAuditModeValue (AuditMode);
UpdateDeployedModeValue (DeployedMode);
//
// Change variable property after synchronize AuditMode and DeployedMode variable.
//
if (AuditMode == 1 || DeployedMode == 1) {
//
// AuditMode (RO) and DeployedMode (RO) in AuditMode or DeployedMode.
//
UpdateDeployedModeProperty (VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY);
UpdateAuditModeProperty (VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY);
} else if (mPlatformMode == USER_MODE) {
//
// AuditMode (RW) and DeployedMode (RW) in user mode.
//
UpdateDeployedModeProperty (0);
UpdateAuditModeProperty (0);
} else {
//
// AuditMode (RW) and DeployedMode (RO) in setup mode.
//
UpdateDeployedModeProperty (VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY);
UpdateAuditModeProperty (0);
}
}
/**
Change all of secure boot mode variables while deleting PK variable.
**/
VOID
ChangeSecureBootModeByDeletePk (
VOID
)
{
//
// If delete PK in user mode, need change to setup mode.
//
UpdatePlatformMode (SETUP_MODE, &mVariableModuleGlobal->VariableBase);
//
// boot mode must change at this point, as untrusted code can now be run
//
UpdatePlatformBootMode (SECURE_BOOT_MODE_DISABLE, &mVariableModuleGlobal->VariableBase);
if (!FeaturePcdGet (PcdH2OCustomizedSecureBootSupported)) {
return;
}
//
// Update related AuditMode and DeployedVariable variable
//
if (DeployedModeValue () == 0) {
//
// Update DeployedMode property to read-only if secure boot mode is changed from user mode to setup mode.
// (SetupMode = 0, SecureBoot = 0 or 1, AuditMode = 0 (R/W), DeployedMode = 0 (R/W)).
// to
// (SetupMode = 1, SecureBoot = 0, AuditMode = 0 (R/W), DeployedMode = 0 (Read-only)).
UpdateDeployedModeProperty (VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY);
} else {
//
// Update AuditMode property to R/W and change DeployedMode value to 0 if secure boot mode is changed from
// deployed mode to setup mode.
// (SetupMode = 0, SecureBoot = 0 or 1, AuditMode = 0 (Read-only), DeployedMode = 1 (Read-only)).
// to
// (SetupMode = 1, SecureBoot = 0, AuditMode = 0 (R/W), DeployedMode = 0 (Read-only)).
UpdateAuditModeProperty (0);
UpdateDeployedModeValue (0);
}
}
/**
Change all of secure boot mode variables while updating PK variable.
**/
VOID
ChangeSecureBootModeByInsertPk (
VOID
)
{
UpdatePlatformMode (USER_MODE, &mVariableModuleGlobal->VariableBase);
if (!FeaturePcdGet (PcdH2OCustomizedSecureBootSupported)) {
return;
}
//
// NOTE: boot mode does NOT change here, since the PK just was enrolled, and untrusted
// code may have run already since reset.
//
if (AuditModeValue () == 0) {
//
// Update DeployedMode property to R/W if secure boot mode is changed from setup mode to user mode.
// (SetupMode = 1, SecureBoot = 0, AuditMode = 0 (R/W), DeployedMode = 0 (Read-only)).
// to
// (SetupMode = 0, SecureBoot = 0, AuditMode = 0 (R/W), DeployedMode = 0 (R/W)).
//
UpdateDeployedModeProperty (0);
} else {
//
// Update AuditMode value to 0 and DeployedMode value to 1 if secure boot mode is changed from audit mode
// to deployed mode
// (SetupMode = 1, SecureBoot = 0, AuditMode = 1 (Read-only), DeployedMode = 0 (Read-only)).
// to
// (SetupMode = 0, SecureBoot = 0, AuditMode = 0 (Read-only), DeployedMode = 1 (Read-only)).
//
UpdateDeployedModeValue (1);
UpdateAuditModeValue (0);
}
}
/**
Process variable with platform key for verification.
@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] Variable The variable information which is used to keep track of variable usage.
@param[in] Attributes Attribute value of the variable
@param[in] IsPk Indicate whether it is to process pk.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
@retval EFI_SUCCESS Variable passed validation successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_SECURITY_VIOLATION The variable does NOT pass the validation.
check carried out by the firmware.
**/
EFI_STATUS
ProcessVarWithPk (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN VOID *Data,
IN UINTN DataSize,
IN VARIABLE_POINTER_TRACK *Variable,
IN UINT32 Attributes OPTIONAL,
IN BOOLEAN IsPk,
IN VARIABLE_GLOBAL *Global
)
{
EFI_STATUS Status;
EFI_TIME *TimeStamp;
BOOLEAN Del;
if (mCryptoService == NULL && mPlatformMode == USER_MODE) {
return EFI_UNSUPPORTED;
}
//
// PK does not support append.
//
if (((Attributes & EFI_VARIABLE_APPEND_WRITE) == EFI_VARIABLE_APPEND_WRITE) &&
(IsPkVariable (VariableName, VendorGuid))) {
return EFI_INVALID_PARAMETER;
}
if (mPlatformMode == USER_MODE) {
Del = FALSE;
Status = WriteTimeBasedPayload (VariableName, VendorGuid, Data, DataSize, Variable, Attributes, Global, AuthVarTypePk, &Del);
if (!EFI_ERROR (Status) && IsPk && Del && !DoesPkExist ()) {
ChangeSecureBootModeByDeletePk ();
}
} else {
TimeStamp = &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp;
DataSize -= AUTHINFO2_SIZE (Data);
Data = (UINT8*) Data + AUTHINFO2_SIZE (Data);
Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, Variable, TimeStamp, Global);
//
// If enroll PK in setup mode, need change to user mode.
//
if (!EFI_ERROR (Status) && IsPk && DataSize != 0) {
ChangeSecureBootModeByInsertPk ();
}
}
return Status;
}
/**
Process variable with common secure variable for verification.
@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] Variable The variable information which is used to keep track of variable usage.
@param[in] Attributes Attribute value of the variable.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
@param[in] AuthVarType Verify against PK or KEK database or private database.
@retval EFI_SUCCESS Variable pass validation successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_SECURITY_VIOLATION The variable does NOT pass the validation
check carried out by the firmware.
**/
STATIC
EFI_STATUS
ProcessVarWithCommonSecureVar (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN VOID *Data,
IN UINTN DataSize,
IN VARIABLE_POINTER_TRACK *Variable,
IN UINT32 Attributes OPTIONAL,
IN VARIABLE_GLOBAL *Global,
IN AUTHVAR_TYPE AuthVarType
)
{
EFI_STATUS Status;
EFI_TIME *TimeStamp;
if (mCryptoService == NULL && mPlatformMode == USER_MODE) {
return EFI_UNSUPPORTED;
}
if ((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) == 0) {
return EFI_INVALID_PARAMETER;
}
if (mPlatformMode == USER_MODE) {
Status = WriteTimeBasedPayload (VariableName, VendorGuid, Data, DataSize, Variable, Attributes, Global, AuthVarType, NULL);
} else {
TimeStamp = &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp;
DataSize -= AUTHINFO2_SIZE (Data);
Data = (UINT8*) Data + AUTHINFO2_SIZE (Data);
Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, Variable, TimeStamp, Global);
}
return Status;
}
/**
Process variable with key exchange key for verification.
@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] Variable The variable information which is used to keep track of variable usage.
@param[in] Attributes Attribute value of the variable.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
@retval EFI_SUCCESS Variable pass validation successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_SECURITY_VIOLATION The variable does NOT pass the validation
check carried out by the firmware.
**/
EFI_STATUS
ProcessVarWithKek (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN VOID *Data,
IN UINTN DataSize,
IN VARIABLE_POINTER_TRACK *Variable,
IN UINT32 Attributes OPTIONAL,
IN VARIABLE_GLOBAL *Global
)
{
return ProcessVarWithCommonSecureVar (VariableName, VendorGuid, Data, DataSize, Variable, Attributes, Global, AuthVarTypeKek);
}
/**
Process variable with dbr for verification.
@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] Variable The variable information which is used to keep track of variable usage.
@param[in] Attributes Attribute value of the variable.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
@retval EFI_SUCCESS Variable pass validation successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_SECURITY_VIOLATION The variable does NOT pass the validation
check carried out by the firmware.
**/
EFI_STATUS
ProcessVarWithDbr (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN VOID *Data,
IN UINTN DataSize,
IN VARIABLE_POINTER_TRACK *Variable,
IN UINT32 Attributes OPTIONAL,
IN VARIABLE_GLOBAL *Global
)
{
return ProcessVarWithCommonSecureVar (VariableName, VendorGuid, Data, DataSize, Variable, Attributes, Global, AuthVarTypeDbr);
}
/**
Process variable with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS/EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set
@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] Variable The variable information which is used to keep track of variable usage.
@param[in] Attributes Attribute value of the variable.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
@retval EFI_SUCCESS Variable is not write-protected or pass validation successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_WRITE_PROTECTED 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.
**/
EFI_STATUS
ProcessVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN VOID *Data,
IN UINTN DataSize,
IN VARIABLE_POINTER_TRACK *Variable,
IN UINT32 Attributes,
IN VARIABLE_GLOBAL *Global
)
{
if (mVariableModuleGlobal->SecureBootCallbackEnabled && IsAdministerSecureVariable (VariableName, VendorGuid)) {
return UpdateAdministerSecureVariable (
VariableName,
VendorGuid,
Data,
DataSize,
Attributes
);
}
if (mCryptoService == NULL) {
return EFI_UNSUPPORTED;
}
if (IsExistingInsydeSecureVariable (Variable)) {
//
// Insyde secure variable only can update in unlocked state, so just return EFI_WRITE_PROTECTED directly.
//
return EFI_WRITE_PROTECTED;
}
//
// Process Time-based Authenticated variable.
//
if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
return WriteTimeBasedPayload (VariableName, VendorGuid, Data, DataSize, Variable, Attributes, Global, AuthVarTypePriv, NULL);
}
if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
//
// Reject Counter Based Auth Variable processing request.
//
return EFI_UNSUPPORTED;
} else if ((Variable->CurrPtr != NULL) &&
(Variable->CurrPtr->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.
//
return UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, Variable, NULL, Global);
}
/**
According to variable name, GUID to determine this variable is whether
support input secure database type.
@param[in] VariableName Name of variable.
@param[in] VendorGuid Variable vendor GUID.
@param[in] SupportedDatabase Supported secure database.
@retval TRUE Parameters not valid
@retval FALSE Variable store successfully updated
**/
STATIC
BOOLEAN
IsSupportedSecureDatabase (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
IN UINT32 SupportedDatabase
)
{
SECURE_DATABASE_TYPE_MAP MapTable[] = {
{IsDbVariable, AUTHORIZED_SIGNATURE_SUPPORT},
{IsDbxVariable, FORBIDDEN_SIGNATURE_SUPPORT},
{IsDbtVariable, TIMESTAMP_SIGNATURE_SUPPORT},
{IsPkVariable, PLATFORM_KEY_SUPPORT},
{IsKekVariable, KEY_EXCHANGE_KEY_SUPPORT}
};
UINTN Index;
UINTN TableCnt;
TableCnt = sizeof (MapTable) / sizeof (SECURE_DATABASE_TYPE_MAP);
for (Index = 0; Index < TableCnt; Index++) {
if (MapTable[Index].VariableFun (VariableName, VendorGuid) &&
(MapTable[Index].SecureDatabaseType & SupportedDatabase) != 0) {
return TRUE;
}
}
return (BOOLEAN) ((SupportedDatabase & NORMAL_SIGNATURE_SUPPORT) != 0);
}
/**
According to input EFI_SIGNATURE_LIST instance to check this signature is whether a
valid X509 certificate.
@param[in] SigList Pointer to the start address of signature list.
@retval TRUE Input data is a valid X509 certificate or system doesn't support check mechanism.
@retval FALSE Input data is an invalid X509 certificate.
**/
STATIC
BOOLEAN
IsValidX509Cert (
IN EFI_SIGNATURE_LIST *SigList
)
{
VOID *RsaContext;
EFI_SIGNATURE_DATA *CertData;
UINTN CertLen;
if (SigList == NULL || !CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {
return FALSE;
}
//
// If system doesn't support RsaNew function, we have no way to check input certifcate.
// Therefore, suppose the input certificate is valid.
//
RsaContext = mCryptoService->RsaNew ();
if (RsaContext == NULL) {
return TRUE;
}
mCryptoService->RsaFree (RsaContext);
CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) + SigList->SignatureHeaderSize);
CertLen = SigList->SignatureSize - sizeof (EFI_GUID);
if (!mCryptoService->RsaGetPublicKeyFromX509 (CertData->SignatureData, CertLen, &RsaContext)) {
return FALSE;
}
mCryptoService->RsaFree (RsaContext);
return TRUE;
}
/**
Perform a sanity check on the data to be written to secure boot variables.
@param[in] VariableName Name of variable.
@param[in] VendorGuid Variable vendor GUID.
@param[in] Data Variable data.
@param[in] DataSize Size of data. 0 means delete.
@retval EFI_SUCCESS The sanity check operation is success.
@retval EFI_INVALID_PARAMETER Variable data has some data structure problem.
**/
EFI_STATUS
CheckSecureBootVarData (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
IN CONST VOID *Data,
IN UINTN DataSize
)
{
EFI_SIGNATURE_LIST *SigList;
UINTN CheckDataSize;
UINTN Index;
UINTN TableCnt;
EFI_STATUS Status;
//
// make sure there is data
//
if ((Data == NULL) || (DataSize == 0)) {
return EFI_SUCCESS;
}
//
// walk through the lists present in the data, checking for supported types, and correct sizes
//
SigList = (EFI_SIGNATURE_LIST *)Data;
CheckDataSize = DataSize;
if (SigList->SignatureListSize > CheckDataSize) {
return EFI_INVALID_PARAMETER;
}
Status = EFI_SUCCESS;
TableCnt = sizeof (mSupportSigItem) / sizeof (SIGNATURE_SUPPORT_INFO);
while (CheckDataSize > 0) {
//
// check the size of signature list size first to make sure signature data integrity
//
if (SigList->SignatureListSize < sizeof (EFI_SIGNATURE_LIST) || CheckDataSize < SigList->SignatureListSize) {
Status = EFI_INVALID_PARAMETER;
break;
}
for (Index = 0; Index < TableCnt; Index++) {
//
// compare singnature type first
//
if (!CompareGuid (&SigList->SignatureType, &mSupportSigItem[Index].SignatureType)) {
continue;
}
//
// Doesn't support to be signed hash if EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION bit is disabled.
//
if ((PcdGet64 (PcdOsIndicationsSupported) & EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION) == 0 &&
mSupportSigItem[Index].SupportedDatabase == FORBIDDEN_SIGNATURE_SUPPORT) {
continue;
}
//
// check relative variable supports this signature type or not
//
if (!IsSupportedSecureDatabase (VariableName, VendorGuid, mSupportSigItem[Index].SupportedDatabase)) {
continue;
}
//
// check signature size is correct or not.
//
if (mSupportSigItem[Index].FixedSigSize && mSupportSigItem[Index].SignatureSize != SigList->SignatureSize) {
continue;
}
//
// Check input certificate is whether a valid certificate if signature type is X509 certificate
//
if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid) && !IsValidX509Cert (SigList)) {
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
CheckDataSize -= SigList->SignatureListSize;
SigList = (EFI_SIGNATURE_LIST *) ((UINT8 *) SigList + SigList->SignatureListSize);
}
return EFI_INVALID_PARAMETER;
}
/**
This function used to check the signature list node is whether can merge any of the signature list.
If yes, return the pointer to the signature list which can be merged.
If no, return NULL pointer.
@param[in] SigList Pointer to the start address of whole signature list.
@param[in] BufferSize The buffer size of whole signature list.
@param[in] SigListNode Pointer to signature list node which want to check.
@retval NULL Cannot find can merged signature list.
@return EFI_SIGNATURE_LIST * Pointer to the can merged signature list.
**/
STATIC
EFI_SIGNATURE_LIST *
FindCanMergeSig (
IN EFI_SIGNATURE_LIST *SigList,
IN UINTN BufferSize,
IN EFI_SIGNATURE_LIST *SigListNode
)
{
UINTN DataOffest;
EFI_SIGNATURE_LIST *WorkingSig;
BOOLEAN SigFound;
if (SigList == NULL || BufferSize == 0 || SigListNode == NULL) {
return NULL;
}
//
// if singnature type isn't a fixed length singnature type, return NULL pointer directly.
//
if (CompareGuid (&SigListNode->SignatureType, &gEfiCertX509Guid)) {
return NULL;
}
SigFound = FALSE;
WorkingSig = SigList;
DataOffest = 0;
while (DataOffest < BufferSize) {
if (CompareGuid (&WorkingSig->SignatureType, &SigListNode->SignatureType)) {
SigFound = TRUE;
break;
}
DataOffest += WorkingSig->SignatureListSize;
WorkingSig = (EFI_SIGNATURE_LIST *) ((UINT8 *) WorkingSig + WorkingSig->SignatureListSize);
}
return SigFound ? WorkingSig : NULL;
}
/**
Internal function to get the start address of next signature list.
@param[in] SignatureList Pointer to EFI_SIGNATURE_LIST instance.
@retval NULL SignatureList is NULL or the data in SignatureList is incorrect.
@return EFI_SIGNATURE_LIST * Pointer to next signature list.
**/
STATIC
EFI_SIGNATURE_LIST *
GetNextSignatureList (
IN EFI_SIGNATURE_LIST *SignatureList
)
/*++
Routine Description:
Internal function to get the start address of next signature list.
Arguments:
SignatureList - Pointer to EFI_SIGNATURE_LIST instance.
Returns:
NULL - SignatureList is NULL of the data in SignatureList is incorrect.
EFI_SIGNATURE_LIST * - Pointer to next signature list.
--*/
{
if (SignatureList == NULL || SignatureList->SignatureListSize < sizeof (EFI_SIGNATURE_LIST)) {
return NULL;
}
return (EFI_SIGNATURE_LIST *) ((UINT8 *) SignatureList + SignatureList->SignatureListSize);
}
/**
Merge two buffers which formatted as EFI_SIGNATURE_LIST. Only the new EFI_SIGNATURE_DATA+
will be appended to the original EFI_SIGNATURE_LIST, duplicate EFI_SIGNATURE_DATA
will be ignored.
@param[in] AppendDataSize Size of NewData buffer.
@param[in] AppendDataBuffer Pointer to new EFI_SIGNATURE_LIST to be appended.
@param[in] TotalDataBufferSize Total size of data buffer which can be used.
@param[in, out] UsedDataBufferSize [in] Used data size by byte before invoking this function.
[out] Used data size by byte after invoking this function.
@param[in, out] DataBuffer [in] Buffer to save original variable data.
[out] Buffer to save merged variable data.
@retval EFI_SUCCESS Append data to singnature list successful.
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
@retval EFI_OUT_OF_RESOURCES Total variable data size is large than maximum buffer size.
**/
EFI_STATUS
AppendSignatureList (
IN UINTN AppendDataSize,
IN CONST UINT8 *AppendDataBuffer,
IN UINTN TotalDataBufferSize,
IN OUT UINTN *UsedDataBufferSize,
IN OUT UINT8 *DataBuffer
)
{
UINTN AppendDataOffset;
UINTN CurrentUsedDataSize;
EFI_SIGNATURE_LIST *WorkingAppendSig;
EFI_SIGNATURE_LIST *WorkingSig;
EFI_SIGNATURE_LIST *NextSig;
EFI_SIGNATURE_DATA *WorkingAppendSigData;
EFI_SIGNATURE_DATA *WorkingSigData;
UINTN DataOffest;
UINTN AppendSigCount;
UINTN SigCount;
UINTN AppendIndex;
UINTN CurrentIndex;
UINT32 CurrentSigSize;
UINT32 AppendSigSize;
BOOLEAN CertFound;
if (AppendDataSize == 0) {
return EFI_SUCCESS;
}
if (AppendDataBuffer == NULL || UsedDataBufferSize == NULL || DataBuffer == NULL) {
return EFI_INVALID_PARAMETER;
}
CurrentUsedDataSize = *UsedDataBufferSize;
WorkingAppendSig = (EFI_SIGNATURE_LIST *) AppendDataBuffer;
//
// Paring whole appended signature list
//
AppendDataOffset = 0;
while (AppendDataOffset < AppendDataSize) {
//
// check append singnature list is a valid signature list
//
if (WorkingAppendSig->SignatureListSize < sizeof (EFI_SIGNATURE_LIST)) {
return EFI_INVALID_PARAMETER;
}
AppendSigSize = WorkingAppendSig->SignatureSize;
AppendSigCount = (WorkingAppendSig->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - WorkingAppendSig->SignatureHeaderSize) / AppendSigSize;
WorkingAppendSigData = (EFI_SIGNATURE_DATA *) (((UINT8 *) WorkingAppendSig) + sizeof (EFI_SIGNATURE_LIST) + WorkingAppendSig->SignatureHeaderSize);
//
// Parsing specific appended signature list
//
for (AppendIndex = 0; AppendIndex < AppendSigCount; AppendIndex++) {
WorkingSig = (EFI_SIGNATURE_LIST *) DataBuffer;
CertFound = FALSE;
//
// Try to find matched signature (use signature type, signature owner and signature data) from whole existed signature list
//
DataOffest = 0;
while (DataOffest < CurrentUsedDataSize) {
if (CompareGuid (&WorkingSig->SignatureType, &WorkingAppendSig->SignatureType) &&
WorkingAppendSig->SignatureSize == WorkingSig->SignatureSize) {
CurrentSigSize = WorkingSig->SignatureSize;
SigCount = (WorkingSig->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - WorkingSig->SignatureHeaderSize) / CurrentSigSize;
WorkingSigData = (EFI_SIGNATURE_DATA *) (((UINT8 *) WorkingSig) + sizeof (EFI_SIGNATURE_LIST) + WorkingSig->SignatureHeaderSize);
for (CurrentIndex = 0; CurrentIndex < SigCount; CurrentIndex++) {
//
// compare certificated data and signature owner to confirm the existence of appended signature data
//
if (CompareMem (WorkingSigData, WorkingAppendSigData, CurrentSigSize) == 0) {
CertFound = TRUE;
break;
}
WorkingSigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) WorkingSigData + CurrentSigSize);
}
}
if (CertFound) {
break;
}
DataOffest += WorkingSig->SignatureListSize;
WorkingSig = GetNextSignatureList (WorkingSig);
if (WorkingSig == NULL) {
return EFI_INVALID_PARAMETER;
}
}
//
// Add signature data to current signature list if can not find mathced signature data
//
if (!CertFound) {
WorkingSig = FindCanMergeSig ((EFI_SIGNATURE_LIST *) DataBuffer, CurrentUsedDataSize, WorkingAppendSig);
if (WorkingSig != NULL) {
if (CurrentUsedDataSize + AppendSigSize > TotalDataBufferSize) {
return EFI_OUT_OF_RESOURCES;
}
//
// merge appended signature node to signature list
//
NextSig = GetNextSignatureList (WorkingSig);
CopyMem (
(UINT8 *) NextSig + AppendSigSize,
NextSig,
CurrentUsedDataSize - (UINTN) ((UINT8 *) NextSig - DataBuffer)
);
CopyMem (NextSig, WorkingAppendSigData, AppendSigSize);
WorkingSig->SignatureListSize += AppendSigSize;
CurrentUsedDataSize += AppendSigSize;
} else {
if (CurrentUsedDataSize + sizeof (EFI_SIGNATURE_LIST) + AppendSigSize > TotalDataBufferSize) {
return EFI_OUT_OF_RESOURCES;
}
//
// Append signature list to the trail of signature list.
//
WorkingSig = (EFI_SIGNATURE_LIST *) (DataBuffer + CurrentUsedDataSize);
CopyMem (WorkingSig, WorkingAppendSig, sizeof (EFI_SIGNATURE_LIST));
WorkingSig->SignatureListSize = AppendSigSize + sizeof (EFI_SIGNATURE_LIST);
WorkingSig++;
CopyMem (WorkingSig, WorkingAppendSigData, AppendSigSize);
CurrentUsedDataSize += (sizeof (EFI_SIGNATURE_LIST) + AppendSigSize);
}
}
WorkingAppendSigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) WorkingAppendSigData + AppendSigSize);
}
AppendDataOffset += WorkingAppendSig->SignatureListSize;
WorkingAppendSig = GetNextSignatureList (WorkingAppendSig);
if (WorkingAppendSig == NULL) {
return EFI_INVALID_PARAMETER;
}
}
*UsedDataBufferSize = CurrentUsedDataSize;
return EFI_SUCCESS;
}
/**
Compare two EFI_TIME data.
@param[in] FirstTime A pointer to the first EFI_TIME data.
@param[in] 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
CompareTimeStamp (
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);
}
/**
This function uses to synchronize authenticated variable data and state between
runtime and SMM.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
@retval EFI_SUCCESS Synchronize authenticated data and state successful.
**/
EFI_STATUS
SyncAuthData (
IN VARIABLE_GLOBAL *Global
)
{
EFI_STATUS Status;
VARIABLE_POINTER_TRACK Variable;
UINT8 *Data;
UINTN VariableCount;
UINT8 SecureBootEnforce;
KERNEL_CONFIGURATION *SystemConfiguration;
if (!FeaturePcdGet (PcdH2OSecureBootSupported)) {
return EFI_SUCCESS;
}
//
// Check PK database's existence to determine the SetupMode value.
// Then create a new "SetupMode" with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.
//
VariableCount = 0;
Status = FindVariableByLifetimeInAllRegions (
EFI_PLATFORM_KEY_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
Global
);
if (Variable.CurrPtr == NULL) {
mPlatformMode = SETUP_MODE;
mPlatformBootMode = SECURE_BOOT_MODE_DISABLE;
} else {
mPlatformMode = USER_MODE;
mPlatformBootMode = SECURE_BOOT_MODE_ENABLE;
Status = FindVariableByLifetime (
SETUP_VARIABLE_NAME,
&gSystemConfigurationGuid,
&Variable,
&VariableCount,
Global
);
//
// Disable secure boot if boot mode isn't UEFI boot mode.
//
if (!EFI_ERROR (Status)) {
SystemConfiguration = (KERNEL_CONFIGURATION *) GetVariableDataPtr (Variable.CurrPtr);
if (SystemConfiguration->BootType != EFI_BOOT_TYPE) {
mPlatformBootMode = SECURE_BOOT_MODE_DISABLE;
}
}
//
// Disable secure boot, if SecureBootEnforce is disabled
//
if (mPlatformBootMode == SECURE_BOOT_MODE_ENABLE) {
Status = FindVariableByLifetime (
EFI_SECURE_BOOT_ENFORCE_NAME,
&gEfiGenericVariableGuid,
&Variable,
&VariableCount,
Global
);
SecureBootEnforce = 1;
if (!EFI_ERROR (Status)) {
Data = GetVariableDataPtr (Variable.CurrPtr);
SecureBootEnforce = (*Data == 1) ? 1 : 0;
}
mPlatformBootMode = (SecureBootEnforce == 1) ? SECURE_BOOT_MODE_ENABLE : SECURE_BOOT_MODE_DISABLE;
}
}
return EFI_SUCCESS;
}
/**
This function uses to find secure boot database default variable.
@param[in] VariableName Name of Variable to be found.
@param[in] VendorGuid Variable vendor GUID.
@param[out] DataSize Size of variable data by byte.
@param[out] Data Data buffer to save variable data.
@retval EFI_SUCCESS Find input secure boot database default variable successful.
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
@retval EFI_NOT_FOUND Cannot find secure boot database default variable.
**/
EFI_STATUS
FindSecureDatabaseDefaultVariables (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
OUT UINTN *DataSize OPTIONAL,
OUT VOID **Data OPTIONAL
)
{
EFI_STATUS Status;
UINTN HeaderSize;
UINTN CurrentSearchedSize;
VARIABLE_HEADER *VariableHeader;
VARIABLE_HEADER *NexVariable;
UINTN Index;
UINTN MaxTableNum;
BOOLEAN CheckFunFound;
BOOLEAN VariableFound;
SPECIFIC_VARIABLE CheckFunTable[][2] = {
{IsPkVariable, IsPkDefaultVariable},
{IsKekVariable, IsKekDefaultVariable},
{IsDbVariable, IsDbDefaultVariable},
{IsDbxVariable, IsDbxDefaultVariable},
{IsDbtVariable, IsDbtDefaultVariable},
{IsDbrVariable, IsDbrDefaultVariable}
};
if (!IsSecureDatabaseDefaultVariable (VariableName, VendorGuid)) {
return EFI_INVALID_PARAMETER;
}
HeaderSize = sizeof (UINT64) + sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY) + GetVariableStoreHeaderSize ();
if (HeaderSize > mVariableModuleGlobal->FactoryDefaultSize) {
return EFI_NOT_FOUND;
}
//
// Doesn't support TimestampSignatureDatabase if EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION is disabled.
//
if ((PcdGet64 (PcdOsIndicationsSupported) & EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION) == 0) {
CheckFunTable[4][1] = NULL;
}
//
// Doesn't support RecoverySignatureDatabase if EFI_OS_INDICATIONS_START_OS_RECOVERY is disabled.
//
if ((PcdGet64 (PcdOsIndicationsSupported) & EFI_OS_INDICATIONS_START_OS_RECOVERY) == 0) {
CheckFunTable[5][1] = NULL;
}
MaxTableNum = sizeof (CheckFunTable) / (sizeof (SPECIFIC_VARIABLE) * 2);
CheckFunFound = FALSE;
for (Index = 0; Index < MaxTableNum; Index++) {
if (CheckFunTable[Index][1] != NULL && CheckFunTable[Index][1] (VariableName, VendorGuid)) {
CheckFunFound = TRUE;
break;
}
}
if (!CheckFunFound) {
return EFI_NOT_FOUND;
}
VariableHeader = (VARIABLE_HEADER *) ((UINTN) mVariableModuleGlobal->FactoryDefaultCache + HeaderSize);
CurrentSearchedSize = HeaderSize;
VariableFound = FALSE;
while (IsValidVariableHeader (VariableHeader) && CurrentSearchedSize < mVariableModuleGlobal->FactoryDefaultSize) {
NexVariable = GetNextVariablePtr (VariableHeader);
if (VariableHeader->State == VAR_ADDED && CheckFunTable[Index][0] (GET_VARIABLE_NAME_PTR (VariableHeader), &VariableHeader->VendorGuid)) {
VariableFound = TRUE;
break;
}
CurrentSearchedSize += ((UINTN) NexVariable - (UINTN) VariableHeader);
VariableHeader = NexVariable;
}
if (!VariableFound && !IsDbxDefaultVariable (VariableName, VendorGuid)) {
return EFI_NOT_FOUND;
}
//
// Needn't output data size and data, so just return EFI_SUCCESS.
//
if (DataSize == NULL || Data == NULL) {
return EFI_SUCCESS;
}
//
// Get variable data and variable size
//
if (VariableFound) {
*Data = GetVariableDataPtr (VariableHeader);
*DataSize = VariableHeader->DataSize;
} else {
*DataSize = APPEND_BUFF_SIZE;
Status = CreateDummyDbxData (DataSize, mStorageArea);
ASSERT_EFI_ERROR (Status);
*Data = mStorageArea;
}
return EFI_SUCCESS;
}
/**
This function uses to get secure boot database default variable.
@param[in] VariableName Name of Variable to be found.
@param[in] VendorGuid Variable vendor GUID.
@param[out] Attributes Attribute value of the variable found.
@param[in, out] DataSize Size of Data found. If size is less than the
data, this value contains the required size.
@param[out] Data Data buffer to save variable data.
@retval EFI_SUCCESS Get secure boot database default variable successful.
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
@retval EFI_NOT_FOUND Cannot find secure boot database default variable.
@retval EFI_BUFFER_TOO_SMALL Find Secure boot database default variable but input buffer size
is too small.
**/
EFI_STATUS
GetSecureDatabaseDefaultVariables (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
OUT UINT32 *Attributes OPTIONAL,
IN OUT UINTN *DataSize,
OUT VOID *Data
)
{
EFI_STATUS Status;
UINTN VariableSize;
UINT8 *VariableData;
Status = FindSecureDatabaseDefaultVariables (VariableName, VendorGuid, &VariableSize, (VOID **)&VariableData);
if (EFI_ERROR (Status)) {
return Status;
}
if (VariableSize > *DataSize) {
*DataSize = VariableSize;
return EFI_BUFFER_TOO_SMALL;
}
if (Attributes != NULL) {
*Attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
}
CopyMem (Data, VariableData, VariableSize);
*DataSize = VariableSize;
return EFI_SUCCESS;
}
/**
Update platform boot mode.
@param[in] Mode SECURE_BOOT_MODE_ENABLE or SECURE_BOOT_MODE_DISABLE.
@param[in] Global Pointer to VARIABLE_GLOBAL instance.
**/
VOID
UpdatePlatformBootMode (
IN UINT32 Mode,
IN VARIABLE_GLOBAL *Global
)
{
EFI_STATUS Status;
VARIABLE_POINTER_TRACK Variable;
UINT32 VarAttr;
UINTN VariableCount;
if (VariableAtRuntime ()) {
//
// 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;
}
VariableCount = 0;
Status = FindVariableByLifetime (
EFI_SECURE_BOOT_MODE_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
Global
);
ASSERT_EFI_ERROR (Status);
mPlatformBootMode = Mode;
VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;
Status = UpdateVariable (
EFI_SECURE_BOOT_MODE_NAME,
&gEfiGlobalVariableGuid,
&mPlatformBootMode,
sizeof (UINT8),
VarAttr,
0,
0,
&Variable,
NULL,
Global
);
ASSERT_EFI_ERROR (Status);
}