mirror of
https://github.com/pbatard/rufus.git
synced 2025-05-09 12:31:57 -04:00
[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.
This commit is contained in:
parent
5439ca8a83
commit
4d42b7a73a
7 changed files with 145 additions and 65 deletions
|
@ -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)
|
||||
|
|
19
src/db.h
19
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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
56
src/hash.c
56
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;
|
||||
}
|
||||
|
||||
|
|
16
src/iso.c
16
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;
|
||||
|
|
86
src/rufus.c
86
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);
|
||||
|
|
15
src/rufus.h
15
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);
|
||||
|
|
10
src/rufus.rc
10
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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue