Wed, 31 Dec 2014 06:09:35 +0100
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 | #include "certificatecheck.h" |
michael@0 | 12 | #include "servicebase.h" |
michael@0 | 13 | |
michael@0 | 14 | #pragma comment(lib, "wintrust.lib") |
michael@0 | 15 | #pragma comment(lib, "crypt32.lib") |
michael@0 | 16 | |
michael@0 | 17 | static const int ENCODING = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; |
michael@0 | 18 | |
michael@0 | 19 | /** |
michael@0 | 20 | * Checks to see if a file stored at filePath matches the specified info. |
michael@0 | 21 | * |
michael@0 | 22 | * @param filePath The PE file path to check |
michael@0 | 23 | * @param infoToMatch The acceptable information to match |
michael@0 | 24 | * @return ERROR_SUCCESS if successful, ERROR_NOT_FOUND if the info |
michael@0 | 25 | * does not match, or the last error otherwise. |
michael@0 | 26 | */ |
michael@0 | 27 | DWORD |
michael@0 | 28 | CheckCertificateForPEFile(LPCWSTR filePath, |
michael@0 | 29 | CertificateCheckInfo &infoToMatch) |
michael@0 | 30 | { |
michael@0 | 31 | HCERTSTORE certStore = nullptr; |
michael@0 | 32 | HCRYPTMSG cryptMsg = nullptr; |
michael@0 | 33 | PCCERT_CONTEXT certContext = nullptr; |
michael@0 | 34 | PCMSG_SIGNER_INFO signerInfo = nullptr; |
michael@0 | 35 | DWORD lastError = ERROR_SUCCESS; |
michael@0 | 36 | |
michael@0 | 37 | // Get the HCERTSTORE and HCRYPTMSG from the signed file. |
michael@0 | 38 | DWORD encoding, contentType, formatType; |
michael@0 | 39 | BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE, |
michael@0 | 40 | filePath, |
michael@0 | 41 | CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, |
michael@0 | 42 | CERT_QUERY_CONTENT_FLAG_ALL, |
michael@0 | 43 | 0, &encoding, &contentType, |
michael@0 | 44 | &formatType, &certStore, &cryptMsg, nullptr); |
michael@0 | 45 | if (!result) { |
michael@0 | 46 | lastError = GetLastError(); |
michael@0 | 47 | LOG_WARN(("CryptQueryObject failed. (%d)", lastError)); |
michael@0 | 48 | goto cleanup; |
michael@0 | 49 | } |
michael@0 | 50 | |
michael@0 | 51 | // Pass in nullptr to get the needed signer information size. |
michael@0 | 52 | DWORD signerInfoSize; |
michael@0 | 53 | result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, |
michael@0 | 54 | nullptr, &signerInfoSize); |
michael@0 | 55 | if (!result) { |
michael@0 | 56 | lastError = GetLastError(); |
michael@0 | 57 | LOG_WARN(("CryptMsgGetParam failed. (%d)", lastError)); |
michael@0 | 58 | goto cleanup; |
michael@0 | 59 | } |
michael@0 | 60 | |
michael@0 | 61 | // Allocate the needed size for the signer information. |
michael@0 | 62 | signerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, signerInfoSize); |
michael@0 | 63 | if (!signerInfo) { |
michael@0 | 64 | lastError = GetLastError(); |
michael@0 | 65 | LOG_WARN(("Unable to allocate memory for Signer Info. (%d)", lastError)); |
michael@0 | 66 | goto cleanup; |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | // Get the signer information (PCMSG_SIGNER_INFO). |
michael@0 | 70 | // In particular we want the issuer and serial number. |
michael@0 | 71 | result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, |
michael@0 | 72 | (PVOID)signerInfo, &signerInfoSize); |
michael@0 | 73 | if (!result) { |
michael@0 | 74 | lastError = GetLastError(); |
michael@0 | 75 | LOG_WARN(("CryptMsgGetParam failed. (%d)", lastError)); |
michael@0 | 76 | goto cleanup; |
michael@0 | 77 | } |
michael@0 | 78 | |
michael@0 | 79 | // Search for the signer certificate in the certificate store. |
michael@0 | 80 | CERT_INFO certInfo; |
michael@0 | 81 | certInfo.Issuer = signerInfo->Issuer; |
michael@0 | 82 | certInfo.SerialNumber = signerInfo->SerialNumber; |
michael@0 | 83 | certContext = CertFindCertificateInStore(certStore, ENCODING, 0, |
michael@0 | 84 | CERT_FIND_SUBJECT_CERT, |
michael@0 | 85 | (PVOID)&certInfo, nullptr); |
michael@0 | 86 | if (!certContext) { |
michael@0 | 87 | lastError = GetLastError(); |
michael@0 | 88 | LOG_WARN(("CertFindCertificateInStore failed. (%d)", lastError)); |
michael@0 | 89 | goto cleanup; |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | if (!DoCertificateAttributesMatch(certContext, infoToMatch)) { |
michael@0 | 93 | lastError = ERROR_NOT_FOUND; |
michael@0 | 94 | LOG_WARN(("Certificate did not match issuer or name. (%d)", lastError)); |
michael@0 | 95 | goto cleanup; |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | cleanup: |
michael@0 | 99 | if (signerInfo) { |
michael@0 | 100 | LocalFree(signerInfo); |
michael@0 | 101 | } |
michael@0 | 102 | if (certContext) { |
michael@0 | 103 | CertFreeCertificateContext(certContext); |
michael@0 | 104 | } |
michael@0 | 105 | if (certStore) { |
michael@0 | 106 | CertCloseStore(certStore, 0); |
michael@0 | 107 | } |
michael@0 | 108 | if (cryptMsg) { |
michael@0 | 109 | CryptMsgClose(cryptMsg); |
michael@0 | 110 | } |
michael@0 | 111 | return lastError; |
michael@0 | 112 | } |
michael@0 | 113 | |
michael@0 | 114 | /** |
michael@0 | 115 | * Checks to see if a file stored at filePath matches the specified info. |
michael@0 | 116 | * |
michael@0 | 117 | * @param certContext The certificate context of the file |
michael@0 | 118 | * @param infoToMatch The acceptable information to match |
michael@0 | 119 | * @return FALSE if the info does not match or if any error occurs in the check |
michael@0 | 120 | */ |
michael@0 | 121 | BOOL |
michael@0 | 122 | DoCertificateAttributesMatch(PCCERT_CONTEXT certContext, |
michael@0 | 123 | CertificateCheckInfo &infoToMatch) |
michael@0 | 124 | { |
michael@0 | 125 | DWORD dwData; |
michael@0 | 126 | LPTSTR szName = nullptr; |
michael@0 | 127 | |
michael@0 | 128 | if (infoToMatch.issuer) { |
michael@0 | 129 | // Pass in nullptr to get the needed size of the issuer buffer. |
michael@0 | 130 | dwData = CertGetNameString(certContext, |
michael@0 | 131 | CERT_NAME_SIMPLE_DISPLAY_TYPE, |
michael@0 | 132 | CERT_NAME_ISSUER_FLAG, nullptr, |
michael@0 | 133 | nullptr, 0); |
michael@0 | 134 | |
michael@0 | 135 | if (!dwData) { |
michael@0 | 136 | LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); |
michael@0 | 137 | return FALSE; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | // Allocate memory for Issuer name buffer. |
michael@0 | 141 | LPTSTR szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR)); |
michael@0 | 142 | if (!szName) { |
michael@0 | 143 | LOG_WARN(("Unable to allocate memory for issuer name. (%d)", |
michael@0 | 144 | GetLastError())); |
michael@0 | 145 | return FALSE; |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | // Get Issuer name. |
michael@0 | 149 | if (!CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, |
michael@0 | 150 | CERT_NAME_ISSUER_FLAG, nullptr, szName, dwData)) { |
michael@0 | 151 | LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); |
michael@0 | 152 | LocalFree(szName); |
michael@0 | 153 | return FALSE; |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | // If the issuer does not match, return a failure. |
michael@0 | 157 | if (!infoToMatch.issuer || |
michael@0 | 158 | wcscmp(szName, infoToMatch.issuer)) { |
michael@0 | 159 | LocalFree(szName); |
michael@0 | 160 | return FALSE; |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | LocalFree(szName); |
michael@0 | 164 | szName = nullptr; |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | if (infoToMatch.name) { |
michael@0 | 168 | // Pass in nullptr to get the needed size of the name buffer. |
michael@0 | 169 | dwData = CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, |
michael@0 | 170 | 0, nullptr, nullptr, 0); |
michael@0 | 171 | if (!dwData) { |
michael@0 | 172 | LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); |
michael@0 | 173 | return FALSE; |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | // Allocate memory for the name buffer. |
michael@0 | 177 | szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR)); |
michael@0 | 178 | if (!szName) { |
michael@0 | 179 | LOG_WARN(("Unable to allocate memory for subject name. (%d)", |
michael@0 | 180 | GetLastError())); |
michael@0 | 181 | return FALSE; |
michael@0 | 182 | } |
michael@0 | 183 | |
michael@0 | 184 | // Obtain the name. |
michael@0 | 185 | if (!(CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, |
michael@0 | 186 | nullptr, szName, dwData))) { |
michael@0 | 187 | LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); |
michael@0 | 188 | LocalFree(szName); |
michael@0 | 189 | return FALSE; |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | // If the issuer does not match, return a failure. |
michael@0 | 193 | if (!infoToMatch.name || |
michael@0 | 194 | wcscmp(szName, infoToMatch.name)) { |
michael@0 | 195 | LocalFree(szName); |
michael@0 | 196 | return FALSE; |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | // We have a match! |
michael@0 | 200 | LocalFree(szName); |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | // If there were any errors we would have aborted by now. |
michael@0 | 204 | return TRUE; |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | /** |
michael@0 | 208 | * Duplicates the specified string |
michael@0 | 209 | * |
michael@0 | 210 | * @param inputString The string to duplicate |
michael@0 | 211 | * @return The duplicated string which should be freed by the caller. |
michael@0 | 212 | */ |
michael@0 | 213 | LPWSTR |
michael@0 | 214 | AllocateAndCopyWideString(LPCWSTR inputString) |
michael@0 | 215 | { |
michael@0 | 216 | LPWSTR outputString = |
michael@0 | 217 | (LPWSTR)LocalAlloc(LPTR, (wcslen(inputString) + 1) * sizeof(WCHAR)); |
michael@0 | 218 | if (outputString) { |
michael@0 | 219 | lstrcpyW(outputString, inputString); |
michael@0 | 220 | } |
michael@0 | 221 | return outputString; |
michael@0 | 222 | } |
michael@0 | 223 | |
michael@0 | 224 | /** |
michael@0 | 225 | * Verifies the trust of the specified file path. |
michael@0 | 226 | * |
michael@0 | 227 | * @param filePath The file path to check. |
michael@0 | 228 | * @return ERROR_SUCCESS if successful, or the last error code otherwise. |
michael@0 | 229 | */ |
michael@0 | 230 | DWORD |
michael@0 | 231 | VerifyCertificateTrustForFile(LPCWSTR filePath) |
michael@0 | 232 | { |
michael@0 | 233 | // Setup the file to check. |
michael@0 | 234 | WINTRUST_FILE_INFO fileToCheck; |
michael@0 | 235 | ZeroMemory(&fileToCheck, sizeof(fileToCheck)); |
michael@0 | 236 | fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO); |
michael@0 | 237 | fileToCheck.pcwszFilePath = filePath; |
michael@0 | 238 | |
michael@0 | 239 | // Setup what to check, we want to check it is signed and trusted. |
michael@0 | 240 | WINTRUST_DATA trustData; |
michael@0 | 241 | ZeroMemory(&trustData, sizeof(trustData)); |
michael@0 | 242 | trustData.cbStruct = sizeof(trustData); |
michael@0 | 243 | trustData.pPolicyCallbackData = nullptr; |
michael@0 | 244 | trustData.pSIPClientData = nullptr; |
michael@0 | 245 | trustData.dwUIChoice = WTD_UI_NONE; |
michael@0 | 246 | trustData.fdwRevocationChecks = WTD_REVOKE_NONE; |
michael@0 | 247 | trustData.dwUnionChoice = WTD_CHOICE_FILE; |
michael@0 | 248 | trustData.dwStateAction = 0; |
michael@0 | 249 | trustData.hWVTStateData = nullptr; |
michael@0 | 250 | trustData.pwszURLReference = nullptr; |
michael@0 | 251 | // no UI |
michael@0 | 252 | trustData.dwUIContext = 0; |
michael@0 | 253 | trustData.pFile = &fileToCheck; |
michael@0 | 254 | |
michael@0 | 255 | GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; |
michael@0 | 256 | // Check if the file is signed by something that is trusted. |
michael@0 | 257 | LONG ret = WinVerifyTrust(nullptr, &policyGUID, &trustData); |
michael@0 | 258 | if (ERROR_SUCCESS == ret) { |
michael@0 | 259 | // The hash that represents the subject is trusted and there were no |
michael@0 | 260 | // verification errors. No publisher nor time stamp chain errors. |
michael@0 | 261 | LOG(("The file \"%ls\" is signed and the signature was verified.", |
michael@0 | 262 | filePath)); |
michael@0 | 263 | return ERROR_SUCCESS; |
michael@0 | 264 | } |
michael@0 | 265 | |
michael@0 | 266 | DWORD lastError = GetLastError(); |
michael@0 | 267 | LOG_WARN(("There was an error validating trust of the certificate for file" |
michael@0 | 268 | " \"%ls\". Returned: %d. (%d)", filePath, ret, lastError)); |
michael@0 | 269 | return ret; |
michael@0 | 270 | } |