[uefi] add parsing and copying of the system's SkuSiPolicy.p7b

* Instead of embedding the content of the most recent revoked bootloader hashes in db.h
  we now parse the system's SkuSiPolicy.p7b to do so. This has the drawback of not alerting
  users running Rufus on systems where SkuSiPolicy.p7b is not up to date, but I believe the
  trade-off is worth it.
* We now also copy the system's SkuSiPolicy.p7b to the created media when possible (for
  Windows 10 or later), so that Microsoft's WDAC UEFI revocations can apply during boot.
This commit is contained in:
Pete Batard 2023-06-18 19:07:45 +02:00
parent 43764268de
commit be5b590cfb
No known key found for this signature in database
GPG Key ID: 38E0CF5E69EDD671
8 changed files with 207 additions and 2396 deletions

View File

@ -13,7 +13,9 @@
[cmdletbinding()]
param(
# (Optional) The path to the .p7b to process
[string]$BinaryFilePath = "SkuSiPolicyp.p7b"
[string]$BinaryFilePath = "SkuSiPolicy.p7b",
# (Optional) Output the straight values
[switch]$Raw = $false
)
#endregion
@ -55,7 +57,7 @@ try {
$ContentType = $null
try {
$ContentType = [Security.Cryptography.Pkcs.ContentInfo]::GetContentType($CIPolicyBytes)
} catch { Write-Host "WTF!" }
} catch { }
# Check for PKCS#7 ASN.1 SignedData type
if ($ContentType -and $ContentType.Value -eq '1.2.840.113549.1.7.2') {
@ -183,14 +185,17 @@ try {
# Sort the array and remove duplicates
$HashArray.Sort()
$HashArray = $HashArray | Select-Object -Unique
# Output as C array data
foreach ($HashStr in $HashArray) {
$HashChars = $HashStr.ToCharArray()
$Line = "`t"
for ($i = 0; $i -lt $Pe256HashLength; $i++) {
$Line += "0x" + $HashChars[2 * $i] + $HashChars[2 * $i + 1] + ", "
if ($Raw) {
Write-Output $HashStr
} else {
$HashChars = $HashStr.ToCharArray()
$Line = "`t"
for ($i = 0; $i -lt $Pe256HashLength; $i++) {
$Line += "0x" + $HashChars[2 * $i] + $HashChars[2 * $i + 1] + ", "
}
Write-Output $Line
}
Write-Output $Line
}
}

2358
src/db.h

File diff suppressed because it is too large Load Diff

View File

@ -1908,6 +1908,18 @@ DWORD WINAPI FormatThread(void* param)
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH);
}
}
} else if ((target_type == TT_UEFI) && IS_WINDOWS_1X(img_report) && pe256ssp_size > 0) {
// Copy this system's SkuSiPolicy.p7b to the target drive so that UEFI bootloaders
// revoked by Windows through WDAC policy do get flagged as revoked.
char src[MAX_PATH], dst[MAX_PATH];
struct __stat64 stat64 = { 0 };
static_sprintf(src, "%s\\SecureBootUpdates\\SKUSiPolicy.p7b", system_dir);
static_sprintf(dst, "%s\\efi\\microsoft\\boot\\SKUSiPolicy.p7b", drive_name);
if ((_stat64U(dst, &stat64) != 0) && (_stat64U(src, &stat64) == 0)) {
uprintf("Copying: %s (%s) (from %s)", dst, SizeToHumanReadable(stat64.st_size, FALSE, FALSE), src);
if (!CopyFileU(src, dst, TRUE))
uprintf(" Error writing file: %s", WindowsErrorString());
}
}
if ( (target_type == TT_BIOS) && HAS_WINPE(img_report) ) {
// Apply WinPE fixup

View File

@ -123,6 +123,8 @@ DWORD read_size[NUM_BUFFERS];
BOOL enable_extra_hashes = FALSE;
uint8_t ALIGNED(64) buffer[NUM_BUFFERS][BUFFER_SIZE];
extern int default_thread_priority;
uint32_t pe256ssp_size = 0;
uint8_t* pe256ssp = NULL;
/*
* Rotate 32 or 64 bit integers by n bytes.
@ -2117,14 +2119,14 @@ BOOL IsFileInDB(const char* path)
int IsUefiBootloaderRevoked(const char* path)
{
int i;
uint32_t i;
uint8_t hash[SHA256_HASHSIZE];
if (!PE256File(path, hash))
return -1;
for (i = 0; i < ARRAYSIZE(pe256dbx); i += SHA256_HASHSIZE)
if (memcmp(hash, &pe256dbx[i], SHA256_HASHSIZE) == 0)
return 1;
for (i = 0; i < ARRAYSIZE(pe256ssp); i += SHA256_HASHSIZE)
for (i = 0; i < pe256ssp_size * SHA256_HASHSIZE; i += SHA256_HASHSIZE)
if (memcmp(hash, &pe256ssp[i], SHA256_HASHSIZE) == 0)
return 2;
return 0;

165
src/pki.c
View File

@ -48,18 +48,62 @@ const char* cert_name[3] = { "Akeo Consulting", "Akeo Systems", "Pete Batard" };
const char* cert_country = "IE";
typedef struct {
LPWSTR lpszProgramName;
LPWSTR lpszPublisherLink;
LPWSTR lpszMoreInfoLink;
LPWSTR lpszProgramName;
LPWSTR lpszPublisherLink;
LPWSTR lpszMoreInfoLink;
} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;
// https://msdn.microsoft.com/en-us/library/ee442238.aspx
typedef struct {
BLOBHEADER BlobHeader;
RSAPUBKEY RsaHeader;
BYTE Modulus[256]; // 2048 bit modulus
BLOBHEADER BlobHeader;
RSAPUBKEY RsaHeader;
BYTE Modulus[256]; // 2048 bit modulus
} RSA_2048_PUBKEY;
// For PKCS7 WDAC Code Integrity processing
#define PE256_HASHSIZE 32
const GUID SKU_CODE_INTEGRITY_POLICY = { 0x976d12c8, 0xcb9f, 0x4730, { 0xbe, 0x52, 0x54, 0x60, 0x08, 0x43, 0x23, 0x8e} };
typedef struct {
WORD Nano;
WORD Micro;
WORD Minor;
WORD Major;
} CIVersion;
typedef struct {
DWORD PolicyFormatVersion;
GUID PolicyTypeGUID;
GUID PlatformGUID;
DWORD OptionFlags;
DWORD EKURuleEntryCount;
DWORD FileRuleEntryCount;
DWORD SignerRuleEntryCount;
DWORD SignerScenarioEntryCount;
CIVersion PolicyVersion;
DWORD HeaderLength;
} CIHeader;
typedef struct {
DWORD Type;
DWORD FileNameLength;
WCHAR FileName[0];
} CIFileRuleHeader;
typedef struct {
DWORD Unknown;
CIVersion Version;
DWORD HashLength;
BYTE Hash[0];
} CIFileRuleData;
typedef enum {
CI_DENY = 0,
CI_ALLOW,
CI_FILE_ATTRIBUTES,
};
// The RSA public key modulus for the private key we use to sign the files on the server.
// NOTE 1: This openssl modulus must be *REVERSED* to be usable with Microsoft APIs
// NOTE 2: Also, this modulus is 2052 bits, and not 2048, because openssl adds an extra
@ -747,3 +791,112 @@ out:
CryptReleaseContext(hProv, 0);
return r;
}
BOOL ParseSKUSiPolicy(void)
{
char path[MAX_PATH];
wchar_t* wpath = NULL;
BOOL r = FALSE;
DWORD i, dwEncoding, dwContentType, dwFormatType;
DWORD dwPolicySize = 0, dwBaseIndex = 0, dwSizeCount;
HCRYPTMSG hMsg = NULL;
CRYPT_DATA_BLOB pkcsData = { 0 };
DWORD* pdwEkuRules;
BYTE* pbRule;
CIHeader* Header;
CIFileRuleHeader* FileRuleHeader;
CIFileRuleData* FileRuleData;
pe256ssp_size = 0;
safe_free(pe256ssp);
static_sprintf(path, "%s\\SecureBootUpdates\\SKUSiPolicy.p7b", system_dir);
wpath = utf8_to_wchar(path);
if (wpath == NULL)
goto out;
r = CryptQueryObject(CERT_QUERY_OBJECT_FILE, wpath, CERT_QUERY_CONTENT_FLAG_ALL,
CERT_QUERY_FORMAT_FLAG_ALL, 0, &dwEncoding, &dwContentType, &dwFormatType, NULL,
&hMsg, NULL);
if (!r || dwContentType != CERT_QUERY_CONTENT_PKCS7_SIGNED)
goto out;
r = CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, NULL, &pkcsData.cbData);
if (!r || pkcsData.cbData == 0) {
uprintf("ParseSKUSiPolicy: Failed to retreive CMSG_CONTENT_PARAM size: %s", WindowsErrorString());
goto out;
}
pkcsData.pbData = malloc(pkcsData.cbData);
if (pkcsData.pbData == NULL)
goto out;
r = CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, pkcsData.pbData, &pkcsData.cbData);
if (!r) {
uprintf("ParseSKUSiPolicy: Failed to retreive CMSG_CONTENT_PARAM: %s", WindowsErrorString());
goto out;
}
// Now process the actual Security Policy content
if (pkcsData.pbData[0] == 4) {
dwPolicySize = pkcsData.pbData[1];
dwBaseIndex = 2;
if ((dwPolicySize & 0x80) == 0x80) {
dwSizeCount = dwPolicySize & 0x7F;
dwBaseIndex += dwSizeCount;
dwPolicySize = 0;
for (i = 0; i < dwSizeCount; i++) {
dwPolicySize = dwPolicySize << 8;
dwPolicySize = dwPolicySize | pkcsData.pbData[2 + i];
}
}
}
// Sanity checks
Header = (CIHeader*)&pkcsData.pbData[dwBaseIndex];
if (Header->HeaderLength + sizeof(uint32_t) != sizeof(CIHeader)) {
uprintf("ParseSKUSiPolicy: Unexpected Code Integrity Header size (0x%02x)", Header->HeaderLength);
goto out;
}
if (!CompareGUID(&Header->PolicyTypeGUID, &SKU_CODE_INTEGRITY_POLICY)) {
uprintf("ParseSKUSiPolicy: Unexpected Policy Type GUID %s", GuidToString(&Header->PolicyTypeGUID));
goto out;
}
// Skip the EKU Rules
pdwEkuRules = (DWORD*) &pkcsData.pbData[dwBaseIndex + sizeof(CIHeader)];
for (i = 0; i < Header->EKURuleEntryCount; i++)
pdwEkuRules = &pdwEkuRules[(*pdwEkuRules + (2 * sizeof(DWORD) - 1)) / sizeof(DWORD)];
// Process the Files Rules
pbRule = (BYTE*)pdwEkuRules;
pe256ssp = malloc(Header->FileRuleEntryCount * PE256_HASHSIZE);
if (pe256ssp == NULL)
goto out;
for (i = 0; i < Header->FileRuleEntryCount; i++) {
FileRuleHeader = (CIFileRuleHeader*)pbRule;
pbRule = &pbRule[sizeof(CIFileRuleHeader)];
if (FileRuleHeader->FileNameLength != 0) {
// uprintf("%S", FileRuleHeader->FileName);
pbRule = &pbRule[((FileRuleHeader->FileNameLength + sizeof(DWORD) - 1) / sizeof(DWORD)) * sizeof(DWORD)];
}
FileRuleData = (CIFileRuleData*)pbRule;
if (FileRuleData->HashLength > 0x80) {
uprintf("ParseSKUSiPolicy: Unexpected hash length for entry %d (0x%02x)", i, FileRuleData->HashLength);
// We're probably screwed, so bail out
break;
}
// We are only interested in 'DENY' type with PE256 hashes
if (FileRuleHeader->Type == CI_DENY && FileRuleData->HashLength == PE256_HASHSIZE) {
memcpy(&pe256ssp[pe256ssp_size * PE256_HASHSIZE], FileRuleData->Hash, PE256_HASHSIZE);
pe256ssp_size++;
}
pbRule = &pbRule[sizeof(CIFileRuleData) + ((FileRuleData->HashLength + sizeof(DWORD) - 1) / sizeof(DWORD)) * sizeof(DWORD)];
}
r = TRUE;
out:
if (hMsg != NULL)
CryptMsgClose(hMsg);
free(pkcsData.pbData);
free(wpath);
return r;
}

View File

@ -2061,6 +2061,10 @@ static void InitDialog(HWND hDlg)
"one. Because of this, some messages will only be displayed in English.", selected_locale->txt[1]);
uprintf("If you think you can help update this translation, please e-mail the author of this application");
}
if (ParseSKUSiPolicy())
uprintf("Found %d revoked UEFI bootloaders from this system's SKUSiPolicy", pe256ssp_size);
else
uprintf("WARNING: Could not parse this system's SkuSiPolicy");
// Detect and report system limitations
if (ReadRegistryKeyBool(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Policies\\Microsoft\\FVE"))
uprintf("WARNING: This system has a policy set to prevent write access to FIXED drives not using BitLocker");
@ -3885,17 +3889,7 @@ extern int TestHashes(void);
// Ctrl-T => Alternate Test mode that doesn't require a full rebuild
if ((ctrl_without_focus || ((GetKeyState(VK_CONTROL) & 0x8000) && (msg.message == WM_KEYDOWN)))
&& (msg.wParam == 'T')) {
uint8_t sum[32] = { 0 };
PE256File("C:\\Projects\\rufus\\winload_win10_1511.exe", sum);
DumpBufferHex(sum, 32);
int aya = IsUefiBootloaderRevoked("C:\\Projects\\WDACTools\\en_windows_10_multiple_editions_version_1607_updated_jan_2017_x64_dvd_9714399.iso\\efi\\boot\\bootx64.efi");
if (aya > 0) {
MessageBoxExU(hMainDialog, lmprintf(MSG_339,
(aya == 1) ? lmprintf(MSG_340) : lmprintf(MSG_341, "Error code: 0xc0000428")),
lmprintf(MSG_338), MB_ICONWARNING | MB_IS_RTL, selected_langid);
}
// TestHashes();
TestHashes();
continue;
}
#endif
@ -4236,6 +4230,7 @@ out:
safe_free(grub2_buf);
safe_free(fido_url);
safe_free(fido_script);
safe_free(pe256ssp);
if (argv != NULL) {
for (i=0; i<argc; i++) safe_free(argv[i]);
safe_free(argv);

View File

@ -577,16 +577,17 @@ extern RUFUS_UPDATE update;
extern RUFUS_IMG_REPORT img_report;
extern HINSTANCE hMainInstance;
extern HWND hMainDialog, hLogDialog, hStatus, hDeviceList, hCapacity, hImageOption;
extern HWND hPartitionScheme, hTargetSystem, hFileSystem, hClusterSize, hLabel, hBootType, hNBPasses, hLog;
extern HWND hInfo, hProgress, hDiskID;
extern HWND hPartitionScheme, hTargetSystem, hFileSystem, hClusterSize, hLabel, hBootType;
extern HWND hNBPasses, hLog, hInfo, hProgress, hDiskID;
extern WORD selected_langid;
extern DWORD FormatStatus, DownloadStatus, MainThreadId, LastWriteError;
extern BOOL use_own_c32[NB_OLD_C32], detect_fakes, op_in_progress, right_to_left_mode;
extern BOOL allow_dual_uefi_bios, large_drive, usb_debug;
extern int64_t iso_blocking_status;
extern uint8_t image_options;
extern uint8_t image_options, *pe256ssp;
extern uint16_t rufus_version[3], embedded_sl_version[2];
extern uint32_t pe256ssp_size;
extern uint64_t persistence_size;
extern int64_t iso_blocking_status;
extern size_t ubuffer_pos;
extern const int nb_steps[FS_MAX];
extern float fScale;
@ -594,8 +595,8 @@ extern windows_version_t WindowsVersion;
extern int dialog_showing, force_update, fs_type, boot_type, partition_type, target_type;
extern unsigned long syslinux_ldlinux_len[2];
extern char ubuffer[UBUFFER_SIZE], embedded_sl_version_str[2][12];
extern char szFolderPath[MAX_PATH], app_dir[MAX_PATH], temp_dir[MAX_PATH], system_dir[MAX_PATH], sysnative_dir[MAX_PATH];
extern char app_data_dir[MAX_PATH], *image_path, *fido_url;
extern char szFolderPath[MAX_PATH], app_dir[MAX_PATH], temp_dir[MAX_PATH], system_dir[MAX_PATH];
extern char sysnative_dir[MAX_PATH], app_data_dir[MAX_PATH], *image_path, *fido_url;
/*
* Shared prototypes
@ -690,6 +691,7 @@ extern char* GetSignatureName(const char* path, const char* country_code, BOOL b
extern uint64_t GetSignatureTimeStamp(const char* path);
extern LONG ValidateSignature(HWND hDlg, const char* path);
extern BOOL ValidateOpensslSignature(BYTE* pbBuffer, DWORD dwBufferLen, BYTE* pbSignature, DWORD dwSigLen);
extern BOOL ParseSKUSiPolicy(void);
extern BOOL IsFontAvailable(const char* font_name);
extern BOOL WriteFileWithRetry(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten, DWORD nNumRetries);

View File

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