1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/other-licenses/nsis/Contrib/CertCheck/CertCheck.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,385 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include <stdio.h> 1.9 +#include <stdlib.h> 1.10 +#include <windows.h> 1.11 +#include <softpub.h> 1.12 +#include <wintrust.h> 1.13 + 1.14 +#pragma comment(lib, "wintrust.lib") 1.15 +#pragma comment(lib, "crypt32.lib") 1.16 + 1.17 +#ifdef UNICODE 1.18 + 1.19 +#ifndef _T 1.20 +#define __T(x) L ## x 1.21 +#define _T(x) __T(x) 1.22 +#define _TEXT(x) __T(x) 1.23 +#endif 1.24 + 1.25 +#else 1.26 + 1.27 +#ifndef _T 1.28 +#define _T(x) x 1.29 +#define _TEXT(x) x 1.30 +#endif 1.31 + 1.32 +#endif // UNICODE 1.33 + 1.34 +static const int ENCODING = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; 1.35 + 1.36 +typedef struct _stack_t { 1.37 + struct _stack_t *next; 1.38 + TCHAR text[MAX_PATH]; 1.39 +} stack_t; 1.40 + 1.41 +int popstring(stack_t **stacktop, LPTSTR str, int len); 1.42 +void pushstring(stack_t **stacktop, LPCTSTR str, int len); 1.43 + 1.44 +struct CertificateCheckInfo 1.45 +{ 1.46 + LPCWSTR name; 1.47 + LPCWSTR issuer; 1.48 +}; 1.49 + 1.50 +/** 1.51 + * Checks to see if a file stored at filePath matches the specified info. This 1.52 + * only supports the name and issuer attributes currently. 1.53 + * 1.54 + * @param certContext The certificate context of the file 1.55 + * @param infoToMatch The acceptable information to match 1.56 + * @return FALSE if the info does not match or if any error occurs in the check 1.57 + */ 1.58 +BOOL 1.59 +DoCertificateAttributesMatch(PCCERT_CONTEXT certContext, 1.60 + CertificateCheckInfo &infoToMatch) 1.61 +{ 1.62 + DWORD dwData; 1.63 + LPTSTR szName = NULL; 1.64 + 1.65 + // Pass in NULL to get the needed size of the issuer buffer. 1.66 + dwData = CertGetNameString(certContext, 1.67 + CERT_NAME_SIMPLE_DISPLAY_TYPE, 1.68 + CERT_NAME_ISSUER_FLAG, NULL, 1.69 + NULL, 0); 1.70 + 1.71 + if (!dwData) { 1.72 + return FALSE; 1.73 + } 1.74 + 1.75 + // Allocate memory for Issuer name buffer. 1.76 + szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR)); 1.77 + if (!szName) { 1.78 + return FALSE; 1.79 + } 1.80 + 1.81 + // Get Issuer name. 1.82 + if (!CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 1.83 + CERT_NAME_ISSUER_FLAG, NULL, szName, dwData)) { 1.84 + LocalFree(szName); 1.85 + return FALSE; 1.86 + } 1.87 + 1.88 + // If the issuer does not match, return a failure. 1.89 + if (!infoToMatch.issuer || 1.90 + wcscmp(szName, infoToMatch.issuer)) { 1.91 + LocalFree(szName); 1.92 + return FALSE; 1.93 + } 1.94 + 1.95 + LocalFree(szName); 1.96 + szName = NULL; 1.97 + 1.98 + // Pass in NULL to get the needed size of the name buffer. 1.99 + dwData = CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 1.100 + 0, NULL, NULL, 0); 1.101 + if (!dwData) { 1.102 + return FALSE; 1.103 + } 1.104 + 1.105 + // Allocate memory for the name buffer. 1.106 + szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR)); 1.107 + if (!szName) { 1.108 + return FALSE; 1.109 + } 1.110 + 1.111 + // Obtain the name. 1.112 + if (!(CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, 1.113 + NULL, szName, dwData))) { 1.114 + LocalFree(szName); 1.115 + return FALSE; 1.116 + } 1.117 + 1.118 + // If the issuer does not match, return a failure. 1.119 + if (!infoToMatch.name || 1.120 + wcscmp(szName, infoToMatch.name)) { 1.121 + LocalFree(szName); 1.122 + return FALSE; 1.123 + } 1.124 + 1.125 + // We have a match! 1.126 + LocalFree(szName); 1.127 + 1.128 + // If there were any errors we would have aborted by now. 1.129 + return TRUE; 1.130 +} 1.131 + 1.132 +/** 1.133 + * Checks to see if a file stored at filePath matches the specified info. This 1.134 + * only supports the name and issuer attributes currently. 1.135 + * 1.136 + * @param filePath The PE file path to check 1.137 + * @param infoToMatch The acceptable information to match 1.138 + * @return ERROR_SUCCESS if successful, ERROR_NOT_FOUND if the info 1.139 + * does not match, or the last error otherwise. 1.140 + */ 1.141 +DWORD 1.142 +CheckCertificateForPEFile(LPCWSTR filePath, 1.143 + CertificateCheckInfo &infoToMatch) 1.144 +{ 1.145 + HCERTSTORE certStore = NULL; 1.146 + HCRYPTMSG cryptMsg = NULL; 1.147 + PCCERT_CONTEXT certContext = NULL; 1.148 + PCMSG_SIGNER_INFO signerInfo = NULL; 1.149 + DWORD lastError = ERROR_SUCCESS; 1.150 + 1.151 + // Get the HCERTSTORE and HCRYPTMSG from the signed file. 1.152 + DWORD encoding, contentType, formatType; 1.153 + BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE, 1.154 + filePath, 1.155 + CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, 1.156 + CERT_QUERY_CONTENT_FLAG_ALL, 1.157 + 0, &encoding, &contentType, 1.158 + &formatType, &certStore, &cryptMsg, NULL); 1.159 + if (!result) { 1.160 + lastError = GetLastError(); 1.161 + goto cleanup; 1.162 + } 1.163 + 1.164 + // Pass in NULL to get the needed signer information size. 1.165 + DWORD signerInfoSize; 1.166 + result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, 1.167 + NULL, &signerInfoSize); 1.168 + if (!result) { 1.169 + lastError = GetLastError(); 1.170 + goto cleanup; 1.171 + } 1.172 + 1.173 + // Allocate the needed size for the signer information. 1.174 + signerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, signerInfoSize); 1.175 + if (!signerInfo) { 1.176 + lastError = GetLastError(); 1.177 + goto cleanup; 1.178 + } 1.179 + 1.180 + // Get the signer information (PCMSG_SIGNER_INFO). 1.181 + // In particular we want the issuer and serial number. 1.182 + result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, 1.183 + (PVOID)signerInfo, &signerInfoSize); 1.184 + if (!result) { 1.185 + lastError = GetLastError(); 1.186 + goto cleanup; 1.187 + } 1.188 + 1.189 + // Search for the signer certificate in the certificate store. 1.190 + CERT_INFO certInfo; 1.191 + certInfo.Issuer = signerInfo->Issuer; 1.192 + certInfo.SerialNumber = signerInfo->SerialNumber; 1.193 + certContext = CertFindCertificateInStore(certStore, ENCODING, 0, 1.194 + CERT_FIND_SUBJECT_CERT, 1.195 + (PVOID)&certInfo, NULL); 1.196 + if (!certContext) { 1.197 + lastError = GetLastError(); 1.198 + goto cleanup; 1.199 + } 1.200 + 1.201 + if (!DoCertificateAttributesMatch(certContext, infoToMatch)) { 1.202 + lastError = ERROR_NOT_FOUND; 1.203 + goto cleanup; 1.204 + } 1.205 + 1.206 +cleanup: 1.207 + if (signerInfo) { 1.208 + LocalFree(signerInfo); 1.209 + } 1.210 + if (certContext) { 1.211 + CertFreeCertificateContext(certContext); 1.212 + } 1.213 + if (certStore) { 1.214 + CertCloseStore(certStore, 0); 1.215 + } 1.216 + if (cryptMsg) { 1.217 + CryptMsgClose(cryptMsg); 1.218 + } 1.219 + return lastError; 1.220 +} 1.221 + 1.222 +/** 1.223 + * Compares the certificate name and issuer values for a signed file's with the 1.224 + * values provided. 1.225 + * 1.226 + * @param stacktop A pointer to the top of the stack. The stack should contain 1.227 + * from the top the file's path, the expected value for the 1.228 + * certificate's name attribute, and the expected value for 1.229 + * the certificate's issuer attribute. 1.230 + * @param variables A pointer to the NSIS variables 1.231 + * @return 1 if the certificate name and issuer attributes matched the expected 1.232 + * values, 0 if they don't match the expected values. 1.233 + */ 1.234 +extern "C" void __declspec(dllexport) 1.235 +VerifyCertNameIssuer(HWND hwndParent, int string_size, 1.236 + TCHAR *variables, stack_t **stacktop, void *extra) 1.237 +{ 1.238 + TCHAR tmp1[MAX_PATH + 1] = { _T('\0') }; 1.239 + TCHAR tmp2[MAX_PATH + 1] = { _T('\0') }; 1.240 + TCHAR tmp3[MAX_PATH + 1] = { _T('\0') }; 1.241 + WCHAR filePath[MAX_PATH + 1] = { L'\0' }; 1.242 + WCHAR certName[MAX_PATH + 1] = { L'\0' }; 1.243 + WCHAR certIssuer[MAX_PATH + 1] = { L'\0' }; 1.244 + 1.245 + popstring(stacktop, tmp1, MAX_PATH); 1.246 + popstring(stacktop, tmp2, MAX_PATH); 1.247 + popstring(stacktop, tmp3, MAX_PATH); 1.248 + 1.249 +#if !defined(UNICODE) 1.250 + MultiByteToWideChar(CP_ACP, 0, tmp1, -1, filePath, MAX_PATH); 1.251 + MultiByteToWideChar(CP_ACP, 0, tmp2, -1, certName, MAX_PATH); 1.252 + MultiByteToWideChar(CP_ACP, 0, tmp3, -1, certIssuer, MAX_PATH); 1.253 +#else 1.254 + wcsncpy(filePath, tmp1, MAX_PATH); 1.255 + wcsncpy(certName, tmp2, MAX_PATH); 1.256 + wcsncpy(certIssuer, tmp3, MAX_PATH); 1.257 +#endif 1.258 + 1.259 + CertificateCheckInfo allowedCertificate = { 1.260 + certName, 1.261 + certIssuer, 1.262 + }; 1.263 + 1.264 + LONG retCode = CheckCertificateForPEFile(filePath, allowedCertificate); 1.265 + if (retCode == ERROR_SUCCESS) { 1.266 + pushstring(stacktop, TEXT("1"), 2); 1.267 + } else { 1.268 + pushstring(stacktop, TEXT("0"), 2); 1.269 + } 1.270 +} 1.271 + 1.272 +/** 1.273 + * Verifies the trust of a signed file's certificate. 1.274 + * 1.275 + * @param filePath The file path to check. 1.276 + * @return ERROR_SUCCESS if successful, or the last error code otherwise. 1.277 + */ 1.278 +DWORD 1.279 +VerifyCertificateTrustForFile(LPCWSTR filePath) 1.280 +{ 1.281 + // Setup the file to check. 1.282 + WINTRUST_FILE_INFO fileToCheck; 1.283 + ZeroMemory(&fileToCheck, sizeof(fileToCheck)); 1.284 + fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO); 1.285 + fileToCheck.pcwszFilePath = filePath; 1.286 + 1.287 + // Setup what to check, we want to check it is signed and trusted. 1.288 + WINTRUST_DATA trustData; 1.289 + ZeroMemory(&trustData, sizeof(trustData)); 1.290 + trustData.cbStruct = sizeof(trustData); 1.291 + trustData.pPolicyCallbackData = NULL; 1.292 + trustData.pSIPClientData = NULL; 1.293 + trustData.dwUIChoice = WTD_UI_NONE; 1.294 + trustData.fdwRevocationChecks = WTD_REVOKE_NONE; 1.295 + trustData.dwUnionChoice = WTD_CHOICE_FILE; 1.296 + trustData.dwStateAction = 0; 1.297 + trustData.hWVTStateData = NULL; 1.298 + trustData.pwszURLReference = NULL; 1.299 + // no UI 1.300 + trustData.dwUIContext = 0; 1.301 + trustData.pFile = &fileToCheck; 1.302 + 1.303 + GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; 1.304 + // Check if the file is signed by something that is trusted. 1.305 + LONG ret = WinVerifyTrust(NULL, &policyGUID, &trustData); 1.306 + return ret; 1.307 +} 1.308 + 1.309 +/** 1.310 + * Verifies the trust of a signed file's certificate. 1.311 + * 1.312 + * @param stacktop A pointer to the top of the stack. This should be the file 1.313 + * path for the file that will have its trust verified. 1.314 + * @param variables A pointer to the NSIS variables 1.315 + * @return 1 if the file's trust was verified successfully, 0 if it was not 1.316 + */ 1.317 +extern "C" void __declspec(dllexport) 1.318 +VerifyCertTrust(HWND hwndParent, int string_size, 1.319 + TCHAR *variables, stack_t **stacktop, void *extra) 1.320 +{ 1.321 + TCHAR tmp[MAX_PATH + 1] = { _T('\0') }; 1.322 + WCHAR filePath[MAX_PATH + 1] = { L'\0' }; 1.323 + 1.324 + popstring(stacktop, tmp, MAX_PATH); 1.325 + 1.326 +#if !defined(UNICODE) 1.327 + MultiByteToWideChar(CP_ACP, 0, tmp, -1, filePath, MAX_PATH); 1.328 +#else 1.329 + wcsncpy(filePath, tmp, MAX_PATH); 1.330 +#endif 1.331 + 1.332 + LONG retCode = VerifyCertificateTrustForFile(filePath); 1.333 + if (retCode == ERROR_SUCCESS) { 1.334 + pushstring(stacktop, TEXT("1"), 2); 1.335 + } else { 1.336 + pushstring(stacktop, TEXT("0"), 2); 1.337 + } 1.338 +} 1.339 + 1.340 +BOOL WINAPI 1.341 +DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) 1.342 +{ 1.343 + return TRUE; 1.344 +} 1.345 + 1.346 +/** 1.347 + * Removes an element from the top of the NSIS stack 1.348 + * 1.349 + * @param stacktop A pointer to the top of the stack 1.350 + * @param str The string to pop to 1.351 + * @param len The max length 1.352 + * @return 0 on success 1.353 +*/ 1.354 +int popstring(stack_t **stacktop, TCHAR *str, int len) 1.355 +{ 1.356 + // Removes the element from the top of the stack and puts it in the buffer 1.357 + stack_t *th; 1.358 + if (!stacktop || !*stacktop) { 1.359 + return 1; 1.360 + } 1.361 + 1.362 + th = (*stacktop); 1.363 + lstrcpyn(str,th->text, len); 1.364 + *stacktop = th->next; 1.365 + GlobalFree((HGLOBAL)th); 1.366 + return 0; 1.367 +} 1.368 + 1.369 +/** 1.370 + * Adds an element to the top of the NSIS stack 1.371 + * 1.372 + * @param stacktop A pointer to the top of the stack 1.373 + * @param str The string to push on the stack 1.374 + * @param len The length of the string to push on the stack 1.375 + * @return 0 on success 1.376 +*/ 1.377 +void pushstring(stack_t **stacktop, const TCHAR *str, int len) 1.378 +{ 1.379 + stack_t *th; 1.380 + if (!stacktop) { 1.381 + return; 1.382 + } 1.383 + 1.384 + th = (stack_t*)GlobalAlloc(GPTR, sizeof(stack_t) + len); 1.385 + lstrcpyn(th->text, str, len); 1.386 + th->next = *stacktop; 1.387 + *stacktop = th; 1.388 +}