X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=SecurityPkg%2FVariableAuthenticated%2FRuntimeDxe%2FAuthService.c;h=07a41b06f8ad01a52d5e232d63cc4357d0986d66;hp=ff5c6539125af152a47bedfaf276107c492fec22;hb=e3ff137e3652a85944ba1ba91ad61f09ef1ff248;hpb=beda2356f5128efa4461046f882b6516ece6afc7 diff --git a/SecurityPkg/VariableAuthenticated/RuntimeDxe/AuthService.c b/SecurityPkg/VariableAuthenticated/RuntimeDxe/AuthService.c index ff5c653912..07a41b06f8 100644 --- a/SecurityPkg/VariableAuthenticated/RuntimeDxe/AuthService.c +++ b/SecurityPkg/VariableAuthenticated/RuntimeDxe/AuthService.c @@ -2,13 +2,26 @@ Implement authentication services for the authenticated variable service in UEFI2.2. -Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at + Caution: This module requires additional review when modified. + This driver will have external input - variable data. It may be input in SMM mode. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + Variable attribute should also be checked to avoid authentication bypass. + + ProcessVarWithPk(), ProcessVarWithKek() and ProcessVariable() are the function to do + variable authentication. + + VerifyTimeBasedPayload() and VerifyCounterBasedPayload() are sub function to do verification. + They will do basic validation for authentication data structure, then call crypto library + to verify the signature. + +Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ @@ -18,11 +31,14 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. /// /// Global database array for scratch -/// +/// UINT8 mPubKeyStore[MAX_KEYDB_SIZE]; UINT32 mPubKeyNumber; +UINT8 mCertDbStore[MAX_CERTDB_SIZE]; UINT32 mPlatformMode; -EFI_GUID mSignatureSupport[SIGSUPPORT_NUM] = {EFI_CERT_RSA2048_SHA256_GUID, EFI_CERT_RSA2048_SHA1_GUID}; +UINT8 mVendorKeyState; + +EFI_GUID mSignatureSupport[] = {EFI_CERT_SHA1_GUID, EFI_CERT_SHA256_GUID, EFI_CERT_RSA2048_GUID, EFI_CERT_X509_GUID}; // // Public Exponent of RSA Key. // @@ -32,28 +48,106 @@ CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 }; // VOID *mHashCtx = NULL; +// +// The serialization of the values of the VariableName, VendorGuid and Attributes +// parameters of the SetVariable() call and the TimeStamp component of the +// EFI_VARIABLE_AUTHENTICATION_2 descriptor followed by the variable's new value +// i.e. (VariableName, VendorGuid, Attributes, TimeStamp, Data) +// +UINT8 *mSerializationRuntimeBuffer = NULL; // -// Pointer to runtime buffer. -// For "Append" operation to an existing variable, a read/modify/write operation -// is supported by firmware internally. Reserve runtime buffer to cache previous -// variable data in runtime phase because memory allocation is forbidden in virtual mode. +// Requirement for different signature type which have been defined in UEFI spec. +// These data are used to peform SignatureList format check while setting PK/KEK variable. // -VOID *mStorageArea = NULL; +EFI_SIGNATURE_ITEM mSupportSigItem[] = { +//{SigType, SigHeaderSize, SigDataSize } + {EFI_CERT_SHA256_GUID, 0, 32 }, + {EFI_CERT_RSA2048_GUID, 0, 256 }, + {EFI_CERT_RSA2048_SHA256_GUID, 0, 256 }, + {EFI_CERT_SHA1_GUID, 0, 20 }, + {EFI_CERT_RSA2048_SHA1_GUID, 0, 256 }, + {EFI_CERT_X509_GUID, 0, ((UINT32) ~0)}, + {EFI_CERT_SHA224_GUID, 0, 28 }, + {EFI_CERT_SHA384_GUID, 0, 48 }, + {EFI_CERT_SHA512_GUID, 0, 64 } +}; /** - Update platform mode. + Determine whether this operation needs a physical present user. - @param[in] Mode SETUP_MODE or USER_MODE. + @param[in] VariableName Name of the Variable. + @param[in] VendorGuid GUID of the Variable. - @return EFI_INVALID_PARAMETER Invalid parameter. - @return EFI_SUCCESS Update platform mode successfully. + @retval TRUE This variable is protected, only a physical present user could set this variable. + @retval FALSE This variable is not protected. + +**/ +BOOLEAN +NeedPhysicallyPresent( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + if ((CompareGuid (VendorGuid, &gEfiSecureBootEnableDisableGuid) && (StrCmp (VariableName, EFI_SECURE_BOOT_ENABLE_NAME) == 0)) + || (CompareGuid (VendorGuid, &gEfiCustomModeEnableGuid) && (StrCmp (VariableName, EFI_CUSTOM_MODE_NAME) == 0))) { + return TRUE; + } + + return FALSE; +} + +/** + Determine whether the platform is operating in Custom Secure Boot mode. + + @retval TRUE The platform is operating in Custom mode. + @retval FALSE The platform is operating in Standard mode. + +**/ +BOOLEAN +InCustomMode ( + VOID + ) +{ + VARIABLE_POINTER_TRACK Variable; + + FindVariable (EFI_CUSTOM_MODE_NAME, &gEfiCustomModeEnableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + if (Variable.CurrPtr != NULL && *(GetVariableDataPtr (Variable.CurrPtr)) == CUSTOM_SECURE_BOOT_MODE) { + return TRUE; + } + + return FALSE; +} + + +/** + Internal function to delete a Variable given its name and GUID, no authentication + required. + + @param[in] VariableName Name of the Variable. + @param[in] VendorGuid GUID of the Variable. + + @retval EFI_SUCCESS Variable deleted successfully. + @retval Others The driver failded to start the device. **/ EFI_STATUS -UpdatePlatformMode ( - IN UINT32 Mode - ); +DeleteVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + VARIABLE_POINTER_TRACK Variable; + + Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + if (EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + + ASSERT (Variable.CurrPtr != NULL); + return UpdateVariable (VariableName, VendorGuid, NULL, 0, 0, 0, 0, &Variable, NULL); +} /** Initializes for authenticated varibale service. @@ -69,7 +163,7 @@ AutenticatedVariableServiceInitialize ( { EFI_STATUS Status; VARIABLE_POINTER_TRACK Variable; - VARIABLE_POINTER_TRACK Variable2; + VARIABLE_POINTER_TRACK PkVariable; UINT8 VarValue; UINT32 VarAttr; UINT8 *Data; @@ -77,7 +171,9 @@ AutenticatedVariableServiceInitialize ( UINTN CtxSize; UINT8 SecureBootMode; UINT8 SecureBootEnable; - + UINT8 CustomMode; + UINT32 ListSize; + // // Initialize hash context. // @@ -88,22 +184,24 @@ AutenticatedVariableServiceInitialize ( } // - // Reserved runtime buffer for "Append" operation in virtual mode. + // Prepare runtime buffer for serialized data of time-based authenticated + // Variable, i.e. (VariableName, VendorGuid, Attributes, TimeStamp, Data). // - mStorageArea = AllocateRuntimePool (PcdGet32 (PcdMaxAppendVariableSize)); - if (mStorageArea == NULL) { + mSerializationRuntimeBuffer = AllocateRuntimePool (PcdGet32 (PcdMaxVariableSize) + sizeof (EFI_GUID) + sizeof (UINT32) + sizeof (EFI_TIME)); + if (mSerializationRuntimeBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } // - // Check "AuthVarKeyDatabase" variable's existence. - // If it doesn't exist, create a new one with initial value of 0 and EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. + // Check "AuthVarKeyDatabase" variable's existence. + // If it doesn't exist, create a new one with initial value of 0 and EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. // Status = FindVariable ( - AUTHVAR_KEYDB_NAME, - &gEfiAuthenticatedVariableGuid, - &Variable, - &mVariableModuleGlobal->VariableGlobal + AUTHVAR_KEYDB_NAME, + &gEfiAuthenticatedVariableGuid, + &Variable, + &mVariableModuleGlobal->VariableGlobal, + FALSE ); if (Variable.CurrPtr == NULL) { @@ -134,98 +232,188 @@ AutenticatedVariableServiceInitialize ( CopyMem (mPubKeyStore, (UINT8 *) Data, DataSize); mPubKeyNumber = (UINT32) (DataSize / EFI_CERT_TYPE_RSA2048_SIZE); } + + FindVariable (EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid, &PkVariable, &mVariableModuleGlobal->VariableGlobal, FALSE); + if (PkVariable.CurrPtr == NULL) { + DEBUG ((EFI_D_INFO, "Variable %s does not exist.\n", EFI_PLATFORM_KEY_NAME)); + } else { + DEBUG ((EFI_D_INFO, "Variable %s exists.\n", EFI_PLATFORM_KEY_NAME)); + } + // - // Check "SetupMode" variable's existence. - // If it doesn't exist, check PK database's existence to determine the value. - // Then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. + // Create "SetupMode" variable with BS+RT attribute set. // - Status = FindVariable ( - EFI_SETUP_MODE_NAME, - &gEfiGlobalVariableGuid, - &Variable, - &mVariableModuleGlobal->VariableGlobal + FindVariable (EFI_SETUP_MODE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + if (PkVariable.CurrPtr == NULL) { + mPlatformMode = SETUP_MODE; + } else { + mPlatformMode = USER_MODE; + } + Status = UpdateVariable ( + EFI_SETUP_MODE_NAME, + &gEfiGlobalVariableGuid, + &mPlatformMode, + sizeof(UINT8), + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + 0, + 0, + &Variable, + NULL ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create "SignatureSupport" variable with BS+RT attribute set. + // + FindVariable (EFI_SIGNATURE_SUPPORT_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + Status = UpdateVariable ( + EFI_SIGNATURE_SUPPORT_NAME, + &gEfiGlobalVariableGuid, + mSignatureSupport, + sizeof(mSignatureSupport), + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + 0, + 0, + &Variable, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } - if (Variable.CurrPtr == NULL) { - Status = FindVariable ( - EFI_PLATFORM_KEY_NAME, - &gEfiGlobalVariableGuid, - &Variable2, - &mVariableModuleGlobal->VariableGlobal + // + // If "SecureBootEnable" variable exists, then update "SecureBoot" variable. + // If "SecureBootEnable" variable is SECURE_BOOT_ENABLE and in USER_MODE, Set "SecureBoot" variable to SECURE_BOOT_MODE_ENABLE. + // If "SecureBootEnable" variable is SECURE_BOOT_DISABLE, Set "SecureBoot" variable to SECURE_BOOT_MODE_DISABLE. + // + SecureBootEnable = SECURE_BOOT_DISABLE; + FindVariable (EFI_SECURE_BOOT_ENABLE_NAME, &gEfiSecureBootEnableDisableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + if (Variable.CurrPtr != NULL) { + SecureBootEnable = *(GetVariableDataPtr (Variable.CurrPtr)); + } else if (mPlatformMode == USER_MODE) { + // + // "SecureBootEnable" not exist, initialize it in USER_MODE. + // + SecureBootEnable = SECURE_BOOT_ENABLE; + Status = UpdateVariable ( + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + &SecureBootEnable, + sizeof (UINT8), + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + 0, + &Variable, + NULL ); - if (Variable2.CurrPtr == NULL) { - mPlatformMode = SETUP_MODE; - } else { - mPlatformMode = USER_MODE; - } - - VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; - Status = UpdateVariable ( - EFI_SETUP_MODE_NAME, - &gEfiGlobalVariableGuid, - &mPlatformMode, - sizeof(UINT8), - VarAttr, - 0, - 0, - &Variable, - NULL - ); if (EFI_ERROR (Status)) { return Status; } + } + + // + // Create "SecureBoot" variable with BS+RT attribute set. + // + if (SecureBootEnable == SECURE_BOOT_ENABLE && mPlatformMode == USER_MODE) { + SecureBootMode = SECURE_BOOT_MODE_ENABLE; } else { - mPlatformMode = *(GetVariableDataPtr (Variable.CurrPtr)); + SecureBootMode = SECURE_BOOT_MODE_DISABLE; + } + FindVariable (EFI_SECURE_BOOT_MODE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + Status = UpdateVariable ( + EFI_SECURE_BOOT_MODE_NAME, + &gEfiGlobalVariableGuid, + &SecureBootMode, + sizeof (UINT8), + EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + 0, + &Variable, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_SETUP_MODE_NAME, mPlatformMode)); + DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_SECURE_BOOT_MODE_NAME, SecureBootMode)); + DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_SECURE_BOOT_ENABLE_NAME, SecureBootEnable)); + + // + // Initialize "CustomMode" in STANDARD_SECURE_BOOT_MODE state. + // + FindVariable (EFI_CUSTOM_MODE_NAME, &gEfiCustomModeEnableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + CustomMode = STANDARD_SECURE_BOOT_MODE; + Status = UpdateVariable ( + EFI_CUSTOM_MODE_NAME, + &gEfiCustomModeEnableGuid, + &CustomMode, + sizeof (UINT8), + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + 0, + &Variable, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; } + + DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_CUSTOM_MODE_NAME, CustomMode)); + // - // Check "SignatureSupport" variable's existence. - // If it doesn't exist, then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. + // Check "certdb" variable's existence. + // If it doesn't exist, then create a new one with + // EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set. // Status = FindVariable ( - EFI_SIGNATURE_SUPPORT_NAME, - &gEfiGlobalVariableGuid, - &Variable, - &mVariableModuleGlobal->VariableGlobal + EFI_CERT_DB_NAME, + &gEfiCertDbGuid, + &Variable, + &mVariableModuleGlobal->VariableGlobal, + FALSE ); - if (Variable.CurrPtr == NULL) { - VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; - Status = UpdateVariable ( - EFI_SIGNATURE_SUPPORT_NAME, - &gEfiGlobalVariableGuid, - mSignatureSupport, - SIGSUPPORT_NUM * sizeof(EFI_GUID), - VarAttr, - 0, - 0, - &Variable, - NULL - ); - } + VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + ListSize = sizeof (UINT32); + Status = UpdateVariable ( + EFI_CERT_DB_NAME, + &gEfiCertDbGuid, + &ListSize, + sizeof (UINT32), + VarAttr, + 0, + 0, + &Variable, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + } // - // If "SecureBootEnable" variable exists, then update "SecureBoot" variable. - // If "SecureBootEnable" variable is SECURE_BOOT_ENABLE, Set "SecureBoot" variable to SECURE_BOOT_MODE_ENABLE. - // If "SecureBootEnable" variable is SECURE_BOOT_DISABLE, Set "SecureBoot" variable to SECURE_BOOT_MODE_DISABLE. + // Check "VendorKeysNv" variable's existence and create "VendorKeys" variable accordingly. // - FindVariable (EFI_SECURE_BOOT_ENABLE_NAME, &gEfiSecureBootEnableDisableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal); + FindVariable (EFI_VENDOR_KEYS_NV_VARIABLE_NAME, &gEfiVendorKeysNvGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); if (Variable.CurrPtr != NULL) { - SecureBootEnable = *(GetVariableDataPtr (Variable.CurrPtr)); - if (SecureBootEnable == SECURE_BOOT_ENABLE) { - SecureBootMode = SECURE_BOOT_MODE_ENABLE; - } else { - SecureBootMode = SECURE_BOOT_MODE_DISABLE; - } - FindVariable (EFI_SECURE_BOOT_MODE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal); + mVendorKeyState = *(GetVariableDataPtr (Variable.CurrPtr)); + } else { + // + // "VendorKeysNv" not exist, initialize it in VENDOR_KEYS_VALID state. + // + mVendorKeyState = VENDOR_KEYS_VALID; Status = UpdateVariable ( - EFI_SECURE_BOOT_MODE_NAME, - &gEfiGlobalVariableGuid, - &SecureBootMode, - sizeof(UINT8), - EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, - 0, - 0, + EFI_VENDOR_KEYS_NV_VARIABLE_NAME, + &gEfiVendorKeysNvGuid, + &mVendorKeyState, + sizeof (UINT8), + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, + 0, + 0, &Variable, NULL ); @@ -235,43 +423,26 @@ AutenticatedVariableServiceInitialize ( } // - // Detect whether a secure platform-specific method to clear PK(Platform Key) - // is configured by platform owner. This method is provided for users force to clear PK - // in case incorrect enrollment mis-haps. + // Create "VendorKeys" variable with BS+RT attribute set. // - if (ForceClearPK ()) { - // - // 1. Check whether PK is existing, and clear PK if existing - // - FindVariable ( - EFI_PLATFORM_KEY_NAME, - &gEfiGlobalVariableGuid, - &Variable, - &mVariableModuleGlobal->VariableGlobal - ); - if (Variable.CurrPtr != NULL) { - VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; - Status = UpdateVariable ( - EFI_PLATFORM_KEY_NAME, - &gEfiGlobalVariableGuid, - NULL, - 0, - VarAttr, - 0, - 0, - &Variable, - NULL - ); - if (EFI_ERROR (Status)) { - return Status; - } - } - - // - // 2. Update "SetupMode" variable to SETUP_MODE - // - UpdatePlatformMode (SETUP_MODE); + FindVariable (EFI_VENDOR_KEYS_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + Status = UpdateVariable ( + EFI_VENDOR_KEYS_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + &mVendorKeyState, + sizeof (UINT8), + EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + 0, + &Variable, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; } + + DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_VENDOR_KEYS_VARIABLE_NAME, mVendorKeyState)); + return Status; } @@ -279,20 +450,26 @@ AutenticatedVariableServiceInitialize ( Add public key in store and return its index. @param[in] PubKey Input pointer to Public Key data + @param[in] VariableDataEntry The variable data entry @return Index of new added item **/ UINT32 AddPubKeyInStore ( - IN UINT8 *PubKey + IN UINT8 *PubKey, + IN VARIABLE_ENTRY_CONSISTENCY *VariableDataEntry ) { - EFI_STATUS Status; - BOOLEAN IsFound; - UINT32 Index; - VARIABLE_POINTER_TRACK Variable; - UINT8 *Ptr; + EFI_STATUS Status; + BOOLEAN IsFound; + UINT32 Index; + VARIABLE_POINTER_TRACK Variable; + UINT8 *Ptr; + UINT8 *Data; + UINTN DataSize; + VARIABLE_ENTRY_CONSISTENCY PublicKeyEntry; + UINT32 Attributes; if (PubKey == NULL) { return 0; @@ -302,9 +479,14 @@ AddPubKeyInStore ( AUTHVAR_KEYDB_NAME, &gEfiAuthenticatedVariableGuid, &Variable, - &mVariableModuleGlobal->VariableGlobal + &mVariableModuleGlobal->VariableGlobal, + FALSE ); - ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Get public key database variable failure, Status = %r\n", Status)); + return 0; + } + // // Check whether the public key entry does exist. // @@ -323,7 +505,62 @@ AddPubKeyInStore ( // if (mPubKeyNumber == MAX_KEY_NUM) { // - // Notes: Database is full, need enhancement here, currently just return 0. + // Public key dadatase is full, try to reclaim invalid key. + // + if (AtRuntime ()) { + // + // NV storage can't reclaim at runtime. + // + return 0; + } + + Status = Reclaim ( + mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase, + &mVariableModuleGlobal->NonVolatileLastVariableOffset, + FALSE, + NULL, + NULL, + 0, + TRUE + ); + if (EFI_ERROR (Status)) { + return 0; + } + + Status = FindVariable ( + AUTHVAR_KEYDB_NAME, + &gEfiAuthenticatedVariableGuid, + &Variable, + &mVariableModuleGlobal->VariableGlobal, + FALSE + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Get public key database variable failure, Status = %r\n", Status)); + return 0; + } + + DataSize = DataSizeOfVariable (Variable.CurrPtr); + Data = GetVariableDataPtr (Variable.CurrPtr); + ASSERT ((DataSize != 0) && (Data != NULL)); + CopyMem (mPubKeyStore, (UINT8 *) Data, DataSize); + mPubKeyNumber = (UINT32) (DataSize / EFI_CERT_TYPE_RSA2048_SIZE); + + if (mPubKeyNumber == MAX_KEY_NUM) { + return 0; + } + } + + // + // Check the variable space for both public key and variable data. + // + PublicKeyEntry.VariableSize = (mPubKeyNumber + 1) * EFI_CERT_TYPE_RSA2048_SIZE; + PublicKeyEntry.Guid = &gEfiAuthenticatedVariableGuid; + PublicKeyEntry.Name = AUTHVAR_KEYDB_NAME; + Attributes = VARIABLE_ATTRIBUTE_NV_BS_RT | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; + + if (!CheckRemainingSpaceForConsistency (Attributes, &PublicKeyEntry, VariableDataEntry, NULL)) { + // + // No enough variable space. // return 0; } @@ -338,29 +575,38 @@ AddPubKeyInStore ( &gEfiAuthenticatedVariableGuid, mPubKeyStore, mPubKeyNumber * EFI_CERT_TYPE_RSA2048_SIZE, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, + Attributes, 0, 0, &Variable, NULL ); - ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Update public key database variable failure, Status = %r\n", Status)); + return 0; + } } return Index; } /** - Verify data payload with AuthInfo in EFI_CERT_TYPE_RSA2048_SHA256 type. + Verify data payload with AuthInfo in EFI_CERT_TYPE_RSA2048_SHA256_GUID type. Follow the steps in UEFI2.2. + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode, and datasize and data are external input. + This function will do basic validation, before parse the data. + This function will parse the authentication carefully to avoid security issues, like + buffer overflow, integer overflow. + @param[in] Data Pointer to data with AuthInfo. @param[in] DataSize Size of Data. @param[in] PubKey Public key used for verification. - @return EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_SECURITY_VIOLATION If authentication failed. - @return EFI_SUCCESS Authentication successful. + @retval EFI_SUCCESS Authentication successful. **/ EFI_STATUS @@ -375,7 +621,9 @@ VerifyCounterBasedPayload ( EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock; UINT8 Digest[SHA256_DIGEST_SIZE]; VOID *Rsa; - + UINTN PayloadSize; + + PayloadSize = DataSize - AUTHINFO_SIZE; Rsa = NULL; CertData = NULL; CertBlock = NULL; @@ -389,10 +637,10 @@ VerifyCounterBasedPayload ( // // wCertificateType should be WIN_CERT_TYPE_EFI_GUID. - // Cert type should be EFI_CERT_TYPE_RSA2048_SHA256. + // Cert type should be EFI_CERT_TYPE_RSA2048_SHA256_GUID. // if ((CertData->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) || - !CompareGuid (&CertData->AuthInfo.CertType, &gEfiCertRsa2048Sha256Guid) + !CompareGuid (&CertData->AuthInfo.CertType, &gEfiCertTypeRsa2048Sha256Guid) ) { // // Invalid AuthInfo type, return EFI_SECURITY_VIOLATION. @@ -407,7 +655,14 @@ VerifyCounterBasedPayload ( if (!Status) { goto Done; } - Status = Sha256Update (mHashCtx, Data + AUTHINFO_SIZE, (UINTN) (DataSize - AUTHINFO_SIZE)); + Status = Sha256Update (mHashCtx, Data + AUTHINFO_SIZE, PayloadSize); + if (!Status) { + goto Done; + } + // + // Hash Size. + // + Status = Sha256Update (mHashCtx, &PayloadSize, sizeof (UINTN)); if (!Status) { goto Done; } @@ -427,7 +682,7 @@ VerifyCounterBasedPayload ( // Rsa = RsaNew (); ASSERT (Rsa != NULL); - // + // // Set RSA Key Components. // NOTE: Only N and E are needed to be set as RSA public key for signature verification. // @@ -443,10 +698,10 @@ VerifyCounterBasedPayload ( // Verify the signature. // Status = RsaPkcs1Verify ( - Rsa, - Digest, - SHA256_DIGEST_SIZE, - CertBlock->Signature, + Rsa, + Digest, + SHA256_DIGEST_SIZE, + CertBlock->Signature, EFI_CERT_TYPE_RSA2048_SHA256_SIZE ); @@ -461,7 +716,6 @@ Done: } } - /** Update platform mode. @@ -478,35 +732,34 @@ UpdatePlatformMode ( { EFI_STATUS Status; VARIABLE_POINTER_TRACK Variable; - UINT32 VarAttr; UINT8 SecureBootMode; UINT8 SecureBootEnable; UINTN VariableDataSize; - + Status = FindVariable ( - EFI_SETUP_MODE_NAME, - &gEfiGlobalVariableGuid, - &Variable, - &mVariableModuleGlobal->VariableGlobal + EFI_SETUP_MODE_NAME, + &gEfiGlobalVariableGuid, + &Variable, + &mVariableModuleGlobal->VariableGlobal, + FALSE ); if (EFI_ERROR (Status)) { return Status; } - mPlatformMode = Mode; - VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; - Status = UpdateVariable ( - EFI_SETUP_MODE_NAME, - &gEfiGlobalVariableGuid, - &mPlatformMode, - sizeof(UINT8), - VarAttr, - 0, - 0, - &Variable, - NULL - ); - if (EFI_ERROR (Status)) { + // + // Update the value of SetupMode variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + mPlatformMode = (UINT8) Mode; + CopyMem (GetVariableDataPtr (Variable.CurrPtr), &mPlatformMode, sizeof(UINT8)); + + if (AtRuntime ()) { + // + // SecureBoot Variable indicates whether the platform firmware is operating + // in Secure boot mode (1) or not (0), so we should not change SecureBoot + // Variable in runtime. + // return Status; } @@ -516,10 +769,11 @@ UpdatePlatformMode ( // then set "SecureBoot" to 0. // Status = FindVariable ( - EFI_SECURE_BOOT_MODE_NAME, - &gEfiGlobalVariableGuid, - &Variable, - &mVariableModuleGlobal->VariableGlobal + EFI_SECURE_BOOT_MODE_NAME, + &gEfiGlobalVariableGuid, + &Variable, + &mVariableModuleGlobal->VariableGlobal, + FALSE ); // // If "SecureBoot" variable exists, then check "SetupMode" variable update. @@ -538,19 +792,17 @@ UpdatePlatformMode ( } } - VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; Status = UpdateVariable ( EFI_SECURE_BOOT_MODE_NAME, &gEfiGlobalVariableGuid, &SecureBootMode, sizeof(UINT8), - VarAttr, + EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, 0, 0, &Variable, NULL ); - if (EFI_ERROR (Status)) { return Status; } @@ -559,12 +811,13 @@ UpdatePlatformMode ( // Check "SecureBootEnable" variable's existence. It can enable/disable secure boot feature. // Status = FindVariable ( - EFI_SECURE_BOOT_ENABLE_NAME, - &gEfiSecureBootEnableDisableGuid, - &Variable, - &mVariableModuleGlobal->VariableGlobal + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + &Variable, + &mVariableModuleGlobal->VariableGlobal, + FALSE ); - + if (SecureBootMode == SECURE_BOOT_MODE_ENABLE) { // // Create the "SecureBootEnable" variable as secure boot is enabled. @@ -573,7 +826,7 @@ UpdatePlatformMode ( VariableDataSize = sizeof (SecureBootEnable); } else { // - // Delete the "SecureBootEnable" variable if this variable exist as "SecureBoot" + // Delete the "SecureBootEnable" variable if this variable exist as "SecureBoot" // variable is not in secure boot state. // if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) { @@ -582,15 +835,15 @@ UpdatePlatformMode ( SecureBootEnable = SECURE_BOOT_DISABLE; VariableDataSize = 0; } - + Status = UpdateVariable ( - EFI_SECURE_BOOT_ENABLE_NAME, - &gEfiSecureBootEnableDisableGuid, - &SecureBootEnable, - VariableDataSize, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, - 0, - 0, + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + &SecureBootEnable, + VariableDataSize, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + 0, &Variable, NULL ); @@ -598,135 +851,295 @@ UpdatePlatformMode ( } /** - Process variable with platform key for verification. + Check input data form to make sure it is a valid EFI_SIGNATURE_LIST for PK/KEK/db/dbx variable. - @param[in] VariableName Name of Variable to be found. + @param[in] VariableName Name of Variable to be check. @param[in] VendorGuid Variable vendor GUID. - @param[in] Data Data pointer. - @param[in] DataSize Size of Data found. If size is less than the - data, this value contains the required size. - @param[in] Variable The variable information which is used to keep track of variable usage. - @param[in] Attributes Attribute value of the variable - @param[in] IsPk Indicate whether it is to process pk. - - @return EFI_INVALID_PARAMETER Invalid parameter. - @return EFI_SECURITY_VIOLATION The variable does NOT pass the validation. - check carried out by the firmware. - @return EFI_SUCCESS Variable passed validation successfully. + @param[in] Data Point to the variable data to be checked. + @param[in] DataSize Size of Data. + @return EFI_INVALID_PARAMETER Invalid signature list format. + @return EFI_SUCCESS Passed signature list format check successfully. + **/ EFI_STATUS -ProcessVarWithPk ( +CheckSignatureListFormat( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN VOID *Data, - IN UINTN DataSize, - IN VARIABLE_POINTER_TRACK *Variable, - IN UINT32 Attributes OPTIONAL, - IN BOOLEAN IsPk + IN UINTN DataSize ) { - EFI_STATUS Status; - VARIABLE_POINTER_TRACK PkVariable; - EFI_SIGNATURE_LIST *OldPkList; - EFI_SIGNATURE_DATA *OldPkData; - EFI_VARIABLE_AUTHENTICATION *CertData; - BOOLEAN TimeBase; - BOOLEAN Del; + EFI_SIGNATURE_LIST *SigList; + UINTN SigDataSize; + UINT32 Index; + UINT32 SigCount; + BOOLEAN IsPk; + VOID *RsaContext; + EFI_SIGNATURE_DATA *CertData; + UINTN CertLen; + + if (DataSize == 0) { + return EFI_SUCCESS; + } - if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) { - // - // PK and KEK should set EFI_VARIABLE_NON_VOLATILE attribute. - // - return EFI_INVALID_PARAMETER; + ASSERT (VariableName != NULL && VendorGuid != NULL && Data != NULL); + + if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_PLATFORM_KEY_NAME) == 0)){ + IsPk = TRUE; + } else if ((CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0) || + (CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) && + (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0 || StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0))){ + IsPk = FALSE; + } else { + return EFI_SUCCESS; } - if (mPlatformMode == USER_MODE) { + SigCount = 0; + SigList = (EFI_SIGNATURE_LIST *) Data; + SigDataSize = DataSize; + RsaContext = NULL; - if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) { - // - // EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute means time-based X509 Cert PK. - // - TimeBase = TRUE; - } else if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) { + // + // Walk throuth the input signature list and check the data format. + // If any signature is incorrectly formed, the whole check will fail. + // + while ((SigDataSize > 0) && (SigDataSize >= SigList->SignatureListSize)) { + for (Index = 0; Index < (sizeof (mSupportSigItem) / sizeof (EFI_SIGNATURE_ITEM)); Index++ ) { + if (CompareGuid (&SigList->SignatureType, &mSupportSigItem[Index].SigType)) { + // + // The value of SignatureSize should always be 16 (size of SignatureOwner + // component) add the data length according to signature type. + // + if (mSupportSigItem[Index].SigDataSize != ((UINT32) ~0) && + (SigList->SignatureSize - sizeof (EFI_GUID)) != mSupportSigItem[Index].SigDataSize) { + return EFI_INVALID_PARAMETER; + } + if (mSupportSigItem[Index].SigHeaderSize != ((UINTN) ~0) && + SigList->SignatureHeaderSize != mSupportSigItem[Index].SigHeaderSize) { + return EFI_INVALID_PARAMETER; + } + break; + } + } + + if (Index == (sizeof (mSupportSigItem) / sizeof (EFI_SIGNATURE_ITEM))) { // - // EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute means counter-based RSA-2048 Cert PK. + // Undefined signature type. // - TimeBase = FALSE; - } else { return EFI_INVALID_PARAMETER; } - if (TimeBase) { + if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) { // - // Verify against X509 Cert PK. + // Try to retrieve the RSA public key from the X.509 certificate. + // If this operation fails, it's not a valid certificate. // - Del = FALSE; - Status = VerifyTimeBasedPayload (VariableName, VendorGuid, Data, DataSize, Variable, Attributes, TRUE, &Del); - if (!EFI_ERROR (Status)) { - // - // If delete PK in user mode, need change to setup mode. - // - if (Del && IsPk) { - Status = UpdatePlatformMode (SETUP_MODE); - } + RsaContext = RsaNew (); + if (RsaContext == NULL) { + return EFI_INVALID_PARAMETER; } - return Status; - } else { - // - // Verify against RSA2048 Cert PK. - // - CertData = (EFI_VARIABLE_AUTHENTICATION *) Data; - if ((Variable->CurrPtr != NULL) && (CertData->MonotonicCount <= Variable->CurrPtr->MonotonicCount)) { - // - // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION. - // - return EFI_SECURITY_VIOLATION; + CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) + SigList->SignatureHeaderSize); + CertLen = SigList->SignatureSize - sizeof (EFI_GUID); + if (!RsaGetPublicKeyFromX509 (CertData->SignatureData, CertLen, &RsaContext)) { + RsaFree (RsaContext); + return EFI_INVALID_PARAMETER; } - // - // Get platform key from variable. - // - Status = FindVariable ( - EFI_PLATFORM_KEY_NAME, - &gEfiGlobalVariableGuid, - &PkVariable, - &mVariableModuleGlobal->VariableGlobal - ); - ASSERT_EFI_ERROR (Status); + RsaFree (RsaContext); + } + + if ((SigList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - SigList->SignatureHeaderSize) % SigList->SignatureSize != 0) { + return EFI_INVALID_PARAMETER; + } + SigCount += (SigList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - SigList->SignatureHeaderSize) / SigList->SignatureSize; + + SigDataSize -= SigList->SignatureListSize; + SigList = (EFI_SIGNATURE_LIST *) ((UINT8 *) SigList + SigList->SignatureListSize); + } + + if (((UINTN) SigList - (UINTN) Data) != DataSize) { + return EFI_INVALID_PARAMETER; + } + + if (IsPk && SigCount > 1) { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Update "VendorKeys" variable to record the out of band secure boot key modification. + + @return EFI_SUCCESS Variable is updated successfully. + @return Others Failed to update variable. - OldPkList = (EFI_SIGNATURE_LIST *) GetVariableDataPtr (PkVariable.CurrPtr); - OldPkData = (EFI_SIGNATURE_DATA *) ((UINT8 *) OldPkList + sizeof (EFI_SIGNATURE_LIST) + OldPkList->SignatureHeaderSize); - Status = VerifyCounterBasedPayload (Data, DataSize, OldPkData->SignatureData); - if (!EFI_ERROR (Status)) { - Status = UpdateVariable ( - VariableName, - VendorGuid, - (UINT8*)Data + AUTHINFO_SIZE, - DataSize - AUTHINFO_SIZE, - Attributes, - 0, - CertData->MonotonicCount, - Variable, - NULL - ); +**/ +EFI_STATUS +VendorKeyIsModified ( + VOID + ) +{ + EFI_STATUS Status; + VARIABLE_POINTER_TRACK Variable; + + if (mVendorKeyState == VENDOR_KEYS_MODIFIED) { + return EFI_SUCCESS; + } + mVendorKeyState = VENDOR_KEYS_MODIFIED; - if (!EFI_ERROR (Status)) { - // - // If delete PK in user mode, need change to setup mode. - // - if ((DataSize == AUTHINFO_SIZE) && IsPk) { - Status = UpdatePlatformMode (SETUP_MODE); - } - } - } + FindVariable (EFI_VENDOR_KEYS_NV_VARIABLE_NAME, &gEfiVendorKeysNvGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + Status = UpdateVariable ( + EFI_VENDOR_KEYS_NV_VARIABLE_NAME, + &gEfiVendorKeysNvGuid, + &mVendorKeyState, + sizeof (UINT8), + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, + 0, + 0, + &Variable, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + FindVariable (EFI_VENDOR_KEYS_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + return UpdateVariable ( + EFI_VENDOR_KEYS_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + &mVendorKeyState, + sizeof (UINT8), + EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + 0, + &Variable, + NULL + ); +} + +/** + Process variable with platform key for verification. + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode, and datasize and data are external input. + This function will do basic validation, before parse the data. + This function will parse the authentication carefully to avoid security issues, like + buffer overflow, integer overflow. + This function will check attribute carefully to avoid authentication bypass. + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. + @param[in] Data Data pointer. + @param[in] DataSize Size of Data found. If size is less than the + data, this value contains the required size. + @param[in] Variable The variable information which is used to keep track of variable usage. + @param[in] Attributes Attribute value of the variable + @param[in] IsPk Indicate whether it is to process pk. + + @return EFI_INVALID_PARAMETER Invalid parameter. + @return EFI_SECURITY_VIOLATION The variable does NOT pass the validation. + check carried out by the firmware. + @return EFI_SUCCESS Variable passed validation successfully. + +**/ +EFI_STATUS +ProcessVarWithPk ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VOID *Data, + IN UINTN DataSize, + IN VARIABLE_POINTER_TRACK *Variable, + IN UINT32 Attributes OPTIONAL, + IN BOOLEAN IsPk + ) +{ + EFI_STATUS Status; + BOOLEAN Del; + UINT8 *Payload; + UINTN PayloadSize; + + if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0 || + (Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) { + // + // PK, KEK and db/dbx should set EFI_VARIABLE_NON_VOLATILE attribute and should be a time-based + // authenticated variable. + // + return EFI_INVALID_PARAMETER; + } + + Del = FALSE; + if ((InCustomMode() && UserPhysicalPresent()) || (mPlatformMode == SETUP_MODE && !IsPk)) { + Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data); + PayloadSize = DataSize - AUTHINFO2_SIZE (Data); + if (PayloadSize == 0) { + Del = TRUE; + } + + Status = CheckSignatureListFormat(VariableName, VendorGuid, Payload, PayloadSize); + if (EFI_ERROR (Status)) { + return Status; } + + Status = UpdateVariable ( + VariableName, + VendorGuid, + Payload, + PayloadSize, + Attributes, + 0, + 0, + Variable, + &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp + ); + if (EFI_ERROR(Status)) { + return Status; + } + + if ((mPlatformMode != SETUP_MODE) || IsPk) { + Status = VendorKeyIsModified (); + } + } else if (mPlatformMode == USER_MODE) { + // + // Verify against X509 Cert in PK database. + // + Status = VerifyTimeBasedPayload ( + VariableName, + VendorGuid, + Data, + DataSize, + Variable, + Attributes, + AuthVarTypePk, + &Del + ); } else { - Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, Variable, NULL); // - // If enroll PK in setup mode, need change to user mode. + // Verify against the certificate in data payload. // - if ((DataSize != 0) && IsPk) { + Status = VerifyTimeBasedPayload ( + VariableName, + VendorGuid, + Data, + DataSize, + Variable, + Attributes, + AuthVarTypePayload, + &Del + ); + } + + if (!EFI_ERROR(Status) && IsPk) { + if (mPlatformMode == SETUP_MODE && !Del) { + // + // If enroll PK in setup mode, need change to user mode. + // Status = UpdatePlatformMode (USER_MODE); + } else if (mPlatformMode == USER_MODE && Del){ + // + // If delete PK in user mode, need change to setup mode. + // + Status = UpdatePlatformMode (SETUP_MODE); } } @@ -736,6 +1149,13 @@ ProcessVarWithPk ( /** Process variable with key exchange key for verification. + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode, and datasize and data are external input. + This function will do basic validation, before parse the data. + This function will parse the authentication carefully to avoid security issues, like + buffer overflow, integer overflow. + This function will check attribute carefully to avoid authentication bypass. + @param[in] VariableName Name of Variable to be found. @param[in] VendorGuid Variable vendor GUID. @param[in] Data Data pointer. @@ -745,8 +1165,8 @@ ProcessVarWithPk ( @param[in] Attributes Attribute value of the variable. @return EFI_INVALID_PARAMETER Invalid parameter. - @return EFI_SECURITY_VIOLATION The variable does NOT pass the validation - check carried out by the firmware. + @return EFI_SECURITY_VIOLATION The variable does NOT pass the validation + check carried out by the firmware. @return EFI_SUCCESS Variable pass validation successfully. **/ @@ -761,100 +1181,63 @@ ProcessVarWithKek ( ) { EFI_STATUS Status; - VARIABLE_POINTER_TRACK KekVariable; - EFI_SIGNATURE_LIST *KekList; - EFI_SIGNATURE_DATA *KekItem; - UINT32 KekCount; - EFI_VARIABLE_AUTHENTICATION *CertData; - EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock; - BOOLEAN IsFound; - UINT32 Index; - UINT32 KekDataSize; - - if (mPlatformMode == USER_MODE) { - if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) { - // - // In user mode, should set EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute. - // - return EFI_INVALID_PARAMETER; - } + UINT8 *Payload; + UINTN PayloadSize; - CertData = (EFI_VARIABLE_AUTHENTICATION *) Data; - CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData); - if ((Variable->CurrPtr != NULL) && (CertData->MonotonicCount <= Variable->CurrPtr->MonotonicCount)) { - // - // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION. - // - return EFI_SECURITY_VIOLATION; - } + if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0 || + (Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) { // - // Get KEK database from variable. + // DB and DBX should set EFI_VARIABLE_NON_VOLATILE attribute and should be a time-based + // authenticated variable. // - Status = FindVariable ( - EFI_KEY_EXCHANGE_KEY_NAME, - &gEfiGlobalVariableGuid, - &KekVariable, - &mVariableModuleGlobal->VariableGlobal - ); - ASSERT_EFI_ERROR (Status); - - KekDataSize = KekVariable.CurrPtr->DataSize; - KekList = (EFI_SIGNATURE_LIST *) GetVariableDataPtr (KekVariable.CurrPtr); + return EFI_INVALID_PARAMETER; + } + Status = EFI_SUCCESS; + if (mPlatformMode == USER_MODE && !(InCustomMode() && UserPhysicalPresent())) { // - // Enumerate all Kek items in this list to verify the variable certificate data. - // If anyone is authenticated successfully, it means the variable is correct! + // Time-based, verify against X509 Cert KEK. // - IsFound = FALSE; - while ((KekDataSize > 0) && (KekDataSize >= KekList->SignatureListSize)) { - if (CompareGuid (&KekList->SignatureType, &gEfiCertRsa2048Guid)) { - KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekList + sizeof (EFI_SIGNATURE_LIST) + KekList->SignatureHeaderSize); - KekCount = (KekList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - KekList->SignatureHeaderSize) / KekList->SignatureSize; - for (Index = 0; Index < KekCount; Index++) { - if (CompareMem (KekItem->SignatureData, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) { - IsFound = TRUE; - break; - } - KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekItem + KekList->SignatureSize); - } - } - KekDataSize -= KekList->SignatureListSize; - KekList = (EFI_SIGNATURE_LIST *) ((UINT8 *) KekList + KekList->SignatureListSize); - } - - if (!IsFound) { - return EFI_SECURITY_VIOLATION; - } - - Status = VerifyCounterBasedPayload (Data, DataSize, CertBlock->PublicKey); - if (!EFI_ERROR (Status)) { - Status = UpdateVariable ( - VariableName, - VendorGuid, - (UINT8*)Data + AUTHINFO_SIZE, - DataSize - AUTHINFO_SIZE, - Attributes, - 0, - CertData->MonotonicCount, - Variable, - NULL - ); - } + return VerifyTimeBasedPayload ( + VariableName, + VendorGuid, + Data, + DataSize, + Variable, + Attributes, + AuthVarTypeKek, + NULL + ); } else { // - // If in setup mode, no authentication needed. + // If in setup mode or custom secure boot mode, no authentication needed. // + Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data); + PayloadSize = DataSize - AUTHINFO2_SIZE (Data); + + Status = CheckSignatureListFormat(VariableName, VendorGuid, Payload, PayloadSize); + if (EFI_ERROR (Status)) { + return Status; + } + Status = UpdateVariable ( - VariableName, - VendorGuid, - Data, - DataSize, - Attributes, - 0, - 0, + VariableName, + VendorGuid, + Payload, + PayloadSize, + Attributes, + 0, + 0, Variable, - NULL + &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (mPlatformMode != SETUP_MODE) { + Status = VendorKeyIsModified (); + } } return Status; @@ -863,6 +1246,13 @@ ProcessVarWithKek ( /** Process variable with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS/EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode, and datasize and data are external input. + This function will do basic validation, before parse the data. + This function will parse the authentication carefully to avoid security issues, like + buffer overflow, integer overflow. + This function will check attribute carefully to avoid authentication bypass. + @param[in] VariableName Name of Variable to be found. @param[in] VendorGuid Variable vendor GUID. @@ -875,9 +1265,10 @@ ProcessVarWithKek ( @return EFI_INVALID_PARAMETER Invalid parameter. @return EFI_WRITE_PROTECTED Variable is write-protected and needs authentication with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. + @return EFI_OUT_OF_RESOURCES The Database to save the public key is full. @return EFI_SECURITY_VIOLATION The variable is with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS - set, but the AuthInfo does NOT pass the validation - check carried out by the firmware. + set, but the AuthInfo does NOT pass the validation + check carried out by the firmware. @return EFI_SUCCESS Variable is not write-protected or pass validation successfully. **/ @@ -899,20 +1290,53 @@ ProcessVariable ( EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock; UINT32 KeyIndex; UINT64 MonotonicCount; + VARIABLE_ENTRY_CONSISTENCY VariableDataEntry; - KeyIndex = 0; + KeyIndex = 0; CertData = NULL; CertBlock = NULL; PubKey = NULL; IsDeletion = FALSE; + if (NeedPhysicallyPresent(VariableName, VendorGuid) && !UserPhysicalPresent()) { + // + // This variable is protected, only physical present user could modify its value. + // + return EFI_SECURITY_VIOLATION; + } + + // + // A time-based authenticated variable and a count-based authenticated variable + // can't be updated by each other. + // + if (Variable->CurrPtr != NULL) { + if (((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) && + ((Variable->CurrPtr->Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0)) { + return EFI_SECURITY_VIOLATION; + } + + if (((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) && + ((Variable->CurrPtr->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0)) { + return EFI_SECURITY_VIOLATION; + } + } + // // Process Time-based Authenticated variable. // if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) { - return VerifyTimeBasedPayload (VariableName, VendorGuid, Data, DataSize, Variable, Attributes, FALSE, NULL); + return VerifyTimeBasedPayload ( + VariableName, + VendorGuid, + Data, + DataSize, + Variable, + Attributes, + AuthVarTypePriv, + NULL + ); } - + // // Determine if first time SetVariable with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS. // @@ -934,9 +1358,9 @@ ProcessVariable ( KeyIndex = Variable->CurrPtr->PubKeyIndex; IsFirstTime = FALSE; } - } else if ((Variable->CurrPtr != NULL) && - (Variable->CurrPtr->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0 - ) { + } else if ((Variable->CurrPtr != NULL) && + ((Variable->CurrPtr->Attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) != 0) + ) { // // If the variable is already write-protected, it always needs authentication before update. // @@ -979,7 +1403,7 @@ ProcessVariable ( // return EFI_SECURITY_VIOLATION; } - } + } // // Verify the certificate in Data payload. // @@ -987,15 +1411,22 @@ ProcessVariable ( if (EFI_ERROR (Status)) { return Status; } - + // // Now, the signature has been verified! // if (IsFirstTime && !IsDeletion) { + VariableDataEntry.VariableSize = DataSize - AUTHINFO_SIZE; + VariableDataEntry.Guid = VendorGuid; + VariableDataEntry.Name = VariableName; + // // Update public key database variable if need. // - KeyIndex = AddPubKeyInStore (PubKey); + KeyIndex = AddPubKeyInStore (PubKey, &VariableDataEntry); + if (KeyIndex == 0) { + return EFI_OUT_OF_RESOURCES; + } } // @@ -1004,6 +1435,129 @@ ProcessVariable ( return UpdateVariable (VariableName, VendorGuid, (UINT8*)Data + AUTHINFO_SIZE, DataSize - AUTHINFO_SIZE, Attributes, KeyIndex, MonotonicCount, Variable, NULL); } +/** + Merge two buffers which formatted as EFI_SIGNATURE_LIST. Only the new EFI_SIGNATURE_DATA + will be appended to the original EFI_SIGNATURE_LIST, duplicate EFI_SIGNATURE_DATA + will be ignored. + + @param[in, out] Data Pointer to original EFI_SIGNATURE_LIST. + @param[in] DataSize Size of Data buffer. + @param[in] FreeBufSize Size of free data buffer + @param[in] NewData Pointer to new EFI_SIGNATURE_LIST to be appended. + @param[in] NewDataSize Size of NewData buffer. + @param[out] MergedBufSize Size of the merged buffer + + @return EFI_BUFFER_TOO_SMALL if input Data buffer overflowed + +**/ +EFI_STATUS +AppendSignatureList ( + IN OUT VOID *Data, + IN UINTN DataSize, + IN UINTN FreeBufSize, + IN VOID *NewData, + IN UINTN NewDataSize, + OUT UINTN *MergedBufSize + ) +{ + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_DATA *Cert; + UINTN CertCount; + EFI_SIGNATURE_LIST *NewCertList; + EFI_SIGNATURE_DATA *NewCert; + UINTN NewCertCount; + UINTN Index; + UINTN Index2; + UINTN Size; + UINT8 *Tail; + UINTN CopiedCount; + UINTN SignatureListSize; + BOOLEAN IsNewCert; + + Tail = (UINT8 *) Data + DataSize; + + NewCertList = (EFI_SIGNATURE_LIST *) NewData; + while ((NewDataSize > 0) && (NewDataSize >= NewCertList->SignatureListSize)) { + NewCert = (EFI_SIGNATURE_DATA *) ((UINT8 *) NewCertList + sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize); + NewCertCount = (NewCertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - NewCertList->SignatureHeaderSize) / NewCertList->SignatureSize; + + CopiedCount = 0; + for (Index = 0; Index < NewCertCount; Index++) { + IsNewCert = TRUE; + + Size = DataSize; + CertList = (EFI_SIGNATURE_LIST *) Data; + while ((Size > 0) && (Size >= CertList->SignatureListSize)) { + if (CompareGuid (&CertList->SignatureType, &NewCertList->SignatureType) && + (CertList->SignatureSize == NewCertList->SignatureSize)) { + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + for (Index2 = 0; Index2 < CertCount; Index2++) { + // + // Iterate each Signature Data in this Signature List. + // + if (CompareMem (NewCert, Cert, CertList->SignatureSize) == 0) { + IsNewCert = FALSE; + break; + } + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); + } + } + + if (!IsNewCert) { + break; + } + Size -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); + } + + if (IsNewCert) { + // + // New EFI_SIGNATURE_DATA, append it. + // + if (CopiedCount == 0) { + if (FreeBufSize < sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize) { + return EFI_BUFFER_TOO_SMALL; + } + + // + // Copy EFI_SIGNATURE_LIST header for only once. + // + + CopyMem (Tail, NewCertList, sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize); + Tail = Tail + sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize; + FreeBufSize -= sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize; + } + + if (FreeBufSize < NewCertList->SignatureSize) { + return EFI_BUFFER_TOO_SMALL; + } + CopyMem (Tail, NewCert, NewCertList->SignatureSize); + Tail += NewCertList->SignatureSize; + FreeBufSize -= NewCertList->SignatureSize; + CopiedCount++; + } + + NewCert = (EFI_SIGNATURE_DATA *) ((UINT8 *) NewCert + NewCertList->SignatureSize); + } + + // + // Update SignatureListSize in newly appended EFI_SIGNATURE_LIST. + // + if (CopiedCount != 0) { + SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize + (CopiedCount * NewCertList->SignatureSize); + CertList = (EFI_SIGNATURE_LIST *) (Tail - SignatureListSize); + CertList->SignatureListSize = (UINT32) SignatureListSize; + } + + NewDataSize -= NewCertList->SignatureListSize; + NewCertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) NewCertList + NewCertList->SignatureListSize); + } + + *MergedBufSize = (Tail - (UINT8 *) Data); + return EFI_SUCCESS; +} + /** Compare two EFI_TIME data. @@ -1030,56 +1584,520 @@ CompareTimeStamp ( } else if (FirstTime->Hour != SecondTime->Hour) { return (BOOLEAN) (FirstTime->Hour < SecondTime->Hour); } else if (FirstTime->Minute != SecondTime->Minute) { - return (BOOLEAN) (FirstTime->Minute < FirstTime->Minute); - } + return (BOOLEAN) (FirstTime->Minute < SecondTime->Minute); + } return (BOOLEAN) (FirstTime->Second <= SecondTime->Second); } /** - Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set + Find matching signer's certificates for common authenticated variable + by corresponding VariableName and VendorGuid from "certdb". - @param[in] VariableName Name of Variable to be found. - @param[in] VendorGuid Variable vendor GUID. - @param[in] Data Data pointer. - @param[in] DataSize Size of Data found. If size is less than the - data, this value contains the required size. - @param[in] Variable The variable information which is used to keep track of variable usage. - @param[in] Attributes Attribute value of the variable. - @param[in] Pk Verify against PK or KEK database. - @param[out] VarDel Delete the variable or not. + The data format of "certdb": + // + // UINT32 CertDbListSize; + // /// AUTH_CERT_DB_DATA Certs1[]; + // /// AUTH_CERT_DB_DATA Certs2[]; + // /// ... + // /// AUTH_CERT_DB_DATA Certsn[]; + // - @retval EFI_INVALID_PARAMETER Invalid parameter. - @retval EFI_SECURITY_VIOLATION The variable does NOT pass the validation - check carried out by the firmware. - @retval EFI_OUT_OF_RESOURCES Failed to process variable due to lack - of resources. - @retval EFI_SUCCESS Variable pass validation successfully. + @param[in] VariableName Name of authenticated Variable. + @param[in] VendorGuid Vendor GUID of authenticated Variable. + @param[in] Data Pointer to variable "certdb". + @param[in] DataSize Size of variable "certdb". + @param[out] CertOffset Offset of matching CertData, from starting of Data. + @param[out] CertDataSize Length of CertData in bytes. + @param[out] CertNodeOffset Offset of matching AUTH_CERT_DB_DATA , from + starting of Data. + @param[out] CertNodeSize Length of AUTH_CERT_DB_DATA in bytes. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_NOT_FOUND Fail to find matching certs. + @retval EFI_SUCCESS Find matching certs and output parameters. **/ EFI_STATUS -VerifyTimeBasedPayload ( - IN CHAR16 *VariableName, - IN EFI_GUID *VendorGuid, - IN VOID *Data, - IN UINTN DataSize, - IN VARIABLE_POINTER_TRACK *Variable, - IN UINT32 Attributes, - IN BOOLEAN Pk, - OUT BOOLEAN *VarDel +FindCertsFromDb ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT8 *Data, + IN UINTN DataSize, + OUT UINT32 *CertOffset, OPTIONAL + OUT UINT32 *CertDataSize, OPTIONAL + OUT UINT32 *CertNodeOffset,OPTIONAL + OUT UINT32 *CertNodeSize OPTIONAL ) { - UINT8 *RootCert; - UINT8 *SigData; - UINT8 *PayLoadPtr; - UINTN RootCertSize; - UINTN Index; - UINTN CertCount; - UINTN PayLoadSize; - UINT32 Attr; - UINT32 SigDataSize; - UINT32 KekDataSize; - BOOLEAN Result; + UINT32 Offset; + AUTH_CERT_DB_DATA *Ptr; + UINT32 CertSize; + UINT32 NameSize; + UINT32 NodeSize; + UINT32 CertDbListSize; + + if ((VariableName == NULL) || (VendorGuid == NULL) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether DataSize matches recorded CertDbListSize. + // + if (DataSize < sizeof (UINT32)) { + return EFI_INVALID_PARAMETER; + } + + CertDbListSize = ReadUnaligned32 ((UINT32 *) Data); + + if (CertDbListSize != (UINT32) DataSize) { + return EFI_INVALID_PARAMETER; + } + + Offset = sizeof (UINT32); + + // + // Get corresponding certificates by VendorGuid and VariableName. + // + while (Offset < (UINT32) DataSize) { + Ptr = (AUTH_CERT_DB_DATA *) (Data + Offset); + // + // Check whether VendorGuid matches. + // + if (CompareGuid (&Ptr->VendorGuid, VendorGuid)) { + NodeSize = ReadUnaligned32 (&Ptr->CertNodeSize); + NameSize = ReadUnaligned32 (&Ptr->NameSize); + CertSize = ReadUnaligned32 (&Ptr->CertDataSize); + + if (NodeSize != sizeof (EFI_GUID) + sizeof (UINT32) * 3 + CertSize + + sizeof (CHAR16) * NameSize) { + return EFI_INVALID_PARAMETER; + } + + Offset = Offset + sizeof (EFI_GUID) + sizeof (UINT32) * 3; + // + // Check whether VariableName matches. + // + if ((NameSize == StrLen (VariableName)) && + (CompareMem (Data + Offset, VariableName, NameSize * sizeof (CHAR16)) == 0)) { + Offset = Offset + NameSize * sizeof (CHAR16); + + if (CertOffset != NULL) { + *CertOffset = Offset; + } + + if (CertDataSize != NULL) { + *CertDataSize = CertSize; + } + + if (CertNodeOffset != NULL) { + *CertNodeOffset = (UINT32) ((UINT8 *) Ptr - Data); + } + + if (CertNodeSize != NULL) { + *CertNodeSize = NodeSize; + } + + return EFI_SUCCESS; + } else { + Offset = Offset + NameSize * sizeof (CHAR16) + CertSize; + } + } else { + NodeSize = ReadUnaligned32 (&Ptr->CertNodeSize); + Offset = Offset + NodeSize; + } + } + + return EFI_NOT_FOUND; +} + +/** + Retrieve signer's certificates for common authenticated variable + by corresponding VariableName and VendorGuid from "certdb". + + @param[in] VariableName Name of authenticated Variable. + @param[in] VendorGuid Vendor GUID of authenticated Variable. + @param[out] CertData Pointer to signer's certificates. + @param[out] CertDataSize Length of CertData in bytes. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_NOT_FOUND Fail to find "certdb" or matching certs. + @retval EFI_SUCCESS Get signer's certificates successfully. + +**/ +EFI_STATUS +GetCertsFromDb ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT8 **CertData, + OUT UINT32 *CertDataSize + ) +{ + VARIABLE_POINTER_TRACK CertDbVariable; + EFI_STATUS Status; + UINT8 *Data; + UINTN DataSize; + UINT32 CertOffset; + + if ((VariableName == NULL) || (VendorGuid == NULL) || (CertData == NULL) || (CertDataSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Get variable "certdb". + // + Status = FindVariable ( + EFI_CERT_DB_NAME, + &gEfiCertDbGuid, + &CertDbVariable, + &mVariableModuleGlobal->VariableGlobal, + FALSE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + DataSize = DataSizeOfVariable (CertDbVariable.CurrPtr); + Data = GetVariableDataPtr (CertDbVariable.CurrPtr); + if ((DataSize == 0) || (Data == NULL)) { + ASSERT (FALSE); + return EFI_NOT_FOUND; + } + + Status = FindCertsFromDb ( + VariableName, + VendorGuid, + Data, + DataSize, + &CertOffset, + CertDataSize, + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + *CertData = Data + CertOffset; + return EFI_SUCCESS; +} + +/** + Delete matching signer's certificates when deleting common authenticated + variable by corresponding VariableName and VendorGuid from "certdb". + + @param[in] VariableName Name of authenticated Variable. + @param[in] VendorGuid Vendor GUID of authenticated Variable. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_NOT_FOUND Fail to find "certdb" or matching certs. + @retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources. + @retval EFI_SUCCESS The operation is completed successfully. + +**/ +EFI_STATUS +DeleteCertsFromDb ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + VARIABLE_POINTER_TRACK CertDbVariable; + EFI_STATUS Status; + UINT8 *Data; + UINTN DataSize; + UINT32 VarAttr; + UINT32 CertNodeOffset; + UINT32 CertNodeSize; + UINT8 *NewCertDb; + UINT32 NewCertDbSize; + + if ((VariableName == NULL) || (VendorGuid == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Get variable "certdb". + // + Status = FindVariable ( + EFI_CERT_DB_NAME, + &gEfiCertDbGuid, + &CertDbVariable, + &mVariableModuleGlobal->VariableGlobal, + FALSE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + DataSize = DataSizeOfVariable (CertDbVariable.CurrPtr); + Data = GetVariableDataPtr (CertDbVariable.CurrPtr); + if ((DataSize == 0) || (Data == NULL)) { + ASSERT (FALSE); + return EFI_NOT_FOUND; + } + + if (DataSize == sizeof (UINT32)) { + // + // There is no certs in certdb. + // + return EFI_SUCCESS; + } + + // + // Get corresponding cert node from certdb. + // + Status = FindCertsFromDb ( + VariableName, + VendorGuid, + Data, + DataSize, + NULL, + NULL, + &CertNodeOffset, + &CertNodeSize + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (DataSize < (CertNodeOffset + CertNodeSize)) { + return EFI_NOT_FOUND; + } + + // + // Construct new data content of variable "certdb". + // + NewCertDbSize = (UINT32) DataSize - CertNodeSize; + NewCertDb = (UINT8*) mCertDbStore; + + // + // Copy the DB entries before deleting node. + // + CopyMem (NewCertDb, Data, CertNodeOffset); + // + // Update CertDbListSize. + // + CopyMem (NewCertDb, &NewCertDbSize, sizeof (UINT32)); + // + // Copy the DB entries after deleting node. + // + if (DataSize > (CertNodeOffset + CertNodeSize)) { + CopyMem ( + NewCertDb + CertNodeOffset, + Data + CertNodeOffset + CertNodeSize, + DataSize - CertNodeOffset - CertNodeSize + ); + } + + // + // Set "certdb". + // + VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + Status = UpdateVariable ( + EFI_CERT_DB_NAME, + &gEfiCertDbGuid, + NewCertDb, + NewCertDbSize, + VarAttr, + 0, + 0, + &CertDbVariable, + NULL + ); + + return Status; +} + +/** + Insert signer's certificates for common authenticated variable with VariableName + and VendorGuid in AUTH_CERT_DB_DATA to "certdb". + + @param[in] VariableName Name of authenticated Variable. + @param[in] VendorGuid Vendor GUID of authenticated Variable. + @param[in] CertData Pointer to signer's certificates. + @param[in] CertDataSize Length of CertData in bytes. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_ACCESS_DENIED An AUTH_CERT_DB_DATA entry with same VariableName + and VendorGuid already exists. + @retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources. + @retval EFI_SUCCESS Insert an AUTH_CERT_DB_DATA entry to "certdb" + +**/ +EFI_STATUS +InsertCertsToDb ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT8 *CertData, + IN UINTN CertDataSize + ) +{ + VARIABLE_POINTER_TRACK CertDbVariable; + EFI_STATUS Status; + UINT8 *Data; + UINTN DataSize; + UINT32 VarAttr; + UINT8 *NewCertDb; + UINT32 NewCertDbSize; + UINT32 CertNodeSize; + UINT32 NameSize; + AUTH_CERT_DB_DATA *Ptr; + + if ((VariableName == NULL) || (VendorGuid == NULL) || (CertData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Get variable "certdb". + // + Status = FindVariable ( + EFI_CERT_DB_NAME, + &gEfiCertDbGuid, + &CertDbVariable, + &mVariableModuleGlobal->VariableGlobal, + FALSE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + DataSize = DataSizeOfVariable (CertDbVariable.CurrPtr); + Data = GetVariableDataPtr (CertDbVariable.CurrPtr); + if ((DataSize == 0) || (Data == NULL)) { + ASSERT (FALSE); + return EFI_NOT_FOUND; + } + + // + // Find whether matching cert node already exists in "certdb". + // If yes return error. + // + Status = FindCertsFromDb ( + VariableName, + VendorGuid, + Data, + DataSize, + NULL, + NULL, + NULL, + NULL + ); + + if (!EFI_ERROR (Status)) { + ASSERT (FALSE); + return EFI_ACCESS_DENIED; + } + + // + // Construct new data content of variable "certdb". + // + NameSize = (UINT32) StrLen (VariableName); + CertNodeSize = sizeof (AUTH_CERT_DB_DATA) + (UINT32) CertDataSize + NameSize * sizeof (CHAR16); + NewCertDbSize = (UINT32) DataSize + CertNodeSize; + if (NewCertDbSize > MAX_CERTDB_SIZE) { + return EFI_OUT_OF_RESOURCES; + } + NewCertDb = (UINT8*) mCertDbStore; + + // + // Copy the DB entries before deleting node. + // + CopyMem (NewCertDb, Data, DataSize); + // + // Update CertDbListSize. + // + CopyMem (NewCertDb, &NewCertDbSize, sizeof (UINT32)); + // + // Construct new cert node. + // + Ptr = (AUTH_CERT_DB_DATA *) (NewCertDb + DataSize); + CopyGuid (&Ptr->VendorGuid, VendorGuid); + CopyMem (&Ptr->CertNodeSize, &CertNodeSize, sizeof (UINT32)); + CopyMem (&Ptr->NameSize, &NameSize, sizeof (UINT32)); + CopyMem (&Ptr->CertDataSize, &CertDataSize, sizeof (UINT32)); + + CopyMem ( + (UINT8 *) Ptr + sizeof (AUTH_CERT_DB_DATA), + VariableName, + NameSize * sizeof (CHAR16) + ); + + CopyMem ( + (UINT8 *) Ptr + sizeof (AUTH_CERT_DB_DATA) + NameSize * sizeof (CHAR16), + CertData, + CertDataSize + ); + + // + // Set "certdb". + // + VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + Status = UpdateVariable ( + EFI_CERT_DB_NAME, + &gEfiCertDbGuid, + NewCertDb, + NewCertDbSize, + VarAttr, + 0, + 0, + &CertDbVariable, + NULL + ); + + return Status; +} + +/** + Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode, and datasize and data are external input. + This function will do basic validation, before parse the data. + This function will parse the authentication carefully to avoid security issues, like + buffer overflow, integer overflow. + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. + @param[in] Data Data pointer. + @param[in] DataSize Size of Data found. If size is less than the + data, this value contains the required size. + @param[in] Variable The variable information which is used to keep track of variable usage. + @param[in] Attributes Attribute value of the variable. + @param[in] AuthVarType Verify against PK, KEK database, private database or certificate in data payload. + @param[out] VarDel Delete the variable or not. + + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_SECURITY_VIOLATION The variable does NOT pass the validation + check carried out by the firmware. + @retval EFI_OUT_OF_RESOURCES Failed to process variable due to lack + of resources. + @retval EFI_SUCCESS Variable pass validation successfully. + +**/ +EFI_STATUS +VerifyTimeBasedPayload ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VOID *Data, + IN UINTN DataSize, + IN VARIABLE_POINTER_TRACK *Variable, + IN UINT32 Attributes, + IN AUTHVAR_TYPE AuthVarType, + OUT BOOLEAN *VarDel + ) +{ + UINT8 *RootCert; + UINT8 *SigData; + UINT8 *PayloadPtr; + UINTN RootCertSize; + UINTN Index; + UINTN CertCount; + UINTN PayloadSize; + UINT32 Attr; + UINT32 SigDataSize; + UINT32 KekDataSize; BOOLEAN VerifyStatus; EFI_STATUS Status; EFI_SIGNATURE_LIST *CertList; @@ -1089,24 +2107,44 @@ VerifyTimeBasedPayload ( UINT8 *NewData; UINTN NewDataSize; VARIABLE_POINTER_TRACK PkVariable; + UINT8 *Buffer; + UINTN Length; + UINT8 *SignerCerts; + UINT8 *WrapSigData; + UINTN CertStackSize; + UINT8 *CertsInCertDb; + UINT32 CertsSizeinDb; - - Result = FALSE; VerifyStatus = FALSE; CertData = NULL; NewData = NULL; Attr = Attributes; + WrapSigData = NULL; + SignerCerts = NULL; + RootCert = NULL; // - // When the attribute EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS is + // When the attribute EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS is // set, then the Data buffer shall begin with an instance of a complete (and serialized) - // EFI_VARIABLE_AUTHENTICATION_2 descriptor. The descriptor shall be followed by the new - // variable value and DataSize shall reflect the combined size of the descriptor and the new - // variable value. The authentication descriptor is not part of the variable data and is not + // EFI_VARIABLE_AUTHENTICATION_2 descriptor. The descriptor shall be followed by the new + // variable value and DataSize shall reflect the combined size of the descriptor and the new + // variable value. The authentication descriptor is not part of the variable data and is not // returned by subsequent calls to GetVariable(). // CertData = (EFI_VARIABLE_AUTHENTICATION_2 *) Data; - + + // + // Verify that Pad1, Nanosecond, TimeZone, Daylight and Pad2 components of the + // TimeStamp value are set to zero. + // + if ((CertData->TimeStamp.Pad1 != 0) || + (CertData->TimeStamp.Nanosecond != 0) || + (CertData->TimeStamp.TimeZone != 0) || + (CertData->TimeStamp.Daylight != 0) || + (CertData->TimeStamp.Pad2 != 0)) { + return EFI_SECURITY_VIOLATION; + } + if ((Variable->CurrPtr != NULL) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) { if (CompareTimeStamp (&CertData->TimeStamp, &Variable->CurrPtr->TimeStamp)) { // @@ -1121,95 +2159,91 @@ VerifyTimeBasedPayload ( // Cert type should be EFI_CERT_TYPE_PKCS7_GUID. // if ((CertData->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) || - !CompareGuid (&CertData->AuthInfo.CertType, &gEfiCertPkcs7Guid) - ) { + !CompareGuid (&CertData->AuthInfo.CertType, &gEfiCertPkcs7Guid)) { // // Invalid AuthInfo type, return EFI_SECURITY_VIOLATION. // return EFI_SECURITY_VIOLATION; } - + // // Find out Pkcs7 SignedData which follows the EFI_VARIABLE_AUTHENTICATION_2 descriptor. // AuthInfo.Hdr.dwLength is the length of the entire certificate, including the length of the header. // - SigData = (UINT8*) ((UINTN)Data + OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) + OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)); + SigData = CertData->AuthInfo.CertData; + SigDataSize = CertData->AuthInfo.Hdr.dwLength - (UINT32) (OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)); - // - // Sanity check to avoid corrupted certificate input. - // - if (CertData->AuthInfo.Hdr.dwLength < (UINT32)(OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData))) { - return EFI_SECURITY_VIOLATION; - } - - - - SigDataSize = CertData->AuthInfo.Hdr.dwLength - (UINT32)(OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)); - // // Find out the new data payload which follows Pkcs7 SignedData directly. // - PayLoadPtr = (UINT8*) ((UINTN) SigData + (UINTN) SigDataSize); - - // - // Sanity check to avoid corrupted certificate input. - // - if (DataSize < (OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) + OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)+ (UINTN) SigDataSize)) { - return EFI_SECURITY_VIOLATION; - } - - PayLoadSize = DataSize - OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) - OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData) - (UINTN) SigDataSize; - + PayloadPtr = SigData + SigDataSize; + PayloadSize = DataSize - OFFSET_OF_AUTHINFO2_CERT_DATA - (UINTN) SigDataSize; // // Construct a buffer to fill with (VariableName, VendorGuid, Attributes, TimeStamp, Data). // - NewDataSize = PayLoadSize + sizeof (EFI_TIME) + sizeof (UINT32) + - sizeof (EFI_GUID) + StrSize (VariableName); - NewData = (UINT8 *) AllocateZeroPool (NewDataSize); + NewDataSize = PayloadSize + sizeof (EFI_TIME) + sizeof (UINT32) + + sizeof (EFI_GUID) + StrSize (VariableName) - sizeof (CHAR16); + NewData = mSerializationRuntimeBuffer; - if (NewData == NULL) { - return EFI_OUT_OF_RESOURCES; - } + Buffer = NewData; + Length = StrLen (VariableName) * sizeof (CHAR16); + CopyMem (Buffer, VariableName, Length); + Buffer += Length; - CopyMem (NewData, VariableName, StrSize (VariableName)); + Length = sizeof (EFI_GUID); + CopyMem (Buffer, VendorGuid, Length); + Buffer += Length; - CopyMem (NewData + StrSize (VariableName), VendorGuid, sizeof (EFI_GUID)); + Length = sizeof (UINT32); + CopyMem (Buffer, &Attr, Length); + Buffer += Length; - CopyMem ( - NewData + StrSize (VariableName) + sizeof (EFI_GUID), - &Attr, - sizeof (UINT32) - ); + Length = sizeof (EFI_TIME); + CopyMem (Buffer, &CertData->TimeStamp, Length); + Buffer += Length; - CopyMem ( - NewData + StrSize (VariableName) + sizeof (EFI_GUID) + sizeof (UINT32), - &CertData->TimeStamp, - sizeof (EFI_TIME) - ); - - CopyMem (NewData + (NewDataSize - PayLoadSize), PayLoadPtr, PayLoadSize); + CopyMem (Buffer, PayloadPtr, PayloadSize); + if (AuthVarType == AuthVarTypePk) { + // + // Verify that the signature has been made with the current Platform Key (no chaining for PK). + // First, get signer's certificates from SignedData. + // + VerifyStatus = Pkcs7GetSigners ( + SigData, + SigDataSize, + &SignerCerts, + &CertStackSize, + &RootCert, + &RootCertSize + ); + if (!VerifyStatus) { + goto Exit; + } - if (Pk) { // - // Get platform key from variable. + // Second, get the current platform key from variable. Check whether it's identical with signer's certificates + // in SignedData. If not, return error immediately. // Status = FindVariable ( - EFI_PLATFORM_KEY_NAME, - &gEfiGlobalVariableGuid, - &PkVariable, - &mVariableModuleGlobal->VariableGlobal + EFI_PLATFORM_KEY_NAME, + &gEfiGlobalVariableGuid, + &PkVariable, + &mVariableModuleGlobal->VariableGlobal, + FALSE ); if (EFI_ERROR (Status)) { - return Status; + VerifyStatus = FALSE; + goto Exit; } - CertList = (EFI_SIGNATURE_LIST *) GetVariableDataPtr (PkVariable.CurrPtr); Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); - RootCert = Cert->SignatureData; - RootCertSize = CertList->SignatureSize; - + if ((RootCertSize != (CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1))) || + (CompareMem (Cert->SignatureData, RootCert, RootCertSize) != 0)) { + VerifyStatus = FALSE; + goto Exit; + } // // Verify Pkcs7 SignedData via Pkcs7Verify library. @@ -1223,16 +2257,17 @@ VerifyTimeBasedPayload ( NewDataSize ); - } else { - + } else if (AuthVarType == AuthVarTypeKek) { + // // Get KEK database from variable. // Status = FindVariable ( - EFI_KEY_EXCHANGE_KEY_NAME, - &gEfiGlobalVariableGuid, - &KekVariable, - &mVariableModuleGlobal->VariableGlobal + EFI_KEY_EXCHANGE_KEY_NAME, + &gEfiGlobalVariableGuid, + &KekVariable, + &mVariableModuleGlobal->VariableGlobal, + FALSE ); if (EFI_ERROR (Status)) { return Status; @@ -1240,7 +2275,7 @@ VerifyTimeBasedPayload ( // // Ready to verify Pkcs7 SignedData. Go through KEK Signature Database to find out X.509 CertList. - // + // KekDataSize = KekVariable.CurrPtr->DataSize; CertList = (EFI_SIGNATURE_LIST *) GetVariableDataPtr (KekVariable.CurrPtr); while ((KekDataSize > 0) && (KekDataSize >= CertList->SignatureListSize)) { @@ -1252,8 +2287,8 @@ VerifyTimeBasedPayload ( // Iterate each Signature Data Node within this CertList for a verify // RootCert = Cert->SignatureData; - RootCertSize = CertList->SignatureSize; - + RootCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1); + // // Verify Pkcs7 SignedData via Pkcs7Verify library. // @@ -1274,32 +2309,127 @@ VerifyTimeBasedPayload ( KekDataSize -= CertList->SignatureListSize; CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); } + } else if (AuthVarType == AuthVarTypePriv) { + + // + // Process common authenticated variable except PK/KEK/DB/DBX. + // Get signer's certificates from SignedData. + // + VerifyStatus = Pkcs7GetSigners ( + SigData, + SigDataSize, + &SignerCerts, + &CertStackSize, + &RootCert, + &RootCertSize + ); + if (!VerifyStatus) { + goto Exit; + } + + // + // Get previously stored signer's certificates from certdb for existing + // variable. Check whether they are identical with signer's certificates + // in SignedData. If not, return error immediately. + // + if ((Variable->CurrPtr != NULL)) { + VerifyStatus = FALSE; + + Status = GetCertsFromDb (VariableName, VendorGuid, &CertsInCertDb, &CertsSizeinDb); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if ((CertStackSize != CertsSizeinDb) || + (CompareMem (SignerCerts, CertsInCertDb, CertsSizeinDb) != 0)) { + goto Exit; + } + } + + VerifyStatus = Pkcs7Verify ( + SigData, + SigDataSize, + RootCert, + RootCertSize, + NewData, + NewDataSize + ); + if (!VerifyStatus) { + goto Exit; + } + + // + // Delete signer's certificates when delete the common authenticated variable. + // + if ((PayloadSize == 0) && (Variable->CurrPtr != NULL) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) { + Status = DeleteCertsFromDb (VariableName, VendorGuid); + if (EFI_ERROR (Status)) { + VerifyStatus = FALSE; + goto Exit; + } + } else if (Variable->CurrPtr == NULL && PayloadSize != 0) { + // + // Insert signer's certificates when adding a new common authenticated variable. + // + Status = InsertCertsToDb (VariableName, VendorGuid, SignerCerts, CertStackSize); + if (EFI_ERROR (Status)) { + VerifyStatus = FALSE; + goto Exit; + } + } + } else if (AuthVarType == AuthVarTypePayload) { + CertList = (EFI_SIGNATURE_LIST *) PayloadPtr; + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + RootCert = Cert->SignatureData; + RootCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1); + + // Verify Pkcs7 SignedData via Pkcs7Verify library. + // + VerifyStatus = Pkcs7Verify ( + SigData, + SigDataSize, + RootCert, + RootCertSize, + NewData, + NewDataSize + ); + } else { + return EFI_SECURITY_VIOLATION; } Exit: - FreePool (NewData); + if (AuthVarType == AuthVarTypePk || AuthVarType == AuthVarTypePriv) { + Pkcs7FreeSigners (RootCert); + Pkcs7FreeSigners (SignerCerts); + } if (!VerifyStatus) { return EFI_SECURITY_VIOLATION; } - if ((PayLoadSize == 0) && (VarDel != NULL)) { + Status = CheckSignatureListFormat(VariableName, VendorGuid, PayloadPtr, PayloadSize); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((PayloadSize == 0) && (VarDel != NULL)) { *VarDel = TRUE; } - + // // Final step: Update/Append Variable if it pass Pkcs7Verify // - return UpdateVariable ( - VariableName, - VendorGuid, - PayLoadPtr, - PayLoadSize, - Attributes, - 0, - 0, - Variable, - &CertData->TimeStamp - ); + return UpdateVariable ( + VariableName, + VendorGuid, + PayloadPtr, + PayloadSize, + Attributes, + 0, + 0, + Variable, + &CertData->TimeStamp + ); } +