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