mirror of
https://github.com/pbatard/rufus.git
synced 2024-08-14 23:57:05 +00:00
[pki] add RSA-2048 signature validation for all server downloads
* Closes #1172 * Also fix a MinGW warning in badblocks.c
This commit is contained in:
parent
2d262df8f3
commit
fdfc9ff82d
8 changed files with 331 additions and 85 deletions
|
@ -430,7 +430,7 @@ static unsigned int test_rw(HANDLE hDrive, blk_t last_block, size_t block_size,
|
||||||
int i, pat_idx;
|
int i, pat_idx;
|
||||||
unsigned int bb_count = 0;
|
unsigned int bb_count = 0;
|
||||||
blk_t got, tryout, recover_block = ~0, *blk_id;
|
blk_t got, tryout, recover_block = ~0, *blk_id;
|
||||||
size_t id_offset;
|
size_t id_offset = 0;
|
||||||
|
|
||||||
if ((pattern_type < 0) || (pattern_type >= BADLOCKS_PATTERN_TYPES)) {
|
if ((pattern_type < 0) || (pattern_type >= BADLOCKS_PATTERN_TYPES)) {
|
||||||
uprintf("%sInvalid pattern type\n", bb_prefix);
|
uprintf("%sInvalid pattern type\n", bb_prefix);
|
||||||
|
|
|
@ -512,6 +512,22 @@ static __inline int PathGetDriveNumberU(char* lpPath)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This one is tricky since we can't blindly convert a
|
||||||
|
// UTF-16 position to a UTF-8 one. So we do it manually.
|
||||||
|
static __inline const char* PathFindFileNameU(const char* szPath)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (szPath == NULL)
|
||||||
|
return NULL;
|
||||||
|
for (i = strlen(szPath); i != 0; i--) {
|
||||||
|
if ((szPath[i] == '/') || (szPath[i] == '\\')) {
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &szPath[i];
|
||||||
|
}
|
||||||
|
|
||||||
// This function differs from regular GetTextExtentPoint in that it uses a zero terminated string
|
// This function differs from regular GetTextExtentPoint in that it uses a zero terminated string
|
||||||
static __inline BOOL GetTextExtentPointU(HDC hdc, const char* lpString, LPSIZE lpSize)
|
static __inline BOOL GetTextExtentPointU(HDC hdc, const char* lpString, LPSIZE lpSize)
|
||||||
{
|
{
|
||||||
|
|
194
src/net.c
194
src/net.c
|
@ -29,6 +29,7 @@
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "rufus.h"
|
#include "rufus.h"
|
||||||
#include "missing.h"
|
#include "missing.h"
|
||||||
|
@ -216,13 +217,14 @@ const char* WinInetErrorString(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Download a file from an URL
|
* Download a file or fill a buffer from an URL
|
||||||
* Mostly taken from http://support.microsoft.com/kb/234913
|
* Mostly taken from http://support.microsoft.com/kb/234913
|
||||||
|
* If file is NULL, a buffer is allocated for the download (that needs to be freed by the caller)
|
||||||
* If hProgressDialog is not NULL, this function will send INIT and EXIT messages
|
* If hProgressDialog is not NULL, this function will send INIT and EXIT messages
|
||||||
* to the dialog in question, with WPARAM being set to nonzero for EXIT on success
|
* to the dialog in question, with WPARAM being set to nonzero for EXIT on success
|
||||||
* and also attempt to indicate progress using an IDC_PROGRESS control
|
* and also attempt to indicate progress using an IDC_PROGRESS control
|
||||||
*/
|
*/
|
||||||
DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog)
|
static DWORD DownloadToFileOrBuffer(const char* url, const char* file, BYTE** buffer, HWND hProgressDialog)
|
||||||
{
|
{
|
||||||
HWND hProgressBar = NULL;
|
HWND hProgressBar = NULL;
|
||||||
BOOL r = FALSE;
|
BOOL r = FALSE;
|
||||||
|
@ -234,8 +236,8 @@ DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog)
|
||||||
HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL;
|
HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL;
|
||||||
URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0,
|
URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0,
|
||||||
hostname, sizeof(hostname), 0, NULL, 1, urlpath, sizeof(urlpath), NULL, 1};
|
hostname, sizeof(hostname), 0, NULL, 1, urlpath, sizeof(urlpath), NULL, 1};
|
||||||
size_t last_slash;
|
const char* short_name;
|
||||||
int i;
|
size_t i;
|
||||||
|
|
||||||
// Can't link with wininet.lib because of sideloading issues
|
// Can't link with wininet.lib because of sideloading issues
|
||||||
PF_TYPE_DECL(WINAPI, BOOL, InternetCrackUrlA, (LPCSTR, DWORD, DWORD, LPURL_COMPONENTSA));
|
PF_TYPE_DECL(WINAPI, BOOL, InternetCrackUrlA, (LPCSTR, DWORD, DWORD, LPURL_COMPONENTSA));
|
||||||
|
@ -257,7 +259,7 @@ DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog)
|
||||||
PF_INIT_OR_OUT(HttpSendRequestA, WinInet);
|
PF_INIT_OR_OUT(HttpSendRequestA, WinInet);
|
||||||
PF_INIT_OR_OUT(HttpQueryInfoA, WinInet);
|
PF_INIT_OR_OUT(HttpQueryInfoA, WinInet);
|
||||||
|
|
||||||
DownloadStatus = 0;
|
DownloadStatus = 404;
|
||||||
if (hProgressDialog != NULL) {
|
if (hProgressDialog != NULL) {
|
||||||
// Use the progress control provided, if any
|
// Use the progress control provided, if any
|
||||||
hProgressBar = GetDlgItem(hProgressDialog, IDC_PROGRESS);
|
hProgressBar = GetDlgItem(hProgressDialog, IDC_PROGRESS);
|
||||||
|
@ -268,22 +270,18 @@ DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog)
|
||||||
SendMessage(hProgressDialog, UM_PROGRESS_INIT, 0, 0);
|
SendMessage(hProgressDialog, UM_PROGRESS_INIT, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file == NULL)
|
if (url == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
for (last_slash = safe_strlen(file); last_slash != 0; last_slash--) {
|
short_name = (file != NULL) ? PathFindFileNameU(file) : PathFindFileNameU(url);
|
||||||
if ((file[last_slash] == '/') || (file[last_slash] == '\\')) {
|
|
||||||
last_slash++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintInfo(0, MSG_085, &file[last_slash]);
|
if (hProgressDialog != NULL)
|
||||||
uprintf("Downloading '%s' from %s\n", &file[last_slash], url);
|
PrintInfo(0, MSG_085, short_name);
|
||||||
|
uprintf("Downloading %s", url);
|
||||||
|
|
||||||
if ( (!pfInternetCrackUrlA(url, (DWORD)safe_strlen(url), 0, &UrlParts))
|
if ( (!pfInternetCrackUrlA(url, (DWORD)safe_strlen(url), 0, &UrlParts))
|
||||||
|| (UrlParts.lpszHostName == NULL) || (UrlParts.lpszUrlPath == NULL)) {
|
|| (UrlParts.lpszHostName == NULL) || (UrlParts.lpszUrlPath == NULL)) {
|
||||||
uprintf("Unable to decode URL: %s\n", WinInetErrorString());
|
uprintf("Unable to decode URL: %s", WinInetErrorString());
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
hostname[sizeof(hostname)-1] = 0;
|
hostname[sizeof(hostname)-1] = 0;
|
||||||
|
@ -295,7 +293,7 @@ DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog)
|
||||||
if (i <= 0) {
|
if (i <= 0) {
|
||||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa384702.aspx is wrong...
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa384702.aspx is wrong...
|
||||||
SetLastError(ERROR_INTERNET_NOT_INITIALIZED);
|
SetLastError(ERROR_INTERNET_NOT_INITIALIZED);
|
||||||
uprintf("Network is unavailable: %s\n", WinInetErrorString());
|
uprintf("Network is unavailable: %s", WinInetErrorString());
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
static_sprintf(agent, APPLICATION_NAME "/%d.%d.%d (Windows NT %d.%d%s)",
|
static_sprintf(agent, APPLICATION_NAME "/%d.%d.%d (Windows NT %d.%d%s)",
|
||||||
|
@ -303,13 +301,13 @@ DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog)
|
||||||
nWindowsVersion>>4, nWindowsVersion&0x0F, is_x64()?"; WOW64":"");
|
nWindowsVersion>>4, nWindowsVersion&0x0F, is_x64()?"; WOW64":"");
|
||||||
hSession = pfInternetOpenA(agent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
|
hSession = pfInternetOpenA(agent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
|
||||||
if (hSession == NULL) {
|
if (hSession == NULL) {
|
||||||
uprintf("Could not open Internet session: %s\n", WinInetErrorString());
|
uprintf("Could not open Internet session: %s", WinInetErrorString());
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
hConnection = pfInternetConnectA(hSession, UrlParts.lpszHostName, UrlParts.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL);
|
hConnection = pfInternetConnectA(hSession, UrlParts.lpszHostName, UrlParts.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL);
|
||||||
if (hConnection == NULL) {
|
if (hConnection == NULL) {
|
||||||
uprintf("Could not connect to server %s:%d: %s\n", UrlParts.lpszHostName, UrlParts.nPort, WinInetErrorString());
|
uprintf("Could not connect to server %s:%d: %s", UrlParts.lpszHostName, UrlParts.nPort, WinInetErrorString());
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,64 +316,84 @@ DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog)
|
||||||
INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_HYPERLINK|
|
INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_HYPERLINK|
|
||||||
((UrlParts.nScheme==INTERNET_SCHEME_HTTPS)?INTERNET_FLAG_SECURE:0), (DWORD_PTR)NULL);
|
((UrlParts.nScheme==INTERNET_SCHEME_HTTPS)?INTERNET_FLAG_SECURE:0), (DWORD_PTR)NULL);
|
||||||
if (hRequest == NULL) {
|
if (hRequest == NULL) {
|
||||||
uprintf("Could not open URL %s: %s\n", url, WinInetErrorString());
|
uprintf("Could not open URL %s: %s", url, WinInetErrorString());
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pfHttpSendRequestA(hRequest, NULL, 0, NULL, 0)) {
|
if (!pfHttpSendRequestA(hRequest, NULL, 0, NULL, 0)) {
|
||||||
uprintf("Unable to send request: %s\n", WinInetErrorString());
|
uprintf("Unable to send request: %s", WinInetErrorString());
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the file size
|
// Get the file size
|
||||||
dwSize = sizeof(DownloadStatus);
|
dwSize = sizeof(DownloadStatus);
|
||||||
DownloadStatus = 404;
|
|
||||||
pfHttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&DownloadStatus, &dwSize, NULL);
|
pfHttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&DownloadStatus, &dwSize, NULL);
|
||||||
if (DownloadStatus != 200) {
|
if (DownloadStatus != 200) {
|
||||||
error_code = ERROR_INTERNET_ITEM_NOT_FOUND;
|
error_code = ERROR_INTERNET_ITEM_NOT_FOUND;
|
||||||
uprintf("Unable to access file: %d\n", DownloadStatus);
|
uprintf("Unable to access file: %d", DownloadStatus);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
dwSize = sizeof(dwTotalSize);
|
dwSize = sizeof(dwTotalSize);
|
||||||
if (!pfHttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwTotalSize, &dwSize, NULL)) {
|
if (!pfHttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwTotalSize, &dwSize, NULL)) {
|
||||||
uprintf("Unable to retrieve file length: %s\n", WinInetErrorString());
|
uprintf("Unable to retrieve file length: %s", WinInetErrorString());
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
uprintf("File length: %d bytes\n", dwTotalSize);
|
uprintf("File length: %d bytes", dwTotalSize);
|
||||||
|
|
||||||
|
if (file != NULL) {
|
||||||
hFile = CreateFileU(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
hFile = CreateFileU(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
if (hFile == INVALID_HANDLE_VALUE) {
|
if (hFile == INVALID_HANDLE_VALUE) {
|
||||||
uprintf("Unable to create file '%s': %s\n", &file[last_slash], WinInetErrorString());
|
uprintf("Unable to create file '%s': %s", short_name, WinInetErrorString());
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (buffer == NULL) {
|
||||||
|
uprintf("No buffer pointer provided for download");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
*buffer = malloc(dwTotalSize);
|
||||||
|
if (*buffer == NULL) {
|
||||||
|
uprintf("Could not allocate buffer for download");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Keep checking for data until there is nothing left.
|
// Keep checking for data until there is nothing left.
|
||||||
dwSize = 0;
|
dwSize = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (IS_ERROR(FormatStatus))
|
if (IS_ERROR(FormatStatus))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!pfInternetReadFile(hRequest, buf, sizeof(buf), &dwDownloaded) || (dwDownloaded == 0))
|
if (!pfInternetReadFile(hRequest, buf, sizeof(buf), &dwDownloaded) || (dwDownloaded == 0))
|
||||||
break;
|
break;
|
||||||
dwSize += dwDownloaded;
|
if (hProgressDialog != NULL) {
|
||||||
SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS*((1.0f*dwSize) / (1.0f*dwTotalSize))), 0);
|
SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS*((1.0f*dwSize) / (1.0f*dwTotalSize))), 0);
|
||||||
PrintInfo(0, MSG_241, (100.0f*dwSize) / (1.0f*dwTotalSize));
|
PrintInfo(0, MSG_241, (100.0f*dwSize) / (1.0f*dwTotalSize));
|
||||||
|
}
|
||||||
|
if (file != NULL) {
|
||||||
if (!WriteFile(hFile, buf, dwDownloaded, &dwWritten, NULL)) {
|
if (!WriteFile(hFile, buf, dwDownloaded, &dwWritten, NULL)) {
|
||||||
uprintf("Error writing file '%s': %s\n", &file[last_slash], WinInetErrorString());
|
uprintf("Error writing file '%s': %s", short_name, WinInetErrorString());
|
||||||
goto out;
|
goto out;
|
||||||
} else if (dwDownloaded != dwWritten) {
|
} else if (dwDownloaded != dwWritten) {
|
||||||
uprintf("Error writing file '%s': Only %d/%d bytes written\n", dwWritten, dwDownloaded);
|
uprintf("Error writing file '%s': Only %d/%d bytes written", short_name, dwWritten, dwDownloaded);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(&(*buffer)[dwSize], buf, dwDownloaded);
|
||||||
|
}
|
||||||
|
dwSize += dwDownloaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dwSize != dwTotalSize) {
|
if (dwSize != dwTotalSize) {
|
||||||
uprintf("Could not download complete file - read: %d bytes, expected: %d bytes\n", dwSize, dwTotalSize);
|
uprintf("Could not download complete file - read: %d bytes, expected: %d bytes", dwSize, dwTotalSize);
|
||||||
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
|
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
|
||||||
goto out;
|
goto out;
|
||||||
} else {
|
} else {
|
||||||
r = TRUE;
|
r = TRUE;
|
||||||
uprintf("Successfully downloaded '%s'\n", &file[last_slash]);
|
uprintf("Successfully downloaded '%s'", short_name);
|
||||||
|
if (hProgressDialog != NULL) {
|
||||||
|
SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)MAX_PROGRESS, 0);
|
||||||
|
PrintInfo(0, MSG_241, 100.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -406,20 +424,76 @@ out:
|
||||||
return r?dwSize:0;
|
return r?dwSize:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Download and validate a signed file. The file must have a corresponding '.sig' on the server.
|
||||||
|
DWORD DownloadSignedFile(const char* url, const char* file, HWND hProgressDialog)
|
||||||
|
{
|
||||||
|
char* url_sig = NULL;
|
||||||
|
BYTE *buf = NULL, *sig = NULL;
|
||||||
|
DWORD buf_len = 0, sig_len = 0;
|
||||||
|
DWORD ret = 0;
|
||||||
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
if (url == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
url_sig = malloc(strlen(url) + 5);
|
||||||
|
if (url_sig == NULL) {
|
||||||
|
uprintf("Could not allocate signature URL");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
strcpy(url_sig, url);
|
||||||
|
strcat(url_sig, ".sig");
|
||||||
|
|
||||||
|
buf_len = DownloadToFileOrBuffer(url, NULL, &buf, hProgressDialog);
|
||||||
|
if (buf_len == 0)
|
||||||
|
goto out;
|
||||||
|
sig_len = DownloadToFileOrBuffer(url_sig, NULL, &sig, NULL);
|
||||||
|
if ((sig_len != RSA_SIGNATURE_SIZE) || (!ValidateOpensslSignature(buf, buf_len, sig, sig_len))) {
|
||||||
|
uprintf("FATAL: Server signature is invalid!");
|
||||||
|
DownloadStatus = 403; // Forbidden
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
uprintf("Download signature is valid");
|
||||||
|
DownloadStatus = 206; // Partial content
|
||||||
|
hFile = CreateFileU(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (hFile == INVALID_HANDLE_VALUE) {
|
||||||
|
uprintf("Unable to create file '%s': %s", PathFindFileNameU(file), WinInetErrorString());
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!WriteFile(hFile, buf, buf_len, &ret, NULL)) {
|
||||||
|
uprintf("Error writing file '%s': %s", PathFindFileNameU(file), WinInetErrorString());
|
||||||
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
} else if (ret != buf_len) {
|
||||||
|
uprintf("Error writing file '%s': Only %d/%d bytes written", PathFindFileNameU(file), ret, buf_len);
|
||||||
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
DownloadStatus = 200; // Full content
|
||||||
|
|
||||||
|
out:
|
||||||
|
safe_closehandle(hFile);
|
||||||
|
free(url_sig);
|
||||||
|
free(buf);
|
||||||
|
free(sig);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Threaded download */
|
/* Threaded download */
|
||||||
static const char *_url, *_file;
|
static const char *_url, *_file;
|
||||||
static HWND _hProgressDialog;
|
static HWND _hProgressDialog;
|
||||||
static DWORD WINAPI _DownloadFileThread(LPVOID param)
|
static DWORD WINAPI _DownloadSignedFileThread(LPVOID param)
|
||||||
{
|
{
|
||||||
ExitThread(DownloadFile(_url, _file, _hProgressDialog) != 0);
|
ExitThread(DownloadSignedFile(_url, _file, _hProgressDialog) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLE DownloadFileThreaded(const char* url, const char* file, HWND hProgressDialog)
|
HANDLE DownloadSignedFileThreaded(const char* url, const char* file, HWND hProgressDialog)
|
||||||
{
|
{
|
||||||
_url = url;
|
_url = url;
|
||||||
_file = file;
|
_file = file;
|
||||||
_hProgressDialog = hProgressDialog;
|
_hProgressDialog = hProgressDialog;
|
||||||
return CreateThread(NULL, 0, _DownloadFileThread, NULL, 0, NULL);
|
return CreateThread(NULL, 0, _DownloadSignedFileThread, NULL, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __inline uint64_t to_uint64_t(uint16_t x[4]) {
|
static __inline uint64_t to_uint64_t(uint16_t x[4]) {
|
||||||
|
@ -443,8 +517,9 @@ static DWORD WINAPI CheckForUpdatesThread(LPVOID param)
|
||||||
static const char* channel[] = {"release", "beta", "test"}; // release channel
|
static const char* channel[] = {"release", "beta", "test"}; // release channel
|
||||||
const char* accept_types[] = {"*/*\0", NULL};
|
const char* accept_types[] = {"*/*\0", NULL};
|
||||||
DWORD dwFlags, dwSize, dwDownloaded, dwTotalSize, dwStatus;
|
DWORD dwFlags, dwSize, dwDownloaded, dwTotalSize, dwStatus;
|
||||||
|
BYTE *sig = NULL;
|
||||||
char* buf = NULL;
|
char* buf = NULL;
|
||||||
char agent[64], hostname[64], urlpath[128], mime[32];
|
char agent[64], hostname[64], urlpath[128], sigpath[256], mime[32];
|
||||||
OSVERSIONINFOA os_version = {sizeof(OSVERSIONINFOA), 0, 0, 0, 0, ""};
|
OSVERSIONINFOA os_version = {sizeof(OSVERSIONINFOA), 0, 0, 0, 0, ""};
|
||||||
HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL;
|
HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL;
|
||||||
URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0,
|
URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0,
|
||||||
|
@ -487,7 +562,7 @@ static DWORD WINAPI CheckForUpdatesThread(LPVOID param)
|
||||||
} while ((!force_update_check) && ((iso_op_in_progress || format_op_in_progress || (dialog_showing>0))));
|
} while ((!force_update_check) && ((iso_op_in_progress || format_op_in_progress || (dialog_showing>0))));
|
||||||
if (!force_update_check) {
|
if (!force_update_check) {
|
||||||
if ((ReadSetting32(SETTING_UPDATE_INTERVAL) == -1)) {
|
if ((ReadSetting32(SETTING_UPDATE_INTERVAL) == -1)) {
|
||||||
vuprintf("Check for updates disabled, as per settings.\n");
|
vuprintf("Check for updates disabled, as per settings.");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
reg_time = ReadSetting64(SETTING_LAST_UPDATE);
|
reg_time = ReadSetting64(SETTING_LAST_UPDATE);
|
||||||
|
@ -500,9 +575,9 @@ static DWORD WINAPI CheckForUpdatesThread(LPVOID param)
|
||||||
if (!SystemTimeToFileTime(&LocalTime, &FileTime))
|
if (!SystemTimeToFileTime(&LocalTime, &FileTime))
|
||||||
goto out;
|
goto out;
|
||||||
local_time = ((((int64_t)FileTime.dwHighDateTime)<<32) + FileTime.dwLowDateTime) / 10000000;
|
local_time = ((((int64_t)FileTime.dwHighDateTime)<<32) + FileTime.dwLowDateTime) / 10000000;
|
||||||
vvuprintf("Local time: %" PRId64 "\n", local_time);
|
vvuprintf("Local time: %" PRId64, local_time);
|
||||||
if (local_time < reg_time + update_interval) {
|
if (local_time < reg_time + update_interval) {
|
||||||
vuprintf("Next update check in %" PRId64 " seconds.\n", reg_time + update_interval - local_time);
|
vuprintf("Next update check in %" PRId64 " seconds.", reg_time + update_interval - local_time);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -512,7 +587,7 @@ static DWORD WINAPI CheckForUpdatesThread(LPVOID param)
|
||||||
status++; // 1
|
status++; // 1
|
||||||
|
|
||||||
if (!GetVersionExA(&os_version)) {
|
if (!GetVersionExA(&os_version)) {
|
||||||
uprintf("Could not read Windows version - Check for updates cancelled.\n");
|
uprintf("Could not read Windows version - Check for updates cancelled.");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,7 +615,7 @@ static DWORD WINAPI CheckForUpdatesThread(LPVOID param)
|
||||||
max_channel = releases_only ? 1 : (int)ARRAYSIZE(channel) - 1;
|
max_channel = releases_only ? 1 : (int)ARRAYSIZE(channel) - 1;
|
||||||
#endif
|
#endif
|
||||||
for (k=0; (k<max_channel) && (!found_new_version); k++) {
|
for (k=0; (k<max_channel) && (!found_new_version); k++) {
|
||||||
uprintf("Checking %s channel...\n", channel[k]);
|
uprintf("Checking %s channel...", channel[k]);
|
||||||
// At this stage we can query the server for various update version files.
|
// At this stage we can query the server for various update version files.
|
||||||
// We first try to lookup for "<appname>_<os_arch>_<os_version_major>_<os_version_minor>.ver"
|
// We first try to lookup for "<appname>_<os_arch>_<os_version_major>_<os_version_minor>.ver"
|
||||||
// and then remove each each of the <os_> components until we find our match. For instance, we may first
|
// and then remove each each of the <os_> components until we find our match. For instance, we may first
|
||||||
|
@ -548,21 +623,18 @@ static DWORD WINAPI CheckForUpdatesThread(LPVOID param)
|
||||||
// This allows sunsetting OS versions (eg XP) or providing different downloads for different archs/groups.
|
// This allows sunsetting OS versions (eg XP) or providing different downloads for different archs/groups.
|
||||||
static_sprintf(urlpath, "%s%s%s_%s_%lu.%lu.ver", APPLICATION_NAME, (k==0)?"":"_",
|
static_sprintf(urlpath, "%s%s%s_%s_%lu.%lu.ver", APPLICATION_NAME, (k==0)?"":"_",
|
||||||
(k==0)?"":channel[k], archname[is_x64()?1:0], os_version.dwMajorVersion, os_version.dwMinorVersion);
|
(k==0)?"":channel[k], archname[is_x64()?1:0], os_version.dwMajorVersion, os_version.dwMinorVersion);
|
||||||
vuprintf("Base update check: %s\n", urlpath);
|
vuprintf("Base update check: %s", urlpath);
|
||||||
for (i=0, j=(int)safe_strlen(urlpath)-5; (j>0)&&(i<ARRAYSIZE(verpos)); j--) {
|
for (i=0, j=(int)safe_strlen(urlpath)-5; (j>0)&&(i<ARRAYSIZE(verpos)); j--) {
|
||||||
if ((urlpath[j] == '.') || (urlpath[j] == '_')) {
|
if ((urlpath[j] == '.') || (urlpath[j] == '_')) {
|
||||||
verpos[i++] = j;
|
verpos[i++] = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i != ARRAYSIZE(verpos)) {
|
assert(i == ARRAYSIZE(verpos));
|
||||||
uprintf("Broken code in CheckForUpdatesThread()!\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
UrlParts.lpszUrlPath = urlpath;
|
UrlParts.lpszUrlPath = urlpath;
|
||||||
UrlParts.dwUrlPathLength = sizeof(urlpath);
|
UrlParts.dwUrlPathLength = sizeof(urlpath);
|
||||||
for (i=0; i<ARRAYSIZE(verpos); i++) {
|
for (i=0; i<ARRAYSIZE(verpos); i++) {
|
||||||
vvuprintf("Trying %s\n", UrlParts.lpszUrlPath);
|
vvuprintf("Trying %s", UrlParts.lpszUrlPath);
|
||||||
hRequest = pfHttpOpenRequestA(hConnection, "GET", UrlParts.lpszUrlPath, NULL, NULL, accept_types,
|
hRequest = pfHttpOpenRequestA(hConnection, "GET", UrlParts.lpszUrlPath, NULL, NULL, accept_types,
|
||||||
INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS|
|
INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS|
|
||||||
INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_HYPERLINK|
|
INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_HYPERLINK|
|
||||||
|
@ -607,7 +679,7 @@ static DWORD WINAPI CheckForUpdatesThread(LPVOID param)
|
||||||
|| (!SystemTimeToFileTime(&ServerTime, &FileTime)) )
|
|| (!SystemTimeToFileTime(&ServerTime, &FileTime)) )
|
||||||
goto out;
|
goto out;
|
||||||
server_time = ((((int64_t)FileTime.dwHighDateTime)<<32) + FileTime.dwLowDateTime) / 10000000;
|
server_time = ((((int64_t)FileTime.dwHighDateTime)<<32) + FileTime.dwLowDateTime) / 10000000;
|
||||||
vvuprintf("Server time: %" PRId64 "\n", server_time);
|
vvuprintf("Server time: %" PRId64, server_time);
|
||||||
// Always store the server response time - the only clock we trust!
|
// Always store the server response time - the only clock we trust!
|
||||||
WriteSetting64(SETTING_LAST_UPDATE, server_time);
|
WriteSetting64(SETTING_LAST_UPDATE, server_time);
|
||||||
// Might as well let the user know
|
// Might as well let the user know
|
||||||
|
@ -625,29 +697,39 @@ static DWORD WINAPI CheckForUpdatesThread(LPVOID param)
|
||||||
safe_free(buf);
|
safe_free(buf);
|
||||||
// Make sure the file is NUL terminated
|
// Make sure the file is NUL terminated
|
||||||
buf = (char*)calloc(dwTotalSize+1, 1);
|
buf = (char*)calloc(dwTotalSize+1, 1);
|
||||||
if (buf == NULL) goto out;
|
if (buf == NULL)
|
||||||
|
goto out;
|
||||||
// This is a version file - we should be able to gulp it down in one go
|
// This is a version file - we should be able to gulp it down in one go
|
||||||
if (!pfInternetReadFile(hRequest, buf, dwTotalSize, &dwDownloaded) || (dwDownloaded != dwTotalSize))
|
if (!pfInternetReadFile(hRequest, buf, dwTotalSize, &dwDownloaded) || (dwDownloaded != dwTotalSize))
|
||||||
goto out;
|
goto out;
|
||||||
|
vuprintf("Successfully downloaded version file (%d bytes)", dwTotalSize);
|
||||||
|
|
||||||
|
// Now download the signature file
|
||||||
|
static_sprintf(sigpath, "%s/%s.sig", server_url, urlpath);
|
||||||
|
dwDownloaded = DownloadToFileOrBuffer(sigpath, NULL, &sig, NULL);
|
||||||
|
if ((dwDownloaded != RSA_SIGNATURE_SIZE) || (!ValidateOpensslSignature(buf, dwTotalSize, sig, dwDownloaded))) {
|
||||||
|
uprintf("FATAL: Version signature is invalid!");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
vuprintf("Version signature is valid");
|
||||||
|
|
||||||
status++;
|
status++;
|
||||||
vuprintf("Successfully downloaded version file (%d bytes)\n", dwTotalSize);
|
|
||||||
|
|
||||||
parse_update(buf, dwTotalSize+1);
|
parse_update(buf, dwTotalSize+1);
|
||||||
|
|
||||||
vuprintf("UPDATE DATA:\n");
|
vuprintf("UPDATE DATA:");
|
||||||
vuprintf(" version: %d.%d.%d (%s)\n", update.version[0], update.version[1], update.version[2], channel[k]);
|
vuprintf(" version: %d.%d.%d (%s)", update.version[0], update.version[1], update.version[2], channel[k]);
|
||||||
vuprintf(" platform_min: %d.%d\n", update.platform_min[0], update.platform_min[1]);
|
vuprintf(" platform_min: %d.%d", update.platform_min[0], update.platform_min[1]);
|
||||||
vuprintf(" url: %s\n", update.download_url);
|
vuprintf(" url: %s", update.download_url);
|
||||||
|
|
||||||
found_new_version = ((to_uint64_t(update.version) > to_uint64_t(rufus_version)) || (force_update))
|
found_new_version = ((to_uint64_t(update.version) > to_uint64_t(rufus_version)) || (force_update))
|
||||||
&& ( (os_version.dwMajorVersion > update.platform_min[0])
|
&& ( (os_version.dwMajorVersion > update.platform_min[0])
|
||||||
|| ( (os_version.dwMajorVersion == update.platform_min[0]) && (os_version.dwMinorVersion >= update.platform_min[1])) );
|
|| ( (os_version.dwMajorVersion == update.platform_min[0]) && (os_version.dwMinorVersion >= update.platform_min[1])) );
|
||||||
uprintf("N%sew %s version found%c\n", found_new_version?"":"o n", channel[k], found_new_version?'!':'.');
|
uprintf("N%sew %s version found%c", found_new_version?"":"o n", channel[k], found_new_version?'!':'.');
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
safe_free(buf);
|
safe_free(buf);
|
||||||
|
safe_free(sig);
|
||||||
if (hRequest)
|
if (hRequest)
|
||||||
pfInternetCloseHandle(hRequest);
|
pfInternetCloseHandle(hRequest);
|
||||||
if (hConnection)
|
if (hConnection)
|
||||||
|
|
146
src/pki.c
146
src/pki.c
|
@ -27,6 +27,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <wincrypt.h>
|
#include <wincrypt.h>
|
||||||
#include <wintrust.h>
|
#include <wintrust.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "rufus.h"
|
#include "rufus.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
@ -52,6 +53,63 @@ typedef struct {
|
||||||
LPWSTR lpszMoreInfoLink;
|
LPWSTR lpszMoreInfoLink;
|
||||||
} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;
|
} 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
|
||||||
|
} RSA_2048_PUBKEY;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// 0x00 at the beginning to force an integer sign. These extra 8 bits *MUST* be removed.
|
||||||
|
static uint8_t rsa_pubkey_modulus[] = {
|
||||||
|
/*
|
||||||
|
$ openssl genrsa -aes256 -out private.pem 2048
|
||||||
|
$ openssl rsa -in private.pem -pubout -out public.pem
|
||||||
|
$ openssl rsa -pubin -inform PEM -text -noout < public.pem
|
||||||
|
Public-Key: (2048 bit)
|
||||||
|
Modulus:
|
||||||
|
00:b6:40:7d:d1:98:7b:81:9e:be:23:0f:32:5d:55:
|
||||||
|
60:c6:bf:b4:41:bb:43:1b:f1:e1:e6:f9:2b:d6:dd:
|
||||||
|
11:50:e8:b9:3f:19:97:5e:a7:8b:4a:30:c6:76:58:
|
||||||
|
72:1c:ac:ff:a1:f8:96:6c:51:5d:13:11:e3:5b:11:
|
||||||
|
82:f5:9a:69:e4:28:97:0f:ca:1f:02:ea:1f:7d:dc:
|
||||||
|
f9:fc:79:2f:61:ff:8e:45:60:65:ba:37:9b:de:49:
|
||||||
|
05:6a:a8:fd:70:d0:0c:79:b6:d7:81:aa:54:c3:c6:
|
||||||
|
4a:87:a0:45:ee:ca:d5:d5:c5:c2:ac:86:42:b3:58:
|
||||||
|
27:d2:43:b9:37:f2:e6:75:66:17:53:d0:38:d0:c6:
|
||||||
|
57:c2:55:36:a2:43:87:ea:24:f0:96:ec:34:dd:79:
|
||||||
|
4d:80:54:9d:84:81:a7:cf:0c:a5:7c:d6:63:fa:7a:
|
||||||
|
66:30:a9:50:ee:f0:e5:f8:a2:2d:ac:fc:24:21:fe:
|
||||||
|
ef:e8:d3:6f:0e:27:b0:64:22:95:3e:6d:a6:66:97:
|
||||||
|
c6:98:c2:47:b3:98:69:4d:b1:b5:d3:6f:43:f5:d7:
|
||||||
|
a5:13:5e:8c:28:4f:62:4e:01:48:0a:63:89:e7:ca:
|
||||||
|
34:aa:7d:2f:bb:70:e0:31:bb:39:49:a3:d2:c9:2e:
|
||||||
|
a6:30:54:9a:5c:4d:58:17:d9:fc:3a:43:e6:8e:2a:
|
||||||
|
18:e9
|
||||||
|
Exponent: 65537 (0x10001)
|
||||||
|
*/
|
||||||
|
0x00, 0xb6, 0x40, 0x7d, 0xd1, 0x98, 0x7b, 0x81, 0x9e, 0xbe, 0x23, 0x0f, 0x32, 0x5d, 0x55,
|
||||||
|
0x60, 0xc6, 0xbf, 0xb4, 0x41, 0xbb, 0x43, 0x1b, 0xf1, 0xe1, 0xe6, 0xf9, 0x2b, 0xd6, 0xdd,
|
||||||
|
0x11, 0x50, 0xe8, 0xb9, 0x3f, 0x19, 0x97, 0x5e, 0xa7, 0x8b, 0x4a, 0x30, 0xc6, 0x76, 0x58,
|
||||||
|
0x72, 0x1c, 0xac, 0xff, 0xa1, 0xf8, 0x96, 0x6c, 0x51, 0x5d, 0x13, 0x11, 0xe3, 0x5b, 0x11,
|
||||||
|
0x82, 0xf5, 0x9a, 0x69, 0xe4, 0x28, 0x97, 0x0f, 0xca, 0x1f, 0x02, 0xea, 0x1f, 0x7d, 0xdc,
|
||||||
|
0xf9, 0xfc, 0x79, 0x2f, 0x61, 0xff, 0x8e, 0x45, 0x60, 0x65, 0xba, 0x37, 0x9b, 0xde, 0x49,
|
||||||
|
0x05, 0x6a, 0xa8, 0xfd, 0x70, 0xd0, 0x0c, 0x79, 0xb6, 0xd7, 0x81, 0xaa, 0x54, 0xc3, 0xc6,
|
||||||
|
0x4a, 0x87, 0xa0, 0x45, 0xee, 0xca, 0xd5, 0xd5, 0xc5, 0xc2, 0xac, 0x86, 0x42, 0xb3, 0x58,
|
||||||
|
0x27, 0xd2, 0x43, 0xb9, 0x37, 0xf2, 0xe6, 0x75, 0x66, 0x17, 0x53, 0xd0, 0x38, 0xd0, 0xc6,
|
||||||
|
0x57, 0xc2, 0x55, 0x36, 0xa2, 0x43, 0x87, 0xea, 0x24, 0xf0, 0x96, 0xec, 0x34, 0xdd, 0x79,
|
||||||
|
0x4d, 0x80, 0x54, 0x9d, 0x84, 0x81, 0xa7, 0xcf, 0x0c, 0xa5, 0x7c, 0xd6, 0x63, 0xfa, 0x7a,
|
||||||
|
0x66, 0x30, 0xa9, 0x50, 0xee, 0xf0, 0xe5, 0xf8, 0xa2, 0x2d, 0xac, 0xfc, 0x24, 0x21, 0xfe,
|
||||||
|
0xef, 0xe8, 0xd3, 0x6f, 0x0e, 0x27, 0xb0, 0x64, 0x22, 0x95, 0x3e, 0x6d, 0xa6, 0x66, 0x97,
|
||||||
|
0xc6, 0x98, 0xc2, 0x47, 0xb3, 0x98, 0x69, 0x4d, 0xb1, 0xb5, 0xd3, 0x6f, 0x43, 0xf5, 0xd7,
|
||||||
|
0xa5, 0x13, 0x5e, 0x8c, 0x28, 0x4f, 0x62, 0x4e, 0x01, 0x48, 0x0a, 0x63, 0x89, 0xe7, 0xca,
|
||||||
|
0x34, 0xaa, 0x7d, 0x2f, 0xbb, 0x70, 0xe0, 0x31, 0xbb, 0x39, 0x49, 0xa3, 0xd2, 0xc9, 0x2e,
|
||||||
|
0xa6, 0x30, 0x54, 0x9a, 0x5c, 0x4d, 0x58, 0x17, 0xd9, 0xfc, 0x3a, 0x43, 0xe6, 0x8e, 0x2a,
|
||||||
|
0x18, 0xe9
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FormatMessage does not handle PKI errors
|
* FormatMessage does not handle PKI errors
|
||||||
|
@ -65,8 +123,19 @@ const char* WinPKIErrorString(void)
|
||||||
return WindowsErrorString();
|
return WindowsErrorString();
|
||||||
|
|
||||||
switch (error_code) {
|
switch (error_code) {
|
||||||
|
// See also https://docs.microsoft.com/en-gb/windows/desktop/com/com-error-codes-4
|
||||||
case NTE_BAD_UID:
|
case NTE_BAD_UID:
|
||||||
return "Bad UID.";
|
return "Bad UID.";
|
||||||
|
case NTE_NO_KEY:
|
||||||
|
return "Key does not exist.";
|
||||||
|
case NTE_BAD_KEYSET:
|
||||||
|
return "Keyset does not exist.";
|
||||||
|
case NTE_BAD_ALGID:
|
||||||
|
return "Invalid algorithm specified.";
|
||||||
|
case NTE_BAD_VER:
|
||||||
|
return "Bad version of provider.";
|
||||||
|
case NTE_BAD_SIGNATURE:
|
||||||
|
return "Invalid Signature.";
|
||||||
case CRYPT_E_MSG_ERROR:
|
case CRYPT_E_MSG_ERROR:
|
||||||
return "An error occurred while performing an operation on a cryptographic message.";
|
return "An error occurred while performing an operation on a cryptographic message.";
|
||||||
case CRYPT_E_UNKNOWN_ALGO:
|
case CRYPT_E_UNKNOWN_ALGO:
|
||||||
|
@ -581,3 +650,80 @@ LONG ValidateSignature(HWND hDlg, const char* path)
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Why-oh-why am I the only one on github doing this openssl vs MS signature validation?!?
|
||||||
|
// For once, I'd like to find code samples from *OTHER PEOPLE* who went through this ordeal first...
|
||||||
|
BOOL ValidateOpensslSignature(BYTE* pbBuffer, DWORD dwBufferLen, BYTE* pbSignature, DWORD dwSigLen)
|
||||||
|
{
|
||||||
|
HCRYPTPROV hProv = 0;
|
||||||
|
HCRYPTHASH hHash = 0;
|
||||||
|
HCRYPTKEY hPubKey;
|
||||||
|
// We could load and convert an openssl PEM, but since we know what we need...
|
||||||
|
RSA_2048_PUBKEY pbMyPubKey = {
|
||||||
|
{ PUBLICKEYBLOB, CUR_BLOB_VERSION, 0, CALG_RSA_KEYX },
|
||||||
|
// $ openssl genrsa -aes256 -out private.pem 2048
|
||||||
|
// Generating RSA private key, 2048 bit long modulus
|
||||||
|
// e is 65537 (0x010001)
|
||||||
|
// => 0x010001 below. Also 0x31415352 = "RSA1"
|
||||||
|
{ 0x31415352, sizeof(pbMyPubKey.Modulus) * 8, 0x010001 },
|
||||||
|
{ 0 } // Modulus is initialized below
|
||||||
|
};
|
||||||
|
USHORT dwMyPubKeyLen = sizeof(pbMyPubKey);
|
||||||
|
BOOL r;
|
||||||
|
BYTE t;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
// Get a handle to the default PROV_RSA_AES provider (AES so we get SHA-256 support).
|
||||||
|
// 2 passes in case we need to create a new container.
|
||||||
|
r = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT);
|
||||||
|
if (!r) {
|
||||||
|
uprintf("PKI: Could not create the default key container: %s", WinPKIErrorString());
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse the modulus bytes from openssl (and also remove the extra unwanted 0x00)
|
||||||
|
assert(sizeof(rsa_pubkey_modulus) >= sizeof(pbMyPubKey.Modulus));
|
||||||
|
for (i = 0; i < sizeof(pbMyPubKey.Modulus); i++)
|
||||||
|
pbMyPubKey.Modulus[i] = rsa_pubkey_modulus[sizeof(rsa_pubkey_modulus) -1 - i];
|
||||||
|
|
||||||
|
// Import our RSA public key so that the MS API can use it
|
||||||
|
r = CryptImportKey(hProv, (BYTE*)&pbMyPubKey.BlobHeader, dwMyPubKeyLen, 0, 0, &hPubKey);
|
||||||
|
if (!r) {
|
||||||
|
uprintf("Could not import public key: %s", WinPKIErrorString());
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the hash object.
|
||||||
|
r = CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash);
|
||||||
|
if (!r) {
|
||||||
|
uprintf("Could not create empty hash: %s", WinPKIErrorString());
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the cryptographic hash of the buffer.
|
||||||
|
r = CryptHashData(hHash, pbBuffer, dwBufferLen, 0);
|
||||||
|
if (!r) {
|
||||||
|
uprintf("Could not hash data: %s", WinPKIErrorString());
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse the signature bytes
|
||||||
|
for (i = 0, j = dwSigLen - 1; i < j; i++, j--) {
|
||||||
|
t = pbSignature[i];
|
||||||
|
pbSignature[i] = pbSignature[j];
|
||||||
|
pbSignature[j] = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we have all of the public key, hash and signature data in a
|
||||||
|
// format that Microsoft can handle, we can call CryptVerifySignature().
|
||||||
|
r = CryptVerifySignature(hHash, pbSignature, dwSigLen, hPubKey, NULL, 0);
|
||||||
|
if (!r)
|
||||||
|
uprintf("Signature validation failed: %s", WinPKIErrorString());
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (hHash)
|
||||||
|
CryptDestroyHash(hHash);
|
||||||
|
if (hProv)
|
||||||
|
CryptReleaseContext(hProv, 0);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
16
src/rufus.c
16
src/rufus.c
|
@ -1567,7 +1567,7 @@ static BOOL BootCheck(void)
|
||||||
IGNORE_RETVAL(_chdir(tmp));
|
IGNORE_RETVAL(_chdir(tmp));
|
||||||
static_sprintf(tmp, "%s/%s-%s/%s", FILES_URL, grub, img_report.grub2_version, core_img);
|
static_sprintf(tmp, "%s/%s-%s/%s", FILES_URL, grub, img_report.grub2_version, core_img);
|
||||||
PromptOnError = FALSE;
|
PromptOnError = FALSE;
|
||||||
grub2_len = (long)DownloadFile(tmp, core_img, hMainDialog);
|
grub2_len = (long)DownloadSignedFile(tmp, core_img, hMainDialog);
|
||||||
PromptOnError = TRUE;
|
PromptOnError = TRUE;
|
||||||
if ((grub2_len == 0) && (DownloadStatus == 404)) {
|
if ((grub2_len == 0) && (DownloadStatus == 404)) {
|
||||||
// Couldn't locate the file on the server => try to download without the version extra
|
// Couldn't locate the file on the server => try to download without the version extra
|
||||||
|
@ -1578,7 +1578,7 @@ static BOOL BootCheck(void)
|
||||||
tmp2[i] = 0;
|
tmp2[i] = 0;
|
||||||
static_sprintf(tmp, "%s/%s-%s/%s", FILES_URL, grub, tmp2, core_img);
|
static_sprintf(tmp, "%s/%s-%s/%s", FILES_URL, grub, tmp2, core_img);
|
||||||
PromptOnError = FALSE;
|
PromptOnError = FALSE;
|
||||||
grub2_len = (long)DownloadFile(tmp, core_img, hMainDialog);
|
grub2_len = (long)DownloadSignedFile(tmp, core_img, hMainDialog);
|
||||||
PromptOnError = TRUE;
|
PromptOnError = TRUE;
|
||||||
static_sprintf(tmp, "%s/%s-%s/%s", FILES_URL, grub, img_report.grub2_version, core_img);
|
static_sprintf(tmp, "%s/%s-%s/%s", FILES_URL, grub, img_report.grub2_version, core_img);
|
||||||
}
|
}
|
||||||
|
@ -1624,7 +1624,7 @@ static BOOL BootCheck(void)
|
||||||
static_sprintf(tmp, "%s-%s", syslinux, embedded_sl_version_str[0]);
|
static_sprintf(tmp, "%s-%s", syslinux, embedded_sl_version_str[0]);
|
||||||
IGNORE_RETVAL(_mkdir(tmp));
|
IGNORE_RETVAL(_mkdir(tmp));
|
||||||
static_sprintf(tmp, "%s/%s-%s/%s", FILES_URL, syslinux, embedded_sl_version_str[0], old_c32_name[i]);
|
static_sprintf(tmp, "%s/%s-%s/%s", FILES_URL, syslinux, embedded_sl_version_str[0], old_c32_name[i]);
|
||||||
len = DownloadFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog);
|
len = DownloadSignedFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog);
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
uprintf("Could not download file - cancelling");
|
uprintf("Could not download file - cancelling");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -1672,14 +1672,14 @@ static BOOL BootCheck(void)
|
||||||
static_sprintf(tmp, "%s/%s-%s%s/%s.%s", FILES_URL, syslinux, img_report.sl_version_str,
|
static_sprintf(tmp, "%s/%s-%s%s/%s.%s", FILES_URL, syslinux, img_report.sl_version_str,
|
||||||
img_report.sl_version_ext, ldlinux, ldlinux_ext[i]);
|
img_report.sl_version_ext, ldlinux, ldlinux_ext[i]);
|
||||||
PromptOnError = (*img_report.sl_version_ext == 0);
|
PromptOnError = (*img_report.sl_version_ext == 0);
|
||||||
syslinux_ldlinux_len[i] = DownloadFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog);
|
syslinux_ldlinux_len[i] = DownloadSignedFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog);
|
||||||
PromptOnError = TRUE;
|
PromptOnError = TRUE;
|
||||||
if ((syslinux_ldlinux_len[i] == 0) && (DownloadStatus == 404) && (*img_report.sl_version_ext != 0)) {
|
if ((syslinux_ldlinux_len[i] == 0) && (DownloadStatus == 404) && (*img_report.sl_version_ext != 0)) {
|
||||||
// Couldn't locate the file on the server => try to download without the version extra
|
// Couldn't locate the file on the server => try to download without the version extra
|
||||||
uprintf("Extended version was not found, trying main version...");
|
uprintf("Extended version was not found, trying main version...");
|
||||||
static_sprintf(tmp, "%s/%s-%s/%s.%s", FILES_URL, syslinux, img_report.sl_version_str,
|
static_sprintf(tmp, "%s/%s-%s/%s.%s", FILES_URL, syslinux, img_report.sl_version_str,
|
||||||
ldlinux, ldlinux_ext[i]);
|
ldlinux, ldlinux_ext[i]);
|
||||||
syslinux_ldlinux_len[i] = DownloadFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog);
|
syslinux_ldlinux_len[i] = DownloadSignedFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog);
|
||||||
if (syslinux_ldlinux_len[i] != 0) {
|
if (syslinux_ldlinux_len[i] != 0) {
|
||||||
// Duplicate the file so that the user won't be prompted to download again
|
// Duplicate the file so that the user won't be prompted to download again
|
||||||
static_sprintf(tmp, "%s-%s\\%s.%s", syslinux, img_report.sl_version_str, ldlinux, ldlinux_ext[i]);
|
static_sprintf(tmp, "%s-%s\\%s.%s", syslinux, img_report.sl_version_str, ldlinux, ldlinux_ext[i]);
|
||||||
|
@ -1722,7 +1722,7 @@ static BOOL BootCheck(void)
|
||||||
static_sprintf(tmp, "%s-%s", syslinux, embedded_sl_version_str[1]);
|
static_sprintf(tmp, "%s-%s", syslinux, embedded_sl_version_str[1]);
|
||||||
IGNORE_RETVAL(_mkdir(tmp));
|
IGNORE_RETVAL(_mkdir(tmp));
|
||||||
static_sprintf(tmp, "%s/%s-%s/%s.%s", FILES_URL, syslinux, embedded_sl_version_str[1], ldlinux, ldlinux_ext[2]);
|
static_sprintf(tmp, "%s/%s-%s/%s.%s", FILES_URL, syslinux, embedded_sl_version_str[1], ldlinux, ldlinux_ext[2]);
|
||||||
if (DownloadFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog) == 0)
|
if (DownloadSignedFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog) == 0)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1752,7 +1752,7 @@ static BOOL BootCheck(void)
|
||||||
static_sprintf(tmp, "grub4dos-%s", GRUB4DOS_VERSION);
|
static_sprintf(tmp, "grub4dos-%s", GRUB4DOS_VERSION);
|
||||||
IGNORE_RETVAL(_mkdir(tmp));
|
IGNORE_RETVAL(_mkdir(tmp));
|
||||||
static_sprintf(tmp, "%s/grub4dos-%s/grldr", FILES_URL, GRUB4DOS_VERSION);
|
static_sprintf(tmp, "%s/grub4dos-%s/grldr", FILES_URL, GRUB4DOS_VERSION);
|
||||||
if (DownloadFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog) == 0)
|
if (DownloadSignedFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog) == 0)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2978,7 +2978,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
#ifdef RUFUS_TEST
|
#ifdef RUFUS_TEST
|
||||||
if (LOWORD(wParam) == IDC_TEST) {
|
if (LOWORD(wParam) == IDC_TEST) {
|
||||||
Notification(MSG_ERROR, NULL, lmprintf(MSG_042), lmprintf(MSG_043, lmprintf(MSG_055)));
|
DownloadSignedFile(FILES_URL "/gendb.sh", "C:\\Downloads\\gendb.sh", hProgress);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
#define FAT32_CLUSTER_THRESHOLD 1.011f // For FAT32, cluster size changes don't occur at power of 2 boundaries but sligthly above
|
#define FAT32_CLUSTER_THRESHOLD 1.011f // For FAT32, cluster size changes don't occur at power of 2 boundaries but sligthly above
|
||||||
#define DD_BUFFER_SIZE 65536 // Minimum size of the buffer we use for DD operations
|
#define DD_BUFFER_SIZE 65536 // Minimum size of the buffer we use for DD operations
|
||||||
#define UBUFFER_SIZE 2048
|
#define UBUFFER_SIZE 2048
|
||||||
|
#define RSA_SIGNATURE_SIZE 256
|
||||||
#define CBN_SELCHANGE_INTERNAL (CBN_SELCHANGE + 256)
|
#define CBN_SELCHANGE_INTERNAL (CBN_SELCHANGE + 256)
|
||||||
#define RUFUS_URL "https://rufus.ie"
|
#define RUFUS_URL "https://rufus.ie"
|
||||||
#define DOWNLOAD_URL RUFUS_URL "/downloads"
|
#define DOWNLOAD_URL RUFUS_URL "/downloads"
|
||||||
|
@ -475,8 +476,8 @@ extern BOOL ResetDevice(int index);
|
||||||
extern BOOL GetOpticalMedia(IMG_SAVE* img_save);
|
extern BOOL GetOpticalMedia(IMG_SAVE* img_save);
|
||||||
extern BOOL SetLGP(BOOL bRestore, BOOL* bExistingKey, const char* szPath, const char* szPolicy, DWORD dwValue);
|
extern BOOL SetLGP(BOOL bRestore, BOOL* bExistingKey, const char* szPath, const char* szPolicy, DWORD dwValue);
|
||||||
extern LONG GetEntryWidth(HWND hDropDown, const char* entry);
|
extern LONG GetEntryWidth(HWND hDropDown, const char* entry);
|
||||||
extern DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog);
|
extern DWORD DownloadSignedFile(const char* url, const char* file, HWND hProgressDialog);
|
||||||
extern HANDLE DownloadFileThreaded(const char* url, const char* file, HWND hProgressDialog);
|
extern HANDLE DownloadSignedFileThreaded(const char* url, const char* file, HWND hProgressDialog);
|
||||||
extern INT_PTR CALLBACK UpdateCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
extern INT_PTR CALLBACK UpdateCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||||
extern BOOL SetUpdateCheck(void);
|
extern BOOL SetUpdateCheck(void);
|
||||||
extern BOOL CheckForUpdates(BOOL force);
|
extern BOOL CheckForUpdates(BOOL force);
|
||||||
|
@ -503,6 +504,7 @@ 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);
|
||||||
extern uint64_t GetSignatureTimeStamp(const char* path);
|
extern uint64_t GetSignatureTimeStamp(const char* path);
|
||||||
extern LONG ValidateSignature(HWND hDlg, const char* path);
|
extern LONG ValidateSignature(HWND hDlg, const char* path);
|
||||||
|
extern BOOL ValidateOpensslSignature(BYTE* pbBuffer, DWORD dwBufferLen, BYTE* pbSignature, DWORD dwSigLen);
|
||||||
extern BOOL IsFontAvailable(const char* font_name);
|
extern BOOL IsFontAvailable(const char* font_name);
|
||||||
extern BOOL WriteFileWithRetry(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
|
extern BOOL WriteFileWithRetry(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
|
||||||
LPDWORD lpNumberOfBytesWritten, DWORD nNumRetries);
|
LPDWORD lpNumberOfBytesWritten, DWORD nNumRetries);
|
||||||
|
|
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 3.2.1324"
|
CAPTION "Rufus 3.2.1325"
|
||||||
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
|
||||||
|
@ -389,8 +389,8 @@ END
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 3,2,1324,0
|
FILEVERSION 3,2,1325,0
|
||||||
PRODUCTVERSION 3,2,1324,0
|
PRODUCTVERSION 3,2,1325,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -407,13 +407,13 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)"
|
VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)"
|
||||||
VALUE "FileDescription", "Rufus"
|
VALUE "FileDescription", "Rufus"
|
||||||
VALUE "FileVersion", "3.2.1324"
|
VALUE "FileVersion", "3.2.1325"
|
||||||
VALUE "InternalName", "Rufus"
|
VALUE "InternalName", "Rufus"
|
||||||
VALUE "LegalCopyright", "© 2011-2018 Pete Batard (GPL v3)"
|
VALUE "LegalCopyright", "© 2011-2018 Pete Batard (GPL v3)"
|
||||||
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
|
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
|
||||||
VALUE "OriginalFilename", "rufus.exe"
|
VALUE "OriginalFilename", "rufus.exe"
|
||||||
VALUE "ProductName", "Rufus"
|
VALUE "ProductName", "Rufus"
|
||||||
VALUE "ProductVersion", "3.2.1324"
|
VALUE "ProductVersion", "3.2.1325"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -1630,7 +1630,7 @@ INT_PTR CALLBACK NewVersionCallback(HWND hDlg, UINT message, WPARAM wParam, LPAR
|
||||||
si.cb = sizeof(si);
|
si.cb = sizeof(si);
|
||||||
if (!CreateProcessU(filepath, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
|
if (!CreateProcessU(filepath, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
|
||||||
PrintInfo(0, MSG_214);
|
PrintInfo(0, MSG_214);
|
||||||
uprintf("Failed to launch new application: %s\n", WindowsErrorString());
|
uprintf("Failed to launch new application: %s", WindowsErrorString());
|
||||||
} else {
|
} else {
|
||||||
PrintInfo(0, MSG_213);
|
PrintInfo(0, MSG_213);
|
||||||
PostMessage(hDlg, WM_COMMAND, (WPARAM)IDCLOSE, 0);
|
PostMessage(hDlg, WM_COMMAND, (WPARAM)IDCLOSE, 0);
|
||||||
|
@ -1639,19 +1639,19 @@ INT_PTR CALLBACK NewVersionCallback(HWND hDlg, UINT message, WPARAM wParam, LPAR
|
||||||
break;
|
break;
|
||||||
default: // Download
|
default: // Download
|
||||||
if (update.download_url == NULL) {
|
if (update.download_url == NULL) {
|
||||||
uprintf("Could not get download URL\n");
|
uprintf("Could not get download URL");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (i=(int)strlen(update.download_url); (i>0)&&(update.download_url[i]!='/'); i--);
|
for (i=(int)strlen(update.download_url); (i>0)&&(update.download_url[i]!='/'); i--);
|
||||||
dl_ext.filename = &update.download_url[i+1];
|
dl_ext.filename = &update.download_url[i+1];
|
||||||
filepath = FileDialog(TRUE, app_dir, &dl_ext, OFN_NOCHANGEDIR);
|
filepath = FileDialog(TRUE, app_dir, &dl_ext, OFN_NOCHANGEDIR);
|
||||||
if (filepath == NULL) {
|
if (filepath == NULL) {
|
||||||
uprintf("Could not get save path\n");
|
uprintf("Could not get save path");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Opening the File Dialog will make us lose tabbing focus - set it back
|
// Opening the File Dialog will make us lose tabbing focus - set it back
|
||||||
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDC_DOWNLOAD), TRUE);
|
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDC_DOWNLOAD), TRUE);
|
||||||
DownloadFileThreaded(update.download_url, filepath, hDlg);
|
DownloadSignedFileThreaded(update.download_url, filepath, hDlg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return (INT_PTR)TRUE;
|
return (INT_PTR)TRUE;
|
||||||
|
|
Loading…
Reference in a new issue