From 4d42b7a73a036a01bf3676852f9eb10fd9f1a16c Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Thu, 10 Oct 2024 13:08:33 +0100 Subject: [PATCH] [efi] further improve revoked UEFI bootloader reporting * Do not report SBAT revocations unless we actually have a formal Secure Boot signed bootloader. * Also reduce verbose log pollution by libcdio. --- ChangeLog.txt | 8 +++++ src/db.h | 19 ++++++++++-- src/hash.c | 56 +++++++++++++++++++++++---------- src/iso.c | 16 ++++++---- src/rufus.c | 86 +++++++++++++++++++++++++++++++-------------------- src/rufus.h | 15 ++++++++- src/rufus.rc | 10 +++--- 7 files changed, 145 insertions(+), 65 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 3200c139ddd..66f61081454 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,11 @@ +o Version 4.6 (2024.10.??) + Add a new setup.exe wrapper to bypass Windows 11 24H2 in-place upgrade restrictions + Add TimeZone to regional options replication + Set local account passwords to not expire by default + Fix an error when trying to write compressed VHD images + Fix an error when invoking Rufus from the PowerShell commandline + Improve revoked UEFI bootloaders check to support Linux SBAT, Windows SVN and cert DBX + o Version 4.5 (2024.05.22) Add new advanced option to perform runtime UEFI media validation of suitable images (Windows, most Linux) Move the 'Use Rufus MBR' advanced option to a cheat mode (Alt-A) diff --git a/src/db.h b/src/db.h index 52c026598e2..1b4f4e85823 100644 --- a/src/db.h +++ b/src/db.h @@ -658,11 +658,26 @@ static uint8_t pe256dbx[] = { }; /* - * Contains the SHA-1 thumbprint of certificates that are being revoked by DBX. + * Contains the SHA-1 thumbprints of the issuer certificate of the official + * Secure Boot signing authority (i.e. Microsoft). + */ +static uint8_t certauth[] = { + // 'Microsoft Windows Production PCA 2011' + 0x58, 0x0a, 0x6f, 0x4c, 0xc4, 0xe4, 0xb6, 0x69, 0xb9, 0xeb, 0xdc, 0x1b, 0x2b, 0x3e, 0x08, 0x7b, 0x80, 0xd0, 0x67, 0x8d, + // 'Microsoft Corporation UEFI CA 2011' + 0x46, 0xde, 0xf6, 0x3b, 0x5c, 0xe6, 0x1c, 0xf8, 0xba, 0x0d, 0xe2, 0xe6, 0x63, 0x9c, 0x10, 0x19, 0xd0, 0xed, 0x14, 0xf3, + // 'Windows UEFI CA 2023' + 0x45, 0xa0, 0xfa, 0x32, 0x60, 0x47, 0x73, 0xc8, 0x24, 0x33, 0xc3, 0xb7, 0xd5, 0x9e, 0x74, 0x66, 0xb3, 0xac, 0x0c, 0x67, + // 'Microsoft UEFI CA 2023' + 0xb5, 0xee, 0xb4, 0xa6, 0x70, 0x60, 0x48, 0x07, 0x3f, 0x0e, 0xd2, 0x96, 0xe7, 0xf5, 0x80, 0xa7, 0x90, 0xb5, 0x9e, 0xaa, +}; + +/* + * Contains the SHA-1 thumbprints of certificates that are being revoked by DBX. * This only includes the 'Microsoft Windows Production PCA 2011' for now. */ static uint8_t certdbx[] = { - 0x58, 0x0a, 0x6f, 0x4c, 0xc4, 0xe4, 0xb6, 0x69, 0xb9, 0xeb, 0xdc, 0x1b, 0x2b, 0x3e, 0x08, 0x7b, 0x80, 0xd0, 0x67, 0x8d + 0x58, 0x0a, 0x6f, 0x4c, 0xc4, 0xe4, 0xb6, 0x69, 0xb9, 0xeb, 0xdc, 0x1b, 0x2b, 0x3e, 0x08, 0x7b, 0x80, 0xd0, 0x67, 0x8d, }; /* diff --git a/src/hash.c b/src/hash.c index ee21cc51e1f..88ffca31da5 100644 --- a/src/hash.c +++ b/src/hash.c @@ -2171,37 +2171,51 @@ static BOOL IsRevokedBySvn(uint8_t* buf, uint32_t len) return FALSE; } -static BOOL IsRevokedByCert(uint8_t* buf, uint32_t len) +static BOOL IsRevokedByCert(cert_info_t* info) { int i; - uint8_t* cert; - cert_info_t info; - - cert = GetPeSignatureData(buf); - i = GetIssuerCertificateInfo(cert, &info); - if (i == 0) - uuprintf(" (Unsigned Bootloader)"); - if (i <= 0) - return FALSE; - uuprintf(" Signed by: %s", info.name); for (i = 0; i < ARRAYSIZE(certdbx); i += SHA1_HASHSIZE) { if (!expert_mode) continue; - if (memcmp(info.thumbprint, &certdbx[i], SHA1_HASHSIZE) == 0) { - uprintf("Found '%s' revoked certificate", info.name); + if (memcmp(info->thumbprint, &certdbx[i], SHA1_HASHSIZE) == 0) { + uprintf("Found '%s' revoked certificate", info->name); return TRUE; } } return FALSE; } +BOOL IsSignedBySecureBootAuthority(uint8_t* buf, uint32_t len) +{ + int i; + uint8_t* cert; + cert_info_t info; + + if (buf == NULL || len < 0x100) + return FALSE; + + // Get the signer/issuer info + cert = GetPeSignatureData(buf); + // Secure Boot Authority is always an issuer + if (GetIssuerCertificateInfo(cert, &info) != 2) + return FALSE; + for (i = 0; i < ARRAYSIZE(certauth); i += SHA1_HASHSIZE) { + if (memcmp(info.thumbprint, &certauth[i], SHA1_HASHSIZE) == 0) + return TRUE; + } + return FALSE; +} + int IsBootloaderRevoked(uint8_t* buf, uint32_t len) { uint32_t i; uint8_t hash[SHA256_HASHSIZE]; IMAGE_DOS_HEADER* dos_header = (IMAGE_DOS_HEADER*)buf; IMAGE_NT_HEADERS32* pe_header; + uint8_t* cert; + cert_info_t info; + int r; // Fall back to embedded sbat_level.txt if we couldn't access remote if (sbat_entries == NULL) { @@ -2214,12 +2228,17 @@ int IsBootloaderRevoked(uint8_t* buf, uint32_t len) pe_header = (IMAGE_NT_HEADERS32*)&buf[dos_header->e_lfanew]; if (pe_header->Signature != IMAGE_NT_SIGNATURE) return -2; + + // Get the signer/issuer info + cert = GetPeSignatureData(buf); + r = GetIssuerCertificateInfo(cert, &info); + if (r == 0) + uuprintf(" (Unsigned Bootloader)"); + else if (r > 0) + uuprintf(" Signed by: %s", info.name); + if (!PE256Buffer(buf, len, hash)) return -1; - // Check for UEFI DBX certificate revocation - // This also displays the name of the signer/issuer cert if available - if (IsRevokedByCert(buf, len)) - return 5; // Check for UEFI DBX revocation for (i = 0; i < ARRAYSIZE(pe256dbx); i += SHA256_HASHSIZE) if (memcmp(hash, &pe256dbx[i], SHA256_HASHSIZE) == 0) @@ -2234,6 +2253,9 @@ int IsBootloaderRevoked(uint8_t* buf, uint32_t len) // Check for Microsoft SVN revocation if (IsRevokedBySvn(buf, len)) return 4; + // Check for UEFI DBX certificate revocation + if (IsRevokedByCert(&info)) + return 5; return 0; } diff --git a/src/iso.c b/src/iso.c index ab043607737..45efbbdeb8c 100644 --- a/src/iso.c +++ b/src/iso.c @@ -249,9 +249,10 @@ static BOOL check_iso_props(const char* psz_dirname, int64_t file_length, const // We may extract the bootloaders for revocation validation later but // to do so, since we're working with case sensitive file systems, we // must store all found UEFI bootloader paths with the right case. - for (j = 0; j < ARRAYSIZE(img_report.efi_boot_path); j++) { - if (img_report.efi_boot_path[j][0] == 0) { - static_strcpy(img_report.efi_boot_path[j], psz_fullpath); + for (j = 0; j < ARRAYSIZE(img_report.efi_boot_entry); j++) { + if (img_report.efi_boot_entry[j].path[0] == 0) { + img_report.efi_boot_entry[j].type = EBT_BOOTMGR; + static_strcpy(img_report.efi_boot_entry[j].path, psz_fullpath); break; } } @@ -291,9 +292,10 @@ static BOOL check_iso_props(const char* psz_dirname, int64_t file_length, const if (safe_stricmp(psz_basename, bootloader_name) == 0) { if (k == 0) img_report.has_efi |= (2 << i); // start at 2 since "bootmgr.efi" is bit 0 - for (j = 0; j < ARRAYSIZE(img_report.efi_boot_path); j++) { - if (img_report.efi_boot_path[j][0] == 0) { - static_strcpy(img_report.efi_boot_path[j], psz_fullpath); + for (j = 0; j < ARRAYSIZE(img_report.efi_boot_entry); j++) { + if (img_report.efi_boot_entry[j].path[0] == 0) { + img_report.efi_boot_entry[j].type = (uint8_t)k; + static_strcpy(img_report.efi_boot_entry[j].path, psz_fullpath); break; } } @@ -1559,6 +1561,7 @@ uint32_t ReadISOFileToBuffer(const char* iso, const char* iso_file, uint8_t** bu iso9660_stat_t* p_statbuf = NULL; *buf = NULL; + cdio_loglevel_default = CDIO_LOG_WARN; // First try to open as UDF - fallback to ISO if it failed p_udf = udf_open(iso); @@ -1632,6 +1635,7 @@ uint32_t ReadISOFileToBuffer(const char* iso, const char* iso_file, uint8_t** bu udf_dirent_free(p_udf_file); iso9660_close(p_iso); udf_close(p_udf); + cdio_loglevel_default = usb_debug ? CDIO_LOG_INFO : CDIO_LOG_WARN; if (ret == 0) safe_free(*buf); return ret; diff --git a/src/rufus.c b/src/rufus.c index 0ed225bddfa..b343c0c9251 100755 --- a/src/rufus.c +++ b/src/rufus.c @@ -1609,43 +1609,61 @@ static DWORD WINAPI BootCheckThread(LPVOID param) // Check UEFI bootloaders for revocation if (IS_EFI_BOOTABLE(img_report)) { - assert(ARRAYSIZE(img_report.efi_boot_path) > 0); + BOOL has_secureboot_signed_bootloader = FALSE; + assert(ARRAYSIZE(img_report.efi_boot_entry) > 0); PrintStatus(0, MSG_351); uuprintf("UEFI Secure Boot revocation checks:"); - rr = 0; - for (i = 0; i < ARRAYSIZE(img_report.efi_boot_path) && img_report.efi_boot_path[i][0] != 0; i++) { - static const char* revocation_type[] = { "UEFI DBX", "Windows SSP", "Linux SBAT", "Windows SVN", "Cert DBX" }; - cdio_loglevel_default = CDIO_LOG_WARN; - len = ReadISOFileToBuffer(image_path, img_report.efi_boot_path[i], &buf); - cdio_loglevel_default = usb_debug ? CDIO_LOG_DEBUG : CDIO_LOG_WARN; - if (len == 0) { - uprintf("Warning: Failed to extract '%s' to check for UEFI revocation", img_report.efi_boot_path[i]); - continue; - } - uuprintf("• %s", img_report.efi_boot_path[i]); - r = IsBootloaderRevoked(buf, len); - safe_free(buf); - if (r > 0) { - assert(r <= ARRAYSIZE(revocation_type)); - if (rr == 0) - rr = r; - uprintf("Warning: '%s' has been revoked by %s", img_report.efi_boot_path[i], revocation_type[r - 1]); - is_bootloader_revoked = TRUE; + // Make sure we have at least one regular EFI bootloader that is formally signed + // for Secure Boot, since it doesn't make sense to report revocation otherwise. + for (i = 0; !has_secureboot_signed_bootloader && i < ARRAYSIZE(img_report.efi_boot_entry) && + img_report.efi_boot_entry[i].path[0] != 0; i++) { + if (img_report.efi_boot_entry[i].type == EBT_MAIN) { + len = ReadISOFileToBuffer(image_path, img_report.efi_boot_entry[i].path, &buf); + if (len == 0) { + uprintf("Warning: Failed to extract '%s' to check for UEFI revocation", img_report.efi_boot_entry[i].path); + continue; + } + if (IsSignedBySecureBootAuthority(buf, len)) + has_secureboot_signed_bootloader = TRUE; + free(buf); } } - if (rr > 0) { - switch (rr) { - case 2: - msg = lmprintf(MSG_341, "Error code: 0xc0000428"); - break; - default: - msg = lmprintf(MSG_340); - break; + if (!has_secureboot_signed_bootloader) { + uuprintf(" No Secure Boot signed bootloader found -- skipping"); + } else { + rr = 0; + for (i = 0; i < ARRAYSIZE(img_report.efi_boot_entry) && img_report.efi_boot_entry[i].path[0] != 0; i++) { + static const char* revocation_type[] = { "UEFI DBX", "Windows SSP", "Linux SBAT", "Windows SVN", "Cert DBX" }; + len = ReadISOFileToBuffer(image_path, img_report.efi_boot_entry[i].path, &buf); + if (len == 0) { + uprintf("Warning: Failed to extract '%s' to check for UEFI revocation", img_report.efi_boot_entry[i].path); + continue; + } + uuprintf("• %s", img_report.efi_boot_entry[i].path); + r = IsBootloaderRevoked(buf, len); + safe_free(buf); + if (r > 0) { + assert(r <= ARRAYSIZE(revocation_type)); + if (rr == 0) + rr = r; + uprintf("Warning: '%s' has been revoked by %s", img_report.efi_boot_entry[i].path, revocation_type[r - 1]); + is_bootloader_revoked = TRUE; + } + } + if (rr > 0) { + switch (rr) { + case 2: + msg = lmprintf(MSG_341, "Error code: 0xc0000428"); + break; + default: + msg = lmprintf(MSG_340); + break; + } + r = MessageBoxExU(hMainDialog, lmprintf(MSG_339, msg), lmprintf(MSG_338), + MB_OKCANCEL | MB_ICONWARNING | MB_IS_RTL, selected_langid); + if (r == IDCANCEL) + goto out; } - r = MessageBoxExU(hMainDialog, lmprintf(MSG_339, msg), lmprintf(MSG_338), - MB_OKCANCEL | MB_ICONWARNING | MB_IS_RTL, selected_langid); - if (r == IDCANCEL) - goto out; } } @@ -3516,7 +3534,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine is_vds_available = IsVdsAvailable(FALSE); use_vds = ReadSettingBool(SETTING_USE_VDS) && is_vds_available; usb_debug = ReadSettingBool(SETTING_ENABLE_USB_DEBUG); - cdio_loglevel_default = usb_debug ? CDIO_LOG_DEBUG : CDIO_LOG_WARN; + cdio_loglevel_default = usb_debug ? CDIO_LOG_INFO : CDIO_LOG_WARN; use_rufus_mbr = !ReadSettingBool(SETTING_DISABLE_RUFUS_MBR); // validate_md5sum = ReadSettingBool(SETTING_ENABLE_RUNTIME_VALIDATION); detect_fakes = !ReadSettingBool(SETTING_DISABLE_FAKE_DRIVES_CHECK); @@ -3807,7 +3825,7 @@ extern int TestHashes(void); // Alt-. => Enable USB enumeration debug if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == VK_OEM_PERIOD)) { usb_debug = !usb_debug; - cdio_loglevel_default = usb_debug ? CDIO_LOG_DEBUG : CDIO_LOG_WARN; + cdio_loglevel_default = usb_debug ? CDIO_LOG_INFO : CDIO_LOG_WARN; WriteSettingBool(SETTING_ENABLE_USB_DEBUG, usb_debug); PrintStatusTimeout(lmprintf(MSG_270), usb_debug); GetDevices(0); diff --git a/src/rufus.h b/src/rufus.h index 0f3d44bc911..540a1bd7682 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -327,6 +327,13 @@ enum file_io_type { FILE_IO_APPEND }; +enum EFI_BOOT_TYPE { + EBT_MAIN = 0, + EBT_GRUB, + EBT_MOKMANAGER, + EBT_BOOTMGR +}; + /* Special handling for old .c32 files we need to replace */ #define NB_OLD_C32 2 #define OLD_C32_NAMES { "menu.c32", "vesamenu.c32" } @@ -383,13 +390,18 @@ enum ArchType { ARCH_MAX }; +typedef struct { + uint8_t type; + char path[64]; +} efi_boot_entry_t; + typedef struct { char label[192]; // 3*64 to account for UTF-8 char usb_label[192]; // converted USB label for workaround char cfg_path[128]; // path to the ISO's isolinux.cfg char reactos_path[128]; // path to the ISO's freeldr.sys or setupldr.sys char wininst_path[MAX_WININST][64]; // path to the Windows install image(s) - char efi_boot_path[64][32]; // paths of detected UEFI bootloaders + efi_boot_entry_t efi_boot_entry[64];// types and paths of detected UEFI bootloaders char efi_img_path[128]; // path to an efi.img file uint64_t image_size; uint64_t projected_size; @@ -823,6 +835,7 @@ extern BOOL PE256Buffer(uint8_t* buf, uint32_t len, uint8_t* hash); extern void UpdateMD5Sum(const char* dest_dir, const char* md5sum_name); extern BOOL HashBuffer(const unsigned type, const uint8_t* buf, const size_t len, uint8_t* sum); extern BOOL IsFileInDB(const char* path); +extern BOOL IsSignedBySecureBootAuthority(uint8_t* buf, uint32_t len); extern int IsBootloaderRevoked(uint8_t* buf, uint32_t len); extern void PrintRevokedBootloaderInfo(void); extern BOOL IsBufferInDB(const unsigned char* buf, const size_t len); diff --git a/src/rufus.rc b/src/rufus.rc index cffac279efd..2e094160c15 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 4.6.2204" +CAPTION "Rufus 4.6.2205" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -399,8 +399,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,6,2204,0 - PRODUCTVERSION 4,6,2204,0 + FILEVERSION 4,6,2205,0 + PRODUCTVERSION 4,6,2205,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -418,13 +418,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "4.6.2204" + VALUE "FileVersion", "4.6.2205" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2024 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-4.6.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "4.6.2204" + VALUE "ProductVersion", "4.6.2205" END END BLOCK "VarFileInfo"