other-licenses/nsis/Contrib/CertCheck/CertCheck.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include <stdio.h>
michael@0 6 #include <stdlib.h>
michael@0 7 #include <windows.h>
michael@0 8 #include <softpub.h>
michael@0 9 #include <wintrust.h>
michael@0 10
michael@0 11 #pragma comment(lib, "wintrust.lib")
michael@0 12 #pragma comment(lib, "crypt32.lib")
michael@0 13
michael@0 14 #ifdef UNICODE
michael@0 15
michael@0 16 #ifndef _T
michael@0 17 #define __T(x) L ## x
michael@0 18 #define _T(x) __T(x)
michael@0 19 #define _TEXT(x) __T(x)
michael@0 20 #endif
michael@0 21
michael@0 22 #else
michael@0 23
michael@0 24 #ifndef _T
michael@0 25 #define _T(x) x
michael@0 26 #define _TEXT(x) x
michael@0 27 #endif
michael@0 28
michael@0 29 #endif // UNICODE
michael@0 30
michael@0 31 static const int ENCODING = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
michael@0 32
michael@0 33 typedef struct _stack_t {
michael@0 34 struct _stack_t *next;
michael@0 35 TCHAR text[MAX_PATH];
michael@0 36 } stack_t;
michael@0 37
michael@0 38 int popstring(stack_t **stacktop, LPTSTR str, int len);
michael@0 39 void pushstring(stack_t **stacktop, LPCTSTR str, int len);
michael@0 40
michael@0 41 struct CertificateCheckInfo
michael@0 42 {
michael@0 43 LPCWSTR name;
michael@0 44 LPCWSTR issuer;
michael@0 45 };
michael@0 46
michael@0 47 /**
michael@0 48 * Checks to see if a file stored at filePath matches the specified info. This
michael@0 49 * only supports the name and issuer attributes currently.
michael@0 50 *
michael@0 51 * @param certContext The certificate context of the file
michael@0 52 * @param infoToMatch The acceptable information to match
michael@0 53 * @return FALSE if the info does not match or if any error occurs in the check
michael@0 54 */
michael@0 55 BOOL
michael@0 56 DoCertificateAttributesMatch(PCCERT_CONTEXT certContext,
michael@0 57 CertificateCheckInfo &infoToMatch)
michael@0 58 {
michael@0 59 DWORD dwData;
michael@0 60 LPTSTR szName = NULL;
michael@0 61
michael@0 62 // Pass in NULL to get the needed size of the issuer buffer.
michael@0 63 dwData = CertGetNameString(certContext,
michael@0 64 CERT_NAME_SIMPLE_DISPLAY_TYPE,
michael@0 65 CERT_NAME_ISSUER_FLAG, NULL,
michael@0 66 NULL, 0);
michael@0 67
michael@0 68 if (!dwData) {
michael@0 69 return FALSE;
michael@0 70 }
michael@0 71
michael@0 72 // Allocate memory for Issuer name buffer.
michael@0 73 szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
michael@0 74 if (!szName) {
michael@0 75 return FALSE;
michael@0 76 }
michael@0 77
michael@0 78 // Get Issuer name.
michael@0 79 if (!CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,
michael@0 80 CERT_NAME_ISSUER_FLAG, NULL, szName, dwData)) {
michael@0 81 LocalFree(szName);
michael@0 82 return FALSE;
michael@0 83 }
michael@0 84
michael@0 85 // If the issuer does not match, return a failure.
michael@0 86 if (!infoToMatch.issuer ||
michael@0 87 wcscmp(szName, infoToMatch.issuer)) {
michael@0 88 LocalFree(szName);
michael@0 89 return FALSE;
michael@0 90 }
michael@0 91
michael@0 92 LocalFree(szName);
michael@0 93 szName = NULL;
michael@0 94
michael@0 95 // Pass in NULL to get the needed size of the name buffer.
michael@0 96 dwData = CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,
michael@0 97 0, NULL, NULL, 0);
michael@0 98 if (!dwData) {
michael@0 99 return FALSE;
michael@0 100 }
michael@0 101
michael@0 102 // Allocate memory for the name buffer.
michael@0 103 szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
michael@0 104 if (!szName) {
michael@0 105 return FALSE;
michael@0 106 }
michael@0 107
michael@0 108 // Obtain the name.
michael@0 109 if (!(CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
michael@0 110 NULL, szName, dwData))) {
michael@0 111 LocalFree(szName);
michael@0 112 return FALSE;
michael@0 113 }
michael@0 114
michael@0 115 // If the issuer does not match, return a failure.
michael@0 116 if (!infoToMatch.name ||
michael@0 117 wcscmp(szName, infoToMatch.name)) {
michael@0 118 LocalFree(szName);
michael@0 119 return FALSE;
michael@0 120 }
michael@0 121
michael@0 122 // We have a match!
michael@0 123 LocalFree(szName);
michael@0 124
michael@0 125 // If there were any errors we would have aborted by now.
michael@0 126 return TRUE;
michael@0 127 }
michael@0 128
michael@0 129 /**
michael@0 130 * Checks to see if a file stored at filePath matches the specified info. This
michael@0 131 * only supports the name and issuer attributes currently.
michael@0 132 *
michael@0 133 * @param filePath The PE file path to check
michael@0 134 * @param infoToMatch The acceptable information to match
michael@0 135 * @return ERROR_SUCCESS if successful, ERROR_NOT_FOUND if the info
michael@0 136 * does not match, or the last error otherwise.
michael@0 137 */
michael@0 138 DWORD
michael@0 139 CheckCertificateForPEFile(LPCWSTR filePath,
michael@0 140 CertificateCheckInfo &infoToMatch)
michael@0 141 {
michael@0 142 HCERTSTORE certStore = NULL;
michael@0 143 HCRYPTMSG cryptMsg = NULL;
michael@0 144 PCCERT_CONTEXT certContext = NULL;
michael@0 145 PCMSG_SIGNER_INFO signerInfo = NULL;
michael@0 146 DWORD lastError = ERROR_SUCCESS;
michael@0 147
michael@0 148 // Get the HCERTSTORE and HCRYPTMSG from the signed file.
michael@0 149 DWORD encoding, contentType, formatType;
michael@0 150 BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
michael@0 151 filePath,
michael@0 152 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
michael@0 153 CERT_QUERY_CONTENT_FLAG_ALL,
michael@0 154 0, &encoding, &contentType,
michael@0 155 &formatType, &certStore, &cryptMsg, NULL);
michael@0 156 if (!result) {
michael@0 157 lastError = GetLastError();
michael@0 158 goto cleanup;
michael@0 159 }
michael@0 160
michael@0 161 // Pass in NULL to get the needed signer information size.
michael@0 162 DWORD signerInfoSize;
michael@0 163 result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0,
michael@0 164 NULL, &signerInfoSize);
michael@0 165 if (!result) {
michael@0 166 lastError = GetLastError();
michael@0 167 goto cleanup;
michael@0 168 }
michael@0 169
michael@0 170 // Allocate the needed size for the signer information.
michael@0 171 signerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, signerInfoSize);
michael@0 172 if (!signerInfo) {
michael@0 173 lastError = GetLastError();
michael@0 174 goto cleanup;
michael@0 175 }
michael@0 176
michael@0 177 // Get the signer information (PCMSG_SIGNER_INFO).
michael@0 178 // In particular we want the issuer and serial number.
michael@0 179 result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0,
michael@0 180 (PVOID)signerInfo, &signerInfoSize);
michael@0 181 if (!result) {
michael@0 182 lastError = GetLastError();
michael@0 183 goto cleanup;
michael@0 184 }
michael@0 185
michael@0 186 // Search for the signer certificate in the certificate store.
michael@0 187 CERT_INFO certInfo;
michael@0 188 certInfo.Issuer = signerInfo->Issuer;
michael@0 189 certInfo.SerialNumber = signerInfo->SerialNumber;
michael@0 190 certContext = CertFindCertificateInStore(certStore, ENCODING, 0,
michael@0 191 CERT_FIND_SUBJECT_CERT,
michael@0 192 (PVOID)&certInfo, NULL);
michael@0 193 if (!certContext) {
michael@0 194 lastError = GetLastError();
michael@0 195 goto cleanup;
michael@0 196 }
michael@0 197
michael@0 198 if (!DoCertificateAttributesMatch(certContext, infoToMatch)) {
michael@0 199 lastError = ERROR_NOT_FOUND;
michael@0 200 goto cleanup;
michael@0 201 }
michael@0 202
michael@0 203 cleanup:
michael@0 204 if (signerInfo) {
michael@0 205 LocalFree(signerInfo);
michael@0 206 }
michael@0 207 if (certContext) {
michael@0 208 CertFreeCertificateContext(certContext);
michael@0 209 }
michael@0 210 if (certStore) {
michael@0 211 CertCloseStore(certStore, 0);
michael@0 212 }
michael@0 213 if (cryptMsg) {
michael@0 214 CryptMsgClose(cryptMsg);
michael@0 215 }
michael@0 216 return lastError;
michael@0 217 }
michael@0 218
michael@0 219 /**
michael@0 220 * Compares the certificate name and issuer values for a signed file's with the
michael@0 221 * values provided.
michael@0 222 *
michael@0 223 * @param stacktop A pointer to the top of the stack. The stack should contain
michael@0 224 * from the top the file's path, the expected value for the
michael@0 225 * certificate's name attribute, and the expected value for
michael@0 226 * the certificate's issuer attribute.
michael@0 227 * @param variables A pointer to the NSIS variables
michael@0 228 * @return 1 if the certificate name and issuer attributes matched the expected
michael@0 229 * values, 0 if they don't match the expected values.
michael@0 230 */
michael@0 231 extern "C" void __declspec(dllexport)
michael@0 232 VerifyCertNameIssuer(HWND hwndParent, int string_size,
michael@0 233 TCHAR *variables, stack_t **stacktop, void *extra)
michael@0 234 {
michael@0 235 TCHAR tmp1[MAX_PATH + 1] = { _T('\0') };
michael@0 236 TCHAR tmp2[MAX_PATH + 1] = { _T('\0') };
michael@0 237 TCHAR tmp3[MAX_PATH + 1] = { _T('\0') };
michael@0 238 WCHAR filePath[MAX_PATH + 1] = { L'\0' };
michael@0 239 WCHAR certName[MAX_PATH + 1] = { L'\0' };
michael@0 240 WCHAR certIssuer[MAX_PATH + 1] = { L'\0' };
michael@0 241
michael@0 242 popstring(stacktop, tmp1, MAX_PATH);
michael@0 243 popstring(stacktop, tmp2, MAX_PATH);
michael@0 244 popstring(stacktop, tmp3, MAX_PATH);
michael@0 245
michael@0 246 #if !defined(UNICODE)
michael@0 247 MultiByteToWideChar(CP_ACP, 0, tmp1, -1, filePath, MAX_PATH);
michael@0 248 MultiByteToWideChar(CP_ACP, 0, tmp2, -1, certName, MAX_PATH);
michael@0 249 MultiByteToWideChar(CP_ACP, 0, tmp3, -1, certIssuer, MAX_PATH);
michael@0 250 #else
michael@0 251 wcsncpy(filePath, tmp1, MAX_PATH);
michael@0 252 wcsncpy(certName, tmp2, MAX_PATH);
michael@0 253 wcsncpy(certIssuer, tmp3, MAX_PATH);
michael@0 254 #endif
michael@0 255
michael@0 256 CertificateCheckInfo allowedCertificate = {
michael@0 257 certName,
michael@0 258 certIssuer,
michael@0 259 };
michael@0 260
michael@0 261 LONG retCode = CheckCertificateForPEFile(filePath, allowedCertificate);
michael@0 262 if (retCode == ERROR_SUCCESS) {
michael@0 263 pushstring(stacktop, TEXT("1"), 2);
michael@0 264 } else {
michael@0 265 pushstring(stacktop, TEXT("0"), 2);
michael@0 266 }
michael@0 267 }
michael@0 268
michael@0 269 /**
michael@0 270 * Verifies the trust of a signed file's certificate.
michael@0 271 *
michael@0 272 * @param filePath The file path to check.
michael@0 273 * @return ERROR_SUCCESS if successful, or the last error code otherwise.
michael@0 274 */
michael@0 275 DWORD
michael@0 276 VerifyCertificateTrustForFile(LPCWSTR filePath)
michael@0 277 {
michael@0 278 // Setup the file to check.
michael@0 279 WINTRUST_FILE_INFO fileToCheck;
michael@0 280 ZeroMemory(&fileToCheck, sizeof(fileToCheck));
michael@0 281 fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO);
michael@0 282 fileToCheck.pcwszFilePath = filePath;
michael@0 283
michael@0 284 // Setup what to check, we want to check it is signed and trusted.
michael@0 285 WINTRUST_DATA trustData;
michael@0 286 ZeroMemory(&trustData, sizeof(trustData));
michael@0 287 trustData.cbStruct = sizeof(trustData);
michael@0 288 trustData.pPolicyCallbackData = NULL;
michael@0 289 trustData.pSIPClientData = NULL;
michael@0 290 trustData.dwUIChoice = WTD_UI_NONE;
michael@0 291 trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
michael@0 292 trustData.dwUnionChoice = WTD_CHOICE_FILE;
michael@0 293 trustData.dwStateAction = 0;
michael@0 294 trustData.hWVTStateData = NULL;
michael@0 295 trustData.pwszURLReference = NULL;
michael@0 296 // no UI
michael@0 297 trustData.dwUIContext = 0;
michael@0 298 trustData.pFile = &fileToCheck;
michael@0 299
michael@0 300 GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
michael@0 301 // Check if the file is signed by something that is trusted.
michael@0 302 LONG ret = WinVerifyTrust(NULL, &policyGUID, &trustData);
michael@0 303 return ret;
michael@0 304 }
michael@0 305
michael@0 306 /**
michael@0 307 * Verifies the trust of a signed file's certificate.
michael@0 308 *
michael@0 309 * @param stacktop A pointer to the top of the stack. This should be the file
michael@0 310 * path for the file that will have its trust verified.
michael@0 311 * @param variables A pointer to the NSIS variables
michael@0 312 * @return 1 if the file's trust was verified successfully, 0 if it was not
michael@0 313 */
michael@0 314 extern "C" void __declspec(dllexport)
michael@0 315 VerifyCertTrust(HWND hwndParent, int string_size,
michael@0 316 TCHAR *variables, stack_t **stacktop, void *extra)
michael@0 317 {
michael@0 318 TCHAR tmp[MAX_PATH + 1] = { _T('\0') };
michael@0 319 WCHAR filePath[MAX_PATH + 1] = { L'\0' };
michael@0 320
michael@0 321 popstring(stacktop, tmp, MAX_PATH);
michael@0 322
michael@0 323 #if !defined(UNICODE)
michael@0 324 MultiByteToWideChar(CP_ACP, 0, tmp, -1, filePath, MAX_PATH);
michael@0 325 #else
michael@0 326 wcsncpy(filePath, tmp, MAX_PATH);
michael@0 327 #endif
michael@0 328
michael@0 329 LONG retCode = VerifyCertificateTrustForFile(filePath);
michael@0 330 if (retCode == ERROR_SUCCESS) {
michael@0 331 pushstring(stacktop, TEXT("1"), 2);
michael@0 332 } else {
michael@0 333 pushstring(stacktop, TEXT("0"), 2);
michael@0 334 }
michael@0 335 }
michael@0 336
michael@0 337 BOOL WINAPI
michael@0 338 DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
michael@0 339 {
michael@0 340 return TRUE;
michael@0 341 }
michael@0 342
michael@0 343 /**
michael@0 344 * Removes an element from the top of the NSIS stack
michael@0 345 *
michael@0 346 * @param stacktop A pointer to the top of the stack
michael@0 347 * @param str The string to pop to
michael@0 348 * @param len The max length
michael@0 349 * @return 0 on success
michael@0 350 */
michael@0 351 int popstring(stack_t **stacktop, TCHAR *str, int len)
michael@0 352 {
michael@0 353 // Removes the element from the top of the stack and puts it in the buffer
michael@0 354 stack_t *th;
michael@0 355 if (!stacktop || !*stacktop) {
michael@0 356 return 1;
michael@0 357 }
michael@0 358
michael@0 359 th = (*stacktop);
michael@0 360 lstrcpyn(str,th->text, len);
michael@0 361 *stacktop = th->next;
michael@0 362 GlobalFree((HGLOBAL)th);
michael@0 363 return 0;
michael@0 364 }
michael@0 365
michael@0 366 /**
michael@0 367 * Adds an element to the top of the NSIS stack
michael@0 368 *
michael@0 369 * @param stacktop A pointer to the top of the stack
michael@0 370 * @param str The string to push on the stack
michael@0 371 * @param len The length of the string to push on the stack
michael@0 372 * @return 0 on success
michael@0 373 */
michael@0 374 void pushstring(stack_t **stacktop, const TCHAR *str, int len)
michael@0 375 {
michael@0 376 stack_t *th;
michael@0 377 if (!stacktop) {
michael@0 378 return;
michael@0 379 }
michael@0 380
michael@0 381 th = (stack_t*)GlobalAlloc(GPTR, sizeof(stack_t) + len);
michael@0 382 lstrcpyn(th->text, str, len);
michael@0 383 th->next = *stacktop;
michael@0 384 *stacktop = th;
michael@0 385 }

mercurial