VirtualBox

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

最後變更 在這個檔案從84540是 84398,由 vboxsync 提交於 5 年 前

SUPHardNt: FNRTCRPKCS7VERIFYCERTCALLBACK changed behaviour with r138008, adjusted code accordingly. bugref:9699

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

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