From 4d42b7a73a036a01bf3676852f9eb10fd9f1a16c Mon Sep 17 00:00:00 2001
From: Pete Batard <pete@akeo.ie>
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 3200c139..66f61081 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 52c02659..1b4f4e85 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 ee21cc51..88ffca31 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -2171,27 +2171,38 @@ 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;
+
+	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);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+BOOL IsSignedBySecureBootAuthority(uint8_t* buf, uint32_t len)
 {
 	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)
+	if (buf == NULL || len < 0x100)
 		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);
+	// 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;
 }
@@ -2202,6 +2213,9 @@ int IsBootloaderRevoked(uint8_t* buf, uint32_t len)
 	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 ab043607..45efbbde 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 @@ out:
 	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 0ed225bd..b343c0c9 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 @@ skip_args_processing:
 	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 0f3d44bc..540a1bd7 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 cffac279..2e094160 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"