X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=SecurityPkg%2FVariableAuthenticated%2FEsalVariableDxeSal%2FAuthService.c;fp=SecurityPkg%2FVariableAuthenticated%2FEsalVariableDxeSal%2FAuthService.c;h=38f462628ad71872772aafac8c6e23cd295e6758;hb=0c18794ea4289f03fefc7117b56740414cc0536c;hp=0000000000000000000000000000000000000000;hpb=986d1dfb0813d6a7623531e85c2e2a7e1f956cf8;p=mirror_edk2.git diff --git a/SecurityPkg/VariableAuthenticated/EsalVariableDxeSal/AuthService.c b/SecurityPkg/VariableAuthenticated/EsalVariableDxeSal/AuthService.c new file mode 100644 index 0000000000..38f462628a --- /dev/null +++ b/SecurityPkg/VariableAuthenticated/EsalVariableDxeSal/AuthService.c @@ -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.
+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; +} +