VirtualBox

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

Last change on this file since 59810 was 59810, checked in by vboxsync, 9 years ago

supR3HardenedWinInitVersion: Don't call RtlGetVersion during early init, it may touch NTDLL BSS data and cause VERR_SUP_VP_MEMORY_VS_FILE_MISMATCH when opening our kernel driver. Problem started with windows 10 build 14267.

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

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