VirtualBox

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

Last change on this file since 64281 was 62677, checked in by vboxsync, 8 years ago

SUPHardNt: -Wall warnings.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use