VirtualBox

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

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

supHardNt: bcrypt.dll is not mandatory

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 119.7 KB
Line 
1/* $Id: SUPHardenedVerifyImage-win.cpp 55017 2015-03-31 07:20:36Z 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 failure if fMandatory = true.
1912 * @param pszName The name of the DLL to load.
1913 * @param fMandatory Whether the library is mandatory.
1914 */
1915DECLHIDDEN(HMODULE) supR3HardenedWinLoadSystem32Dll(const char *pszName, bool fMandatory)
1916{
1917 WCHAR wszName[200+60];
1918 UINT cwcDir = GetSystemDirectoryW(wszName, RT_ELEMENTS(wszName) - 60);
1919 wszName[cwcDir] = '\\';
1920 RTUtf16CopyAscii(&wszName[cwcDir + 1], RT_ELEMENTS(wszName) - cwcDir, pszName);
1921
1922 DWORD fFlags = 0;
1923 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
1924 fFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;
1925 HMODULE hMod = LoadLibraryExW(wszName, NULL, fFlags);
1926 if ( hMod == NULL
1927 && fFlags
1928 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
1929 && RtlGetLastWin32Error() == ERROR_INVALID_PARAMETER)
1930 {
1931 fFlags = 0;
1932 hMod = LoadLibraryExW(wszName, NULL, fFlags);
1933 }
1934 if ( hMod == NULL
1935 && fMandatory)
1936 supR3HardenedFatal("Error loading '%s': %u [%ls]", pszName, RtlGetLastWin32Error(), wszName);
1937 return hMod;
1938}
1939
1940
1941/**
1942 * Called by supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation to
1943 * import selected root CAs from the system certificate store.
1944 *
1945 * These certificates permits us to correctly validate third party DLLs.
1946 */
1947static void supR3HardenedWinRetrieveTrustedRootCAs(void)
1948{
1949 uint32_t cAdded = 0;
1950
1951 /*
1952 * Load crypt32.dll and resolve the APIs we need.
1953 */
1954 HMODULE hCrypt32 = supR3HardenedWinLoadSystem32Dll("crypt32.dll", true /*fMandatory*/);
1955
1956#define RESOLVE_CRYPT32_API(a_Name, a_pfnType) \
1957 a_pfnType pfn##a_Name = (a_pfnType)GetProcAddress(hCrypt32, #a_Name); \
1958 if (pfn##a_Name == NULL) supR3HardenedFatal("Error locating '" #a_Name "' in 'crypt32.dll': %u", RtlGetLastWin32Error())
1959 RESOLVE_CRYPT32_API(CertOpenStore, PFNCERTOPENSTORE);
1960 RESOLVE_CRYPT32_API(CertCloseStore, PFNCERTCLOSESTORE);
1961 RESOLVE_CRYPT32_API(CertEnumCertificatesInStore, PFNCERTENUMCERTIFICATESINSTORE);
1962#undef RESOLVE_CRYPT32_API
1963
1964 /*
1965 * Open the root store and look for the certificates we wish to use.
1966 */
1967 DWORD fOpenStore = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
1968 HCERTSTORE hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1969 NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_LOCAL_MACHINE | fOpenStore, L"Root");
1970 if (!hStore)
1971 hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1972 NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_CURRENT_USER | fOpenStore, L"Root");
1973 if (hStore)
1974 {
1975 PCCERT_CONTEXT pCurCtx = NULL;
1976 while ((pCurCtx = pfnCertEnumCertificatesInStore(hStore, pCurCtx)) != NULL)
1977 {
1978 if (pCurCtx->dwCertEncodingType & X509_ASN_ENCODING)
1979 {
1980 RTERRINFOSTATIC StaticErrInfo;
1981 RTASN1CURSORPRIMARY PrimaryCursor;
1982 RTAsn1CursorInitPrimary(&PrimaryCursor, pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded,
1983 RTErrInfoInitStatic(&StaticErrInfo),
1984 &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
1985 RTCRX509CERTIFICATE MyCert;
1986 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &MyCert, "Cert");
1987 if (RT_SUCCESS(rc))
1988 {
1989 if (supR3HardenedWinIsDesiredRootCA(&MyCert))
1990 {
1991 rc = RTCrStoreCertAddEncoded(g_hSpcRootStore, RTCRCERTCTX_F_ENC_X509_DER,
1992 pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
1993 AssertRC(rc);
1994
1995 rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
1996 pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
1997 AssertRC(rc);
1998 cAdded++;
1999 }
2000
2001 RTCrX509Certificate_Delete(&MyCert);
2002 }
2003 /* XP root certificate "C&W HKT SecureNet CA SGC Root" has non-standard validity
2004 timestamps, the UTC formatting isn't Zulu time but specifies timezone offsets.
2005 Ignore these failures and certificates. */
2006 else if (rc != VERR_ASN1_INVALID_UTC_TIME_ENCODING)
2007 AssertMsgFailed(("RTCrX509Certificate_DecodeAsn1 failed: rc=%#x: %s\n", rc, StaticErrInfo.szMsg));
2008 }
2009 }
2010 pfnCertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
2011 g_fHaveOtherRoots = true;
2012 }
2013 SUP_DPRINTF(("supR3HardenedWinRetrieveTrustedRootCAs: cAdded=%u\n", cAdded));
2014}
2015
2016
2017/**
2018 * Resolves the WinVerifyTrust API after the process has been verified and
2019 * installs a thread creation hook.
2020 *
2021 * The WinVerifyTrust API is used in addition our own Authenticode verification
2022 * code. If the image has the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag
2023 * set, it will be checked again by the kernel. All our image has this flag set
2024 * and we require all VBox extensions to have it set as well. In effect, the
2025 * authenticode signature will be checked two or three times.
2026 *
2027 * @param pszProgName The program name.
2028 */
2029DECLHIDDEN(void) supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(const char *pszProgName)
2030{
2031# ifdef IN_SUP_HARDENED_R3
2032 /*
2033 * Load our the support library DLL that does the thread hooking as the
2034 * security API may trigger the creation of COM worker threads (or
2035 * whatever they are).
2036 *
2037 * The thread creation hook makes the threads very slippery to debuggers by
2038 * irreversably disabling most (if not all) debug events for them.
2039 */
2040 char szPath[RTPATH_MAX];
2041 supR3HardenedPathSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxSupLib.DLL"));
2042 suplibHardenedStrCat(szPath, "/VBoxSupLib.DLL");
2043 HMODULE hSupLibMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, true /*fSystem32Only*/);
2044 if (hSupLibMod == NULL)
2045 supR3HardenedFatal("Error loading '%s': %u", szPath, RtlGetLastWin32Error());
2046# endif
2047
2048 /*
2049 * Allocate TLS entry for WinVerifyTrust recursion prevention.
2050 */
2051 DWORD iTls = TlsAlloc();
2052 if (iTls != TLS_OUT_OF_INDEXES)
2053 g_iTlsWinVerifyTrustRecursion = iTls;
2054 else
2055 supR3HardenedError(RtlGetLastWin32Error(), false /*fFatal*/, "TlsAlloc failed");
2056
2057 /*
2058 * Resolve the imports we need.
2059 */
2060 HMODULE hWintrust = supR3HardenedWinLoadSystem32Dll("Wintrust.dll", true /*fMandatory*/);
2061#define RESOLVE_CRYPT_API(a_Name, a_pfnType, a_uMinWinVer) \
2062 do { \
2063 g_pfn##a_Name = (a_pfnType)GetProcAddress(hWintrust, #a_Name); \
2064 if (g_pfn##a_Name == NULL && (a_uMinWinVer) < g_uNtVerCombined) \
2065 supR3HardenedFatal("Error locating '" #a_Name "' in 'Wintrust.dll': %u", RtlGetLastWin32Error()); \
2066 } while (0)
2067
2068 PFNWINVERIFYTRUST pfnWinVerifyTrust = (PFNWINVERIFYTRUST)GetProcAddress(hWintrust, "WinVerifyTrust");
2069 if (!pfnWinVerifyTrust)
2070 supR3HardenedFatal("Error locating 'WinVerifyTrust' in 'Wintrust.dll': %u", RtlGetLastWin32Error());
2071
2072 RESOLVE_CRYPT_API(CryptCATAdminAcquireContext, PFNCRYPTCATADMINACQUIRECONTEXT, 0);
2073 RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE, 0);
2074 RESOLVE_CRYPT_API(CryptCATAdminEnumCatalogFromHash, PFNCRYPTCATADMINENUMCATALOGFROMHASH, 0);
2075 RESOLVE_CRYPT_API(CryptCATAdminReleaseCatalogContext, PFNCRYPTCATADMINRELEASECATALOGCONTEXT, 0);
2076 RESOLVE_CRYPT_API(CryptCATAdminReleaseContext, PFNCRYPTCATDADMINRELEASECONTEXT, 0);
2077 RESOLVE_CRYPT_API(CryptCATCatalogInfoFromContext, PFNCRYPTCATCATALOGINFOFROMCONTEXT, 0);
2078
2079 RESOLVE_CRYPT_API(CryptCATAdminAcquireContext2, PFNCRYPTCATADMINACQUIRECONTEXT2, SUP_NT_VER_W80);
2080 RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle2, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2, SUP_NT_VER_W80);
2081
2082# ifdef IN_SUP_HARDENED_R3
2083 /*
2084 * Load bcrypt.dll and instantiate a few hashing and signing providers to
2085 * make sure the providers are cached for later us. Avoid recursion issues.
2086 */
2087 HMODULE hBCrypt = supR3HardenedWinLoadSystem32Dll("bcrypt.dll", false /*fMandatory*/);
2088 if (hBCrypt)
2089 {
2090 PFNBCRYPTOPENALGORTIHMPROVIDER pfnOpenAlgoProvider;
2091 pfnOpenAlgoProvider = (PFNBCRYPTOPENALGORTIHMPROVIDER)GetProcAddress(hBCrypt, "BCryptOpenAlgorithmProvider");
2092 if (pfnOpenAlgoProvider)
2093 {
2094 SUP_DPRINTF(("bcrypt.dll loaded at %p, BCryptOpenAlgorithmProvider at %p, preloading providers:\n",
2095 hBCrypt, pfnOpenAlgoProvider));
2096# define PRELOAD_ALGO_PROVIDER(a_Name) \
2097 do { \
2098 BCRYPT_ALG_HANDLE hAlgo = NULL; \
2099 NTSTATUS rcNt = pfnOpenAlgoProvider(&hAlgo, a_Name, NULL, 0); \
2100 SUP_DPRINTF(("%sBCryptOpenAlgorithmProvider(,'%ls',0,0) -> %#x (hAlgo=%p)\n", \
2101 NT_SUCCESS(rcNt) ? " " : "warning: ", a_Name, rcNt, hAlgo)); \
2102 } while (0)
2103 PRELOAD_ALGO_PROVIDER(BCRYPT_MD2_ALGORITHM);
2104 PRELOAD_ALGO_PROVIDER(BCRYPT_MD4_ALGORITHM);
2105 PRELOAD_ALGO_PROVIDER(BCRYPT_MD5_ALGORITHM);
2106 PRELOAD_ALGO_PROVIDER(BCRYPT_SHA1_ALGORITHM);
2107 PRELOAD_ALGO_PROVIDER(BCRYPT_SHA256_ALGORITHM);
2108 PRELOAD_ALGO_PROVIDER(BCRYPT_SHA512_ALGORITHM);
2109 PRELOAD_ALGO_PROVIDER(BCRYPT_RSA_ALGORITHM);
2110 PRELOAD_ALGO_PROVIDER(BCRYPT_DSA_ALGORITHM);
2111# undef PRELOAD_ALGO_PROVIDER
2112 }
2113 else
2114 SUP_DPRINTF(("Warning! Failed to find BCryptOpenAlgorithmProvider in bcrypt.dll\n"));
2115 }
2116 else
2117 SUP_DPRINTF(("Warning! Failed to load bcrypt.dll\n"));
2118
2119 /*
2120 * Call the verification API on ourselves and ntdll to make sure it works
2121 * and loads more stuff it needs, preventing any recursive fun we'd run
2122 * into after we set g_pfnWinVerifyTrust.
2123 */
2124 RTERRINFOSTATIC ErrInfoStatic;
2125 RTErrInfoInitStatic(&ErrInfoStatic);
2126 int rc = supR3HardNtViCallWinVerifyTrust(NULL, g_SupLibHardenedExeNtPath.UniStr.Buffer, 0,
2127 &ErrInfoStatic.Core, pfnWinVerifyTrust, NULL);
2128 if (RT_FAILURE(rc))
2129 supR3HardenedFatalMsg(pszProgName, kSupInitOp_Integrity, rc,
2130 "WinVerifyTrust failed on stub executable: %s", ErrInfoStatic.szMsg);
2131# endif
2132
2133 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. */
2134 supR3HardNtViCallWinVerifyTrust(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust, NULL);
2135 supR3HardNtViCallWinVerifyTrustCatFile(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
2136
2137 g_pfnWinVerifyTrust = pfnWinVerifyTrust;
2138 SUP_DPRINTF(("g_pfnWinVerifyTrust=%p\n", pfnWinVerifyTrust));
2139
2140# ifdef IN_SUP_HARDENED_R3
2141 /*
2142 * Load some problematic DLLs into the verifier cache to prevent
2143 * recursion trouble.
2144 */
2145 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\crypt32.dll");
2146 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\Wintrust.dll");
2147# endif
2148
2149 /*
2150 * Now, get trusted root CAs so we can verify a broader scope of signatures.
2151 */
2152 supR3HardenedWinRetrieveTrustedRootCAs();
2153}
2154
2155
2156static int supR3HardNtViNtToWinPath(PCRTUTF16 pwszNtName, PCRTUTF16 *ppwszWinPath,
2157 PRTUTF16 pwszWinPathBuf, size_t cwcWinPathBuf)
2158{
2159 static const RTUTF16 s_wszPrefix[] = L"\\\\.\\GLOBALROOT";
2160
2161 if (*pwszNtName != '\\' && *pwszNtName != '/')
2162 return VERR_PATH_DOES_NOT_START_WITH_ROOT;
2163
2164 size_t cwcNtName = RTUtf16Len(pwszNtName);
2165 if (RT_ELEMENTS(s_wszPrefix) + cwcNtName > cwcWinPathBuf)
2166 return VERR_FILENAME_TOO_LONG;
2167
2168 memcpy(pwszWinPathBuf, s_wszPrefix, sizeof(s_wszPrefix));
2169 memcpy(&pwszWinPathBuf[sizeof(s_wszPrefix) / sizeof(RTUTF16) - 1], pwszNtName, (cwcNtName + 1) * sizeof(RTUTF16));
2170 *ppwszWinPath = pwszWinPathBuf;
2171 return VINF_SUCCESS;
2172}
2173
2174
2175/**
2176 * Calls WinVerifyTrust to verify an PE image.
2177 *
2178 * @returns VBox status code.
2179 * @param hFile File handle to the executable file.
2180 * @param pwszName Full NT path to the DLL in question, used for
2181 * dealing with unsigned system dlls as well as for
2182 * error/logging.
2183 * @param fFlags Flags, SUPHNTVI_F_XXX.
2184 * @param pErrInfo Pointer to error info structure. Optional.
2185 * @param pfnWinVerifyTrust Pointer to the API.
2186 * @param phrcWinVerifyTrust Where to WinVerifyTrust error status on failure,
2187 * optional.
2188 */
2189static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
2190 PFNWINVERIFYTRUST pfnWinVerifyTrust, HRESULT *phrcWinVerifyTrust)
2191{
2192 if (phrcWinVerifyTrust)
2193 *phrcWinVerifyTrust = S_OK;
2194
2195 /*
2196 * Convert the name into a Windows name.
2197 */
2198 RTUTF16 wszWinPathBuf[MAX_PATH];
2199 PCRTUTF16 pwszWinPath;
2200 int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
2201 if (RT_FAILURE(rc))
2202 return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrust: rc=%Rrc '%ls'", rc, pwszName);
2203
2204 /*
2205 * Construct input parameters and call the API.
2206 */
2207 WINTRUST_FILE_INFO FileInfo;
2208 RT_ZERO(FileInfo);
2209 FileInfo.cbStruct = sizeof(FileInfo);
2210 FileInfo.pcwszFilePath = pwszWinPath;
2211 FileInfo.hFile = hFile;
2212
2213 GUID PolicyActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
2214
2215 WINTRUST_DATA TrustData;
2216 RT_ZERO(TrustData);
2217 TrustData.cbStruct = sizeof(TrustData);
2218 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
2219 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
2220 TrustData.dwUIChoice = WTD_UI_NONE;
2221 TrustData.dwProvFlags = 0;
2222 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2223 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
2224 else
2225 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
2226 TrustData.dwUnionChoice = WTD_CHOICE_FILE;
2227 TrustData.pFile = &FileInfo;
2228
2229 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
2230 if (hrc == S_OK)
2231 rc = VINF_SUCCESS;
2232 else
2233 {
2234 /*
2235 * Failed. Format a nice error message.
2236 */
2237# ifdef DEBUG_bird
2238 if (hrc != CERT_E_CHAINING /* Un-updated vistas, XPs, ++ */)
2239 __debugbreak();
2240# endif
2241 const char *pszErrConst = NULL;
2242 switch (hrc)
2243 {
2244 case TRUST_E_SYSTEM_ERROR: pszErrConst = "TRUST_E_SYSTEM_ERROR"; break;
2245 case TRUST_E_NO_SIGNER_CERT: pszErrConst = "TRUST_E_NO_SIGNER_CERT"; break;
2246 case TRUST_E_COUNTER_SIGNER: pszErrConst = "TRUST_E_COUNTER_SIGNER"; break;
2247 case TRUST_E_CERT_SIGNATURE: pszErrConst = "TRUST_E_CERT_SIGNATURE"; break;
2248 case TRUST_E_TIME_STAMP: pszErrConst = "TRUST_E_TIME_STAMP"; break;
2249 case TRUST_E_BAD_DIGEST: pszErrConst = "TRUST_E_BAD_DIGEST"; break;
2250 case TRUST_E_BASIC_CONSTRAINTS: pszErrConst = "TRUST_E_BASIC_CONSTRAINTS"; break;
2251 case TRUST_E_FINANCIAL_CRITERIA: pszErrConst = "TRUST_E_FINANCIAL_CRITERIA"; break;
2252 case TRUST_E_PROVIDER_UNKNOWN: pszErrConst = "TRUST_E_PROVIDER_UNKNOWN"; break;
2253 case TRUST_E_ACTION_UNKNOWN: pszErrConst = "TRUST_E_ACTION_UNKNOWN"; break;
2254 case TRUST_E_SUBJECT_FORM_UNKNOWN: pszErrConst = "TRUST_E_SUBJECT_FORM_UNKNOWN"; break;
2255 case TRUST_E_SUBJECT_NOT_TRUSTED: pszErrConst = "TRUST_E_SUBJECT_NOT_TRUSTED"; break;
2256 case TRUST_E_NOSIGNATURE: pszErrConst = "TRUST_E_NOSIGNATURE"; break;
2257 case TRUST_E_FAIL: pszErrConst = "TRUST_E_FAIL"; break;
2258 case TRUST_E_EXPLICIT_DISTRUST: pszErrConst = "TRUST_E_EXPLICIT_DISTRUST"; break;
2259 case CERT_E_CHAINING: pszErrConst = "CERT_E_CHAINING"; break;
2260 case CERT_E_REVOCATION_FAILURE: pszErrConst = "CERT_E_REVOCATION_FAILURE"; break;
2261 case CRYPT_E_FILE_ERROR: pszErrConst = "CRYPT_E_FILE_ERROR"; break;
2262 case CRYPT_E_REVOKED: pszErrConst = "CRYPT_E_REVOKED"; break;
2263 }
2264 if (pszErrConst)
2265 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
2266 "WinVerifyTrust failed with hrc=%s on '%ls'", pszErrConst, pwszName);
2267 else
2268 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
2269 "WinVerifyTrust failed with hrc=%Rhrc on '%ls'", hrc, pwszName);
2270 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrust: WinVerifyTrust failed with %#x (%s) on '%ls'\n",
2271 hrc, pszErrConst, pwszName));
2272 if (phrcWinVerifyTrust)
2273 *phrcWinVerifyTrust = hrc;
2274 }
2275
2276 /* clean up state data. */
2277 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
2278 FileInfo.hFile = NULL;
2279 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
2280
2281 return rc;
2282}
2283
2284
2285/**
2286 * Calls WinVerifyTrust to verify an PE image via catalog files.
2287 *
2288 * @returns VBox status code.
2289 * @param hFile File handle to the executable file.
2290 * @param pwszName Full NT path to the DLL in question, used for
2291 * dealing with unsigned system dlls as well as for
2292 * error/logging.
2293 * @param fFlags Flags, SUPHNTVI_F_XXX.
2294 * @param pErrInfo Pointer to error info structure. Optional.
2295 * @param pfnWinVerifyTrust Pointer to the API.
2296 */
2297static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
2298 PFNWINVERIFYTRUST pfnWinVerifyTrust)
2299{
2300 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hFile=%p pwszName=%ls\n", hFile, pwszName));
2301
2302 /*
2303 * Convert the name into a Windows name.
2304 */
2305 RTUTF16 wszWinPathBuf[MAX_PATH];
2306 PCRTUTF16 pwszWinPath;
2307 int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
2308 if (RT_FAILURE(rc))
2309 return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrustCatFile: rc=%Rrc '%ls'", rc, pwszName);
2310
2311 /*
2312 * Open the file if we didn't get a handle.
2313 */
2314 HANDLE hFileClose = NULL;
2315 if (hFile == RTNT_INVALID_HANDLE_VALUE || hFile == NULL)
2316 {
2317 hFile = RTNT_INVALID_HANDLE_VALUE;
2318 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2319
2320 UNICODE_STRING NtName;
2321 NtName.Buffer = (PWSTR)pwszName;
2322 NtName.Length = (USHORT)(RTUtf16Len(pwszName) * sizeof(WCHAR));
2323 NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
2324
2325 OBJECT_ATTRIBUTES ObjAttr;
2326 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
2327
2328 NTSTATUS rcNt = NtCreateFile(&hFile,
2329 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
2330 &ObjAttr,
2331 &Ios,
2332 NULL /* Allocation Size*/,
2333 FILE_ATTRIBUTE_NORMAL,
2334 FILE_SHARE_READ,
2335 FILE_OPEN,
2336 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
2337 NULL /*EaBuffer*/,
2338 0 /*EaLength*/);
2339 if (NT_SUCCESS(rcNt))
2340 rcNt = Ios.Status;
2341 if (!NT_SUCCESS(rcNt))
2342 return RTErrInfoSetF(pErrInfo, RTErrConvertFromNtStatus(rcNt),
2343 "NtCreateFile returned %#x opening '%ls'.", rcNt, pwszName);
2344 hFileClose = hFile;
2345 }
2346
2347 /*
2348 * On Windows 8.0 and later there are more than one digest choice.
2349 */
2350 int fNoSignedCatalogFound = -1;
2351 rc = VERR_LDRVI_NOT_SIGNED;
2352 static struct
2353 {
2354 /** The digest algorithm name. */
2355 const WCHAR *pszAlgorithm;
2356 /** Cached catalog admin handle. */
2357 HCATADMIN volatile hCachedCatAdmin;
2358 } s_aHashes[] =
2359 {
2360 { NULL, NULL },
2361 { L"SHA256", NULL },
2362 };
2363 for (uint32_t i = 0; i < RT_ELEMENTS(s_aHashes); i++)
2364 {
2365 /*
2366 * Another loop for dealing with different trust provider policies
2367 * required for successfully validating different catalog signatures.
2368 */
2369 bool fTryNextPolicy;
2370 uint32_t iPolicy = 0;
2371 static const GUID s_aPolicies[] =
2372 {
2373 DRIVER_ACTION_VERIFY, /* Works with microsoft bits. Most frequently used, thus first. */
2374 WINTRUST_ACTION_GENERIC_VERIFY_V2, /* Works with ATI and other SPC kernel-code signed stuff. */
2375 };
2376 do
2377 {
2378 /*
2379 * Create a context.
2380 */
2381 fTryNextPolicy = false;
2382 bool fFreshContext = false;
2383 BOOL fRc;
2384 HCATADMIN hCatAdmin = ASMAtomicXchgPtr(&s_aHashes[i].hCachedCatAdmin, NULL);
2385 if (hCatAdmin)
2386 {
2387 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Cached context %p\n", hCatAdmin));
2388 fFreshContext = false;
2389 fRc = TRUE;
2390 }
2391 else
2392 {
2393l_fresh_context:
2394 fFreshContext = true;
2395 if (g_pfnCryptCATAdminAcquireContext2)
2396 fRc = g_pfnCryptCATAdminAcquireContext2(&hCatAdmin, &s_aPolicies[iPolicy], s_aHashes[i].pszAlgorithm,
2397 NULL /*pStrongHashPolicy*/, 0 /*dwFlags*/);
2398 else
2399 fRc = g_pfnCryptCATAdminAcquireContext(&hCatAdmin, &s_aPolicies[iPolicy], 0 /*dwFlags*/);
2400 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: New context %p\n", hCatAdmin));
2401 }
2402 if (fRc)
2403 {
2404 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hCatAdmin=%p\n", hCatAdmin));
2405
2406 /*
2407 * Hash the file.
2408 */
2409 BYTE abHash[SUPHARDNTVI_MAX_CAT_HASH_SIZE];
2410 DWORD cbHash = sizeof(abHash);
2411 if (g_pfnCryptCATAdminCalcHashFromFileHandle2)
2412 fRc = g_pfnCryptCATAdminCalcHashFromFileHandle2(hCatAdmin, hFile, &cbHash, abHash, 0 /*dwFlags*/);
2413 else
2414 fRc = g_pfnCryptCATAdminCalcHashFromFileHandle(hFile, &cbHash, abHash, 0 /*dwFlags*/);
2415 if (fRc)
2416 {
2417 /* Produce a string version of it that we can pass to WinVerifyTrust. */
2418 RTUTF16 wszDigest[SUPHARDNTVI_MAX_CAT_HASH_SIZE * 2 + 1];
2419 int rc2 = RTUtf16PrintHexBytes(wszDigest, RT_ELEMENTS(wszDigest), abHash, cbHash, RTSTRPRINTHEXBYTES_F_UPPER);
2420 if (RT_SUCCESS(rc2))
2421 {
2422 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: cbHash=%u wszDigest=%ls\n", cbHash, wszDigest));
2423
2424 /*
2425 * Enumerate catalog information that matches the hash.
2426 */
2427 uint32_t iCat = 0;
2428 HCATINFO hCatInfoPrev = NULL;
2429 do
2430 {
2431 /* Get the next match. */
2432 HCATINFO hCatInfo = g_pfnCryptCATAdminEnumCatalogFromHash(hCatAdmin, abHash, cbHash, 0, &hCatInfoPrev);
2433 if (!hCatInfo)
2434 {
2435 if (!fFreshContext)
2436 {
2437 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Retrying with fresh context (CryptCATAdminEnumCatalogFromHash -> %u; iCat=%#x)\n", RtlGetLastWin32Error(), iCat));
2438 if (hCatInfoPrev != NULL)
2439 g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/);
2440 g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/);
2441 goto l_fresh_context;
2442 }
2443 ULONG ulErr = RtlGetLastWin32Error();
2444 fNoSignedCatalogFound = ulErr == ERROR_NOT_FOUND && fNoSignedCatalogFound != 0;
2445 if (iCat == 0)
2446 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed ERRROR_NOT_FOUND (%u)\n", ulErr));
2447 else if (iCat == 0)
2448 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed %u\n", ulErr));
2449 break;
2450 }
2451 fNoSignedCatalogFound = 0;
2452 Assert(hCatInfoPrev == NULL);
2453 hCatInfoPrev = hCatInfo;
2454
2455 /*
2456 * Call WinVerifyTrust.
2457 */
2458 CATALOG_INFO CatInfo;
2459 CatInfo.cbStruct = sizeof(CatInfo);
2460 CatInfo.wszCatalogFile[0] = '\0';
2461 if (g_pfnCryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0 /*dwFlags*/))
2462 {
2463 WINTRUST_CATALOG_INFO WtCatInfo;
2464 RT_ZERO(WtCatInfo);
2465 WtCatInfo.cbStruct = sizeof(WtCatInfo);
2466 WtCatInfo.dwCatalogVersion = 0;
2467 WtCatInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
2468 WtCatInfo.pcwszMemberTag = wszDigest;
2469 WtCatInfo.pcwszMemberFilePath = pwszWinPath;
2470 WtCatInfo.pbCalculatedFileHash = abHash;
2471 WtCatInfo.cbCalculatedFileHash = cbHash;
2472 WtCatInfo.pcCatalogContext = NULL;
2473
2474 WINTRUST_DATA TrustData;
2475 RT_ZERO(TrustData);
2476 TrustData.cbStruct = sizeof(TrustData);
2477 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
2478 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
2479 TrustData.dwUIChoice = WTD_UI_NONE;
2480 TrustData.dwProvFlags = 0;
2481 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2482 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
2483 else
2484 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
2485 TrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
2486 TrustData.pCatalog = &WtCatInfo;
2487
2488 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
2489 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: WinVerifyTrust => %#x; cat='%ls'; file='%ls'\n",
2490 hrc, CatInfo.wszCatalogFile, pwszName));
2491
2492 if (SUCCEEDED(hrc))
2493 rc = VINF_SUCCESS;
2494 else if (hrc == TRUST_E_NOSIGNATURE)
2495 { /* ignore because it's useless. */ }
2496 else if (hrc == ERROR_INVALID_PARAMETER)
2497 { /* This is returned if the given file isn't found in the catalog, it seems. */ }
2498 else
2499 {
2500 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_WINTRUST_CAT_FAILURE,
2501 "WinVerifyTrust failed with hrc=%#x on '%ls' and .cat-file='%ls'.",
2502 hrc, pwszWinPath, CatInfo.wszCatalogFile);
2503 fTryNextPolicy |= (hrc == CERT_E_UNTRUSTEDROOT);
2504 }
2505
2506 /* clean up state data. */
2507 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
2508 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
2509 Assert(SUCCEEDED(hrc));
2510 }
2511 else
2512 {
2513 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2514 "CryptCATCatalogInfoFromContext failed: %d [file=%s]",
2515 RtlGetLastWin32Error(), pwszName);
2516 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATCatalogInfoFromContext failed\n"));
2517 }
2518 iCat++;
2519 } while (rc == VERR_LDRVI_NOT_SIGNED && iCat < 128);
2520
2521 if (hCatInfoPrev != NULL)
2522 if (!g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/))
2523 AssertFailed();
2524 }
2525 else
2526 rc = RTErrInfoSetF(pErrInfo, rc2, "RTUtf16PrintHexBytes failed: %Rrc", rc);
2527 }
2528 else
2529 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2530 "CryptCATAdminCalcHashFromFileHandle[2] failed: %d [file=%s]", RtlGetLastWin32Error(), pwszName);
2531
2532 if (!ASMAtomicCmpXchgPtr(&s_aHashes[i].hCachedCatAdmin, hCatAdmin, NULL))
2533 if (!g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/))
2534 AssertFailed();
2535 }
2536 else
2537 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2538 "CryptCATAdminAcquireContext[2] failed: %d [file=%s]", RtlGetLastWin32Error(), pwszName);
2539 iPolicy++;
2540 } while ( fTryNextPolicy
2541 && iPolicy < RT_ELEMENTS(s_aPolicies));
2542
2543 /*
2544 * Only repeat if we've got g_pfnCryptCATAdminAcquireContext2 and can specify the hash algorithm.
2545 */
2546 if (!g_pfnCryptCATAdminAcquireContext2)
2547 break;
2548 if (rc != VERR_LDRVI_NOT_SIGNED)
2549 break;
2550 }
2551
2552 if (hFileClose != NULL)
2553 NtClose(hFileClose);
2554
2555 /*
2556 * DLLs that are likely candidates for local modifications.
2557 */
2558 if (rc == VERR_LDRVI_NOT_SIGNED)
2559 {
2560 bool fCoreSystemDll = false;
2561 PCRTUTF16 pwsz;
2562 uint32_t cwcName = (uint32_t)RTUtf16Len(pwszName);
2563 uint32_t cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR);
2564 if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_System32NtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
2565 {
2566 pwsz = pwszName + cwcOther + 1;
2567 if ( supHardViUtf16PathIsEqual(pwsz, "uxtheme.dll")
2568 || supHardViUtf16PathIsEqual(pwsz, "user32.dll")
2569 || supHardViUtf16PathIsEqual(pwsz, "gdi32.dll")
2570 || supHardViUtf16PathIsEqual(pwsz, "opengl32.dll")
2571 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "KernelBase.dll"))
2572 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "kernel32.dll"))
2573 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "ntdll.dll"))
2574 )
2575 {
2576 if (RTErrInfoIsSet(pErrInfo))
2577 RTErrInfoAdd(pErrInfo, rc, "\n");
2578 RTErrInfoAddF(pErrInfo, rc, "'%ls' is most likely modified.", pwszName);
2579 }
2580 }
2581
2582 /* Kludge for ancient windows versions we don't want to support but
2583 users still wants to use. Keep things as safe as possible without
2584 unnecessary effort. Problem is that 3rd party catalog files cannot
2585 easily be found. Showstopper for ATI users. */
2586 if ( fNoSignedCatalogFound == 1
2587 && g_uNtVerCombined < SUP_NT_VER_VISTA
2588 && !fCoreSystemDll)
2589 {
2590 rc = VINF_LDRVI_NOT_SIGNED;
2591 }
2592 }
2593
2594 return rc;
2595}
2596
2597
2598/**
2599 * Verifies the given image using WinVerifyTrust in some way.
2600 *
2601 * This is used by supHardenedWinVerifyImageByLdrMod as well as
2602 * supR3HardenedScreenImage.
2603 *
2604 * @returns IPRT status code.
2605 * @param hFile Handle of the file to verify.
2606 * @param pwszName Full NT path to the DLL in question, used for
2607 * dealing with unsigned system dlls as well as for
2608 * error/logging.
2609 * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was
2610 * actually used.
2611 * @param pErrInfo Pointer to error info structure. Optional.
2612 */
2613DECLHIDDEN(int) supHardenedWinVerifyImageTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, int rc,
2614 bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
2615{
2616 if (pfWinVerifyTrust)
2617 *pfWinVerifyTrust = false;
2618
2619 /*
2620 * Call the windows verify trust API if we've resolved it and aren't in
2621 * some obvious recursion.
2622 */
2623 if (g_pfnWinVerifyTrust != NULL)
2624 {
2625 uint32_t const idCurrentThread = RTNtCurrentThreadId();
2626
2627 /* Check if loader lock owner. */
2628 struct _RTL_CRITICAL_SECTION volatile *pLoaderLock = NtCurrentPeb()->LoaderLock;
2629 bool fOwnsLoaderLock = pLoaderLock
2630 && pLoaderLock->OwningThread == (HANDLE)(uintptr_t)idCurrentThread
2631 && pLoaderLock->RecursionCount > 0;
2632 if (!fOwnsLoaderLock)
2633 {
2634 /* Check for recursion. */
2635 bool fNoRecursion;
2636 if (g_iTlsWinVerifyTrustRecursion != UINT32_MAX)
2637 {
2638 fNoRecursion = TlsGetValue(g_iTlsWinVerifyTrustRecursion) == 0;
2639 if (fNoRecursion)
2640 TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)1);
2641 }
2642 else
2643 fNoRecursion = ASMAtomicCmpXchgU32(&g_idActiveThread, idCurrentThread, UINT32_MAX);
2644
2645 if (fNoRecursion && !fOwnsLoaderLock)
2646 {
2647 /* We can call WinVerifyTrust. */
2648 if (pfWinVerifyTrust)
2649 *pfWinVerifyTrust = true;
2650
2651 if (rc != VERR_LDRVI_NOT_SIGNED)
2652 {
2653 if (rc == VINF_LDRVI_NOT_SIGNED)
2654 {
2655 if (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION)
2656 {
2657 int rc2 = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo,
2658 g_pfnWinVerifyTrust);
2659 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (org %d)\n", rc2, rc));
2660 rc = rc2;
2661 }
2662 else
2663 {
2664 AssertFailed();
2665 rc = VERR_LDRVI_NOT_SIGNED;
2666 }
2667 }
2668 else if (RT_SUCCESS(rc))
2669 {
2670 HRESULT hrcWinVerifyTrust;
2671 rc = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust,
2672 &hrcWinVerifyTrust);
2673
2674 /* DLLs signed with special roots, like "Microsoft Digital Media Authority 2005",
2675 may fail here because the root cert is not in the normal certificate stores
2676 (if any). Our verification code has the basics of these certificates included
2677 and can verify them, which is why we end up here instead of in the
2678 VINF_LDRVI_NOT_SIGNED case above. Current workaround is to do as above.
2679 (Intel graphics driver DLLs, like igdusc64.dll. */
2680 if ( RT_FAILURE(rc)
2681 && hrcWinVerifyTrust == CERT_E_CHAINING
2682 && (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION))
2683 {
2684 rc = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust);
2685 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (was CERT_E_CHAINING)\n", rc));
2686 }
2687 }
2688 else
2689 {
2690 int rc2 = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust, NULL);
2691 AssertMsg(RT_FAILURE_NP(rc2),
2692 ("rc=%Rrc, rc2=%Rrc %s", rc, rc2, pErrInfo ? pErrInfo->pszMsg : "<no-err-info>"));
2693 }
2694 }
2695
2696 /* Unwind recursion. */
2697 if (g_iTlsWinVerifyTrustRecursion != UINT32_MAX)
2698 TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)0);
2699 else
2700 ASMAtomicWriteU32(&g_idActiveThread, UINT32_MAX);
2701 }
2702 /*
2703 * No can do.
2704 */
2705 else
2706 SUP_DPRINTF(("Detected WinVerifyTrust recursion: rc=%Rrc '%ls'.\n", rc, pwszName));
2707 }
2708 else
2709 SUP_DPRINTF(("Detected loader lock ownership: rc=%Rrc '%ls'.\n", rc, pwszName));
2710 }
2711 return rc;
2712}
2713
2714
2715/**
2716 * Checks if WinVerifyTrust is callable on the current thread.
2717 *
2718 * Used by the main code to figure whether it makes sense to try revalidate an
2719 * image that hasn't passed thru WinVerifyTrust yet.
2720 *
2721 * @returns true if callable on current thread, false if not.
2722 */
2723DECLHIDDEN(bool) supHardenedWinIsWinVerifyTrustCallable(void)
2724{
2725 return g_pfnWinVerifyTrust != NULL
2726 && ( g_iTlsWinVerifyTrustRecursion != UINT32_MAX
2727 ? (uintptr_t)TlsGetValue(g_iTlsWinVerifyTrustRecursion) == 0
2728 : g_idActiveThread != RTNtCurrentThreadId() );
2729}
2730
2731
2732
2733/**
2734 * Initializes g_uNtVerCombined and g_NtVerInfo.
2735 * Called from suplibHardenedWindowsMain and suplibOsInit.
2736 */
2737DECLHIDDEN(void) supR3HardenedWinInitVersion(void)
2738{
2739 /*
2740 * Get the windows version. Use RtlGetVersion as GetVersionExW and
2741 * GetVersion might not be telling the whole truth (8.0 on 8.1 depending on
2742 * the application manifest).
2743 */
2744 OSVERSIONINFOEXW NtVerInfo;
2745
2746 RT_ZERO(NtVerInfo);
2747 NtVerInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
2748 if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&NtVerInfo)))
2749 {
2750 RT_ZERO(NtVerInfo);
2751 PPEB pPeb = NtCurrentPeb();
2752 NtVerInfo.dwMajorVersion = pPeb->OSMajorVersion;
2753 NtVerInfo.dwMinorVersion = pPeb->OSMinorVersion;
2754 NtVerInfo.dwBuildNumber = pPeb->OSPlatformId;
2755 }
2756
2757 g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(NtVerInfo.dwMajorVersion, NtVerInfo.dwMinorVersion, NtVerInfo.dwBuildNumber,
2758 NtVerInfo.wServicePackMajor, NtVerInfo.wServicePackMinor);
2759}
2760
2761#endif /* IN_RING3 */
2762
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