From 4fc08e8d683522f255727626197d919a40d4836c Mon Sep 17 00:00:00 2001 From: Chao Zhang Date: Mon, 7 Dec 2015 06:20:02 +0000 Subject: [PATCH] SecurityPkg: AuthVariableLib: Customized SecureBoot Mode transition. Implement Customized SecureBoot Mode transition logic according to Mantis 1263, including AuditMode/DeployedMode/PK update management. Also implement image verification logic in AuditMode. Image Certificate & Hash are recorded to EFI Image Execution Table. https://mantis.uefi.org/mantis/view.php?id=1263 Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Chao Zhang Reviewed-by: Zeng Star Reviewed-by: Long Qin git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19133 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Library/AuthVariableLib/AuthService.c | 1304 +++++++++++++++-- .../AuthVariableLib/AuthServiceInternal.h | 93 ++ .../Library/AuthVariableLib/AuthVariableLib.c | 111 +- .../AuthVariableLib/AuthVariableLib.inf | 4 + .../DxeImageVerificationLib.c | 670 ++++++++- 5 files changed, 1913 insertions(+), 269 deletions(-) diff --git a/SecurityPkg/Library/AuthVariableLib/AuthService.c b/SecurityPkg/Library/AuthVariableLib/AuthService.c index 1f9ba15384..5546c2e5c9 100644 --- a/SecurityPkg/Library/AuthVariableLib/AuthService.c +++ b/SecurityPkg/Library/AuthVariableLib/AuthService.c @@ -56,6 +56,54 @@ EFI_SIGNATURE_ITEM mSupportSigItem[] = { {EFI_CERT_X509_SHA512_GUID, 0, 80 } }; +// +// Secure Boot Mode state machine +// +SECURE_BOOT_MODE mSecureBootState[SecureBootModeTypeMax] = { + // USER MODE + { + AUDIT_MODE_DISABLE, // AuditMode + FALSE, // IsAuditModeRO, AuditMode is RW + DEPLOYED_MODE_DISABLE, // DeployedMode + FALSE, // IsDeployedModeRO, DeployedMode is RW + SETUP_MODE_DISABLE, // SetupMode + // SetupMode is always RO + SECURE_BOOT_MODE_ENABLE // SecureBoot + }, + // SETUP MODE + { + AUDIT_MODE_DISABLE, // AuditMode + FALSE, // IsAuditModeRO, AuditMode is RW + DEPLOYED_MODE_DISABLE, // DeployedMode + TRUE, // IsDeployedModeRO, DeployedMode is RO + SETUP_MODE_ENABLE, // SetupMode + // SetupMode is always RO + SECURE_BOOT_MODE_DISABLE // SecureBoot + }, + // AUDIT MODE + { + AUDIT_MODE_ENABLE, // AuditMode + TRUE, // AuditModeValAttr RO, AuditMode is RO + DEPLOYED_MODE_DISABLE, // DeployedMode + TRUE, // DeployedModeValAttr RO, DeployedMode is RO + SETUP_MODE_ENABLE, // SetupMode + // SetupMode is always RO + SECURE_BOOT_MODE_DISABLE // SecureBoot + }, + // DEPLOYED MODE + { + AUDIT_MODE_DISABLE, // AuditMode, AuditMode is RO + TRUE, // AuditModeValAttr RO + DEPLOYED_MODE_ENABLE, // DeployedMode + TRUE, // DeployedModeValAttr RO, DeployedMode is RO + SETUP_MODE_DISABLE, // SetupMode + // SetupMode is always RO + SECURE_BOOT_MODE_ENABLE // SecureBoot + } +}; + +SECURE_BOOT_MODE_TYPE mSecureBootMode; + /** Finds variable in storage blocks of volatile and non-volatile storage areas. @@ -249,6 +297,914 @@ AuthServiceInternalUpdateVariableWithTimeStamp ( ); } +/** + Initialize Secure Boot variables. + + @retval EFI_SUCCESS The initialization operation is successful. + @retval EFI_OUT_OF_RESOURCES There is not enough resource. + +**/ +EFI_STATUS +InitSecureBootVariables ( + VOID + ) +{ + EFI_STATUS Status; + UINT8 *Data; + UINTN DataSize; + UINT32 SecureBoot; + UINT8 SecureBootEnable; + SECURE_BOOT_MODE_TYPE SecureBootMode; + BOOLEAN IsPkPresent; + + // + // Find "PK" variable + // + Status = AuthServiceInternalFindVariable (EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid, (VOID **) &Data, &DataSize); + if (EFI_ERROR (Status)) { + IsPkPresent = FALSE; + DEBUG ((EFI_D_INFO, "Variable %s does not exist.\n", EFI_PLATFORM_KEY_NAME)); + } else { + IsPkPresent = TRUE; + DEBUG ((EFI_D_INFO, "Variable %s exists.\n", EFI_PLATFORM_KEY_NAME)); + } + + // + // Init "SecureBootMode" variable. + // Initial case + // SecureBootMode doesn't exist. Init it with PK state + // 3 inconsistency cases need to sync + // 1.1 Add PK -> system break -> update SecureBootMode Var + // 1.2 Delete PK -> system break -> update SecureBootMode Var + // 1.3 Set AuditMode ->Delete PK -> system break -> Update SecureBootMode Var + // + Status = AuthServiceInternalFindVariable (EDKII_SECURE_BOOT_MODE_NAME, &gEdkiiSecureBootModeGuid, (VOID **)&Data, &DataSize); + if (EFI_ERROR(Status)) { + // + // Variable driver Initial Case + // + if (IsPkPresent) { + SecureBootMode = SecureBootModeTypeUserMode; + } else { + SecureBootMode = SecureBootModeTypeSetupMode; + } + } else { + // + // 3 inconsistency cases need to sync + // + SecureBootMode = (SECURE_BOOT_MODE_TYPE)*Data; + ASSERT(SecureBootMode < SecureBootModeTypeMax); + + if (IsPkPresent) { + // + // 3.1 Add PK -> system break -> update SecureBootMode Var + // + if (SecureBootMode == SecureBootModeTypeSetupMode) { + SecureBootMode = SecureBootModeTypeUserMode; + } else if (SecureBootMode == SecureBootModeTypeAuditMode) { + SecureBootMode = SecureBootModeTypeDeployedMode; + } + } else { + // + // 3.2 Delete PK -> system break -> update SecureBootMode Var + // 3.3 Set AuditMode ->Delete PK -> system break -> Update SecureBootMode Var. Reinit to be SetupMode + // + if ((SecureBootMode == SecureBootModeTypeUserMode) || (SecureBootMode == SecureBootModeTypeDeployedMode)) { + SecureBootMode = SecureBootModeTypeSetupMode; + } + } + } + + if (EFI_ERROR(Status) || (SecureBootMode != (SECURE_BOOT_MODE_TYPE)*Data)) { + // + // Update SecureBootMode Var + // + Status = AuthServiceInternalUpdateVariable ( + EDKII_SECURE_BOOT_MODE_NAME, + &gEdkiiSecureBootModeGuid, + &SecureBootMode, + sizeof (UINT8), + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS + ); + if (EFI_ERROR(Status)) { + return Status; + } + } + + // + // Init "AuditMode" + // + Status = AuthServiceInternalUpdateVariable ( + EFI_AUDIT_MODE_NAME, + &gEfiGlobalVariableGuid, + &mSecureBootState[SecureBootMode].AuditMode, + sizeof(UINT8), + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS + ); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Init "DeployedMode" + // + Status = AuthServiceInternalUpdateVariable ( + EFI_DEPLOYED_MODE_NAME, + &gEfiGlobalVariableGuid, + &mSecureBootState[SecureBootMode].DeployedMode, + sizeof(UINT8), + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS + ); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Init "SetupMode" + // + Status = AuthServiceInternalUpdateVariable ( + EFI_SETUP_MODE_NAME, + &gEfiGlobalVariableGuid, + &mSecureBootState[SecureBootMode].SetupMode, + sizeof(UINT8), + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS + ); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // If "SecureBootEnable" variable exists, then update "SecureBoot" variable. + // If "SecureBootEnable" variable is SECURE_BOOT_ENABLE and in User Mode or Deployed 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; + Status = AuthServiceInternalFindVariable (EFI_SECURE_BOOT_ENABLE_NAME, &gEfiSecureBootEnableDisableGuid, (VOID **)&Data, &DataSize); + if (!EFI_ERROR(Status)) { + if (!IsPkPresent) { + // + // PK is cleared in runtime. "SecureBootMode" is not updated before reboot + // Delete "SecureBootMode" + // + Status = AuthServiceInternalUpdateVariable ( + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + &SecureBootEnable, + 0, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + } else { + SecureBootEnable = *Data; + } + } else if ((SecureBootMode == SecureBootModeTypeUserMode) || (SecureBootMode == SecureBootModeTypeDeployedMode)) { + // + // "SecureBootEnable" not exist, initialize it in User Mode or Deployed Mode. + // + SecureBootEnable = SECURE_BOOT_ENABLE; + Status = AuthServiceInternalUpdateVariable ( + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + &SecureBootEnable, + sizeof (UINT8), + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Create "SecureBoot" variable with BS+RT attribute set. + // + if ((SecureBootEnable == SECURE_BOOT_ENABLE) + && ((SecureBootMode == SecureBootModeTypeUserMode) || (SecureBootMode == SecureBootModeTypeDeployedMode))) { + SecureBoot = SECURE_BOOT_MODE_ENABLE; + } else { + SecureBoot = SECURE_BOOT_MODE_DISABLE; + } + Status = AuthServiceInternalUpdateVariable ( + EFI_SECURE_BOOT_MODE_NAME, + &gEfiGlobalVariableGuid, + &SecureBoot, + sizeof (UINT8), + EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + + DEBUG ((EFI_D_INFO, "SecureBootMode is %x\n", SecureBootMode)); + DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_SECURE_BOOT_MODE_NAME, SecureBoot)); + DEBUG ((EFI_D_INFO, "Variable %s is %x\n", EFI_SECURE_BOOT_ENABLE_NAME, SecureBootEnable)); + + // + // Save SecureBootMode in global space + // + mSecureBootMode = SecureBootMode; + + return Status; +} + +/** + Update SecureBootMode variable. + + @param[in] NewMode New Secure Boot Mode. + + @retval EFI_SUCCESS The initialization operation is successful. + @retval EFI_OUT_OF_RESOURCES There is not enough resource. + +**/ +EFI_STATUS +UpdateSecureBootMode( + IN SECURE_BOOT_MODE_TYPE NewMode + ) +{ + EFI_STATUS Status; + + // + // Update "SecureBootMode" variable to new Secure Boot Mode + // + Status = AuthServiceInternalUpdateVariable ( + EDKII_SECURE_BOOT_MODE_NAME, + &gEdkiiSecureBootModeGuid, + &NewMode, + sizeof (UINT8), + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS + ); + + if (!EFI_ERROR(Status)) { + DEBUG((EFI_D_INFO, "SecureBootMode Update to %x\n", NewMode)); + mSecureBootMode = NewMode; + } else { + DEBUG((EFI_D_ERROR, "SecureBootMode Update failure %x\n", Status)); + } + + return Status; +} + +/** + Current secure boot mode is AuditMode. This function performs secure boot mode transition + to a new mode. + + @param[in] NewMode New Secure Boot Mode. + + @retval EFI_SUCCESS The initialization operation is successful. + @retval EFI_OUT_OF_RESOURCES There is not enough resource. + +**/ +EFI_STATUS +TransitionFromAuditMode( + IN SECURE_BOOT_MODE_TYPE NewMode + ) +{ + EFI_STATUS Status; + UINT8 *AuditVarData; + UINT8 *DeployedVarData; + UINT8 *SetupVarData; + UINT8 *SecureBootVarData; + UINT8 SecureBootEnable; + UINTN DataSize; + + // + // AuditMode/DeployedMode/SetupMode/SecureBoot are all NON_NV variable maintained by Variable driver + // they can be RW. but can't be deleted. so they can always be found. + // + Status = AuthServiceInternalFindVariable ( + EFI_AUDIT_MODE_NAME, + &gEfiGlobalVariableGuid, + &AuditVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + Status = AuthServiceInternalFindVariable ( + EFI_DEPLOYED_MODE_NAME, + &gEfiGlobalVariableGuid, + &DeployedVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + Status = AuthServiceInternalFindVariable ( + EFI_SETUP_MODE_NAME, + &gEfiGlobalVariableGuid, + &SetupVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + Status = AuthServiceInternalFindVariable ( + EFI_SECURE_BOOT_MODE_NAME, + &gEfiGlobalVariableGuid, + &SecureBootVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + // + // Make Secure Boot Mode transition ATOMIC + // Update Private NV SecureBootMode Variable first, because it may fail due to NV range overflow. + // other tranisition logic are all memory operations. + // + Status = UpdateSecureBootMode(NewMode); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "Update SecureBootMode Variable fail %x\n", Status)); + } + + if (NewMode == SecureBootModeTypeDeployedMode) { + // + // Since PK is enrolled, can't rollback, always update SecureBootMode in memory + // + mSecureBootMode = NewMode; + Status = EFI_SUCCESS; + + // + // AuditMode ----> DeployedMode + // Side Effects + // AuditMode =: 0 / DeployedMode := 1 / SetupMode := 0 + // + // Update the value of AuditMode variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (AuditVarData, &mSecureBootState[NewMode].AuditMode, sizeof(UINT8)); + // + // Update the value of DeployedMode variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (DeployedVarData, &mSecureBootState[NewMode].DeployedMode, sizeof(UINT8)); + // + // Update the value of SetupMode variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (SetupVarData, &mSecureBootState[NewMode].SetupMode, sizeof(UINT8)); + + if (mAuthVarLibContextIn->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; + } + + // + // Update the value of SecureBoot variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (SecureBootVarData, &mSecureBootState[NewMode].SecureBoot, sizeof(UINT8)); + + // + // Create "SecureBootEnable" variable as secure boot is enabled. + // + SecureBootEnable = SECURE_BOOT_ENABLE; + AuthServiceInternalUpdateVariable ( + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + &SecureBootEnable, + sizeof (SecureBootEnable), + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + } else { + DEBUG((EFI_D_ERROR, "Invalid state tranition from %x to %x\n", SecureBootModeTypeAuditMode, NewMode)); + ASSERT(FALSE); + } + + return Status; +} + +/** + Current secure boot mode is DeployedMode. This function performs secure boot mode transition + to a new mode. + + @param[in] NewMode New Secure Boot Mode. + + @retval EFI_SUCCESS The initialization operation is successful. + @retval EFI_OUT_OF_RESOURCES There is not enough resource. + +**/ +EFI_STATUS +TransitionFromDeployedMode( + IN SECURE_BOOT_MODE_TYPE NewMode + ) +{ + EFI_STATUS Status; + UINT8 *DeployedVarData; + UINT8 *SetupVarData; + UINT8 *SecureBootVarData; + UINT8 SecureBootEnable; + UINTN DataSize; + + // + // AuditMode/DeployedMode/SetupMode/SecureBoot are all NON_NV variable maintained by Variable driver + // they can be RW. but can't be deleted. so they can always be found. + // + Status = AuthServiceInternalFindVariable ( + EFI_DEPLOYED_MODE_NAME, + &gEfiGlobalVariableGuid, + &DeployedVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + Status = AuthServiceInternalFindVariable ( + EFI_SETUP_MODE_NAME, + &gEfiGlobalVariableGuid, + &SetupVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + Status = AuthServiceInternalFindVariable ( + EFI_SECURE_BOOT_MODE_NAME, + &gEfiGlobalVariableGuid, + &SecureBootVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + // + // Make Secure Boot Mode transition ATOMIC + // Update Private NV SecureBootMode Variable first, because it may fail due to NV range overflow. + // other tranisition logic are all memory operations. + // + Status = UpdateSecureBootMode(NewMode); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "Update SecureBootMode Variable fail %x\n", Status)); + } + + switch(NewMode) { + case SecureBootModeTypeUserMode: + // + // DeployedMode ----> UserMode + // Side Effects + // DeployedMode := 0 + // + // Platform Specific DeployedMode clear. UpdateSecureBootMode fails and no other variables are updated before. rollback this transition + // + if (EFI_ERROR(Status)) { + return Status; + } + CopyMem (DeployedVarData, &mSecureBootState[NewMode].DeployedMode, sizeof(UINT8)); + + break; + + case SecureBootModeTypeSetupMode: + // + // Since PK is processed before, can't rollback, still update SecureBootMode in memory + // + mSecureBootMode = NewMode; + Status = EFI_SUCCESS; + + // + // DeployedMode ----> SetupMode + // + // Platform Specific PKpub clear or Delete Pkpub + // Side Effects + // DeployedMode := 0 / SetupMode := 1 / SecureBoot := 0 + // + // Update the value of DeployedMode variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (DeployedVarData, &mSecureBootState[NewMode].DeployedMode, sizeof(UINT8)); + // + // Update the value of SetupMode variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (SetupVarData, &mSecureBootState[NewMode].SetupMode, sizeof(UINT8)); + + if (mAuthVarLibContextIn->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; + } + + // + // Update the value of SecureBoot variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (SecureBootVarData, &mSecureBootState[NewMode].SecureBoot, sizeof(UINT8)); + + // + // Delete the "SecureBootEnable" variable as secure boot is Disabled. + // + SecureBootEnable = SECURE_BOOT_DISABLE; + AuthServiceInternalUpdateVariable ( + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + &SecureBootEnable, + 0, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + break; + + default: + DEBUG((EFI_D_ERROR, "Invalid state tranition from %x to %x\n", SecureBootModeTypeDeployedMode, NewMode)); + ASSERT(FALSE); + } + + return Status; +} + +/** + Current secure boot mode is UserMode. This function performs secure boot mode transition + to a new mode. + + @param[in] NewMode New Secure Boot Mode. + + @retval EFI_SUCCESS The initialization operation is successful. + @retval EFI_OUT_OF_RESOURCES There is not enough resource. + +**/ +EFI_STATUS +TransitionFromUserMode( + IN SECURE_BOOT_MODE_TYPE NewMode + ) +{ + EFI_STATUS Status; + UINT8 *AuditVarData; + UINT8 *DeployedVarData; + UINT8 *SetupVarData; + UINT8 *PkVarData; + UINT8 *SecureBootVarData; + UINT8 SecureBootEnable; + UINTN DataSize; + VARIABLE_ENTRY_CONSISTENCY VariableEntry; + + // + // AuditMode/DeployedMode/SetupMode/SecureBoot are all NON_NV variable maintained by Variable driver + // they can be RW. but can't be deleted. so they can always be found. + // + Status = AuthServiceInternalFindVariable ( + EFI_AUDIT_MODE_NAME, + &gEfiGlobalVariableGuid, + &AuditVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + Status = AuthServiceInternalFindVariable ( + EFI_DEPLOYED_MODE_NAME, + &gEfiGlobalVariableGuid, + &DeployedVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + Status = AuthServiceInternalFindVariable ( + EFI_SETUP_MODE_NAME, + &gEfiGlobalVariableGuid, + &SetupVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + Status = AuthServiceInternalFindVariable ( + EFI_SECURE_BOOT_MODE_NAME, + &gEfiGlobalVariableGuid, + &SecureBootVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + // + // Make Secure Boot Mode transition ATOMIC + // Update Private NV SecureBootMode Variable first, because it may fail due to NV range overflow. + // Other tranisition logic are all memory operations and PK delete is assumed to be always successful. + // + if (NewMode != SecureBootModeTypeAuditMode) { + Status = UpdateSecureBootMode(NewMode); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "Update SecureBootMode Variable fail %x\n", Status)); + } + } else { + // + // UserMode -----> AuditMode. Check RemainingSpace for SecureBootMode var first. + // Will update SecureBootMode after DeletePK logic + // + VariableEntry.VariableSize = sizeof(UINT8); + VariableEntry.Guid = &gEdkiiSecureBootModeGuid; + VariableEntry.Name = EDKII_SECURE_BOOT_MODE_NAME; + if (!mAuthVarLibContextIn->CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry, NULL)) { + return EFI_OUT_OF_RESOURCES; + } + } + + switch(NewMode) { + case SecureBootModeTypeDeployedMode: + // + // UpdateSecureBootMode fails and no other variables are updated before. rollback this transition + // + if (EFI_ERROR(Status)) { + return Status; + } + + // + // UserMode ----> DeployedMode + // Side Effects + // DeployedMode := 1 + // + CopyMem (DeployedVarData, &mSecureBootState[NewMode].DeployedMode, sizeof(UINT8)); + break; + + case SecureBootModeTypeAuditMode: + // + // UserMode ----> AuditMode + // Side Effects + // Delete PKpub / SetupMode := 1 / SecureBoot := 0 + // + // Delete PKpub without verification. Should always succeed. + // + PkVarData = NULL; + Status = AuthServiceInternalUpdateVariable ( + EFI_PLATFORM_KEY_NAME, + &gEfiGlobalVariableGuid, + PkVarData, + 0, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS + ); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "UserMode -> AuditMode. Delete PK fail %x\n", Status)); + ASSERT(FALSE); + } + + // + // Update Private NV SecureBootMode Variable + // + Status = UpdateSecureBootMode(NewMode); + if (EFI_ERROR(Status)) { + // + // Since PK is deleted successfully, Doesn't break, continue to update other variable. + // + DEBUG((EFI_D_ERROR, "Update SecureBootMode Variable fail %x\n", Status)); + } + CopyMem (AuditVarData, &mSecureBootState[NewMode].AuditMode, sizeof(UINT8)); + + // + // Fall into SetupMode logic + // + case SecureBootModeTypeSetupMode: + // + // Since PK is deleted before , can't rollback, still update SecureBootMode in memory + // + mSecureBootMode = NewMode; + Status = EFI_SUCCESS; + + // + // UserMode ----> SetupMode + // Side Effects + // DeployedMode :=0 / SetupMode :=1 / SecureBoot :=0 + // + // Update the value of SetupMode variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (SetupVarData, &mSecureBootState[NewMode].SetupMode, sizeof(UINT8)); + + if (mAuthVarLibContextIn->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; + } + + // + // Update the value of SecureBoot variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (SecureBootVarData, &mSecureBootState[NewMode].SecureBoot, sizeof(UINT8)); + + // + // Delete the "SecureBootEnable" variable as secure boot is Disabled. + // + SecureBootEnable = SECURE_BOOT_DISABLE; + AuthServiceInternalUpdateVariable ( + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + &SecureBootEnable, + 0, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + + break; + + default: + DEBUG((EFI_D_ERROR, "Invalid state tranition from %x to %x\n", SecureBootModeTypeUserMode, NewMode)); + ASSERT(FALSE); + } + + return Status; +} + +/** + Current secure boot mode is SetupMode. This function performs secure boot mode transition + to a new mode. + + @param[in] NewMode New Secure Boot Mode. + + @retval EFI_SUCCESS The initialization operation is successful. + @retval EFI_OUT_OF_RESOURCES There is not enough resource. + +**/ +EFI_STATUS +TransitionFromSetupMode( + IN SECURE_BOOT_MODE_TYPE NewMode + ) +{ + EFI_STATUS Status; + UINT8 *AuditVarData; + UINT8 *SetupVarData; + UINT8 *SecureBootVarData; + UINT8 SecureBootEnable; + UINTN DataSize; + + // + // AuditMode/DeployedMode/SetupMode/SecureBoot are all NON_NV variable maintained by Variable driver + // they can be RW. but can't be deleted. so they can always be found. + // + Status = AuthServiceInternalFindVariable ( + EFI_AUDIT_MODE_NAME, + &gEfiGlobalVariableGuid, + &AuditVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + Status = AuthServiceInternalFindVariable ( + EFI_SETUP_MODE_NAME, + &gEfiGlobalVariableGuid, + &SetupVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + Status = AuthServiceInternalFindVariable ( + EFI_SECURE_BOOT_MODE_NAME, + &gEfiGlobalVariableGuid, + &SecureBootVarData, + &DataSize + ); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + } + + // + // Make Secure Boot Mode transition ATOMIC + // Update Private NV SecureBootMode Variable first, because it may fail due to NV range overflow. + // Other tranisition logic are all memory operations and PK delete is assumed to be always successful. + // + Status = UpdateSecureBootMode(NewMode); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "Update SecureBootMode Variable fail %x\n", Status)); + } + + switch(NewMode) { + case SecureBootModeTypeAuditMode: + // + // UpdateSecureBootMode fails and no other variables are updated before. rollback this transition + // + if (EFI_ERROR(Status)) { + return Status; + } + + // + // SetupMode ----> AuditMode + // Side Effects + // AuditMode := 1 + // + // Update the value of AuditMode variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (AuditVarData, &mSecureBootState[NewMode].AuditMode, sizeof(UINT8)); + break; + + case SecureBootModeTypeUserMode: + // + // Since PK is enrolled before, can't rollback, still update SecureBootMode in memory + // + mSecureBootMode = NewMode; + Status = EFI_SUCCESS; + + // + // SetupMode ----> UserMode + // Side Effects + // SetupMode := 0 / SecureBoot := 1 + // + // Update the value of AuditMode variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (SetupVarData, &mSecureBootState[NewMode].SetupMode, sizeof(UINT8)); + + if (mAuthVarLibContextIn->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; + } + + // + // Update the value of SecureBoot variable by a simple mem copy, this could avoid possible + // variable storage reclaim at runtime. + // + CopyMem (SecureBootVarData, &mSecureBootState[NewMode].SecureBoot, sizeof(UINT8)); + + // + // Create the "SecureBootEnable" variable as secure boot is enabled. + // + SecureBootEnable = SECURE_BOOT_ENABLE; + AuthServiceInternalUpdateVariable ( + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + &SecureBootEnable, + sizeof (SecureBootEnable), + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + break; + + default: + DEBUG((EFI_D_ERROR, "Invalid state tranition from %x to %x\n", SecureBootModeTypeSetupMode, NewMode)); + ASSERT(FALSE); + } + + return Status; +} + +/** + This function performs main secure boot mode transition logic. + + @param[in] CurMode Current Secure Boot Mode. + @param[in] NewMode New Secure Boot Mode. + + @retval EFI_SUCCESS The initialization operation is successful. + @retval EFI_OUT_OF_RESOURCES There is not enough resource. + @retval EFI_INVALID_PARAMETER The Current Secure Boot Mode is wrong. + +**/ +EFI_STATUS +SecureBootModeTransition( + IN SECURE_BOOT_MODE_TYPE CurMode, + IN SECURE_BOOT_MODE_TYPE NewMode + ) +{ + EFI_STATUS Status; + + // + // SecureBootMode transition + // + switch (CurMode) { + case SecureBootModeTypeUserMode: + Status = TransitionFromUserMode(NewMode); + break; + + case SecureBootModeTypeSetupMode: + Status = TransitionFromSetupMode(NewMode); + break; + + case SecureBootModeTypeAuditMode: + Status = TransitionFromAuditMode(NewMode); + break; + + case SecureBootModeTypeDeployedMode: + Status = TransitionFromDeployedMode(NewMode); + break; + + default: + Status = EFI_INVALID_PARAMETER; + ASSERT(FALSE); + } + + return Status; + +} + /** Determine whether this operation needs a physical present user. @@ -597,129 +1553,6 @@ Done: } } -/** - Update platform mode. - - @param[in] Mode SETUP_MODE or USER_MODE. - - @return EFI_INVALID_PARAMETER Invalid parameter. - @return EFI_SUCCESS Update platform mode successfully. - -**/ -EFI_STATUS -UpdatePlatformMode ( - IN UINT32 Mode - ) -{ - EFI_STATUS Status; - VOID *Data; - UINTN DataSize; - UINT8 SecureBootMode; - UINT8 SecureBootEnable; - UINTN VariableDataSize; - - Status = AuthServiceInternalFindVariable ( - EFI_SETUP_MODE_NAME, - &gEfiGlobalVariableGuid, - &Data, - &DataSize - ); - if (EFI_ERROR (Status)) { - return 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 (Data, &mPlatformMode, sizeof(UINT8)); - - if (mAuthVarLibContextIn->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; - } - - // - // Check "SecureBoot" variable's existence. - // If it doesn't exist, firmware has no capability to perform driver signing verification, - // then set "SecureBoot" to 0. - // - Status = AuthServiceInternalFindVariable ( - EFI_SECURE_BOOT_MODE_NAME, - &gEfiGlobalVariableGuid, - &Data, - &DataSize - ); - // - // If "SecureBoot" variable exists, then check "SetupMode" variable update. - // If "SetupMode" variable is USER_MODE, "SecureBoot" variable is set to 1. - // If "SetupMode" variable is SETUP_MODE, "SecureBoot" variable is set to 0. - // - if (EFI_ERROR (Status)) { - SecureBootMode = SECURE_BOOT_MODE_DISABLE; - } else { - if (mPlatformMode == USER_MODE) { - SecureBootMode = SECURE_BOOT_MODE_ENABLE; - } else if (mPlatformMode == SETUP_MODE) { - SecureBootMode = SECURE_BOOT_MODE_DISABLE; - } else { - return EFI_NOT_FOUND; - } - } - - Status = AuthServiceInternalUpdateVariable ( - EFI_SECURE_BOOT_MODE_NAME, - &gEfiGlobalVariableGuid, - &SecureBootMode, - sizeof(UINT8), - EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS - ); - if (EFI_ERROR (Status)) { - return Status; - } - - // - // Check "SecureBootEnable" variable's existence. It can enable/disable secure boot feature. - // - Status = AuthServiceInternalFindVariable ( - EFI_SECURE_BOOT_ENABLE_NAME, - &gEfiSecureBootEnableDisableGuid, - &Data, - &DataSize - ); - - if (SecureBootMode == SECURE_BOOT_MODE_ENABLE) { - // - // Create the "SecureBootEnable" variable as secure boot is enabled. - // - SecureBootEnable = SECURE_BOOT_ENABLE; - VariableDataSize = sizeof (SecureBootEnable); - } else { - // - // Delete the "SecureBootEnable" variable if this variable exist as "SecureBoot" - // variable is not in secure boot state. - // - if (EFI_ERROR (Status)) { - return EFI_SUCCESS; - } - SecureBootEnable = SECURE_BOOT_DISABLE; - VariableDataSize = 0; - } - - Status = AuthServiceInternalUpdateVariable ( - EFI_SECURE_BOOT_ENABLE_NAME, - &gEfiSecureBootEnableDisableGuid, - &SecureBootEnable, - VariableDataSize, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS - ); - return Status; -} /** Check input data form to make sure it is a valid EFI_SIGNATURE_LIST for PK/KEK/db/dbx/dbt variable. @@ -879,6 +1712,121 @@ VendorKeyIsModified ( ); } +/** + Process Secure Boot Mode variable. + + 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] 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_WRITE_PROTECTED Variable is Read-Only. + @return EFI_SUCCESS Variable passed validation successfully. + +**/ +EFI_STATUS +ProcessSecureBootModeVar ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VOID *Data, + IN UINTN DataSize, + IN UINT32 Attributes OPTIONAL + ) +{ + EFI_STATUS Status; + UINT8 *VarData; + UINTN VarDataSize; + + // + // Check "AuditMode", "DeployedMode" Variable ReadWrite Attributes + // if in Runtime, Always RO + // if in Boottime, Depends on current Secure Boot Mode + // + if (mAuthVarLibContextIn->AtRuntime()) { + return EFI_WRITE_PROTECTED; + } + + // + // Delete not OK + // + if ((DataSize != sizeof(UINT8)) || (Attributes == 0)) { + return EFI_INVALID_PARAMETER; + } + + if (StrCmp (VariableName, EFI_AUDIT_MODE_NAME) == 0) { + if(mSecureBootState[mSecureBootMode].IsAuditModeRO) { + return EFI_WRITE_PROTECTED; + } + } else { + // + // Platform specific deployedMode clear. Set DeployedMode = RW + // + if (!InCustomMode() || !UserPhysicalPresent() || mSecureBootMode != SecureBootModeTypeDeployedMode) { + if(mSecureBootState[mSecureBootMode].IsDeployedModeRO) { + return EFI_WRITE_PROTECTED; + } + } + } + + if (*(UINT8 *)Data != 0 && *(UINT8 *)Data != 1) { + return EFI_INVALID_PARAMETER; + } + + // + // AuditMode/DeployedMode/SetupMode/SecureBoot are all NON_NV variable maintained by Variable driver + // they can be RW. but can't be deleted. so they can always be found. + // + Status = AuthServiceInternalFindVariable ( + VariableName, + VendorGuid, + &VarData, + &VarDataSize + ); + if (EFI_ERROR(Status)) { + ASSERT(FALSE); + } + + // + // If AuditMode/DeployedMode is assigned same value. Simply return EFI_SUCCESS + // + if (*VarData == *(UINT8 *)Data) { + return EFI_SUCCESS; + } + + // + // Perform SecureBootMode transition + // + if (StrCmp (VariableName, EFI_AUDIT_MODE_NAME) == 0) { + DEBUG((EFI_D_INFO, "Current SecureBootMode %x Transfer to SecureBootMode %x\n", mSecureBootMode, SecureBootModeTypeAuditMode)); + return SecureBootModeTransition(mSecureBootMode, SecureBootModeTypeAuditMode); + } else if (StrCmp (VariableName, EFI_DEPLOYED_MODE_NAME) == 0) { + if (mSecureBootMode == SecureBootModeTypeDeployedMode) { + // + // Platform specific DeployedMode clear. InCustomMode() && UserPhysicalPresent() is checked before + // + DEBUG((EFI_D_INFO, "Current SecureBootMode %x. Transfer to SecureBootMode %x\n", mSecureBootMode, SecureBootModeTypeUserMode)); + return SecureBootModeTransition(mSecureBootMode, SecureBootModeTypeUserMode); + } else { + DEBUG((EFI_D_INFO, "Current SecureBootMode %x. Transfer to SecureBootMode %x\n", mSecureBootMode, SecureBootModeTypeDeployedMode)); + return SecureBootModeTransition(mSecureBootMode, SecureBootModeTypeDeployedMode); + } + } + + return EFI_INVALID_PARAMETER; +} + /** Process variable with platform key for verification. @@ -917,6 +1865,7 @@ ProcessVarWithPk ( BOOLEAN Del; UINT8 *Payload; UINTN PayloadSize; + VARIABLE_ENTRY_CONSISTENCY VariableEntry[2]; if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0 || (Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) { @@ -927,19 +1876,55 @@ ProcessVarWithPk ( return EFI_INVALID_PARAMETER; } + // + // Init state of Del. State may change due to secure check + // 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; - } + Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data); + PayloadSize = DataSize - AUTHINFO2_SIZE (Data); + if (PayloadSize == 0) { + Del = TRUE; + } + + // + // Check the variable space for both PKpub and SecureBootMode variable. + // + VariableEntry[0].VariableSize = PayloadSize; + VariableEntry[0].Guid = &gEfiGlobalVariableGuid; + VariableEntry[0].Name = EFI_PLATFORM_KEY_NAME; + + VariableEntry[1].VariableSize = sizeof(UINT8); + VariableEntry[1].Guid = &gEdkiiSecureBootModeGuid; + VariableEntry[1].Name = EDKII_SECURE_BOOT_MODE_NAME; + + if ((InCustomMode() && UserPhysicalPresent()) || + (((mSecureBootMode == SecureBootModeTypeSetupMode) || (mSecureBootMode == SecureBootModeTypeAuditMode)) && !IsPk)) { Status = CheckSignatureListFormat(VariableName, VendorGuid, Payload, PayloadSize); if (EFI_ERROR (Status)) { return Status; } + // + // If delete PKpub, only check for "SecureBootMode" only + // if update / add PKpub, check both NewPKpub & "SecureBootMode" + // + if (IsPk) { + // + // Delete PKpub + // + if (Del && ((mSecureBootMode == SecureBootModeTypeUserMode) || (mSecureBootMode == SecureBootModeTypeDeployedMode)) + && !mAuthVarLibContextIn->CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry[1], NULL)){ + return EFI_OUT_OF_RESOURCES; + // + // Add PKpub + // + } else if (!Del && ((mSecureBootMode == SecureBootModeTypeSetupMode) || (mSecureBootMode == SecureBootModeTypeAuditMode)) + && !mAuthVarLibContextIn->CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry[0], &VariableEntry[1], NULL)) { + return EFI_OUT_OF_RESOURCES; + } + } + Status = AuthServiceInternalUpdateVariableWithTimeStamp ( VariableName, VendorGuid, @@ -952,10 +1937,17 @@ ProcessVarWithPk ( return Status; } - if ((mPlatformMode != SETUP_MODE) || IsPk) { + if (((mSecureBootMode != SecureBootModeTypeSetupMode) && (mSecureBootMode != SecureBootModeTypeAuditMode)) || IsPk) { Status = VendorKeyIsModified (); } - } else if (mPlatformMode == USER_MODE) { + } else if (mSecureBootMode == SecureBootModeTypeUserMode || mSecureBootMode == SecureBootModeTypeDeployedMode) { + // + // If delete PKpub, check "SecureBootMode" only + // + if (IsPk && Del && !mAuthVarLibContextIn->CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry[1], NULL)){ + return EFI_OUT_OF_RESOURCES; + } + // // Verify against X509 Cert in PK database. // @@ -970,8 +1962,19 @@ ProcessVarWithPk ( ); } else { // + // SetupMode or AuditMode to add PK // Verify against the certificate in data payload. // + // + // Check PKpub & SecureBootMode variable space consistency + // + if (!mAuthVarLibContextIn->CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry[0], &VariableEntry[1], NULL)) { + // + // No enough variable space to set PK successfully. + // + return EFI_OUT_OF_RESOURCES; + } + Status = VerifyTimeBasedPayloadAndUpdate ( VariableName, VendorGuid, @@ -984,16 +1987,30 @@ ProcessVarWithPk ( } 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); + // + // Delete or Enroll PK causes SecureBootMode change + // + if (!Del) { + if (mSecureBootMode == SecureBootModeTypeSetupMode) { + // + // If enroll PK in setup mode, change to user mode. + // + Status = SecureBootModeTransition (mSecureBootMode, SecureBootModeTypeUserMode); + } else if (mSecureBootMode == SecureBootModeTypeAuditMode) { + // + // If enroll PK in Audit mode, change to Deployed mode. + // + Status = SecureBootModeTransition (mSecureBootMode, SecureBootModeTypeDeployedMode); + } else { + DEBUG((EFI_D_INFO, "PK is updated in %x mode. No SecureBootMode change.\n", mSecureBootMode)); + } + } else { + if ((mSecureBootMode == SecureBootModeTypeUserMode) || (mSecureBootMode == SecureBootModeTypeDeployedMode)) { + // + // If delete PK in User Mode or DeployedMode, change to Setup Mode. + // + Status = SecureBootModeTransition (mSecureBootMode, SecureBootModeTypeSetupMode); + } } } @@ -1046,7 +2063,8 @@ ProcessVarWithKek ( } Status = EFI_SUCCESS; - if (mPlatformMode == USER_MODE && !(InCustomMode() && UserPhysicalPresent())) { + if ((mSecureBootMode == SecureBootModeTypeUserMode || mSecureBootMode == SecureBootModeTypeDeployedMode) + && !(InCustomMode() && UserPhysicalPresent())) { // // Time-based, verify against X509 Cert KEK. // @@ -1083,7 +2101,7 @@ ProcessVarWithKek ( return Status; } - if (mPlatformMode != SETUP_MODE) { + if ((mSecureBootMode != SecureBootModeTypeSetupMode) && (mSecureBootMode != SecureBootModeTypeAuditMode)) { Status = VendorKeyIsModified (); } } diff --git a/SecurityPkg/Library/AuthVariableLib/AuthServiceInternal.h b/SecurityPkg/Library/AuthVariableLib/AuthServiceInternal.h index add05c21cc..ec4b3d97f5 100644 --- a/SecurityPkg/Library/AuthVariableLib/AuthServiceInternal.h +++ b/SecurityPkg/Library/AuthVariableLib/AuthServiceInternal.h @@ -117,6 +117,54 @@ typedef struct { } AUTH_CERT_DB_DATA; #pragma pack() +/// +/// "SecureBootMode" variable stores current secure boot mode. +/// The value type is SECURE_BOOT_MODE_TYPE. +/// +#define EDKII_SECURE_BOOT_MODE_NAME L"SecureBootMode" + +typedef enum { + SecureBootModeTypeUserMode, + SecureBootModeTypeSetupMode, + SecureBootModeTypeAuditMode, + SecureBootModeTypeDeployedMode, + SecureBootModeTypeMax +} SECURE_BOOT_MODE_TYPE; + +// +// Record status info of Customized Secure Boot Mode. +// +typedef struct { + /// + /// AuditMode variable value + /// + UINT8 AuditMode; + /// + /// AuditMode variable RW + /// + BOOLEAN IsAuditModeRO; + /// + /// DeployedMode variable value + /// + UINT8 DeployedMode; + /// + /// AuditMode variable RW + /// + BOOLEAN IsDeployedModeRO; + /// + /// SetupMode variable value + /// + UINT8 SetupMode; + /// + /// SetupMode is always RO. Skip IsSetupModeRO; + /// + + /// + /// SecureBoot variable value + /// + UINT8 SecureBoot; +} SECURE_BOOT_MODE; + extern UINT8 *mPubKeyStore; extern UINT32 mPubKeyNumber; extern UINT32 mMaxKeyNumber; @@ -130,6 +178,18 @@ extern VOID *mHashCtx; extern AUTH_VAR_LIB_CONTEXT_IN *mAuthVarLibContextIn; +/** + Initialize Secure Boot variables. + + @retval EFI_SUCCESS The initialization operation is successful. + @retval EFI_OUT_OF_RESOURCES There is not enough resource. + +**/ +EFI_STATUS +InitSecureBootVariables ( + VOID + ); + /** Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set @@ -219,6 +279,39 @@ FilterSignatureList ( IN OUT UINTN *NewDataSize ); +/** + Process Secure Boot Mode variable. + + 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] 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_WRITE_PROTECTED Variable is Read-Only. + @return EFI_SUCCESS Variable passed validation successfully. + +**/ +EFI_STATUS +ProcessSecureBootModeVar ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VOID *Data, + IN UINTN DataSize, + IN UINT32 Attributes OPTIONAL + ); + /** Process variable with platform key for verification. diff --git a/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.c b/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.c index a54eaaa066..dee5e1dd9d 100644 --- a/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.c +++ b/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.c @@ -33,7 +33,6 @@ UINT32 mMaxKeyNumber; UINT32 mMaxKeyDbSize; UINT8 *mCertDbStore; UINT32 mMaxCertDbSize; -UINT32 mPlatformMode; UINT8 mVendorKeyState; EFI_GUID mSignatureSupport[] = {EFI_CERT_SHA1_GUID, EFI_CERT_SHA256_GUID, EFI_CERT_RSA2048_GUID, EFI_CERT_X509_GUID}; @@ -99,6 +98,17 @@ VARIABLE_ENTRY_PROPERTY mAuthVarEntry[] = { MAX_UINTN } }, + { + &gEdkiiSecureBootModeGuid, + L"SecureBootMode", + { + VAR_CHECK_VARIABLE_PROPERTY_REVISION, + VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY, + VARIABLE_ATTRIBUTE_NV_BS_RT, + sizeof (UINT8), + sizeof (UINT8) + } + } }; VOID **mAuthVarAddressPointer[10]; @@ -132,8 +142,6 @@ AuthVariableLibInitialize ( UINT8 *Data; UINTN DataSize; UINTN CtxSize; - UINT8 SecureBootMode; - UINT8 SecureBootEnable; UINT8 CustomMode; UINT32 ListSize; @@ -208,31 +216,11 @@ AuthVariableLibInitialize ( mPubKeyNumber = (UINT32) (DataSize / sizeof (AUTHVAR_KEY_DB_DATA)); } - Status = AuthServiceInternalFindVariable (EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid, (VOID **) &Data, &DataSize); - if (EFI_ERROR (Status)) { - 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)); - } + // + // Init Secure Boot variables + // + Status = InitSecureBootVariables (); - // - // Create "SetupMode" variable with BS+RT attribute set. - // - if (EFI_ERROR (Status)) { - mPlatformMode = SETUP_MODE; - } else { - mPlatformMode = USER_MODE; - } - Status = AuthServiceInternalUpdateVariable ( - EFI_SETUP_MODE_NAME, - &gEfiGlobalVariableGuid, - &mPlatformMode, - sizeof(UINT8), - EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS - ); - if (EFI_ERROR (Status)) { - return Status; - } // // Create "SignatureSupport" variable with BS+RT attribute set. @@ -248,69 +236,6 @@ AuthVariableLibInitialize ( return Status; } - // - // 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; - Status = AuthServiceInternalFindVariable (EFI_SECURE_BOOT_ENABLE_NAME, &gEfiSecureBootEnableDisableGuid, (VOID **) &Data, &DataSize); - if (!EFI_ERROR (Status)) { - if (mPlatformMode == SETUP_MODE){ - // - // PK is cleared in runtime. "SecureBootMode" is not updated before reboot - // Delete "SecureBootMode" in SetupMode - // - Status = AuthServiceInternalUpdateVariable ( - EFI_SECURE_BOOT_ENABLE_NAME, - &gEfiSecureBootEnableDisableGuid, - &SecureBootEnable, - 0, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS - ); - } else { - SecureBootEnable = *(UINT8 *) Data; - } - } else if (mPlatformMode == USER_MODE) { - // - // "SecureBootEnable" not exist, initialize it in USER_MODE. - // - SecureBootEnable = SECURE_BOOT_ENABLE; - Status = AuthServiceInternalUpdateVariable ( - EFI_SECURE_BOOT_ENABLE_NAME, - &gEfiSecureBootEnableDisableGuid, - &SecureBootEnable, - sizeof (UINT8), - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS - ); - 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 { - SecureBootMode = SECURE_BOOT_MODE_DISABLE; - } - Status = AuthServiceInternalUpdateVariable ( - EFI_SECURE_BOOT_MODE_NAME, - &gEfiGlobalVariableGuid, - &SecureBootMode, - sizeof (UINT8), - EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS - ); - 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. // @@ -455,10 +380,16 @@ AuthVariableLibProcessVariable ( { EFI_STATUS Status; + // + // Process PK, KEK, Sigdb, AuditMode, DeployedMode separately. + // if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_PLATFORM_KEY_NAME) == 0)){ Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, Attributes, TRUE); } else if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0)) { Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, Attributes, FALSE); + } else if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) + && (StrCmp (VariableName, EFI_AUDIT_MODE_NAME) == 0 || StrCmp (VariableName, EFI_DEPLOYED_MODE_NAME) == 0)) { + Status = ProcessSecureBootModeVar(VariableName, VendorGuid, Data, DataSize, Attributes); } else if (CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) && ((StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) || (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0) || diff --git a/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf b/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf index 3709f7baae..07a3ed5419 100644 --- a/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf +++ b/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf @@ -85,6 +85,10 @@ ## PRODUCES ## Variable:L"AuthVarKeyDatabase" gEfiAuthenticatedVariableGuid + ## CONSUMES ## Variable:L"SecureBootMode" + ## PRODUCES ## Variable:L"SecureBootMode" + gEdkiiSecureBootModeGuid + gEfiCertTypeRsa2048Sha256Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the certificate. gEfiCertPkcs7Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the certificate. gEfiCertX509Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c index 5cb9f8144e..4b4d3bf77d 100644 --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c @@ -711,6 +711,58 @@ GetImageExeInfoTableSize ( return TotalSize; } +/** + Create signature list based on input signature data and certificate type GUID. Caller is reposible + to free new created SignatureList. + + @param[in] SignatureData Signature data in SignatureList. + @param[in] SignatureDataSize Signature data size. + @param[in] CertType Certificate Type. + @param[out] SignatureList Created SignatureList. + @param[out] SignatureListSize Created SignatureListSize. + + @return EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources. + @retval EFI_SUCCESS Successfully create signature list. + +**/ +EFI_STATUS +CreateSignatureList( + IN UINT8 *SignatureData, + IN UINTN SignatureDataSize, + IN EFI_GUID *CertType, + OUT EFI_SIGNATURE_LIST **SignatureList, + OUT UINTN *SignatureListSize + ) +{ + EFI_SIGNATURE_LIST *SignList; + UINTN SignListSize; + EFI_SIGNATURE_DATA *Signature; + + SignList = NULL; + *SignatureList = NULL; + + SignListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + SignatureDataSize; + SignList = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SignListSize); + if (SignList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SignList->SignatureHeaderSize = 0; + SignList->SignatureListSize = (UINT32) SignListSize; + SignList->SignatureSize = (UINT32) SignatureDataSize + sizeof (EFI_SIGNATURE_DATA) - 1; + CopyMem (&SignList->SignatureType, CertType, sizeof (EFI_GUID)); + + DEBUG((EFI_D_INFO, "SignatureDataSize %x\n", SignatureDataSize)); + Signature = (EFI_SIGNATURE_DATA *) ((UINT8 *) SignList + sizeof (EFI_SIGNATURE_LIST)); + CopyMem (Signature->SignatureData, SignatureData, SignatureDataSize); + + *SignatureList = SignList; + *SignatureListSize = SignListSize; + + return EFI_SUCCESS; + +} + /** Create an Image Execution Information Table entry and add it to system configuration table. @@ -737,11 +789,13 @@ AddImageExeInfo ( UINTN NewImageExeInfoEntrySize; UINTN NameStringLen; UINTN DevicePathSize; + CHAR16 *NameStr; ImageExeInfoTable = NULL; NewImageExeInfoTable = NULL; ImageExeInfoEntry = NULL; NameStringLen = 0; + NameStr = NULL; if (DevicePath == NULL) { return ; @@ -769,7 +823,12 @@ AddImageExeInfo ( } DevicePathSize = GetDevicePathSize (DevicePath); - NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize; + + // + // Signature size can be odd. Pad after signature to ensure next EXECUTION_INFO entry align + // + NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize; + NewImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *) AllocateRuntimePool (ImageExeInfoTableSize + NewImageExeInfoEntrySize); if (NewImageExeInfoTable == NULL) { return ; @@ -788,19 +847,21 @@ AddImageExeInfo ( WriteUnaligned32 ((UINT32 *) ImageExeInfoEntry, Action); WriteUnaligned32 ((UINT32 *) ((UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION)), (UINT32) NewImageExeInfoEntrySize); + NameStr = (CHAR16 *)(ImageExeInfoEntry + 1); if (Name != NULL) { - CopyMem ((UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION) + sizeof (UINT32), Name, NameStringLen); + CopyMem ((UINT8 *) NameStr, Name, NameStringLen); } else { - ZeroMem ((UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION) + sizeof (UINT32), sizeof (CHAR16)); + ZeroMem ((UINT8 *) NameStr, sizeof (CHAR16)); } + CopyMem ( - (UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION) + sizeof (UINT32) + NameStringLen, + (UINT8 *) NameStr + NameStringLen, DevicePath, DevicePathSize ); if (Signature != NULL) { CopyMem ( - (UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION) + sizeof (UINT32) + NameStringLen + DevicePathSize, + (UINT8 *) NameStr + NameStringLen + DevicePathSize, Signature, SignatureSize ); @@ -1087,6 +1148,53 @@ IsTimeZero ( return FALSE; } +/** + Record multiple certificate list & verification state of a verified image to + IMAGE_EXECUTION_TABLE. + + @param[in] CertBuf Certificate list buffer. + @param[in] CertBufLength Certificate list buffer. + @param[in] Action Certificate list action to be record. + @param[in] ImageName Image name. + @param[in] ImageDevicePath Image device path. + +**/ +VOID +RecordCertListToImageExeuctionTable( + IN UINT8 *CertBuf, + IN UINTN CertBufLength, + IN EFI_IMAGE_EXECUTION_ACTION Action, + IN CHAR16 *ImageName OPTIONAL, + IN CONST EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath OPTIONAL + ) +{ + UINT8 CertNumber; + UINT8 *CertPtr; + UINTN Index; + UINT8 *Cert; + UINTN CertSize; + EFI_STATUS Status; + EFI_SIGNATURE_LIST *SignatureList; + UINTN SignatureListSize; + + CertNumber = (UINT8) (*CertBuf); + CertPtr = CertBuf + 1; + for (Index = 0; Index < CertNumber; Index++) { + CertSize = (UINTN) ReadUnaligned32 ((UINT32 *)CertPtr); + Cert = (UINT8 *)CertPtr + sizeof (UINT32); + + // + // Record all cert in cert chain to be passed + // + Status = CreateSignatureList(Cert, CertSize, &gEfiCertX509Guid, &SignatureList, &SignatureListSize); + if (!EFI_ERROR(Status)) { + AddImageExeInfo (Action, ImageName, ImageDevicePath, SignatureList, SignatureListSize); + FreePool (SignatureList); + } + } +} + + /** Check whether the timestamp signature is valid and the signing time is also earlier than the revocation time. @@ -1197,8 +1305,11 @@ Done: Check whether the image signature is forbidden by the forbidden database (dbx). The image is forbidden to load if any certificates for signing are revoked before signing time. - @param[in] AuthData Pointer to the Authenticode signature retrieved from the signed image. - @param[in] AuthDataSize Size of the Authenticode signature in bytes. + @param[in] AuthData Pointer to the Authenticode signature retrieved from the signed image. + @param[in] AuthDataSize Size of the Authenticode signature in bytes. + @param[in] IsAuditMode Whether system Secure Boot Mode is in AuditMode. + @param[in] ImageName Name of the image to verify. + @param[in] ImageDevicePath DevicePath of the image to verify. @retval TRUE Image is forbidden by dbx. @retval FALSE Image is not forbidden by dbx. @@ -1206,8 +1317,11 @@ Done: **/ BOOLEAN IsForbiddenByDbx ( - IN UINT8 *AuthData, - IN UINTN AuthDataSize + IN UINT8 *AuthData, + IN UINTN AuthDataSize, + IN BOOLEAN IsAuditMode, + IN CHAR16 *ImageName OPTIONAL, + IN CONST EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath OPTIONAL ) { EFI_STATUS Status; @@ -1230,7 +1344,10 @@ IsForbiddenByDbx ( UINT8 *Cert; UINTN CertSize; EFI_TIME RevocationTime; - + UINT8 *SignerCert; + UINTN SignerCertLength; + UINT8 *UnchainCert; + UINTN UnchainCertLength; // // Variable Initialization // @@ -1245,6 +1362,10 @@ IsForbiddenByDbx ( BufferLength = 0; TrustedCert = NULL; TrustedCertLength = 0; + SignerCert = NULL; + SignerCertLength = 0; + UnchainCert = NULL; + UnchainCertLength = 0; // // The image will not be forbidden if dbx can't be got. @@ -1352,21 +1473,54 @@ IsForbiddenByDbx ( } Done: + if (IsForbidden && IsAuditMode) { + Pkcs7GetCertificatesList(AuthData, AuthDataSize, &SignerCert, &SignerCertLength, &UnchainCert, &UnchainCertLength); + + // + // Record all certs in image to be failed + // + if ((SignerCertLength != 0) && (SignerCert != NULL)) { + RecordCertListToImageExeuctionTable( + SignerCert, + SignerCertLength, + EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED, + ImageName, + ImageDevicePath + ); + } + + if ((UnchainCertLength != 0) && (UnchainCert != NULL)) { + RecordCertListToImageExeuctionTable( + UnchainCert, + UnchainCertLength, + EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED, + ImageName, + ImageDevicePath + ); + } + } + if (Data != NULL) { FreePool (Data); } Pkcs7FreeSigners (CertBuffer); Pkcs7FreeSigners (TrustedCert); + Pkcs7FreeSigners (SignerCert); + Pkcs7FreeSigners (UnchainCert); return IsForbidden; } + /** Check whether the image signature can be verified by the trusted certificates in DB database. - @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image. - @param[in] AuthDataSize Size of the Authenticode signature in bytes. + @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image. + @param[in] AuthDataSize Size of the Authenticode signature in bytes. + @param[in] IsAuditMode Whether system Secure Boot Mode is in AuditMode. + @param[in] ImageName Name of the image to verify. + @param[in] ImageDevicePath DevicePath of the image to verify. @retval TRUE Image passed verification using certificate in db. @retval FALSE Image didn't pass verification using certificate in db. @@ -1374,14 +1528,17 @@ Done: **/ BOOLEAN IsAllowedByDb ( - IN UINT8 *AuthData, - IN UINTN AuthDataSize + IN UINT8 *AuthData, + IN UINTN AuthDataSize, + IN BOOLEAN IsAuditMode, + IN CHAR16 *ImageName OPTIONAL, + IN CONST EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath OPTIONAL ) { EFI_STATUS Status; BOOLEAN VerifyStatus; EFI_SIGNATURE_LIST *CertList; - EFI_SIGNATURE_DATA *Cert; + EFI_SIGNATURE_DATA *CertData; UINTN DataSize; UINT8 *Data; UINT8 *RootCert; @@ -1391,14 +1548,22 @@ IsAllowedByDb ( UINTN DbxDataSize; UINT8 *DbxData; EFI_TIME RevocationTime; + UINT8 *SignerCert; + UINTN SignerCertLength; + UINT8 *UnchainCert; + UINTN UnchainCertLength; - Data = NULL; - CertList = NULL; - Cert = NULL; - RootCert = NULL; - DbxData = NULL; - RootCertSize = 0; - VerifyStatus = FALSE; + Data = NULL; + CertList = NULL; + CertData = NULL; + RootCert = NULL; + DbxData = NULL; + RootCertSize = 0; + VerifyStatus = FALSE; + SignerCert = NULL; + SignerCertLength = 0; + UnchainCert = NULL; + UnchainCertLength = 0; DataSize = 0; Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL); @@ -1419,14 +1584,14 @@ IsAllowedByDb ( CertList = (EFI_SIGNATURE_LIST *) Data; while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) { if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) { - Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); - CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; for (Index = 0; Index < CertCount; Index++) { // // Iterate each Signature Data Node within this CertList for verify. // - RootCert = Cert->SignatureData; + RootCert = CertData->SignatureData; RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID); // @@ -1468,7 +1633,7 @@ IsAllowedByDb ( goto Done; } - Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); + CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertData + CertList->SignatureSize); } } @@ -1478,10 +1643,67 @@ IsAllowedByDb ( } Done: + if (VerifyStatus) { - SecureBootHook (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, CertList->SignatureSize, Cert); + SecureBootHook (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, CertList->SignatureSize, CertData); } + if (IsAuditMode) { + + Pkcs7GetCertificatesList(AuthData, AuthDataSize, &SignerCert, &SignerCertLength, &UnchainCert, &UnchainCertLength); + if (VerifyStatus) { + if ((SignerCertLength != 0) && (SignerCert != NULL)) { + // + // Record all cert in signer's cert chain to be passed + // + RecordCertListToImageExeuctionTable( + SignerCert, + SignerCertLength, + EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED | EFI_IMAGE_EXECUTION_INITIALIZED, + ImageName, + ImageDevicePath + ); + } + + if ((UnchainCertLength != 0) && (UnchainCert != NULL)) { + // + // Record all certs in unchained certificates lists to be failed + // + RecordCertListToImageExeuctionTable( + UnchainCert, + UnchainCertLength, + EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED, + ImageName, + ImageDevicePath + ); + } + } else { + // + // Record all certs in image to be failed + // + if ((SignerCertLength != 0) && (SignerCert != NULL)) { + RecordCertListToImageExeuctionTable( + SignerCert, + SignerCertLength, + EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED, + ImageName, + ImageDevicePath + ); + } + + if ((UnchainCertLength != 0) && (UnchainCert != NULL)) { + RecordCertListToImageExeuctionTable( + UnchainCert, + UnchainCertLength, + EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED, + ImageName, + ImageDevicePath + ); + } + } + } + + if (Data != NULL) { FreePool (Data); } @@ -1489,9 +1711,369 @@ Done: FreePool (DbxData); } + Pkcs7FreeSigners (SignerCert); + Pkcs7FreeSigners (UnchainCert); + return VerifyStatus; } +/** + Provide verification service for signed images in AuditMode, which include both signature validation + and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and + MSFT Authenticode type signatures are supported. + + In this implementation, only verify external executables when in AuditMode. + Executables from FV is bypass, so pass in AuthenticationStatus is ignored. Other authentication status + are record into IMAGE_EXECUTION_TABLE. + + The image verification policy is: + If the image is signed, + At least one valid signature or at least one hash value of the image must match a record + in the security database "db", and no valid signature nor any hash value of the image may + be reflected in the security database "dbx". + Otherwise, the image is not signed, + The SHA256 hash value of the image must match a record in the security database "db", and + not be reflected in the security data base "dbx". + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will validate its data structure + within this image buffer before use. + + @param[in] AuthenticationStatus + This is the authentication status returned from the security + measurement services for the input file. + @param[in] File This is a pointer to the device path of the file that is + being dispatched. This will optionally be used for logging. + @param[in] FileBuffer File buffer matches the input file device path. + @param[in] FileSize Size of File buffer matches the input file device path. + @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service. + + @retval EFI_SUCCESS The authenticate info is sucessfully stored for the file + specified by DevicePath and non-NULL FileBuffer + @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not + authenticate, and the platform policy dictates that the DXE + Foundation many not use File. + +**/ +EFI_STATUS +EFIAPI +ImageVerificationInAuditMode ( + IN UINT32 AuthenticationStatus, + IN CONST EFI_DEVICE_PATH_PROTOCOL *File, + IN VOID *FileBuffer, + IN UINTN FileSize, + IN BOOLEAN BootPolicy + ) +{ + EFI_STATUS Status; + UINT16 Magic; + EFI_IMAGE_DOS_HEADER *DosHdr; + EFI_SIGNATURE_LIST *SignatureList; + EFI_IMAGE_EXECUTION_ACTION Action; + WIN_CERTIFICATE *WinCertificate; + UINT32 Policy; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + UINT32 NumberOfRvaAndSizes; + WIN_CERTIFICATE_EFI_PKCS *PkcsCertData; + WIN_CERTIFICATE_UEFI_GUID *WinCertUefiGuid; + UINT8 *AuthData; + UINTN AuthDataSize; + EFI_IMAGE_DATA_DIRECTORY *SecDataDir; + UINT32 OffSet; + CHAR16 *FilePathStr; + UINTN SignatureListSize; + + SignatureList = NULL; + WinCertificate = NULL; + SecDataDir = NULL; + PkcsCertData = NULL; + FilePathStr = NULL; + Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED; + Status = EFI_ACCESS_DENIED; + + + // + // Check the image type and get policy setting. + // + switch (GetImageType (File)) { + + case IMAGE_FROM_FV: + Policy = ALWAYS_EXECUTE; + break; + + case IMAGE_FROM_OPTION_ROM: + Policy = PcdGet32 (PcdOptionRomImageVerificationPolicy); + break; + + case IMAGE_FROM_REMOVABLE_MEDIA: + Policy = PcdGet32 (PcdRemovableMediaImageVerificationPolicy); + break; + + case IMAGE_FROM_FIXED_MEDIA: + Policy = PcdGet32 (PcdFixedMediaImageVerificationPolicy); + break; + + default: + Policy = DENY_EXECUTE_ON_SECURITY_VIOLATION; + break; + } + + // + // If policy is always/never execute, return directly. + // + if (Policy == ALWAYS_EXECUTE) { + return EFI_SUCCESS; + } + + // + // Get Image Device Path Str + // + FilePathStr = ConvertDevicePathToText (File, FALSE, TRUE); + + // + // Authentication failed because of (unspecified) firmware security policy + // + if (Policy == NEVER_EXECUTE) { + // + // No signature, record FilePath/FilePathStr only + // + AddImageExeInfo (EFI_IMAGE_EXECUTION_POLICY_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED, FilePathStr, File, NULL, 0); + goto END; + } + + // + // The policy QUERY_USER_ON_SECURITY_VIOLATION and ALLOW_EXECUTE_ON_SECURITY_VIOLATION + // violates the UEFI spec and has been removed. + // + ASSERT (Policy != QUERY_USER_ON_SECURITY_VIOLATION && Policy != ALLOW_EXECUTE_ON_SECURITY_VIOLATION); + if (Policy == QUERY_USER_ON_SECURITY_VIOLATION || Policy == ALLOW_EXECUTE_ON_SECURITY_VIOLATION) { + CpuDeadLoop (); + } + + // + // Read the Dos header. + // + if (FileBuffer == NULL) { + Status = EFI_INVALID_PARAMETER; + goto END; + } + + mImageBase = (UINT8 *) FileBuffer; + mImageSize = FileSize; + + ZeroMem (&ImageContext, sizeof (ImageContext)); + ImageContext.Handle = (VOID *) FileBuffer; + ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) DxeImageVerificationLibImageRead; + + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + // + // The information can't be got from the invalid PeImage + // + goto END; + } + + + DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // DOS image header is present, + // so read the PE header after the DOS image header. + // + mPeCoffHeaderOffset = DosHdr->e_lfanew; + } else { + mPeCoffHeaderOffset = 0; + } + + // + // Check PE/COFF image. + // + mNtHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (mImageBase + mPeCoffHeaderOffset); + if (mNtHeader.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { + // + // It is not a valid Pe/Coff file. + // + Status = EFI_ACCESS_DENIED; + goto END; + } + + if (mNtHeader.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value + // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the + // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC + // then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC + // + Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC; + } else { + // + // Get the magic value from the PE/COFF Optional Header + // + Magic = mNtHeader.Pe32->OptionalHeader.Magic; + } + + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset. + // + NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes; + if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { + SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + } + } else { + // + // Use PE32+ offset. + // + NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; + if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { + SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + } + } + + // + // Start Image Validation. + // + if (SecDataDir == NULL || SecDataDir->Size == 0) { + // + // This image is not signed. The SHA256 hash value of the image must match a record in the security database "db", + // and not be reflected in the security data base "dbx". + // + if (!HashPeImage (HASHALG_SHA256)) { + Status = EFI_ACCESS_DENIED; + goto END; + } + + // + // Image Hash is in forbidden database (DBX). + // + if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) { + // + // Image Hash is in allowed database (DB). + // + if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) { + Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED | EFI_IMAGE_EXECUTION_INITIALIZED; + } + } + + // + // Add HASH digest for image without signature + // + Status = CreateSignatureList(mImageDigest, mImageDigestSize, &mCertType, &SignatureList, &SignatureListSize); + if (!EFI_ERROR(Status)) { + AddImageExeInfo (Action, FilePathStr, File, SignatureList, SignatureListSize); + FreePool (SignatureList); + } + goto END; + } + + // + // Verify the signature of the image, multiple signatures are allowed as per PE/COFF Section 4.7 + // "Attribute Certificate Table". + // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file. + // + for (OffSet = SecDataDir->VirtualAddress; + OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size); + OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) { + WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet); + if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) || + (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) { + break; + } + + // + // Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported. + // + if (WinCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { + // + // The certificate is formatted as WIN_CERTIFICATE_EFI_PKCS which is described in the + // Authenticode specification. + // + PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) WinCertificate; + if (PkcsCertData->Hdr.dwLength <= sizeof (PkcsCertData->Hdr)) { + break; + } + AuthData = PkcsCertData->CertData; + AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr); + } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) { + // + // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec. + // + WinCertUefiGuid = (WIN_CERTIFICATE_UEFI_GUID *) WinCertificate; + if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) { + break; + } + if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) { + continue; + } + AuthData = WinCertUefiGuid->CertData; + AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData); + } else { + if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) { + break; + } + continue; + } + + Status = HashPeImageByType (AuthData, AuthDataSize); + if (EFI_ERROR (Status)) { + continue; + } + + Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED; + + // + // Check the digital signature against the revoked certificate in forbidden database (dbx). + // Check the digital signature against the valid certificate in allowed database (db). + // + if (!IsForbiddenByDbx (AuthData, AuthDataSize, TRUE, FilePathStr, File)) { + IsAllowedByDb (AuthData, AuthDataSize, TRUE, FilePathStr, File); + } + + // + // Check the image's hash value. + // + if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) { + if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) { + Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED | EFI_IMAGE_EXECUTION_INITIALIZED; + } + } + + // + // Add HASH digest for image with signature + // + Status = CreateSignatureList(mImageDigest, mImageDigestSize, &mCertType, &SignatureList, &SignatureListSize); + + if (!EFI_ERROR(Status)) { + AddImageExeInfo (Action, FilePathStr, File, SignatureList, SignatureListSize); + FreePool (SignatureList); + } else { + goto END; + } + } + + + if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) { + // + // The Size in Certificate Table or the attribute certicate table is corrupted. + // + Status = EFI_ACCESS_DENIED; + } else { + Status = EFI_SUCCESS; + } + +END: + + if (FilePathStr != NULL) { + FreePool(FilePathStr); + FilePathStr = NULL; + } + + return Status; +} + /** Provide verification service for signed images, which include both signature validation and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and @@ -1559,7 +2141,9 @@ DxeImageVerificationHandler ( EFI_IMAGE_EXECUTION_ACTION Action; WIN_CERTIFICATE *WinCertificate; UINT32 Policy; - UINT8 *SecureBoot; + UINT8 *VarData; + UINT8 SecureBoot; + UINT8 AuditMode; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; UINT32 NumberOfRvaAndSizes; WIN_CERTIFICATE_EFI_PKCS *PkcsCertData; @@ -1579,6 +2163,20 @@ DxeImageVerificationHandler ( Status = EFI_ACCESS_DENIED; VerifyStatus = EFI_ACCESS_DENIED; + GetEfiGlobalVariable2 (EFI_AUDIT_MODE_NAME, (VOID**)&VarData, NULL); + // + // Skip verification if AuditMode variable doesn't exist. AuditMode should always exist + // + if (VarData == NULL) { + return EFI_SUCCESS; + } + AuditMode = *VarData; + FreePool(VarData); + + if (AuditMode == AUDIT_MODE_ENABLE) { + return ImageVerificationInAuditMode(AuthenticationStatus, File, FileBuffer, FileSize, BootPolicy); + } + // // Check the image type and get policy setting. // @@ -1622,22 +2220,22 @@ DxeImageVerificationHandler ( CpuDeadLoop (); } - GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBoot, NULL); + GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&VarData, NULL); // // Skip verification if SecureBoot variable doesn't exist. // - if (SecureBoot == NULL) { + if (VarData == NULL) { return EFI_SUCCESS; } + SecureBoot = *VarData; + FreePool(VarData); // - // Skip verification if SecureBoot is disabled. + // Skip verification if SecureBoot is disabled but not AuditMode // - if (*SecureBoot == SECURE_BOOT_MODE_DISABLE) { - FreePool (SecureBoot); + if (SecureBoot == SECURE_BOOT_MODE_DISABLE) { return EFI_SUCCESS; } - FreePool (SecureBoot); // // Read the Dos header. @@ -1808,7 +2406,7 @@ DxeImageVerificationHandler ( // // Check the digital signature against the revoked certificate in forbidden database (dbx). // - if (IsForbiddenByDbx (AuthData, AuthDataSize)) { + if (IsForbiddenByDbx (AuthData, AuthDataSize, FALSE, NULL, NULL)) { Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED; VerifyStatus = EFI_ACCESS_DENIED; break; @@ -1818,7 +2416,7 @@ DxeImageVerificationHandler ( // Check the digital signature against the valid certificate in allowed database (db). // if (EFI_ERROR (VerifyStatus)) { - if (IsAllowedByDb (AuthData, AuthDataSize)) { + if (IsAllowedByDb (AuthData, AuthDataSize, FALSE, NULL, NULL)) { VerifyStatus = EFI_SUCCESS; } }