diff --git a/_sign.cmd b/_sign.cmd index 1207bc91..73f01ed2 100644 --- a/_sign.cmd +++ b/_sign.cmd @@ -1,3 +1,3 @@ @echo off -"C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64\signtool" sign /v /sha1 9ce9a71ccab3b38a74781b975f1c228222cf7d3b /fd SHA256 /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp %1 +"C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool" sign /v /sha1 9ce9a71ccab3b38a74781b975f1c228222cf7d3b /fd SHA256 /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp %1 exit diff --git a/res/appstore/Package.appxmanifest b/res/appstore/Package.appxmanifest index 650152ef..9b54f8e2 100644 --- a/res/appstore/Package.appxmanifest +++ b/res/appstore/Package.appxmanifest @@ -11,7 +11,7 @@ + Version="3.14.1794.0" /> Rufus diff --git a/src/net.c b/src/net.c index 97f9b41e..1e1597c0 100644 --- a/src/net.c +++ b/src/net.c @@ -52,7 +52,7 @@ HANDLE update_check_thread = NULL; extern loc_cmd* selected_locale; extern HANDLE dialog_handle; -extern BOOL is_x86_32, close_fido_cookie_prompts; +extern BOOL is_x86_32; static DWORD error_code, fido_len = 0; static BOOL force_update_check = FALSE; static const char* request_headers = "Accept-Encoding: gzip, deflate"; @@ -918,7 +918,7 @@ static DWORD WINAPI DownloadISOThread(LPVOID param) static_sprintf(sig_url, "%s.sig", fido_url); dwSize = (DWORD)DownloadToFileOrBuffer(sig_url, NULL, &sig, NULL, FALSE); if ((dwSize != RSA_SIGNATURE_SIZE) || (!ValidateOpensslSignature(compressed, dwCompressedSize, sig, dwSize))) { - uprintf("FATAL: Signature is invalid ✗"); + uprintf("FATAL: Download signature is invalid ✗"); FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_BAD_SIGNATURE); SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_ERROR, 0); SetTaskbarProgressState(TASKBAR_ERROR); @@ -927,7 +927,7 @@ static DWORD WINAPI DownloadISOThread(LPVOID param) goto out; } free(sig); - uprintf("Signature is valid ✓"); + uprintf("Download signature is valid ✓"); uncompressed_size = *((uint64_t*)&compressed[5]); if ((uncompressed_size < 1 * MB) && (bled_init(_uprintf, NULL, NULL, NULL, NULL, &FormatStatus) >= 0)) { fido_script = malloc((size_t)uncompressed_size); @@ -983,13 +983,22 @@ static DWORD WINAPI DownloadISOThread(LPVOID param) } static_sprintf(cmdline, "\"%s\" -NonInteractive -Sta -NoProfile –ExecutionPolicy Bypass " - "-File \"%s\" -DisableFirstRunCustomize -PipeName %s -LocData \"%s\" -Icon \"%s\" -AppTitle \"%s\"", + "-File \"%s\" -PipeName %s -LocData \"%s\" -Icon \"%s\" -AppTitle \"%s\"", powershell_path, script_path, &pipe[9], locale_str, icon_path, lmprintf(MSG_149)); - // Signal our Windows alert hook that it should close the IE cookie prompts from Fido - close_fido_cookie_prompts = TRUE; + + // For extra security, even after we validated that the LZMA download is properly + // signed, we also validate the Authenticode signature of the local script. + if (ValidateSignature(INVALID_HANDLE_VALUE, script_path) != NO_ERROR) { + uprintf("FATAL: Script signature is invalid ✗"); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_BAD_SIGNATURE); + SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_ERROR, 0); + SetTaskbarProgressState(TASKBAR_ERROR); + goto out; + } + uprintf("Script signature is valid ✓"); + dwExitCode = RunCommand(cmdline, app_dir, TRUE); uprintf("Exited download script with code: %d", dwExitCode); - close_fido_cookie_prompts = FALSE; if ((dwExitCode == 0) && PeekNamedPipe(hPipe, NULL, dwPipeSize, NULL, &dwAvail, NULL) && (dwAvail != 0)) { url = malloc(dwAvail + 1); dwSize = 0; diff --git a/src/pki.c b/src/pki.c index a94a15fb..251d6cb1 100644 --- a/src/pki.c +++ b/src/pki.c @@ -192,12 +192,24 @@ const char* WinPKIErrorString(void) return "None of the signers of the cryptographic message or certificate trust list is trusted."; case CERT_E_UNTRUSTEDROOT: return "The root certificate is not trusted."; + case TRUST_E_SYSTEM_ERROR: + return "A system-level error occurred while verifying trust."; + case TRUST_E_NO_SIGNER_CERT: + return "The certificate for the signer of the message is invalid or not found."; + case TRUST_E_COUNTER_SIGNER: + return "One of the counter signatures was invalid."; + case TRUST_E_CERT_SIGNATURE: + return "The signature of the certificate cannot be verified."; + case TRUST_E_TIME_STAMP: + return "The timestamp could not be verified."; + case TRUST_E_BAD_DIGEST: + return "The file content has been altered."; + case TRUST_E_BASIC_CONSTRAINTS: + return "A certificate's basic constraint extension has not been observed."; case TRUST_E_NOSIGNATURE: return "Not digitally signed."; case TRUST_E_EXPLICIT_DISTRUST: return "One of the certificates used was marked as untrusted by the user."; - case TRUST_E_TIME_STAMP: - return "The timestamp could not be verified."; default: static_sprintf(error_string, "Unknown PKI error 0x%08lX", error_code); return error_string; @@ -205,7 +217,7 @@ const char* WinPKIErrorString(void) } // Mostly from https://support.microsoft.com/en-us/kb/323809 -char* GetSignatureName(const char* path, const char* country_code) +char* GetSignatureName(const char* path, const char* country_code, BOOL bSilent) { static char szSubjectName[128]; char szCountry[3] = "__"; @@ -308,9 +320,9 @@ char* GetSignatureName(const char* path, const char* country_code) } if (szCountry[0] == '_') - uprintf("Binary executable is signed by '%s'", szSubjectName); + suprintf("Binary executable is signed by '%s'", szSubjectName); else - uprintf("Binary executable is signed by '%s' (%s)", szSubjectName, szCountry); + suprintf("Binary executable is signed by '%s' (%s)", szSubjectName, szCountry); p = szSubjectName; out: @@ -572,10 +584,11 @@ LONG ValidateSignature(HWND hDlg, const char* path) // Check the signature name. Make it specific enough (i.e. don't simply check for "Akeo") // so that, besides hacking our server, it'll place an extra hurdle on any malicious entity // into also fooling a C.A. to issue a certificate that passes our test. - signature_name = GetSignatureName(path, cert_country); + signature_name = GetSignatureName(path, cert_country, (hDlg == INVALID_HANDLE_VALUE)); if (signature_name == NULL) { uprintf("PKI: Could not get signature name"); - MessageBoxExU(hDlg, lmprintf(MSG_284), lmprintf(MSG_283), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); + if (hDlg != INVALID_HANDLE_VALUE) + MessageBoxExU(hDlg, lmprintf(MSG_284), lmprintf(MSG_283), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); return TRUST_E_NOSIGNATURE; } for (i = 0; i < ARRAYSIZE(cert_name); i++) { @@ -584,8 +597,9 @@ LONG ValidateSignature(HWND hDlg, const char* path) } if (i >= ARRAYSIZE(cert_name)) { uprintf("PKI: Signature '%s' is unexpected...", signature_name); - if (MessageBoxExU(hDlg, lmprintf(MSG_285, signature_name), lmprintf(MSG_283), - MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid) != IDYES) + if ((hDlg == INVALID_HANDLE_VALUE) || (MessageBoxExU(hDlg, + lmprintf(MSG_285, signature_name), lmprintf(MSG_283), + MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid) != IDYES)) return TRUST_E_EXPLICIT_DISTRUST; } @@ -615,6 +629,9 @@ LONG ValidateSignature(HWND hDlg, const char* path) safe_free(trust_file.pcwszFilePath); switch (r) { case ERROR_SUCCESS: + // hDlg = INVALID_HANDLE_VALUE is used when validating the Fido PS1 script + if (hDlg == INVALID_HANDLE_VALUE) + break; // Verify that the timestamp of the downloaded update is in the future of our current one. // This is done to prevent the use of an officially signed, but older binary, as potential attack vector. current_ts = GetSignatureTimeStamp(NULL); @@ -634,11 +651,13 @@ LONG ValidateSignature(HWND hDlg, const char* path) case TRUST_E_NOSIGNATURE: // Should already have been reported, but since we have a custom message for it... uprintf("PKI: File does not appear to be signed: %s", WinPKIErrorString()); - MessageBoxExU(hDlg, lmprintf(MSG_284), lmprintf(MSG_283), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); + if (hDlg != INVALID_HANDLE_VALUE) + MessageBoxExU(hDlg, lmprintf(MSG_284), lmprintf(MSG_283), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); break; default: uprintf("PKI: Failed to validate signature: %s", WinPKIErrorString()); - MessageBoxExU(hDlg, lmprintf(MSG_240), lmprintf(MSG_283), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); + if (hDlg != INVALID_HANDLE_VALUE) + MessageBoxExU(hDlg, lmprintf(MSG_240), lmprintf(MSG_283), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); break; } diff --git a/src/rufus.c b/src/rufus.c index d8a20c14..4682c3c9 100755 --- a/src/rufus.c +++ b/src/rufus.c @@ -3335,7 +3335,7 @@ skip_args_processing: static_sprintf(ini_path, "%s\\rufus.ini", app_dir); fd = fopenU(ini_path, ini_flags); // Will create the file if portable mode is requested // Using the string directly in safe_strcmp() would call GetSignatureName() twice - tmp = GetSignatureName(NULL, NULL); + tmp = GetSignatureName(NULL, NULL, FALSE); vc |= (safe_strcmp(tmp, cert_name[0]) == 0); if (fd != NULL) { ini_file = ini_path; diff --git a/src/rufus.h b/src/rufus.h index 8555e25f..80e60d4c 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -572,7 +572,7 @@ extern uint8_t IsBootableImage(const char* path); extern BOOL AppendVHDFooter(const char* vhd_path); extern int SetWinToGoIndex(void); extern int IsHDD(DWORD DriveIndex, uint16_t vid, uint16_t pid, const char* strid); -extern char* GetSignatureName(const char* path, const char* country_code); +extern char* GetSignatureName(const char* path, const char* country_code, BOOL bSilent); 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); diff --git a/src/rufus.rc b/src/rufus.rc index 16628880..f1f56cb6 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 3.14.1793" +CAPTION "Rufus 3.14.1794" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -395,8 +395,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,14,1793,0 - PRODUCTVERSION 3,14,1793,0 + FILEVERSION 3,14,1794,0 + PRODUCTVERSION 3,14,1794,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -414,13 +414,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.14.1793" + VALUE "FileVersion", "3.14.1794" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2021 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-3.14.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.14.1793" + VALUE "ProductVersion", "3.14.1794" END END BLOCK "VarFileInfo" diff --git a/src/stdlg.c b/src/stdlg.c index 5fe5c4fc..9769a599 100644 --- a/src/stdlg.c +++ b/src/stdlg.c @@ -59,9 +59,8 @@ static const notification_info* notification_more_info; static const char* notification_dont_display_setting; static WNDPROC update_original_proc = NULL; static HWINEVENTHOOK ap_weh = NULL; -static char title_str[3][128], button_str[128]; +static char title_str[2][128], button_str[128]; HWND hFidoDlg = NULL; -BOOL close_fido_cookie_prompts = FALSE; static int update_settings_reposition_ids[] = { IDI_ICON, @@ -1989,14 +1988,13 @@ INT_PTR MyDialogBox(HINSTANCE hInstance, int Dialog_ID, HWND hWndParent, DLGPROC /* * The following function calls are used to automatically detect and close the native - * Windows format prompt "You must format the disk in drive X:" as well as the cookies - * alert being popped by Windows when running our Download script. To do that, we use + * Windows format prompt "You must format the disk in drive X:". To do that, we use * an event hook that gets triggered whenever a window is placed in the foreground. * In that hook, we look for a dialog that has style WS_POPUPWINDOW and has the relevant - * title. However, in case of the Format prompt, because the title in itself is too - * generic (the expectation is that it will be "Microsoft Windows") we also enumerate - * all the child controls from that prompt, using another callback, until we find one - * that contains the text we expect for the "Format disk" button. + * title. However, because the title in itself is too generic (the expectation is that + * it will be "Microsoft Windows") we also enumerate all the child controls from that + * prompt, using another callback, until we find one that contains the text we expect + * for the "Format disk" button. * Oh, and since all of these strings are localized, we must first pick them up from * the relevant mui's. */ @@ -2028,9 +2026,7 @@ static void CALLBACK AlertPromptHook(HWINEVENTHOOK hWinEventHook, DWORD Event, H SendMessage(hWnd, WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0); uprintf("Closed Windows format prompt"); } - } else if (close_fido_cookie_prompts && strcmp(str, title_str[1]) == 0) { - SendMessage(hWnd, WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0); - } else if ((strcmp(str, title_str[2]) == 0) && (hWnd != hFidoDlg)) { + } else if ((strcmp(str, title_str[1]) == 0) && (hWnd != hFidoDlg)) { // A wild Fido dialog appeared! => Keep track of its handle and center it hFidoDlg = hWnd; CenterDialog(hWnd, hMainDialog); @@ -2062,17 +2058,7 @@ void SetAlertPromptMessages(void) } FreeLibrary(mui_lib); } - static_sprintf(mui_path, "%s\\%s\\urlmon.dll.mui", sysnative_dir, GetCurrentMUI()); - mui_lib = LoadLibraryExU(mui_path, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); - if (mui_lib != NULL) { - // 2070 = "Windows Security Warning" (yes, that's what MS uses for a stupid cookie!) - if (LoadStringU(mui_lib, 2070, title_str[1], sizeof(title_str[1])) <= 0) { - static_strcpy(title_str[1], "Windows Security Warning"); - uprintf("Warning: Could not locate localized cookie prompt title string in '%s': %s", mui_path, WindowsErrorString()); - } - FreeLibrary(mui_lib); - } - static_strcpy(title_str[2], lmprintf(MSG_149)); + static_strcpy(title_str[1], lmprintf(MSG_149)); } BOOL SetAlertPromptHook(void)