[iso] improve UEFI bootloader reporting

* Add systemd-boot version reporting and fix GRUB version detection for CentOS
* Add notes about the various Secure Boot gotchas (log only)
* Enable download of remote active/revoked Secure Boot certificate thumbprints
* Also rename the ceiling/floor align macros
This commit is contained in:
Pete Batard 2025-05-26 20:50:33 +01:00
parent 207a330979
commit 688f011f31
No known key found for this signature in database
GPG key ID: 38E0CF5E69EDD671
12 changed files with 325 additions and 146 deletions

View file

@ -1,3 +1,15 @@
o Version 4.8 (2025.06.??)
Switch to wimlib for all WIM image processing:
- Greatly speeds up image analysis when opening Windows ISOs
- Can speed up Windows To Go drive creation (But won't do miracles if you have a crap drive)
- Might help with Parallels limitations on Mac (But Rufus on Parallels is still UNSUPPORTED)
- Enables the splitting of >4GB files with Alt-E (But still WAY SLOWER than using UEFI:NTFS)
Switch to using Visual Studio binaries everywhere, due to MinGW DLL delay-loading limitations
Add more exceptions for Linux ISOs that restrict themselves to DD mode (Nobara, openSUSE, ...)
Improve reporting of UEFI bootloaders in the log, with info on the Secure Boot status
Fix an issue with size limitations when writing an uncompressed VHD back to the same drive
Fix a crash when opening the log with the 32-bit MinGW compiled version
o Version 4.7 (2025.04.09) o Version 4.7 (2025.04.09)
Add a mechanism to detect and download updated DBXs from the official UEFI repository Add a mechanism to detect and download updated DBXs from the official UEFI repository
Add ztsd compression support for disk images Add ztsd compression support for disk images

View file

@ -127,24 +127,22 @@ static uint8_t sha256db[] = {
* Contains the SHA-1 thumbprints of the issuer certificate of the official * Contains the SHA-1 thumbprints of the issuer certificate of the official
* Secure Boot signing authority (i.e. Microsoft). * Secure Boot signing authority (i.e. Microsoft).
*/ */
static uint8_t certauth[] = { static const char db_sb_active_txt[] =
// 'Microsoft Windows Production PCA 2011' // '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, "580a6f4cc4e4b669b9ebdc1b2b3e087b80d0678d\n"
// 'Microsoft Corporation UEFI CA 2011' // '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, "46def63b5ce61cf8ba0de2e6639c1019d0ed14f3\n"
// 'Windows UEFI CA 2023' // 'Windows UEFI CA 2023'
0x45, 0xa0, 0xfa, 0x32, 0x60, 0x47, 0x73, 0xc8, 0x24, 0x33, 0xc3, 0xb7, 0xd5, 0x9e, 0x74, 0x66, 0xb3, 0xac, 0x0c, 0x67, "45a0fa32604773c82433c3b7d59e7466b3ac0c67\n"
// 'Microsoft UEFI CA 2023' // 'Microsoft UEFI CA 2023'
0xb5, 0xee, 0xb4, 0xa6, 0x70, 0x60, 0x48, 0x07, 0x3f, 0x0e, 0xd2, 0x96, 0xe7, 0xf5, 0x80, 0xa7, 0x90, 0xb5, 0x9e, 0xaa, "b5eeb4a6706048073f0ed296e7f580a790b59eaa";
};
/* /*
* Contains the SHA-1 thumbprints of certificates that are being revoked by DBX. * Contains the SHA-1 thumbprints of certificates that are being revoked by DBX.
* This only includes the 'Microsoft Windows Production PCA 2011' for now. * This only includes the 'Microsoft Windows Production PCA 2011' for now.
*/ */
static uint8_t certdbx[] = { static const char db_sb_revoked_txt[] =
0x58, 0x0a, 0x6f, 0x4c, 0xc4, 0xe4, 0xb6, 0x69, 0xb9, 0xeb, 0xdc, 0x1b, 0x2b, 0x3e, 0x08, 0x7b, 0x80, 0xd0, 0x67, 0x8d, "580a6f4cc4e4b669b9ebdc1b2b3e087b80d0678d";
};
/* /*
* Extended SBATLevel.txt that merges Linux SBAT with Microsoft's SVN * Extended SBATLevel.txt that merges Linux SBAT with Microsoft's SVN

View file

@ -1,7 +1,7 @@
/* /*
* Rufus: The Reliable USB Formatting Utility * Rufus: The Reliable USB Formatting Utility
* Drive access function calls * Drive access function calls
* Copyright © 2011-2024 Pete Batard <pete@akeo.ie> * Copyright © 2011-2025 Pete Batard <pete@akeo.ie>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -2299,7 +2299,7 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m
// CHS sizes that IBM imparted upon us. Long story short, we now align to a // CHS sizes that IBM imparted upon us. Long story short, we now align to a
// cylinder size that is itself aligned to the cluster size. // cylinder size that is itself aligned to the cluster size.
// If this actually breaks old systems, please send your complaints to IBM. // If this actually breaks old systems, please send your complaints to IBM.
SelectedDrive.Partition[pi].Offset = HI_ALIGN_X_TO_Y(bytes_per_track, ClusterSize); SelectedDrive.Partition[pi].Offset = CEILING_ALIGN(bytes_per_track, ClusterSize);
// GRUB2 no longer fits in the usual 31½ KB that the above computation provides // GRUB2 no longer fits in the usual 31½ KB that the above computation provides
// so just unconditionally double that size and get on with it. // so just unconditionally double that size and get on with it.
SelectedDrive.Partition[pi].Offset *= 2; SelectedDrive.Partition[pi].Offset *= 2;
@ -2316,9 +2316,9 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m
SelectedDrive.Partition[pi].Size = esp_size; SelectedDrive.Partition[pi].Size = esp_size;
SelectedDrive.Partition[pi + 1].Offset = SelectedDrive.Partition[pi].Offset + SelectedDrive.Partition[pi].Size; SelectedDrive.Partition[pi + 1].Offset = SelectedDrive.Partition[pi].Offset + SelectedDrive.Partition[pi].Size;
// Align next partition to track and cluster // Align next partition to track and cluster
SelectedDrive.Partition[pi + 1].Offset = HI_ALIGN_X_TO_Y(SelectedDrive.Partition[pi + 1].Offset, bytes_per_track); SelectedDrive.Partition[pi + 1].Offset = CEILING_ALIGN(SelectedDrive.Partition[pi + 1].Offset, bytes_per_track);
if (ClusterSize % SelectedDrive.SectorSize == 0) if (ClusterSize % SelectedDrive.SectorSize == 0)
SelectedDrive.Partition[pi + 1].Offset = LO_ALIGN_X_TO_Y(SelectedDrive.Partition[pi + 1].Offset, ClusterSize); SelectedDrive.Partition[pi + 1].Offset = FLOOR_ALIGN(SelectedDrive.Partition[pi + 1].Offset, ClusterSize);
assert(SelectedDrive.Partition[pi + 1].Offset >= SelectedDrive.Partition[pi].Offset + SelectedDrive.Partition[pi].Size); assert(SelectedDrive.Partition[pi + 1].Offset >= SelectedDrive.Partition[pi].Offset + SelectedDrive.Partition[pi].Size);
pi++; pi++;
// Clear the extra partition we processed // Clear the extra partition we processed
@ -2331,9 +2331,9 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m
wcscpy(SelectedDrive.Partition[pi].Name, L"Microsoft Reserved Partition"); wcscpy(SelectedDrive.Partition[pi].Name, L"Microsoft Reserved Partition");
SelectedDrive.Partition[pi].Size = 128 * MB; SelectedDrive.Partition[pi].Size = 128 * MB;
SelectedDrive.Partition[pi + 1].Offset = SelectedDrive.Partition[pi].Offset + SelectedDrive.Partition[pi].Size; SelectedDrive.Partition[pi + 1].Offset = SelectedDrive.Partition[pi].Offset + SelectedDrive.Partition[pi].Size;
SelectedDrive.Partition[pi + 1].Offset = HI_ALIGN_X_TO_Y(SelectedDrive.Partition[pi + 1].Offset, bytes_per_track); SelectedDrive.Partition[pi + 1].Offset = CEILING_ALIGN(SelectedDrive.Partition[pi + 1].Offset, bytes_per_track);
if (ClusterSize % SelectedDrive.SectorSize == 0) if (ClusterSize % SelectedDrive.SectorSize == 0)
SelectedDrive.Partition[pi + 1].Offset = LO_ALIGN_X_TO_Y(SelectedDrive.Partition[pi + 1].Offset, ClusterSize); SelectedDrive.Partition[pi + 1].Offset = FLOOR_ALIGN(SelectedDrive.Partition[pi + 1].Offset, ClusterSize);
assert(SelectedDrive.Partition[pi + 1].Offset >= SelectedDrive.Partition[pi].Offset + SelectedDrive.Partition[pi].Size); assert(SelectedDrive.Partition[pi + 1].Offset >= SelectedDrive.Partition[pi].Offset + SelectedDrive.Partition[pi].Size);
pi++; pi++;
extra_partitions &= ~(XP_MSR); extra_partitions &= ~(XP_MSR);
@ -2352,16 +2352,16 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m
assert(persistence_size != 0); assert(persistence_size != 0);
partition_index[PI_CASPER] = pi; partition_index[PI_CASPER] = pi;
wcscpy(SelectedDrive.Partition[pi].Name, L"Linux Persistence"); wcscpy(SelectedDrive.Partition[pi].Name, L"Linux Persistence");
SelectedDrive.Partition[pi++].Size = HI_ALIGN_X_TO_Y(persistence_size, bytes_per_track); SelectedDrive.Partition[pi++].Size = CEILING_ALIGN(persistence_size, bytes_per_track);
} }
if (extra_partitions & XP_ESP) { if (extra_partitions & XP_ESP) {
partition_index[PI_ESP] = pi; partition_index[PI_ESP] = pi;
wcscpy(SelectedDrive.Partition[pi].Name, L"EFI System Partition"); wcscpy(SelectedDrive.Partition[pi].Name, L"EFI System Partition");
SelectedDrive.Partition[pi++].Size = HI_ALIGN_X_TO_Y(esp_size, bytes_per_track); SelectedDrive.Partition[pi++].Size = CEILING_ALIGN(esp_size, bytes_per_track);
} else if (extra_partitions & XP_UEFI_NTFS) { } else if (extra_partitions & XP_UEFI_NTFS) {
partition_index[PI_UEFI_NTFS] = pi; partition_index[PI_UEFI_NTFS] = pi;
wcscpy(SelectedDrive.Partition[pi].Name, L"UEFI:NTFS"); wcscpy(SelectedDrive.Partition[pi].Name, L"UEFI:NTFS");
SelectedDrive.Partition[pi++].Size = HI_ALIGN_X_TO_Y(uefi_ntfs_size, bytes_per_track); SelectedDrive.Partition[pi++].Size = CEILING_ALIGN(uefi_ntfs_size, bytes_per_track);
} else if (extra_partitions & XP_COMPAT) { } else if (extra_partitions & XP_COMPAT) {
wcscpy(SelectedDrive.Partition[pi].Name, L"BIOS Compatibility"); wcscpy(SelectedDrive.Partition[pi].Name, L"BIOS Compatibility");
SelectedDrive.Partition[pi++].Size = bytes_per_track; // One track for the extra partition SelectedDrive.Partition[pi++].Size = bytes_per_track; // One track for the extra partition
@ -2377,13 +2377,13 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m
last_offset -= 33ULL * SelectedDrive.SectorSize; last_offset -= 33ULL * SelectedDrive.SectorSize;
for (i = pi - 1; i > mi; i--) { for (i = pi - 1; i > mi; i--) {
assert(SelectedDrive.Partition[i].Size < last_offset); assert(SelectedDrive.Partition[i].Size < last_offset);
SelectedDrive.Partition[i].Offset = LO_ALIGN_X_TO_Y(last_offset - SelectedDrive.Partition[i].Size, bytes_per_track); SelectedDrive.Partition[i].Offset = FLOOR_ALIGN(last_offset - SelectedDrive.Partition[i].Size, bytes_per_track);
last_offset = SelectedDrive.Partition[i].Offset; last_offset = SelectedDrive.Partition[i].Offset;
} }
// With the above, Compute the main partition size (which we align to a track) // With the above, Compute the main partition size (which we align to a track)
assert(last_offset > SelectedDrive.Partition[mi].Offset); assert(last_offset > SelectedDrive.Partition[mi].Offset);
SelectedDrive.Partition[mi].Size = LO_ALIGN_X_TO_Y(last_offset - SelectedDrive.Partition[mi].Offset, bytes_per_track); SelectedDrive.Partition[mi].Size = FLOOR_ALIGN(last_offset - SelectedDrive.Partition[mi].Offset, bytes_per_track);
// Try to make sure that the main partition size is a multiple of the cluster size // Try to make sure that the main partition size is a multiple of the cluster size
// This can be especially important when trying to capture an NTFS partition as FFU, as, when // This can be especially important when trying to capture an NTFS partition as FFU, as, when
// the NTFS partition is aligned to cluster size, the FFU capture parses the NTFS allocated // the NTFS partition is aligned to cluster size, the FFU capture parses the NTFS allocated
@ -2391,7 +2391,7 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m
// a full sector by sector scan of the NTFS partition and records any non-zero garbage, which // a full sector by sector scan of the NTFS partition and records any non-zero garbage, which
// may include garbage leftover data from a previous reformat... // may include garbage leftover data from a previous reformat...
if (ClusterSize % SelectedDrive.SectorSize == 0) if (ClusterSize % SelectedDrive.SectorSize == 0)
SelectedDrive.Partition[mi].Size = LO_ALIGN_X_TO_Y(SelectedDrive.Partition[mi].Size, ClusterSize); SelectedDrive.Partition[mi].Size = FLOOR_ALIGN(SelectedDrive.Partition[mi].Size, ClusterSize);
if (SelectedDrive.Partition[mi].Size <= 0) { if (SelectedDrive.Partition[mi].Size <= 0) {
uprintf("Error: Invalid %S size", SelectedDrive.Partition[mi].Name); uprintf("Error: Invalid %S size", SelectedDrive.Partition[mi].Name);
return FALSE; return FALSE;

View file

@ -1372,9 +1372,9 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive)
// 2. WriteFile fails unless the size is a multiple of sector size // 2. WriteFile fails unless the size is a multiple of sector size
if (read_size[read_bufnum] % SelectedDrive.SectorSize != 0) { if (read_size[read_bufnum] % SelectedDrive.SectorSize != 0) {
if_not_assert(HI_ALIGN_X_TO_Y(read_size[read_bufnum], SelectedDrive.SectorSize) <= buf_size) if_not_assert(CEILING_ALIGN(read_size[read_bufnum], SelectedDrive.SectorSize) <= buf_size)
goto out; goto out;
read_size[read_bufnum] = HI_ALIGN_X_TO_Y(read_size[read_bufnum], SelectedDrive.SectorSize); read_size[read_bufnum] = CEILING_ALIGN(read_size[read_bufnum], SelectedDrive.SectorSize);
} }
// 3. Switch to the next reading buffer // 3. Switch to the next reading buffer

View file

@ -114,7 +114,7 @@ StrArray modified_files = { 0 };
extern int default_thread_priority; extern int default_thread_priority;
extern const char* efi_archname[ARCH_MAX]; extern const char* efi_archname[ARCH_MAX];
extern char* sbat_level_txt; extern char *sbat_level_txt, *sb_active_txt, *sb_revoked_txt;
extern BOOL expert_mode, usb_debug; extern BOOL expert_mode, usb_debug;
/* /*
@ -2162,6 +2162,12 @@ static BOOL IsRevokedBySbat(uint8_t* buf, uint32_t len)
uint32_t i, j, sbat_len; uint32_t i, j, sbat_len;
sbat_entry_t entry; sbat_entry_t entry;
// Fall back to embedded sbat_level.txt if we couldn't access remote
if (sbat_entries == NULL) {
sbat_level_txt = safe_strdup(db_sbat_level_txt);
sbat_entries = GetSbatEntries(sbat_level_txt);
}
assert(sbat_entries != NULL);
if (sbat_entries == NULL) if (sbat_entries == NULL)
return FALSE; return FALSE;
@ -2220,7 +2226,7 @@ static BOOL IsRevokedByDbx(uint8_t* hash, uint8_t* buf, uint32_t len)
dbx_size = read_file(path, &dbx_data); dbx_size = read_file(path, &dbx_data);
needs_free = (dbx_data != NULL); needs_free = (dbx_data != NULL);
if (needs_free) if (needs_free)
duprintf("Using local %s for revocation check", path); duprintf(" Using local %s for revocation check", path);
} }
if (dbx_size == 0) { if (dbx_size == 0) {
dbx_data = (BYTE*)GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_DBX + i), dbx_data = (BYTE*)GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_DBX + i),
@ -2239,7 +2245,7 @@ static BOOL IsRevokedByDbx(uint8_t* hash, uint8_t* buf, uint32_t len)
goto out; goto out;
// Expect SHA-256 hashes // Expect SHA-256 hashes
if (!CompareGUID(&efi_sig_list->SignatureType, &EFI_CERT_SHA256_GUID)) { if (!CompareGUID(&efi_sig_list->SignatureType, &EFI_CERT_SHA256_GUID)) {
uprintf("WARNING: %s is not using SHA-256 hashes - Cannot check for UEFI revocation!", dbx_name); uprintf(" Warning: %s is not using SHA-256 hashes - Cannot check for UEFI revocation!", dbx_name);
goto out; goto out;
} }
fluff_size += efi_sig_list->SignatureHeaderSize; fluff_size += efi_sig_list->SignatureHeaderSize;
@ -2301,7 +2307,7 @@ static BOOL IsRevokedBySvn(uint8_t* buf, uint32_t len)
return TRUE; return TRUE;
} }
} else { } else {
uprintf("WARNING: Unexpected Secure Version Number size"); uprintf(" Warning: Unexpected Secure Version Number size");
} }
} }
} }
@ -2310,13 +2316,24 @@ static BOOL IsRevokedBySvn(uint8_t* buf, uint32_t len)
static BOOL IsRevokedByCert(cert_info_t* info) static BOOL IsRevokedByCert(cert_info_t* info)
{ {
int i; uint32_t i;
for (i = 0; i < ARRAYSIZE(certdbx); i += SHA1_HASHSIZE) { // TODO: Enable this for non expert mode after enforcement of PCA2011 cert revocation
if (!expert_mode) if (!expert_mode)
continue; return FALSE;
if (memcmp(info->thumbprint, &certdbx[i], SHA1_HASHSIZE) == 0) {
uprintf("Found '%s' revoked certificate", info->name); // Fall back to embedded Secure Boot thumbprints if we couldn't access remote
if (sb_revoked_certs == NULL) {
sb_revoked_txt = safe_strdup(db_sb_revoked_txt);
sb_revoked_certs = GetThumbprintEntries(sb_revoked_txt);
}
assert(sb_revoked_certs != NULL && sb_revoked_certs->count != 0);
if (sb_revoked_certs == NULL)
return FALSE;
for (i = 0; i < sb_revoked_certs->count; i++) {
if (memcmp(info->thumbprint, sb_revoked_certs->list[i], SHA1_HASHSIZE) == 0) {
uuprintf(" Found '%s' revoked certificate", info->name);
return TRUE; return TRUE;
} }
} }
@ -2325,7 +2342,7 @@ static BOOL IsRevokedByCert(cert_info_t* info)
BOOL IsSignedBySecureBootAuthority(uint8_t* buf, uint32_t len) BOOL IsSignedBySecureBootAuthority(uint8_t* buf, uint32_t len)
{ {
int i; uint32_t i;
uint8_t* cert; uint8_t* cert;
cert_info_t info; cert_info_t info;
@ -2337,8 +2354,19 @@ BOOL IsSignedBySecureBootAuthority(uint8_t* buf, uint32_t len)
// Secure Boot Authority is always an issuer // Secure Boot Authority is always an issuer
if (GetIssuerCertificateInfo(cert, &info) != 2) if (GetIssuerCertificateInfo(cert, &info) != 2)
return FALSE; return FALSE;
for (i = 0; i < ARRAYSIZE(certauth); i += SHA1_HASHSIZE) {
if (memcmp(info.thumbprint, &certauth[i], SHA1_HASHSIZE) == 0) // Fall back to embedded Secure Boot thumbprints if we couldn't access remote
if (sb_active_certs == NULL) {
sb_active_txt = safe_strdup(db_sb_active_txt);
sb_active_certs = GetThumbprintEntries(sb_active_txt);
}
// If we still manage to get an empty list at this stage, I sure wanna know about it!
assert(sb_active_certs != NULL && sb_active_certs->count != 0);
if (sb_active_certs == NULL || sb_active_certs->count == 0)
return FALSE;
for (i = 0; i < sb_active_certs->count; i++) {
if (memcmp(info.thumbprint, sb_active_certs->list[i], SHA1_HASHSIZE) == 0)
return TRUE; return TRUE;
} }
return FALSE; return FALSE;
@ -2352,13 +2380,7 @@ int IsBootloaderRevoked(uint8_t* buf, uint32_t len)
IMAGE_NT_HEADERS32* pe_header; IMAGE_NT_HEADERS32* pe_header;
uint8_t* cert; uint8_t* cert;
cert_info_t info; cert_info_t info;
int r; int r, revoked = 0;
// Fall back to embedded sbat_level.txt if we couldn't access remote
if (sbat_entries == NULL) {
sbat_level_txt = safe_strdup(db_sbat_level_txt);
sbat_entries = GetSbatEntries(sbat_level_txt);
}
if (buf == NULL || len < 0x100 || dos_header->e_magic != IMAGE_DOS_SIGNATURE) if (buf == NULL || len < 0x100 || dos_header->e_magic != IMAGE_DOS_SIGNATURE)
return -2; return -2;
@ -2370,29 +2392,45 @@ int IsBootloaderRevoked(uint8_t* buf, uint32_t len)
cert = GetPeSignatureData(buf); cert = GetPeSignatureData(buf);
r = GetIssuerCertificateInfo(cert, &info); r = GetIssuerCertificateInfo(cert, &info);
if (r == 0) if (r == 0)
uuprintf(" (Unsigned Bootloader)"); uprintf(" (Unsigned Bootloader)");
else if (r > 0) else if (r > 0)
uuprintf(" Signed by: %s", info.name); uprintf(" Signed by '%s'", info.name);
if (!PE256Buffer(buf, len, hash)) if (!PE256Buffer(buf, len, hash))
return -1; return -1;
// Check for UEFI DBX revocation // Check for UEFI DBX revocation
if (IsRevokedByDbx(hash, buf, len)) if (IsRevokedByDbx(hash, buf, len))
return 1; revoked = 1;
// Check for Microsoft SSP revocation // Check for Microsoft SSP revocation
for (i = 0; i < pe256ssp_size * SHA256_HASHSIZE; i += SHA256_HASHSIZE) for (i = 0; revoked == 0 && i < pe256ssp_size * SHA256_HASHSIZE; i += SHA256_HASHSIZE)
if (memcmp(hash, &pe256ssp[i], SHA256_HASHSIZE) == 0) if (memcmp(hash, &pe256ssp[i], SHA256_HASHSIZE) == 0)
return 2; revoked = 2;
// Check for Linux SBAT revocation // Check for Linux SBAT revocation
if (IsRevokedBySbat(buf, len)) if (revoked == 0 && IsRevokedBySbat(buf, len))
return 3; revoked = 3;
// Check for Microsoft SVN revocation // Check for Microsoft SVN revocation
if (IsRevokedBySvn(buf, len)) if (revoked == 0 && IsRevokedBySvn(buf, len))
return 4; revoked = 4;
// Check for UEFI DBX certificate revocation // Check for UEFI DBX certificate revocation
if (IsRevokedByCert(&info)) if (revoked == 0 && IsRevokedByCert(&info))
return 5; revoked = 5;
return 0;
// If signed and not revoked, print the various Secure Boot "gotchas"
if (r > 0 && revoked == 0) {
if (strcmp(info.name, "Microsoft Windows Production PCA 2011") == 0) {
uprintf(" Note: This bootloader may fail Secure Boot validation on systems that");
uprintf(" have been updated to use the 'Windows UEFI CA 2023' certificate.");
} else if (strcmp(info.name, "Windows UEFI CA 2023") == 0) {
uprintf(" Note: This bootloader will fail Secure Boot validation on systems that");
uprintf(" have not been updated to use the latest Secure Boot certificates");
} else if (strcmp(info.name, "Microsoft Corporation UEFI CA 2011") == 0 ||
strcmp(info.name, "Microsoft UEFI CA 2023") == 0) {
uprintf(" Note: This bootloader may fail Secure Boot validation on *some* systems,");
uprintf(" unless you enable \"Microsoft 3rd-party UEFI CA\" in your 'BIOS'.");
}
}
return revoked;
} }
/* /*

View file

@ -1055,8 +1055,12 @@ void GetGrubVersion(char* buf, size_t buf_size, const char* source)
if (buf_size > max_string_size) { if (buf_size > max_string_size) {
for (i = 0; i < buf_size - max_string_size; i++) { for (i = 0; i < buf_size - max_string_size; i++) {
for (j = 0; j < ARRAYSIZE(grub_version_str); j++) { for (j = 0; j < ARRAYSIZE(grub_version_str); j++) {
if (memcmp(&buf[i], grub_version_str[j], strlen(grub_version_str[j]) + 1) == 0) if (memcmp(&buf[i], grub_version_str[j], strlen(grub_version_str[j])) == 0) {
// For CentOS, who decided to add a '\n' after "GRUB version %s"
if (buf[i + strlen(grub_version_str[j]) + 1] == '\0')
i++;
static_strcpy(grub_version, &buf[i + strlen(grub_version_str[j]) + 1]); static_strcpy(grub_version, &buf[i + strlen(grub_version_str[j]) + 1]);
}
} }
if (memcmp(&buf[i], grub_debug_is_enabled_str, strlen(grub_debug_is_enabled_str)) == 0) if (memcmp(&buf[i], grub_debug_is_enabled_str, strlen(grub_debug_is_enabled_str)) == 0)
has_grub_debug_is_enabled = TRUE; has_grub_debug_is_enabled = TRUE;
@ -1139,6 +1143,37 @@ void GetGrubFs(char* buf, size_t buf_size)
} }
} }
void GetEfiBootInfo(char* buf, size_t buf_size, const char* source)
{
// Data to help us identify the EFI bootloader type
const struct {
const char* label;
const char* search_string;
} boot_info[] = {
{ "Shim", "UEFI SHIM\n$Version: "},
// NB: There's also an ID=systemd-boot\nVERSION="x.y.z" footer
// in the Arch systemd-boot EFI binary, but I'm not sure if we
// can count on this metadata footer to always be present...
{ "systemd-boot", "#### LoaderInfo: systemd-boot " },
};
const size_t max_string_size = 64;
size_t i, j, k;
if (buf_size > max_string_size) {
for (i = 0; i < buf_size - max_string_size; i++) {
for (j = 0; j < ARRAYSIZE(boot_info); j++) {
if (memcmp(&buf[i], boot_info[j].search_string, strlen(boot_info[j].search_string)) == 0) {
i += strlen(boot_info[j].search_string);
for (k = 0; k < 32 && i + k < buf_size - 1 && !isspace(buf[i + k]); k++);
buf[i + k] = '\0';
uprintf(" Detected %s version: %s (from '%s')", boot_info[j].label, &buf[i], source);
return;
}
}
}
}
}
BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan) BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan)
{ {
const char* basedir[] = { "i386", "amd64", "minint" }; const char* basedir[] = { "i386", "amd64", "minint" };
@ -1386,8 +1421,9 @@ out:
} }
} }
for (j = 0; j < ARRAYSIZE(img_report.efi_boot_entry); j++) { for (j = 0; j < ARRAYSIZE(img_report.efi_boot_entry); j++) {
if (!img_report.efi_boot_entry[j].path[0])
continue;
if (img_report.efi_boot_entry[j].type == EBT_GRUB) { if (img_report.efi_boot_entry[j].type == EBT_GRUB) {
size = (size_t)ReadISOFileToBuffer(src_iso, img_report.efi_boot_entry[j].path, &buf); size = (size_t)ReadISOFileToBuffer(src_iso, img_report.efi_boot_entry[j].path, &buf);
if (size == 0) { if (size == 0) {
uprintf(" Could not read Grub version from '%s'", img_report.efi_boot_entry[j].path); uprintf(" Could not read Grub version from '%s'", img_report.efi_boot_entry[j].path);
@ -1397,6 +1433,14 @@ out:
GetGrubFs(buf, size); GetGrubFs(buf, size);
} }
safe_free(buf); safe_free(buf);
} else if (img_report.efi_boot_entry[j].type == EBT_MAIN) {
size = (size_t)ReadISOFileToBuffer(src_iso, img_report.efi_boot_entry[j].path, &buf);
if (size == 0) {
uprintf(" Could not parse '%s'", img_report.efi_boot_entry[j].path);
} else {
GetEfiBootInfo(buf, size, img_report.efi_boot_entry[j].path);
}
safe_free(buf);
} }
} }
for (i = 0; i < (int)grub_filesystems.Index; i++) { for (i = 0; i < (int)grub_filesystems.Index; i++) {

View file

@ -30,10 +30,14 @@
#define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif #endif
#define LO_ALIGN_X_TO_Y(x, y) (((x) / (y)) * (y)) #define MAP_BIT(bit) do { map[_log2(bit)] = b; b <<= 1; } while(0)
#define HI_ALIGN_X_TO_Y(x, y) ((((x) + (y) - 1) / (y)) * (y))
#define FLOOR_ALIGN(x, y) (((x) / (y)) * (y))
#define CEILING_ALIGN(x, y) ((((x) + (y) - 1) / (y)) * (y))
#define IS_HEXASCII(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f')) #define IS_HEXASCII(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f'))
#define FROM_HEXASCII(c) (((c) >= '0' && (c) <= '9') ? (c) - '0' : (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 10 : \
(((c) >= 'a' && (c) <= 'z') ? (c) - 'a' + 10 : 0 )))
/* /*
* Prefetch 64 bytes at address m, for read-only operation * Prefetch 64 bytes at address m, for read-only operation

View file

@ -1582,20 +1582,18 @@ sbat_entry_t* GetSbatEntries(char* sbatlevel)
BOOL eol, eof; BOOL eol, eof;
char* version_str; char* version_str;
uint32_t i, num_entries; uint32_t i, num_entries;
sbat_entry_t* _sbat_entries; sbat_entry_t* sbat_list;
if (sbatlevel == NULL) if (sbatlevel == NULL)
return NULL; return NULL;
num_entries = 0; num_entries = 1;
for (i = 0; sbatlevel[i] != '\0'; i++) for (i = 0; sbatlevel[i] != '\0'; i++)
if (sbatlevel[i] == '\n') if (sbatlevel[i] == '\n')
num_entries++; num_entries++;
if (num_entries == 0) sbat_list = calloc(num_entries + 1, sizeof(sbat_entry_t));
return NULL; if (sbat_list == NULL)
_sbat_entries = calloc(num_entries + 2, sizeof(sbat_entry_t));
if (_sbat_entries == NULL)
return NULL; return NULL;
num_entries = 0; num_entries = 0;
@ -1611,7 +1609,7 @@ sbat_entry_t* GetSbatEntries(char* sbatlevel)
i++; i++;
continue; continue;
} }
_sbat_entries[num_entries].product = &sbatlevel[i]; sbat_list[num_entries].product = &sbatlevel[i];
for (; sbatlevel[i] != ',' && sbatlevel[i] != '\0' && sbatlevel[i] != '\n'; i++); for (; sbatlevel[i] != ',' && sbatlevel[i] != '\0' && sbatlevel[i] != '\n'; i++);
if (sbatlevel[i] == '\0' || sbatlevel[i] == '\n') if (sbatlevel[i] == '\0' || sbatlevel[i] == '\n')
break; break;
@ -1625,16 +1623,77 @@ sbat_entry_t* GetSbatEntries(char* sbatlevel)
i++; i++;
// Allow the provision of an hex version // Allow the provision of an hex version
if (version_str[0] == '0' && version_str[1] == 'x') if (version_str[0] == '0' && version_str[1] == 'x')
_sbat_entries[num_entries].version = strtoul(version_str, NULL, 16); sbat_list[num_entries].version = strtoul(version_str, NULL, 16);
else else
_sbat_entries[num_entries].version = strtoul(version_str, NULL, 10); sbat_list[num_entries].version = strtoul(version_str, NULL, 10);
if (!eol) if (!eol)
for (; sbatlevel[i] != '\0' && sbatlevel[i] != '\n'; i++); for (; sbatlevel[i] != '\0' && sbatlevel[i] != '\n'; i++);
if (_sbat_entries[num_entries].version != 0) if (sbat_list[num_entries].version != 0)
num_entries++; num_entries++;
} }
if (num_entries == 0) {
free(sbat_list);
return NULL;
}
return _sbat_entries; return sbat_list;
}
/*
* Parse a list of SHA-1 certificate hexascii thumbprints.
* List must be freed by the caller.
*/
thumbprint_list_t* GetThumbprintEntries(char* thumbprints_txt)
{
uint32_t i, j, num_entries;
thumbprint_list_t* thumbprints;
if (thumbprints_txt == NULL)
return NULL;
num_entries = 1;
for (i = 0; thumbprints_txt[i] != '\0'; i++)
if (thumbprints_txt[i] == '\n')
num_entries++;
thumbprints = malloc(sizeof(thumbprint_list_t) + num_entries * SHA1_HASHSIZE);
if (thumbprints == NULL)
return NULL;
thumbprints->count = 0;
for (i = 0; thumbprints_txt[i] != '\0'; ) {
// Eliminate blank lines
if (thumbprints_txt[i] == '\n') {
i++;
continue;
}
// Eliminate lines that don't start by an hexadecimal digit
if (!IS_HEXASCII(thumbprints_txt[i])) {
while (thumbprints_txt[i] != '\n' && thumbprints_txt[i] != '\0')
i++;
continue;
}
for (j = 0; thumbprints_txt[i] != '\n' && thumbprints_txt[i] != '\0'; i++, j++) {
if (!IS_HEXASCII(thumbprints_txt[i]))
break;
if ((j / 2) >= SHA1_HASHSIZE)
break;
thumbprints->list[thumbprints->count][j / 2] = thumbprints->list[thumbprints->count][j / 2] << 4;
thumbprints->list[thumbprints->count][j / 2] |= FROM_HEXASCII(thumbprints_txt[i]);
if (j == 2 * SHA1_HASHSIZE - 1)
thumbprints->count++;
}
while (thumbprints_txt[i] != '\n' && thumbprints_txt[i] != '\0')
i++;
}
if (thumbprints->count == 0) {
free(thumbprints);
return NULL;
}
return thumbprints;
} }
/* /*

View file

@ -143,13 +143,14 @@ char embedded_sl_version_ext[2][32];
char ClusterSizeLabel[MAX_CLUSTER_SIZES][64]; char ClusterSizeLabel[MAX_CLUSTER_SIZES][64];
char msgbox[1024], msgbox_title[32], *ini_file = NULL, *image_path = NULL, *short_image_path; char msgbox[1024], msgbox_title[32], *ini_file = NULL, *image_path = NULL, *short_image_path;
char *archive_path = NULL, image_option_txt[128], *fido_url = NULL, *save_image_type = NULL; char *archive_path = NULL, image_option_txt[128], *fido_url = NULL, *save_image_type = NULL;
char* sbat_level_txt = NULL; char *sbat_level_txt = NULL, *sb_active_txt = NULL, *sb_revoked_txt = NULL;
StrArray BlockingProcessList, ImageList; StrArray BlockingProcessList, ImageList;
// Number of steps for each FS for FCC_STRUCTURE_PROGRESS // Number of steps for each FS for FCC_STRUCTURE_PROGRESS
const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10, 1, 1, 1, 1 }; const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10, 1, 1, 1, 1 };
const char* flash_type[BADLOCKS_PATTERN_TYPES] = { "SLC", "MLC", "TLC" }; const char* flash_type[BADLOCKS_PATTERN_TYPES] = { "SLC", "MLC", "TLC" };
RUFUS_DRIVE rufus_drive[MAX_DRIVES] = { 0 }; RUFUS_DRIVE rufus_drive[MAX_DRIVES] = { 0 };
sbat_entry_t* sbat_entries = NULL; sbat_entry_t* sbat_entries = NULL;
thumbprint_list_t *sb_active_certs = NULL, *sb_revoked_certs = NULL;
// TODO: Remember to update copyright year in stdlg's AboutCallback() WM_INITDIALOG, // TODO: Remember to update copyright year in stdlg's AboutCallback() WM_INITDIALOG,
// localization_data.sh and the .rc when the year changes! // localization_data.sh and the .rc when the year changes!
@ -1243,6 +1244,42 @@ out:
return ret; return ret;
} }
void GetBootladerInfo(void)
{
static const char* revocation_type[] = { "UEFI DBX", "Windows SSP", "Linux SBAT", "Windows SVN", "Cert DBX" };
int r;
BOOL sb_signed;
uint32_t i, len;
uint8_t* buf = NULL;
// Check UEFI bootloaders for revocation
if (!IS_EFI_BOOTABLE(img_report))
return;
assert(ARRAYSIZE(img_report.efi_boot_entry) > 0);
PrintStatus(0, MSG_351);
uprintf("UEFI bootloaders analysis:");
for (i = 0; i < ARRAYSIZE(img_report.efi_boot_entry) && img_report.efi_boot_entry[i].path[0] != 0; i++) {
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 Secure Boot info", img_report.efi_boot_entry[i].path);
continue;
}
sb_signed = IsSignedBySecureBootAuthority(buf, len);
if (sb_signed)
img_report.has_secureboot_bootloader |= 1;
uprintf(" • %s%s", img_report.efi_boot_entry[i].path, sb_signed ? "*" : "");
r = IsBootloaderRevoked(buf, len);
if (r > 0) {
assert(r <= ARRAYSIZE(revocation_type));
assert(r <= 7);
uprintf(" WARNING: '%s' has been revoked by %s", img_report.efi_boot_entry[i].path, revocation_type[r - 1]);
img_report.has_secureboot_bootloader |= 1 << r;
}
safe_free(buf);
}
}
// The scanning process can be blocking for message processing => use a thread // The scanning process can be blocking for message processing => use a thread
DWORD WINAPI ImageScanThread(LPVOID param) DWORD WINAPI ImageScanThread(LPVOID param)
{ {
@ -1348,6 +1385,7 @@ DWORD WINAPI ImageScanThread(LPVOID param)
} }
if (img_report.is_iso) { if (img_report.is_iso) {
GetBootladerInfo();
DisplayISOProps(); DisplayISOProps();
for (i = 0; i < ARRAYSIZE(redhat8_derivative); i++) { for (i = 0; i < ARRAYSIZE(redhat8_derivative); i++) {
@ -1431,15 +1469,12 @@ out:
ExitThread(0); ExitThread(0);
} }
#define MAP_BIT(bit) do { map[_log2(bit)] = b; b <<= 1; } while(0)
// Likewise, boot check will block message processing => use a thread // Likewise, boot check will block message processing => use a thread
static DWORD WINAPI BootCheckThread(LPVOID param) static DWORD WINAPI BootCheckThread(LPVOID param)
{ {
int i, r, rr, username_index = -1; int i, r, username_index = -1;
FILE *fd; FILE *fd;
uint32_t len; uint32_t len;
uint8_t* buf = NULL;
WPARAM ret = BOOTCHECK_CANCEL; WPARAM ret = BOOTCHECK_CANCEL;
BOOL in_files_dir = FALSE, esp_already_asked = FALSE; BOOL in_files_dir = FALSE, esp_already_asked = FALSE;
BOOL is_windows_to_go = ((image_options & IMOP_WINTOGO) && (ComboBox_GetCurItemData(hImageOption) == IMOP_WIN_TO_GO)); BOOL is_windows_to_go = ((image_options & IMOP_WINTOGO) && (ComboBox_GetCurItemData(hImageOption) == IMOP_WIN_TO_GO));
@ -1638,64 +1673,20 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
} }
} }
// Check UEFI bootloaders for revocation // Display an alert if any of the UEFI bootloaders have been revoked
if (IS_EFI_BOOTABLE(img_report)) { if (img_report.has_secureboot_bootloader & 0xfe) {
BOOL has_secureboot_signed_bootloader = FALSE; switch (img_report.has_secureboot_bootloader & 0xfe) {
assert(ARRAYSIZE(img_report.efi_boot_entry) > 0); case 4:
PrintStatus(0, MSG_351); msg = lmprintf(MSG_341, "Error code: 0xc0000428");
uuprintf("UEFI Secure Boot revocation checks:"); break;
// Make sure we have at least one regular EFI bootloader that is formally signed default:
// for Secure Boot, since it doesn't make sense to report revocation otherwise. msg = lmprintf(MSG_340);
for (i = 0; !has_secureboot_signed_bootloader && i < ARRAYSIZE(img_report.efi_boot_entry) && break;
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 (!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;
} }
if ((img_report.projected_size < MAX_ISO_TO_ESP_SIZE) && HAS_REGULAR_EFI(img_report) && if ((img_report.projected_size < MAX_ISO_TO_ESP_SIZE) && HAS_REGULAR_EFI(img_report) &&
@ -4211,6 +4202,10 @@ out:
safe_free(pe256ssp); safe_free(pe256ssp);
safe_free(sbat_entries); safe_free(sbat_entries);
safe_free(sbat_level_txt); safe_free(sbat_level_txt);
safe_free(sb_active_certs);
safe_free(sb_active_txt);
safe_free(sb_revoked_certs);
safe_free(sb_revoked_txt);
if (argv != NULL) { if (argv != NULL) {
for (i = 0; i < argc; i++) for (i = 0; i < argc; i++)
safe_free(argv[i]); safe_free(argv[i]);

View file

@ -436,6 +436,7 @@ typedef struct {
BOOLEAN rh8_derivative; BOOLEAN rh8_derivative;
uint16_t winpe; uint16_t winpe;
uint16_t has_efi; uint16_t has_efi;
uint8_t has_secureboot_bootloader;
uint8_t has_md5sum; uint8_t has_md5sum;
uint8_t wininst_index; uint8_t wininst_index;
uint8_t has_symlinks; uint8_t has_symlinks;
@ -519,12 +520,6 @@ typedef struct {
uint32_t* address; // 32-bit will do, as we're not dealing with >4GB DLLs... uint32_t* address; // 32-bit will do, as we're not dealing with >4GB DLLs...
} dll_resolver_t; } dll_resolver_t;
/* SBAT entry */
typedef struct {
char* product;
uint32_t version;
} sbat_entry_t;
/* Alignment macro */ /* Alignment macro */
#if defined(__GNUC__) #if defined(__GNUC__)
#define ALIGNED(m) __attribute__ ((__aligned__(m))) #define ALIGNED(m) __attribute__ ((__aligned__(m)))
@ -576,6 +571,18 @@ extern hash_init_t* hash_init[HASH_MAX];
extern hash_write_t* hash_write[HASH_MAX]; extern hash_write_t* hash_write[HASH_MAX];
extern hash_final_t* hash_final[HASH_MAX]; extern hash_final_t* hash_final[HASH_MAX];
/* SBAT entry */
typedef struct {
char* product;
uint32_t version;
} sbat_entry_t;
/* Certificate thumbprint list */
typedef struct {
uint32_t count;
uint8_t list[0][SHA1_HASHSIZE];
} thumbprint_list_t;
#ifndef __VA_GROUP__ #ifndef __VA_GROUP__
#define __VA_GROUP__(...) __VA_ARGS__ #define __VA_GROUP__(...) __VA_ARGS__
#endif #endif
@ -741,6 +748,7 @@ extern const int nb_steps[FS_MAX];
extern float fScale; extern float fScale;
extern windows_version_t WindowsVersion; extern windows_version_t WindowsVersion;
extern sbat_entry_t* sbat_entries; extern sbat_entry_t* sbat_entries;
extern thumbprint_list_t *sb_active_certs, *sb_revoked_certs;
extern int dialog_showing, force_update, fs_type, boot_type, partition_type, target_type; extern int dialog_showing, force_update, fs_type, boot_type, partition_type, target_type;
extern unsigned long syslinux_ldlinux_len[2]; extern unsigned long syslinux_ldlinux_len[2];
extern char ubuffer[UBUFFER_SIZE], embedded_sl_version_str[2][12]; extern char ubuffer[UBUFFER_SIZE], embedded_sl_version_str[2][12];
@ -886,6 +894,7 @@ extern HANDLE CreatePreallocatedFile(const char* lpFileName, DWORD dwDesiredAcce
DWORD dwFlagsAndAttributes, LONGLONG fileSize); DWORD dwFlagsAndAttributes, LONGLONG fileSize);
extern uint32_t ResolveDllAddress(dll_resolver_t* resolver); extern uint32_t ResolveDllAddress(dll_resolver_t* resolver);
extern sbat_entry_t* GetSbatEntries(char* sbatlevel); extern sbat_entry_t* GetSbatEntries(char* sbatlevel);
extern thumbprint_list_t* GetThumbprintEntries(char* thumbprints_txt);
extern uint16_t GetPeArch(uint8_t* buf); extern uint16_t GetPeArch(uint8_t* buf);
extern uint8_t* GetPeSection(uint8_t* buf, const char* name, uint32_t* len); extern uint8_t* GetPeSection(uint8_t* buf, const char* name, uint32_t* len);
extern uint8_t* GetPeSignatureData(uint8_t* buf); extern uint8_t* GetPeSignatureData(uint8_t* buf);

View file

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 232, 326 IDD_DIALOG DIALOGEX 12, 12, 232, 326
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 4.8.2249" CAPTION "Rufus 4.8.2250"
FONT 9, "Segoe UI Symbol", 400, 0, 0x0 FONT 9, "Segoe UI Symbol", 400, 0, 0x0
BEGIN BEGIN
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
@ -407,8 +407,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,8,2249,0 FILEVERSION 4,8,2250,0
PRODUCTVERSION 4,8,2249,0 PRODUCTVERSION 4,8,2250,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -426,13 +426,13 @@ BEGIN
VALUE "Comments", "https://rufus.ie" VALUE "Comments", "https://rufus.ie"
VALUE "CompanyName", "Akeo Consulting" VALUE "CompanyName", "Akeo Consulting"
VALUE "FileDescription", "Rufus" VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "4.8.2249" VALUE "FileVersion", "4.8.2250"
VALUE "InternalName", "Rufus" VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2025 Pete Batard (GPL v3)" VALUE "LegalCopyright", "© 2011-2025 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
VALUE "OriginalFilename", "rufus-4.8.exe" VALUE "OriginalFilename", "rufus-4.8.exe"
VALUE "ProductName", "Rufus" VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "4.8.2249" VALUE "ProductVersion", "4.8.2250"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -46,7 +46,7 @@
/* Globals */ /* Globals */
extern BOOL is_x86_64, appstore_version; extern BOOL is_x86_64, appstore_version;
extern char unattend_username[MAX_USERNAME_LENGTH], *sbat_level_txt; extern char unattend_username[MAX_USERNAME_LENGTH], *sbat_level_txt, *sb_active_txt, *sb_revoked_txt;
extern HICON hSmallIcon, hBigIcon; extern HICON hSmallIcon, hBigIcon;
static HICON hMessageIcon = (HICON)INVALID_HANDLE_VALUE; static HICON hMessageIcon = (HICON)INVALID_HANDLE_VALUE;
static char* szMessageText = NULL; static char* szMessageText = NULL;
@ -1404,18 +1404,38 @@ static DWORD WINAPI CheckForFidoThread(LPVOID param)
safe_free(fido_url); safe_free(fido_url);
safe_free(sbat_entries); safe_free(sbat_entries);
safe_free(sbat_level_txt); safe_free(sbat_level_txt);
safe_free(sb_active_txt);
safe_free(sb_revoked_txt);
// Get the latest sbat_level.txt data while we're poking the network for Fido. // Get the latest sbat_level.txt data while we're poking the network for Fido.
len = DownloadToFileOrBuffer(RUFUS_URL "/sbat_level.txt", NULL, (BYTE**)&sbat_level_txt, NULL, FALSE); len = DownloadToFileOrBuffer(RUFUS_URL "/sbat_level.txt", NULL, (BYTE**)&sbat_level_txt, NULL, FALSE);
if (len != 0 && len < 512) { if (len != 0 && len < 1 * KB) {
sbat_entries = GetSbatEntries(sbat_level_txt); sbat_entries = GetSbatEntries(sbat_level_txt);
if (sbat_entries != 0) { if (sbat_entries != NULL) {
for (i = 0; sbat_entries[i].product != NULL; i++); for (i = 0; sbat_entries[i].product != NULL; i++);
if (i > 0) if (i > 0)
uprintf("Found %d additional UEFI revocation filters from remote SBAT", i); uprintf("Found %d additional UEFI revocation filters from remote SBAT", i);
} }
} }
// Get the active Secure Boot certificate thumbprints
len = DownloadToFileOrBuffer(RUFUS_URL "/sb_active.txt", NULL, (BYTE**)&sb_active_txt, NULL, FALSE);
if (len != 0 && len < 1 * KB) {
sb_active_certs = GetThumbprintEntries(sb_active_txt);
if (sb_active_certs != NULL) {
uprintf("Found %d active Secure Boot certificate entries from remote", sb_active_certs->count);
}
}
// Get the revoked Secure Boot certificate thumbprints
len = DownloadToFileOrBuffer(RUFUS_URL "/sb_revoked.txt", NULL, (BYTE**)&sb_revoked_txt, NULL, FALSE);
if (len != 0 && len < 1 * KB) {
sb_revoked_certs = GetThumbprintEntries(sb_revoked_txt);
if (sb_revoked_certs != NULL) {
uprintf("Found %d revoked Secure Boot certificate entries from remote", sb_revoked_certs->count);
}
}
// Get the Fido URL from parsing a 'Fido.ver' on our server. This enables the use of different // Get the Fido URL from parsing a 'Fido.ver' on our server. This enables the use of different
// Fido versions from different versions of Rufus, if needed, as opposed to always downloading // Fido versions from different versions of Rufus, if needed, as opposed to always downloading
// the latest release from GitHub, which may contain incompatible changes... // the latest release from GitHub, which may contain incompatible changes...