--- /dev/null
+/** @file\r
+ Implement authentication services for the authenticated variables.\r
+\r
+ Caution: This module requires additional review when modified.\r
+ This driver will have external input - variable data. It may be input in SMM mode.\r
+ This external input must be validated carefully to avoid security issue like\r
+ buffer overflow, integer overflow.\r
+ Variable attribute should also be checked to avoid authentication bypass.\r
+ The whole SMM authentication variable design relies on the integrity of flash part and SMM.\r
+ which is assumed to be protected by platform. All variable code and metadata in flash/SMM Memory\r
+ may not be modified without authorization. If platform fails to protect these resources,\r
+ the authentication service provided in this driver will be broken, and the behavior is undefined.\r
+\r
+ ProcessVarWithPk(), ProcessVarWithKek() and ProcessVariable() are the function to do\r
+ variable authentication.\r
+\r
+ VerifyTimeBasedPayloadAndUpdate() and VerifyCounterBasedPayload() are sub function to do verification.\r
+ They will do basic validation for authentication data structure, then call crypto library\r
+ to verify the signature.\r
+\r
+Copyright (c) 2009 - 2015, 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 "AuthServiceInternal.h"\r
+\r
+//\r
+// Public Exponent of RSA Key.\r
+//\r
+CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 };\r
+\r
+//\r
+// Requirement for different signature type which have been defined in UEFI spec.\r
+// These data are used to perform SignatureList format check while setting PK/KEK variable.\r
+//\r
+EFI_SIGNATURE_ITEM mSupportSigItem[] = {\r
+//{SigType, SigHeaderSize, SigDataSize }\r
+ {EFI_CERT_SHA256_GUID, 0, 32 },\r
+ {EFI_CERT_RSA2048_GUID, 0, 256 },\r
+ {EFI_CERT_RSA2048_SHA256_GUID, 0, 256 },\r
+ {EFI_CERT_SHA1_GUID, 0, 20 },\r
+ {EFI_CERT_RSA2048_SHA1_GUID, 0, 256 },\r
+ {EFI_CERT_X509_GUID, 0, ((UINT32) ~0)},\r
+ {EFI_CERT_SHA224_GUID, 0, 28 },\r
+ {EFI_CERT_SHA384_GUID, 0, 48 },\r
+ {EFI_CERT_SHA512_GUID, 0, 64 },\r
+ {EFI_CERT_X509_SHA256_GUID, 0, 48 },\r
+ {EFI_CERT_X509_SHA384_GUID, 0, 64 },\r
+ {EFI_CERT_X509_SHA512_GUID, 0, 80 }\r
+};\r
+\r
+/**\r
+ Finds variable in storage blocks of volatile and non-volatile storage areas.\r
+\r
+ This code finds variable in storage blocks of volatile and non-volatile storage areas.\r
+ If VariableName is an empty string, then we just return the first\r
+ qualified variable without comparing VariableName and VendorGuid.\r
+\r
+ @param[in] VariableName Name of the variable to be found.\r
+ @param[in] VendorGuid Variable vendor GUID to be found.\r
+ @param[out] Data Pointer to data address.\r
+ @param[out] DataSize Pointer to data size.\r
+\r
+ @retval EFI_INVALID_PARAMETER If VariableName is not an empty string,\r
+ while VendorGuid is NULL.\r
+ @retval EFI_SUCCESS Variable successfully found.\r
+ @retval EFI_NOT_FOUND Variable not found\r
+\r
+**/\r
+EFI_STATUS\r
+AuthServiceInternalFindVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ OUT VOID **Data,\r
+ OUT UINTN *DataSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ AUTH_VARIABLE_INFO AuthVariableInfo;\r
+\r
+ ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));\r
+ Status = mAuthVarLibContextIn->FindVariable (\r
+ VariableName,\r
+ VendorGuid,\r
+ &AuthVariableInfo\r
+ );\r
+ *Data = AuthVariableInfo.Data;\r
+ *DataSize = AuthVariableInfo.DataSize;\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Update the variable region with Variable information.\r
+\r
+ @param[in] VariableName Name of variable.\r
+ @param[in] VendorGuid Guid of variable.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] Attributes Attribute value of the variable.\r
+\r
+ @retval EFI_SUCCESS The update operation is success.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_WRITE_PROTECTED Variable is write-protected.\r
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource.\r
+\r
+**/\r
+EFI_STATUS\r
+AuthServiceInternalUpdateVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes\r
+ )\r
+{\r
+ AUTH_VARIABLE_INFO AuthVariableInfo;\r
+\r
+ ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));\r
+ AuthVariableInfo.VariableName = VariableName;\r
+ AuthVariableInfo.VendorGuid = VendorGuid;\r
+ AuthVariableInfo.Data = Data;\r
+ AuthVariableInfo.DataSize = DataSize;\r
+ AuthVariableInfo.Attributes = Attributes;\r
+\r
+ return mAuthVarLibContextIn->UpdateVariable (\r
+ &AuthVariableInfo\r
+ );\r
+}\r
+\r
+/**\r
+ Update the variable region with Variable information.\r
+\r
+ @param[in] VariableName Name of variable.\r
+ @param[in] VendorGuid Guid of variable.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] Attributes Attribute value of the variable.\r
+ @param[in] KeyIndex Index of associated public key.\r
+ @param[in] MonotonicCount Value of associated monotonic count.\r
+\r
+ @retval EFI_SUCCESS The update operation is success.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_WRITE_PROTECTED Variable is write-protected.\r
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource.\r
+\r
+**/\r
+EFI_STATUS\r
+AuthServiceInternalUpdateVariableWithMonotonicCount (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes,\r
+ IN UINT32 KeyIndex,\r
+ IN UINT64 MonotonicCount\r
+ )\r
+{\r
+ AUTH_VARIABLE_INFO AuthVariableInfo;\r
+\r
+ ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));\r
+ AuthVariableInfo.VariableName = VariableName;\r
+ AuthVariableInfo.VendorGuid = VendorGuid;\r
+ AuthVariableInfo.Data = Data;\r
+ AuthVariableInfo.DataSize = DataSize;\r
+ AuthVariableInfo.Attributes = Attributes;\r
+ AuthVariableInfo.PubKeyIndex = KeyIndex;\r
+ AuthVariableInfo.MonotonicCount = MonotonicCount;\r
+\r
+ return mAuthVarLibContextIn->UpdateVariable (\r
+ &AuthVariableInfo\r
+ );\r
+}\r
+\r
+/**\r
+ Update the variable region with Variable information.\r
+\r
+ @param[in] VariableName Name of variable.\r
+ @param[in] VendorGuid Guid of variable.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] Attributes Attribute value of the variable.\r
+ @param[in] TimeStamp Value of associated TimeStamp.\r
+\r
+ @retval EFI_SUCCESS The update operation is success.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_WRITE_PROTECTED Variable is write-protected.\r
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource.\r
+\r
+**/\r
+EFI_STATUS\r
+AuthServiceInternalUpdateVariableWithTimeStamp (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes,\r
+ IN EFI_TIME *TimeStamp\r
+ )\r
+{\r
+ EFI_STATUS FindStatus;\r
+ VOID *OrgData;\r
+ UINTN OrgDataSize;\r
+ AUTH_VARIABLE_INFO AuthVariableInfo;\r
+\r
+ FindStatus = AuthServiceInternalFindVariable (\r
+ VariableName,\r
+ VendorGuid,\r
+ &OrgData,\r
+ &OrgDataSize\r
+ );\r
+\r
+ //\r
+ // EFI_VARIABLE_APPEND_WRITE attribute only effects for existing variable\r
+ //\r
+ if (!EFI_ERROR (FindStatus) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0)) {\r
+ if ((CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) &&\r
+ ((StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) || (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0) ||\r
+ (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE2) == 0))) ||\r
+ (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0))) {\r
+ //\r
+ // For variables with formatted as EFI_SIGNATURE_LIST, the driver shall not perform an append of\r
+ // EFI_SIGNATURE_DATA values that are already part of the existing variable value.\r
+ //\r
+ FilterSignatureList (\r
+ OrgData,\r
+ OrgDataSize,\r
+ Data,\r
+ &DataSize\r
+ );\r
+ }\r
+ }\r
+\r
+ ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));\r
+ AuthVariableInfo.VariableName = VariableName;\r
+ AuthVariableInfo.VendorGuid = VendorGuid;\r
+ AuthVariableInfo.Data = Data;\r
+ AuthVariableInfo.DataSize = DataSize;\r
+ AuthVariableInfo.Attributes = Attributes;\r
+ AuthVariableInfo.TimeStamp = TimeStamp;\r
+ return mAuthVarLibContextIn->UpdateVariable (\r
+ &AuthVariableInfo\r
+ );\r
+}\r
+\r
+/**\r
+ Determine whether this operation needs a physical present user.\r
+\r
+ @param[in] VariableName Name of the Variable.\r
+ @param[in] VendorGuid GUID of the Variable.\r
+\r
+ @retval TRUE This variable is protected, only a physical present user could set this variable.\r
+ @retval FALSE This variable is not protected.\r
+\r
+**/\r
+BOOLEAN\r
+NeedPhysicallyPresent(\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid\r
+ )\r
+{\r
+ if ((CompareGuid (VendorGuid, &gEfiSecureBootEnableDisableGuid) && (StrCmp (VariableName, EFI_SECURE_BOOT_ENABLE_NAME) == 0))\r
+ || (CompareGuid (VendorGuid, &gEfiCustomModeEnableGuid) && (StrCmp (VariableName, EFI_CUSTOM_MODE_NAME) == 0))) {\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Determine whether the platform is operating in Custom Secure Boot mode.\r
+\r
+ @retval TRUE The platform is operating in Custom mode.\r
+ @retval FALSE The platform is operating in Standard mode.\r
+\r
+**/\r
+BOOLEAN\r
+InCustomMode (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VOID *Data;\r
+ UINTN DataSize;\r
+\r
+ Status = AuthServiceInternalFindVariable (EFI_CUSTOM_MODE_NAME, &gEfiCustomModeEnableGuid, &Data, &DataSize);\r
+ if (!EFI_ERROR (Status) && (*(UINT8 *) Data == CUSTOM_SECURE_BOOT_MODE)) {\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Get available public key index.\r
+\r
+ @param[in] PubKey Pointer to Public Key data.\r
+\r
+ @return Public key index, 0 if no any public key index available.\r
+\r
+**/\r
+UINT32\r
+GetAvailableKeyIndex (\r
+ IN UINT8 *PubKey\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *Data;\r
+ UINTN DataSize;\r
+ UINT8 *Ptr;\r
+ UINT32 Index;\r
+ BOOLEAN IsFound;\r
+ EFI_GUID VendorGuid;\r
+ CHAR16 Name[1];\r
+ AUTH_VARIABLE_INFO AuthVariableInfo;\r
+ UINT32 KeyIndex;\r
+\r
+ Status = AuthServiceInternalFindVariable (\r
+ AUTHVAR_KEYDB_NAME,\r
+ &gEfiAuthenticatedVariableGuid,\r
+ (VOID **) &Data,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Get public key database variable failure, Status = %r\n", Status));\r
+ return 0;\r
+ }\r
+\r
+ if (mPubKeyNumber == mMaxKeyNumber) {\r
+ Name[0] = 0;\r
+ AuthVariableInfo.VariableName = Name;\r
+ ZeroMem (&VendorGuid, sizeof (VendorGuid));\r
+ AuthVariableInfo.VendorGuid = &VendorGuid;\r
+ mPubKeyNumber = 0;\r
+ //\r
+ // Collect valid key data.\r
+ //\r
+ do {\r
+ Status = mAuthVarLibContextIn->FindNextVariable (AuthVariableInfo.VariableName, AuthVariableInfo.VendorGuid, &AuthVariableInfo);\r
+ if (!EFI_ERROR (Status)) {\r
+ if (AuthVariableInfo.PubKeyIndex != 0) {\r
+ for (Ptr = Data; Ptr < (Data + DataSize); Ptr += sizeof (AUTHVAR_KEY_DB_DATA)) {\r
+ if (ReadUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) Ptr)->KeyIndex)) == AuthVariableInfo.PubKeyIndex) {\r
+ //\r
+ // Check if the key data has been collected.\r
+ //\r
+ for (Index = 0; Index < mPubKeyNumber; Index++) {\r
+ if (ReadUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + Index)->KeyIndex)) == AuthVariableInfo.PubKeyIndex) {\r
+ break;\r
+ }\r
+ }\r
+ if (Index == mPubKeyNumber) {\r
+ //\r
+ // New key data.\r
+ //\r
+ CopyMem ((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + mPubKeyNumber, Ptr, sizeof (AUTHVAR_KEY_DB_DATA));\r
+ mPubKeyNumber++;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ } while (Status != EFI_NOT_FOUND);\r
+\r
+ //\r
+ // No available space to add new public key.\r
+ //\r
+ if (mPubKeyNumber == mMaxKeyNumber) {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Find available public key index.\r
+ //\r
+ for (KeyIndex = 1; KeyIndex <= mMaxKeyNumber; KeyIndex++) {\r
+ IsFound = FALSE;\r
+ for (Ptr = mPubKeyStore; Ptr < (mPubKeyStore + mPubKeyNumber * sizeof (AUTHVAR_KEY_DB_DATA)); Ptr += sizeof (AUTHVAR_KEY_DB_DATA)) {\r
+ if (ReadUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) Ptr)->KeyIndex)) == KeyIndex) {\r
+ IsFound = TRUE;\r
+ break;\r
+ }\r
+ }\r
+ if (!IsFound) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ return KeyIndex;\r
+}\r
+\r
+/**\r
+ Add public key in store and return its index.\r
+\r
+ @param[in] PubKey Input pointer to Public Key data.\r
+ @param[in] VariableDataEntry The variable data entry.\r
+\r
+ @return Index of new added public key.\r
+\r
+**/\r
+UINT32\r
+AddPubKeyInStore (\r
+ IN UINT8 *PubKey,\r
+ IN VARIABLE_ENTRY_CONSISTENCY *VariableDataEntry\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 Index;\r
+ VARIABLE_ENTRY_CONSISTENCY PublicKeyEntry;\r
+ UINT32 Attributes;\r
+ UINT32 KeyIndex;\r
+\r
+ if (PubKey == NULL) {\r
+ return 0;\r
+ }\r
+\r
+ //\r
+ // Check whether the public key entry does exist.\r
+ //\r
+ for (Index = 0; Index < mPubKeyNumber; Index++) {\r
+ if (CompareMem (((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + Index)->KeyData, PubKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {\r
+ return ReadUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + Index)->KeyIndex));\r
+ }\r
+ }\r
+\r
+ KeyIndex = GetAvailableKeyIndex (PubKey);\r
+ if (KeyIndex == 0) {\r
+ return 0;\r
+ }\r
+\r
+ //\r
+ // Check the variable space for both public key and variable data.\r
+ //\r
+ PublicKeyEntry.VariableSize = (mPubKeyNumber + 1) * sizeof (AUTHVAR_KEY_DB_DATA);\r
+ PublicKeyEntry.Guid = &gEfiAuthenticatedVariableGuid;\r
+ PublicKeyEntry.Name = AUTHVAR_KEYDB_NAME;\r
+ Attributes = VARIABLE_ATTRIBUTE_NV_BS_RT | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS;\r
+\r
+ if (!mAuthVarLibContextIn->CheckRemainingSpaceForConsistency (Attributes, &PublicKeyEntry, VariableDataEntry, NULL)) {\r
+ //\r
+ // No enough variable space.\r
+ //\r
+ return 0;\r
+ }\r
+\r
+ WriteUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + mPubKeyNumber)->KeyIndex), KeyIndex);\r
+ CopyMem (((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + mPubKeyNumber)->KeyData, PubKey, EFI_CERT_TYPE_RSA2048_SIZE);\r
+ mPubKeyNumber++;\r
+\r
+ //\r
+ // Update public key database variable.\r
+ //\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ AUTHVAR_KEYDB_NAME,\r
+ &gEfiAuthenticatedVariableGuid,\r
+ mPubKeyStore,\r
+ mPubKeyNumber * sizeof (AUTHVAR_KEY_DB_DATA),\r
+ Attributes\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Update public key database variable failure, Status = %r\n", Status));\r
+ return 0;\r
+ }\r
+\r
+ return KeyIndex;\r
+}\r
+\r
+/**\r
+ Verify data payload with AuthInfo in EFI_CERT_TYPE_RSA2048_SHA256_GUID type.\r
+ Follow the steps in UEFI2.2.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ This function may be invoked in SMM mode, and datasize and data are external input.\r
+ This function will do basic validation, before parse the data.\r
+ This function will parse the authentication carefully to avoid security issues, like\r
+ buffer overflow, integer overflow.\r
+\r
+ @param[in] Data Pointer to data with AuthInfo.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] PubKey Public key used for verification.\r
+\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_SECURITY_VIOLATION If authentication failed.\r
+ @retval EFI_SUCCESS Authentication successful.\r
+\r
+**/\r
+EFI_STATUS\r
+VerifyCounterBasedPayload (\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
+ UINTN PayloadSize;\r
+\r
+ PayloadSize = DataSize - AUTHINFO_SIZE;\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_GUID.\r
+ //\r
+ if ((CertData->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) ||\r
+ !CompareGuid (&CertData->AuthInfo.CertType, &gEfiCertTypeRsa2048Sha256Guid)) {\r
+ //\r
+ // Invalid AuthInfo type, return EFI_SECURITY_VIOLATION.\r
+ //\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+ //\r
+ // Hash data payload with SHA256.\r
+ //\r
+ ZeroMem (Digest, SHA256_DIGEST_SIZE);\r
+ Status = Sha256Init (mHashCtx);\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
+ Status = Sha256Update (mHashCtx, Data + AUTHINFO_SIZE, PayloadSize);\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // Hash Size.\r
+ //\r
+ Status = Sha256Update (mHashCtx, &PayloadSize, sizeof (UINTN));\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // Hash Monotonic Count.\r
+ //\r
+ Status = Sha256Update (mHashCtx, &CertData->MonotonicCount, sizeof (UINT64));\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
+ Status = Sha256Final (mHashCtx, 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
+ Update platform mode.\r
+\r
+ @param[in] Mode SETUP_MODE or USER_MODE.\r
+\r
+ @return EFI_INVALID_PARAMETER Invalid parameter.\r
+ @return EFI_SUCCESS Update platform mode successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+UpdatePlatformMode (\r
+ IN UINT32 Mode\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VOID *Data;\r
+ UINTN DataSize;\r
+ UINT8 SecureBootMode;\r
+ UINT8 SecureBootEnable;\r
+ UINTN VariableDataSize;\r
+\r
+ Status = AuthServiceInternalFindVariable (\r
+ EFI_SETUP_MODE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ &Data,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Update the value of SetupMode variable by a simple mem copy, this could avoid possible\r
+ // variable storage reclaim at runtime.\r
+ //\r
+ mPlatformMode = (UINT8) Mode;\r
+ CopyMem (Data, &mPlatformMode, sizeof(UINT8));\r
+\r
+ if (mAuthVarLibContextIn->AtRuntime ()) {\r
+ //\r
+ // SecureBoot Variable indicates whether the platform firmware is operating\r
+ // in Secure boot mode (1) or not (0), so we should not change SecureBoot\r
+ // Variable in runtime.\r
+ //\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check "SecureBoot" variable's existence.\r
+ // If it doesn't exist, firmware has no capability to perform driver signing verification,\r
+ // then set "SecureBoot" to 0.\r
+ //\r
+ Status = AuthServiceInternalFindVariable (\r
+ EFI_SECURE_BOOT_MODE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ &Data,\r
+ &DataSize\r
+ );\r
+ //\r
+ // If "SecureBoot" variable exists, then check "SetupMode" variable update.\r
+ // If "SetupMode" variable is USER_MODE, "SecureBoot" variable is set to 1.\r
+ // If "SetupMode" variable is SETUP_MODE, "SecureBoot" variable is set to 0.\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+ SecureBootMode = SECURE_BOOT_MODE_DISABLE;\r
+ } else {\r
+ if (mPlatformMode == USER_MODE) {\r
+ SecureBootMode = SECURE_BOOT_MODE_ENABLE;\r
+ } else if (mPlatformMode == SETUP_MODE) {\r
+ SecureBootMode = SECURE_BOOT_MODE_DISABLE;\r
+ } else {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ }\r
+\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_SECURE_BOOT_MODE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ &SecureBootMode,\r
+ sizeof(UINT8),\r
+ EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check "SecureBootEnable" variable's existence. It can enable/disable secure boot feature.\r
+ //\r
+ Status = AuthServiceInternalFindVariable (\r
+ EFI_SECURE_BOOT_ENABLE_NAME,\r
+ &gEfiSecureBootEnableDisableGuid,\r
+ &Data,\r
+ &DataSize\r
+ );\r
+\r
+ if (SecureBootMode == SECURE_BOOT_MODE_ENABLE) {\r
+ //\r
+ // Create the "SecureBootEnable" variable as secure boot is enabled.\r
+ //\r
+ SecureBootEnable = SECURE_BOOT_ENABLE;\r
+ VariableDataSize = sizeof (SecureBootEnable);\r
+ } else {\r
+ //\r
+ // Delete the "SecureBootEnable" variable if this variable exist as "SecureBoot"\r
+ // variable is not in secure boot state.\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ SecureBootEnable = SECURE_BOOT_DISABLE;\r
+ VariableDataSize = 0;\r
+ }\r
+\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_SECURE_BOOT_ENABLE_NAME,\r
+ &gEfiSecureBootEnableDisableGuid,\r
+ &SecureBootEnable,\r
+ VariableDataSize,\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS\r
+ );\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check input data form to make sure it is a valid EFI_SIGNATURE_LIST for PK/KEK/db/dbx/dbt variable.\r
+\r
+ @param[in] VariableName Name of Variable to be check.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Point to the variable data to be checked.\r
+ @param[in] DataSize Size of Data.\r
+\r
+ @return EFI_INVALID_PARAMETER Invalid signature list format.\r
+ @return EFI_SUCCESS Passed signature list format check successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+CheckSignatureListFormat(\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize\r
+ )\r
+{\r
+ EFI_SIGNATURE_LIST *SigList;\r
+ UINTN SigDataSize;\r
+ UINT32 Index;\r
+ UINT32 SigCount;\r
+ BOOLEAN IsPk;\r
+ VOID *RsaContext;\r
+ EFI_SIGNATURE_DATA *CertData;\r
+ UINTN CertLen;\r
+\r
+ if (DataSize == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ ASSERT (VariableName != NULL && VendorGuid != NULL && Data != NULL);\r
+\r
+ if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_PLATFORM_KEY_NAME) == 0)){\r
+ IsPk = TRUE;\r
+ } else if ((CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0)) ||\r
+ (CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) &&\r
+ ((StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) || (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0) ||\r
+ (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE2) == 0)))) {\r
+ IsPk = FALSE;\r
+ } else {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ SigCount = 0;\r
+ SigList = (EFI_SIGNATURE_LIST *) Data;\r
+ SigDataSize = DataSize;\r
+ RsaContext = NULL;\r
+\r
+ //\r
+ // Walk throuth the input signature list and check the data format.\r
+ // If any signature is incorrectly formed, the whole check will fail.\r
+ //\r
+ while ((SigDataSize > 0) && (SigDataSize >= SigList->SignatureListSize)) {\r
+ for (Index = 0; Index < (sizeof (mSupportSigItem) / sizeof (EFI_SIGNATURE_ITEM)); Index++ ) {\r
+ if (CompareGuid (&SigList->SignatureType, &mSupportSigItem[Index].SigType)) {\r
+ //\r
+ // The value of SignatureSize should always be 16 (size of SignatureOwner\r
+ // component) add the data length according to signature type.\r
+ //\r
+ if (mSupportSigItem[Index].SigDataSize != ((UINT32) ~0) &&\r
+ (SigList->SignatureSize - sizeof (EFI_GUID)) != mSupportSigItem[Index].SigDataSize) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (mSupportSigItem[Index].SigHeaderSize != ((UINT32) ~0) &&\r
+ SigList->SignatureHeaderSize != mSupportSigItem[Index].SigHeaderSize) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Index == (sizeof (mSupportSigItem) / sizeof (EFI_SIGNATURE_ITEM))) {\r
+ //\r
+ // Undefined signature type.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {\r
+ //\r
+ // Try to retrieve the RSA public key from the X.509 certificate.\r
+ // If this operation fails, it's not a valid certificate.\r
+ //\r
+ RsaContext = RsaNew ();\r
+ if (RsaContext == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) + SigList->SignatureHeaderSize);\r
+ CertLen = SigList->SignatureSize - sizeof (EFI_GUID);\r
+ if (!RsaGetPublicKeyFromX509 (CertData->SignatureData, CertLen, &RsaContext)) {\r
+ RsaFree (RsaContext);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ RsaFree (RsaContext);\r
+ }\r
+\r
+ if ((SigList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - SigList->SignatureHeaderSize) % SigList->SignatureSize != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ SigCount += (SigList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - SigList->SignatureHeaderSize) / SigList->SignatureSize;\r
+\r
+ SigDataSize -= SigList->SignatureListSize;\r
+ SigList = (EFI_SIGNATURE_LIST *) ((UINT8 *) SigList + SigList->SignatureListSize);\r
+ }\r
+\r
+ if (((UINTN) SigList - (UINTN) Data) != DataSize) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (IsPk && SigCount > 1) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Update "VendorKeys" variable to record the out of band secure boot key modification.\r
+\r
+ @return EFI_SUCCESS Variable is updated successfully.\r
+ @return Others Failed to update variable.\r
+\r
+**/\r
+EFI_STATUS\r
+VendorKeyIsModified (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if (mVendorKeyState == VENDOR_KEYS_MODIFIED) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ mVendorKeyState = VENDOR_KEYS_MODIFIED;\r
+\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_VENDOR_KEYS_NV_VARIABLE_NAME,\r
+ &gEfiVendorKeysNvGuid,\r
+ &mVendorKeyState,\r
+ sizeof (UINT8),\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return AuthServiceInternalUpdateVariable (\r
+ EFI_VENDOR_KEYS_VARIABLE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ &mVendorKeyState,\r
+ sizeof (UINT8),\r
+ EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS\r
+ );\r
+}\r
+\r
+/**\r
+ Process variable with platform key for verification.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ This function may be invoked in SMM mode, and datasize and data are external input.\r
+ This function will do basic validation, before parse the data.\r
+ This function will parse the authentication carefully to avoid security issues, like\r
+ buffer overflow, integer overflow.\r
+ This function will check attribute carefully to avoid authentication bypass.\r
+\r
+ @param[in] VariableName Name of Variable to be found.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data found. If size is less than the\r
+ data, this value contains the required size.\r
+ @param[in] Attributes Attribute value of the variable\r
+ @param[in] IsPk Indicate whether it is to process pk.\r
+\r
+ @return EFI_INVALID_PARAMETER Invalid parameter.\r
+ @return EFI_SECURITY_VIOLATION The variable does NOT pass the validation.\r
+ check carried out by the firmware.\r
+ @return EFI_SUCCESS 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 UINT32 Attributes OPTIONAL,\r
+ IN BOOLEAN IsPk\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN Del;\r
+ UINT8 *Payload;\r
+ UINTN PayloadSize;\r
+\r
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0 ||\r
+ (Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) {\r
+ //\r
+ // PK, KEK and db/dbx/dbt should set EFI_VARIABLE_NON_VOLATILE attribute and should be a time-based\r
+ // authenticated variable.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Del = FALSE;\r
+ if ((InCustomMode() && UserPhysicalPresent()) || (mPlatformMode == SETUP_MODE && !IsPk)) {\r
+ Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data);\r
+ PayloadSize = DataSize - AUTHINFO2_SIZE (Data);\r
+ if (PayloadSize == 0) {\r
+ Del = TRUE;\r
+ }\r
+\r
+ Status = CheckSignatureListFormat(VariableName, VendorGuid, Payload, PayloadSize);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = AuthServiceInternalUpdateVariableWithTimeStamp (\r
+ VariableName,\r
+ VendorGuid,\r
+ Payload,\r
+ PayloadSize,\r
+ Attributes,\r
+ &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if ((mPlatformMode != SETUP_MODE) || IsPk) {\r
+ Status = VendorKeyIsModified ();\r
+ }\r
+ } else if (mPlatformMode == USER_MODE) {\r
+ //\r
+ // Verify against X509 Cert in PK database.\r
+ //\r
+ Status = VerifyTimeBasedPayloadAndUpdate (\r
+ VariableName,\r
+ VendorGuid,\r
+ Data,\r
+ DataSize,\r
+ Attributes,\r
+ AuthVarTypePk,\r
+ &Del\r
+ );\r
+ } else {\r
+ //\r
+ // Verify against the certificate in data payload.\r
+ //\r
+ Status = VerifyTimeBasedPayloadAndUpdate (\r
+ VariableName,\r
+ VendorGuid,\r
+ Data,\r
+ DataSize,\r
+ Attributes,\r
+ AuthVarTypePayload,\r
+ &Del\r
+ );\r
+ }\r
+\r
+ if (!EFI_ERROR(Status) && IsPk) {\r
+ if (mPlatformMode == SETUP_MODE && !Del) {\r
+ //\r
+ // If enroll PK in setup mode, need change to user mode.\r
+ //\r
+ Status = UpdatePlatformMode (USER_MODE);\r
+ } else if (mPlatformMode == USER_MODE && Del){\r
+ //\r
+ // If delete PK in user mode, need change to setup mode.\r
+ //\r
+ Status = UpdatePlatformMode (SETUP_MODE);\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Process variable with key exchange key for verification.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ This function may be invoked in SMM mode, and datasize and data are external input.\r
+ This function will do basic validation, before parse the data.\r
+ This function will parse the authentication carefully to avoid security issues, like\r
+ buffer overflow, integer overflow.\r
+ This function will check attribute carefully to avoid authentication bypass.\r
+\r
+ @param[in] VariableName Name of Variable to be found.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data found. If size is less than the\r
+ data, this value contains the required size.\r
+ @param[in] Attributes Attribute value of the variable.\r
+\r
+ @return EFI_INVALID_PARAMETER Invalid parameter.\r
+ @return EFI_SECURITY_VIOLATION The variable does NOT pass the validation\r
+ check carried out by the firmware.\r
+ @return EFI_SUCCESS Variable pass 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 UINT32 Attributes OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *Payload;\r
+ UINTN PayloadSize;\r
+\r
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0 ||\r
+ (Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) {\r
+ //\r
+ // DB, DBX and DBT should set EFI_VARIABLE_NON_VOLATILE attribute and should be a time-based\r
+ // authenticated variable.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ if (mPlatformMode == USER_MODE && !(InCustomMode() && UserPhysicalPresent())) {\r
+ //\r
+ // Time-based, verify against X509 Cert KEK.\r
+ //\r
+ return VerifyTimeBasedPayloadAndUpdate (\r
+ VariableName,\r
+ VendorGuid,\r
+ Data,\r
+ DataSize,\r
+ Attributes,\r
+ AuthVarTypeKek,\r
+ NULL\r
+ );\r
+ } else {\r
+ //\r
+ // If in setup mode or custom secure boot mode, no authentication needed.\r
+ //\r
+ Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data);\r
+ PayloadSize = DataSize - AUTHINFO2_SIZE (Data);\r
+\r
+ Status = CheckSignatureListFormat(VariableName, VendorGuid, Payload, PayloadSize);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = AuthServiceInternalUpdateVariableWithTimeStamp (\r
+ VariableName,\r
+ VendorGuid,\r
+ Payload,\r
+ PayloadSize,\r
+ Attributes,\r
+ &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (mPlatformMode != SETUP_MODE) {\r
+ Status = VendorKeyIsModified ();\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check if it is to delete auth variable.\r
+\r
+ @param[in] OrgAttributes Original attribute value of the variable.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] Attributes Attribute value of the variable.\r
+\r
+ @retval TRUE It is to delete auth variable.\r
+ @retval FALSE It is not to delete auth variable.\r
+\r
+**/\r
+BOOLEAN\r
+IsDeleteAuthVariable (\r
+ IN UINT32 OrgAttributes,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes\r
+ )\r
+{\r
+ BOOLEAN Del;\r
+ UINTN PayloadSize;\r
+\r
+ Del = FALSE;\r
+\r
+ //\r
+ // To delete a variable created with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS\r
+ // or the EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute,\r
+ // SetVariable must be used with attributes matching the existing variable\r
+ // and the DataSize set to the size of the AuthInfo descriptor.\r
+ //\r
+ if ((Attributes == OrgAttributes) &&\r
+ ((Attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) != 0)) {\r
+ if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {\r
+ PayloadSize = DataSize - AUTHINFO2_SIZE (Data);\r
+ if (PayloadSize == 0) {\r
+ Del = TRUE;\r
+ }\r
+ } else {\r
+ PayloadSize = DataSize - AUTHINFO_SIZE;\r
+ if (PayloadSize == 0) {\r
+ Del = TRUE;\r
+ }\r
+ }\r
+ }\r
+\r
+ return Del;\r
+}\r
+\r
+/**\r
+ Process variable with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS/EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set\r
+\r
+ Caution: This function may receive untrusted input.\r
+ This function may be invoked in SMM mode, and datasize and data are external input.\r
+ This function will do basic validation, before parse the data.\r
+ This function will parse the authentication carefully to avoid security issues, like\r
+ buffer overflow, integer overflow.\r
+ This function will check attribute carefully to avoid authentication bypass.\r
+\r
+ @param[in] VariableName Name of the variable.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] Attributes Attribute value of the variable.\r
+\r
+ @return EFI_INVALID_PARAMETER Invalid parameter.\r
+ @return EFI_WRITE_PROTECTED Variable is write-protected and needs authentication with\r
+ EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.\r
+ @return EFI_OUT_OF_RESOURCES The Database to save the public key is full.\r
+ @return 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
+ @return EFI_SUCCESS Variable is not write-protected or pass validation successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+ProcessVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes 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
+ UINT32 KeyIndex;\r
+ UINT64 MonotonicCount;\r
+ VARIABLE_ENTRY_CONSISTENCY VariableDataEntry;\r
+ UINT32 Index;\r
+ AUTH_VARIABLE_INFO OrgVariableInfo;\r
+\r
+ KeyIndex = 0;\r
+ CertData = NULL;\r
+ CertBlock = NULL;\r
+ PubKey = NULL;\r
+ IsDeletion = FALSE;\r
+ Status = EFI_SUCCESS;\r
+\r
+ ZeroMem (&OrgVariableInfo, sizeof (OrgVariableInfo));\r
+ Status = mAuthVarLibContextIn->FindVariable (\r
+ VariableName,\r
+ VendorGuid,\r
+ &OrgVariableInfo\r
+ );\r
+\r
+ if ((!EFI_ERROR (Status)) && IsDeleteAuthVariable (OrgVariableInfo.Attributes, Data, DataSize, Attributes) && UserPhysicalPresent()) {\r
+ //\r
+ // Allow the delete operation of common authenticated variable at user physical presence.\r
+ //\r
+ if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {\r
+ Status = DeleteCertsFromDb (VariableName, VendorGuid);\r
+ }\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ VariableName,\r
+ VendorGuid,\r
+ NULL,\r
+ 0,\r
+ 0\r
+ );\r
+ }\r
+ return Status;\r
+ }\r
+\r
+ if (NeedPhysicallyPresent (VariableName, VendorGuid) && !UserPhysicalPresent()) {\r
+ //\r
+ // This variable is protected, only physical present user could modify its value.\r
+ //\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+\r
+ //\r
+ // A time-based authenticated variable and a count-based authenticated variable\r
+ // can't be updated by each other.\r
+ //\r
+ if (OrgVariableInfo.Data != NULL) {\r
+ if (((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) &&\r
+ ((OrgVariableInfo.Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0)) {\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+\r
+ if (((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) &&\r
+ ((OrgVariableInfo.Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0)) {\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Process Time-based Authenticated variable.\r
+ //\r
+ if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {\r
+ return VerifyTimeBasedPayloadAndUpdate (\r
+ VariableName,\r
+ VendorGuid,\r
+ Data,\r
+ DataSize,\r
+ Attributes,\r
+ AuthVarTypePriv,\r
+ NULL\r
+ );\r
+ }\r
+\r
+ //\r
+ // Determine if first time SetVariable with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS.\r
+ //\r
+ if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {\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 (OrgVariableInfo.Data == NULL) {\r
+ IsFirstTime = TRUE;\r
+ } else if ((OrgVariableInfo.Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) {\r
+ IsFirstTime = TRUE;\r
+ } else {\r
+ KeyIndex = OrgVariableInfo.PubKeyIndex;\r
+ IsFirstTime = FALSE;\r
+ }\r
+ } else if ((OrgVariableInfo.Data != NULL) &&\r
+ ((OrgVariableInfo.Attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) != 0)\r
+ ) {\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 update variable as usual.\r
+ //\r
+ Status = AuthServiceInternalUpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes);\r
+ return Status;\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
+ //\r
+ // Update Monotonic Count value.\r
+ //\r
+ MonotonicCount = CertData->MonotonicCount;\r
+\r
+ if (!IsFirstTime) {\r
+ //\r
+ // 2 cases need to check here\r
+ // 1. Internal PubKey variable. PubKeyIndex is always 0\r
+ // 2. Other counter-based AuthVariable. Check input PubKey.\r
+ //\r
+ if (KeyIndex == 0) {\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+ for (Index = 0; Index < mPubKeyNumber; Index++) {\r
+ if (ReadUnaligned32 (&(((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + Index)->KeyIndex)) == KeyIndex) {\r
+ if (CompareMem (((AUTHVAR_KEY_DB_DATA *) mPubKeyStore + Index)->KeyData, PubKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {\r
+ break;\r
+ } else {\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+ }\r
+ }\r
+ if (Index == mPubKeyNumber) {\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\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 (MonotonicCount <= OrgVariableInfo.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 = VerifyCounterBasedPayload (Data, DataSize, PubKey);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Now, the signature has been verified!\r
+ //\r
+ if (IsFirstTime && !IsDeletion) {\r
+ VariableDataEntry.VariableSize = DataSize - AUTHINFO_SIZE;\r
+ VariableDataEntry.Guid = VendorGuid;\r
+ VariableDataEntry.Name = VariableName;\r
+\r
+ //\r
+ // Update public key database variable if need.\r
+ //\r
+ KeyIndex = AddPubKeyInStore (PubKey, &VariableDataEntry);\r
+ if (KeyIndex == 0) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Verification pass.\r
+ //\r
+ return AuthServiceInternalUpdateVariableWithMonotonicCount (VariableName, VendorGuid, (UINT8*)Data + AUTHINFO_SIZE, DataSize - AUTHINFO_SIZE, Attributes, KeyIndex, MonotonicCount);\r
+}\r
+\r
+/**\r
+ Filter out the duplicated EFI_SIGNATURE_DATA from the new data by comparing to the original data.\r
+\r
+ @param[in] Data Pointer to original EFI_SIGNATURE_LIST.\r
+ @param[in] DataSize Size of Data buffer.\r
+ @param[in, out] NewData Pointer to new EFI_SIGNATURE_LIST.\r
+ @param[in, out] NewDataSize Size of NewData buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+FilterSignatureList (\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN OUT VOID *NewData,\r
+ IN OUT UINTN *NewDataSize\r
+ )\r
+{\r
+ EFI_SIGNATURE_LIST *CertList;\r
+ EFI_SIGNATURE_DATA *Cert;\r
+ UINTN CertCount;\r
+ EFI_SIGNATURE_LIST *NewCertList;\r
+ EFI_SIGNATURE_DATA *NewCert;\r
+ UINTN NewCertCount;\r
+ UINTN Index;\r
+ UINTN Index2;\r
+ UINTN Size;\r
+ UINT8 *Tail;\r
+ UINTN CopiedCount;\r
+ UINTN SignatureListSize;\r
+ BOOLEAN IsNewCert;\r
+ UINT8 *TempData;\r
+ UINTN TempDataSize;\r
+ EFI_STATUS Status;\r
+\r
+ if (*NewDataSize == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ TempDataSize = *NewDataSize;\r
+ Status = mAuthVarLibContextIn->GetScratchBuffer (&TempDataSize, (VOID **) &TempData);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Tail = TempData;\r
+\r
+ NewCertList = (EFI_SIGNATURE_LIST *) NewData;\r
+ while ((*NewDataSize > 0) && (*NewDataSize >= NewCertList->SignatureListSize)) {\r
+ NewCert = (EFI_SIGNATURE_DATA *) ((UINT8 *) NewCertList + sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize);\r
+ NewCertCount = (NewCertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - NewCertList->SignatureHeaderSize) / NewCertList->SignatureSize;\r
+\r
+ CopiedCount = 0;\r
+ for (Index = 0; Index < NewCertCount; Index++) {\r
+ IsNewCert = TRUE;\r
+\r
+ Size = DataSize;\r
+ CertList = (EFI_SIGNATURE_LIST *) Data;\r
+ while ((Size > 0) && (Size >= CertList->SignatureListSize)) {\r
+ if (CompareGuid (&CertList->SignatureType, &NewCertList->SignatureType) &&\r
+ (CertList->SignatureSize == NewCertList->SignatureSize)) {\r
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
+ for (Index2 = 0; Index2 < CertCount; Index2++) {\r
+ //\r
+ // Iterate each Signature Data in this Signature List.\r
+ //\r
+ if (CompareMem (NewCert, Cert, CertList->SignatureSize) == 0) {\r
+ IsNewCert = FALSE;\r
+ break;\r
+ }\r
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);\r
+ }\r
+ }\r
+\r
+ if (!IsNewCert) {\r
+ break;\r
+ }\r
+ Size -= CertList->SignatureListSize;\r
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);\r
+ }\r
+\r
+ if (IsNewCert) {\r
+ //\r
+ // New EFI_SIGNATURE_DATA, keep it.\r
+ //\r
+ if (CopiedCount == 0) {\r
+ //\r
+ // Copy EFI_SIGNATURE_LIST header for only once.\r
+ //\r
+ CopyMem (Tail, NewCertList, sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize);\r
+ Tail = Tail + sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize;\r
+ }\r
+\r
+ CopyMem (Tail, NewCert, NewCertList->SignatureSize);\r
+ Tail += NewCertList->SignatureSize;\r
+ CopiedCount++;\r
+ }\r
+\r
+ NewCert = (EFI_SIGNATURE_DATA *) ((UINT8 *) NewCert + NewCertList->SignatureSize);\r
+ }\r
+\r
+ //\r
+ // Update SignatureListSize in the kept EFI_SIGNATURE_LIST.\r
+ //\r
+ if (CopiedCount != 0) {\r
+ SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize + (CopiedCount * NewCertList->SignatureSize);\r
+ CertList = (EFI_SIGNATURE_LIST *) (Tail - SignatureListSize);\r
+ CertList->SignatureListSize = (UINT32) SignatureListSize;\r
+ }\r
+\r
+ *NewDataSize -= NewCertList->SignatureListSize;\r
+ NewCertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) NewCertList + NewCertList->SignatureListSize);\r
+ }\r
+\r
+ TempDataSize = (Tail - (UINT8 *) TempData);\r
+\r
+ CopyMem (NewData, TempData, TempDataSize);\r
+ *NewDataSize = TempDataSize;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Compare two EFI_TIME data.\r
+\r
+\r
+ @param FirstTime A pointer to the first EFI_TIME data.\r
+ @param SecondTime A pointer to the second EFI_TIME data.\r
+\r
+ @retval TRUE The FirstTime is not later than the SecondTime.\r
+ @retval FALSE The FirstTime is later than the SecondTime.\r
+\r
+**/\r
+BOOLEAN\r
+AuthServiceInternalCompareTimeStamp (\r
+ IN EFI_TIME *FirstTime,\r
+ IN EFI_TIME *SecondTime\r
+ )\r
+{\r
+ if (FirstTime->Year != SecondTime->Year) {\r
+ return (BOOLEAN) (FirstTime->Year < SecondTime->Year);\r
+ } else if (FirstTime->Month != SecondTime->Month) {\r
+ return (BOOLEAN) (FirstTime->Month < SecondTime->Month);\r
+ } else if (FirstTime->Day != SecondTime->Day) {\r
+ return (BOOLEAN) (FirstTime->Day < SecondTime->Day);\r
+ } else if (FirstTime->Hour != SecondTime->Hour) {\r
+ return (BOOLEAN) (FirstTime->Hour < SecondTime->Hour);\r
+ } else if (FirstTime->Minute != SecondTime->Minute) {\r
+ return (BOOLEAN) (FirstTime->Minute < SecondTime->Minute);\r
+ }\r
+\r
+ return (BOOLEAN) (FirstTime->Second <= SecondTime->Second);\r
+}\r
+\r
+/**\r
+ Find matching signer's certificates for common authenticated variable\r
+ by corresponding VariableName and VendorGuid from "certdb".\r
+\r
+ The data format of "certdb":\r
+ //\r
+ // UINT32 CertDbListSize;\r
+ // /// AUTH_CERT_DB_DATA Certs1[];\r
+ // /// AUTH_CERT_DB_DATA Certs2[];\r
+ // /// ...\r
+ // /// AUTH_CERT_DB_DATA Certsn[];\r
+ //\r
+\r
+ @param[in] VariableName Name of authenticated Variable.\r
+ @param[in] VendorGuid Vendor GUID of authenticated Variable.\r
+ @param[in] Data Pointer to variable "certdb".\r
+ @param[in] DataSize Size of variable "certdb".\r
+ @param[out] CertOffset Offset of matching CertData, from starting of Data.\r
+ @param[out] CertDataSize Length of CertData in bytes.\r
+ @param[out] CertNodeOffset Offset of matching AUTH_CERT_DB_DATA , from\r
+ starting of Data.\r
+ @param[out] CertNodeSize Length of AUTH_CERT_DB_DATA in bytes.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_NOT_FOUND Fail to find matching certs.\r
+ @retval EFI_SUCCESS Find matching certs and output parameters.\r
+\r
+**/\r
+EFI_STATUS\r
+FindCertsFromDb (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN UINT8 *Data,\r
+ IN UINTN DataSize,\r
+ OUT UINT32 *CertOffset, OPTIONAL\r
+ OUT UINT32 *CertDataSize, OPTIONAL\r
+ OUT UINT32 *CertNodeOffset,OPTIONAL\r
+ OUT UINT32 *CertNodeSize OPTIONAL\r
+ )\r
+{\r
+ UINT32 Offset;\r
+ AUTH_CERT_DB_DATA *Ptr;\r
+ UINT32 CertSize;\r
+ UINT32 NameSize;\r
+ UINT32 NodeSize;\r
+ UINT32 CertDbListSize;\r
+\r
+ if ((VariableName == NULL) || (VendorGuid == NULL) || (Data == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Check whether DataSize matches recorded CertDbListSize.\r
+ //\r
+ if (DataSize < sizeof (UINT32)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ CertDbListSize = ReadUnaligned32 ((UINT32 *) Data);\r
+\r
+ if (CertDbListSize != (UINT32) DataSize) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Offset = sizeof (UINT32);\r
+\r
+ //\r
+ // Get corresponding certificates by VendorGuid and VariableName.\r
+ //\r
+ while (Offset < (UINT32) DataSize) {\r
+ Ptr = (AUTH_CERT_DB_DATA *) (Data + Offset);\r
+ //\r
+ // Check whether VendorGuid matches.\r
+ //\r
+ if (CompareGuid (&Ptr->VendorGuid, VendorGuid)) {\r
+ NodeSize = ReadUnaligned32 (&Ptr->CertNodeSize);\r
+ NameSize = ReadUnaligned32 (&Ptr->NameSize);\r
+ CertSize = ReadUnaligned32 (&Ptr->CertDataSize);\r
+\r
+ if (NodeSize != sizeof (EFI_GUID) + sizeof (UINT32) * 3 + CertSize +\r
+ sizeof (CHAR16) * NameSize) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Offset = Offset + sizeof (EFI_GUID) + sizeof (UINT32) * 3;\r
+ //\r
+ // Check whether VariableName matches.\r
+ //\r
+ if ((NameSize == StrLen (VariableName)) &&\r
+ (CompareMem (Data + Offset, VariableName, NameSize * sizeof (CHAR16)) == 0)) {\r
+ Offset = Offset + NameSize * sizeof (CHAR16);\r
+\r
+ if (CertOffset != NULL) {\r
+ *CertOffset = Offset;\r
+ }\r
+\r
+ if (CertDataSize != NULL) {\r
+ *CertDataSize = CertSize;\r
+ }\r
+\r
+ if (CertNodeOffset != NULL) {\r
+ *CertNodeOffset = (UINT32) ((UINT8 *) Ptr - Data);\r
+ }\r
+\r
+ if (CertNodeSize != NULL) {\r
+ *CertNodeSize = NodeSize;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ Offset = Offset + NameSize * sizeof (CHAR16) + CertSize;\r
+ }\r
+ } else {\r
+ NodeSize = ReadUnaligned32 (&Ptr->CertNodeSize);\r
+ Offset = Offset + NodeSize;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Retrieve signer's certificates for common authenticated variable\r
+ by corresponding VariableName and VendorGuid from "certdb".\r
+\r
+ @param[in] VariableName Name of authenticated Variable.\r
+ @param[in] VendorGuid Vendor GUID of authenticated Variable.\r
+ @param[out] CertData Pointer to signer's certificates.\r
+ @param[out] CertDataSize Length of CertData in bytes.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_NOT_FOUND Fail to find "certdb" or matching certs.\r
+ @retval EFI_SUCCESS Get signer's certificates successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+GetCertsFromDb (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ OUT UINT8 **CertData,\r
+ OUT UINT32 *CertDataSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *Data;\r
+ UINTN DataSize;\r
+ UINT32 CertOffset;\r
+\r
+ if ((VariableName == NULL) || (VendorGuid == NULL) || (CertData == NULL) || (CertDataSize == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Get variable "certdb".\r
+ //\r
+ Status = AuthServiceInternalFindVariable (\r
+ EFI_CERT_DB_NAME,\r
+ &gEfiCertDbGuid,\r
+ (VOID **) &Data,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if ((DataSize == 0) || (Data == NULL)) {\r
+ ASSERT (FALSE);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = FindCertsFromDb (\r
+ VariableName,\r
+ VendorGuid,\r
+ Data,\r
+ DataSize,\r
+ &CertOffset,\r
+ CertDataSize,\r
+ NULL,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ *CertData = Data + CertOffset;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Delete matching signer's certificates when deleting common authenticated\r
+ variable by corresponding VariableName and VendorGuid from "certdb".\r
+\r
+ @param[in] VariableName Name of authenticated Variable.\r
+ @param[in] VendorGuid Vendor GUID of authenticated Variable.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_NOT_FOUND Fail to find "certdb" or matching certs.\r
+ @retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources.\r
+ @retval EFI_SUCCESS The operation is completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DeleteCertsFromDb (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *Data;\r
+ UINTN DataSize;\r
+ UINT32 VarAttr;\r
+ UINT32 CertNodeOffset;\r
+ UINT32 CertNodeSize;\r
+ UINT8 *NewCertDb;\r
+ UINT32 NewCertDbSize;\r
+\r
+ if ((VariableName == NULL) || (VendorGuid == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Get variable "certdb".\r
+ //\r
+ Status = AuthServiceInternalFindVariable (\r
+ EFI_CERT_DB_NAME,\r
+ &gEfiCertDbGuid,\r
+ (VOID **) &Data,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if ((DataSize == 0) || (Data == NULL)) {\r
+ ASSERT (FALSE);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (DataSize == sizeof (UINT32)) {\r
+ //\r
+ // There is no certs in certdb.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Get corresponding cert node from certdb.\r
+ //\r
+ Status = FindCertsFromDb (\r
+ VariableName,\r
+ VendorGuid,\r
+ Data,\r
+ DataSize,\r
+ NULL,\r
+ NULL,\r
+ &CertNodeOffset,\r
+ &CertNodeSize\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (DataSize < (CertNodeOffset + CertNodeSize)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Construct new data content of variable "certdb".\r
+ //\r
+ NewCertDbSize = (UINT32) DataSize - CertNodeSize;\r
+ NewCertDb = (UINT8*) mCertDbStore;\r
+\r
+ //\r
+ // Copy the DB entries before deleting node.\r
+ //\r
+ CopyMem (NewCertDb, Data, CertNodeOffset);\r
+ //\r
+ // Update CertDbListSize.\r
+ //\r
+ CopyMem (NewCertDb, &NewCertDbSize, sizeof (UINT32));\r
+ //\r
+ // Copy the DB entries after deleting node.\r
+ //\r
+ if (DataSize > (CertNodeOffset + CertNodeSize)) {\r
+ CopyMem (\r
+ NewCertDb + CertNodeOffset,\r
+ Data + CertNodeOffset + CertNodeSize,\r
+ DataSize - CertNodeOffset - CertNodeSize\r
+ );\r
+ }\r
+\r
+ //\r
+ // Set "certdb".\r
+ //\r
+ VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_CERT_DB_NAME,\r
+ &gEfiCertDbGuid,\r
+ NewCertDb,\r
+ NewCertDbSize,\r
+ VarAttr\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Insert signer's certificates for common authenticated variable with VariableName\r
+ and VendorGuid in AUTH_CERT_DB_DATA to "certdb".\r
+\r
+ @param[in] VariableName Name of authenticated Variable.\r
+ @param[in] VendorGuid Vendor GUID of authenticated Variable.\r
+ @param[in] CertData Pointer to signer's certificates.\r
+ @param[in] CertDataSize Length of CertData in bytes.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_ACCESS_DENIED An AUTH_CERT_DB_DATA entry with same VariableName\r
+ and VendorGuid already exists.\r
+ @retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources.\r
+ @retval EFI_SUCCESS Insert an AUTH_CERT_DB_DATA entry to "certdb"\r
+\r
+**/\r
+EFI_STATUS\r
+InsertCertsToDb (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN UINT8 *CertData,\r
+ IN UINTN CertDataSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *Data;\r
+ UINTN DataSize;\r
+ UINT32 VarAttr;\r
+ UINT8 *NewCertDb;\r
+ UINT32 NewCertDbSize;\r
+ UINT32 CertNodeSize;\r
+ UINT32 NameSize;\r
+ AUTH_CERT_DB_DATA *Ptr;\r
+\r
+ if ((VariableName == NULL) || (VendorGuid == NULL) || (CertData == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Get variable "certdb".\r
+ //\r
+ Status = AuthServiceInternalFindVariable (\r
+ EFI_CERT_DB_NAME,\r
+ &gEfiCertDbGuid,\r
+ (VOID **) &Data,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if ((DataSize == 0) || (Data == NULL)) {\r
+ ASSERT (FALSE);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Find whether matching cert node already exists in "certdb".\r
+ // If yes return error.\r
+ //\r
+ Status = FindCertsFromDb (\r
+ VariableName,\r
+ VendorGuid,\r
+ Data,\r
+ DataSize,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
+ NULL\r
+ );\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ ASSERT (FALSE);\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ //\r
+ // Construct new data content of variable "certdb".\r
+ //\r
+ NameSize = (UINT32) StrLen (VariableName);\r
+ CertNodeSize = sizeof (AUTH_CERT_DB_DATA) + (UINT32) CertDataSize + NameSize * sizeof (CHAR16);\r
+ NewCertDbSize = (UINT32) DataSize + CertNodeSize;\r
+ if (NewCertDbSize > mMaxCertDbSize) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ NewCertDb = (UINT8*) mCertDbStore;\r
+\r
+ //\r
+ // Copy the DB entries before inserting node.\r
+ //\r
+ CopyMem (NewCertDb, Data, DataSize);\r
+ //\r
+ // Update CertDbListSize.\r
+ //\r
+ CopyMem (NewCertDb, &NewCertDbSize, sizeof (UINT32));\r
+ //\r
+ // Construct new cert node.\r
+ //\r
+ Ptr = (AUTH_CERT_DB_DATA *) (NewCertDb + DataSize);\r
+ CopyGuid (&Ptr->VendorGuid, VendorGuid);\r
+ CopyMem (&Ptr->CertNodeSize, &CertNodeSize, sizeof (UINT32));\r
+ CopyMem (&Ptr->NameSize, &NameSize, sizeof (UINT32));\r
+ CopyMem (&Ptr->CertDataSize, &CertDataSize, sizeof (UINT32));\r
+\r
+ CopyMem (\r
+ (UINT8 *) Ptr + sizeof (AUTH_CERT_DB_DATA),\r
+ VariableName,\r
+ NameSize * sizeof (CHAR16)\r
+ );\r
+\r
+ CopyMem (\r
+ (UINT8 *) Ptr + sizeof (AUTH_CERT_DB_DATA) + NameSize * sizeof (CHAR16),\r
+ CertData,\r
+ CertDataSize\r
+ );\r
+\r
+ //\r
+ // Set "certdb".\r
+ //\r
+ VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_CERT_DB_NAME,\r
+ &gEfiCertDbGuid,\r
+ NewCertDb,\r
+ NewCertDbSize,\r
+ VarAttr\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set\r
+\r
+ Caution: This function may receive untrusted input.\r
+ This function may be invoked in SMM mode, and datasize and data are external input.\r
+ This function will do basic validation, before parse the data.\r
+ This function will parse the authentication carefully to avoid security issues, like\r
+ buffer overflow, integer overflow.\r
+\r
+ @param[in] VariableName Name of Variable to be found.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data found. If size is less than the\r
+ data, this value contains the required size.\r
+ @param[in] Attributes Attribute value of the variable.\r
+ @param[in] AuthVarType Verify against PK, KEK database, private database or certificate in data payload.\r
+ @param[in] OrgTimeStamp Pointer to original time stamp,\r
+ original variable is not found if NULL.\r
+ @param[out] VarPayloadPtr Pointer to variable payload address.\r
+ @param[out] VarPayloadSize Pointer to variable payload size.\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_OUT_OF_RESOURCES Failed to process variable due to lack\r
+ of resources.\r
+ @retval EFI_SUCCESS Variable pass validation successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+VerifyTimeBasedPayload (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes,\r
+ IN AUTHVAR_TYPE AuthVarType,\r
+ IN EFI_TIME *OrgTimeStamp,\r
+ OUT UINT8 **VarPayloadPtr,\r
+ OUT UINTN *VarPayloadSize\r
+ )\r
+{\r
+ EFI_VARIABLE_AUTHENTICATION_2 *CertData;\r
+ UINT8 *SigData;\r
+ UINT32 SigDataSize;\r
+ UINT8 *PayloadPtr;\r
+ UINTN PayloadSize;\r
+ UINT32 Attr;\r
+ BOOLEAN VerifyStatus;\r
+ EFI_STATUS Status;\r
+ EFI_SIGNATURE_LIST *CertList;\r
+ EFI_SIGNATURE_DATA *Cert;\r
+ UINTN Index;\r
+ UINTN CertCount;\r
+ UINT32 KekDataSize;\r
+ UINT8 *NewData;\r
+ UINTN NewDataSize;\r
+ UINT8 *Buffer;\r
+ UINTN Length;\r
+ UINT8 *RootCert;\r
+ UINTN RootCertSize;\r
+ UINT8 *SignerCerts;\r
+ UINTN CertStackSize;\r
+ UINT8 *CertsInCertDb;\r
+ UINT32 CertsSizeinDb;\r
+\r
+ VerifyStatus = FALSE;\r
+ CertData = NULL;\r
+ NewData = NULL;\r
+ Attr = Attributes;\r
+ SignerCerts = NULL;\r
+ RootCert = NULL;\r
+ CertsInCertDb = NULL;\r
+\r
+ //\r
+ // When the attribute EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS is\r
+ // set, then the Data buffer shall begin with an instance of a complete (and serialized)\r
+ // EFI_VARIABLE_AUTHENTICATION_2 descriptor. The descriptor shall be followed by the new\r
+ // variable value and DataSize shall reflect the combined size of the descriptor and the new\r
+ // variable value. The authentication descriptor is not part of the variable data and is not\r
+ // returned by subsequent calls to GetVariable().\r
+ //\r
+ CertData = (EFI_VARIABLE_AUTHENTICATION_2 *) Data;\r
+\r
+ //\r
+ // Verify that Pad1, Nanosecond, TimeZone, Daylight and Pad2 components of the\r
+ // TimeStamp value are set to zero.\r
+ //\r
+ if ((CertData->TimeStamp.Pad1 != 0) ||\r
+ (CertData->TimeStamp.Nanosecond != 0) ||\r
+ (CertData->TimeStamp.TimeZone != 0) ||\r
+ (CertData->TimeStamp.Daylight != 0) ||\r
+ (CertData->TimeStamp.Pad2 != 0)) {\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+\r
+ if ((OrgTimeStamp != NULL) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {\r
+ if (AuthServiceInternalCompareTimeStamp (&CertData->TimeStamp, OrgTimeStamp)) {\r
+ //\r
+ // TimeStamp check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.\r
+ //\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+ }\r
+\r
+ //\r
+ // wCertificateType should be WIN_CERT_TYPE_EFI_GUID.\r
+ // Cert type should be EFI_CERT_TYPE_PKCS7_GUID.\r
+ //\r
+ if ((CertData->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) ||\r
+ !CompareGuid (&CertData->AuthInfo.CertType, &gEfiCertPkcs7Guid)) {\r
+ //\r
+ // Invalid AuthInfo type, return EFI_SECURITY_VIOLATION.\r
+ //\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+\r
+ //\r
+ // Find out Pkcs7 SignedData which follows the EFI_VARIABLE_AUTHENTICATION_2 descriptor.\r
+ // AuthInfo.Hdr.dwLength is the length of the entire certificate, including the length of the header.\r
+ //\r
+ SigData = CertData->AuthInfo.CertData;\r
+ SigDataSize = CertData->AuthInfo.Hdr.dwLength - (UINT32) (OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData));\r
+\r
+ //\r
+ // Find out the new data payload which follows Pkcs7 SignedData directly.\r
+ //\r
+ PayloadPtr = SigData + SigDataSize;\r
+ PayloadSize = DataSize - OFFSET_OF_AUTHINFO2_CERT_DATA - (UINTN) SigDataSize;\r
+\r
+ //\r
+ // Construct a serialization buffer of the values of the VariableName, VendorGuid and Attributes\r
+ // parameters of the SetVariable() call and the TimeStamp component of the\r
+ // EFI_VARIABLE_AUTHENTICATION_2 descriptor followed by the variable's new value\r
+ // i.e. (VariableName, VendorGuid, Attributes, TimeStamp, Data)\r
+ //\r
+ NewDataSize = PayloadSize + sizeof (EFI_TIME) + sizeof (UINT32) +\r
+ sizeof (EFI_GUID) + StrSize (VariableName) - sizeof (CHAR16);\r
+\r
+ //\r
+ // Here is to reuse scratch data area(at the end of volatile variable store)\r
+ // to reduce SMRAM consumption for SMM variable driver.\r
+ // The scratch buffer is enough to hold the serialized data and safe to use,\r
+ // because it is only used at here to do verification temporarily first\r
+ // and then used in UpdateVariable() for a time based auth variable set.\r
+ //\r
+ Status = mAuthVarLibContextIn->GetScratchBuffer (&NewDataSize, (VOID **) &NewData);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Buffer = NewData;\r
+ Length = StrLen (VariableName) * sizeof (CHAR16);\r
+ CopyMem (Buffer, VariableName, Length);\r
+ Buffer += Length;\r
+\r
+ Length = sizeof (EFI_GUID);\r
+ CopyMem (Buffer, VendorGuid, Length);\r
+ Buffer += Length;\r
+\r
+ Length = sizeof (UINT32);\r
+ CopyMem (Buffer, &Attr, Length);\r
+ Buffer += Length;\r
+\r
+ Length = sizeof (EFI_TIME);\r
+ CopyMem (Buffer, &CertData->TimeStamp, Length);\r
+ Buffer += Length;\r
+\r
+ CopyMem (Buffer, PayloadPtr, PayloadSize);\r
+\r
+ if (AuthVarType == AuthVarTypePk) {\r
+ //\r
+ // Verify that the signature has been made with the current Platform Key (no chaining for PK).\r
+ // First, get signer's certificates from SignedData.\r
+ //\r
+ VerifyStatus = Pkcs7GetSigners (\r
+ SigData,\r
+ SigDataSize,\r
+ &SignerCerts,\r
+ &CertStackSize,\r
+ &RootCert,\r
+ &RootCertSize\r
+ );\r
+ if (!VerifyStatus) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Second, get the current platform key from variable. Check whether it's identical with signer's certificates\r
+ // in SignedData. If not, return error immediately.\r
+ //\r
+ Status = AuthServiceInternalFindVariable (\r
+ EFI_PLATFORM_KEY_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ &Data,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ VerifyStatus = FALSE;\r
+ goto Exit;\r
+ }\r
+ CertList = (EFI_SIGNATURE_LIST *) Data;\r
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
+ if ((RootCertSize != (CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1))) ||\r
+ (CompareMem (Cert->SignatureData, RootCert, RootCertSize) != 0)) {\r
+ VerifyStatus = FALSE;\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Verify Pkcs7 SignedData via Pkcs7Verify library.\r
+ //\r
+ VerifyStatus = Pkcs7Verify (\r
+ SigData,\r
+ SigDataSize,\r
+ RootCert,\r
+ RootCertSize,\r
+ NewData,\r
+ NewDataSize\r
+ );\r
+\r
+ } else if (AuthVarType == AuthVarTypeKek) {\r
+\r
+ //\r
+ // Get KEK database from variable.\r
+ //\r
+ Status = AuthServiceInternalFindVariable (\r
+ EFI_KEY_EXCHANGE_KEY_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ &Data,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Ready to verify Pkcs7 SignedData. Go through KEK Signature Database to find out X.509 CertList.\r
+ //\r
+ KekDataSize = (UINT32) DataSize;\r
+ CertList = (EFI_SIGNATURE_LIST *) Data;\r
+ while ((KekDataSize > 0) && (KekDataSize >= CertList->SignatureListSize)) {\r
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {\r
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
+ for (Index = 0; Index < CertCount; Index++) {\r
+ //\r
+ // Iterate each Signature Data Node within this CertList for a verify\r
+ //\r
+ RootCert = Cert->SignatureData;\r
+ RootCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1);\r
+\r
+ //\r
+ // Verify Pkcs7 SignedData via Pkcs7Verify library.\r
+ //\r
+ VerifyStatus = Pkcs7Verify (\r
+ SigData,\r
+ SigDataSize,\r
+ RootCert,\r
+ RootCertSize,\r
+ NewData,\r
+ NewDataSize\r
+ );\r
+ if (VerifyStatus) {\r
+ goto Exit;\r
+ }\r
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);\r
+ }\r
+ }\r
+ KekDataSize -= CertList->SignatureListSize;\r
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);\r
+ }\r
+ } else if (AuthVarType == AuthVarTypePriv) {\r
+\r
+ //\r
+ // Process common authenticated variable except PK/KEK/DB/DBX/DBT.\r
+ // Get signer's certificates from SignedData.\r
+ //\r
+ VerifyStatus = Pkcs7GetSigners (\r
+ SigData,\r
+ SigDataSize,\r
+ &SignerCerts,\r
+ &CertStackSize,\r
+ &RootCert,\r
+ &RootCertSize\r
+ );\r
+ if (!VerifyStatus) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Get previously stored signer's certificates from certdb for existing\r
+ // variable. Check whether they are identical with signer's certificates\r
+ // in SignedData. If not, return error immediately.\r
+ //\r
+ if (OrgTimeStamp != NULL) {\r
+ VerifyStatus = FALSE;\r
+\r
+ Status = GetCertsFromDb (VariableName, VendorGuid, &CertsInCertDb, &CertsSizeinDb);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ if ((CertStackSize != CertsSizeinDb) ||\r
+ (CompareMem (SignerCerts, CertsInCertDb, CertsSizeinDb) != 0)) {\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ VerifyStatus = Pkcs7Verify (\r
+ SigData,\r
+ SigDataSize,\r
+ RootCert,\r
+ RootCertSize,\r
+ NewData,\r
+ NewDataSize\r
+ );\r
+ if (!VerifyStatus) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Delete signer's certificates when delete the common authenticated variable.\r
+ //\r
+ if ((PayloadSize == 0) && (OrgTimeStamp != NULL) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {\r
+ Status = DeleteCertsFromDb (VariableName, VendorGuid);\r
+ if (EFI_ERROR (Status)) {\r
+ VerifyStatus = FALSE;\r
+ goto Exit;\r
+ }\r
+ } else if ((OrgTimeStamp == NULL) && (PayloadSize != 0)) {\r
+ //\r
+ // Insert signer's certificates when adding a new common authenticated variable.\r
+ //\r
+ Status = InsertCertsToDb (VariableName, VendorGuid, SignerCerts, CertStackSize);\r
+ if (EFI_ERROR (Status)) {\r
+ VerifyStatus = FALSE;\r
+ goto Exit;\r
+ }\r
+ }\r
+ } else if (AuthVarType == AuthVarTypePayload) {\r
+ CertList = (EFI_SIGNATURE_LIST *) PayloadPtr;\r
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
+ RootCert = Cert->SignatureData;\r
+ RootCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1);\r
+ //\r
+ // Verify Pkcs7 SignedData via Pkcs7Verify library.\r
+ //\r
+ VerifyStatus = Pkcs7Verify (\r
+ SigData,\r
+ SigDataSize,\r
+ RootCert,\r
+ RootCertSize,\r
+ NewData,\r
+ NewDataSize\r
+ );\r
+ } else {\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+\r
+Exit:\r
+\r
+ if (AuthVarType == AuthVarTypePk || AuthVarType == AuthVarTypePriv) {\r
+ Pkcs7FreeSigners (RootCert);\r
+ Pkcs7FreeSigners (SignerCerts);\r
+ }\r
+\r
+ if (!VerifyStatus) {\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+\r
+ Status = CheckSignatureListFormat(VariableName, VendorGuid, PayloadPtr, PayloadSize);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ *VarPayloadPtr = PayloadPtr;\r
+ *VarPayloadSize = PayloadSize;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set\r
+\r
+ Caution: This function may receive untrusted input.\r
+ This function may be invoked in SMM mode, and datasize and data are external input.\r
+ This function will do basic validation, before parse the data.\r
+ This function will parse the authentication carefully to avoid security issues, like\r
+ buffer overflow, integer overflow.\r
+\r
+ @param[in] VariableName Name of Variable to be found.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data found. If size is less than the\r
+ data, this value contains the required size.\r
+ @param[in] Attributes Attribute value of the variable.\r
+ @param[in] AuthVarType Verify against PK, KEK database, private database or certificate in data payload.\r
+ @param[out] VarDel Delete the variable or not.\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_OUT_OF_RESOURCES Failed to process variable due to lack\r
+ of resources.\r
+ @retval EFI_SUCCESS Variable pass validation successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+VerifyTimeBasedPayloadAndUpdate (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes,\r
+ IN AUTHVAR_TYPE AuthVarType,\r
+ OUT BOOLEAN *VarDel\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS FindStatus;\r
+ UINT8 *PayloadPtr;\r
+ UINTN PayloadSize;\r
+ EFI_VARIABLE_AUTHENTICATION_2 *CertData;\r
+ AUTH_VARIABLE_INFO OrgVariableInfo;\r
+\r
+ ZeroMem (&OrgVariableInfo, sizeof (OrgVariableInfo));\r
+ FindStatus = mAuthVarLibContextIn->FindVariable (\r
+ VariableName,\r
+ VendorGuid,\r
+ &OrgVariableInfo\r
+ );\r
+\r
+ Status = VerifyTimeBasedPayload (\r
+ VariableName,\r
+ VendorGuid,\r
+ Data,\r
+ DataSize,\r
+ Attributes,\r
+ AuthVarType,\r
+ (!EFI_ERROR (FindStatus)) ? OrgVariableInfo.TimeStamp : NULL,\r
+ &PayloadPtr,\r
+ &PayloadSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if ((PayloadSize == 0) && (VarDel != NULL)) {\r
+ *VarDel = TRUE;\r
+ }\r
+\r
+ CertData = (EFI_VARIABLE_AUTHENTICATION_2 *) Data;\r
+\r
+ //\r
+ // Final step: Update/Append Variable if it pass Pkcs7Verify\r
+ //\r
+ return AuthServiceInternalUpdateVariableWithTimeStamp (\r
+ VariableName,\r
+ VendorGuid,\r
+ PayloadPtr,\r
+ PayloadSize,\r
+ Attributes,\r
+ &CertData->TimeStamp\r
+ );\r
+}\r
--- /dev/null
+/** @file\r
+ The internal header file includes the common header files, defines\r
+ internal structure and functions used by AuthService module.\r
+\r
+ Caution: This module requires additional review when modified.\r
+ This driver will have external input - variable data. It may be input in SMM mode.\r
+ This external input must be validated carefully to avoid security issue like\r
+ buffer overflow, integer overflow.\r
+ Variable attribute should also be checked to avoid authentication bypass.\r
+ The whole SMM authentication variable design relies on the integrity of flash part and SMM.\r
+ which is assumed to be protected by platform. All variable code and metadata in flash/SMM Memory\r
+ may not be modified without authorization. If platform fails to protect these resources,\r
+ the authentication service provided in this driver will be broken, and the behavior is undefined.\r
+\r
+Copyright (c) 2009 - 2015, 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
+#ifndef _AUTHSERVICE_INTERNAL_H_\r
+#define _AUTHSERVICE_INTERNAL_H_\r
+\r
+#include <Library/AuthVariableLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/BaseCryptLib.h>\r
+#include <Library/PlatformSecureLib.h>\r
+\r
+#include <Guid/AuthenticatedVariableFormat.h>\r
+#include <Guid/ImageAuthentication.h>\r
+\r
+///\r
+/// Struct to record signature requirement defined by UEFI spec.\r
+/// For SigHeaderSize and SigDataSize, ((UINT32) ~0) means NO exact length requirement for this field.\r
+///\r
+typedef struct {\r
+ EFI_GUID SigType;\r
+ // Expected SignatureHeader size in Bytes.\r
+ UINT32 SigHeaderSize;\r
+ // Expected SignatureData size in Bytes.\r
+ UINT32 SigDataSize;\r
+} EFI_SIGNATURE_ITEM;\r
+\r
+typedef enum {\r
+ AuthVarTypePk,\r
+ AuthVarTypeKek,\r
+ AuthVarTypePriv,\r
+ AuthVarTypePayload\r
+} AUTHVAR_TYPE;\r
+\r
+///\r
+/// "AuthVarKeyDatabase" variable for the Public Key store\r
+/// of variables with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.\r
+///\r
+/// GUID: gEfiAuthenticatedVariableGuid\r
+///\r
+/// We need maintain atomicity.\r
+///\r
+/// Format:\r
+/// +----------------------------+\r
+/// | AUTHVAR_KEY_DB_DATA | <-- First AuthVarKey\r
+/// +----------------------------+\r
+/// | ...... |\r
+/// +----------------------------+\r
+/// | AUTHVAR_KEY_DB_DATA | <-- Last AuthKey\r
+/// +----------------------------+\r
+///\r
+#define AUTHVAR_KEYDB_NAME L"AuthVarKeyDatabase"\r
+\r
+#define EFI_CERT_TYPE_RSA2048_SHA256_SIZE 256\r
+#define EFI_CERT_TYPE_RSA2048_SIZE 256\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+ UINT32 KeyIndex;\r
+ UINT8 KeyData[EFI_CERT_TYPE_RSA2048_SIZE];\r
+} AUTHVAR_KEY_DB_DATA;\r
+#pragma pack()\r
+\r
+///\r
+/// "certdb" variable stores the signer's certificates for non PK/KEK/DB/DBX\r
+/// variables with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set.\r
+///\r
+/// GUID: gEfiCertDbGuid\r
+///\r
+/// We need maintain atomicity.\r
+///\r
+/// Format:\r
+/// +----------------------------+\r
+/// | UINT32 | <-- CertDbListSize, including this UINT32\r
+/// +----------------------------+\r
+/// | AUTH_CERT_DB_DATA | <-- First CERT\r
+/// +----------------------------+\r
+/// | ........ |\r
+/// +----------------------------+\r
+/// | AUTH_CERT_DB_DATA | <-- Last CERT\r
+/// +----------------------------+\r
+///\r
+#define EFI_CERT_DB_NAME L"certdb"\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+ EFI_GUID VendorGuid;\r
+ UINT32 CertNodeSize;\r
+ UINT32 NameSize;\r
+ UINT32 CertDataSize;\r
+ /// CHAR16 VariableName[NameSize];\r
+ /// UINT8 CertData[CertDataSize];\r
+} AUTH_CERT_DB_DATA;\r
+#pragma pack()\r
+\r
+extern UINT8 *mPubKeyStore;\r
+extern UINT32 mPubKeyNumber;\r
+extern UINT32 mMaxKeyNumber;\r
+extern UINT32 mMaxKeyDbSize;\r
+extern UINT8 *mCertDbStore;\r
+extern UINT32 mMaxCertDbSize;\r
+extern UINT32 mPlatformMode;\r
+extern UINT8 mVendorKeyState;\r
+\r
+extern VOID *mHashCtx;\r
+\r
+extern AUTH_VAR_LIB_CONTEXT_IN *mAuthVarLibContextIn;\r
+\r
+/**\r
+ Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set\r
+\r
+ Caution: This function may receive untrusted input.\r
+ This function may be invoked in SMM mode, and datasize and data are external input.\r
+ This function will do basic validation, before parse the data.\r
+ This function will parse the authentication carefully to avoid security issues, like\r
+ buffer overflow, integer overflow.\r
+\r
+ @param[in] VariableName Name of Variable to be found.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data found. If size is less than the\r
+ data, this value contains the required size.\r
+ @param[in] Attributes Attribute value of the variable.\r
+ @param[in] AuthVarType Verify against PK, KEK database, private database or certificate in data payload.\r
+ @param[out] VarDel Delete the variable or not.\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_OUT_OF_RESOURCES Failed to process variable due to lack\r
+ of resources.\r
+ @retval EFI_SUCCESS Variable pass validation successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+VerifyTimeBasedPayloadAndUpdate (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes,\r
+ IN AUTHVAR_TYPE AuthVarType,\r
+ OUT BOOLEAN *VarDel\r
+ );\r
+\r
+/**\r
+ Delete matching signer's certificates when deleting common authenticated\r
+ variable by corresponding VariableName and VendorGuid from "certdb".\r
+\r
+ @param[in] VariableName Name of authenticated Variable.\r
+ @param[in] VendorGuid Vendor GUID of authenticated Variable.\r
+\r
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+ @retval EFI_NOT_FOUND Fail to find "certdb" or matching certs.\r
+ @retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources.\r
+ @retval EFI_SUCCESS The operation is completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+DeleteCertsFromDb (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid\r
+ );\r
+\r
+/**\r
+ Filter out the duplicated EFI_SIGNATURE_DATA from the new data by comparing to the original data.\r
+\r
+ @param[in] Data Pointer to original EFI_SIGNATURE_LIST.\r
+ @param[in] DataSize Size of Data buffer.\r
+ @param[in, out] NewData Pointer to new EFI_SIGNATURE_LIST.\r
+ @param[in, out] NewDataSize Size of NewData buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+FilterSignatureList (\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN OUT VOID *NewData,\r
+ IN OUT UINTN *NewDataSize\r
+ );\r
+\r
+/**\r
+ Process variable with platform key for verification.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ This function may be invoked in SMM mode, and datasize and data are external input.\r
+ This function will do basic validation, before parse the data.\r
+ This function will parse the authentication carefully to avoid security issues, like\r
+ buffer overflow, integer overflow.\r
+ This function will check attribute carefully to avoid authentication bypass.\r
+\r
+ @param[in] VariableName Name of Variable to be found.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data found. If size is less than the\r
+ data, this value contains the required size.\r
+ @param[in] Attributes Attribute value of the variable\r
+ @param[in] IsPk Indicate whether it is to process pk.\r
+\r
+ @return EFI_INVALID_PARAMETER Invalid parameter.\r
+ @return EFI_SECURITY_VIOLATION The variable does NOT pass the validation.\r
+ check carried out by the firmware.\r
+ @return EFI_SUCCESS 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 UINT32 Attributes OPTIONAL,\r
+ IN BOOLEAN IsPk\r
+ );\r
+\r
+/**\r
+ Process variable with key exchange key for verification.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ This function may be invoked in SMM mode, and datasize and data are external input.\r
+ This function will do basic validation, before parse the data.\r
+ This function will parse the authentication carefully to avoid security issues, like\r
+ buffer overflow, integer overflow.\r
+ This function will check attribute carefully to avoid authentication bypass.\r
+\r
+ @param[in] VariableName Name of Variable to be found.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data found. If size is less than the\r
+ data, this value contains the required size.\r
+ @param[in] Attributes Attribute value of the variable.\r
+\r
+ @return EFI_INVALID_PARAMETER Invalid parameter.\r
+ @return EFI_SECURITY_VIOLATION The variable does NOT pass the validation\r
+ check carried out by the firmware.\r
+ @return EFI_SUCCESS Variable pass 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 UINT32 Attributes OPTIONAL\r
+ );\r
+\r
+/**\r
+ Process variable with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS/EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set\r
+\r
+ Caution: This function may receive untrusted input.\r
+ This function may be invoked in SMM mode, and datasize and data are external input.\r
+ This function will do basic validation, before parse the data.\r
+ This function will parse the authentication carefully to avoid security issues, like\r
+ buffer overflow, integer overflow.\r
+ This function will check attribute carefully to avoid authentication bypass.\r
+\r
+ @param[in] VariableName Name of the variable.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] Attributes Attribute value of the variable.\r
+\r
+ @return EFI_INVALID_PARAMETER Invalid parameter.\r
+ @return EFI_WRITE_PROTECTED Variable is write-protected and needs authentication with\r
+ EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set.\r
+ @return EFI_OUT_OF_RESOURCES The Database to save the public key is full.\r
+ @return 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
+ @return EFI_SUCCESS Variable is not write-protected or pass validation successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+ProcessVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes OPTIONAL\r
+ );\r
+\r
+/**\r
+ Finds variable in storage blocks of volatile and non-volatile storage areas.\r
+\r
+ This code finds variable in storage blocks of volatile and non-volatile storage areas.\r
+ If VariableName is an empty string, then we just return the first\r
+ qualified variable without comparing VariableName and VendorGuid.\r
+\r
+ @param[in] VariableName Name of the variable to be found.\r
+ @param[in] VendorGuid Variable vendor GUID to be found.\r
+ @param[out] Data Pointer to data address.\r
+ @param[out] DataSize Pointer to data size.\r
+\r
+ @retval EFI_INVALID_PARAMETER If VariableName is not an empty string,\r
+ while VendorGuid is NULL.\r
+ @retval EFI_SUCCESS Variable successfully found.\r
+ @retval EFI_NOT_FOUND Variable not found\r
+\r
+**/\r
+EFI_STATUS\r
+AuthServiceInternalFindVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ OUT VOID **Data,\r
+ OUT UINTN *DataSize\r
+ );\r
+\r
+/**\r
+ Update the variable region with Variable information.\r
+\r
+ @param[in] VariableName Name of variable.\r
+ @param[in] VendorGuid Guid of variable.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] Attributes Attribute value of the variable.\r
+\r
+ @retval EFI_SUCCESS The update operation is success.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_WRITE_PROTECTED Variable is write-protected.\r
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource.\r
+\r
+**/\r
+EFI_STATUS\r
+AuthServiceInternalUpdateVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes\r
+ );\r
+\r
+/**\r
+ Update the variable region with Variable information.\r
+\r
+ @param[in] VariableName Name of variable.\r
+ @param[in] VendorGuid Guid of variable.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] Attributes Attribute value of the variable.\r
+ @param[in] KeyIndex Index of associated public key.\r
+ @param[in] MonotonicCount Value of associated monotonic count.\r
+\r
+ @retval EFI_SUCCESS The update operation is success.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_WRITE_PROTECTED Variable is write-protected.\r
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource.\r
+\r
+**/\r
+EFI_STATUS\r
+AuthServiceInternalUpdateVariableWithMonotonicCount (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes,\r
+ IN UINT32 KeyIndex,\r
+ IN UINT64 MonotonicCount\r
+ );\r
+\r
+/**\r
+ Update the variable region with Variable information.\r
+\r
+ @param[in] VariableName Name of variable.\r
+ @param[in] VendorGuid Guid of variable.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] Attributes Attribute value of the variable.\r
+ @param[in] TimeStamp Value of associated TimeStamp.\r
+\r
+ @retval EFI_SUCCESS The update operation is success.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_WRITE_PROTECTED Variable is write-protected.\r
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource.\r
+\r
+**/\r
+EFI_STATUS\r
+AuthServiceInternalUpdateVariableWithTimeStamp (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes,\r
+ IN EFI_TIME *TimeStamp\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ Implement authentication services for the authenticated variables.\r
+\r
+ Caution: This module requires additional review when modified.\r
+ This driver will have external input - variable data. It may be input in SMM mode.\r
+ This external input must be validated carefully to avoid security issue like\r
+ buffer overflow, integer overflow.\r
+ Variable attribute should also be checked to avoid authentication bypass.\r
+ The whole SMM authentication variable design relies on the integrity of flash part and SMM.\r
+ which is assumed to be protected by platform. All variable code and metadata in flash/SMM Memory\r
+ may not be modified without authorization. If platform fails to protect these resources,\r
+ the authentication service provided in this driver will be broken, and the behavior is undefined.\r
+\r
+Copyright (c) 2015, 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 "AuthServiceInternal.h"\r
+\r
+///\r
+/// Global database array for scratch\r
+///\r
+UINT8 *mPubKeyStore;\r
+UINT32 mPubKeyNumber;\r
+UINT32 mMaxKeyNumber;\r
+UINT32 mMaxKeyDbSize;\r
+UINT8 *mCertDbStore;\r
+UINT32 mMaxCertDbSize;\r
+UINT32 mPlatformMode;\r
+UINT8 mVendorKeyState;\r
+\r
+EFI_GUID mSignatureSupport[] = {EFI_CERT_SHA1_GUID, EFI_CERT_SHA256_GUID, EFI_CERT_RSA2048_GUID, EFI_CERT_X509_GUID};\r
+\r
+//\r
+// Hash context pointer\r
+//\r
+VOID *mHashCtx = NULL;\r
+\r
+VARIABLE_ENTRY_PROPERTY mAuthVarEntry[] = {\r
+ {\r
+ &gEfiSecureBootEnableDisableGuid,\r
+ EFI_SECURE_BOOT_ENABLE_NAME,\r
+ {\r
+ VAR_CHECK_VARIABLE_PROPERTY_REVISION,\r
+ 0,\r
+ VARIABLE_ATTRIBUTE_NV_BS,\r
+ sizeof (UINT8),\r
+ sizeof (UINT8)\r
+ }\r
+ },\r
+ {\r
+ &gEfiCustomModeEnableGuid,\r
+ EFI_CUSTOM_MODE_NAME,\r
+ {\r
+ VAR_CHECK_VARIABLE_PROPERTY_REVISION,\r
+ 0,\r
+ VARIABLE_ATTRIBUTE_NV_BS,\r
+ sizeof (UINT8),\r
+ sizeof (UINT8)\r
+ }\r
+ },\r
+ {\r
+ &gEfiVendorKeysNvGuid,\r
+ EFI_VENDOR_KEYS_NV_VARIABLE_NAME,\r
+ {\r
+ VAR_CHECK_VARIABLE_PROPERTY_REVISION,\r
+ VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY,\r
+ VARIABLE_ATTRIBUTE_NV_BS_RT_AT,\r
+ sizeof (UINT8),\r
+ sizeof (UINT8)\r
+ }\r
+ },\r
+ {\r
+ &gEfiAuthenticatedVariableGuid,\r
+ AUTHVAR_KEYDB_NAME,\r
+ {\r
+ VAR_CHECK_VARIABLE_PROPERTY_REVISION,\r
+ VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY,\r
+ VARIABLE_ATTRIBUTE_NV_BS_RT_AW,\r
+ sizeof (UINT8),\r
+ MAX_UINTN\r
+ }\r
+ },\r
+ {\r
+ &gEfiCertDbGuid,\r
+ EFI_CERT_DB_NAME,\r
+ {\r
+ VAR_CHECK_VARIABLE_PROPERTY_REVISION,\r
+ VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY,\r
+ VARIABLE_ATTRIBUTE_NV_BS_RT_AT,\r
+ sizeof (UINT32),\r
+ MAX_UINTN\r
+ }\r
+ },\r
+};\r
+\r
+VOID *mAddressPointer[3];\r
+\r
+AUTH_VAR_LIB_CONTEXT_IN *mAuthVarLibContextIn = NULL;\r
+\r
+/**\r
+ Initialization for authenticated varibale services.\r
+ If this initialization returns error status, other APIs will not work\r
+ and expect to be not called then.\r
+\r
+ @param[in] AuthVarLibContextIn Pointer to input auth variable lib context.\r
+ @param[out] AuthVarLibContextOut Pointer to output auth variable lib context.\r
+\r
+ @retval EFI_SUCCESS Function successfully executed.\r
+ @retval EFI_INVALID_PARAMETER If AuthVarLibContextIn == NULL or AuthVarLibContextOut == NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough resource.\r
+ @retval EFI_UNSUPPORTED Unsupported to process authenticated variable.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AuthVariableLibInitialize (\r
+ IN AUTH_VAR_LIB_CONTEXT_IN *AuthVarLibContextIn,\r
+ OUT AUTH_VAR_LIB_CONTEXT_OUT *AuthVarLibContextOut\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 VarValue;\r
+ UINT32 VarAttr;\r
+ UINT8 *Data;\r
+ UINTN DataSize;\r
+ UINTN CtxSize;\r
+ UINT8 SecureBootMode;\r
+ UINT8 SecureBootEnable;\r
+ UINT8 CustomMode;\r
+ UINT32 ListSize;\r
+\r
+ if ((AuthVarLibContextIn == NULL) || (AuthVarLibContextOut == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ mAuthVarLibContextIn = AuthVarLibContextIn;\r
+\r
+ //\r
+ // Initialize hash context.\r
+ //\r
+ CtxSize = Sha256GetContextSize ();\r
+ mHashCtx = AllocateRuntimePool (CtxSize);\r
+ if (mHashCtx == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Reserve runtime buffer for public key database. The size excludes variable header and name size.\r
+ //\r
+ mMaxKeyDbSize = (UINT32) (mAuthVarLibContextIn->MaxAuthVariableSize - sizeof (AUTHVAR_KEYDB_NAME));\r
+ mMaxKeyNumber = mMaxKeyDbSize / sizeof (AUTHVAR_KEY_DB_DATA);\r
+ mPubKeyStore = AllocateRuntimePool (mMaxKeyDbSize);\r
+ if (mPubKeyStore == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Reserve runtime buffer for certificate database. The size excludes variable header and name size.\r
+ //\r
+ mMaxCertDbSize = (UINT32) (mAuthVarLibContextIn->MaxAuthVariableSize - sizeof (EFI_CERT_DB_NAME));\r
+ mCertDbStore = AllocateRuntimePool (mMaxCertDbSize);\r
+ if (mCertDbStore == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\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 = AuthServiceInternalFindVariable (\r
+ AUTHVAR_KEYDB_NAME,\r
+ &gEfiAuthenticatedVariableGuid,\r
+ (VOID **) &Data,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\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 = AuthServiceInternalUpdateVariable (\r
+ AUTHVAR_KEYDB_NAME,\r
+ &gEfiAuthenticatedVariableGuid,\r
+ &VarValue,\r
+ sizeof(UINT8),\r
+ VarAttr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ } else {\r
+ //\r
+ // Load database in global variable for cache.\r
+ //\r
+ ASSERT ((DataSize != 0) && (Data != NULL));\r
+ //\r
+ // "AuthVarKeyDatabase" is an internal variable. Its DataSize is always ensured not to exceed mPubKeyStore buffer size(See definition before)\r
+ // Therefore, there is no memory overflow in underlying CopyMem.\r
+ //\r
+ CopyMem (mPubKeyStore, (UINT8 *) Data, DataSize);\r
+ mPubKeyNumber = (UINT32) (DataSize / sizeof (AUTHVAR_KEY_DB_DATA));\r
+ }\r
+\r
+ Status = AuthServiceInternalFindVariable (EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid, (VOID **) &Data, &DataSize);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_INFO, "Variable %s does not exist.\n", EFI_PLATFORM_KEY_NAME));\r
+ } else {\r
+ DEBUG ((EFI_D_INFO, "Variable %s exists.\n", EFI_PLATFORM_KEY_NAME));\r
+ }\r
+\r
+ //\r
+ // Create "SetupMode" variable with BS+RT attribute set.\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+ mPlatformMode = SETUP_MODE;\r
+ } else {\r
+ mPlatformMode = USER_MODE;\r
+ }\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_SETUP_MODE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ &mPlatformMode,\r
+ sizeof(UINT8),\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Create "SignatureSupport" variable with BS+RT attribute set.\r
+ //\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_SIGNATURE_SUPPORT_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ mSignatureSupport,\r
+ sizeof(mSignatureSupport),\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // If "SecureBootEnable" variable exists, then update "SecureBoot" variable.\r
+ // If "SecureBootEnable" variable is SECURE_BOOT_ENABLE and in USER_MODE, Set "SecureBoot" variable to SECURE_BOOT_MODE_ENABLE.\r
+ // If "SecureBootEnable" variable is SECURE_BOOT_DISABLE, Set "SecureBoot" variable to SECURE_BOOT_MODE_DISABLE.\r
+ //\r
+ SecureBootEnable = SECURE_BOOT_DISABLE;\r
+ Status = AuthServiceInternalFindVariable (EFI_SECURE_BOOT_ENABLE_NAME, &gEfiSecureBootEnableDisableGuid, (VOID **) &Data, &DataSize);\r
+ if (!EFI_ERROR (Status)) {\r
+ if (mPlatformMode == SETUP_MODE){\r
+ //\r
+ // PK is cleared in runtime. "SecureBootMode" is not updated before reboot\r
+ // Delete "SecureBootMode" in SetupMode\r
+ //\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_SECURE_BOOT_ENABLE_NAME,\r
+ &gEfiSecureBootEnableDisableGuid,\r
+ &SecureBootEnable,\r
+ 0,\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS\r
+ );\r
+ } else {\r
+ SecureBootEnable = *(UINT8 *) Data;\r
+ }\r
+ } else if (mPlatformMode == USER_MODE) {\r
+ //\r
+ // "SecureBootEnable" not exist, initialize it in USER_MODE.\r
+ //\r
+ SecureBootEnable = SECURE_BOOT_ENABLE;\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_SECURE_BOOT_ENABLE_NAME,\r
+ &gEfiSecureBootEnableDisableGuid,\r
+ &SecureBootEnable,\r
+ sizeof (UINT8),\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Create "SecureBoot" variable with BS+RT attribute set.\r
+ //\r
+ if (SecureBootEnable == SECURE_BOOT_ENABLE && mPlatformMode == USER_MODE) {\r
+ SecureBootMode = SECURE_BOOT_MODE_ENABLE;\r
+ } else {\r
+ SecureBootMode = SECURE_BOOT_MODE_DISABLE;\r
+ }\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_SECURE_BOOT_MODE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ &SecureBootMode,\r
+ sizeof (UINT8),\r
+ EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_SETUP_MODE_NAME, mPlatformMode));\r
+ DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_SECURE_BOOT_MODE_NAME, SecureBootMode));\r
+ DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_SECURE_BOOT_ENABLE_NAME, SecureBootEnable));\r
+\r
+ //\r
+ // Initialize "CustomMode" in STANDARD_SECURE_BOOT_MODE state.\r
+ //\r
+ CustomMode = STANDARD_SECURE_BOOT_MODE;\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_CUSTOM_MODE_NAME,\r
+ &gEfiCustomModeEnableGuid,\r
+ &CustomMode,\r
+ sizeof (UINT8),\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_CUSTOM_MODE_NAME, CustomMode));\r
+\r
+ //\r
+ // Check "certdb" variable's existence.\r
+ // If it doesn't exist, then create a new one with\r
+ // EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set.\r
+ //\r
+ Status = AuthServiceInternalFindVariable (\r
+ EFI_CERT_DB_NAME,\r
+ &gEfiCertDbGuid,\r
+ (VOID **) &Data,\r
+ &DataSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;\r
+ ListSize = sizeof (UINT32);\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_CERT_DB_NAME,\r
+ &gEfiCertDbGuid,\r
+ &ListSize,\r
+ sizeof (UINT32),\r
+ VarAttr\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check "VendorKeysNv" variable's existence and create "VendorKeys" variable accordingly.\r
+ //\r
+ Status = AuthServiceInternalFindVariable (EFI_VENDOR_KEYS_NV_VARIABLE_NAME, &gEfiVendorKeysNvGuid, (VOID **) &Data, &DataSize);\r
+ if (!EFI_ERROR (Status)) {\r
+ mVendorKeyState = *(UINT8 *)Data;\r
+ } else {\r
+ //\r
+ // "VendorKeysNv" not exist, initialize it in VENDOR_KEYS_VALID state.\r
+ //\r
+ mVendorKeyState = VENDOR_KEYS_VALID;\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_VENDOR_KEYS_NV_VARIABLE_NAME,\r
+ &gEfiVendorKeysNvGuid,\r
+ &mVendorKeyState,\r
+ sizeof (UINT8),\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Create "VendorKeys" variable with BS+RT attribute set.\r
+ //\r
+ Status = AuthServiceInternalUpdateVariable (\r
+ EFI_VENDOR_KEYS_VARIABLE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ &mVendorKeyState,\r
+ sizeof (UINT8),\r
+ EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_VENDOR_KEYS_VARIABLE_NAME, mVendorKeyState));\r
+\r
+ AuthVarLibContextOut->StructVersion = AUTH_VAR_LIB_CONTEXT_OUT_STRUCT_VERSION;\r
+ AuthVarLibContextOut->StructSize = sizeof (AUTH_VAR_LIB_CONTEXT_OUT);\r
+ AuthVarLibContextOut->AuthVarEntry = mAuthVarEntry;\r
+ AuthVarLibContextOut->AuthVarEntryCount = sizeof (mAuthVarEntry) / sizeof (mAuthVarEntry[0]);\r
+ mAddressPointer[0] = mHashCtx;\r
+ mAddressPointer[1] = mPubKeyStore;\r
+ mAddressPointer[2] = mCertDbStore;\r
+ AuthVarLibContextOut->AddressPointer = mAddressPointer;\r
+ AuthVarLibContextOut->AddressPointerCount = sizeof (mAddressPointer) / sizeof (mAddressPointer[0]);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Process variable with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS/EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set.\r
+\r
+ @param[in] VariableName Name of the variable.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] Data Data pointer.\r
+ @param[in] DataSize Size of Data.\r
+ @param[in] Attributes Attribute value of the variable.\r
+\r
+ @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as\r
+ defined by the Attributes.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_WRITE_PROTECTED Variable is write-protected.\r
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource.\r
+ @retval EFI_SECURITY_VIOLATION The variable is with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS\r
+ or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS\r
+ set, but the AuthInfo does NOT pass the validation\r
+ check carried out by the firmware.\r
+ @retval EFI_UNSUPPORTED Unsupported to process authenticated variable.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AuthVariableLibProcessVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_PLATFORM_KEY_NAME) == 0)){\r
+ Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, Attributes, TRUE);\r
+ } else if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0)) {\r
+ Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, Attributes, FALSE);\r
+ } else if (CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) &&\r
+ ((StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) ||\r
+ (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0) ||\r
+ (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE2) == 0)\r
+ )) {\r
+ Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, Attributes, FALSE);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ProcessVarWithKek (VariableName, VendorGuid, Data, DataSize, Attributes);\r
+ }\r
+ } else {\r
+ Status = ProcessVariable (VariableName, VendorGuid, Data, DataSize, Attributes);\r
+ }\r
+\r
+ return Status;\r
+}\r