mirror of
https://github.com/pbatard/rufus.git
synced 2025-06-08 10:22:30 -04:00
[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:
parent
207a330979
commit
688f011f31
12 changed files with 325 additions and 146 deletions
|
@ -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
|
||||||
|
|
16
src/db.h
16
src/db.h
|
@ -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
|
||||||
|
|
24
src/drive.c
24
src/drive.c
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
102
src/hash.c
102
src/hash.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
48
src/iso.c
48
src/iso.c
|
@ -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++) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
81
src/parser.c
81
src/parser.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
119
src/rufus.c
119
src/rufus.c
|
@ -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]);
|
||||||
|
|
21
src/rufus.h
21
src/rufus.h
|
@ -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);
|
||||||
|
|
10
src/rufus.rc
10
src/rufus.rc
|
@ -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"
|
||||||
|
|
26
src/stdlg.c
26
src/stdlg.c
|
@ -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...
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue