"Fossies" - the Fresh Open Source Software Archive 
Member "VirtualBox-6.1.18/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyImage-win.cpp" (7 Jan 2021, 135556 Bytes) of package /linux/misc/VirtualBox-6.1.18.tar.bz2:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the last
Fossies "Diffs" side-by-side code changes report for "SUPHardenedVerifyImage-win.cpp":
6.1.14a_vs_6.1.16.
1 /* $Id: SUPHardenedVerifyImage-win.cpp $ */
2 /** @file
3 * VirtualBox Support Library/Driver - Hardened Image Verification, Windows.
4 */
5
6 /*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28 /*********************************************************************************************************************************
29 * Header Files *
30 *********************************************************************************************************************************/
31 #ifdef IN_RING0
32 # ifndef IPRT_NT_MAP_TO_ZW
33 # define IPRT_NT_MAP_TO_ZW
34 # endif
35 # include <iprt/nt/nt.h>
36 # include <ntimage.h>
37 #else
38 # include <iprt/nt/nt-and-windows.h>
39 # include "Wintrust.h"
40 # include "Softpub.h"
41 # include "mscat.h"
42 # ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
43 # define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
44 # endif
45 #endif
46
47 #include <VBox/sup.h>
48 #include <VBox/err.h>
49 #include <iprt/ctype.h>
50 #include <iprt/ldr.h>
51 #include <iprt/log.h>
52 #include <iprt/path.h>
53 #include <iprt/string.h>
54 #include <iprt/utf16.h>
55 #include <iprt/crypto/pkcs7.h>
56 #include <iprt/crypto/store.h>
57
58 #ifdef IN_RING0
59 # include "SUPDrvInternal.h"
60 #else
61 # include "SUPLibInternal.h"
62 #endif
63 #include "win/SUPHardenedVerify-win.h"
64
65
66 /*********************************************************************************************************************************
67 * Defined Constants And Macros *
68 *********************************************************************************************************************************/
69 /** The size of static hash (output) buffers.
70 * Avoids dynamic allocations and cleanups for of small buffers as well as extra
71 * calls for getting the appropriate buffer size. The largest digest in regular
72 * use by current windows version is SHA-512, we double this and hope it's
73 * enough a good while. */
74 #define SUPHARDNTVI_MAX_CAT_HASH_SIZE 128
75
76
77 #if defined(VBOX_PERMIT_EVEN_MORE) && !defined(VBOX_PERMIT_MORE)
78 # error "VBOX_PERMIT_EVEN_MORE without VBOX_PERMIT_MORE!"
79 #endif
80
81
82 /*********************************************************************************************************************************
83 * Structures and Typedefs *
84 *********************************************************************************************************************************/
85
86 #ifdef IN_RING3
87 typedef LONG (WINAPI * PFNWINVERIFYTRUST)(HWND hwnd, GUID const *pgActionID, PVOID pWVTData);
88 typedef BOOL (WINAPI * PFNCRYPTCATADMINACQUIRECONTEXT)(HCATADMIN *phCatAdmin, const GUID *pGuidSubsystem, DWORD dwFlags);
89 typedef BOOL (WINAPI * PFNCRYPTCATADMINACQUIRECONTEXT2)(HCATADMIN *phCatAdmin, const GUID *pGuidSubsystem, PCWSTR pwszHashAlgorithm,
90 struct _CERT_STRONG_SIGN_PARA const *pStrongHashPolicy, DWORD dwFlags);
91 typedef BOOL (WINAPI * PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE)(HANDLE hFile, DWORD *pcbHash, BYTE *pbHash, DWORD dwFlags);
92 typedef BOOL (WINAPI * PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2)(HCATADMIN hCatAdmin, HANDLE hFile, DWORD *pcbHash,
93 BYTE *pbHash, DWORD dwFlags);
94 typedef HCATINFO (WINAPI *PFNCRYPTCATADMINENUMCATALOGFROMHASH)(HCATADMIN hCatAdmin, BYTE *pbHash, DWORD cbHash,
95 DWORD dwFlags, HCATINFO *phPrevCatInfo);
96 typedef BOOL (WINAPI * PFNCRYPTCATADMINRELEASECATALOGCONTEXT)(HCATADMIN hCatAdmin, HCATINFO hCatInfo, DWORD dwFlags);
97 typedef BOOL (WINAPI * PFNCRYPTCATDADMINRELEASECONTEXT)(HCATADMIN hCatAdmin, DWORD dwFlags);
98 typedef BOOL (WINAPI * PFNCRYPTCATCATALOGINFOFROMCONTEXT)(HCATINFO hCatInfo, CATALOG_INFO *psCatInfo, DWORD dwFlags);
99
100 typedef HCERTSTORE (WINAPI *PFNCERTOPENSTORE)(PCSTR pszStoreProvider, DWORD dwEncodingType, HCRYPTPROV_LEGACY hCryptProv,
101 DWORD dwFlags, const void *pvParam);
102 typedef BOOL (WINAPI *PFNCERTCLOSESTORE)(HCERTSTORE hCertStore, DWORD dwFlags);
103 typedef PCCERT_CONTEXT (WINAPI *PFNCERTENUMCERTIFICATESINSTORE)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
104
105 typedef NTSTATUS (WINAPI *PFNBCRYPTOPENALGORTIHMPROVIDER)(BCRYPT_ALG_HANDLE *phAlgo, PCWSTR pwszAlgoId,
106 PCWSTR pwszImpl, DWORD dwFlags);
107 #endif
108
109
110 /*********************************************************************************************************************************
111 * Global Variables *
112 *********************************************************************************************************************************/
113 /** The build certificate. */
114 static RTCRX509CERTIFICATE g_BuildX509Cert;
115
116 /** Store for root software publisher certificates. */
117 static RTCRSTORE g_hSpcRootStore = NIL_RTCRSTORE;
118 /** Store for root NT kernel certificates. */
119 static RTCRSTORE g_hNtKernelRootStore = NIL_RTCRSTORE;
120
121 /** Store containing SPC, NT kernel signing, and timestamp root certificates. */
122 static RTCRSTORE g_hSpcAndNtKernelRootStore = NIL_RTCRSTORE;
123 /** Store for supplemental certificates for use with
124 * g_hSpcAndNtKernelRootStore. */
125 static RTCRSTORE g_hSpcAndNtKernelSuppStore = NIL_RTCRSTORE;
126
127 /** The full \\SystemRoot\\System32 path. */
128 SUPSYSROOTDIRBUF g_System32NtPath;
129 /** The full \\SystemRoot\\WinSxS path. */
130 SUPSYSROOTDIRBUF g_WinSxSNtPath;
131 #if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
132 /** The full 'Program Files' path. */
133 SUPSYSROOTDIRBUF g_ProgramFilesNtPath;
134 # ifdef RT_ARCH_AMD64
135 /** The full 'Program Files (x86)' path. */
136 SUPSYSROOTDIRBUF g_ProgramFilesX86NtPath;
137 # endif
138 /** The full 'Common Files' path. */
139 SUPSYSROOTDIRBUF g_CommonFilesNtPath;
140 # ifdef RT_ARCH_AMD64
141 /** The full 'Common Files (x86)' path. */
142 SUPSYSROOTDIRBUF g_CommonFilesX86NtPath;
143 # endif
144 #endif /* IN_RING3 && !VBOX_PERMIT_MORE*/
145
146 /**
147 * Blacklisted DLL names.
148 */
149 const RTSTRTUPLE g_aSupNtViBlacklistedDlls[] =
150 {
151 { RT_STR_TUPLE("SCROBJ.dll") },
152 { NULL, 0 } /* terminator entry */
153 };
154
155
156 static union
157 {
158 SID Sid;
159 uint8_t abPadding[SECURITY_MAX_SID_SIZE];
160 }
161 /** The TrustedInstaller SID (Vista+). */
162 g_TrustedInstallerSid,
163 /** Local system ID (S-1-5-21). */
164 g_LocalSystemSid,
165 /** Builtin Administrators group alias (S-1-5-32-544). */
166 g_AdminsGroupSid;
167
168
169 /** Set after we've retrived other SPC root certificates from the system. */
170 static bool g_fHaveOtherRoots = false;
171
172 #if defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
173 /** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED and
174 * SUP_MAKE_NT_VER_SIMPLE. */
175 uint32_t g_uNtVerCombined;
176 #endif
177
178 #ifdef IN_RING3
179 /** Timestamp hack working around issues with old DLLs that we ship.
180 * See supHardenedWinVerifyImageByHandle() for details. */
181 static uint64_t g_uBuildTimestampHack = 0;
182 #endif
183
184 #ifdef IN_RING3
185 /** Pointer to WinVerifyTrust. */
186 PFNWINVERIFYTRUST g_pfnWinVerifyTrust;
187 /** Pointer to CryptCATAdminAcquireContext. */
188 PFNCRYPTCATADMINACQUIRECONTEXT g_pfnCryptCATAdminAcquireContext;
189 /** Pointer to CryptCATAdminAcquireContext2 if available. */
190 PFNCRYPTCATADMINACQUIRECONTEXT2 g_pfnCryptCATAdminAcquireContext2;
191 /** Pointer to CryptCATAdminCalcHashFromFileHandle. */
192 PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE g_pfnCryptCATAdminCalcHashFromFileHandle;
193 /** Pointer to CryptCATAdminCalcHashFromFileHandle2. */
194 PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2 g_pfnCryptCATAdminCalcHashFromFileHandle2;
195 /** Pointer to CryptCATAdminEnumCatalogFromHash. */
196 PFNCRYPTCATADMINENUMCATALOGFROMHASH g_pfnCryptCATAdminEnumCatalogFromHash;
197 /** Pointer to CryptCATAdminReleaseCatalogContext. */
198 PFNCRYPTCATADMINRELEASECATALOGCONTEXT g_pfnCryptCATAdminReleaseCatalogContext;
199 /** Pointer to CryptCATAdminReleaseContext. */
200 PFNCRYPTCATDADMINRELEASECONTEXT g_pfnCryptCATAdminReleaseContext;
201 /** Pointer to CryptCATCatalogInfoFromContext. */
202 PFNCRYPTCATCATALOGINFOFROMCONTEXT g_pfnCryptCATCatalogInfoFromContext;
203
204 /** Where we store the TLS entry for detecting WinVerifyTrustRecursion. */
205 static uint32_t g_iTlsWinVerifyTrustRecursion = UINT32_MAX;
206 /** Fallback WinVerifyTrust recursion protection. */
207 static uint32_t volatile g_idActiveThread = UINT32_MAX;
208
209 #endif
210
211
212 /*********************************************************************************************************************************
213 * Internal Functions *
214 *********************************************************************************************************************************/
215 #ifdef IN_RING3
216 static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
217 PFNWINVERIFYTRUST pfnWinVerifyTrust, HRESULT *phrcWinVerifyTrust);
218 static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
219 PFNWINVERIFYTRUST pfnWinVerifyTrust);
220 #endif
221
222
223
224
225 /** @copydoc RTLDRREADER::pfnRead */
226 static DECLCALLBACK(int) supHardNtViRdrRead(PRTLDRREADER pReader, void *pvBuf, size_t cb, RTFOFF off)
227 {
228 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
229 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
230 NTSTATUS rcNt;
231
232 /* Check for type overflow (paranoia). */
233 if ((ULONG)cb != cb)
234 return VERR_OUT_OF_RANGE;
235
236 #ifdef IN_RING3
237 /* Make sure the event semaphore is reset (normally we don't use one). */
238 if (pNtViRdr->hEvent)
239 {
240 rcNt = NtClearEvent(pNtViRdr->hEvent);
241 if (!NT_SUCCESS(rcNt))
242 return RTErrConvertFromNtStatus(rcNt);
243 }
244 #endif
245
246 /* Perform the read. */
247 LARGE_INTEGER offNt;
248 offNt.QuadPart = off;
249
250 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
251 rcNt = NtReadFile(pNtViRdr->hFile,
252 pNtViRdr->hEvent,
253 NULL /*ApcRoutine*/,
254 NULL /*ApcContext*/,
255 &Ios,
256 pvBuf,
257 (ULONG)cb,
258 &offNt,
259 NULL);
260
261 #ifdef IN_RING0
262 /* In ring-0 the handles shall be synchronized and not alertable. */
263 AssertMsg(rcNt == STATUS_SUCCESS || !NT_SUCCESS(rcNt), ("%#x\n", rcNt));
264 #else
265 /* In ring-3 we like our handles synchronized and non-alertable, but we
266 sometimes have to take what we can get. So, deal with pending I/O as
267 best we can. */
268 if (rcNt == STATUS_PENDING)
269 rcNt = NtWaitForSingleObject(pNtViRdr->hEvent ? pNtViRdr->hEvent : pNtViRdr->hFile, FALSE /*Alertable*/, NULL);
270 #endif
271 if (NT_SUCCESS(rcNt))
272 rcNt = Ios.Status;
273 if (NT_SUCCESS(rcNt))
274 {
275 /* We require the caller to not read beyond the end of the file since
276 we don't have any way to communicate that we've read less that
277 requested. */
278 if (Ios.Information == cb)
279 {
280 pNtViRdr->off = off + cb; /* (just for show) */
281 return VINF_SUCCESS;
282 }
283 #ifdef IN_RING3
284 supR3HardenedError(VERR_READ_ERROR, false,
285 "supHardNtViRdrRead: Only got %#zx bytes when requesting %#zx bytes at %#llx in '%s'.\n",
286 Ios.Information, off, cb, pNtViRdr->szFilename);
287 #endif
288 }
289 pNtViRdr->off = -1;
290 return VERR_READ_ERROR;
291 }
292
293
294 /** @copydoc RTLDRREADER::pfnTell */
295 static DECLCALLBACK(RTFOFF) supHardNtViRdrTell(PRTLDRREADER pReader)
296 {
297 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
298 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
299 return pNtViRdr->off;
300 }
301
302
303 /** @copydoc RTLDRREADER::pfnSize */
304 static DECLCALLBACK(uint64_t) supHardNtViRdrSize(PRTLDRREADER pReader)
305 {
306 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
307 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
308 return pNtViRdr->cbFile;
309 }
310
311
312 /** @copydoc RTLDRREADER::pfnLogName */
313 static DECLCALLBACK(const char *) supHardNtViRdrLogName(PRTLDRREADER pReader)
314 {
315 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
316 return pNtViRdr->szFilename;
317 }
318
319
320 /** @copydoc RTLDRREADER::pfnMap */
321 static DECLCALLBACK(int) supHardNtViRdrMap(PRTLDRREADER pReader, const void **ppvBits)
322 {
323 RT_NOREF2(pReader, ppvBits);
324 return VERR_NOT_SUPPORTED;
325 }
326
327
328 /** @copydoc RTLDRREADER::pfnUnmap */
329 static DECLCALLBACK(int) supHardNtViRdrUnmap(PRTLDRREADER pReader, const void *pvBits)
330 {
331 RT_NOREF2(pReader, pvBits);
332 return VERR_NOT_SUPPORTED;
333 }
334
335
336 /** @copydoc RTLDRREADER::pfnDestroy */
337 static DECLCALLBACK(int) supHardNtViRdrDestroy(PRTLDRREADER pReader)
338 {
339 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
340 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
341
342 pNtViRdr->Core.uMagic = ~RTLDRREADER_MAGIC;
343 pNtViRdr->hFile = NULL;
344 #ifdef IN_RING3
345 if (pNtViRdr->hEvent)
346 {
347 NtClose(pNtViRdr->hEvent);
348 pNtViRdr->hEvent = NULL;
349 }
350 #endif
351 RTMemFree(pNtViRdr);
352 return VINF_SUCCESS;
353 }
354
355
356 /**
357 * Creates a loader reader instance for the given NT file handle.
358 *
359 * @returns iprt status code.
360 * @param hFile Native NT file handle.
361 * @param pwszName Optional file name.
362 * @param fFlags Flags, SUPHNTVI_F_XXX.
363 * @param ppNtViRdr Where to store the reader instance on success.
364 */
365 DECLHIDDEN(int) supHardNtViRdrCreate(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PSUPHNTVIRDR *ppNtViRdr)
366 {
367 /*
368 * Try determine the size of the file.
369 */
370 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
371 FILE_STANDARD_INFORMATION StdInfo;
372 NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
373 if (!NT_SUCCESS(rcNt) || !NT_SUCCESS(Ios.Status))
374 return VERR_LDRVI_FILE_LENGTH_ERROR;
375
376 /*
377 * Figure the file mode so we can see whether we'll be needing an event
378 * semaphore for waiting on reads. This may happen in very unlikely
379 * NtCreateSection scenarios.
380 */
381 #if defined(IN_RING3) || defined(VBOX_STRICT)
382 Ios.Status = STATUS_UNSUCCESSFUL;
383 ULONG fMode;
384 rcNt = NtQueryInformationFile(hFile, &Ios, &fMode, sizeof(fMode), FileModeInformation);
385 if (!NT_SUCCESS(rcNt) || !NT_SUCCESS(Ios.Status))
386 return VERR_SUP_VP_FILE_MODE_ERROR;
387 #endif
388
389 HANDLE hEvent = NULL;
390 #ifdef IN_RING3
391 if (!(fMode & (FILE_SYNCHRONOUS_IO_NONALERT | FILE_SYNCHRONOUS_IO_ALERT)))
392 {
393 rcNt = NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
394 if (!NT_SUCCESS(rcNt))
395 return VERR_SUP_VP_CREATE_READ_EVT_SEM_FAILED;
396 }
397 #else
398 Assert(fMode & FILE_SYNCHRONOUS_IO_NONALERT);
399 #endif
400
401 /*
402 * Calc the file name length and allocate memory for the reader instance.
403 */
404 size_t cchFilename = 0;
405 if (pwszName)
406 cchFilename = RTUtf16CalcUtf8Len(pwszName);
407
408 int rc = VERR_NO_MEMORY;
409 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)RTMemAllocZ(sizeof(*pNtViRdr) + cchFilename);
410 if (!pNtViRdr)
411 {
412 #ifdef IN_RING3
413 if (hEvent != NULL)
414 NtClose(hEvent);
415 #endif
416 return VERR_NO_MEMORY;
417 }
418
419 /*
420 * Initialize the structure.
421 */
422 if (cchFilename)
423 {
424 char *pszName = &pNtViRdr->szFilename[0];
425 rc = RTUtf16ToUtf8Ex(pwszName, RTSTR_MAX, &pszName, cchFilename + 1, NULL);
426 AssertStmt(RT_SUCCESS(rc), pNtViRdr->szFilename[0] = '\0');
427 }
428 else
429 pNtViRdr->szFilename[0] = '\0';
430
431 pNtViRdr->Core.uMagic = RTLDRREADER_MAGIC;
432 pNtViRdr->Core.pfnRead = supHardNtViRdrRead;
433 pNtViRdr->Core.pfnTell = supHardNtViRdrTell;
434 pNtViRdr->Core.pfnSize = supHardNtViRdrSize;
435 pNtViRdr->Core.pfnLogName = supHardNtViRdrLogName;
436 pNtViRdr->Core.pfnMap = supHardNtViRdrMap;
437 pNtViRdr->Core.pfnUnmap = supHardNtViRdrUnmap;
438 pNtViRdr->Core.pfnDestroy = supHardNtViRdrDestroy;
439 pNtViRdr->hFile = hFile;
440 pNtViRdr->hEvent = hEvent;
441 pNtViRdr->off = 0;
442 pNtViRdr->cbFile = (uint64_t)StdInfo.EndOfFile.QuadPart;
443 pNtViRdr->fFlags = fFlags;
444 *ppNtViRdr = pNtViRdr;
445 return VINF_SUCCESS;
446 }
447
448
449 /**
450 * Checks if the file is owned by TrustedInstaller (Vista+) or similar.
451 *
452 * @returns true if owned by TrustedInstaller of pre-Vista, false if not.
453 *
454 * @param hFile The handle to the file.
455 * @param pwszName The name of the file.
456 */
457 static bool supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(HANDLE hFile, PCRTUTF16 pwszName)
458 {
459 if (g_uNtVerCombined < SUP_NT_VER_VISTA)
460 return true;
461
462 /*
463 * Get the ownership information.
464 */
465 union
466 {
467 SECURITY_DESCRIPTOR_RELATIVE Rel;
468 SECURITY_DESCRIPTOR Abs;
469 uint8_t abView[256];
470 } uBuf;
471 ULONG cbActual;
472 NTSTATUS rcNt = NtQuerySecurityObject(hFile, OWNER_SECURITY_INFORMATION, &uBuf.Abs, sizeof(uBuf), &cbActual);
473 if (!NT_SUCCESS(rcNt))
474 {
475 SUP_DPRINTF(("NtQuerySecurityObject failed with rcNt=%#x on '%ls'\n", rcNt, pwszName));
476 return false;
477 }
478
479 /*
480 * Check the owner.
481 *
482 * Initially we wished to only allow TrustedInstaller. But a Windows CAPI
483 * plugin "Program Files\Tumbleweed\Desktop Validator\tmwdcapiclient.dll"
484 * turned up owned by the local system user, and we cannot operate without
485 * the plugin loaded once it's installed (WinVerityTrust fails).
486 *
487 * We'd like to avoid allowing Builtin\Administrators here since it's the
488 * default owner of anything an admin user creates (at least when elevated).
489 * Seems windows update or someone ends up installing or modifying system
490 * DLL ownership to this group, so for system32 and winsxs it's unavoidable.
491 * And, not surprise, a bunch of products, including AV, firewalls and similar
492 * ends up with their files installed with this group as owner. For instance
493 * if we wish to have NAT continue working, we need to allow this.
494 *
495 * Hopefully, we can limit the allowed files to these owners though, so
496 * we won't be subject to ordinary (non-admin, or not elevated) users
497 * downloading or be tricked into putting evil DLLs around the place...
498 */
499 PSID pOwner = uBuf.Rel.Control & SE_SELF_RELATIVE ? &uBuf.abView[uBuf.Rel.Owner] : uBuf.Abs.Owner;
500 Assert((uintptr_t)pOwner - (uintptr_t)&uBuf < sizeof(uBuf) - sizeof(SID));
501 if (RtlEqualSid(pOwner, &g_TrustedInstallerSid))
502 return true;
503 if (RtlEqualSid(pOwner, &g_LocalSystemSid))
504 return true;
505 if (RtlEqualSid(pOwner, &g_AdminsGroupSid))
506 {
507 SUP_DPRINTF(("%ls: Owner is administrators group.\n", pwszName));
508 return true;
509 }
510
511 SUP_DPRINTF(("%ls: Owner is not trusted installer (%.*Rhxs)\n",
512 pwszName, ((uint8_t *)pOwner)[1] /*SubAuthorityCount*/ * sizeof(ULONG) + 8, pOwner));
513 RT_NOREF1(pwszName);
514 return false;
515 }
516
517
518 /**
519 * Simple case insensitive UTF-16 / ASCII path compare.
520 *
521 * @returns true if equal, false if not.
522 * @param pawcLeft The UTF-16 path string, not necessarily null
523 * terminated.
524 * @param cwcLeft The number of chars in the left string,
525 * RTSTR_MAX if unknown but terminated.
526 * @param pszRight The ascii string.
527 */
528 DECLHIDDEN(bool) supHardViUtf16PathIsEqualEx(PCRTUTF16 pawcLeft, size_t cwcLeft, const char *pszRight)
529 {
530 for (;;)
531 {
532 RTUTF16 wc;
533 if (cwcLeft-- > 0)
534 wc =*pawcLeft++;
535 else
536 wc = 0;
537 uint8_t b = *pszRight++;
538 if (b != wc)
539 {
540 if (wc >= 0x80)
541 return false;
542 wc = RT_C_TO_LOWER(wc);
543 if (wc != b)
544 {
545 b = RT_C_TO_LOWER(b);
546 if (wc != b)
547 {
548 if (wc == '/')
549 wc = '\\';
550 if (b == '/')
551 b = '\\';
552 if (wc != b)
553 return false;
554 }
555 }
556 }
557 if (!b)
558 return true;
559 }
560 }
561
562
563 /**
564 * Simple case insensitive UTF-16 / ASCII path compare.
565 *
566 * @returns true if equal, false if not.
567 * @param pwszLeft The UTF-16 path string.
568 * @param pszRight The ascii string.
569 */
570 static bool supHardViUtf16PathIsEqual(PCRTUTF16 pwszLeft, const char *pszRight)
571 {
572 return supHardViUtf16PathIsEqualEx(pwszLeft, RTSTR_MAX, pszRight);
573 }
574
575
576 #if 0 /* unused */
577 /**
578 * Simple case insensitive UTF-16 / ASCII ends-with path predicate.
579 *
580 * @returns true if equal, false if not.
581 * @param pwsz The UTF-16 path string.
582 * @param pszSuffix The ascii suffix string.
583 */
584 static bool supHardViUtf16PathEndsWith(PCRTUTF16 pwsz, const char *pszSuffix)
585 {
586 size_t cwc = RTUtf16Len(pwsz);
587 size_t cchSuffix = strlen(pszSuffix);
588 if (cwc >= cchSuffix)
589 return supHardViUtf16PathIsEqual(pwsz + cwc - cchSuffix, pszSuffix);
590 return false;
591 }
592 #endif
593
594
595 /**
596 * Simple case insensitive UTF-16 / ASCII starts-with path predicate.
597 *
598 * @returns true if starts with given string, false if not.
599 * @param pwszLeft The UTF-16 path string.
600 * @param pszRight The ascii prefix string.
601 */
602 static bool supHardViUtf16PathStartsWithAscii(PCRTUTF16 pwszLeft, const char *pszRight)
603 {
604 for (;;)
605 {
606 RTUTF16 wc = *pwszLeft++;
607 uint8_t b = *pszRight++;
608 if (b != wc)
609 {
610 if (!b)
611 return true;
612 if (wc >= 0x80 || wc == 0)
613 return false;
614 wc = RT_C_TO_LOWER(wc);
615 if (wc != b)
616 {
617 b = RT_C_TO_LOWER(b);
618 if (wc != b)
619 {
620 if (wc == '/')
621 wc = '\\';
622 if (b == '/')
623 b = '\\';
624 if (wc != b)
625 return false;
626 }
627 }
628 }
629 }
630 }
631
632
633 /**
634 * Simple case insensitive UNICODE_STRING starts-with path predicate.
635 *
636 * @returns true if starts with given string, false if not.
637 * @param pwszLeft The path to check.
638 * @param cwcLeft The length of @a pwszLeft
639 * @param pwszRight The starts-with path.
640 * @param cwcRight The length of @a pwszRight.
641 * @param fCheckSlash Check for a slash following the prefix.
642 */
643 DECLHIDDEN(bool) supHardViUtf16PathStartsWithEx(PCRTUTF16 pwszLeft, uint32_t cwcLeft,
644 PCRTUTF16 pwszRight, uint32_t cwcRight, bool fCheckSlash)
645 {
646 if (cwcLeft < cwcRight || !cwcRight || !pwszRight)
647 return false;
648
649 /* See if we can get away with a case sensitive compare first. */
650 if (memcmp(pwszLeft, pwszRight, cwcRight * sizeof(RTUTF16)) == 0)
651 pwszLeft += cwcRight;
652 else
653 {
654 /* No luck, do a slow case insensitive comapre. */
655 uint32_t cLeft = cwcRight;
656 while (cLeft-- > 0)
657 {
658 RTUTF16 wcLeft = *pwszLeft++;
659 RTUTF16 wcRight = *pwszRight++;
660 if (wcLeft != wcRight)
661 {
662 wcLeft = wcLeft < 0x80 ? wcLeft == '/' ? '\\' : RT_C_TO_LOWER(wcLeft) : wcLeft;
663 wcRight = wcRight < 0x80 ? wcRight == '/' ? '\\' : RT_C_TO_LOWER(wcRight) : wcRight;
664 if (wcLeft != wcRight)
665 return false;
666 }
667 }
668 }
669
670 /* Check for slash following the prefix, if request. */
671 if ( !fCheckSlash
672 || *pwszLeft == '\\'
673 || *pwszLeft == '/')
674 return true;
675 return false;
676 }
677
678
679 /**
680 * Simple case insensitive UNICODE_STRING starts-with path predicate.
681 *
682 * @returns true if starts with given string, false if not.
683 * @param pUniStrLeft The path to check.
684 * @param pUniStrRight The starts-with path.
685 * @param fCheckSlash Check for a slash following the prefix.
686 */
687 DECLHIDDEN(bool) supHardViUniStrPathStartsWithUniStr(UNICODE_STRING const *pUniStrLeft, UNICODE_STRING const *pUniStrRight,
688 bool fCheckSlash)
689 {
690 return supHardViUtf16PathStartsWithEx(pUniStrLeft->Buffer, pUniStrLeft->Length / sizeof(WCHAR),
691 pUniStrRight->Buffer, pUniStrRight->Length / sizeof(WCHAR), fCheckSlash);
692 }
693
694
695 #ifndef IN_RING0
696 /**
697 * Counts slashes in the given UTF-8 path string.
698 *
699 * @returns Number of slashes.
700 * @param pwsz The UTF-16 path string.
701 */
702 static uint32_t supHardViUtf16PathCountSlashes(PCRTUTF16 pwsz)
703 {
704 uint32_t cSlashes = 0;
705 RTUTF16 wc;
706 while ((wc = *pwsz++) != '\0')
707 if (wc == '/' || wc == '\\')
708 cSlashes++;
709 return cSlashes;
710 }
711 #endif
712
713
714 #ifdef VBOX_PERMIT_MORE
715 /**
716 * Checks if the path goes into %windir%\apppatch\.
717 *
718 * @returns true if apppatch, false if not.
719 * @param pwszPath The path to examine.
720 */
721 DECLHIDDEN(bool) supHardViIsAppPatchDir(PCRTUTF16 pwszPath, uint32_t cwcName)
722 {
723 uint32_t cwcWinDir = (g_System32NtPath.UniStr.Length - sizeof(L"System32")) / sizeof(WCHAR);
724
725 if (cwcName <= cwcWinDir + sizeof("AppPatch"))
726 return false;
727
728 if (memcmp(pwszPath, g_System32NtPath.UniStr.Buffer, cwcWinDir * sizeof(WCHAR)))
729 return false;
730
731 if (!supHardViUtf16PathStartsWithAscii(&pwszPath[cwcWinDir], "\\AppPatch\\"))
732 return false;
733
734 return g_uNtVerCombined >= SUP_NT_VER_VISTA;
735 }
736 #else
737 # error should not get here..
738 #endif
739
740
741
742 /**
743 * Checks if the unsigned DLL is fine or not.
744 *
745 * @returns VINF_LDRVI_NOT_SIGNED or @a rc.
746 * @param hLdrMod The loader module handle.
747 * @param pwszName The NT name of the DLL/EXE.
748 * @param fFlags Flags.
749 * @param hFile The file handle.
750 * @param rc The status code..
751 */
752 static int supHardNtViCheckIfNotSignedOk(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, uint32_t fFlags, HANDLE hFile, int rc)
753 {
754 RT_NOREF1(hLdrMod);
755
756 if (fFlags & (SUPHNTVI_F_REQUIRE_BUILD_CERT | SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING))
757 return rc;
758
759 /*
760 * Version macros.
761 */
762 uint32_t const uNtVer = g_uNtVerCombined;
763 #define IS_XP() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(5, 1) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(5, 2) )
764 #define IS_W2K3() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(5, 2) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(5, 3) )
765 #define IS_VISTA() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 0) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 1) )
766 #define IS_W70() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 1) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 2) )
767 #define IS_W80() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 2) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 3) )
768 #define IS_W81() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 3) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) )
769
770 /*
771 * The System32 directory.
772 *
773 * System32 is full of unsigned DLLs shipped by microsoft, graphics
774 * hardware vendors, input device/method vendors and whatnot else that
775 * actually needs to be loaded into a process for it to work correctly.
776 * We have to ASSUME that anything our process attempts to load from
777 * System32 is trustworthy and that the Windows system with the help of
778 * anti-virus software make sure there is nothing evil lurking in System32
779 * or being loaded from it.
780 *
781 * A small measure of protection is to list DLLs we know should be signed
782 * and decline loading unsigned versions of them, assuming they have been
783 * replaced by an adversary with evil intentions.
784 */
785 PCRTUTF16 pwsz;
786 uint32_t cwcName = (uint32_t)RTUtf16Len(pwszName);
787 uint32_t cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR);
788 if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_System32NtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
789 {
790 pwsz = pwszName + cwcOther + 1;
791
792 /* Must be owned by trusted installer. (This test is superfuous, thus no relaxation here.) */
793 if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
794 && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(hFile, pwszName))
795 return rc;
796
797 /* Core DLLs. */
798 if (supHardViUtf16PathIsEqual(pwsz, "ntdll.dll"))
799 return uNtVer < SUP_NT_VER_VISTA ? VINF_LDRVI_NOT_SIGNED : rc;
800 if (supHardViUtf16PathIsEqual(pwsz, "kernel32.dll"))
801 return uNtVer < SUP_NT_VER_W81 ? VINF_LDRVI_NOT_SIGNED : rc;
802 if (supHardViUtf16PathIsEqual(pwsz, "kernelbase.dll"))
803 return IS_W80() || IS_W70() ? VINF_LDRVI_NOT_SIGNED : rc;
804 if (supHardViUtf16PathIsEqual(pwsz, "apisetschema.dll"))
805 return IS_W70() ? VINF_LDRVI_NOT_SIGNED : rc;
806 if (supHardViUtf16PathIsEqual(pwsz, "apphelp.dll"))
807 return VINF_LDRVI_NOT_SIGNED; /* So far, never signed... */
808 #ifdef VBOX_PERMIT_VERIFIER_DLL
809 if (supHardViUtf16PathIsEqual(pwsz, "verifier.dll"))
810 return uNtVer < SUP_NT_VER_W81 ? VINF_LDRVI_NOT_SIGNED : rc;
811 #endif
812 #ifdef VBOX_PERMIT_MORE
813 if (uNtVer >= SUP_NT_VER_W70) /* hard limit: user32.dll is unwanted prior to w7. */
814 {
815 if (supHardViUtf16PathIsEqual(pwsz, "sfc.dll"))
816 return uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) ? VINF_LDRVI_NOT_SIGNED : rc;
817 if (supHardViUtf16PathIsEqual(pwsz, "sfc_os.dll"))
818 return uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) ? VINF_LDRVI_NOT_SIGNED : rc;
819 if (supHardViUtf16PathIsEqual(pwsz, "user32.dll"))
820 return uNtVer < SUP_NT_VER_W81 ? VINF_LDRVI_NOT_SIGNED : rc;
821 }
822 #endif
823
824 #ifndef IN_RING0
825 /* Check that this DLL isn't supposed to be signed on this windows
826 version. If it should, it's likely to be a fake. */
827 /** @todo list of signed dlls for various windows versions. */
828 return VINF_LDRVI_NOT_SIGNED;
829 #else
830 return rc;
831 #endif /* IN_RING0 */
832 }
833
834
835 #ifndef IN_RING0
836 /*
837 * The WinSxS white list.
838 *
839 * Just like with System32 there are potentially a number of DLLs that
840 * could be required from WinSxS.
841 */
842 cwcOther = g_WinSxSNtPath.UniStr.Length / sizeof(WCHAR);
843 if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_WinSxSNtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
844 {
845 pwsz = pwszName + cwcOther + 1;
846 cwcName -= cwcOther + 1;
847
848 /* The WinSxS layout means everything worth loading is exactly one level down. */
849 uint32_t cSlashes = supHardViUtf16PathCountSlashes(pwsz);
850 if (cSlashes != 1)
851 return rc;
852
853 /* Must be owned by trusted installer. */
854 if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
855 && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(hFile, pwszName))
856 return rc;
857 return VINF_LDRVI_NOT_SIGNED;
858 }
859 #endif /* !IN_RING0 */
860
861
862 #ifdef VBOX_PERMIT_MORE
863 /*
864 * AppPatch whitelist.
865 */
866 if (supHardViIsAppPatchDir(pwszName, cwcName))
867 {
868 cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR); /* ASSUMES System32 is called System32. */
869 pwsz = pwszName + cwcOther + 1;
870
871 if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
872 && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(hFile, pwszName))
873 return rc;
874
875 # ifndef VBOX_PERMIT_EVEN_MORE
876 if (supHardViUtf16PathIsEqual(pwsz, "acres.dll"))
877 return VINF_LDRVI_NOT_SIGNED;
878
879 # ifdef RT_ARCH_AMD64
880 if (supHardViUtf16PathIsEqual(pwsz, "AppPatch64\\AcGenral.dll"))
881 return VINF_LDRVI_NOT_SIGNED;
882 # elif defined(RT_ARCH_X86)
883 if (supHardViUtf16PathIsEqual(pwsz, "AcGenral.dll"))
884 return VINF_LDRVI_NOT_SIGNED;
885 # endif
886 # endif /* !VBOX_PERMIT_EVEN_MORE */
887
888 # ifdef IN_RING0
889 return rc;
890 # else
891 return VINF_LDRVI_NOT_SIGNED;
892 # endif
893 }
894 #endif /* VBOX_PERMIT_MORE */
895
896
897 #ifndef IN_RING0
898 # if defined(VBOX_PERMIT_MORE) && !defined(VBOX_PERMIT_EVEN_MORE)
899 /*
900 * Program files and common files.
901 * Permit anything that's signed and correctly installed.
902 */
903 if ( supHardViUtf16PathStartsWithEx(pwszName, cwcName,
904 g_ProgramFilesNtPath.UniStr.Buffer, g_ProgramFilesNtPath.UniStr.Length,
905 true /*fCheckSlash*/)
906 || supHardViUtf16PathStartsWithEx(pwszName, cwcName,
907 g_CommonFilesNtPath.UniStr.Buffer, g_CommonFilesNtPath.UniStr.Length,
908 true /*fCheckSlash*/)
909 # ifdef RT_ARCH_AMD64
910 || supHardViUtf16PathStartsWithEx(pwszName, cwcName,
911 g_ProgramFilesX86NtPath.UniStr.Buffer, g_ProgramFilesX86NtPath.UniStr.Length,
912 true /*fCheckSlash*/)
913 || supHardViUtf16PathStartsWithEx(pwszName, cwcName,
914 g_CommonFilesX86NtPath.UniStr.Buffer, g_CommonFilesX86NtPath.UniStr.Length,
915 true /*fCheckSlash*/)
916 # endif
917 )
918 {
919 if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
920 && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(hFile, pwszName))
921 return rc;
922 return VINF_LDRVI_NOT_SIGNED;
923 }
924
925 # elif defined(VBOX_PERMIT_MORE) && defined(VBOX_PERMIT_EVEN_MORE)
926 /*
927 * Anything that's owned by the trusted installer.
928 */
929 if ( (fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
930 || supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(hFile, pwszName))
931 return VINF_LDRVI_NOT_SIGNED;
932
933 # endif
934 #endif /* !IN_RING0 */
935
936 /*
937 * Not permitted.
938 */
939 return rc;
940 }
941
942
943 /**
944 * @callback_method_impl{FNRTDUMPPRINTFV, Formats into RTERRINFO. }
945 */
946 static DECLCALLBACK(void) supHardNtViAsn1DumpToErrInfo(void *pvUser, const char *pszFormat, va_list va)
947 {
948 PRTERRINFO pErrInfo = (PRTERRINFO)pvUser;
949 RTErrInfoAddV(pErrInfo, pErrInfo->rc, pszFormat, va);
950 }
951
952
953 /**
954 * Attempts to locate a root certificate in the specified store.
955 *
956 * @returns IPRT status code.
957 * @retval VINF_SUCCESS if found.
958 * @retval VWRN_NOT_FOUND if not found.
959 *
960 * @param hRootStore The root certificate store to search.
961 * @param pSubject The root certificate subject.
962 * @param pPublicKeyInfo The public key of the root certificate to find.
963 */
964 static int supHardNtViCertVerifyFindRootCert(RTCRSTORE hRootStore, PCRTCRX509NAME pSubject,
965 PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo)
966 {
967 RTCRSTORECERTSEARCH Search;
968 int rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(hRootStore, pSubject, &Search);
969 AssertRCReturn(rc, rc);
970
971 rc = VWRN_NOT_FOUND;
972 PCRTCRCERTCTX pCertCtx;
973 while ((pCertCtx = RTCrStoreCertSearchNext(hRootStore, &Search)) != NULL)
974 {
975 PCRTCRX509SUBJECTPUBLICKEYINFO pCertPubKeyInfo = NULL;
976 if (pCertCtx->pCert)
977 pCertPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
978 else if (pCertCtx->pTaInfo)
979 pCertPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
980 else
981 pCertPubKeyInfo = NULL;
982 if ( pCertPubKeyInfo
983 && RTCrX509SubjectPublicKeyInfo_Compare(pCertPubKeyInfo, pPublicKeyInfo) == 0)
984 {
985 RTCrCertCtxRelease(pCertCtx);
986 rc = VINF_SUCCESS;
987 break;
988 }
989 RTCrCertCtxRelease(pCertCtx);
990 }
991
992 int rc2 = RTCrStoreCertSearchDestroy(hRootStore, &Search);
993 AssertRC(rc2);
994 return rc;
995 }
996
997
998 /**
999 * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK,
1000 * Standard code signing. Use this for Microsoft SPC.}
1001 */
1002 static DECLCALLBACK(int) supHardNtViCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths,
1003 uint32_t fFlags, void *pvUser, PRTERRINFO pErrInfo)
1004 {
1005 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pvUser;
1006 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
1007
1008 /*
1009 * If there is no certificate path build & validator associated with this
1010 * callback, it must be because of the build certificate. We trust the
1011 * build certificate without any second thoughts.
1012 */
1013 if (hCertPaths == NIL_RTCRX509CERTPATHS)
1014 {
1015 if (RTCrX509Certificate_Compare(pCert, &g_BuildX509Cert) == 0) /* healthy paranoia */
1016 return VINF_SUCCESS;
1017 int rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_BUILD_CERT_IPE, "Not valid kernel code signature (fFlags=%#x).", fFlags);
1018 if (pErrInfo)
1019 {
1020 RTErrInfoAdd(pErrInfo, rc, "\n\nExe cert:\n");
1021 RTAsn1Dump(&pCert->SeqCore.Asn1Core, 0 /*fFlags*/, 0 /*uLevel*/, supHardNtViAsn1DumpToErrInfo, pErrInfo);
1022 RTErrInfoAdd(pErrInfo, rc, "\n\nBuild cert:\n");
1023 RTAsn1Dump(&g_BuildX509Cert.SeqCore.Asn1Core, 0 /*fFlags*/, 0 /*uLevel*/, supHardNtViAsn1DumpToErrInfo, pErrInfo);
1024 }
1025 return rc;
1026 }
1027
1028 /*
1029 * Standard code signing capabilites required.
1030 */
1031 int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
1032 if ( RT_SUCCESS(rc)
1033 && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
1034 {
1035 /*
1036 * For kernel code signing there are two options for a valid certificate path:
1037 * 1. Anchored by the microsoft kernel signing root certificate (g_hNtKernelRootStore).
1038 * 2. Anchored by an SPC root and signing entity including a 1.3.6.1.4.1.311.10.3.5 (WHQL)
1039 * or 1.3.6.1.4.1.311.10.3.5.1 (WHQL attestation) extended usage key.
1040 */
1041 if (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING)
1042 {
1043 uint32_t cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
1044 uint32_t cFound = 0;
1045 uint32_t cValid = 0;
1046 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
1047 {
1048 bool fTrusted;
1049 PCRTCRX509NAME pSubject;
1050 PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
1051 int rcVerify;
1052 rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
1053 NULL, NULL /*pCertCtx*/, &rcVerify);
1054 AssertRCBreak(rc);
1055
1056 if (RT_SUCCESS(rcVerify))
1057 {
1058 Assert(fTrusted);
1059 cValid++;
1060
1061 /*
1062 * 1. Search the kernel signing root store for a matching anchor.
1063 */
1064 rc = supHardNtViCertVerifyFindRootCert(g_hNtKernelRootStore, pSubject, pPublicKeyInfo);
1065 if (rc == VINF_SUCCESS)
1066 cFound++;
1067 /*
1068 * 2. Check for WHQL EKU and make sure it has a SPC root.
1069 */
1070 else if ( rc == VWRN_NOT_FOUND
1071 && ( pCert->TbsCertificate.T3.fExtKeyUsage
1072 & (RTCRX509CERT_EKU_F_MS_ATTEST_WHQL_CRYPTO | RTCRX509CERT_EKU_F_MS_WHQL_CRYPTO)))
1073 {
1074 rc = supHardNtViCertVerifyFindRootCert(g_hSpcRootStore, pSubject, pPublicKeyInfo);
1075 if (rc == VINF_SUCCESS)
1076 cFound++;
1077 }
1078 AssertRCBreak(rc);
1079 }
1080 }
1081 if (RT_SUCCESS(rc) && cFound == 0)
1082 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_VALID_KERNEL_CODE_SIGNATURE,
1083 "Signature #%u/%u: Not valid kernel code signature.",
1084 pNtViRdr->iCurSignature + 1, pNtViRdr->cTotalSignatures);
1085
1086
1087 if (RT_SUCCESS(rc) && cValid < 2 && g_fHaveOtherRoots)
1088 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNEXPECTED_VALID_PATH_COUNT,
1089 "Signature #%u/%u: Expected at least %u valid paths, not %u.",
1090 pNtViRdr->iCurSignature + 1, pNtViRdr->cTotalSignatures, 2, cValid);
1091 if (rc == VWRN_NOT_FOUND)
1092 rc = VINF_SUCCESS;
1093 }
1094 }
1095
1096 /*
1097 * More requirements? NT5 build lab?
1098 */
1099
1100 return rc;
1101 }
1102
1103
1104 /**
1105 * RTTimeNow equivaltent that handles ring-3 where we cannot use it.
1106 *
1107 * @returns pNow
1108 * @param pNow Where to return the current time.
1109 */
1110 static PRTTIMESPEC supHardNtTimeNow(PRTTIMESPEC pNow)
1111 {
1112 #ifdef IN_RING3
1113 /*
1114 * Just read system time.
1115 */
1116 KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA;
1117 # ifdef RT_ARCH_AMD64
1118 uint64_t uRet = *(uint64_t volatile *)&pUserSharedData->SystemTime; /* This is what KeQuerySystemTime does (missaligned). */
1119 return RTTimeSpecSetNtTime(pNow, uRet);
1120 # else
1121
1122 LARGE_INTEGER NtTime;
1123 do
1124 {
1125 NtTime.HighPart = pUserSharedData->SystemTime.High1Time;
1126 NtTime.LowPart = pUserSharedData->SystemTime.LowPart;
1127 } while (pUserSharedData->SystemTime.High2Time != NtTime.HighPart);
1128 return RTTimeSpecSetNtTime(pNow, NtTime.QuadPart);
1129 # endif
1130 #else /* IN_RING0 */
1131 return RTTimeNow(pNow);
1132 #endif /* IN_RING0 */
1133 }
1134
1135
1136 /**
1137 * @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA}
1138 */
1139 static DECLCALLBACK(int) supHardNtViCallback(RTLDRMOD hLdrMod, PCRTLDRSIGNATUREINFO pInfo, PRTERRINFO pErrInfo, void *pvUser)
1140 {
1141 RT_NOREF(hLdrMod);
1142
1143 /*
1144 * Check out the input.
1145 */
1146 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pvUser;
1147 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
1148 pNtViRdr->cTotalSignatures = pInfo->cSignatures;
1149 pNtViRdr->iCurSignature = pInfo->iSignature;
1150
1151 AssertReturn(pInfo->enmType == RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA, VERR_INTERNAL_ERROR_5);
1152 AssertReturn(!pInfo->pvExternalData, VERR_INTERNAL_ERROR_5);
1153 AssertReturn(pInfo->cbSignature == sizeof(RTCRPKCS7CONTENTINFO), VERR_INTERNAL_ERROR_5);
1154 PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pInfo->pvSignature;
1155 AssertReturn(RTCrPkcs7ContentInfo_IsSignedData(pContentInfo), VERR_INTERNAL_ERROR_5);
1156 AssertReturn(pContentInfo->u.pSignedData->SignerInfos.cItems == 1, VERR_INTERNAL_ERROR_5);
1157 PCRTCRPKCS7SIGNERINFO pSignerInfo = pContentInfo->u.pSignedData->SignerInfos.papItems[0];
1158
1159
1160 /*
1161 * If special certificate requirements, check them out before validating
1162 * the signature. These only apply to the first signature (for now).
1163 */
1164 if ( (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT)
1165 && pInfo->iSignature == 0)
1166 {
1167 if (!RTCrX509Certificate_MatchIssuerAndSerialNumber(&g_BuildX509Cert,
1168 &pSignerInfo->IssuerAndSerialNumber.Name,
1169 &pSignerInfo->IssuerAndSerialNumber.SerialNumber))
1170 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_SIGNED_WITH_BUILD_CERT,
1171 "Signature #%u/%u: Not signed with the build certificate (serial %.*Rhxs, expected %.*Rhxs)",
1172 pInfo->iSignature + 1, pInfo->cSignatures,
1173 pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.cb,
1174 pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.uData.pv,
1175 g_BuildX509Cert.TbsCertificate.SerialNumber.Asn1Core.cb,
1176 g_BuildX509Cert.TbsCertificate.SerialNumber.Asn1Core.uData.pv);
1177 }
1178
1179 /*
1180 * We instruction the verifier to use the signing time counter signature
1181 * when present, but provides the linker time then the current time as
1182 * fallbacks should the timestamp be missing or unusable.
1183 *
1184 * Update: Save the first timestamp we validate with build cert and
1185 * use this as a minimum timestamp for further build cert
1186 * validations. This works around issues with old DLLs that
1187 * we sign against with our certificate (crt, sdl, qt).
1188 *
1189 * Update: If the validation fails, retry with the current timestamp. This
1190 * is a workaround for NTDLL.DLL in build 14971 having a weird
1191 * timestamp: 0xDF1E957E (Sat Aug 14 14:05:18 2088).
1192 */
1193 uint32_t fFlags = RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
1194 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
1195 | RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY;
1196
1197 /* In ring-0 we don't have all the necessary timestamp server root certificate
1198 * info, so we have to allow using counter signatures unverified there.
1199 * Ditto for the early period of ring-3 hardened stub execution. */
1200 #ifndef IN_RING0
1201 if (!g_fHaveOtherRoots)
1202 #endif
1203 fFlags |= RTCRPKCS7VERIFY_SD_F_USE_SIGNING_TIME_UNVERIFIED | RTCRPKCS7VERIFY_SD_F_USE_MS_TIMESTAMP_UNVERIFIED;
1204
1205 /* Fallback timestamps to try: */
1206 struct { RTTIMESPEC TimeSpec; const char *pszDesc; } aTimes[2];
1207 unsigned cTimes = 0;
1208
1209 /* 1. The linking timestamp: */
1210 uint64_t uTimestamp = 0;
1211 int rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp, sizeof(uTimestamp));
1212 if (RT_SUCCESS(rc))
1213 {
1214 #ifdef IN_RING3 /* Hack alert! (see above) */
1215 if ( (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING)
1216 && (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT)
1217 && uTimestamp < g_uBuildTimestampHack)
1218 uTimestamp = g_uBuildTimestampHack;
1219 #endif
1220 RTTimeSpecSetSeconds(&aTimes[0].TimeSpec, uTimestamp);
1221 aTimes[0].pszDesc = "link";
1222 cTimes++;
1223 }
1224 else
1225 SUP_DPRINTF(("RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on %s: %Rrc", pNtViRdr->szFilename, rc));
1226
1227 /* 2. Current time. */
1228 supHardNtTimeNow(&aTimes[cTimes].TimeSpec);
1229 aTimes[cTimes].pszDesc = "now";
1230 cTimes++;
1231
1232 /* Make the verfication attempts. */
1233 for (unsigned i = 0; ; i++)
1234 {
1235 Assert(i < cTimes);
1236 rc = RTCrPkcs7VerifySignedData(pContentInfo, fFlags, g_hSpcAndNtKernelSuppStore, g_hSpcAndNtKernelRootStore,
1237 &aTimes[i].TimeSpec, supHardNtViCertVerifyCallback, pNtViRdr, pErrInfo);
1238 if (RT_SUCCESS(rc))
1239 {
1240 if (rc != VINF_SUCCESS)
1241 {
1242 SUP_DPRINTF(("%s: Signature #%u/%u: info status: %d\n", pNtViRdr->szFilename, pInfo->iSignature + 1, pInfo->cSignatures, rc));
1243 if (pNtViRdr->rcLastSignatureFailure == VINF_SUCCESS)
1244 pNtViRdr->rcLastSignatureFailure = rc;
1245 }
1246 pNtViRdr->cOkaySignatures++;
1247
1248 #ifdef IN_RING3 /* Hack alert! (see above) */
1249 if ((pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT) && g_uBuildTimestampHack == 0 && cTimes > 1)
1250 g_uBuildTimestampHack = uTimestamp;
1251 #endif
1252 return VINF_SUCCESS;
1253 }
1254
1255 if (rc == VERR_CR_X509_CPV_NOT_VALID_AT_TIME && i + 1 < cTimes)
1256 SUP_DPRINTF(("%s: Signature #%u/%u: VERR_CR_X509_CPV_NOT_VALID_AT_TIME for %#RX64; retrying against current time: %#RX64.\n",
1257 pNtViRdr->szFilename, pInfo->iSignature + 1, pInfo->cSignatures,
1258 RTTimeSpecGetSeconds(&aTimes[0].TimeSpec), RTTimeSpecGetSeconds(&aTimes[1].TimeSpec)));
1259 else
1260 {
1261 /* There are a couple of failures we can tollerate if there are more than
1262 one signature and one of them works out fine. The RTLdrVerifySignature
1263 caller will have to check the failure counts though to make sure
1264 something succeeded. */
1265 pNtViRdr->rcLastSignatureFailure = rc;
1266 if ( rc == VERR_CR_X509_CPV_NOT_VALID_AT_TIME
1267 || rc == VERR_CR_X509_CPV_NO_TRUSTED_PATHS)
1268 {
1269 SUP_DPRINTF(("%s: Signature #%u/%u: %s (%d) w/ timestamp=%#RX64/%s.\n", pNtViRdr->szFilename, pInfo->iSignature + 1, pInfo->cSignatures,
1270 rc == VERR_CR_X509_CPV_NOT_VALID_AT_TIME ? "VERR_CR_X509_CPV_NOT_VALID_AT_TIME" : "VERR_CR_X509_CPV_NO_TRUSTED_PATHS", rc,
1271 RTTimeSpecGetSeconds(&aTimes[i].TimeSpec), aTimes[i].pszDesc));
1272
1273 /* This leniency is not applicable to build certificate requirements (signature #1 only). */
1274 if ( !(pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT)
1275 || pInfo->iSignature != 0)
1276 {
1277 pNtViRdr->cNokSignatures++;
1278 rc = VINF_SUCCESS;
1279 }
1280 }
1281 else
1282 SUP_DPRINTF(("%s: Signature #%u/%u: %Rrc w/ timestamp=%#RX64/%s.\n", pNtViRdr->szFilename, pInfo->iSignature + 1, pInfo->cSignatures,
1283 rc, RTTimeSpecGetSeconds(&aTimes[i].TimeSpec), aTimes[i].pszDesc));
1284 return rc;
1285 }
1286 }
1287 }
1288
1289
1290 /**
1291 * Verifies the given loader image.
1292 *
1293 * @returns IPRT status code.
1294 * @param hLdrMod File handle to the executable file.
1295 * @param pwszName Full NT path to the DLL in question, used for
1296 * dealing with unsigned system dlls as well as for
1297 * error/logging.
1298 * @param pNtViRdr The reader instance /w flags.
1299 * @param fAvoidWinVerifyTrust Whether to avoid WinVerifyTrust because of
1300 * deadlock or other loader related dangers.
1301 * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was used.
1302 * @param pErrInfo Pointer to error info structure. Optional.
1303 */
1304 DECLHIDDEN(int) supHardenedWinVerifyImageByLdrMod(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, PSUPHNTVIRDR pNtViRdr,
1305 bool fAvoidWinVerifyTrust, bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
1306 {
1307 if (pfWinVerifyTrust)
1308 *pfWinVerifyTrust = false;
1309
1310 #ifdef IN_RING3
1311 /* Check that the caller has performed the necessary library initialization. */
1312 if (!RTCrX509Certificate_IsPresent(&g_BuildX509Cert))
1313 return RTErrInfoSet(pErrInfo, VERR_WRONG_ORDER,
1314 "supHardenedWinVerifyImageByHandle: supHardenedWinInitImageVerifier was not called.");
1315 #endif
1316
1317 /*
1318 * Check the trusted installer bit first, if requested as it's somewhat
1319 * cheaper than the rest.
1320 *
1321 * We relax this for system32 and a little for WinSxS, like we used to, as
1322 * there are apparently some systems out there where the user, admin, or
1323 * someone has changed the ownership of core windows DLLs like user32.dll
1324 * and comctl32.dll. Since we need user32.dll and will be checking it's
1325 * digital signature, it's reasonably safe to let this thru. (The report
1326 * was of SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS
1327 * owning user32.dll, see public ticket 13187, VBoxStartup.3.log.)
1328 *
1329 * We've also had problems with graphics driver components like ig75icd64.dll
1330 * and atig6pxx.dll not being owned by TrustedInstaller, with the result
1331 * that 3D got broken (mod by zero issue in test build 5). These were also
1332 * SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS.
1333 *
1334 * In one report by 'thor' the WinSxS resident comctl32.dll was owned by
1335 * SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS (with 4.3.16).
1336 */
1337 /** @todo Since we're now allowing Builtin\\Administrators after all, perhaps we
1338 * could drop these system32 + winsxs hacks?? */
1339 if ( (pNtViRdr->fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
1340 && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(pNtViRdr->hFile, pwszName))
1341 {
1342 if (supHardViUtf16PathStartsWithEx(pwszName, (uint32_t)RTUtf16Len(pwszName),
1343 g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length / sizeof(WCHAR),
1344 true /*fCheckSlash*/))
1345 SUP_DPRINTF(("%ls: Relaxing the TrustedInstaller requirement for this DLL (it's in system32).\n", pwszName));
1346 else if (supHardViUtf16PathStartsWithEx(pwszName, (uint32_t)RTUtf16Len(pwszName),
1347 g_WinSxSNtPath.UniStr.Buffer, g_WinSxSNtPath.UniStr.Length / sizeof(WCHAR),
1348 true /*fCheckSlash*/))
1349 SUP_DPRINTF(("%ls: Relaxing the TrustedInstaller requirement for this DLL (it's in WinSxS).\n", pwszName));
1350 else
1351 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_OWNED_BY_TRUSTED_INSTALLER,
1352 "supHardenedWinVerifyImageByHandle: TrustedInstaller is not the owner of '%ls'.", pwszName);
1353 }
1354
1355 /*
1356 * Verify it.
1357 *
1358 * The PKCS #7 SignedData signature is checked in the callback. Any
1359 * signing certificate restrictions are also enforced there.
1360 */
1361 pNtViRdr->cOkaySignatures = 0;
1362 pNtViRdr->cNokSignatures = 0;
1363 pNtViRdr->cTotalSignatures = 0;
1364 pNtViRdr->rcLastSignatureFailure = VINF_SUCCESS;
1365 int rc = RTLdrVerifySignature(hLdrMod, supHardNtViCallback, pNtViRdr, pErrInfo);
1366 if (RT_SUCCESS(rc))
1367 {
1368 Assert(pNtViRdr->cOkaySignatures + pNtViRdr->cNokSignatures == pNtViRdr->cTotalSignatures);
1369 if ( !pNtViRdr->cOkaySignatures
1370 || pNtViRdr->cOkaySignatures + pNtViRdr->cNokSignatures < pNtViRdr->cTotalSignatures /* paranoia */)
1371 {
1372 rc = pNtViRdr->rcLastSignatureFailure;
1373 AssertStmt(RT_FAILURE_NP(rc), rc = VERR_INTERNAL_ERROR_3);
1374 }
1375 else if (rc == VINF_SUCCESS && RT_SUCCESS(pNtViRdr->rcLastSignatureFailure))
1376 rc = pNtViRdr->rcLastSignatureFailure;
1377 }
1378
1379 /*
1380 * Microsoft doesn't sign a whole bunch of DLLs, so we have to
1381 * ASSUME that a bunch of system DLLs are fine.
1382 */
1383 if (rc == VERR_LDRVI_NOT_SIGNED)
1384 rc = supHardNtViCheckIfNotSignedOk(hLdrMod, pwszName, pNtViRdr->fFlags, pNtViRdr->hFile, rc);
1385 if (RT_FAILURE(rc))
1386 RTErrInfoAddF(pErrInfo, rc, ": %ls", pwszName);
1387
1388 /*
1389 * Check for the signature checking enforcement, if requested to do so.
1390 */
1391 if (RT_SUCCESS(rc) && (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT))
1392 {
1393 bool fEnforced = false;
1394 int rc2 = RTLdrQueryProp(hLdrMod, RTLDRPROP_SIGNATURE_CHECKS_ENFORCED, &fEnforced, sizeof(fEnforced));
1395 if (RT_FAILURE(rc2))
1396 rc = RTErrInfoSetF(pErrInfo, rc2, "Querying RTLDRPROP_SIGNATURE_CHECKS_ENFORCED failed on %ls: %Rrc.",
1397 pwszName, rc2);
1398 else if (!fEnforced)
1399 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SIGNATURE_CHECKS_NOT_ENFORCED,
1400 "The image '%ls' was not linked with /IntegrityCheck.", pwszName);
1401 }
1402
1403 #ifdef IN_RING3
1404 /*
1405 * Pass it thru WinVerifyTrust when possible.
1406 */
1407 if (!fAvoidWinVerifyTrust)
1408 rc = supHardenedWinVerifyImageTrust(pNtViRdr->hFile, pwszName, pNtViRdr->fFlags, rc, pfWinVerifyTrust, pErrInfo);
1409 #else
1410 RT_NOREF1(fAvoidWinVerifyTrust);
1411 #endif
1412
1413 /*
1414 * Check for blacklisted DLLs, both internal name and filename.
1415 */
1416 if (RT_SUCCESS(rc))
1417 {
1418 size_t const cwcName = RTUtf16Len(pwszName);
1419 char szIntName[64];
1420 int rc2 = RTLdrQueryProp(hLdrMod, RTLDRPROP_INTERNAL_NAME, szIntName, sizeof(szIntName));
1421 if (RT_SUCCESS(rc2))
1422 {
1423 size_t const cchIntName = strlen(szIntName);
1424 for (unsigned i = 0; g_aSupNtViBlacklistedDlls[i].psz != NULL; i++)
1425 if ( cchIntName == g_aSupNtViBlacklistedDlls[i].cch
1426 && RTStrICmpAscii(szIntName, g_aSupNtViBlacklistedDlls[i].psz) == 0)
1427 {
1428 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNDESIRABLE_MODULE,
1429 "The image '%ls' is listed as undesirable.", pwszName);
1430 break;
1431 }
1432 }
1433 if (RT_SUCCESS(rc))
1434 {
1435 for (unsigned i = 0; g_aSupNtViBlacklistedDlls[i].psz != NULL; i++)
1436 if (cwcName >= g_aSupNtViBlacklistedDlls[i].cch)
1437 {
1438 PCRTUTF16 pwszTmp = &pwszName[cwcName - g_aSupNtViBlacklistedDlls[i].cch];
1439 if ( ( cwcName == g_aSupNtViBlacklistedDlls[i].cch
1440 || pwszTmp[-1] == '\\'
1441 || pwszTmp[-1] == '/')
1442 && RTUtf16ICmpAscii(pwszTmp, g_aSupNtViBlacklistedDlls[i].psz) == 0)
1443 {
1444 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNDESIRABLE_MODULE,
1445 "The image '%ls' is listed as undesirable.", pwszName);
1446 break;
1447 }
1448 }
1449 }
1450 }
1451
1452 #ifdef IN_SUP_HARDENED_R3
1453 /*
1454 * Hook for the LdrLoadDll code to schedule scanning of imports.
1455 */
1456 if (RT_SUCCESS(rc))
1457 supR3HardenedWinVerifyCacheScheduleImports(hLdrMod, pwszName);
1458 #endif
1459
1460 return rc;
1461 }
1462
1463
1464 /**
1465 * Verifies the given executable image.
1466 *
1467 * @returns IPRT status code.
1468 * @param hFile File handle to the executable file.
1469 * @param pwszName Full NT path to the DLL in question, used for
1470 * dealing with unsigned system dlls as well as for
1471 * error/logging.
1472 * @param fFlags Flags, SUPHNTVI_F_XXX.
1473 * @param fAvoidWinVerifyTrust Whether to avoid WinVerifyTrust because of
1474 * deadlock or other loader related dangers.
1475 * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was used.
1476 * @param pErrInfo Pointer to error info structure. Optional.
1477 */
1478 DECLHIDDEN(int) supHardenedWinVerifyImageByHandle(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, bool fAvoidWinVerifyTrust,
1479 bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
1480 {
1481 /*
1482 * Create a reader instance.
1483 */
1484 PSUPHNTVIRDR pNtViRdr;
1485 int rc = supHardNtViRdrCreate(hFile, pwszName, fFlags, &pNtViRdr);
1486 if (RT_SUCCESS(rc))
1487 {
1488 /*
1489 * Open the image.
1490 */
1491 RTLDRMOD hLdrMod;
1492 RTLDRARCH enmArch = fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST;
1493 uint32_t fLdrFlags = RTLDR_O_FOR_VALIDATION | RTLDR_O_IGNORE_ARCH_IF_NO_CODE;
1494 if (fFlags & SUPHNTVI_F_IGNORE_ARCHITECTURE)
1495 fLdrFlags |= RTLDR_O_IGNORE_ARCH_IF_NO_CODE;
1496 rc = RTLdrOpenWithReader(&pNtViRdr->Core, fLdrFlags, enmArch, &hLdrMod, pErrInfo);
1497 if (RT_SUCCESS(rc))
1498 {
1499 /*
1500 * Verify it.
1501 */
1502 rc = supHardenedWinVerifyImageByLdrMod(hLdrMod, pwszName, pNtViRdr, fAvoidWinVerifyTrust, pfWinVerifyTrust, pErrInfo);
1503 int rc2 = RTLdrClose(hLdrMod); AssertRC(rc2);
1504 }
1505 else
1506 supHardNtViRdrDestroy(&pNtViRdr->Core);
1507 }
1508 SUP_DPRINTF(("supHardenedWinVerifyImageByHandle: -> %d (%ls)%s\n",
1509 rc, pwszName, pfWinVerifyTrust && *pfWinVerifyTrust ? " WinVerifyTrust" : ""));
1510 return rc;
1511 }
1512
1513
1514 #ifdef IN_RING3
1515 /**
1516 * supHardenedWinVerifyImageByHandle version without the name.
1517 *
1518 * The name is derived from the handle.
1519 *
1520 * @returns IPRT status code.
1521 * @param hFile File handle to the executable file.
1522 * @param fFlags Flags, SUPHNTVI_F_XXX.
1523 * @param pErrInfo Pointer to error info structure. Optional.
1524 */
1525 DECLHIDDEN(int) supHardenedWinVerifyImageByHandleNoName(HANDLE hFile, uint32_t fFlags, PRTERRINFO pErrInfo)
1526 {
1527 /*
1528 * Determine the NT name and call the verification function.
1529 */
1530 union
1531 {
1532 UNICODE_STRING UniStr;
1533 uint8_t abBuffer[(MAX_PATH + 8 + 1) * 2];
1534 } uBuf;
1535
1536 ULONG cbIgn;
1537 NTSTATUS rcNt = NtQueryObject(hFile,
1538 ObjectNameInformation,
1539 &uBuf,
1540 sizeof(uBuf) - sizeof(WCHAR),
1541 &cbIgn);
1542 if (NT_SUCCESS(rcNt))
1543 uBuf.UniStr.Buffer[uBuf.UniStr.Length / sizeof(WCHAR)] = '\0';
1544 else
1545 uBuf.UniStr.Buffer = (WCHAR *)L"TODO3";
1546
1547 return supHardenedWinVerifyImageByHandle(hFile, uBuf.UniStr.Buffer, fFlags, false /*fAvoidWinVerifyTrust*/,
1548 NULL /*pfWinVerifyTrust*/, pErrInfo);
1549 }
1550 #endif /* IN_RING3 */
1551
1552
1553 /**
1554 * Retrieves the full official path to the system root or one of it's sub
1555 * directories.
1556 *
1557 * This code is also used by the support driver.
1558 *
1559 * @returns VBox status code.
1560 * @param pvBuf The output buffer. This will contain a
1561 * UNICODE_STRING followed (at the kernel's
1562 * discretion) the string buffer.
1563 * @param cbBuf The size of the buffer @a pvBuf points to.
1564 * @param enmDir Which directory under the system root we're
1565 * interested in.
1566 * @param pErrInfo Pointer to error info structure. Optional.
1567 */
1568 DECLHIDDEN(int) supHardNtGetSystemRootDir(void *pvBuf, uint32_t cbBuf, SUPHARDNTSYSROOTDIR enmDir, PRTERRINFO pErrInfo)
1569 {
1570 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
1571 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1572
1573 UNICODE_STRING NtName;
1574 switch (enmDir)
1575 {
1576 case kSupHardNtSysRootDir_System32:
1577 {
1578 static const WCHAR s_wszNameSystem32[] = L"\\SystemRoot\\System32\\";
1579 NtName.Buffer = (PWSTR)s_wszNameSystem32;
1580 NtName.Length = sizeof(s_wszNameSystem32) - sizeof(WCHAR);
1581 NtName.MaximumLength = sizeof(s_wszNameSystem32);
1582 break;
1583 }
1584 case kSupHardNtSysRootDir_WinSxS:
1585 {
1586 static const WCHAR s_wszNameWinSxS[] = L"\\SystemRoot\\WinSxS\\";
1587 NtName.Buffer = (PWSTR)s_wszNameWinSxS;
1588 NtName.Length = sizeof(s_wszNameWinSxS) - sizeof(WCHAR);
1589 NtName.MaximumLength = sizeof(s_wszNameWinSxS);
1590 break;
1591 }
1592 default:
1593 AssertFailed();
1594 return VERR_INVALID_PARAMETER;
1595 }
1596
1597 OBJECT_ATTRIBUTES ObjAttr;
1598 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1599
1600 NTSTATUS rcNt = NtCreateFile(&hFile,
1601 FILE_READ_DATA | SYNCHRONIZE,
1602 &ObjAttr,
1603 &Ios,
1604 NULL /* Allocation Size*/,
1605 FILE_ATTRIBUTE_NORMAL,
1606 FILE_SHARE_READ | FILE_SHARE_WRITE,
1607 FILE_OPEN,
1608 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1609 NULL /*EaBuffer*/,
1610 0 /*EaLength*/);
1611 if (NT_SUCCESS(rcNt))
1612 rcNt = Ios.Status;
1613 if (NT_SUCCESS(rcNt))
1614 {
1615 ULONG cbIgn;
1616 rcNt = NtQueryObject(hFile,
1617 ObjectNameInformation,
1618 pvBuf,
1619 cbBuf - sizeof(WCHAR),
1620 &cbIgn);
1621 NtClose(hFile);
1622 if (NT_SUCCESS(rcNt))
1623 {
1624 PUNICODE_STRING pUniStr = (PUNICODE_STRING)pvBuf;
1625 if (pUniStr->Length > 0)
1626 {
1627 /* Make sure it's terminated so it can safely be printed.*/
1628 pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
1629 return VINF_SUCCESS;
1630 }
1631
1632 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH,
1633 "NtQueryObject returned an empty path for '%ls'", NtName.Buffer);
1634 }
1635 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "NtQueryObject failed on '%ls' dir: %#x", NtName.Buffer, rcNt);
1636 }
1637 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "Failure to open '%ls': %#x", NtName.Buffer, rcNt);
1638 }
1639
1640
1641 /**
1642 * Initialize one certificate entry.
1643 *
1644 * @returns VBox status code.
1645 * @param pCert The X.509 certificate representation to init.
1646 * @param pabCert The raw DER encoded certificate.
1647 * @param cbCert The size of the raw certificate.
1648 * @param pErrInfo Where to return extended error info. Optional.
1649 * @param pszErrorTag Error tag.
1650 */
1651 static int supHardNtViCertInit(PRTCRX509CERTIFICATE pCert, unsigned char const *pabCert, unsigned cbCert,
1652 PRTERRINFO pErrInfo, const char *pszErrorTag)
1653 {
1654 AssertReturn(cbCert > 16 && cbCert < _128K,
1655 RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, "%s: cbCert=%#x out of range", pszErrorTag, cbCert));
1656 AssertReturn(!RTCrX509Certificate_IsPresent(pCert),
1657 RTErrInfoSetF(pErrInfo, VERR_WRONG_ORDER, "%s: Certificate already decoded?", pszErrorTag));
1658
1659 RTASN1CURSORPRIMARY PrimaryCursor;
1660 RTAsn1CursorInitPrimary(&PrimaryCursor, pabCert, cbCert, pErrInfo, &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, NULL);
1661 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, pCert, pszErrorTag);
1662 if (RT_SUCCESS(rc))
1663 rc = RTCrX509Certificate_CheckSanity(pCert, 0, pErrInfo, pszErrorTag);
1664 return rc;
1665 }
1666
1667
1668 static int supHardNtViCertStoreAddArray(RTCRSTORE hStore, PCSUPTAENTRY paCerts, unsigned cCerts, PRTERRINFO pErrInfo)
1669 {
1670 for (uint32_t i = 0; i < cCerts; i++)
1671 {
1672 int rc = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_TAF_DER, paCerts[i].pch, paCerts[i].cb, pErrInfo);
1673 if (RT_FAILURE(rc))
1674 return rc;
1675 }
1676 return VINF_SUCCESS;
1677 }
1678
1679
1680 /**
1681 * Initialize a certificate table.
1682 *
1683 * @param phStore Where to return the store pointer.
1684 * @param paCerts1 Pointer to the first certificate table.
1685 * @param cCerts1 Entries in the first certificate table.
1686 * @param paCerts2 Pointer to the second certificate table.
1687 * @param cCerts2 Entries in the second certificate table.
1688 * @param paCerts3 Pointer to the third certificate table.
1689 * @param cCerts3 Entries in the third certificate table.
1690 * @param pErrInfo Where to return extended error info. Optional.
1691 * @param pszErrorTag Error tag.
1692 */
1693 static int supHardNtViCertStoreInit(PRTCRSTORE phStore,
1694 PCSUPTAENTRY paCerts1, unsigned cCerts1,
1695 PCSUPTAENTRY paCerts2, unsigned cCerts2,
1696 PCSUPTAENTRY paCerts3, unsigned cCerts3,
1697 PRTERRINFO pErrInfo, const char *pszErrorTag)
1698 {
1699 AssertReturn(*phStore == NIL_RTCRSTORE, VERR_WRONG_ORDER);
1700 RT_NOREF1(pszErrorTag);
1701
1702 int rc = RTCrStoreCreateInMem(phStore, cCerts1 + cCerts2);
1703 if (RT_FAILURE(rc))
1704 return RTErrInfoSetF(pErrInfo, rc, "RTCrStoreCreateMemoryStore failed: %Rrc", rc);
1705
1706 rc = supHardNtViCertStoreAddArray(*phStore, paCerts1, cCerts1, pErrInfo);
1707 if (RT_SUCCESS(rc))
1708 rc = supHardNtViCertStoreAddArray(*phStore, paCerts2, cCerts2, pErrInfo);
1709 if (RT_SUCCESS(rc))
1710 rc = supHardNtViCertStoreAddArray(*phStore, paCerts3, cCerts3, pErrInfo);
1711 return rc;
1712 }
1713
1714
1715 #if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
1716 /**
1717 * Initializes the windows paths.
1718 */
1719 static void supHardenedWinInitImageVerifierWinPaths(void)
1720 {
1721 /*
1722 * Windows paths that we're interested in.
1723 */
1724 static const struct
1725 {
1726 SUPSYSROOTDIRBUF *pNtPath;
1727 WCHAR const *pwszRegValue;
1728 const char *pszLogName;
1729 } s_aPaths[] =
1730 {
1731 { &g_ProgramFilesNtPath, L"ProgramFilesDir", "ProgDir" },
1732 { &g_CommonFilesNtPath, L"CommonFilesDir", "ComDir" },
1733 # ifdef RT_ARCH_AMD64
1734 { &g_ProgramFilesX86NtPath, L"ProgramFilesDir (x86)", "ProgDir32" },
1735 { &g_CommonFilesX86NtPath, L"CommonFilesDir (x86)", "ComDir32" },
1736 # endif
1737 };
1738
1739 /*
1740 * Open the registry key containing the paths.
1741 */
1742 UNICODE_STRING NtName = RTNT_CONSTANT_UNISTR(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion");
1743 OBJECT_ATTRIBUTES ObjAttr;
1744 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1745 HANDLE hKey;
1746 NTSTATUS rcNt = NtOpenKey(&hKey, KEY_QUERY_VALUE, &ObjAttr);
1747 if (NT_SUCCESS(rcNt))
1748 {
1749 /*
1750 * Loop over the paths and resolve their NT paths.
1751 */
1752 for (uint32_t i = 0; i < RT_ELEMENTS(s_aPaths); i++)
1753 {
1754 /*
1755 * Query the value first.
1756 */
1757 UNICODE_STRING ValueName;
1758 ValueName.Buffer = (WCHAR *)s_aPaths[i].pwszRegValue;
1759 ValueName.Length = (USHORT)(RTUtf16Len(s_aPaths[i].pwszRegValue) * sizeof(WCHAR));
1760 ValueName.MaximumLength = ValueName.Length + sizeof(WCHAR);
1761
1762 union
1763 {
1764 KEY_VALUE_PARTIAL_INFORMATION PartialInfo;
1765 uint8_t abPadding[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(WCHAR) * 128];
1766 uint64_t uAlign;
1767 } uBuf;
1768
1769 ULONG cbActual = 0;
1770 rcNt = NtQueryValueKey(hKey, &ValueName, KeyValuePartialInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR), &cbActual);
1771 if (NT_SUCCESS(rcNt))
1772 {
1773 /*
1774 * Must be a simple string value, terminate it.
1775 */
1776 if ( uBuf.PartialInfo.Type == REG_EXPAND_SZ
1777 || uBuf.PartialInfo.Type == REG_SZ)
1778 {
1779 /*
1780 * Expand any environment variable references before opening it.
1781 * We use the result buffer as storage for the expaneded path,
1782 * reserving space for the windows name space prefix.
1783 */
1784 UNICODE_STRING Src;
1785 Src.Buffer = (WCHAR *)uBuf.PartialInfo.Data;
1786 Src.Length = uBuf.PartialInfo.DataLength;
1787 if (Src.Length >= sizeof(WCHAR) && Src.Buffer[Src.Length / sizeof(WCHAR) - 1] == '\0')
1788 Src.Length -= sizeof(WCHAR);
1789 Src.MaximumLength = Src.Length + sizeof(WCHAR);
1790 Src.Buffer[uBuf.PartialInfo.DataLength / sizeof(WCHAR)] = '\0';
1791
1792 s_aPaths[i].pNtPath->awcBuffer[0] = '\\';
1793 s_aPaths[i].pNtPath->awcBuffer[1] = '?';
1794 s_aPaths[i].pNtPath->awcBuffer[2] = '?';
1795 s_aPaths[i].pNtPath->awcBuffer[3] = '\\';
1796 UNICODE_STRING Dst;
1797 Dst.Buffer = &s_aPaths[i].pNtPath->awcBuffer[4];
1798 Dst.MaximumLength = sizeof(s_aPaths[i].pNtPath->awcBuffer) - sizeof(WCHAR) * 5;
1799 Dst.Length = Dst.MaximumLength;
1800
1801 if (uBuf.PartialInfo.Type == REG_EXPAND_SZ)
1802 rcNt = RtlExpandEnvironmentStrings_U(NULL, &Src, &Dst, NULL);
1803 else
1804 {
1805 memcpy(Dst.Buffer, Src.Buffer, Src.Length);
1806 Dst.Length = Src.Length;
1807 }
1808 if (NT_SUCCESS(rcNt))
1809 {
1810 Dst.Buffer[Dst.Length / sizeof(WCHAR)] = '\0';
1811
1812 /*
1813 * Include the \\??\\ prefix in the result and open the path.
1814 */
1815 Dst.Buffer -= 4;
1816 Dst.Length += 4 * sizeof(WCHAR);
1817 Dst.MaximumLength += 4 * sizeof(WCHAR);
1818 InitializeObjectAttributes(&ObjAttr, &Dst, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1819 HANDLE hFile = INVALID_HANDLE_VALUE;
1820 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1821 NTSTATUS rcNt = NtCreateFile(&hFile,
1822 FILE_READ_DATA | SYNCHRONIZE,
1823 &ObjAttr,
1824 &Ios,
1825 NULL /* Allocation Size*/,
1826 FILE_ATTRIBUTE_NORMAL,
1827 FILE_SHARE_READ | FILE_SHARE_WRITE,
1828 FILE_OPEN,
1829 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
1830 | FILE_SYNCHRONOUS_IO_NONALERT,
1831 NULL /*EaBuffer*/,
1832 0 /*EaLength*/);
1833 if (NT_SUCCESS(rcNt))
1834 rcNt = Ios.Status;
1835 if (NT_SUCCESS(rcNt))
1836 {
1837 /*
1838 * Query the real NT name.
1839 */
1840 ULONG cbIgn;
1841 rcNt = NtQueryObject(hFile,
1842 ObjectNameInformation,
1843 s_aPaths[i].pNtPath,
1844 sizeof(*s_aPaths[i].pNtPath) - sizeof(WCHAR),
1845 &cbIgn);
1846 if (NT_SUCCESS(rcNt))
1847 {
1848 if (s_aPaths[i].pNtPath->UniStr.Length > 0)
1849 {
1850 /* Make sure it's terminated.*/
1851 s_aPaths[i].pNtPath->UniStr.Buffer[s_aPaths[i].pNtPath->UniStr.Length / sizeof(WCHAR)] = '\0';
1852 SUP_DPRINTF(("%s:%*s %ls\n", s_aPaths[i].pszLogName, 9 - strlen(s_aPaths[i].pszLogName), "",
1853 s_aPaths[i].pNtPath->UniStr.Buffer));
1854 }
1855 else
1856 {
1857 SUP_DPRINTF(("%s: NtQueryObject returned empty string\n", s_aPaths[i].pszLogName));
1858 rcNt = STATUS_INVALID_PARAMETER;
1859 }
1860 }
1861 else
1862 SUP_DPRINTF(("%s: NtQueryObject failed: %#x\n", s_aPaths[i].pszLogName, rcNt));
1863 NtClose(hFile);
1864 }
1865 else
1866 SUP_DPRINTF(("%s: NtCreateFile failed: %#x (%ls)\n",
1867 s_aPaths[i].pszLogName, rcNt, Dst.Buffer));
1868 }
1869 else
1870 SUP_DPRINTF(("%s: RtlExpandEnvironmentStrings_U failed: %#x (%ls)\n",
1871 s_aPaths[i].pszLogName, rcNt, Src.Buffer));
1872 }
1873 else
1874 {
1875 SUP_DPRINTF(("%s: type mismatch: %#x\n", s_aPaths[i].pszLogName, uBuf.PartialInfo.Type));
1876 rcNt = STATUS_INVALID_PARAMETER;
1877 }
1878 }
1879 else
1880 SUP_DPRINTF(("%s: NtQueryValueKey failed: %#x\n", s_aPaths[i].pszLogName, rcNt));
1881
1882 /* Stub the entry on failure. */
1883 if (!NT_SUCCESS(rcNt))
1884 {
1885 s_aPaths[i].pNtPath->UniStr.Length = 0;
1886 s_aPaths[i].pNtPath->UniStr.Buffer = NULL;
1887 }
1888 }
1889 NtClose(hKey);
1890 }
1891 else
1892 {
1893 SUP_DPRINTF(("NtOpenKey(%ls) failed: %#x\n", NtName.Buffer, rcNt));
1894
1895 /* Stub all the entries on failure. */
1896 for (uint32_t i = 0; i < RT_ELEMENTS(s_aPaths); i++)
1897 {
1898 s_aPaths[i].pNtPath->UniStr.Length = 0;
1899 s_aPaths[i].pNtPath->UniStr.Buffer = NULL;
1900 }
1901 }
1902 }
1903 #endif /* IN_RING3 && !VBOX_PERMIT_EVEN_MORE */
1904
1905
1906 /**
1907 * This initializes the certificates globals so we don't have to reparse them
1908 * every time we need to verify an image.
1909 *
1910 * @returns IPRT status code.
1911 * @param pErrInfo Where to return extended error info. Optional.
1912 */
1913 DECLHIDDEN(int) supHardenedWinInitImageVerifier(PRTERRINFO pErrInfo)
1914 {
1915 AssertReturn(!RTCrX509Certificate_IsPresent(&g_BuildX509Cert), VERR_WRONG_ORDER);
1916
1917 /*
1918 * Get the system root paths.
1919 */
1920 int rc = supHardNtGetSystemRootDir(&g_System32NtPath, sizeof(g_System32NtPath), kSupHardNtSysRootDir_System32, pErrInfo);
1921 if (RT_SUCCESS(rc))
1922 rc = supHardNtGetSystemRootDir(&g_WinSxSNtPath, sizeof(g_WinSxSNtPath), kSupHardNtSysRootDir_WinSxS, pErrInfo);
1923 if (RT_SUCCESS(rc))
1924 {
1925 SUP_DPRINTF(("System32: %ls\n", g_System32NtPath.UniStr.Buffer));
1926 SUP_DPRINTF(("WinSxS: %ls\n", g_WinSxSNtPath.UniStr.Buffer));
1927 #if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
1928 supHardenedWinInitImageVerifierWinPaths();
1929 #endif
1930
1931 /*
1932 * Initialize it, leaving the cleanup to the termination call.
1933 */
1934 rc = supHardNtViCertInit(&g_BuildX509Cert, g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo, "BuildCertificate");
1935 if (RT_SUCCESS(rc))
1936 rc = supHardNtViCertStoreInit(&g_hSpcRootStore, g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
1937 NULL, 0, NULL, 0, pErrInfo, "SpcRoot");
1938 if (RT_SUCCESS(rc))
1939 rc = supHardNtViCertStoreInit(&g_hNtKernelRootStore, g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
1940 NULL, 0, NULL, 0, pErrInfo, "NtKernelRoot");
1941 if (RT_SUCCESS(rc))
1942 rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelRootStore,
1943 g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
1944 g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
1945 g_aSUPTimestampTAs, g_cSUPTimestampTAs,
1946 pErrInfo, "SpcAndNtKernelRoot");
1947 if (RT_SUCCESS(rc))
1948 rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelSuppStore,
1949 NULL, 0, NULL, 0, NULL, 0,
1950 pErrInfo, "SpcAndNtKernelSupplemental");
1951
1952 #if 0 /* For the time being, always trust the build certificate. It bypasses the timestamp issues of CRT and SDL. */
1953 /* If the build certificate is a test singing certificate, it must be a
1954 trusted root or we'll fail to validate anything. */
1955 if ( RT_SUCCESS(rc)
1956 && RTCrX509Name_Compare(&g_BuildX509Cert.TbsCertificate.Subject, &g_BuildX509Cert.TbsCertificate.Issuer) == 0)
1957 #else
1958 if (RT_SUCCESS(rc))
1959 #endif
1960 rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
1961 g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo);
1962
1963 if (RT_SUCCESS(rc))
1964 {
1965 /*
1966 * Finally initialize known SIDs that we use.
1967 */
1968 SID_IDENTIFIER_AUTHORITY s_NtAuth = SECURITY_NT_AUTHORITY;
1969 NTSTATUS rcNt = RtlInitializeSid(&g_TrustedInstallerSid, &s_NtAuth, SECURITY_SERVICE_ID_RID_COUNT);
1970 if (NT_SUCCESS(rcNt))
1971 {
1972 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 0) = SECURITY_SERVICE_ID_BASE_RID;
1973 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 1) = 956008885;
1974 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 2) = 3418522649;
1975 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 3) = 1831038044;
1976 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 4) = 1853292631;
1977 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 5) = 2271478464;
1978
1979 rcNt = RtlInitializeSid(&g_LocalSystemSid, &s_NtAuth, 1);
1980 if (NT_SUCCESS(rcNt))
1981 {
1982 *RtlSubAuthoritySid(&g_LocalSystemSid, 0) = SECURITY_LOCAL_SYSTEM_RID;
1983
1984 rcNt = RtlInitializeSid(&g_AdminsGroupSid, &s_NtAuth, 2);
1985 if (NT_SUCCESS(rcNt))
1986 {
1987 *RtlSubAuthoritySid(&g_AdminsGroupSid, 0) = SECURITY_BUILTIN_DOMAIN_RID;
1988 *RtlSubAuthoritySid(&g_AdminsGroupSid, 1) = DOMAIN_ALIAS_RID_ADMINS;
1989 return VINF_SUCCESS;
1990 }
1991 }
1992 }
1993 rc = RTErrConvertFromNtStatus(rcNt);
1994 }
1995 supHardenedWinTermImageVerifier();
1996 }
1997 return rc;
1998 }
1999
2000
2001 /**
2002 * Releases resources allocated by supHardenedWinInitImageVerifier.
2003 */
2004 DECLHIDDEN(void) supHardenedWinTermImageVerifier(void)
2005 {
2006 if (RTCrX509Certificate_IsPresent(&g_BuildX509Cert))
2007 RTAsn1VtDelete(&g_BuildX509Cert.SeqCore.Asn1Core);
2008
2009 RTCrStoreRelease(g_hSpcAndNtKernelSuppStore);
2010 g_hSpcAndNtKernelSuppStore = NIL_RTCRSTORE;
2011 RTCrStoreRelease(g_hSpcAndNtKernelRootStore);
2012 g_hSpcAndNtKernelRootStore = NIL_RTCRSTORE;
2013
2014 RTCrStoreRelease(g_hNtKernelRootStore);
2015 g_hNtKernelRootStore = NIL_RTCRSTORE;
2016 RTCrStoreRelease(g_hSpcRootStore);
2017 g_hSpcRootStore = NIL_RTCRSTORE;
2018 }
2019
2020 #ifdef IN_RING3
2021
2022 /**
2023 * This is a hardcoded list of certificates we thing we might need.
2024 *
2025 * @returns true if wanted, false if not.
2026 * @param pCert The certificate.
2027 */
2028 static bool supR3HardenedWinIsDesiredRootCA(PCRTCRX509CERTIFICATE pCert)
2029 {
2030 char szSubject[512];
2031 szSubject[sizeof(szSubject) - 1] = '\0';
2032 RTCrX509Name_FormatAsString(&pCert->TbsCertificate.Subject, szSubject, sizeof(szSubject) - 1, NULL);
2033
2034 /*
2035 * Check that it's a plausible root certificate.
2036 */
2037 if (!RTCrX509Certificate_IsSelfSigned(pCert))
2038 {
2039 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - not-self-signed: %s\n", szSubject));
2040 return false;
2041 }
2042
2043 if (RTAsn1Integer_UnsignedCompareWithU32(&pCert->TbsCertificate.T0.Version, 3) > 0)
2044 {
2045 if ( !(pCert->TbsCertificate.T3.fExtKeyUsage & RTCRX509CERT_KEY_USAGE_F_KEY_CERT_SIGN)
2046 && (pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE) )
2047 {
2048 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - non-cert-sign: %s\n", szSubject));
2049 return false;
2050 }
2051 if ( pCert->TbsCertificate.T3.pBasicConstraints
2052 && !pCert->TbsCertificate.T3.pBasicConstraints->CA.fValue)
2053 {
2054 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - non-CA: %s\n", szSubject));
2055 return false;
2056 }
2057 }
2058 if (pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits < 256) /* mostly for u64KeyId reading. */
2059 {
2060 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - key too small: %u bits %s\n",
2061 pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits, szSubject));
2062 return false;
2063 }
2064 uint64_t const u64KeyId = pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.uBits.pu64[1];
2065
2066 # if 0
2067 /*
2068 * Whitelist - Array of names and key clues of the certificates we want.
2069 */
2070 static struct
2071 {
2072 uint64_t u64KeyId;
2073 const char *pszName;
2074 } const s_aWanted[] =
2075 {
2076 /* SPC */
2077 { UINT64_C(0xffffffffffffffff), "C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority" },
2078 { UINT64_C(0xffffffffffffffff), "L=Internet, O=VeriSign, Inc., OU=VeriSign Commercial Software Publishers CA" },
2079 { UINT64_C(0x491857ead79dde00), "C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority" },
2080
2081 /* TS */
2082 { UINT64_C(0xffffffffffffffff), "O=Microsoft Trust Network, OU=Microsoft Corporation, OU=Microsoft Time Stamping Service Root, OU=Copyright (c) 1997 Microsoft Corp." },
2083 { UINT64_C(0xffffffffffffffff), "O=VeriSign Trust Network, OU=VeriSign, Inc., OU=VeriSign Time Stamping Service Root, OU=NO LIABILITY ACCEPTED, (c)97 VeriSign, Inc." },
2084 { UINT64_C(0xffffffffffffffff), "C=ZA, ST=Western Cape, L=Durbanville, O=Thawte, OU=Thawte Certification, CN=Thawte Timestamping CA" },
2085
2086 /* Additional Windows 8.1 list: */
2087 { UINT64_C(0x5ad46780fa5df300), "DC=com, DC=microsoft, CN=Microsoft Root Certificate Authority" },
2088 { UINT64_C(0x3be670c1bd02a900), "OU=Copyright (c) 1997 Microsoft Corp., OU=Microsoft Corporation, CN=Microsoft Root Authority" },
2089 { UINT64_C(0x4d3835aa4180b200), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2011" },
2090 { UINT64_C(0x646e3fe3ba08df00), "C=US, O=MSFT, CN=Microsoft Authenticode(tm) Root Authority" },
2091 { UINT64_C(0xece4e4289e08b900), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010" },
2092 { UINT64_C(0x59faf1086271bf00), "C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2" },
2093 { UINT64_C(0x3d98ab22bb04a300), "C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root" },
2094 { UINT64_C(0x91e3728b8b40d000), "C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority" },
2095 { UINT64_C(0x61a3a33f81aace00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Object" },
2096 { UINT64_C(0x9e5bc2d78b6a3636), "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA, Email=premium-server@thawte.com" },
2097 { UINT64_C(0xf4fd306318ccda00), "C=US, O=GeoTrust Inc., CN=GeoTrust Global CA" },
2098 { UINT64_C(0xa0ee62086758b15d), "C=US, O=Equifax, OU=Equifax Secure Certificate Authority" },
2099 { UINT64_C(0x8ff6fc03c1edbd00), "C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2" },
2100 { UINT64_C(0xa3ce8d99e60eda00), "C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA" },
2101 { UINT64_C(0xa671e9fec832b700), "C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority" },
2102 { UINT64_C(0xa8de7211e13be200), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA" },
2103 { UINT64_C(0x0ff3891b54348328), "C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.netSecure Server Certification Authority" },
2104 { UINT64_C(0x7ae89c50f0b6a00f), "C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root" },
2105 { UINT64_C(0xd45980fbf0a0ac00), "C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA" },
2106 { UINT64_C(0x9e5bc2d78b6a3636), "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA, Email=premium-server@thawte.com" },
2107 { UINT64_C(0x7c4fd32ec1b1ce00), "C=PL, O=Unizeto Sp. z o.o., CN=Certum CA" },
2108 { UINT64_C(0xd4fbe673e5ccc600), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA" },
2109 { UINT64_C(0x16e64d2a56ccf200), "C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., OU=http://certificates.starfieldtech.com/repository/, CN=Starfield Services Root Certificate Authority" },
2110 { UINT64_C(0x6e2ba21058eedf00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN - DATACorp SGC" },
2111 { UINT64_C(0xb28612a94b4dad00), "O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.netCertification Authority (2048)" },
2112 { UINT64_C(0x357a29080824af00), "C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class3 Public Primary Certification Authority - G5" },
2113 { UINT64_C(0x466cbc09db88c100), "C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority" },
2114 { UINT64_C(0x9259c8abe5ca713a), "L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com/, Email=info@valicert.com" },
2115 { UINT64_C(0x1f78fc529cbacb00), "C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 1999 VeriSign, Inc. - For authorized use only, CN=VeriSign Class3 Public Primary Certification Authority - G3" },
2116 { UINT64_C(0x8043e4ce150ead00), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Assured ID Root CA" },
2117 { UINT64_C(0x00f2e6331af7b700), "C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root" },
2118 };
2119
2120
2121 uint32_t i = RT_ELEMENTS(s_aWanted);
2122 while (i-- > 0)
2123 if ( s_aWanted[i].u64KeyId == u64KeyId
2124 || s_aWanted[i].u64KeyId == UINT64_MAX)
2125 if (RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, s_aWanted[i].pszName))
2126 {
2127 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: Adding %#llx %s\n", u64KeyId, szSubject));
2128 return true;
2129 }
2130
2131 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping %#llx %s\n", u64KeyId, szSubject));
2132 return false;
2133 # else
2134 /*
2135 * Blacklist approach.
2136 */
2137 static struct
2138 {
2139 uint64_t u64KeyId;
2140 const char *pszName;
2141 } const s_aUnwanted[] =
2142 {
2143 { UINT64_C(0xffffffffffffffff), "C=US, O=U.S. Robots and Mechanical Men, Inc., OU=V.I.K.I." }, /* dummy entry */
2144 };
2145
2146 uint32_t i = RT_ELEMENTS(s_aUnwanted);
2147 while (i-- > 0)
2148 if ( s_aUnwanted[i].u64KeyId == u64KeyId
2149 || s_aUnwanted[i].u64KeyId == UINT64_MAX)
2150 if (RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, s_aUnwanted[i].pszName))
2151 {
2152 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - blacklisted: %#llx %s\n", u64KeyId, szSubject));
2153 return false;
2154 }
2155
2156 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: Adding %#llx %s\n", u64KeyId, szSubject));
2157 return true;
2158 # endif
2159 }
2160
2161
2162 /**
2163 * Loads a module in the system32 directory.
2164 *
2165 * @returns Module handle on success. Won't return on failure if fMandatory = true.
2166 * @param pszName The name of the DLL to load.
2167 * @param fMandatory Whether the library is mandatory.
2168 */
2169 DECLHIDDEN(HMODULE) supR3HardenedWinLoadSystem32Dll(const char *pszName, bool fMandatory)
2170 {
2171 WCHAR wszName[200+60];
2172 UINT cwcDir = GetSystemDirectoryW(wszName, RT_ELEMENTS(wszName) - 60);
2173 wszName[cwcDir] = '\\';
2174 RTUtf16CopyAscii(&wszName[cwcDir + 1], RT_ELEMENTS(wszName) - cwcDir, pszName);
2175
2176 DWORD fFlags = 0;
2177 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2178 fFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;
2179 HMODULE hMod = LoadLibraryExW(wszName, NULL, fFlags);
2180 if ( hMod == NULL
2181 && fFlags
2182 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
2183 && RtlGetLastWin32Error() == ERROR_INVALID_PARAMETER)
2184 {
2185 fFlags = 0;
2186 hMod = LoadLibraryExW(wszName, NULL, fFlags);
2187 }
2188 if ( hMod == NULL
2189 && fMandatory)
2190 supR3HardenedFatal("Error loading '%s': %u [%ls]", pszName, RtlGetLastWin32Error(), wszName);
2191 return hMod;
2192 }
2193
2194
2195 /**
2196 * Called by supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation to
2197 * import selected root CAs from the system certificate store.
2198 *
2199 * These certificates permits us to correctly validate third party DLLs.
2200 */
2201 static void supR3HardenedWinRetrieveTrustedRootCAs(void)
2202 {
2203 uint32_t cAdded = 0;
2204
2205 /*
2206 * Load crypt32.dll and resolve the APIs we need.
2207 */
2208 HMODULE hCrypt32 = supR3HardenedWinLoadSystem32Dll("crypt32.dll", true /*fMandatory*/);
2209
2210 #define RESOLVE_CRYPT32_API(a_Name, a_pfnType) \
2211 a_pfnType pfn##a_Name = (a_pfnType)GetProcAddress(hCrypt32, #a_Name); \
2212 if (pfn##a_Name == NULL) supR3HardenedFatal("Error locating '" #a_Name "' in 'crypt32.dll': %u", RtlGetLastWin32Error())
2213 RESOLVE_CRYPT32_API(CertOpenStore, PFNCERTOPENSTORE);
2214 RESOLVE_CRYPT32_API(CertCloseStore, PFNCERTCLOSESTORE);
2215 RESOLVE_CRYPT32_API(CertEnumCertificatesInStore, PFNCERTENUMCERTIFICATESINSTORE);
2216 #undef RESOLVE_CRYPT32_API
2217
2218 /*
2219 * Open the root store and look for the certificates we wish to use.
2220 */
2221 DWORD fOpenStore = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
2222 HCERTSTORE hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
2223 NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_LOCAL_MACHINE | fOpenStore, L"Root");
2224 if (!hStore)
2225 hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
2226 NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_CURRENT_USER | fOpenStore, L"Root");
2227 if (hStore)
2228 {
2229 PCCERT_CONTEXT pCurCtx = NULL;
2230 while ((pCurCtx = pfnCertEnumCertificatesInStore(hStore, pCurCtx)) != NULL)
2231 {
2232 if (pCurCtx->dwCertEncodingType & X509_ASN_ENCODING)
2233 {
2234 RTERRINFOSTATIC StaticErrInfo;
2235 RTASN1CURSORPRIMARY PrimaryCursor;
2236 RTAsn1CursorInitPrimary(&PrimaryCursor, pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded,
2237 RTErrInfoInitStatic(&StaticErrInfo),
2238 &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
2239 RTCRX509CERTIFICATE MyCert;
2240 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &MyCert, "Cert");
2241 if (RT_SUCCESS(rc))
2242 {
2243 if (supR3HardenedWinIsDesiredRootCA(&MyCert))
2244 {
2245 rc = RTCrStoreCertAddEncoded(g_hSpcRootStore, RTCRCERTCTX_F_ENC_X509_DER,
2246 pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
2247 AssertRC(rc);
2248
2249 rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
2250 pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
2251 AssertRC(rc);
2252 cAdded++;
2253 }
2254
2255 RTCrX509Certificate_Delete(&MyCert);
2256 }
2257 /* XP root certificate "C&W HKT SecureNet CA SGC Root" has non-standard validity
2258 timestamps, the UTC formatting isn't Zulu time but specifies timezone offsets.
2259 Ignore these failures and certificates. */
2260 else if (rc != VERR_ASN1_INVALID_UTC_TIME_ENCODING)
2261 AssertMsgFailed(("RTCrX509Certificate_DecodeAsn1 failed: rc=%#x: %s\n", rc, StaticErrInfo.szMsg));
2262 }
2263 }
2264 pfnCertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
2265 g_fHaveOtherRoots = true;
2266 }
2267 SUP_DPRINTF(("supR3HardenedWinRetrieveTrustedRootCAs: cAdded=%u\n", cAdded));
2268 }
2269
2270
2271 /**
2272 * Resolves the WinVerifyTrust API after the process has been verified and
2273 * installs a thread creation hook.
2274 *
2275 * The WinVerifyTrust API is used in addition our own Authenticode verification
2276 * code. If the image has the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag
2277 * set, it will be checked again by the kernel. All our image has this flag set
2278 * and we require all VBox extensions to have it set as well. In effect, the
2279 * authenticode signature will be checked two or three times.
2280 *
2281 * @param pszProgName The program name.
2282 */
2283 DECLHIDDEN(void) supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(const char *pszProgName)
2284 {
2285 # ifdef IN_SUP_HARDENED_R3
2286 /*
2287 * Load our the support library DLL that does the thread hooking as the
2288 * security API may trigger the creation of COM worker threads (or
2289 * whatever they are).
2290 *
2291 * The thread creation hook makes the threads very slippery to debuggers by
2292 * irreversably disabling most (if not all) debug events for them.
2293 */
2294 char szPath[RTPATH_MAX];
2295 supR3HardenedPathAppSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxSupLib.DLL"));
2296 suplibHardenedStrCat(szPath, "/VBoxSupLib.DLL");
2297 HMODULE hSupLibMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, true /*fSystem32Only*/, 0 /*fMainFlags*/);
2298 if (hSupLibMod == NULL)
2299 supR3HardenedFatal("Error loading '%s': %u", szPath, RtlGetLastWin32Error());
2300 # endif
2301
2302 /*
2303 * Allocate TLS entry for WinVerifyTrust recursion prevention.
2304 */
2305 DWORD iTls = TlsAlloc();
2306 if (iTls != TLS_OUT_OF_INDEXES)
2307 g_iTlsWinVerifyTrustRecursion = iTls;
2308 else
2309 supR3HardenedError(RtlGetLastWin32Error(), false /*fFatal*/, "TlsAlloc failed");
2310
2311 /*
2312 * Resolve the imports we need.
2313 */
2314 HMODULE hWintrust = supR3HardenedWinLoadSystem32Dll("Wintrust.dll", true /*fMandatory*/);
2315 #define RESOLVE_CRYPT_API(a_Name, a_pfnType, a_uMinWinVer) \
2316 do { \
2317 g_pfn##a_Name = (a_pfnType)GetProcAddress(hWintrust, #a_Name); \
2318 if (g_pfn##a_Name == NULL && (a_uMinWinVer) < g_uNtVerCombined) \
2319 supR3HardenedFatal("Error locating '" #a_Name "' in 'Wintrust.dll': %u", RtlGetLastWin32Error()); \
2320 } while (0)
2321
2322 PFNWINVERIFYTRUST pfnWinVerifyTrust = (PFNWINVERIFYTRUST)GetProcAddress(hWintrust, "WinVerifyTrust");
2323 if (!pfnWinVerifyTrust)
2324 supR3HardenedFatal("Error locating 'WinVerifyTrust' in 'Wintrust.dll': %u", RtlGetLastWin32Error());
2325
2326 RESOLVE_CRYPT_API(CryptCATAdminAcquireContext, PFNCRYPTCATADMINACQUIRECONTEXT, 0);
2327 RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE, 0);
2328 RESOLVE_CRYPT_API(CryptCATAdminEnumCatalogFromHash, PFNCRYPTCATADMINENUMCATALOGFROMHASH, 0);
2329 RESOLVE_CRYPT_API(CryptCATAdminReleaseCatalogContext, PFNCRYPTCATADMINRELEASECATALOGCONTEXT, 0);
2330 RESOLVE_CRYPT_API(CryptCATAdminReleaseContext, PFNCRYPTCATDADMINRELEASECONTEXT, 0);
2331 RESOLVE_CRYPT_API(CryptCATCatalogInfoFromContext, PFNCRYPTCATCATALOGINFOFROMCONTEXT, 0);
2332
2333 RESOLVE_CRYPT_API(CryptCATAdminAcquireContext2, PFNCRYPTCATADMINACQUIRECONTEXT2, SUP_NT_VER_W80);
2334 RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle2, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2, SUP_NT_VER_W80);
2335
2336 # ifdef IN_SUP_HARDENED_R3
2337 /*
2338 * Load bcrypt.dll and instantiate a few hashing and signing providers to
2339 * make sure the providers are cached for later us. Avoid recursion issues.
2340 */
2341 HMODULE hBCrypt = supR3HardenedWinLoadSystem32Dll("bcrypt.dll", false /*fMandatory*/);
2342 if (hBCrypt)
2343 {
2344 PFNBCRYPTOPENALGORTIHMPROVIDER pfnOpenAlgoProvider;
2345 pfnOpenAlgoProvider = (PFNBCRYPTOPENALGORTIHMPROVIDER)GetProcAddress(hBCrypt, "BCryptOpenAlgorithmProvider");
2346 if (pfnOpenAlgoProvider)
2347 {
2348 SUP_DPRINTF(("bcrypt.dll loaded at %p, BCryptOpenAlgorithmProvider at %p, preloading providers:\n",
2349 hBCrypt, pfnOpenAlgoProvider));
2350 # define PRELOAD_ALGO_PROVIDER(a_Name) \
2351 do { \
2352 BCRYPT_ALG_HANDLE hAlgo = NULL; \
2353 NTSTATUS rcNt = pfnOpenAlgoProvider(&hAlgo, a_Name, NULL, 0); \
2354 SUP_DPRINTF(("%sBCryptOpenAlgorithmProvider(,'%ls',0,0) -> %#x (hAlgo=%p)\n", \
2355 NT_SUCCESS(rcNt) ? " " : "warning: ", a_Name, rcNt, hAlgo)); \
2356 } while (0)
2357 PRELOAD_ALGO_PROVIDER(BCRYPT_MD2_ALGORITHM);
2358 PRELOAD_ALGO_PROVIDER(BCRYPT_MD4_ALGORITHM);
2359 PRELOAD_ALGO_PROVIDER(BCRYPT_MD5_ALGORITHM);
2360 PRELOAD_ALGO_PROVIDER(BCRYPT_SHA1_ALGORITHM);
2361 PRELOAD_ALGO_PROVIDER(BCRYPT_SHA256_ALGORITHM);
2362 PRELOAD_ALGO_PROVIDER(BCRYPT_SHA512_ALGORITHM);
2363 PRELOAD_ALGO_PROVIDER(BCRYPT_RSA_ALGORITHM);
2364 PRELOAD_ALGO_PROVIDER(BCRYPT_DSA_ALGORITHM);
2365 # undef PRELOAD_ALGO_PROVIDER
2366 }
2367 else
2368 SUP_DPRINTF(("Warning! Failed to find BCryptOpenAlgorithmProvider in bcrypt.dll\n"));
2369 }
2370 else
2371 SUP_DPRINTF(("Warning! Failed to load bcrypt.dll\n"));
2372
2373 /*
2374 * Call the verification API on ourselves and ntdll to make sure it works
2375 * and loads more stuff it needs, preventing any recursive fun we'd run
2376 * into after we set g_pfnWinVerifyTrust.
2377 */
2378 RTERRINFOSTATIC ErrInfoStatic;
2379 RTErrInfoInitStatic(&ErrInfoStatic);
2380 int rc = supR3HardNtViCallWinVerifyTrust(NULL, g_SupLibHardenedExeNtPath.UniStr.Buffer, 0,
2381 &ErrInfoStatic.Core, pfnWinVerifyTrust, NULL);
2382 if (RT_FAILURE(rc))
2383 supR3HardenedFatalMsg(pszProgName, kSupInitOp_Integrity, rc,
2384 "WinVerifyTrust failed on stub executable: %s", ErrInfoStatic.szMsg);
2385 # else
2386 RT_NOREF1(pszProgName);
2387 # endif
2388
2389 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0)) /* ntdll isn't signed on XP, assuming this is the case on W2K3 for now. */
2390 supR3HardNtViCallWinVerifyTrust(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust, NULL);
2391 supR3HardNtViCallWinVerifyTrustCatFile(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
2392
2393 g_pfnWinVerifyTrust = pfnWinVerifyTrust;
2394 SUP_DPRINTF(("g_pfnWinVerifyTrust=%p\n", pfnWinVerifyTrust));
2395
2396 # ifdef IN_SUP_HARDENED_R3
2397 /*
2398 * Load some problematic DLLs into the verifier cache to prevent
2399 * recursion trouble.
2400 */
2401 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\crypt32.dll");
2402 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\Wintrust.dll");
2403 # endif
2404
2405 /*
2406 * Now, get trusted root CAs so we can verify a broader scope of signatures.
2407 */
2408 supR3HardenedWinRetrieveTrustedRootCAs();
2409 }
2410
2411
2412 static int supR3HardNtViNtToWinPath(PCRTUTF16 pwszNtName, PCRTUTF16 *ppwszWinPath,
2413 PRTUTF16 pwszWinPathBuf, size_t cwcWinPathBuf)
2414 {
2415 static const RTUTF16 s_wszPrefix[] = L"\\\\.\\GLOBALROOT";
2416
2417 if (*pwszNtName != '\\' && *pwszNtName != '/')
2418 return VERR_PATH_DOES_NOT_START_WITH_ROOT;
2419
2420 size_t cwcNtName = RTUtf16Len(pwszNtName);
2421 if (RT_ELEMENTS(s_wszPrefix) + cwcNtName > cwcWinPathBuf)
2422 return VERR_FILENAME_TOO_LONG;
2423
2424 memcpy(pwszWinPathBuf, s_wszPrefix, sizeof(s_wszPrefix));
2425 memcpy(&pwszWinPathBuf[sizeof(s_wszPrefix) / sizeof(RTUTF16) - 1], pwszNtName, (cwcNtName + 1) * sizeof(RTUTF16));
2426 *ppwszWinPath = pwszWinPathBuf;
2427 return VINF_SUCCESS;
2428 }
2429
2430
2431 /**
2432 * Calls WinVerifyTrust to verify an PE image.
2433 *
2434 * @returns VBox status code.
2435 * @param hFile File handle to the executable file.
2436 * @param pwszName Full NT path to the DLL in question, used for
2437 * dealing with unsigned system dlls as well as for
2438 * error/logging.
2439 * @param fFlags Flags, SUPHNTVI_F_XXX.
2440 * @param pErrInfo Pointer to error info structure. Optional.
2441 * @param pfnWinVerifyTrust Pointer to the API.
2442 * @param phrcWinVerifyTrust Where to WinVerifyTrust error status on failure,
2443 * optional.
2444 */
2445 static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
2446 PFNWINVERIFYTRUST pfnWinVerifyTrust, HRESULT *phrcWinVerifyTrust)
2447 {
2448 RT_NOREF1(fFlags);
2449 if (phrcWinVerifyTrust)
2450 *phrcWinVerifyTrust = S_OK;
2451
2452 /*
2453 * Convert the name into a Windows name.
2454 */
2455 RTUTF16 wszWinPathBuf[MAX_PATH];
2456 PCRTUTF16 pwszWinPath;
2457 int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
2458 if (RT_FAILURE(rc))
2459 return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrust: rc=%Rrc '%ls'", rc, pwszName);
2460
2461 /*
2462 * Construct input parameters and call the API.
2463 */
2464 WINTRUST_FILE_INFO FileInfo;
2465 RT_ZERO(FileInfo);
2466 FileInfo.cbStruct = sizeof(FileInfo);
2467 FileInfo.pcwszFilePath = pwszWinPath;
2468 FileInfo.hFile = hFile;
2469
2470 GUID PolicyActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
2471
2472 WINTRUST_DATA TrustData;
2473 RT_ZERO(TrustData);
2474 TrustData.cbStruct = sizeof(TrustData);
2475 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
2476 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
2477 TrustData.dwUIChoice = WTD_UI_NONE;
2478 TrustData.dwProvFlags = 0;
2479 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2480 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
2481 else
2482 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
2483 TrustData.dwUnionChoice = WTD_CHOICE_FILE;
2484 TrustData.pFile = &FileInfo;
2485
2486 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
2487 if (hrc == S_OK)
2488 rc = VINF_SUCCESS;
2489 else
2490 {
2491 /*
2492 * Failed. Format a nice error message.
2493 */
2494 # ifdef DEBUG_bird
2495 if (hrc != CERT_E_CHAINING /* Un-updated vistas, XPs, ++ */)
2496 __debugbreak();
2497 # endif
2498 const char *pszErrConst = NULL;
2499 switch (hrc)
2500 {
2501 case TRUST_E_SYSTEM_ERROR: pszErrConst = "TRUST_E_SYSTEM_ERROR"; break;
2502 case TRUST_E_NO_SIGNER_CERT: pszErrConst = "TRUST_E_NO_SIGNER_CERT"; break;
2503 case TRUST_E_COUNTER_SIGNER: pszErrConst = "TRUST_E_COUNTER_SIGNER"; break;
2504 case TRUST_E_CERT_SIGNATURE: pszErrConst = "TRUST_E_CERT_SIGNATURE"; break;
2505 case TRUST_E_TIME_STAMP: pszErrConst = "TRUST_E_TIME_STAMP"; break;
2506 case TRUST_E_BAD_DIGEST: pszErrConst = "TRUST_E_BAD_DIGEST"; break;
2507 case TRUST_E_BASIC_CONSTRAINTS: pszErrConst = "TRUST_E_BASIC_CONSTRAINTS"; break;
2508 case TRUST_E_FINANCIAL_CRITERIA: pszErrConst = "TRUST_E_FINANCIAL_CRITERIA"; break;
2509 case TRUST_E_PROVIDER_UNKNOWN: pszErrConst = "TRUST_E_PROVIDER_UNKNOWN"; break;
2510 case TRUST_E_ACTION_UNKNOWN: pszErrConst = "TRUST_E_ACTION_UNKNOWN"; break;
2511 case TRUST_E_SUBJECT_FORM_UNKNOWN: pszErrConst = "TRUST_E_SUBJECT_FORM_UNKNOWN"; break;
2512 case TRUST_E_SUBJECT_NOT_TRUSTED: pszErrConst = "TRUST_E_SUBJECT_NOT_TRUSTED"; break;
2513 case TRUST_E_NOSIGNATURE: pszErrConst = "TRUST_E_NOSIGNATURE"; break;
2514 case TRUST_E_FAIL: pszErrConst = "TRUST_E_FAIL"; break;
2515 case TRUST_E_EXPLICIT_DISTRUST: pszErrConst = "TRUST_E_EXPLICIT_DISTRUST"; break;
2516 case CERT_E_EXPIRED: pszErrConst = "CERT_E_EXPIRED"; break;
2517 case CERT_E_VALIDITYPERIODNESTING: pszErrConst = "CERT_E_VALIDITYPERIODNESTING"; break;
2518 case CERT_E_ROLE: pszErrConst = "CERT_E_ROLE"; break;
2519 case CERT_E_PATHLENCONST: pszErrConst = "CERT_E_PATHLENCONST"; break;
2520 case CERT_E_CRITICAL: pszErrConst = "CERT_E_CRITICAL"; break;
2521 case CERT_E_PURPOSE: pszErrConst = "CERT_E_PURPOSE"; break;
2522 case CERT_E_ISSUERCHAINING: pszErrConst = "CERT_E_ISSUERCHAINING"; break;
2523 case CERT_E_MALFORMED: pszErrConst = "CERT_E_MALFORMED"; break;
2524 case CERT_E_UNTRUSTEDROOT: pszErrConst = "CERT_E_UNTRUSTEDROOT"; break;
2525 case CERT_E_CHAINING: pszErrConst = "CERT_E_CHAINING"; break;
2526 case CERT_E_REVOKED: pszErrConst = "CERT_E_REVOKED"; break;
2527 case CERT_E_UNTRUSTEDTESTROOT: pszErrConst = "CERT_E_UNTRUSTEDTESTROOT"; break;
2528 case CERT_E_REVOCATION_FAILURE: pszErrConst = "CERT_E_REVOCATION_FAILURE"; break;
2529 case CERT_E_CN_NO_MATCH: pszErrConst = "CERT_E_CN_NO_MATCH"; break;
2530 case CERT_E_WRONG_USAGE: pszErrConst = "CERT_E_WRONG_USAGE"; break;
2531 case CERT_E_UNTRUSTEDCA: pszErrConst = "CERT_E_UNTRUSTEDCA"; break;
2532 case CERT_E_INVALID_POLICY: pszErrConst = "CERT_E_INVALID_POLICY"; break;
2533 case CERT_E_INVALID_NAME: pszErrConst = "CERT_E_INVALID_NAME"; break;
2534 case CRYPT_E_FILE_ERROR: pszErrConst = "CRYPT_E_FILE_ERROR"; break;
2535 case CRYPT_E_REVOKED: pszErrConst = "CRYPT_E_REVOKED"; break;
2536 }
2537 if (pszErrConst)
2538 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
2539 "WinVerifyTrust failed with hrc=%s on '%ls'", pszErrConst, pwszName);
2540 else
2541 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
2542 "WinVerifyTrust failed with hrc=%Rhrc on '%ls'", hrc, pwszName);
2543 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrust: WinVerifyTrust failed with %#x (%s) on '%ls'\n",
2544 hrc, pszErrConst, pwszName));
2545 if (phrcWinVerifyTrust)
2546 *phrcWinVerifyTrust = hrc;
2547 }
2548
2549 /* clean up state data. */
2550 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
2551 FileInfo.hFile = NULL;
2552 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
2553
2554 return rc;
2555 }
2556
2557
2558 /**
2559 * Calls WinVerifyTrust to verify an PE image via catalog files.
2560 *
2561 * @returns VBox status code.
2562 * @param hFile File handle to the executable file.
2563 * @param pwszName Full NT path to the DLL in question, used for
2564 * dealing with unsigned system dlls as well as for
2565 * error/logging.
2566 * @param fFlags Flags, SUPHNTVI_F_XXX.
2567 * @param pErrInfo Pointer to error info structure. Optional.
2568 * @param pfnWinVerifyTrust Pointer to the API.
2569 */
2570 static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
2571 PFNWINVERIFYTRUST pfnWinVerifyTrust)
2572 {
2573 RT_NOREF1(fFlags);
2574 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hFile=%p pwszName=%ls\n", hFile, pwszName));
2575
2576 /*
2577 * Convert the name into a Windows name.
2578 */
2579 RTUTF16 wszWinPathBuf[MAX_PATH];
2580 PCRTUTF16 pwszWinPath;
2581 int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
2582 if (RT_FAILURE(rc))
2583 return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrustCatFile: rc=%Rrc '%ls'", rc, pwszName);
2584
2585 /*
2586 * Open the file if we didn't get a handle.
2587 */
2588 HANDLE hFileClose = NULL;
2589 if (hFile == RTNT_INVALID_HANDLE_VALUE || hFile == NULL)
2590 {
2591 hFile = RTNT_INVALID_HANDLE_VALUE;
2592 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2593
2594 UNICODE_STRING NtName;
2595 NtName.Buffer = (PWSTR)pwszName;
2596 NtName.Length = (USHORT)(RTUtf16Len(pwszName) * sizeof(WCHAR));
2597 NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
2598
2599 OBJECT_ATTRIBUTES ObjAttr;
2600 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
2601
2602 NTSTATUS rcNt = NtCreateFile(&hFile,
2603 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
2604 &ObjAttr,
2605 &Ios,
2606 NULL /* Allocation Size*/,
2607 FILE_ATTRIBUTE_NORMAL,
2608 FILE_SHARE_READ,
2609 FILE_OPEN,
2610 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
2611 NULL /*EaBuffer*/,
2612 0 /*EaLength*/);
2613 if (NT_SUCCESS(rcNt))
2614 rcNt = Ios.Status;
2615 if (!NT_SUCCESS(rcNt))
2616 return RTErrInfoSetF(pErrInfo, RTErrConvertFromNtStatus(rcNt),
2617 "NtCreateFile returned %#x opening '%ls'.", rcNt, pwszName);
2618 hFileClose = hFile;
2619 }
2620
2621 /*
2622 * On Windows 8.0 and later there are more than one digest choice.
2623 */
2624 int fNoSignedCatalogFound = -1;
2625 rc = VERR_LDRVI_NOT_SIGNED;
2626 static struct
2627 {
2628 /** The digest algorithm name. */
2629 const WCHAR *pszAlgorithm;
2630 /** Cached catalog admin handle. */
2631 HCATADMIN volatile hCachedCatAdmin;
2632 } s_aHashes[] =
2633 {
2634 { NULL, NULL },
2635 { L"SHA256", NULL },
2636 };
2637 for (uint32_t i = 0; i < RT_ELEMENTS(s_aHashes); i++)
2638 {
2639 /*
2640 * Another loop for dealing with different trust provider policies
2641 * required for successfully validating different catalog signatures.
2642 */
2643 bool fTryNextPolicy;
2644 uint32_t iPolicy = 0;
2645 static const GUID s_aPolicies[] =
2646 {
2647 DRIVER_ACTION_VERIFY, /* Works with microsoft bits. Most frequently used, thus first. */
2648 WINTRUST_ACTION_GENERIC_VERIFY_V2, /* Works with ATI and other SPC kernel-code signed stuff. */
2649 };
2650 do
2651 {
2652 /*
2653 * Create a context.
2654 */
2655 fTryNextPolicy = false;
2656 bool fFreshContext = false;
2657 BOOL fRc;
2658 HCATADMIN hCatAdmin = ASMAtomicXchgPtr(&s_aHashes[i].hCachedCatAdmin, NULL);
2659 if (hCatAdmin)
2660 {
2661 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Cached context %p\n", hCatAdmin));
2662 fFreshContext = false;
2663 fRc = TRUE;
2664 }
2665 else
2666 {
2667 l_fresh_context:
2668 fFreshContext = true;
2669 if (g_pfnCryptCATAdminAcquireContext2)
2670 fRc = g_pfnCryptCATAdminAcquireContext2(&hCatAdmin, &s_aPolicies[iPolicy], s_aHashes[i].pszAlgorithm,
2671 NULL /*pStrongHashPolicy*/, 0 /*dwFlags*/);
2672 else
2673 fRc = g_pfnCryptCATAdminAcquireContext(&hCatAdmin, &s_aPolicies[iPolicy], 0 /*dwFlags*/);
2674 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: New context %p\n", hCatAdmin));
2675 }
2676 if (fRc)
2677 {
2678 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hCatAdmin=%p\n", hCatAdmin));
2679
2680 /*
2681 * Hash the file.
2682 */
2683 BYTE abHash[SUPHARDNTVI_MAX_CAT_HASH_SIZE];
2684 DWORD cbHash = sizeof(abHash);
2685 if (g_pfnCryptCATAdminCalcHashFromFileHandle2)
2686 fRc = g_pfnCryptCATAdminCalcHashFromFileHandle2(hCatAdmin, hFile, &cbHash, abHash, 0 /*dwFlags*/);
2687 else
2688 fRc = g_pfnCryptCATAdminCalcHashFromFileHandle(hFile, &cbHash, abHash, 0 /*dwFlags*/);
2689 if (fRc)
2690 {
2691 /* Produce a string version of it that we can pass to WinVerifyTrust. */
2692 RTUTF16 wszDigest[SUPHARDNTVI_MAX_CAT_HASH_SIZE * 2 + 1];
2693 int rc2 = RTUtf16PrintHexBytes(wszDigest, RT_ELEMENTS(wszDigest), abHash, cbHash, RTSTRPRINTHEXBYTES_F_UPPER);
2694 if (RT_SUCCESS(rc2))
2695 {
2696 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: cbHash=%u wszDigest=%ls\n", cbHash, wszDigest));
2697
2698 /*
2699 * Enumerate catalog information that matches the hash.
2700 */
2701 uint32_t iCat = 0;
2702 HCATINFO hCatInfoPrev = NULL;
2703 do
2704 {
2705 /* Get the next match. */
2706 HCATINFO hCatInfo = g_pfnCryptCATAdminEnumCatalogFromHash(hCatAdmin, abHash, cbHash, 0, &hCatInfoPrev);
2707 if (!hCatInfo)
2708 {
2709 if (!fFreshContext)
2710 {
2711 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Retrying with fresh context (CryptCATAdminEnumCatalogFromHash -> %u; iCat=%#x)\n", RtlGetLastWin32Error(), iCat));
2712 if (hCatInfoPrev != NULL)
2713 g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/);
2714 g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/);
2715 goto l_fresh_context;
2716 }
2717 ULONG ulErr = RtlGetLastWin32Error();
2718 fNoSignedCatalogFound = ulErr == ERROR_NOT_FOUND && fNoSignedCatalogFound != 0;
2719 if (iCat == 0)
2720 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed ERROR_NOT_FOUND (%u)\n", ulErr));
2721 else if (iCat == 0)
2722 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed %u\n", ulErr));
2723 break;
2724 }
2725 fNoSignedCatalogFound = 0;
2726 Assert(hCatInfoPrev == NULL);
2727 hCatInfoPrev = hCatInfo;
2728
2729 /*
2730 * Call WinVerifyTrust.
2731 */
2732 CATALOG_INFO CatInfo;
2733 CatInfo.cbStruct = sizeof(CatInfo);
2734 CatInfo.wszCatalogFile[0] = '\0';
2735 if (g_pfnCryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0 /*dwFlags*/))
2736 {
2737 WINTRUST_CATALOG_INFO WtCatInfo;
2738 RT_ZERO(WtCatInfo);
2739 WtCatInfo.cbStruct = sizeof(WtCatInfo);
2740 WtCatInfo.dwCatalogVersion = 0;
2741 WtCatInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
2742 WtCatInfo.pcwszMemberTag = wszDigest;
2743 WtCatInfo.pcwszMemberFilePath = pwszWinPath;
2744 WtCatInfo.pbCalculatedFileHash = abHash;
2745 WtCatInfo.cbCalculatedFileHash = cbHash;
2746 WtCatInfo.pcCatalogContext = NULL;
2747
2748 WINTRUST_DATA TrustData;
2749 RT_ZERO(TrustData);
2750 TrustData.cbStruct = sizeof(TrustData);
2751 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
2752 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
2753 TrustData.dwUIChoice = WTD_UI_NONE;
2754 TrustData.dwProvFlags = 0;
2755 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2756 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
2757 else
2758 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
2759 TrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
2760 TrustData.pCatalog = &WtCatInfo;
2761
2762 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
2763 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: WinVerifyTrust => %#x; cat='%ls'; file='%ls'\n",
2764 hrc, CatInfo.wszCatalogFile, pwszName));
2765
2766 if (SUCCEEDED(hrc))
2767 rc = VINF_SUCCESS;
2768 else if (hrc == TRUST_E_NOSIGNATURE)
2769 { /* ignore because it's useless. */ }
2770 else if (hrc == ERROR_INVALID_PARAMETER)
2771 { /* This is returned if the given file isn't found in the catalog, it seems. */ }
2772 else
2773 {
2774 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_WINTRUST_CAT_FAILURE,
2775 "WinVerifyTrust failed with hrc=%#x on '%ls' and .cat-file='%ls'.",
2776 hrc, pwszWinPath, CatInfo.wszCatalogFile);
2777 fTryNextPolicy |= (hrc == CERT_E_UNTRUSTEDROOT);
2778 }
2779
2780 /* clean up state data. */
2781 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
2782 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
2783 Assert(SUCCEEDED(hrc));
2784 }
2785 else
2786 {
2787 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2788 "CryptCATCatalogInfoFromContext failed: %d [file=%s]",
2789 RtlGetLastWin32Error(), pwszName);
2790 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATCatalogInfoFromContext failed\n"));
2791 }
2792 iCat++;
2793 } while (rc == VERR_LDRVI_NOT_SIGNED && iCat < 128);
2794
2795 if (hCatInfoPrev != NULL)
2796 if (!g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/))
2797 AssertFailed();
2798 }
2799 else
2800 rc = RTErrInfoSetF(pErrInfo, rc2, "RTUtf16PrintHexBytes failed: %Rrc", rc);
2801 }
2802 else
2803 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2804 "CryptCATAdminCalcHashFromFileHandle[2] failed: %d [file=%s]", RtlGetLastWin32Error(), pwszName);
2805
2806 if (!ASMAtomicCmpXchgPtr(&s_aHashes[i].hCachedCatAdmin, hCatAdmin, NULL))
2807 if (!g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/))
2808 AssertFailed();
2809 }
2810 else
2811 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2812 "CryptCATAdminAcquireContext[2] failed: %d [file=%s]", RtlGetLastWin32Error(), pwszName);
2813 iPolicy++;
2814 } while ( fTryNextPolicy
2815 && iPolicy < RT_ELEMENTS(s_aPolicies));
2816
2817 /*
2818 * Only repeat if we've got g_pfnCryptCATAdminAcquireContext2 and can specify the hash algorithm.
2819 */
2820 if (!g_pfnCryptCATAdminAcquireContext2)
2821 break;
2822 if (rc != VERR_LDRVI_NOT_SIGNED)
2823 break;
2824 }
2825
2826 if (hFileClose != NULL)
2827 NtClose(hFileClose);
2828
2829 /*
2830 * DLLs that are likely candidates for local modifications.
2831 */
2832 if (rc == VERR_LDRVI_NOT_SIGNED)
2833 {
2834 bool fCoreSystemDll = false;
2835 PCRTUTF16 pwsz;
2836 uint32_t cwcName = (uint32_t)RTUtf16Len(pwszName);
2837 uint32_t cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR);
2838 if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_System32NtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
2839 {
2840 pwsz = pwszName + cwcOther + 1;
2841 if ( supHardViUtf16PathIsEqual(pwsz, "uxtheme.dll")
2842 || supHardViUtf16PathIsEqual(pwsz, "user32.dll")
2843 || supHardViUtf16PathIsEqual(pwsz, "gdi32.dll")
2844 || supHardViUtf16PathIsEqual(pwsz, "opengl32.dll")
2845 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "KernelBase.dll"))
2846 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "kernel32.dll"))
2847 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "ntdll.dll"))
2848 )
2849 {
2850 if (RTErrInfoIsSet(pErrInfo))
2851 RTErrInfoAdd(pErrInfo, rc, "\n");
2852 RTErrInfoAddF(pErrInfo, rc, "'%ls' is most likely modified.", pwszName);
2853 }
2854 }
2855
2856 /* Kludge for ancient windows versions we don't want to support but
2857 users still wants to use. Keep things as safe as possible without
2858 unnecessary effort. Problem is that 3rd party catalog files cannot
2859 easily be found. Showstopper for ATI users. */
2860 if ( fNoSignedCatalogFound == 1
2861 && g_uNtVerCombined < SUP_NT_VER_VISTA
2862 && !fCoreSystemDll)
2863 {
2864 rc = VINF_LDRVI_NOT_SIGNED;
2865 }
2866 }
2867
2868 return rc;
2869 }
2870
2871
2872 /**
2873 * Verifies the given image using WinVerifyTrust in some way.
2874 *
2875 * This is used by supHardenedWinVerifyImageByLdrMod as well as
2876 * supR3HardenedScreenImage.
2877 *
2878 * @returns IPRT status code, modified @a rc.
2879 * @param hFile Handle of the file to verify.
2880 * @param pwszName Full NT path to the DLL in question, used for
2881 * dealing with unsigned system dlls as well as for
2882 * error/logging.
2883 * @param fFlags SUPHNTVI_F_XXX.
2884 * @param rc The current status code.
2885 * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was
2886 * actually used.
2887 * @param pErrInfo Pointer to error info structure. Optional.
2888 */
2889 DECLHIDDEN(int) supHardenedWinVerifyImageTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, int rc,
2890 bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
2891 {
2892 if (pfWinVerifyTrust)
2893 *pfWinVerifyTrust = false;
2894
2895 /*
2896 * Call the windows verify trust API if we've resolved it and aren't in
2897 * some obvious recursion.
2898 */
2899 if (g_pfnWinVerifyTrust != NULL)
2900 {
2901 uint32_t const idCurrentThread = RTNtCurrentThreadId();
2902
2903 /* Check if loader lock owner. */
2904 struct _RTL_CRITICAL_SECTION volatile *pLoaderLock = NtCurrentPeb()->LoaderLock;
2905 bool fOwnsLoaderLock = pLoaderLock
2906 && pLoaderLock->OwningThread == (HANDLE)(uintptr_t)idCurrentThread
2907 && pLoaderLock->RecursionCount > 0;
2908 if (!fOwnsLoaderLock)
2909 {
2910 /* Check for recursion. */
2911 bool fNoRecursion;
2912 if (g_iTlsWinVerifyTrustRecursion != UINT32_MAX)
2913 {
2914 fNoRecursion = TlsGetValue(g_iTlsWinVerifyTrustRecursion) == 0;
2915 if (fNoRecursion)
2916 TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)1);
2917 }
2918 else
2919 fNoRecursion = ASMAtomicCmpXchgU32(&g_idActiveThread, idCurrentThread, UINT32_MAX);
2920
2921 if (fNoRecursion && !fOwnsLoaderLock)
2922 {
2923 /* We can call WinVerifyTrust. */
2924 if (pfWinVerifyTrust)
2925 *pfWinVerifyTrust = true;
2926
2927 if (rc != VERR_LDRVI_NOT_SIGNED)
2928 {
2929 if (rc == VINF_LDRVI_NOT_SIGNED)
2930 {
2931 if (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION)
2932 {
2933 int rc2 = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo,
2934 g_pfnWinVerifyTrust);
2935 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (org %d)\n", rc2, rc));
2936 rc = rc2;
2937 }
2938 else
2939 {
2940 AssertFailed();
2941 rc = VERR_LDRVI_NOT_SIGNED;
2942 }
2943 }
2944 else if (RT_SUCCESS(rc))
2945 {
2946 HRESULT hrcWinVerifyTrust;
2947 rc = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust,
2948 &hrcWinVerifyTrust);
2949
2950 /* DLLs signed with special roots, like "Microsoft Digital Media Authority 2005",
2951 may fail here because the root cert is not in the normal certificate stores
2952 (if any). Our verification code has the basics of these certificates included
2953 and can verify them, which is why we end up here instead of in the
2954 VINF_LDRVI_NOT_SIGNED case above. Current workaround is to do as above.
2955 (Intel graphics driver DLLs, like igdusc64.dll. */
2956 if ( RT_FAILURE(rc)
2957 && hrcWinVerifyTrust == CERT_E_CHAINING
2958 && (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION))
2959 {
2960 rc = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust);
2961 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (was CERT_E_CHAINING)\n", rc));
2962 }
2963 }
2964 else
2965 {
2966 int rc2 = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust, NULL);
2967 AssertMsg(RT_FAILURE_NP(rc2),
2968 ("rc=%Rrc, rc2=%Rrc %s", rc, rc2, pErrInfo ? pErrInfo->pszMsg : "<no-err-info>"));
2969 RT_NOREF_PV(rc2);
2970 }
2971 }
2972
2973 /* Unwind recursion. */
2974 if (g_iTlsWinVerifyTrustRecursion != UINT32_MAX)
2975 TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)0);
2976 else
2977 ASMAtomicWriteU32(&g_idActiveThread, UINT32_MAX);
2978 }
2979 /*
2980 * No can do.
2981 */
2982 else
2983 SUP_DPRINTF(("Detected WinVerifyTrust recursion: rc=%Rrc '%ls'.\n", rc, pwszName));
2984 }
2985 else
2986 SUP_DPRINTF(("Detected loader lock ownership: rc=%Rrc '%ls'.\n", rc, pwszName));
2987 }
2988 return rc;
2989 }
2990
2991
2992 /**
2993 * Checks if WinVerifyTrust is callable on the current thread.
2994 *
2995 * Used by the main code to figure whether it makes sense to try revalidate an
2996 * image that hasn't passed thru WinVerifyTrust yet.
2997 *
2998 * @returns true if callable on current thread, false if not.
2999 */
3000 DECLHIDDEN(bool) supHardenedWinIsWinVerifyTrustCallable(void)
3001 {
3002 return g_pfnWinVerifyTrust != NULL
3003 && ( g_iTlsWinVerifyTrustRecursion != UINT32_MAX
3004 ? (uintptr_t)TlsGetValue(g_iTlsWinVerifyTrustRecursion) == 0
3005 : g_idActiveThread != RTNtCurrentThreadId() );
3006 }
3007
3008
3009
3010 /**
3011 * Initializes g_uNtVerCombined and g_NtVerInfo.
3012 * Called from suplibHardenedWindowsMain and suplibOsInit.
3013 */
3014 DECLHIDDEN(void) supR3HardenedWinInitVersion(bool fEarly)
3015 {
3016 /*
3017 * Get the windows version. Use RtlGetVersion as GetVersionExW and
3018 * GetVersion might not be telling the whole truth (8.0 on 8.1 depending on
3019 * the application manifest).
3020 *
3021 * Note! Windows 10 build 14267+ touches BSS when calling RtlGetVersion, so we
3022 * have to use the fallback for the call from the early init code.
3023 */
3024 OSVERSIONINFOEXW NtVerInfo;
3025
3026 RT_ZERO(NtVerInfo);
3027 NtVerInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
3028 if ( fEarly
3029 || !NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&NtVerInfo)))
3030 {
3031 RT_ZERO(NtVerInfo);
3032 PPEB pPeb = NtCurrentPeb();
3033 NtVerInfo.dwMajorVersion = pPeb->OSMajorVersion;
3034 NtVerInfo.dwMinorVersion = pPeb->OSMinorVersion;
3035 NtVerInfo.dwBuildNumber = pPeb->OSBuildNumber;
3036 }
3037
3038 g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(NtVerInfo.dwMajorVersion, NtVerInfo.dwMinorVersion, NtVerInfo.dwBuildNumber,
3039 NtVerInfo.wServicePackMajor, NtVerInfo.wServicePackMinor);
3040 }
3041
3042 #endif /* IN_RING3 */
3043