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