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