/* * refit/scan/secureboot.c * * Copyright (c) 2006-2010 Christoph Pfisterer * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * * Neither the name of Christoph Pfisterer nor the names of the * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //CONST INTN SecureBootDef = 0; // jief : not used ?? #ifdef ENABLE_SECURE_BOOT #include #include "../Platform/Settings.h" #include "../Settings/Self.h" #include "entry_scan.h" #include "secureboot.h" extern "C" { #include #include #include } #ifndef DEBUG_ALL #define DEBUG_SECURE_BOOT 1 #else #define DEBUG_SECURE_BOOT DEBUG_ALL #endif #if DEBUG_SECURE_BOOT == 0 #define DBG(...) #else #define DBG(...) DebugLog(DEBUG_SECURE_BOOT, __VA_ARGS__) #endif // Enable secure boot void EnableSecureBoot(void) { EFI_STATUS Status = EFI_NOT_FOUND; XBool WantDefaultKeys; CONST CHAR16 *ErrorString = NULL; UINTN CloverSignatureSize = 0; void *CloverSignature = NULL; // Check in setup mode if (GlobalConfig.SecureBoot || !GlobalConfig.SecureBootSetupMode) { return; } // Ask user if they want to use default keys WantDefaultKeys = YesNoMessage(L"Secure Boot", L"Enroll the default keys too?"); DBG("Enabling secure boot with%s default keys\n", WantDefaultKeys ? "" : "out"); // Get this image's certificate UINT32 AuthenticationStatus = 0; UINTN FileSize = 0; // Open the file buffer void *FileBuffer = GetFileBufferByFilePath(false, &self.getCloverDirFullDevicePath(), &FileSize, &AuthenticationStatus); if (FileBuffer != NULL) { if (FileSize > 0) { // Retrieve the certificates CloverSignature = GetImageSignatureDatabase(FileBuffer, FileSize, &CloverSignatureSize, false); if (CloverSignature != NULL) { if (CloverSignatureSize > 0) { // Found signature Status = EFI_SUCCESS; } else { FreePool(CloverSignature); CloverSignature = NULL; } } } FreePool(FileBuffer); } // Check and alert about image not found if ((FileBuffer == NULL) || (FileSize == 0)) { XStringW FilePath = FileDevicePathToXStringW(&self.getCloverDirFullDevicePath()); if (FilePath.notEmpty()) { DBG("Failed to load Clover image from %ls\n", FilePath.wc_str()); } else { DBG("Failed to load Clover image\n"); } } if (EFI_ERROR(Status) || (CloverSignature == NULL)) { ErrorString = L"Clover does not have a certificate"; } else { // Enroll secure boot keys Status = EnrollSecureBootKeys(CloverSignature, CloverSignatureSize, WantDefaultKeys); if (EFI_ERROR(Status)) { ErrorString = L"Failed to enroll secure boot keys"; } FreePool(CloverSignature); } // Reinit secure boot now InitializeSecureBoot(); // Install the security policy hooks or redisable if (!EFI_ERROR(Status)) { Status = InstallSecureBoot(); if (EFI_ERROR(Status)) { ErrorString = L"Secure boot protocols not found"; } } if (EFI_ERROR(Status)) { XStringW Str = SWPrintf("Enabling secure boot failed because\n%ls", ErrorString); AlertMessage(L"Enable Secure Boot"_XSW, Str); DBG("Enabling secure boot failed because %ls! Status: %s\n", ErrorString, efiStrError(Status)); DisableSecureBoot(); } } CONST CHAR16 *SecureBootPolicyToStr(IN UINTN Policy) { STATIC CONST CHAR16 *SecureBootPolicyStrings[] = { L"Deny", L"Allow", L"Query", L"Insert", L"WhiteList", L"BlackList", L"User", }; STATIC CONST UINTN SecureBootPolicyStringsCount = (sizeof(SecureBootPolicyStrings) / (sizeof(CONST CHAR16 *))); if (Policy < SecureBootPolicyStringsCount) { return SecureBootPolicyStrings[Policy]; } return L"Deny"; } STATIC void PrintSecureBootInfo(void) { // Nothing to do if secure boot is disabled or in setup mode if (!GlobalConfig.SecureBoot) { DBG("Secure Boot: %s\n", (GlobalConfig.SecureBootSetupMode ? "Setup" : "Disabled")); } else { // Secure boot is enabled DBG("Secure Boot: %s\n", (GlobalConfig.SecureBootSetupMode ? "Forced" : "Enabled")); DBG("Boot Policy: %ls\n", SecureBootPolicyToStr(gSettings.Boot.SecureBootPolicy)); } } // Alert message for disable failure STATIC void DisableMessage(const EFI_STATUS Status, const CHAR16 *String, const CHAR16 *ErrorString) { XStringW Str; if (ErrorString != NULL) { Str = SWPrintf("%ls\n%ls\n%s", String, ErrorString, efiStrError(Status)); } else { Str = SWPrintf("%ls\n%s", String, efiStrError(Status)); } DBG("Secure Boot: %ls", Str.wc_str()); AlertMessage(L"Disable Secure Boot"_XSW, Str); } // Disable secure boot void DisableSecureBoot(void) { EFI_STATUS Status; const CHAR16 *ErrorString = NULL; // Check in user mode if (GlobalConfig.SecureBootSetupMode || !GlobalConfig.SecureBoot) { return; } UninstallSecureBoot(); // Clear the platform database Status = ClearSecureBootKeys(); if (EFI_ERROR(Status)) { ErrorString = L"Failed to clear platform database!\nIs Clover platform database owner?"; } else if (YesNoMessage(L"Disable Secure Boot", L"Do you want to clear all databases (suggested)?")) { // Clear all databases if wanted Status = SetSignatureDatabase(EXCHANGE_DATABASE_NAME, EXCHANGE_DATABASE_GUID, NULL, 0); if (EFI_ERROR(Status)) { DisableMessage(Status, L"Failed to disable secure boot!", L"Failed to clear exchange database!"); } Status = ClearAuthorizedDatabase(); if (EFI_ERROR(Status)) { ErrorString = L"Failed to clear authorized database!"; } } // Alert failure to user if (EFI_ERROR(Status)) { DisableMessage(Status, L"Failed to disable secure boot!", ErrorString); } else { // Reinit secure boot now InitializeSecureBoot(); PrintSecureBootInfo(); } } // The previous protocol functions STATIC EFI_SECURITY_FILE_AUTHENTICATION_STATE gSecurityFileAuthentication; STATIC EFI_SECURITY2_FILE_AUTHENTICATION gSecurity2FileAuthentication; // Pre check the secure boot policy STATIC XBool EFIAPI PrecheckSecureBootPolicy(IN OUT EFI_STATUS *AuthenticationStatus, IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath) { CHAR16 *DevicePathStr; UINTN Index; if ((AuthenticationStatus == NULL) || (DevicePath == NULL)) { return false; } switch (gSettings.Boot.SecureBootPolicy) { case SECURE_BOOT_POLICY_ALLOW: // Allow all images *AuthenticationStatus = EFI_SUCCESS; return true; case SECURE_BOOT_POLICY_WHITELIST: // Check the white list for this image DevicePathStr = FileDevicePathToStr((EFI_DEVICE_PATH_PROTOCOL *)DevicePath); if (DevicePathStr == NULL) { return false; } for (Index = 0; Index < gSettings.Boot.SecureBootWhiteList.size(); ++Index) { if ((gSettings.Boot.SecureBootWhiteList[Index].notEmpty()) && (StriStr(DevicePathStr, gSettings.Boot.SecureBootWhiteList[Index].wc_str()) != NULL)) { // White listed *AuthenticationStatus = EFI_SUCCESS; return true; } } return true; case SECURE_BOOT_POLICY_BLACKLIST: // Check the black list for this image DevicePathStr = FileDevicePathToStr((EFI_DEVICE_PATH_PROTOCOL *)DevicePath); if (DevicePathStr == NULL) { return false; } for (Index = 0; Index < gSettings.Boot.SecureBootBlackList.size(); ++Index) { if ((gSettings.Boot.SecureBootBlackList[Index].notEmpty()) && (StriStr(DevicePathStr, gSettings.Boot.SecureBootBlackList[Index].wc_str()) != NULL)) { // Black listed return true; } } *AuthenticationStatus = EFI_SUCCESS; return true; case SECURE_BOOT_POLICY_USER: DevicePathStr = FileDevicePathToStr((EFI_DEVICE_PATH_PROTOCOL *)DevicePath); if (DevicePathStr == NULL) { return false; } // Check the black list for this image for (Index = 0; Index < gSettings.Boot.SecureBootBlackList.size(); ++Index) { if ((gSettings.Boot.SecureBootBlackList[Index].notEmpty()) && (StriStr(DevicePathStr, gSettings.Boot.SecureBootBlackList[Index].wc_str()) != NULL)) { // Black listed return true; } } // Check the white list for this image for (Index = 0; Index < gSettings.Boot.SecureBootWhiteList.size(); ++Index) { if ((gSettings.Boot.SecureBootWhiteList[Index].notEmpty()) && (StriStr(DevicePathStr, gSettings.Boot.SecureBootWhiteList[Index].wc_str()) != NULL)) { // White listed *AuthenticationStatus = EFI_SUCCESS; return true; } } break; } return false; } // Check the secure boot policy STATIC XBool EFIAPI CheckSecureBootPolicy(IN OUT EFI_STATUS *AuthenticationStatus, IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN void *FileBuffer, IN UINTN FileSize) { UINTN UserResponse = SECURE_BOOT_POLICY_DENY; switch (gSettings.Boot.SecureBootPolicy) { case SECURE_BOOT_POLICY_QUERY: case SECURE_BOOT_POLICY_USER: // Query user to allow image or deny image or insert image signature UserResponse = QuerySecureBootUser(DevicePath); DBG("VerifySecureBootImage: User selected policy: %ls\n", SecureBootPolicyToStr(UserResponse)); // Perform user action switch (UserResponse) { case SECURE_BOOT_POLICY_ALLOW: *AuthenticationStatus = EFI_SUCCESS; case SECURE_BOOT_POLICY_DENY: default: return true; case SECURE_BOOT_POLICY_INSERT: // If this is forced mode then no insert if (GlobalConfig.SecureBootSetupMode) { return true; } break; } // Purposeful fallback to insert case SECURE_BOOT_POLICY_INSERT: // Insert image signature AppendImageToAuthorizedDatabase(DevicePath, FileBuffer, FileSize); *AuthenticationStatus = EFI_SUCCESS; return true; case SECURE_BOOT_POLICY_ALLOW: // Allow all images *AuthenticationStatus = EFI_SUCCESS; return true; case SECURE_BOOT_POLICY_DENY: // Deny all images return true; } return false; } // Override EFI_SECURITY_ARCH_PROTOCOL EFI_STATUS EFIAPI InternalFileAuthentication(IN CONST EFI_SECURITY_ARCH_PROTOCOL *This, IN UINT32 AuthenticationStatus, IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath) { EFI_STATUS Status = EFI_SECURITY_VIOLATION; // Check secure boot policy if (!PrecheckSecureBootPolicy(&Status, DevicePath)) { // Return original security policy Status = gSecurityFileAuthentication(This, AuthenticationStatus, DevicePath); if (EFI_ERROR(Status)) { // Check security policy on image CheckSecureBootPolicy(&Status, DevicePath, NULL, 0); } } if (EFI_ERROR(Status)) { CHAR16 *DevicePathStr = FileDevicePathToStr((EFI_DEVICE_PATH_PROTOCOL *)DevicePath); if (DevicePathStr) { DBG("VerifySecureBootImage(1): %s %ls\n", efiStrError(Status), DevicePathStr); FreePool(DevicePathStr); } } return Status; } // Override EFI_SECURITY2_ARCH_PROTOCOL EFI_STATUS EFIAPI Internal2FileAuthentication(IN CONST EFI_SECURITY2_ARCH_PROTOCOL *This, IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN void *FileBuffer, IN UINTN FileSize, IN BOOLEAN BootPolicy) { EFI_STATUS Status = EFI_SECURITY_VIOLATION; // Check secure boot policy if (!PrecheckSecureBootPolicy(&Status, DevicePath)) { // Return original security policy Status = gSecurity2FileAuthentication(This, DevicePath, FileBuffer, FileSize, BootPolicy); if (EFI_ERROR(Status)) { CheckSecureBootPolicy(&Status, DevicePath, FileBuffer, FileSize); } } if (EFI_ERROR(Status)) { CHAR16 *DevicePathStr = FileDevicePathToStr((EFI_DEVICE_PATH_PROTOCOL *)DevicePath); if (DevicePathStr) { DBG("VerifySecureBootImage(2): %s %ls\n", efiStrError(Status), DevicePathStr); FreePool(DevicePathStr); } } return Status; } // Verify boot policy for image EFI_STATUS VerifySecureBootImage(IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath) { EFI_STATUS Status = EFI_SECURITY_VIOLATION; if (!PrecheckSecureBootPolicy(&Status, DevicePath)) { if (!CheckSecureBootPolicy(&Status, DevicePath, NULL, 0)) { Status = EFI_SUCCESS; } } if (EFI_ERROR(Status)) { CHAR16 *DevicePathStr = FileDevicePathToStr((EFI_DEVICE_PATH_PROTOCOL *)DevicePath); if (DevicePathStr) { DBG("VerifySecureBootImage: %s %ls\n", efiStrError(Status), DevicePathStr); FreePool(DevicePathStr); } } return Status; } // Install secure boot EFI_STATUS InstallSecureBoot(void) { EFI_STATUS Status; EFI_SECURITY_ARCH_PROTOCOL *Security = NULL; EFI_SECURITY2_ARCH_PROTOCOL *Security2 = NULL; // Check if already installed if (gSecurityFileAuthentication) { return EFI_SUCCESS; } PrintSecureBootInfo(); // Nothing to do if secure boot is disabled or in setup mode if (!GlobalConfig.SecureBoot || GlobalConfig.SecureBootSetupMode) { return EFI_SUCCESS; } // Locate security protocols gBS->LocateProtocol(gEfiSecurity2ArchProtocolGuid, NULL, (void **)&Security2); Status = gBS->LocateProtocol(gEfiSecurityArchProtocolGuid, NULL, (void **)&Security); if (EFI_ERROR(Status)) { return Status; } if (Security == NULL) { return EFI_NOT_FOUND; } // Install policy hooks gSecurityFileAuthentication = Security->FileAuthenticationState; Security->FileAuthenticationState = InternalFileAuthentication; if (Security2) { gSecurity2FileAuthentication = Security2->FileAuthentication; Security2->FileAuthentication = Internal2FileAuthentication; } return EFI_SUCCESS; } // Uninstall secure boot void UninstallSecureBoot(void) { // Uninstall policy hooks if (gSecurityFileAuthentication) { EFI_SECURITY_ARCH_PROTOCOL *Security = NULL; // Restore the security protocol function gBS->LocateProtocol(gEfiSecurityArchProtocolGuid, NULL, (void **)&Security); if (Security) { Security->FileAuthenticationState = gSecurityFileAuthentication; } gSecurityFileAuthentication = 0; } if (gSecurity2FileAuthentication) { EFI_SECURITY2_ARCH_PROTOCOL *Security2 = NULL; // Restory the security 2 protocol function gBS->LocateProtocol(gEfiSecurity2ArchProtocolGuid, NULL, (void **)&Security2); if (Security2) { Security2->FileAuthentication = gSecurity2FileAuthentication; } gSecurity2FileAuthentication = 0; } } // Initialize secure boot void InitializeSecureBoot(void) { // Set secure boot variables to firmware values UINTN Size = sizeof(GlobalConfig.SecureBootSetupMode); gRT->GetVariable(L"SetupMode", gEfiGlobalVariableGuid, NULL, &Size, &GlobalConfig.SecureBootSetupMode); Size = sizeof(GlobalConfig.SecureBoot); gRT->GetVariable(L"SecureBoot", gEfiGlobalVariableGuid, NULL, &Size, &GlobalConfig.SecureBoot); // Make sure that secure boot is disabled if in setup mode, this will // allow us to specify later in settings that we want to override // setup mode and pretend like we are in secure boot mode to enforce // secure boot policy even when secure boot is not present/disabled. if (GlobalConfig.SecureBootSetupMode) { GlobalConfig.SecureBoot = 0; } } #endif // ENABLE_SECURE_BOOT