VirtualBox

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

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

SUPHardenedVerfiyImage-win.cpp: Tell RTLdr to disregard the specified arch if there is no code in the DLL, this is what LdrLoadDll does.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 116.7 KB
Line 
1/* $Id: SUPHardenedVerifyImage-win.cpp 53820 2015-01-15 13:52:02Z 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 uint32_t fLdrFlags = RTLDR_O_FOR_VALIDATION | RTLDR_O_IGNORE_ARCH_IF_NO_CODE;
1238 if (fFlags & SUPHNTVI_F_IGNORE_ARCHITECTURE)
1239 fLdrFlags |= RTLDR_O_IGNORE_ARCH_IF_NO_CODE;
1240 rc = RTLdrOpenWithReader(&pNtViRdr->Core, fLdrFlags, enmArch, &hLdrMod, pErrInfo);
1241 if (RT_SUCCESS(rc))
1242 {
1243 /*
1244 * Verify it.
1245 */
1246 rc = supHardenedWinVerifyImageByLdrMod(hLdrMod, pwszName, pNtViRdr, fAvoidWinVerifyTrust, pfWinVerifyTrust, pErrInfo);
1247 int rc2 = RTLdrClose(hLdrMod); AssertRC(rc2);
1248 }
1249 else
1250 supHardNtViRdrDestroy(&pNtViRdr->Core);
1251 }
1252 SUP_DPRINTF(("supHardenedWinVerifyImageByHandle: -> %d (%ls)%s\n",
1253 rc, pwszName, pfWinVerifyTrust && *pfWinVerifyTrust ? "WinVerifyTrust" : ""));
1254 return rc;
1255}
1256
1257
1258#ifdef IN_RING3
1259/**
1260 * supHardenedWinVerifyImageByHandle version without the name.
1261 *
1262 * The name is derived from the handle.
1263 *
1264 * @returns IPRT status code.
1265 * @param hFile File handle to the executable file.
1266 * @param fFlags Flags, SUPHNTVI_F_XXX.
1267 * @param pErrInfo Pointer to error info structure. Optional.
1268 */
1269DECLHIDDEN(int) supHardenedWinVerifyImageByHandleNoName(HANDLE hFile, uint32_t fFlags, PRTERRINFO pErrInfo)
1270{
1271 /*
1272 * Determine the NT name and call the verification function.
1273 */
1274 union
1275 {
1276 UNICODE_STRING UniStr;
1277 uint8_t abBuffer[(MAX_PATH + 8 + 1) * 2];
1278 } uBuf;
1279
1280 ULONG cbIgn;
1281 NTSTATUS rcNt = NtQueryObject(hFile,
1282 ObjectNameInformation,
1283 &uBuf,
1284 sizeof(uBuf) - sizeof(WCHAR),
1285 &cbIgn);
1286 if (NT_SUCCESS(rcNt))
1287 uBuf.UniStr.Buffer[uBuf.UniStr.Length / sizeof(WCHAR)] = '\0';
1288 else
1289 uBuf.UniStr.Buffer = (WCHAR *)L"TODO3";
1290
1291 return supHardenedWinVerifyImageByHandle(hFile, uBuf.UniStr.Buffer, fFlags, false /*fAvoidWinVerifyTrust*/,
1292 NULL /*pfWinVerifyTrust*/, pErrInfo);
1293}
1294#endif /* IN_RING3 */
1295
1296
1297/**
1298 * Retrieves the full official path to the system root or one of it's sub
1299 * directories.
1300 *
1301 * This code is also used by the support driver.
1302 *
1303 * @returns VBox status code.
1304 * @param pvBuf The output buffer. This will contain a
1305 * UNICODE_STRING followed (at the kernel's
1306 * discretion) the string buffer.
1307 * @param cbBuf The size of the buffer @a pvBuf points to.
1308 * @param enmDir Which directory under the system root we're
1309 * interested in.
1310 * @param pErrInfo Pointer to error info structure. Optional.
1311 */
1312DECLHIDDEN(int) supHardNtGetSystemRootDir(void *pvBuf, uint32_t cbBuf, SUPHARDNTSYSROOTDIR enmDir, PRTERRINFO pErrInfo)
1313{
1314 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
1315 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1316
1317 UNICODE_STRING NtName;
1318 switch (enmDir)
1319 {
1320 case kSupHardNtSysRootDir_System32:
1321 {
1322 static const WCHAR s_wszNameSystem32[] = L"\\SystemRoot\\System32\\";
1323 NtName.Buffer = (PWSTR)s_wszNameSystem32;
1324 NtName.Length = sizeof(s_wszNameSystem32) - sizeof(WCHAR);
1325 NtName.MaximumLength = sizeof(s_wszNameSystem32);
1326 break;
1327 }
1328 case kSupHardNtSysRootDir_WinSxS:
1329 {
1330 static const WCHAR s_wszNameWinSxS[] = L"\\SystemRoot\\WinSxS\\";
1331 NtName.Buffer = (PWSTR)s_wszNameWinSxS;
1332 NtName.Length = sizeof(s_wszNameWinSxS) - sizeof(WCHAR);
1333 NtName.MaximumLength = sizeof(s_wszNameWinSxS);
1334 break;
1335 }
1336 default:
1337 AssertFailed();
1338 return VERR_INVALID_PARAMETER;
1339 }
1340
1341 OBJECT_ATTRIBUTES ObjAttr;
1342 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1343
1344 NTSTATUS rcNt = NtCreateFile(&hFile,
1345 FILE_READ_DATA | SYNCHRONIZE,
1346 &ObjAttr,
1347 &Ios,
1348 NULL /* Allocation Size*/,
1349 FILE_ATTRIBUTE_NORMAL,
1350 FILE_SHARE_READ | FILE_SHARE_WRITE,
1351 FILE_OPEN,
1352 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1353 NULL /*EaBuffer*/,
1354 0 /*EaLength*/);
1355 if (NT_SUCCESS(rcNt))
1356 rcNt = Ios.Status;
1357 if (NT_SUCCESS(rcNt))
1358 {
1359 ULONG cbIgn;
1360 rcNt = NtQueryObject(hFile,
1361 ObjectNameInformation,
1362 pvBuf,
1363 cbBuf - sizeof(WCHAR),
1364 &cbIgn);
1365 NtClose(hFile);
1366 if (NT_SUCCESS(rcNt))
1367 {
1368 PUNICODE_STRING pUniStr = (PUNICODE_STRING)pvBuf;
1369 if (pUniStr->Length > 0)
1370 {
1371 /* Make sure it's terminated so it can safely be printed.*/
1372 pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
1373 return VINF_SUCCESS;
1374 }
1375
1376 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH,
1377 "NtQueryObject returned an empty path for '%ls'", NtName.Buffer);
1378 }
1379 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "NtQueryObject failed on '%ls' dir: %#x", NtName.Buffer, rcNt);
1380 }
1381 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "Failure to open '%ls': %#x", NtName.Buffer, rcNt);
1382}
1383
1384
1385/**
1386 * Initialize one certificate entry.
1387 *
1388 * @returns VBox status code.
1389 * @param pCert The X.509 certificate representation to init.
1390 * @param pabCert The raw DER encoded certificate.
1391 * @param cbCert The size of the raw certificate.
1392 * @param pErrInfo Where to return extended error info. Optional.
1393 * @param pszErrorTag Error tag.
1394 */
1395static int supHardNtViCertInit(PRTCRX509CERTIFICATE pCert, unsigned char const *pabCert, unsigned cbCert,
1396 PRTERRINFO pErrInfo, const char *pszErrorTag)
1397{
1398 AssertReturn(cbCert > 16 && cbCert < _128K,
1399 RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, "%s: cbCert=%#x out of range", pszErrorTag, cbCert));
1400 AssertReturn(!RTCrX509Certificate_IsPresent(pCert),
1401 RTErrInfoSetF(pErrInfo, VERR_WRONG_ORDER, "%s: Certificate already decoded?", pszErrorTag));
1402
1403 RTASN1CURSORPRIMARY PrimaryCursor;
1404 RTAsn1CursorInitPrimary(&PrimaryCursor, pabCert, cbCert, pErrInfo, &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, NULL);
1405 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, pCert, pszErrorTag);
1406 if (RT_SUCCESS(rc))
1407 rc = RTCrX509Certificate_CheckSanity(pCert, 0, pErrInfo, pszErrorTag);
1408 return rc;
1409}
1410
1411
1412static int supHardNtViCertStoreAddArray(RTCRSTORE hStore, PCSUPTAENTRY paCerts, unsigned cCerts, PRTERRINFO pErrInfo)
1413{
1414 for (uint32_t i = 0; i < cCerts; i++)
1415 {
1416 int rc = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_TAF_DER, paCerts[i].pch, paCerts[i].cb, pErrInfo);
1417 if (RT_FAILURE(rc))
1418 return rc;
1419 }
1420 return VINF_SUCCESS;
1421}
1422
1423
1424/**
1425 * Initialize a certificate table.
1426 *
1427 * @param phStore Where to return the store pointer.
1428 * @param paCerts1 Pointer to the first certificate table.
1429 * @param cCerts1 Entries in the first certificate table.
1430 * @param paCerts2 Pointer to the second certificate table.
1431 * @param cCerts2 Entries in the second certificate table.
1432 * @param paCerts3 Pointer to the third certificate table.
1433 * @param cCerts3 Entries in the third certificate table.
1434 * @param pErrInfo Where to return extended error info. Optional.
1435 * @param pszErrorTag Error tag.
1436 */
1437static int supHardNtViCertStoreInit(PRTCRSTORE phStore,
1438 PCSUPTAENTRY paCerts1, unsigned cCerts1,
1439 PCSUPTAENTRY paCerts2, unsigned cCerts2,
1440 PCSUPTAENTRY paCerts3, unsigned cCerts3,
1441 PRTERRINFO pErrInfo, const char *pszErrorTag)
1442{
1443 AssertReturn(*phStore == NIL_RTCRSTORE, VERR_WRONG_ORDER);
1444
1445 int rc = RTCrStoreCreateInMem(phStore, cCerts1 + cCerts2);
1446 if (RT_FAILURE(rc))
1447 return RTErrInfoSetF(pErrInfo, rc, "RTCrStoreCreateMemoryStore failed: %Rrc", rc);
1448
1449 rc = supHardNtViCertStoreAddArray(*phStore, paCerts1, cCerts1, pErrInfo);
1450 if (RT_SUCCESS(rc))
1451 rc = supHardNtViCertStoreAddArray(*phStore, paCerts2, cCerts2, pErrInfo);
1452 if (RT_SUCCESS(rc))
1453 rc = supHardNtViCertStoreAddArray(*phStore, paCerts3, cCerts3, pErrInfo);
1454 return rc;
1455}
1456
1457
1458#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
1459/**
1460 * Initializes the windows paths.
1461 */
1462static void supHardenedWinInitImageVerifierWinPaths(void)
1463{
1464 /*
1465 * Windows paths that we're interested in.
1466 */
1467 static const struct
1468 {
1469 SUPSYSROOTDIRBUF *pNtPath;
1470 WCHAR const *pwszRegValue;
1471 const char *pszLogName;
1472 } s_aPaths[] =
1473 {
1474 { &g_ProgramFilesNtPath, L"ProgramFilesDir", "ProgDir" },
1475 { &g_CommonFilesNtPath, L"CommonFilesDir", "ComDir" },
1476# ifdef RT_ARCH_AMD64
1477 { &g_ProgramFilesX86NtPath, L"ProgramFilesDir (x86)", "ProgDir32" },
1478 { &g_CommonFilesX86NtPath, L"CommonFilesDir (x86)", "ComDir32" },
1479# endif
1480 };
1481
1482 /*
1483 * Open the registry key containing the paths.
1484 */
1485 UNICODE_STRING NtName = RTNT_CONSTANT_UNISTR(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion");
1486 OBJECT_ATTRIBUTES ObjAttr;
1487 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1488 HANDLE hKey;
1489 NTSTATUS rcNt = NtOpenKey(&hKey, KEY_QUERY_VALUE, &ObjAttr);
1490 if (NT_SUCCESS(rcNt))
1491 {
1492 /*
1493 * Loop over the paths and resolve their NT paths.
1494 */
1495 for (uint32_t i = 0; i < RT_ELEMENTS(s_aPaths); i++)
1496 {
1497 /*
1498 * Query the value first.
1499 */
1500 UNICODE_STRING ValueName;
1501 ValueName.Buffer = (WCHAR *)s_aPaths[i].pwszRegValue;
1502 ValueName.Length = (USHORT)(RTUtf16Len(s_aPaths[i].pwszRegValue) * sizeof(WCHAR));
1503 ValueName.MaximumLength = ValueName.Length + sizeof(WCHAR);
1504
1505 union
1506 {
1507 KEY_VALUE_PARTIAL_INFORMATION PartialInfo;
1508 uint8_t abPadding[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(WCHAR) * 128];
1509 uint64_t uAlign;
1510 } uBuf;
1511
1512 ULONG cbActual = 0;
1513 rcNt = NtQueryValueKey(hKey, &ValueName, KeyValuePartialInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR), &cbActual);
1514 if (NT_SUCCESS(rcNt))
1515 {
1516 /*
1517 * Must be a simple string value, terminate it.
1518 */
1519 if ( uBuf.PartialInfo.Type == REG_EXPAND_SZ
1520 || uBuf.PartialInfo.Type == REG_SZ)
1521 {
1522 /*
1523 * Expand any environment variable references before opening it.
1524 * We use the result buffer as storage for the expaneded path,
1525 * reserving space for the windows name space prefix.
1526 */
1527 UNICODE_STRING Src;
1528 Src.Buffer = (WCHAR *)uBuf.PartialInfo.Data;
1529 Src.Length = uBuf.PartialInfo.DataLength;
1530 if (Src.Length >= sizeof(WCHAR) && Src.Buffer[Src.Length / sizeof(WCHAR) - 1] == '\0')
1531 Src.Length -= sizeof(WCHAR);
1532 Src.MaximumLength = Src.Length + sizeof(WCHAR);
1533 Src.Buffer[uBuf.PartialInfo.DataLength / sizeof(WCHAR)] = '\0';
1534
1535 s_aPaths[i].pNtPath->awcBuffer[0] = '\\';
1536 s_aPaths[i].pNtPath->awcBuffer[1] = '?';
1537 s_aPaths[i].pNtPath->awcBuffer[2] = '?';
1538 s_aPaths[i].pNtPath->awcBuffer[3] = '\\';
1539 UNICODE_STRING Dst;
1540 Dst.Buffer = &s_aPaths[i].pNtPath->awcBuffer[4];
1541 Dst.MaximumLength = sizeof(s_aPaths[i].pNtPath->awcBuffer) - sizeof(WCHAR) * 5;
1542 Dst.Length = Dst.MaximumLength;
1543
1544 if (uBuf.PartialInfo.Type == REG_EXPAND_SZ)
1545 rcNt = RtlExpandEnvironmentStrings_U(NULL, &Src, &Dst, NULL);
1546 else
1547 {
1548 memcpy(Dst.Buffer, Src.Buffer, Src.Length);
1549 Dst.Length = Src.Length;
1550 }
1551 if (NT_SUCCESS(rcNt))
1552 {
1553 Dst.Buffer[Dst.Length / sizeof(WCHAR)] = '\0';
1554
1555 /*
1556 * Include the \\??\\ prefix in the result and open the path.
1557 */
1558 Dst.Buffer -= 4;
1559 Dst.Length += 4 * sizeof(WCHAR);
1560 Dst.MaximumLength += 4 * sizeof(WCHAR);
1561 InitializeObjectAttributes(&ObjAttr, &Dst, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1562 HANDLE hFile = INVALID_HANDLE_VALUE;
1563 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1564 NTSTATUS rcNt = NtCreateFile(&hFile,
1565 FILE_READ_DATA | SYNCHRONIZE,
1566 &ObjAttr,
1567 &Ios,
1568 NULL /* Allocation Size*/,
1569 FILE_ATTRIBUTE_NORMAL,
1570 FILE_SHARE_READ | FILE_SHARE_WRITE,
1571 FILE_OPEN,
1572 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
1573 | FILE_SYNCHRONOUS_IO_NONALERT,
1574 NULL /*EaBuffer*/,
1575 0 /*EaLength*/);
1576 if (NT_SUCCESS(rcNt))
1577 rcNt = Ios.Status;
1578 if (NT_SUCCESS(rcNt))
1579 {
1580 /*
1581 * Query the real NT name.
1582 */
1583 ULONG cbIgn;
1584 rcNt = NtQueryObject(hFile,
1585 ObjectNameInformation,
1586 s_aPaths[i].pNtPath,
1587 sizeof(*s_aPaths[i].pNtPath) - sizeof(WCHAR),
1588 &cbIgn);
1589 if (NT_SUCCESS(rcNt))
1590 {
1591 if (s_aPaths[i].pNtPath->UniStr.Length > 0)
1592 {
1593 /* Make sure it's terminated.*/
1594 s_aPaths[i].pNtPath->UniStr.Buffer[s_aPaths[i].pNtPath->UniStr.Length / sizeof(WCHAR)] = '\0';
1595 SUP_DPRINTF(("%s:%*s %ls\n", s_aPaths[i].pszLogName, 9 - strlen(s_aPaths[i].pszLogName), "",
1596 s_aPaths[i].pNtPath->UniStr.Buffer));
1597 }
1598 else
1599 {
1600 SUP_DPRINTF(("%s: NtQueryObject returned empty string\n", s_aPaths[i].pszLogName));
1601 rcNt = STATUS_INVALID_PARAMETER;
1602 }
1603 }
1604 else
1605 SUP_DPRINTF(("%s: NtQueryObject failed: %#x\n", s_aPaths[i].pszLogName, rcNt));
1606 NtClose(hFile);
1607 }
1608 else
1609 SUP_DPRINTF(("%s: NtCreateFile failed: %#x (%ls)\n",
1610 s_aPaths[i].pszLogName, rcNt, Dst.Buffer));
1611 }
1612 else
1613 SUP_DPRINTF(("%s: RtlExpandEnvironmentStrings_U failed: %#x (%ls)\n",
1614 s_aPaths[i].pszLogName, rcNt, Src.Buffer));
1615 }
1616 else
1617 {
1618 SUP_DPRINTF(("%s: type mismatch: %#x\n", s_aPaths[i].pszLogName, uBuf.PartialInfo.Type));
1619 rcNt = STATUS_INVALID_PARAMETER;
1620 }
1621 }
1622 else
1623 SUP_DPRINTF(("%s: NtQueryValueKey failed: %#x\n", s_aPaths[i].pszLogName, rcNt));
1624
1625 /* Stub the entry on failure. */
1626 if (!NT_SUCCESS(rcNt))
1627 {
1628 s_aPaths[i].pNtPath->UniStr.Length = 0;
1629 s_aPaths[i].pNtPath->UniStr.Buffer = NULL;
1630 }
1631 }
1632 NtClose(hKey);
1633 }
1634 else
1635 {
1636 SUP_DPRINTF(("NtOpenKey(%ls) failed: %#x\n", NtName.Buffer, rcNt));
1637
1638 /* Stub all the entries on failure. */
1639 for (uint32_t i = 0; i < RT_ELEMENTS(s_aPaths); i++)
1640 {
1641 s_aPaths[i].pNtPath->UniStr.Length = 0;
1642 s_aPaths[i].pNtPath->UniStr.Buffer = NULL;
1643 }
1644 }
1645}
1646#endif /* IN_RING3 && !VBOX_PERMIT_EVEN_MORE */
1647
1648
1649/**
1650 * This initializes the certificates globals so we don't have to reparse them
1651 * every time we need to verify an image.
1652 *
1653 * @returns IPRT status code.
1654 * @param pErrInfo Where to return extended error info. Optional.
1655 */
1656DECLHIDDEN(int) supHardenedWinInitImageVerifier(PRTERRINFO pErrInfo)
1657{
1658 AssertReturn(!RTCrX509Certificate_IsPresent(&g_BuildX509Cert), VERR_WRONG_ORDER);
1659
1660 /*
1661 * Get the system root paths.
1662 */
1663 int rc = supHardNtGetSystemRootDir(&g_System32NtPath, sizeof(g_System32NtPath), kSupHardNtSysRootDir_System32, pErrInfo);
1664 if (RT_SUCCESS(rc))
1665 rc = supHardNtGetSystemRootDir(&g_WinSxSNtPath, sizeof(g_WinSxSNtPath), kSupHardNtSysRootDir_WinSxS, pErrInfo);
1666 if (RT_SUCCESS(rc))
1667 {
1668 SUP_DPRINTF(("System32: %ls\n", g_System32NtPath.UniStr.Buffer));
1669 SUP_DPRINTF(("WinSxS: %ls\n", g_WinSxSNtPath.UniStr.Buffer));
1670#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
1671 supHardenedWinInitImageVerifierWinPaths();
1672#endif
1673
1674 /*
1675 * Initialize it, leaving the cleanup to the termination call.
1676 */
1677 rc = supHardNtViCertInit(&g_BuildX509Cert, g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo, "BuildCertificate");
1678 if (RT_SUCCESS(rc))
1679 rc = supHardNtViCertStoreInit(&g_hSpcRootStore, g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
1680 NULL, 0, NULL, 0, pErrInfo, "SpcRoot");
1681 if (RT_SUCCESS(rc))
1682 rc = supHardNtViCertStoreInit(&g_hNtKernelRootStore, g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
1683 NULL, 0, NULL, 0, pErrInfo, "NtKernelRoot");
1684 if (RT_SUCCESS(rc))
1685 rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelRootStore,
1686 g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
1687 g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
1688 g_aSUPTimestampTAs, g_cSUPTimestampTAs,
1689 pErrInfo, "SpcAndNtKernelRoot");
1690 if (RT_SUCCESS(rc))
1691 rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelSuppStore,
1692 NULL, 0, NULL, 0, NULL, 0,
1693 pErrInfo, "SpcAndNtKernelSupplemental");
1694
1695#if 0 /* For the time being, always trust the build certificate. It bypasses the timestamp issues of CRT and SDL. */
1696 /* If the build certificate is a test singing certificate, it must be a
1697 trusted root or we'll fail to validate anything. */
1698 if ( RT_SUCCESS(rc)
1699 && RTCrX509Name_Compare(&g_BuildX509Cert.TbsCertificate.Subject, &g_BuildX509Cert.TbsCertificate.Issuer) == 0)
1700#else
1701 if (RT_SUCCESS(rc))
1702#endif
1703 rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
1704 g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo);
1705
1706 if (RT_SUCCESS(rc))
1707 {
1708 /*
1709 * Finally initialize known SIDs that we use.
1710 */
1711 SID_IDENTIFIER_AUTHORITY s_NtAuth = SECURITY_NT_AUTHORITY;
1712 NTSTATUS rcNt = RtlInitializeSid(&g_TrustedInstallerSid, &s_NtAuth, SECURITY_SERVICE_ID_RID_COUNT);
1713 if (NT_SUCCESS(rcNt))
1714 {
1715 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 0) = SECURITY_SERVICE_ID_BASE_RID;
1716 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 1) = 956008885;
1717 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 2) = 3418522649;
1718 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 3) = 1831038044;
1719 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 4) = 1853292631;
1720 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 5) = 2271478464;
1721
1722 rcNt = RtlInitializeSid(&g_LocalSystemSid, &s_NtAuth, 1);
1723 if (NT_SUCCESS(rcNt))
1724 {
1725 *RtlSubAuthoritySid(&g_LocalSystemSid, 0) = SECURITY_LOCAL_SYSTEM_RID;
1726
1727 rcNt = RtlInitializeSid(&g_AdminsGroupSid, &s_NtAuth, 2);
1728 if (NT_SUCCESS(rcNt))
1729 {
1730 *RtlSubAuthoritySid(&g_AdminsGroupSid, 0) = SECURITY_BUILTIN_DOMAIN_RID;
1731 *RtlSubAuthoritySid(&g_AdminsGroupSid, 1) = DOMAIN_ALIAS_RID_ADMINS;
1732 return VINF_SUCCESS;
1733 }
1734 }
1735 }
1736 rc = RTErrConvertFromNtStatus(rcNt);
1737 }
1738 supHardenedWinTermImageVerifier();
1739 }
1740 return rc;
1741}
1742
1743
1744/**
1745 * Releases resources allocated by supHardenedWinInitImageVerifier.
1746 */
1747DECLHIDDEN(void) supHardenedWinTermImageVerifier(void)
1748{
1749 if (RTCrX509Certificate_IsPresent(&g_BuildX509Cert))
1750 RTAsn1VtDelete(&g_BuildX509Cert.SeqCore.Asn1Core);
1751
1752 RTCrStoreRelease(g_hSpcAndNtKernelSuppStore);
1753 g_hSpcAndNtKernelSuppStore = NIL_RTCRSTORE;
1754 RTCrStoreRelease(g_hSpcAndNtKernelRootStore);
1755 g_hSpcAndNtKernelRootStore = NIL_RTCRSTORE;
1756
1757 RTCrStoreRelease(g_hNtKernelRootStore);
1758 g_hNtKernelRootStore = NIL_RTCRSTORE;
1759 RTCrStoreRelease(g_hSpcRootStore);
1760 g_hSpcRootStore = NIL_RTCRSTORE;
1761}
1762
1763#ifdef IN_RING3
1764
1765/**
1766 * This is a hardcoded list of certificates we thing we might need.
1767 *
1768 * @returns true if wanted, false if not.
1769 * @param pCert The certificate.
1770 */
1771static bool supR3HardenedWinIsDesiredRootCA(PCRTCRX509CERTIFICATE pCert)
1772{
1773 char szSubject[512];
1774 szSubject[sizeof(szSubject) - 1] = '\0';
1775 RTCrX509Name_FormatAsString(&pCert->TbsCertificate.Subject, szSubject, sizeof(szSubject) - 1, NULL);
1776
1777 /*
1778 * Check that it's a plausible root certificate.
1779 */
1780 if (!RTCrX509Certificate_IsSelfSigned(pCert))
1781 {
1782 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - not-self-signed: %s\n", szSubject));
1783 return false;
1784 }
1785
1786 if (RTAsn1Integer_UnsignedCompareWithU32(&pCert->TbsCertificate.T0.Version, 3) > 0)
1787 {
1788 if ( !(pCert->TbsCertificate.T3.fExtKeyUsage & RTCRX509CERT_KEY_USAGE_F_KEY_CERT_SIGN)
1789 && (pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE) )
1790 {
1791 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - non-cert-sign: %s\n", szSubject));
1792 return false;
1793 }
1794 if ( pCert->TbsCertificate.T3.pBasicConstraints
1795 && !pCert->TbsCertificate.T3.pBasicConstraints->CA.fValue)
1796 {
1797 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - non-CA: %s\n", szSubject));
1798 return false;
1799 }
1800 }
1801 if (pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits < 256) /* mostly for u64KeyId reading. */
1802 {
1803 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - key too small: %u bits %s\n",
1804 pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits, szSubject));
1805 return false;
1806 }
1807 uint64_t const u64KeyId = pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.uBits.pu64[1];
1808
1809# if 0
1810 /*
1811 * Whitelist - Array of names and key clues of the certificates we want.
1812 */
1813 static struct
1814 {
1815 uint64_t u64KeyId;
1816 const char *pszName;
1817 } const s_aWanted[] =
1818 {
1819 /* SPC */
1820 { UINT64_C(0xffffffffffffffff), "C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority" },
1821 { UINT64_C(0xffffffffffffffff), "L=Internet, O=VeriSign, Inc., OU=VeriSign Commercial Software Publishers CA" },
1822 { UINT64_C(0x491857ead79dde00), "C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority" },
1823
1824 /* TS */
1825 { UINT64_C(0xffffffffffffffff), "O=Microsoft Trust Network, OU=Microsoft Corporation, OU=Microsoft Time Stamping Service Root, OU=Copyright (c) 1997 Microsoft Corp." },
1826 { UINT64_C(0xffffffffffffffff), "O=VeriSign Trust Network, OU=VeriSign, Inc., OU=VeriSign Time Stamping Service Root, OU=NO LIABILITY ACCEPTED, (c)97 VeriSign, Inc." },
1827 { UINT64_C(0xffffffffffffffff), "C=ZA, ST=Western Cape, L=Durbanville, O=Thawte, OU=Thawte Certification, CN=Thawte Timestamping CA" },
1828
1829 /* Additional Windows 8.1 list: */
1830 { UINT64_C(0x5ad46780fa5df300), "DC=com, DC=microsoft, CN=Microsoft Root Certificate Authority" },
1831 { UINT64_C(0x3be670c1bd02a900), "OU=Copyright (c) 1997 Microsoft Corp., OU=Microsoft Corporation, CN=Microsoft Root Authority" },
1832 { UINT64_C(0x4d3835aa4180b200), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2011" },
1833 { UINT64_C(0x646e3fe3ba08df00), "C=US, O=MSFT, CN=Microsoft Authenticode(tm) Root Authority" },
1834 { UINT64_C(0xece4e4289e08b900), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010" },
1835 { UINT64_C(0x59faf1086271bf00), "C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2" },
1836 { UINT64_C(0x3d98ab22bb04a300), "C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root" },
1837 { UINT64_C(0x91e3728b8b40d000), "C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority" },
1838 { UINT64_C(0x61a3a33f81aace00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Object" },
1839 { UINT64_C(0x9e5bc2d78b6a3636), "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA, Email=premium-server@thawte.com" },
1840 { UINT64_C(0xf4fd306318ccda00), "C=US, O=GeoTrust Inc., CN=GeoTrust Global CA" },
1841 { UINT64_C(0xa0ee62086758b15d), "C=US, O=Equifax, OU=Equifax Secure Certificate Authority" },
1842 { UINT64_C(0x8ff6fc03c1edbd00), "C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2" },
1843 { UINT64_C(0xa3ce8d99e60eda00), "C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA" },
1844 { UINT64_C(0xa671e9fec832b700), "C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority" },
1845 { UINT64_C(0xa8de7211e13be200), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA" },
1846 { 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" },
1847 { UINT64_C(0x7ae89c50f0b6a00f), "C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root" },
1848 { 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" },
1849 { UINT64_C(0x9e5bc2d78b6a3636), "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA, Email=premium-server@thawte.com" },
1850 { UINT64_C(0x7c4fd32ec1b1ce00), "C=PL, O=Unizeto Sp. z o.o., CN=Certum CA" },
1851 { UINT64_C(0xd4fbe673e5ccc600), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA" },
1852 { 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" },
1853 { UINT64_C(0x6e2ba21058eedf00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN - DATACorp SGC" },
1854 { 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)" },
1855 { 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" },
1856 { UINT64_C(0x466cbc09db88c100), "C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority" },
1857 { UINT64_C(0x9259c8abe5ca713a), "L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com/, Email=info@valicert.com" },
1858 { 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" },
1859 { UINT64_C(0x8043e4ce150ead00), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Assured ID Root CA" },
1860 { UINT64_C(0x00f2e6331af7b700), "C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root" },
1861 };
1862
1863
1864 uint32_t i = RT_ELEMENTS(s_aWanted);
1865 while (i-- > 0)
1866 if ( s_aWanted[i].u64KeyId == u64KeyId
1867 || s_aWanted[i].u64KeyId == UINT64_MAX)
1868 if (RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, s_aWanted[i].pszName))
1869 {
1870 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: Adding %#llx %s\n", u64KeyId, szSubject));
1871 return true;
1872 }
1873
1874 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping %#llx %s\n", u64KeyId, szSubject));
1875 return false;
1876# else
1877 /*
1878 * Blacklist approach.
1879 */
1880 static struct
1881 {
1882 uint64_t u64KeyId;
1883 const char *pszName;
1884 } const s_aUnwanted[] =
1885 {
1886 { UINT64_C(0xffffffffffffffff), "C=US, O=U.S. Robots and Mechanical Men, Inc., OU=V.I.K.I." }, /* dummy entry */
1887 };
1888
1889 uint32_t i = RT_ELEMENTS(s_aUnwanted);
1890 while (i-- > 0)
1891 if ( s_aUnwanted[i].u64KeyId == u64KeyId
1892 || s_aUnwanted[i].u64KeyId == UINT64_MAX)
1893 if (RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, s_aUnwanted[i].pszName))
1894 {
1895 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - blacklisted: %#llx %s\n", u64KeyId, szSubject));
1896 return false;
1897 }
1898
1899 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: Adding %#llx %s\n", u64KeyId, szSubject));
1900 return true;
1901# endif
1902}
1903
1904
1905/**
1906 * Loads a module in the system32 directory.
1907 *
1908 * @returns Module handle on success. Won't return on faliure.
1909 * @param pszName The name of the DLL to load.
1910 */
1911DECLHIDDEN(HMODULE) supR3HardenedWinLoadSystem32Dll(const char *pszName)
1912{
1913 WCHAR wszName[200+60];
1914 UINT cwcDir = GetSystemDirectoryW(wszName, RT_ELEMENTS(wszName) - 60);
1915 wszName[cwcDir] = '\\';
1916 RTUtf16CopyAscii(&wszName[cwcDir + 1], RT_ELEMENTS(wszName) - cwcDir, pszName);
1917
1918 DWORD fFlags = 0;
1919 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
1920 fFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;
1921 HMODULE hMod = LoadLibraryExW(wszName, NULL, fFlags);
1922 if ( hMod == NULL
1923 && fFlags
1924 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
1925 && RtlGetLastWin32Error() == ERROR_INVALID_PARAMETER)
1926 {
1927 fFlags = 0;
1928 hMod = LoadLibraryExW(wszName, NULL, fFlags);
1929 }
1930 if (hMod == NULL)
1931 supR3HardenedFatal("Error loading '%s': %u [%ls]", pszName, RtlGetLastWin32Error(), wszName);
1932 return hMod;
1933}
1934
1935
1936/**
1937 * Called by supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation to
1938 * import selected root CAs from the system certificate store.
1939 *
1940 * These certificates permits us to correctly validate third party DLLs.
1941 */
1942static void supR3HardenedWinRetrieveTrustedRootCAs(void)
1943{
1944 uint32_t cAdded = 0;
1945
1946 /*
1947 * Load crypt32.dll and resolve the APIs we need.
1948 */
1949 HMODULE hCrypt32 = supR3HardenedWinLoadSystem32Dll("crypt32.dll");
1950
1951#define RESOLVE_CRYPT32_API(a_Name, a_pfnType) \
1952 a_pfnType pfn##a_Name = (a_pfnType)GetProcAddress(hCrypt32, #a_Name); \
1953 if (pfn##a_Name == NULL) supR3HardenedFatal("Error locating '" #a_Name "' in 'crypt32.dll': %u", RtlGetLastWin32Error())
1954 RESOLVE_CRYPT32_API(CertOpenStore, PFNCERTOPENSTORE);
1955 RESOLVE_CRYPT32_API(CertCloseStore, PFNCERTCLOSESTORE);
1956 RESOLVE_CRYPT32_API(CertEnumCertificatesInStore, PFNCERTENUMCERTIFICATESINSTORE);
1957#undef RESOLVE_CRYPT32_API
1958
1959 /*
1960 * Open the root store and look for the certificates we wish to use.
1961 */
1962 DWORD fOpenStore = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
1963 HCERTSTORE hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1964 NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_LOCAL_MACHINE | fOpenStore, L"Root");
1965 if (!hStore)
1966 hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1967 NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_CURRENT_USER | fOpenStore, L"Root");
1968 if (hStore)
1969 {
1970 PCCERT_CONTEXT pCurCtx = NULL;
1971 while ((pCurCtx = pfnCertEnumCertificatesInStore(hStore, pCurCtx)) != NULL)
1972 {
1973 if (pCurCtx->dwCertEncodingType & X509_ASN_ENCODING)
1974 {
1975 RTERRINFOSTATIC StaticErrInfo;
1976 RTASN1CURSORPRIMARY PrimaryCursor;
1977 RTAsn1CursorInitPrimary(&PrimaryCursor, pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded,
1978 RTErrInfoInitStatic(&StaticErrInfo),
1979 &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
1980 RTCRX509CERTIFICATE MyCert;
1981 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &MyCert, "Cert");
1982 if (RT_SUCCESS(rc))
1983 {
1984 if (supR3HardenedWinIsDesiredRootCA(&MyCert))
1985 {
1986 rc = RTCrStoreCertAddEncoded(g_hSpcRootStore, RTCRCERTCTX_F_ENC_X509_DER,
1987 pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
1988 AssertRC(rc);
1989
1990 rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
1991 pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
1992 AssertRC(rc);
1993 cAdded++;
1994 }
1995
1996 RTCrX509Certificate_Delete(&MyCert);
1997 }
1998 /* XP root certificate "C&W HKT SecureNet CA SGC Root" has non-standard validity
1999 timestamps, the UTC formatting isn't Zulu time but specifies timezone offsets.
2000 Ignore these failures and certificates. */
2001 else if (rc != VERR_ASN1_INVALID_UTC_TIME_ENCODING)
2002 AssertMsgFailed(("RTCrX509Certificate_DecodeAsn1 failed: rc=%#x: %s\n", rc, StaticErrInfo.szMsg));
2003 }
2004 }
2005 pfnCertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
2006 g_fHaveOtherRoots = true;
2007 }
2008 SUP_DPRINTF(("supR3HardenedWinRetrieveTrustedRootCAs: cAdded=%u\n", cAdded));
2009}
2010
2011
2012/**
2013 * Resolves the WinVerifyTrust API after the process has been verified and
2014 * installs a thread creation hook.
2015 *
2016 * The WinVerifyTrust API is used in addition our own Authenticode verification
2017 * code. If the image has the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag
2018 * set, it will be checked again by the kernel. All our image has this flag set
2019 * and we require all VBox extensions to have it set as well. In effect, the
2020 * authenticode signature will be checked two or three times.
2021 *
2022 * @param pszProgName The program name.
2023 */
2024DECLHIDDEN(void) supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(const char *pszProgName)
2025{
2026# ifdef IN_SUP_HARDENED_R3
2027 /*
2028 * Load our the support library DLL that does the thread hooking as the
2029 * security API may trigger the creation of COM worker threads (or
2030 * whatever they are).
2031 *
2032 * The thread creation hook makes the threads very slippery to debuggers by
2033 * irreversably disabling most (if not all) debug events for them.
2034 */
2035 char szPath[RTPATH_MAX];
2036 supR3HardenedPathSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxSupLib.DLL"));
2037 suplibHardenedStrCat(szPath, "/VBoxSupLib.DLL");
2038 HMODULE hSupLibMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, true /*fSystem32Only*/);
2039 if (hSupLibMod == NULL)
2040 supR3HardenedFatal("Error loading '%s': %u", szPath, RtlGetLastWin32Error());
2041# endif
2042
2043 /*
2044 * Allocate TLS entry for WinVerifyTrust recursion prevention.
2045 */
2046 DWORD iTls = TlsAlloc();
2047 if (iTls != TLS_OUT_OF_INDEXES)
2048 g_iTlsWinVerifyTrustRecursion = iTls;
2049 else
2050 supR3HardenedError(RtlGetLastWin32Error(), false /*fFatal*/, "TlsAlloc failed");
2051
2052 /*
2053 * Resolve it.
2054 */
2055 HMODULE hWintrust = supR3HardenedWinLoadSystem32Dll("Wintrust.dll");
2056#define RESOLVE_CRYPT_API(a_Name, a_pfnType, a_uMinWinVer) \
2057 do { \
2058 g_pfn##a_Name = (a_pfnType)GetProcAddress(hWintrust, #a_Name); \
2059 if (g_pfn##a_Name == NULL && (a_uMinWinVer) < g_uNtVerCombined) \
2060 supR3HardenedFatal("Error locating '" #a_Name "' in 'Wintrust.dll': %u", RtlGetLastWin32Error()); \
2061 } while (0)
2062
2063 PFNWINVERIFYTRUST pfnWinVerifyTrust = (PFNWINVERIFYTRUST)GetProcAddress(hWintrust, "WinVerifyTrust");
2064 if (!pfnWinVerifyTrust)
2065 supR3HardenedFatal("Error locating 'WinVerifyTrust' in 'Wintrust.dll': %u", RtlGetLastWin32Error());
2066
2067 RESOLVE_CRYPT_API(CryptCATAdminAcquireContext, PFNCRYPTCATADMINACQUIRECONTEXT, 0);
2068 RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE, 0);
2069 RESOLVE_CRYPT_API(CryptCATAdminEnumCatalogFromHash, PFNCRYPTCATADMINENUMCATALOGFROMHASH, 0);
2070 RESOLVE_CRYPT_API(CryptCATAdminReleaseCatalogContext, PFNCRYPTCATADMINRELEASECATALOGCONTEXT, 0);
2071 RESOLVE_CRYPT_API(CryptCATAdminReleaseContext, PFNCRYPTCATDADMINRELEASECONTEXT, 0);
2072 RESOLVE_CRYPT_API(CryptCATCatalogInfoFromContext, PFNCRYPTCATCATALOGINFOFROMCONTEXT, 0);
2073
2074 RESOLVE_CRYPT_API(CryptCATAdminAcquireContext2, PFNCRYPTCATADMINACQUIRECONTEXT2, SUP_NT_VER_W80);
2075 RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle2, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2, SUP_NT_VER_W80);
2076
2077 /*
2078 * Call it on ourselves and ntdll to make sure it loads all the providers
2079 * now, we would otherwise geting into recursive trouble in the
2080 * NtCreateSection hook.
2081 */
2082# ifdef IN_SUP_HARDENED_R3
2083 RTERRINFOSTATIC ErrInfoStatic;
2084 RTErrInfoInitStatic(&ErrInfoStatic);
2085 int rc = supR3HardNtViCallWinVerifyTrust(NULL, g_SupLibHardenedExeNtPath.UniStr.Buffer, 0,
2086 &ErrInfoStatic.Core, pfnWinVerifyTrust, NULL);
2087 if (RT_FAILURE(rc))
2088 supR3HardenedFatalMsg(pszProgName, kSupInitOp_Integrity, rc,
2089 "WinVerifyTrust failed on stub executable: %s", ErrInfoStatic.szMsg);
2090# endif
2091
2092 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. */
2093 supR3HardNtViCallWinVerifyTrust(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust, NULL);
2094 supR3HardNtViCallWinVerifyTrustCatFile(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
2095
2096 g_pfnWinVerifyTrust = pfnWinVerifyTrust;
2097 SUP_DPRINTF(("g_pfnWinVerifyTrust=%p\n", pfnWinVerifyTrust));
2098
2099# ifdef IN_SUP_HARDENED_R3
2100 /*
2101 * Load some problematic DLLs into the verifier cache to prevent
2102 * recursion trouble.
2103 */
2104 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\crypt32.dll");
2105 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\Wintrust.dll");
2106# endif
2107
2108 /*
2109 * Now, get trusted root CAs so we can verify a broader scope of signatures.
2110 */
2111 supR3HardenedWinRetrieveTrustedRootCAs();
2112}
2113
2114
2115static int supR3HardNtViNtToWinPath(PCRTUTF16 pwszNtName, PCRTUTF16 *ppwszWinPath,
2116 PRTUTF16 pwszWinPathBuf, size_t cwcWinPathBuf)
2117{
2118 static const RTUTF16 s_wszPrefix[] = L"\\\\.\\GLOBALROOT";
2119
2120 if (*pwszNtName != '\\' && *pwszNtName != '/')
2121 return VERR_PATH_DOES_NOT_START_WITH_ROOT;
2122
2123 size_t cwcNtName = RTUtf16Len(pwszNtName);
2124 if (RT_ELEMENTS(s_wszPrefix) + cwcNtName > cwcWinPathBuf)
2125 return VERR_FILENAME_TOO_LONG;
2126
2127 memcpy(pwszWinPathBuf, s_wszPrefix, sizeof(s_wszPrefix));
2128 memcpy(&pwszWinPathBuf[sizeof(s_wszPrefix) / sizeof(RTUTF16) - 1], pwszNtName, (cwcNtName + 1) * sizeof(RTUTF16));
2129 *ppwszWinPath = pwszWinPathBuf;
2130 return VINF_SUCCESS;
2131}
2132
2133
2134/**
2135 * Calls WinVerifyTrust to verify an PE image.
2136 *
2137 * @returns VBox status code.
2138 * @param hFile File handle to the executable file.
2139 * @param pwszName Full NT path to the DLL in question, used for
2140 * dealing with unsigned system dlls as well as for
2141 * error/logging.
2142 * @param fFlags Flags, SUPHNTVI_F_XXX.
2143 * @param pErrInfo Pointer to error info structure. Optional.
2144 * @param pfnWinVerifyTrust Pointer to the API.
2145 * @param phrcWinVerifyTrust Where to WinVerifyTrust error status on failure,
2146 * optional.
2147 */
2148static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
2149 PFNWINVERIFYTRUST pfnWinVerifyTrust, HRESULT *phrcWinVerifyTrust)
2150{
2151 if (phrcWinVerifyTrust)
2152 *phrcWinVerifyTrust = S_OK;
2153
2154 /*
2155 * Convert the name into a Windows name.
2156 */
2157 RTUTF16 wszWinPathBuf[MAX_PATH];
2158 PCRTUTF16 pwszWinPath;
2159 int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
2160 if (RT_FAILURE(rc))
2161 return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrust: rc=%Rrc '%ls'", rc, pwszName);
2162
2163 /*
2164 * Construct input parameters and call the API.
2165 */
2166 WINTRUST_FILE_INFO FileInfo;
2167 RT_ZERO(FileInfo);
2168 FileInfo.cbStruct = sizeof(FileInfo);
2169 FileInfo.pcwszFilePath = pwszWinPath;
2170 FileInfo.hFile = hFile;
2171
2172 GUID PolicyActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
2173
2174 WINTRUST_DATA TrustData;
2175 RT_ZERO(TrustData);
2176 TrustData.cbStruct = sizeof(TrustData);
2177 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
2178 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
2179 TrustData.dwUIChoice = WTD_UI_NONE;
2180 TrustData.dwProvFlags = 0;
2181 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2182 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
2183 else
2184 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
2185 TrustData.dwUnionChoice = WTD_CHOICE_FILE;
2186 TrustData.pFile = &FileInfo;
2187
2188 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
2189 if (hrc == S_OK)
2190 rc = VINF_SUCCESS;
2191 else
2192 {
2193 /*
2194 * Failed. Format a nice error message.
2195 */
2196# ifdef DEBUG_bird
2197 if (hrc != CERT_E_CHAINING /* Un-updated vistas, XPs, ++ */)
2198 __debugbreak();
2199# endif
2200 const char *pszErrConst = NULL;
2201 switch (hrc)
2202 {
2203 case TRUST_E_SYSTEM_ERROR: pszErrConst = "TRUST_E_SYSTEM_ERROR"; break;
2204 case TRUST_E_NO_SIGNER_CERT: pszErrConst = "TRUST_E_NO_SIGNER_CERT"; break;
2205 case TRUST_E_COUNTER_SIGNER: pszErrConst = "TRUST_E_COUNTER_SIGNER"; break;
2206 case TRUST_E_CERT_SIGNATURE: pszErrConst = "TRUST_E_CERT_SIGNATURE"; break;
2207 case TRUST_E_TIME_STAMP: pszErrConst = "TRUST_E_TIME_STAMP"; break;
2208 case TRUST_E_BAD_DIGEST: pszErrConst = "TRUST_E_BAD_DIGEST"; break;
2209 case TRUST_E_BASIC_CONSTRAINTS: pszErrConst = "TRUST_E_BASIC_CONSTRAINTS"; break;
2210 case TRUST_E_FINANCIAL_CRITERIA: pszErrConst = "TRUST_E_FINANCIAL_CRITERIA"; break;
2211 case TRUST_E_PROVIDER_UNKNOWN: pszErrConst = "TRUST_E_PROVIDER_UNKNOWN"; break;
2212 case TRUST_E_ACTION_UNKNOWN: pszErrConst = "TRUST_E_ACTION_UNKNOWN"; break;
2213 case TRUST_E_SUBJECT_FORM_UNKNOWN: pszErrConst = "TRUST_E_SUBJECT_FORM_UNKNOWN"; break;
2214 case TRUST_E_SUBJECT_NOT_TRUSTED: pszErrConst = "TRUST_E_SUBJECT_NOT_TRUSTED"; break;
2215 case TRUST_E_NOSIGNATURE: pszErrConst = "TRUST_E_NOSIGNATURE"; break;
2216 case TRUST_E_FAIL: pszErrConst = "TRUST_E_FAIL"; break;
2217 case TRUST_E_EXPLICIT_DISTRUST: pszErrConst = "TRUST_E_EXPLICIT_DISTRUST"; break;
2218 case CERT_E_CHAINING: pszErrConst = "CERT_E_CHAINING"; break;
2219 case CERT_E_REVOCATION_FAILURE: pszErrConst = "CERT_E_REVOCATION_FAILURE"; break;
2220 case CRYPT_E_FILE_ERROR: pszErrConst = "CRYPT_E_FILE_ERROR"; break;
2221 case CRYPT_E_REVOKED: pszErrConst = "CRYPT_E_REVOKED"; break;
2222 }
2223 if (pszErrConst)
2224 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
2225 "WinVerifyTrust failed with hrc=%s on '%ls'", pszErrConst, pwszName);
2226 else
2227 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
2228 "WinVerifyTrust failed with hrc=%Rhrc on '%ls'", hrc, pwszName);
2229 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrust: WinVerifyTrust failed with %#x (%s) on '%ls'\n",
2230 hrc, pszErrConst, pwszName));
2231 if (phrcWinVerifyTrust)
2232 *phrcWinVerifyTrust = hrc;
2233 }
2234
2235 /* clean up state data. */
2236 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
2237 FileInfo.hFile = NULL;
2238 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
2239
2240 return rc;
2241}
2242
2243
2244/**
2245 * Calls WinVerifyTrust to verify an PE image via catalog files.
2246 *
2247 * @returns VBox status code.
2248 * @param hFile File handle to the executable file.
2249 * @param pwszName Full NT path to the DLL in question, used for
2250 * dealing with unsigned system dlls as well as for
2251 * error/logging.
2252 * @param fFlags Flags, SUPHNTVI_F_XXX.
2253 * @param pErrInfo Pointer to error info structure. Optional.
2254 * @param pfnWinVerifyTrust Pointer to the API.
2255 */
2256static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
2257 PFNWINVERIFYTRUST pfnWinVerifyTrust)
2258{
2259 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hFile=%p pwszName=%ls\n", hFile, pwszName));
2260
2261 /*
2262 * Convert the name into a Windows name.
2263 */
2264 RTUTF16 wszWinPathBuf[MAX_PATH];
2265 PCRTUTF16 pwszWinPath;
2266 int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
2267 if (RT_FAILURE(rc))
2268 return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrustCatFile: rc=%Rrc '%ls'", rc, pwszName);
2269
2270 /*
2271 * Open the file if we didn't get a handle.
2272 */
2273 HANDLE hFileClose = NULL;
2274 if (hFile == RTNT_INVALID_HANDLE_VALUE || hFile == NULL)
2275 {
2276 hFile = RTNT_INVALID_HANDLE_VALUE;
2277 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2278
2279 UNICODE_STRING NtName;
2280 NtName.Buffer = (PWSTR)pwszName;
2281 NtName.Length = (USHORT)(RTUtf16Len(pwszName) * sizeof(WCHAR));
2282 NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
2283
2284 OBJECT_ATTRIBUTES ObjAttr;
2285 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
2286
2287 NTSTATUS rcNt = NtCreateFile(&hFile,
2288 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
2289 &ObjAttr,
2290 &Ios,
2291 NULL /* Allocation Size*/,
2292 FILE_ATTRIBUTE_NORMAL,
2293 FILE_SHARE_READ,
2294 FILE_OPEN,
2295 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
2296 NULL /*EaBuffer*/,
2297 0 /*EaLength*/);
2298 if (NT_SUCCESS(rcNt))
2299 rcNt = Ios.Status;
2300 if (!NT_SUCCESS(rcNt))
2301 return RTErrInfoSetF(pErrInfo, RTErrConvertFromNtStatus(rcNt),
2302 "NtCreateFile returned %#x opening '%ls'.", rcNt, pwszName);
2303 hFileClose = hFile;
2304 }
2305
2306 /*
2307 * On Windows 8.0 and later there are more than one digest choice.
2308 */
2309 int fNoSignedCatalogFound = -1;
2310 rc = VERR_LDRVI_NOT_SIGNED;
2311 static struct
2312 {
2313 /** The digest algorithm name. */
2314 const WCHAR *pszAlgorithm;
2315 /** Cached catalog admin handle. */
2316 HCATADMIN volatile hCachedCatAdmin;
2317 } s_aHashes[] =
2318 {
2319 { NULL, NULL },
2320 { L"SHA256", NULL },
2321 };
2322 for (uint32_t i = 0; i < RT_ELEMENTS(s_aHashes); i++)
2323 {
2324 /*
2325 * Another loop for dealing with different trust provider policies
2326 * required for successfully validating different catalog signatures.
2327 */
2328 bool fTryNextPolicy;
2329 uint32_t iPolicy = 0;
2330 static const GUID s_aPolicies[] =
2331 {
2332 DRIVER_ACTION_VERIFY, /* Works with microsoft bits. Most frequently used, thus first. */
2333 WINTRUST_ACTION_GENERIC_VERIFY_V2, /* Works with ATI and other SPC kernel-code signed stuff. */
2334 };
2335 do
2336 {
2337 /*
2338 * Create a context.
2339 */
2340 fTryNextPolicy = false;
2341 bool fFreshContext = false;
2342 BOOL fRc;
2343 HCATADMIN hCatAdmin = ASMAtomicXchgPtr(&s_aHashes[i].hCachedCatAdmin, NULL);
2344 if (hCatAdmin)
2345 {
2346 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Cached context %p\n", hCatAdmin));
2347 fFreshContext = false;
2348 fRc = TRUE;
2349 }
2350 else
2351 {
2352l_fresh_context:
2353 fFreshContext = true;
2354 if (g_pfnCryptCATAdminAcquireContext2)
2355 fRc = g_pfnCryptCATAdminAcquireContext2(&hCatAdmin, &s_aPolicies[iPolicy], s_aHashes[i].pszAlgorithm,
2356 NULL /*pStrongHashPolicy*/, 0 /*dwFlags*/);
2357 else
2358 fRc = g_pfnCryptCATAdminAcquireContext(&hCatAdmin, &s_aPolicies[iPolicy], 0 /*dwFlags*/);
2359 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: New context %p\n", hCatAdmin));
2360 }
2361 if (fRc)
2362 {
2363 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hCatAdmin=%p\n", hCatAdmin));
2364
2365 /*
2366 * Hash the file.
2367 */
2368 BYTE abHash[SUPHARDNTVI_MAX_CAT_HASH_SIZE];
2369 DWORD cbHash = sizeof(abHash);
2370 if (g_pfnCryptCATAdminCalcHashFromFileHandle2)
2371 fRc = g_pfnCryptCATAdminCalcHashFromFileHandle2(hCatAdmin, hFile, &cbHash, abHash, 0 /*dwFlags*/);
2372 else
2373 fRc = g_pfnCryptCATAdminCalcHashFromFileHandle(hFile, &cbHash, abHash, 0 /*dwFlags*/);
2374 if (fRc)
2375 {
2376 /* Produce a string version of it that we can pass to WinVerifyTrust. */
2377 RTUTF16 wszDigest[SUPHARDNTVI_MAX_CAT_HASH_SIZE * 2 + 1];
2378 int rc2 = RTUtf16PrintHexBytes(wszDigest, RT_ELEMENTS(wszDigest), abHash, cbHash, RTSTRPRINTHEXBYTES_F_UPPER);
2379 if (RT_SUCCESS(rc2))
2380 {
2381 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: cbHash=%u wszDigest=%ls\n", cbHash, wszDigest));
2382
2383 /*
2384 * Enumerate catalog information that matches the hash.
2385 */
2386 uint32_t iCat = 0;
2387 HCATINFO hCatInfoPrev = NULL;
2388 do
2389 {
2390 /* Get the next match. */
2391 HCATINFO hCatInfo = g_pfnCryptCATAdminEnumCatalogFromHash(hCatAdmin, abHash, cbHash, 0, &hCatInfoPrev);
2392 if (!hCatInfo)
2393 {
2394 if (!fFreshContext)
2395 {
2396 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Retrying with fresh context (CryptCATAdminEnumCatalogFromHash -> %u; iCat=%#x)\n", RtlGetLastWin32Error(), iCat));
2397 if (hCatInfoPrev != NULL)
2398 g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/);
2399 g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/);
2400 goto l_fresh_context;
2401 }
2402 ULONG ulErr = RtlGetLastWin32Error();
2403 fNoSignedCatalogFound = ulErr == ERROR_NOT_FOUND && fNoSignedCatalogFound != 0;
2404 if (iCat == 0)
2405 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed ERRROR_NOT_FOUND (%u)\n", ulErr));
2406 else if (iCat == 0)
2407 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed %u\n", ulErr));
2408 break;
2409 }
2410 fNoSignedCatalogFound = 0;
2411 Assert(hCatInfoPrev == NULL);
2412 hCatInfoPrev = hCatInfo;
2413
2414 /*
2415 * Call WinVerifyTrust.
2416 */
2417 CATALOG_INFO CatInfo;
2418 CatInfo.cbStruct = sizeof(CatInfo);
2419 CatInfo.wszCatalogFile[0] = '\0';
2420 if (g_pfnCryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0 /*dwFlags*/))
2421 {
2422 WINTRUST_CATALOG_INFO WtCatInfo;
2423 RT_ZERO(WtCatInfo);
2424 WtCatInfo.cbStruct = sizeof(WtCatInfo);
2425 WtCatInfo.dwCatalogVersion = 0;
2426 WtCatInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
2427 WtCatInfo.pcwszMemberTag = wszDigest;
2428 WtCatInfo.pcwszMemberFilePath = pwszWinPath;
2429 WtCatInfo.pbCalculatedFileHash = abHash;
2430 WtCatInfo.cbCalculatedFileHash = cbHash;
2431 WtCatInfo.pcCatalogContext = NULL;
2432
2433 WINTRUST_DATA TrustData;
2434 RT_ZERO(TrustData);
2435 TrustData.cbStruct = sizeof(TrustData);
2436 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
2437 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
2438 TrustData.dwUIChoice = WTD_UI_NONE;
2439 TrustData.dwProvFlags = 0;
2440 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2441 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
2442 else
2443 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
2444 TrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
2445 TrustData.pCatalog = &WtCatInfo;
2446
2447 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
2448 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: WinVerifyTrust => %#x; cat='%ls'; file='%ls'\n",
2449 hrc, CatInfo.wszCatalogFile, pwszName));
2450
2451 if (SUCCEEDED(hrc))
2452 rc = VINF_SUCCESS;
2453 else if (hrc == TRUST_E_NOSIGNATURE)
2454 { /* ignore because it's useless. */ }
2455 else if (hrc == ERROR_INVALID_PARAMETER)
2456 { /* This is returned if the given file isn't found in the catalog, it seems. */ }
2457 else
2458 {
2459 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_WINTRUST_CAT_FAILURE,
2460 "WinVerifyTrust failed with hrc=%#x on '%ls' and .cat-file='%ls'.",
2461 hrc, pwszWinPath, CatInfo.wszCatalogFile);
2462 fTryNextPolicy |= (hrc == CERT_E_UNTRUSTEDROOT);
2463 }
2464
2465 /* clean up state data. */
2466 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
2467 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
2468 Assert(SUCCEEDED(hrc));
2469 }
2470 else
2471 {
2472 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2473 "CryptCATCatalogInfoFromContext failed: %d [file=%s]",
2474 RtlGetLastWin32Error(), pwszName);
2475 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATCatalogInfoFromContext failed\n"));
2476 }
2477 iCat++;
2478 } while (rc == VERR_LDRVI_NOT_SIGNED && iCat < 128);
2479
2480 if (hCatInfoPrev != NULL)
2481 if (!g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/))
2482 AssertFailed();
2483 }
2484 else
2485 rc = RTErrInfoSetF(pErrInfo, rc2, "RTUtf16PrintHexBytes failed: %Rrc", rc);
2486 }
2487 else
2488 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2489 "CryptCATAdminCalcHashFromFileHandle[2] failed: %d [file=%s]", RtlGetLastWin32Error(), pwszName);
2490
2491 if (!ASMAtomicCmpXchgPtr(&s_aHashes[i].hCachedCatAdmin, hCatAdmin, NULL))
2492 if (!g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/))
2493 AssertFailed();
2494 }
2495 else
2496 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2497 "CryptCATAdminAcquireContext[2] failed: %d [file=%s]", RtlGetLastWin32Error(), pwszName);
2498 iPolicy++;
2499 } while ( fTryNextPolicy
2500 && iPolicy < RT_ELEMENTS(s_aPolicies));
2501
2502 /*
2503 * Only repeat if we've got g_pfnCryptCATAdminAcquireContext2 and can specify the hash algorithm.
2504 */
2505 if (!g_pfnCryptCATAdminAcquireContext2)
2506 break;
2507 if (rc != VERR_LDRVI_NOT_SIGNED)
2508 break;
2509 }
2510
2511 if (hFileClose != NULL)
2512 NtClose(hFileClose);
2513
2514 /*
2515 * DLLs that are likely candidates for local modifications.
2516 */
2517 if (rc == VERR_LDRVI_NOT_SIGNED)
2518 {
2519 bool fCoreSystemDll = false;
2520 PCRTUTF16 pwsz;
2521 uint32_t cwcName = (uint32_t)RTUtf16Len(pwszName);
2522 uint32_t cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR);
2523 if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_System32NtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
2524 {
2525 pwsz = pwszName + cwcOther + 1;
2526 if ( supHardViUtf16PathIsEqual(pwsz, "uxtheme.dll")
2527 || supHardViUtf16PathIsEqual(pwsz, "user32.dll")
2528 || supHardViUtf16PathIsEqual(pwsz, "gdi32.dll")
2529 || supHardViUtf16PathIsEqual(pwsz, "opengl32.dll")
2530 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "KernelBase.dll"))
2531 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "kernel32.dll"))
2532 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "ntdll.dll"))
2533 )
2534 {
2535 if (RTErrInfoIsSet(pErrInfo))
2536 RTErrInfoAdd(pErrInfo, rc, "\n");
2537 RTErrInfoAddF(pErrInfo, rc, "'%ls' is most likely modified.", pwszName);
2538 }
2539 }
2540
2541 /* Kludge for ancient windows versions we don't want to support but
2542 users still wants to use. Keep things as safe as possible without
2543 unnecessary effort. Problem is that 3rd party catalog files cannot
2544 easily be found. Showstopper for ATI users. */
2545 if ( fNoSignedCatalogFound == 1
2546 && g_uNtVerCombined < SUP_NT_VER_VISTA
2547 && !fCoreSystemDll)
2548 {
2549 rc = VINF_LDRVI_NOT_SIGNED;
2550 }
2551 }
2552
2553 return rc;
2554}
2555
2556
2557/**
2558 * Verifies the given image using WinVerifyTrust in some way.
2559 *
2560 * This is used by supHardenedWinVerifyImageByLdrMod as well as
2561 * supR3HardenedScreenImage.
2562 *
2563 * @returns IPRT status code.
2564 * @param hFile Handle of the file to verify.
2565 * @param pwszName Full NT path to the DLL in question, used for
2566 * dealing with unsigned system dlls as well as for
2567 * error/logging.
2568 * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was
2569 * actually used.
2570 * @param pErrInfo Pointer to error info structure. Optional.
2571 */
2572DECLHIDDEN(int) supHardenedWinVerifyImageTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, int rc,
2573 bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
2574{
2575 if (pfWinVerifyTrust)
2576 *pfWinVerifyTrust = false;
2577
2578 /*
2579 * Call the windows verify trust API if we've resolved it and aren't in
2580 * some obvious recursion.
2581 */
2582 if (g_pfnWinVerifyTrust != NULL)
2583 {
2584 /* Check for recursion. */
2585 bool fNoRecursion;
2586 if (g_iTlsWinVerifyTrustRecursion != UINT32_MAX)
2587 {
2588 fNoRecursion = TlsGetValue(g_iTlsWinVerifyTrustRecursion) == 0;
2589 if (fNoRecursion)
2590 TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)1);
2591 }
2592 else
2593 {
2594 uint32_t const idCurrentThread = RTNtCurrentThreadId();
2595 fNoRecursion = ASMAtomicCmpXchgU32(&g_idActiveThread, idCurrentThread, UINT32_MAX);
2596 }
2597 if (fNoRecursion)
2598 {
2599 /* We can call WinVerifyTrust. */
2600 if (pfWinVerifyTrust)
2601 *pfWinVerifyTrust = true;
2602
2603 if (rc != VERR_LDRVI_NOT_SIGNED)
2604 {
2605 if (rc == VINF_LDRVI_NOT_SIGNED)
2606 {
2607 if (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION)
2608 {
2609 int rc2 = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust);
2610 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (org %d)\n", rc2, rc));
2611 rc = rc2;
2612 }
2613 else
2614 {
2615 AssertFailed();
2616 rc = VERR_LDRVI_NOT_SIGNED;
2617 }
2618 }
2619 else if (RT_SUCCESS(rc))
2620 {
2621 HRESULT hrcWinVerifyTrust;
2622 rc = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust,
2623 &hrcWinVerifyTrust);
2624
2625 /* DLLs signed with special roots, like "Microsoft Digital Media Authority 2005",
2626 may fail here because the root cert is not in the normal certificate stores
2627 (if any). Our verification code has the basics of these certificates included
2628 and can verify them, which is why we end up here instead of in the
2629 VINF_LDRVI_NOT_SIGNED case above. Current workaround is to do as above.
2630 (Intel graphics driver DLLs, like igdusc64.dll. */
2631 if ( RT_FAILURE(rc)
2632 && hrcWinVerifyTrust == CERT_E_CHAINING
2633 && (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION))
2634 {
2635 rc = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust);
2636 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (was CERT_E_CHAINING)\n", rc));
2637 }
2638 }
2639 else
2640 {
2641 int rc2 = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust, NULL);
2642 AssertMsg(RT_FAILURE_NP(rc2),
2643 ("rc=%Rrc, rc2=%Rrc %s", rc, rc2, pErrInfo ? pErrInfo->pszMsg : "<no-err-info>"));
2644 }
2645 }
2646
2647 /* Unwind recursion. */
2648 if (g_iTlsWinVerifyTrustRecursion != UINT32_MAX)
2649 TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)0);
2650 else
2651 ASMAtomicWriteU32(&g_idActiveThread, UINT32_MAX);
2652 }
2653 else
2654 SUP_DPRINTF(("Detected WinVerifyTrust recursion: rc=%Rrc '%ls'.\n", rc, pwszName));
2655 }
2656 return rc;
2657}
2658
2659
2660/**
2661 * Checks if WinVerifyTrust is callable on the current thread.
2662 *
2663 * Used by the main code to figure whether it makes sense to try revalidate an
2664 * image that hasn't passed thru WinVerifyTrust yet.
2665 *
2666 * @returns true if callable on current thread, false if not.
2667 */
2668DECLHIDDEN(bool) supHardenedWinIsWinVerifyTrustCallable(void)
2669{
2670 return g_pfnWinVerifyTrust != NULL
2671 && ( g_iTlsWinVerifyTrustRecursion != UINT32_MAX
2672 ? (uintptr_t)TlsGetValue(g_iTlsWinVerifyTrustRecursion) == 0
2673 : g_idActiveThread != RTNtCurrentThreadId() );
2674}
2675
2676
2677
2678/**
2679 * Initializes g_uNtVerCombined and g_NtVerInfo.
2680 * Called from suplibHardenedWindowsMain and suplibOsInit.
2681 */
2682DECLHIDDEN(void) supR3HardenedWinInitVersion(void)
2683{
2684 /*
2685 * Get the windows version. Use RtlGetVersion as GetVersionExW and
2686 * GetVersion might not be telling the whole truth (8.0 on 8.1 depending on
2687 * the application manifest).
2688 */
2689 OSVERSIONINFOEXW NtVerInfo;
2690
2691 RT_ZERO(NtVerInfo);
2692 NtVerInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
2693 if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&NtVerInfo)))
2694 {
2695 RT_ZERO(NtVerInfo);
2696 PPEB pPeb = NtCurrentPeb();
2697 NtVerInfo.dwMajorVersion = pPeb->OSMajorVersion;
2698 NtVerInfo.dwMinorVersion = pPeb->OSMinorVersion;
2699 NtVerInfo.dwBuildNumber = pPeb->OSPlatformId;
2700 }
2701
2702 g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(NtVerInfo.dwMajorVersion, NtVerInfo.dwMinorVersion, NtVerInfo.dwBuildNumber,
2703 NtVerInfo.wServicePackMajor, NtVerInfo.wServicePackMinor);
2704}
2705
2706#endif /* IN_RING3 */
2707
Note: See TracBrowser for help on using the repository browser.

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