VirtualBox

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

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

SUP: Relax image architecture restrictions so 32-bit resource DLLs won't cause unnecessary trouble in 64-bit processes. (untested)

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

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