1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/maintenanceservice/certificatecheck.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,270 @@ 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 +#include "certificatecheck.h" 1.15 +#include "servicebase.h" 1.16 + 1.17 +#pragma comment(lib, "wintrust.lib") 1.18 +#pragma comment(lib, "crypt32.lib") 1.19 + 1.20 +static const int ENCODING = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; 1.21 + 1.22 +/** 1.23 + * Checks to see if a file stored at filePath matches the specified info. 1.24 + * 1.25 + * @param filePath The PE file path to check 1.26 + * @param infoToMatch The acceptable information to match 1.27 + * @return ERROR_SUCCESS if successful, ERROR_NOT_FOUND if the info 1.28 + * does not match, or the last error otherwise. 1.29 + */ 1.30 +DWORD 1.31 +CheckCertificateForPEFile(LPCWSTR filePath, 1.32 + CertificateCheckInfo &infoToMatch) 1.33 +{ 1.34 + HCERTSTORE certStore = nullptr; 1.35 + HCRYPTMSG cryptMsg = nullptr; 1.36 + PCCERT_CONTEXT certContext = nullptr; 1.37 + PCMSG_SIGNER_INFO signerInfo = nullptr; 1.38 + DWORD lastError = ERROR_SUCCESS; 1.39 + 1.40 + // Get the HCERTSTORE and HCRYPTMSG from the signed file. 1.41 + DWORD encoding, contentType, formatType; 1.42 + BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE, 1.43 + filePath, 1.44 + CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, 1.45 + CERT_QUERY_CONTENT_FLAG_ALL, 1.46 + 0, &encoding, &contentType, 1.47 + &formatType, &certStore, &cryptMsg, nullptr); 1.48 + if (!result) { 1.49 + lastError = GetLastError(); 1.50 + LOG_WARN(("CryptQueryObject failed. (%d)", lastError)); 1.51 + goto cleanup; 1.52 + } 1.53 + 1.54 + // Pass in nullptr to get the needed signer information size. 1.55 + DWORD signerInfoSize; 1.56 + result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, 1.57 + nullptr, &signerInfoSize); 1.58 + if (!result) { 1.59 + lastError = GetLastError(); 1.60 + LOG_WARN(("CryptMsgGetParam failed. (%d)", lastError)); 1.61 + goto cleanup; 1.62 + } 1.63 + 1.64 + // Allocate the needed size for the signer information. 1.65 + signerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, signerInfoSize); 1.66 + if (!signerInfo) { 1.67 + lastError = GetLastError(); 1.68 + LOG_WARN(("Unable to allocate memory for Signer Info. (%d)", lastError)); 1.69 + goto cleanup; 1.70 + } 1.71 + 1.72 + // Get the signer information (PCMSG_SIGNER_INFO). 1.73 + // In particular we want the issuer and serial number. 1.74 + result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, 1.75 + (PVOID)signerInfo, &signerInfoSize); 1.76 + if (!result) { 1.77 + lastError = GetLastError(); 1.78 + LOG_WARN(("CryptMsgGetParam failed. (%d)", lastError)); 1.79 + goto cleanup; 1.80 + } 1.81 + 1.82 + // Search for the signer certificate in the certificate store. 1.83 + CERT_INFO certInfo; 1.84 + certInfo.Issuer = signerInfo->Issuer; 1.85 + certInfo.SerialNumber = signerInfo->SerialNumber; 1.86 + certContext = CertFindCertificateInStore(certStore, ENCODING, 0, 1.87 + CERT_FIND_SUBJECT_CERT, 1.88 + (PVOID)&certInfo, nullptr); 1.89 + if (!certContext) { 1.90 + lastError = GetLastError(); 1.91 + LOG_WARN(("CertFindCertificateInStore failed. (%d)", lastError)); 1.92 + goto cleanup; 1.93 + } 1.94 + 1.95 + if (!DoCertificateAttributesMatch(certContext, infoToMatch)) { 1.96 + lastError = ERROR_NOT_FOUND; 1.97 + LOG_WARN(("Certificate did not match issuer or name. (%d)", lastError)); 1.98 + goto cleanup; 1.99 + } 1.100 + 1.101 +cleanup: 1.102 + if (signerInfo) { 1.103 + LocalFree(signerInfo); 1.104 + } 1.105 + if (certContext) { 1.106 + CertFreeCertificateContext(certContext); 1.107 + } 1.108 + if (certStore) { 1.109 + CertCloseStore(certStore, 0); 1.110 + } 1.111 + if (cryptMsg) { 1.112 + CryptMsgClose(cryptMsg); 1.113 + } 1.114 + return lastError; 1.115 +} 1.116 + 1.117 +/** 1.118 + * Checks to see if a file stored at filePath matches the specified info. 1.119 + * 1.120 + * @param certContext The certificate context of the file 1.121 + * @param infoToMatch The acceptable information to match 1.122 + * @return FALSE if the info does not match or if any error occurs in the check 1.123 + */ 1.124 +BOOL 1.125 +DoCertificateAttributesMatch(PCCERT_CONTEXT certContext, 1.126 + CertificateCheckInfo &infoToMatch) 1.127 +{ 1.128 + DWORD dwData; 1.129 + LPTSTR szName = nullptr; 1.130 + 1.131 + if (infoToMatch.issuer) { 1.132 + // Pass in nullptr to get the needed size of the issuer buffer. 1.133 + dwData = CertGetNameString(certContext, 1.134 + CERT_NAME_SIMPLE_DISPLAY_TYPE, 1.135 + CERT_NAME_ISSUER_FLAG, nullptr, 1.136 + nullptr, 0); 1.137 + 1.138 + if (!dwData) { 1.139 + LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); 1.140 + return FALSE; 1.141 + } 1.142 + 1.143 + // Allocate memory for Issuer name buffer. 1.144 + LPTSTR szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR)); 1.145 + if (!szName) { 1.146 + LOG_WARN(("Unable to allocate memory for issuer name. (%d)", 1.147 + GetLastError())); 1.148 + return FALSE; 1.149 + } 1.150 + 1.151 + // Get Issuer name. 1.152 + if (!CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 1.153 + CERT_NAME_ISSUER_FLAG, nullptr, szName, dwData)) { 1.154 + LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); 1.155 + LocalFree(szName); 1.156 + return FALSE; 1.157 + } 1.158 + 1.159 + // If the issuer does not match, return a failure. 1.160 + if (!infoToMatch.issuer || 1.161 + wcscmp(szName, infoToMatch.issuer)) { 1.162 + LocalFree(szName); 1.163 + return FALSE; 1.164 + } 1.165 + 1.166 + LocalFree(szName); 1.167 + szName = nullptr; 1.168 + } 1.169 + 1.170 + if (infoToMatch.name) { 1.171 + // Pass in nullptr to get the needed size of the name buffer. 1.172 + dwData = CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 1.173 + 0, nullptr, nullptr, 0); 1.174 + if (!dwData) { 1.175 + LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); 1.176 + return FALSE; 1.177 + } 1.178 + 1.179 + // Allocate memory for the name buffer. 1.180 + szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR)); 1.181 + if (!szName) { 1.182 + LOG_WARN(("Unable to allocate memory for subject name. (%d)", 1.183 + GetLastError())); 1.184 + return FALSE; 1.185 + } 1.186 + 1.187 + // Obtain the name. 1.188 + if (!(CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, 1.189 + nullptr, szName, dwData))) { 1.190 + LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); 1.191 + LocalFree(szName); 1.192 + return FALSE; 1.193 + } 1.194 + 1.195 + // If the issuer does not match, return a failure. 1.196 + if (!infoToMatch.name || 1.197 + wcscmp(szName, infoToMatch.name)) { 1.198 + LocalFree(szName); 1.199 + return FALSE; 1.200 + } 1.201 + 1.202 + // We have a match! 1.203 + LocalFree(szName); 1.204 + } 1.205 + 1.206 + // If there were any errors we would have aborted by now. 1.207 + return TRUE; 1.208 +} 1.209 + 1.210 +/** 1.211 + * Duplicates the specified string 1.212 + * 1.213 + * @param inputString The string to duplicate 1.214 + * @return The duplicated string which should be freed by the caller. 1.215 + */ 1.216 +LPWSTR 1.217 +AllocateAndCopyWideString(LPCWSTR inputString) 1.218 +{ 1.219 + LPWSTR outputString = 1.220 + (LPWSTR)LocalAlloc(LPTR, (wcslen(inputString) + 1) * sizeof(WCHAR)); 1.221 + if (outputString) { 1.222 + lstrcpyW(outputString, inputString); 1.223 + } 1.224 + return outputString; 1.225 +} 1.226 + 1.227 +/** 1.228 + * Verifies the trust of the specified file path. 1.229 + * 1.230 + * @param filePath The file path to check. 1.231 + * @return ERROR_SUCCESS if successful, or the last error code otherwise. 1.232 + */ 1.233 +DWORD 1.234 +VerifyCertificateTrustForFile(LPCWSTR filePath) 1.235 +{ 1.236 + // Setup the file to check. 1.237 + WINTRUST_FILE_INFO fileToCheck; 1.238 + ZeroMemory(&fileToCheck, sizeof(fileToCheck)); 1.239 + fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO); 1.240 + fileToCheck.pcwszFilePath = filePath; 1.241 + 1.242 + // Setup what to check, we want to check it is signed and trusted. 1.243 + WINTRUST_DATA trustData; 1.244 + ZeroMemory(&trustData, sizeof(trustData)); 1.245 + trustData.cbStruct = sizeof(trustData); 1.246 + trustData.pPolicyCallbackData = nullptr; 1.247 + trustData.pSIPClientData = nullptr; 1.248 + trustData.dwUIChoice = WTD_UI_NONE; 1.249 + trustData.fdwRevocationChecks = WTD_REVOKE_NONE; 1.250 + trustData.dwUnionChoice = WTD_CHOICE_FILE; 1.251 + trustData.dwStateAction = 0; 1.252 + trustData.hWVTStateData = nullptr; 1.253 + trustData.pwszURLReference = nullptr; 1.254 + // no UI 1.255 + trustData.dwUIContext = 0; 1.256 + trustData.pFile = &fileToCheck; 1.257 + 1.258 + GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; 1.259 + // Check if the file is signed by something that is trusted. 1.260 + LONG ret = WinVerifyTrust(nullptr, &policyGUID, &trustData); 1.261 + if (ERROR_SUCCESS == ret) { 1.262 + // The hash that represents the subject is trusted and there were no 1.263 + // verification errors. No publisher nor time stamp chain errors. 1.264 + LOG(("The file \"%ls\" is signed and the signature was verified.", 1.265 + filePath)); 1.266 + return ERROR_SUCCESS; 1.267 + } 1.268 + 1.269 + DWORD lastError = GetLastError(); 1.270 + LOG_WARN(("There was an error validating trust of the certificate for file" 1.271 + " \"%ls\". Returned: %d. (%d)", filePath, ret, lastError)); 1.272 + return ret; 1.273 +}