VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyImage-win.cpp@ 55007

Last change on this file since 55007 was 55007, checked in by vboxsync, 10 years ago

supHardNt: Corrected loader lock ownership check. Turns out LockCount doesn't behave like expected, check RecursionCount instead to make a definite positive on ownership.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette