From 1e01918302558828b91a697dc020224817d52877 Mon Sep 17 00:00:00 2001 From: Ziliang Guo Date: Fri, 25 Mar 2016 02:10:08 +0000 Subject: [PATCH] Additional rapps sync involving SSL certs. Porting done by Mark Jensen. Original code from Ismael Ferreras Morezuelas. CORE-10986 svn path=/trunk/; revision=71044 --- .../applications/rapps_new/CMakeLists.txt | 1 + .../base/applications/rapps_new/loaddlg.cpp | 273 ++++++++++++++++-- reactos/base/applications/rapps_new/rapps.h | 8 +- 3 files changed, 250 insertions(+), 32 deletions(-) diff --git a/reactos/base/applications/rapps_new/CMakeLists.txt b/reactos/base/applications/rapps_new/CMakeLists.txt index 2c4a95f144d..6eca7d8f9b8 100644 --- a/reactos/base/applications/rapps_new/CMakeLists.txt +++ b/reactos/base/applications/rapps_new/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND SOURCE winmain.cpp rapps.h) +add_definitions(-DUSE_CERT_PINNING) file(GLOB_RECURSE rapps_new_rc_deps res/*.*) add_rc_deps(rapps.rc ${rapps_new_rc_deps}) add_executable(rapps_new ${SOURCE} rapps.rc) diff --git a/reactos/base/applications/rapps_new/loaddlg.cpp b/reactos/base/applications/rapps_new/loaddlg.cpp index 3ce0aef7833..cc14e6c7838 100644 --- a/reactos/base/applications/rapps_new/loaddlg.cpp +++ b/reactos/base/applications/rapps_new/loaddlg.cpp @@ -6,6 +6,7 @@ * Copyright 2004 Mike McCormack (for CodeWeavers) * Copyright 2005 Ge van Geldorp (gvg@reactos.org) * Copyright 2009 Dmitry Chapyshev (dmitry@reactos.org) + * Copyright 2015 Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com) */ /* * Based on Wine dlls/shdocvw/shdocvw_main.c @@ -34,6 +35,7 @@ #include #include #include +#include static PAPPLICATION_INFO AppInfo; @@ -43,6 +45,9 @@ class CDownloadDialog : { HWND m_hDialog; PBOOL m_pbCancelled; + BOOL m_UrlHasBeenCopied; + WCHAR m_ProgressText[MAX_PATH]; + public: ~CDownloadDialog() @@ -54,6 +59,7 @@ public: { m_hDialog = Dlg; m_pbCancelled = pbCancelled; + m_UrlHasBeenCopied = FALSE; return S_OK; } @@ -84,21 +90,55 @@ public: { HWND Item; LONG r; - WCHAR OldText[100]; Item = GetDlgItem(m_hDialog, IDC_DOWNLOAD_PROGRESS); if (Item && ulProgressMax) { - SendMessageW(Item, PBM_SETPOS, MulDiv(ulProgress, 100, ulProgressMax), 0); + WCHAR szProgress[100]; + WCHAR szProgressMax[100]; + UINT uiPercentage = ((ULONGLONG)ulProgress * 100) / ulProgressMax; + + /* send the current progress to the progress bar */ + SendMessageW(Item, PBM_SETPOS, uiPercentage, 0); + + /* format the bits and bytes into pretty and accesible units... */ + StrFormatByteSizeW(ulProgress, szProgress, _countof(szProgress)); + StrFormatByteSizeW(ulProgressMax, szProgressMax, _countof(szProgressMax)); + + /* ...and post all of it to our subclassed progress bar text subroutine */ + StringCbPrintfW(m_ProgressText, + sizeof(m_ProgressText), + L"%u%% \x2014 %ls / %ls", + uiPercentage, + szProgress, + szProgressMax); + SendMessageW(Item, WM_SETTEXT, 0, (LPARAM)m_ProgressText); } Item = GetDlgItem(m_hDialog, IDC_DOWNLOAD_STATUS); - if (Item && szStatusText) + if (Item && szStatusText && wcslen(szStatusText) > 0 && m_UrlHasBeenCopied == FALSE) { - SendMessageW(Item, WM_GETTEXT, sizeof(OldText) / sizeof(OldText[0]), (LPARAM) OldText); - if (sizeof(OldText) / sizeof(OldText[0]) - 1 <= wcslen(OldText) || 0 != wcscmp(OldText, szStatusText)) + DWORD len = wcslen(szStatusText) + 1; + PWSTR buf = (PWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR)); + + if (buf) { - SendMessageW(Item, WM_SETTEXT, 0, (LPARAM) szStatusText); + /* beautify our url for display purposes */ + InternetCanonicalizeUrl(szStatusText, buf, &len, ICU_DECODE | ICU_NO_ENCODE); + } + else + { + /* just use the original */ + buf = (PWSTR)szStatusText; + } + + /* paste it into our dialog and don't do it again in this instance */ + SendMessageW(Item, WM_SETTEXT, 0, (LPARAM)buf); + m_UrlHasBeenCopied = TRUE; + + if (buf != szStatusText) + { + HeapFree(GetProcessHeap(), 0, buf); } } @@ -154,13 +194,67 @@ HRESULT WINAPI CDownloadDialog_Constructor(HWND Dlg, BOOL *pbCancelled, REFIID r return ShellObjectCreatorInit(Dlg, pbCancelled, riid, ppv); } +#ifdef USE_CERT_PINNING +static BOOL CertIsValid(HINTERNET hInternet, LPWSTR lpszHostName) +{ + HINTERNET hConnect; + HINTERNET hRequest; + DWORD certInfoLength; + BOOL Ret = FALSE; + INTERNET_CERTIFICATE_INFOW certInfo; + + hConnect = InternetConnectW(hInternet, lpszHostName, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, INTERNET_FLAG_SECURE, 0); + if (hConnect) + { + hRequest = HttpOpenRequestW(hConnect, L"HEAD", NULL, NULL, NULL, NULL, INTERNET_FLAG_SECURE, 0); + if (hRequest != NULL) + { + Ret = HttpSendRequestW(hRequest, L"", 0, NULL, 0); + if (Ret) + { + certInfoLength = sizeof(INTERNET_CERTIFICATE_INFOW); + Ret = InternetQueryOptionW(hRequest, + INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, + &certInfo, + &certInfoLength); + if (Ret) + { + if (certInfo.lpszEncryptionAlgName) + LocalFree(certInfo.lpszEncryptionAlgName); + if (certInfo.lpszIssuerInfo) + { + if (strcmp((LPSTR)certInfo.lpszIssuerInfo, CERT_ISSUER_INFO) != 0) + Ret = FALSE; + LocalFree(certInfo.lpszIssuerInfo); + } + if (certInfo.lpszProtocolName) + LocalFree(certInfo.lpszProtocolName); + if (certInfo.lpszSignatureAlgName) + LocalFree(certInfo.lpszSignatureAlgName); + if (certInfo.lpszSubjectInfo) + { + if (strcmp((LPSTR)certInfo.lpszSubjectInfo, CERT_SUBJECT_INFO) != 0) + Ret = FALSE; + LocalFree(certInfo.lpszSubjectInfo); + } + } + } + InternetCloseHandle(hRequest); + } + InternetCloseHandle(hConnect); + } + return Ret; +} +#endif + + static DWORD WINAPI ThreadFunc(LPVOID Context) { CComPtr dl; WCHAR path[MAX_PATH]; - LPWSTR p; + PWSTR p, q; HWND Dlg = (HWND) Context; ULONG dwContentLen, dwBytesWritten, dwBytesRead, dwStatus; ULONG dwCurrentBytesRead = 0; @@ -174,36 +268,48 @@ ThreadFunc(LPVOID Context) unsigned char lpBuffer[4096]; PCWSTR lpszAgent = L"RApps/1.0"; URL_COMPONENTS urlComponents; - size_t urlLength; + size_t urlLength, filenameLength; - /* built the path for the download */ + /* build the path for the download */ p = wcsrchr(AppInfo->szUrlDownload, L'/'); - + q = wcsrchr(AppInfo->szUrlDownload, L'?'); + + /* do we have a final slash separator? */ if (!p) goto end; - if (wcscmp(AppInfo->szUrlDownload, APPLICATION_DATABASE_URL) == 0) - { - bCab = TRUE; - if (!GetStorageDirectory(path, sizeof(path) / sizeof(path[0]))) - goto end; - } - else - { - if (FAILED(StringCbCopyW(path, sizeof(path), SettingsInfo.szDownloadDir))) - goto end; - } + /* prepare the tentative length of the filename, maybe we've to remove part of it later on */ + filenameLength = wcslen(p) * sizeof(WCHAR); + /* do we have query arguments in the target URL after the filename? account for them + (e.g. https://example.org/myfile.exe?no_adware_plz) */ + if (q && q > p && (q - p) > 0) + filenameLength -= wcslen(q - 1) * sizeof(WCHAR); + /* is this URL an update package for RAPPS? if so store it in a different place */ + if (wcscmp(AppInfo->szUrlDownload, APPLICATION_DATABASE_URL) == 0) + { + bCab = TRUE; + if (!GetStorageDirectory(path, _countof(path))) + goto end; + } + else + { + if (FAILED(StringCbCopyW(path, sizeof(path), SettingsInfo.szDownloadDir))) + goto end; + } + + /* is the path valid? can we access it? */ if (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES) { if (!CreateDirectoryW(path, NULL)) goto end; } + /* append a \ to the provided file system path, and the filename portion from the URL after that */ if (FAILED(StringCbCatW(path, sizeof(path), L"\\"))) goto end; - if (FAILED(StringCbCatW(path, sizeof(path), p + 1))) + if (FAILED(StringCbCatNW(path, sizeof(path), p + 1, filenameLength))) goto end; if (!bCab && AppInfo->szSHA1[0] != 0 && GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) @@ -220,6 +326,7 @@ ThreadFunc(LPVOID Context) if (dl == NULL) goto end; + /* FIXME: this should just be using the system-wide proxy settings */ switch(SettingsInfo.Proxy) { case 0: /* preconfig */ @@ -264,10 +371,13 @@ ThreadFunc(LPVOID Context) if(FAILED(StringCbLengthW(AppInfo->szUrlDownload, sizeof(AppInfo->szUrlDownload), &urlLength))) goto end; - - urlComponents.dwSchemeLength = urlLength*sizeof(WCHAR); - urlComponents.lpszScheme = (PWSTR)malloc(urlComponents.dwSchemeLength); - + + urlLength /= sizeof(WCHAR); + urlComponents.dwSchemeLength = urlLength + 1; + urlComponents.lpszScheme = (LPWSTR)malloc(urlComponents.dwSchemeLength * sizeof(WCHAR)); + urlComponents.dwHostNameLength = urlLength + 1; + urlComponents.lpszHostName = (LPWSTR)malloc(urlComponents.dwHostNameLength * sizeof(WCHAR)); + if(!InternetCrackUrlW(AppInfo->szUrlDownload, urlLength+1, ICU_DECODE | ICU_ESCAPE, &urlComponents)) goto end; @@ -277,7 +387,24 @@ ThreadFunc(LPVOID Context) if(urlComponents.nScheme == INTERNET_SCHEME_FTP) dwContentLen = FtpGetFileSize(hFile, &dwStatus); +#ifdef USE_CERT_PINNING + /* are we using HTTPS to download the RAPPS update package? check if the certificate is original */ + if ((urlComponents.nScheme == INTERNET_SCHEME_HTTPS) && + (wcscmp(AppInfo->szUrlDownload, APPLICATION_DATABASE_URL) == 0) && + (!CertIsValid(hOpen, urlComponents.lpszHostName))) + { + WCHAR szMsgText[MAX_STR_LEN]; + + if (!LoadStringW(hInst, IDS_CERT_DOES_NOT_MATCH, szMsgText, sizeof(szMsgText) / sizeof(WCHAR))) + goto end; + + MessageBoxW(Dlg, szMsgText, NULL, MB_OK | MB_ICONERROR); + goto end; + } +#endif + free(urlComponents.lpszScheme); + free(urlComponents.lpszHostName); hOut = CreateFileW(path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL); @@ -291,7 +418,7 @@ ThreadFunc(LPVOID Context) dwCurrentBytesRead += dwBytesRead; dl->OnProgress(dwCurrentBytesRead, dwContentLen, 0, AppInfo->szUrlDownload); } - while (dwBytesRead); + while (dwBytesRead && !bCancelled); CloseHandle(hOut); hOut = INVALID_HANDLE_VALUE; @@ -347,15 +474,92 @@ end: return 0; } + +LRESULT CALLBACK +DownloadProgressProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) +{ + static WCHAR szProgressText[MAX_STR_LEN] = {0}; + + switch (uMsg) + { + case WM_SETTEXT: + { + if (lParam) + { + StringCbCopyW(szProgressText, + sizeof(szProgressText), + (PCWSTR)lParam); + } + } + + case WM_ERASEBKGND: + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hDC = BeginPaint(hWnd, &ps), hdcMem; + HBITMAP hbmMem; + HANDLE hOld; + RECT myRect; + UINT win_width, win_height; + + GetClientRect(hWnd, &myRect); + + /* grab the progress bar rect size */ + win_width = myRect.right - myRect.left; + win_height = myRect.bottom - myRect.top; + + /* create an off-screen DC for double-buffering */ + hdcMem = CreateCompatibleDC(hDC); + hbmMem = CreateCompatibleBitmap(hDC, win_width, win_height); + + hOld = SelectObject(hdcMem, hbmMem); + + /* call the original draw code and redirect it to our memory buffer */ + DefSubclassProc(hWnd, uMsg, (WPARAM)hdcMem, lParam); + + /* draw our nifty progress text over it */ + SelectFont(hdcMem, GetStockFont(DEFAULT_GUI_FONT)); + DrawShadowText(hdcMem, szProgressText, wcslen(szProgressText), + &myRect, + DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE, + GetSysColor(COLOR_CAPTIONTEXT), + GetSysColor(COLOR_3DSHADOW), + 1, 1); + + /* transfer the off-screen DC to the screen */ + BitBlt(hDC, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY); + + /* free the off-screen DC */ + SelectObject(hdcMem, hOld); + DeleteObject(hbmMem); + DeleteDC(hdcMem); + + EndPaint(hWnd, &ps); + return 0; + } + + /* Raymond Chen says that we should safely unsubclass all the things! + (http://blogs.msdn.com/b/oldnewthing/archive/2003/11/11/55653.aspx) */ + case WM_NCDESTROY: + { + ZeroMemory(szProgressText, sizeof(szProgressText)); + RemoveWindowSubclass(hWnd, DownloadProgressProc, uIdSubclass); + } + + default: + return DefSubclassProc(hWnd, uMsg, wParam, lParam); + } +} + static INT_PTR CALLBACK -DownloadDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam) +DownloadDlgProc(HWND Dlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HANDLE Thread; DWORD ThreadId; HWND Item; - switch (Msg) + switch (uMsg) { case WM_INITDIALOG: { @@ -374,12 +578,21 @@ DownloadDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam) Item = GetDlgItem(Dlg, IDC_DOWNLOAD_PROGRESS); if (Item) { + /* initialize the default values for our nifty progress bar + and subclass it so that it learns to print a status text */ SendMessageW(Item, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); SendMessageW(Item, PBM_SETPOS, 0, 0); + + SetWindowSubclass(Item, DownloadProgressProc, 0, 0); } + /* add a neat placeholder until the download URL is retrieved */ + Item = GetDlgItem(Dlg, IDC_DOWNLOAD_STATUS); + SendMessageW(Item, WM_SETTEXT, 0, (LPARAM)L"\x2022 \x2022 \x2022"); + Thread = CreateThread(NULL, 0, ThreadFunc, Dlg, 0, &ThreadId); - if (!Thread) return FALSE; + if (!Thread) + return FALSE; CloseHandle(Thread); return TRUE; } diff --git a/reactos/base/applications/rapps_new/rapps.h b/reactos/base/applications/rapps_new/rapps.h index c0f60dcd42e..539f3c3bfd6 100644 --- a/reactos/base/applications/rapps_new/rapps.h +++ b/reactos/base/applications/rapps_new/rapps.h @@ -28,8 +28,12 @@ #include "resource.h" -/* FIXME: this should be downloaded by HTTPS once is supported */ -#define APPLICATION_DATABASE_URL L"http://svn.reactos.org/packages/rappmgr.cab" +#ifdef USE_CERT_PINNING + #define CERT_ISSUER_INFO "BE\r\nGlobalSign nv-sa\r\nGlobalSign Domain Validation CA - SHA256 - G2" + #define CERT_SUBJECT_INFO "Domain Control Validated\r\n*.reactos.org" +#endif + +#define APPLICATION_DATABASE_URL L"https://svn.reactos.org/packages/rappmgr.cab" #define SPLIT_WIDTH 4 #define MAX_STR_LEN 256 -- 2.17.1