Skip to content

Commit

Permalink
[efi] further improve revoked UEFI bootloader reporting
Browse files Browse the repository at this point in the history
* Do not report SBAT revocations unless we actually have a formal Secure Boot signed bootloader.
* Also reduce verbose log pollution by libcdio.
  • Loading branch information
pbatard committed Oct 10, 2024
1 parent 5439ca8 commit 4d42b7a
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 65 deletions.
8 changes: 8 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
19 changes: 17 additions & 2 deletions src/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

/*
Expand Down
56 changes: 39 additions & 17 deletions src/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
Expand All @@ -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;
}

Expand Down
16 changes: 10 additions & 6 deletions src/iso.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
86 changes: 52 additions & 34 deletions src/rufus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
15 changes: 14 additions & 1 deletion src/rufus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
10 changes: 5 additions & 5 deletions src/rufus.rc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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"
Expand Down

0 comments on commit 4d42b7a

Please sign in to comment.