VirtualBox

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

Last change on this file since 78351 was 77972, checked in by vboxsync, 6 years ago

HostDrivers/Support/win: Burn fix for r129708 (wasn't aware of the Windows part, bugref:9188)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 127.7 KB
Line 
1/* $Id: SUPHardenedVerifyImage-win.cpp 77972 2019-04-01 09:40:16Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library/Driver - Hardened Image Verification, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#ifdef IN_RING0
32# 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 (hCertPaths == NIL_RTCRX509CERTPATHS)
969 {
970 if (RTCrX509Certificate_Compare(pCert, &g_BuildX509Cert) == 0) /* healthy paranoia */
971 return VINF_SUCCESS;
972 int rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_BUILD_CERT_IPE, "Not valid kernel code signature (fFlags=%#x).", fFlags);
973 if (pErrInfo)
974 {
975 RTErrInfoAdd(pErrInfo, rc, "\n\nExe cert:\n");
976 RTAsn1Dump(&pCert->SeqCore.Asn1Core, 0 /*fFlags*/, 0 /*uLevel*/, supHardNtViAsn1DumpToErrInfo, pErrInfo);
977 RTErrInfoAdd(pErrInfo, rc, "\n\nBuild cert:\n");
978 RTAsn1Dump(&g_BuildX509Cert.SeqCore.Asn1Core, 0 /*fFlags*/, 0 /*uLevel*/, supHardNtViAsn1DumpToErrInfo, pErrInfo);
979 }
980 return rc;
981 }
982
983 /*
984 * Standard code signing capabilites required.
985 */
986 int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
987 if ( RT_SUCCESS(rc)
988 && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
989 {
990 /*
991 * If kernel signing, a valid certificate path must be anchored by the
992 * microsoft kernel signing root certificate.
993 */
994 if (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING)
995 {
996 uint32_t cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
997 uint32_t cFound = 0;
998 uint32_t cValid = 0;
999 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
1000 {
1001 bool fTrusted;
1002 PCRTCRX509NAME pSubject;
1003 PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
1004 int rcVerify;
1005 rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
1006 NULL, NULL /*pCertCtx*/, &rcVerify);
1007 AssertRCBreak(rc);
1008
1009 if (RT_SUCCESS(rcVerify))
1010 {
1011 Assert(fTrusted);
1012 cValid++;
1013
1014 /*
1015 * Search the kernel signing root store for a matching anchor.
1016 */
1017 RTCRSTORECERTSEARCH Search;
1018 rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(g_hNtKernelRootStore, pSubject, &Search);
1019 AssertRCBreak(rc);
1020
1021 PCRTCRCERTCTX pCertCtx;
1022 while ((pCertCtx = RTCrStoreCertSearchNext(g_hNtKernelRootStore, &Search)) != NULL)
1023 {
1024 PCRTCRX509SUBJECTPUBLICKEYINFO pCertPubKeyInfo = NULL;
1025 if (pCertCtx->pCert)
1026 pCertPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
1027 else if (pCertCtx->pTaInfo)
1028 pCertPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
1029 else
1030 pCertPubKeyInfo = NULL;
1031 if ( pCertPubKeyInfo
1032 && RTCrX509SubjectPublicKeyInfo_Compare(pCertPubKeyInfo, pPublicKeyInfo) == 0)
1033 cFound++;
1034 RTCrCertCtxRelease(pCertCtx);
1035 }
1036
1037 int rc2 = RTCrStoreCertSearchDestroy(g_hNtKernelRootStore, &Search); AssertRC(rc2);
1038 }
1039 }
1040 if (RT_SUCCESS(rc) && cFound == 0)
1041 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_VALID_KERNEL_CODE_SIGNATURE, "Not valid kernel code signature.");
1042 if (RT_SUCCESS(rc) && cValid < 2 && g_fHaveOtherRoots)
1043 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNEXPECTED_VALID_PATH_COUNT,
1044 "Expected at least %u valid paths, not %u.", 2, cValid);
1045 }
1046 }
1047
1048 /*
1049 * More requirements? NT5 build lab?
1050 */
1051
1052 return rc;
1053}
1054
1055
1056static DECLCALLBACK(int) supHardNtViCallback(RTLDRMOD hLdrMod, RTLDRSIGNATURETYPE enmSignature,
1057 void const *pvSignature, size_t cbSignature,
1058 void const *pvExternalData, size_t cbExternalData,
1059 PRTERRINFO pErrInfo, void *pvUser)
1060{
1061 RT_NOREF(hLdrMod, enmSignature, pvExternalData, cbExternalData);
1062
1063 /*
1064 * Check out the input.
1065 */
1066 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pvUser;
1067 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
1068
1069 AssertReturn(cbSignature == sizeof(RTCRPKCS7CONTENTINFO), VERR_INTERNAL_ERROR_5);
1070 PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pvSignature;
1071 AssertReturn(RTCrPkcs7ContentInfo_IsSignedData(pContentInfo), VERR_INTERNAL_ERROR_5);
1072 AssertReturn(pContentInfo->u.pSignedData->SignerInfos.cItems == 1, VERR_INTERNAL_ERROR_5);
1073 PCRTCRPKCS7SIGNERINFO pSignerInfo = pContentInfo->u.pSignedData->SignerInfos.papItems[0];
1074
1075 AssertReturn(!pvExternalData, VERR_INTERNAL_ERROR_5);
1076
1077 /*
1078 * If special certificate requirements, check them out before validating
1079 * the signature.
1080 */
1081 if (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT)
1082 {
1083 if (!RTCrX509Certificate_MatchIssuerAndSerialNumber(&g_BuildX509Cert,
1084 &pSignerInfo->IssuerAndSerialNumber.Name,
1085 &pSignerInfo->IssuerAndSerialNumber.SerialNumber))
1086 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_SIGNED_WITH_BUILD_CERT,
1087 "Not signed with the build certificate (serial %.*Rhxs, expected %.*Rhxs)",
1088 pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.cb,
1089 pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.uData.pv,
1090 g_BuildX509Cert.TbsCertificate.SerialNumber.Asn1Core.cb,
1091 g_BuildX509Cert.TbsCertificate.SerialNumber.Asn1Core.uData.pv);
1092 }
1093
1094 /*
1095 * Verify the signature. We instruct the verifier to use the signing time
1096 * counter signature present when present, falling back on the timestamp
1097 * planted by the linker when absent. In ring-0 we don't have all the
1098 * necessary timestamp server root certificate info, so we have to allow
1099 * using counter signatures unverified there. Ditto for the early period
1100 * of ring-3 hardened stub execution.
1101 */
1102 RTTIMESPEC ValidationTime;
1103 RTTimeSpecSetSeconds(&ValidationTime, pNtViRdr->uTimestamp);
1104
1105 uint32_t fFlags = RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
1106 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
1107 | RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY;
1108#ifndef IN_RING0
1109 if (!g_fHaveOtherRoots)
1110#endif
1111 fFlags |= RTCRPKCS7VERIFY_SD_F_USE_SIGNING_TIME_UNVERIFIED | RTCRPKCS7VERIFY_SD_F_USE_MS_TIMESTAMP_UNVERIFIED;
1112 return RTCrPkcs7VerifySignedData(pContentInfo, fFlags, g_hSpcAndNtKernelSuppStore, g_hSpcAndNtKernelRootStore,
1113 &ValidationTime, supHardNtViCertVerifyCallback, pNtViRdr, pErrInfo);
1114}
1115
1116
1117/**
1118 * RTTimeNow equivaltent that handles ring-3 where we cannot use it.
1119 *
1120 * @returns pNow
1121 * @param pNow Where to return the current time.
1122 */
1123static PRTTIMESPEC supHardNtTimeNow(PRTTIMESPEC pNow)
1124{
1125#ifdef IN_RING3
1126 /*
1127 * Just read system time.
1128 */
1129 KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA;
1130# ifdef RT_ARCH_AMD64
1131 uint64_t uRet = *(uint64_t volatile *)&pUserSharedData->SystemTime; /* This is what KeQuerySystemTime does (missaligned). */
1132 return RTTimeSpecSetNtTime(pNow, uRet);
1133# else
1134
1135 LARGE_INTEGER NtTime;
1136 do
1137 {
1138 NtTime.HighPart = pUserSharedData->SystemTime.High1Time;
1139 NtTime.LowPart = pUserSharedData->SystemTime.LowPart;
1140 } while (pUserSharedData->SystemTime.High2Time != NtTime.HighPart);
1141 return RTTimeSpecSetNtTime(pNow, NtTime.QuadPart);
1142# endif
1143#else /* IN_RING0 */
1144 return RTTimeNow(pNow);
1145#endif /* IN_RING0 */
1146}
1147
1148
1149/**
1150 * Verifies the given loader image.
1151 *
1152 * @returns IPRT status code.
1153 * @param hLdrMod File handle to the executable file.
1154 * @param pwszName Full NT path to the DLL in question, used for
1155 * dealing with unsigned system dlls as well as for
1156 * error/logging.
1157 * @param pNtViRdr The reader instance /w flags.
1158 * @param fAvoidWinVerifyTrust Whether to avoid WinVerifyTrust because of
1159 * deadlock or other loader related dangers.
1160 * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was used.
1161 * @param pErrInfo Pointer to error info structure. Optional.
1162 */
1163DECLHIDDEN(int) supHardenedWinVerifyImageByLdrMod(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, PSUPHNTVIRDR pNtViRdr,
1164 bool fAvoidWinVerifyTrust, bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
1165{
1166 if (pfWinVerifyTrust)
1167 *pfWinVerifyTrust = false;
1168
1169#ifdef IN_RING3
1170 /* Check that the caller has performed the necessary library initialization. */
1171 if (!RTCrX509Certificate_IsPresent(&g_BuildX509Cert))
1172 return RTErrInfoSet(pErrInfo, VERR_WRONG_ORDER,
1173 "supHardenedWinVerifyImageByHandle: supHardenedWinInitImageVerifier was not called.");
1174#endif
1175
1176 /*
1177 * Check the trusted installer bit first, if requested as it's somewhat
1178 * cheaper than the rest.
1179 *
1180 * We relax this for system32 and a little for WinSxS, like we used to, as
1181 * there are apparently some systems out there where the user, admin, or
1182 * someone has changed the ownership of core windows DLLs like user32.dll
1183 * and comctl32.dll. Since we need user32.dll and will be checking it's
1184 * digital signature, it's reasonably safe to let this thru. (The report
1185 * was of SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS
1186 * owning user32.dll, see public ticket 13187, VBoxStartup.3.log.)
1187 *
1188 * We've also had problems with graphics driver components like ig75icd64.dll
1189 * and atig6pxx.dll not being owned by TrustedInstaller, with the result
1190 * that 3D got broken (mod by zero issue in test build 5). These were also
1191 * SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS.
1192 *
1193 * In one report by 'thor' the WinSxS resident comctl32.dll was owned by
1194 * SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS (with 4.3.16).
1195 */
1196 /** @todo Since we're now allowing Builtin\\Administrators after all, perhaps we
1197 * could drop these system32 + winsxs hacks?? */
1198 if ( (pNtViRdr->fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
1199 && !supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar(pNtViRdr->hFile, pwszName))
1200 {
1201 if (supHardViUtf16PathStartsWithEx(pwszName, (uint32_t)RTUtf16Len(pwszName),
1202 g_System32NtPath.UniStr.Buffer, g_System32NtPath.UniStr.Length / sizeof(WCHAR),
1203 true /*fCheckSlash*/))
1204 SUP_DPRINTF(("%ls: Relaxing the TrustedInstaller requirement for this DLL (it's in system32).\n", pwszName));
1205 else if (supHardViUtf16PathStartsWithEx(pwszName, (uint32_t)RTUtf16Len(pwszName),
1206 g_WinSxSNtPath.UniStr.Buffer, g_WinSxSNtPath.UniStr.Length / sizeof(WCHAR),
1207 true /*fCheckSlash*/))
1208 SUP_DPRINTF(("%ls: Relaxing the TrustedInstaller requirement for this DLL (it's in WinSxS).\n", pwszName));
1209 else
1210 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_OWNED_BY_TRUSTED_INSTALLER,
1211 "supHardenedWinVerifyImageByHandle: TrustedInstaller is not the owner of '%ls'.", pwszName);
1212 }
1213
1214 /*
1215 * Verify it.
1216 *
1217 * The PKCS #7 SignedData signature is checked in the callback. Any
1218 * signing certificate restrictions are also enforced there.
1219 *
1220 * For the time being, we use the executable timestamp as the
1221 * certificate validation date. We must query that first to avoid
1222 * potential issues re-entering the loader code from the callback.
1223 *
1224 * Update: Save the first timestamp we validate with build cert and
1225 * use this as a minimum timestamp for further build cert
1226 * validations. This works around issues with old DLLs that
1227 * we sign against with our certificate (crt, sdl, qt).
1228 *
1229 * Update: If the validation fails, retry with the current timestamp. This
1230 * is a workaround for NTDLL.DLL in build 14971 having a weird
1231 * timestamp: 0xDF1E957E (Sat Aug 14 14:05:18 2088).
1232 */
1233 int rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &pNtViRdr->uTimestamp, sizeof(pNtViRdr->uTimestamp));
1234 if (RT_SUCCESS(rc))
1235 {
1236#ifdef IN_RING3 /* Hack alert! (see above) */
1237 if ( (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING)
1238 && (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT)
1239 && pNtViRdr->uTimestamp < g_uBuildTimestampHack)
1240 pNtViRdr->uTimestamp = g_uBuildTimestampHack;
1241#endif
1242
1243 rc = RTLdrVerifySignature(hLdrMod, supHardNtViCallback, pNtViRdr, pErrInfo);
1244
1245#ifdef IN_RING3 /* Hack alert! (see above) */
1246 if ((pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT) && g_uBuildTimestampHack == 0 && RT_SUCCESS(rc))
1247 g_uBuildTimestampHack = pNtViRdr->uTimestamp;
1248#endif
1249
1250 if (rc == VERR_CR_X509_CPV_NOT_VALID_AT_TIME)
1251 {
1252 RTTIMESPEC Now;
1253 uint64_t uOld = pNtViRdr->uTimestamp;
1254 pNtViRdr->uTimestamp = RTTimeSpecGetSeconds(supHardNtTimeNow(&Now));
1255 SUP_DPRINTF(("%ls: VERR_CR_X509_CPV_NOT_VALID_AT_TIME for %#RX64; retrying against current time: %#RX64.\n",
1256 pwszName, uOld, pNtViRdr->uTimestamp)); NOREF(uOld);
1257 rc = RTLdrVerifySignature(hLdrMod, supHardNtViCallback, pNtViRdr, pErrInfo);
1258 }
1259
1260 /*
1261 * Microsoft doesn't sign a whole bunch of DLLs, so we have to
1262 * ASSUME that a bunch of system DLLs are fine.
1263 */
1264 if (rc == VERR_LDRVI_NOT_SIGNED)
1265 rc = supHardNtViCheckIfNotSignedOk(hLdrMod, pwszName, pNtViRdr->fFlags, pNtViRdr->hFile, rc);
1266 if (RT_FAILURE(rc))
1267 RTErrInfoAddF(pErrInfo, rc, ": %ls", pwszName);
1268
1269 /*
1270 * Check for the signature checking enforcement, if requested to do so.
1271 */
1272 if (RT_SUCCESS(rc) && (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT))
1273 {
1274 bool fEnforced = false;
1275 int rc2 = RTLdrQueryProp(hLdrMod, RTLDRPROP_SIGNATURE_CHECKS_ENFORCED, &fEnforced, sizeof(fEnforced));
1276 if (RT_FAILURE(rc2))
1277 rc = RTErrInfoSetF(pErrInfo, rc2, "Querying RTLDRPROP_SIGNATURE_CHECKS_ENFORCED failed on %ls: %Rrc.",
1278 pwszName, rc2);
1279 else if (!fEnforced)
1280 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SIGNATURE_CHECKS_NOT_ENFORCED,
1281 "The image '%ls' was not linked with /IntegrityCheck.", pwszName);
1282 }
1283 }
1284 else
1285 RTErrInfoSetF(pErrInfo, rc, "RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on %ls: %Rrc", pwszName, rc);
1286
1287#ifdef IN_RING3
1288 /*
1289 * Pass it thru WinVerifyTrust when possible.
1290 */
1291 if (!fAvoidWinVerifyTrust)
1292 rc = supHardenedWinVerifyImageTrust(pNtViRdr->hFile, pwszName, pNtViRdr->fFlags, rc, pfWinVerifyTrust, pErrInfo);
1293#else
1294 RT_NOREF1(fAvoidWinVerifyTrust);
1295#endif
1296
1297 /*
1298 * Check for blacklisted DLLs, both internal name and filename.
1299 */
1300 if (RT_SUCCESS(rc))
1301 {
1302 size_t const cwcName = RTUtf16Len(pwszName);
1303 char szIntName[64];
1304 int rc2 = RTLdrQueryProp(hLdrMod, RTLDRPROP_INTERNAL_NAME, szIntName, sizeof(szIntName));
1305 if (RT_SUCCESS(rc2))
1306 {
1307 size_t const cchIntName = strlen(szIntName);
1308 for (unsigned i = 0; g_aSupNtViBlacklistedDlls[i].psz != NULL; i++)
1309 if ( cchIntName == g_aSupNtViBlacklistedDlls[i].cch
1310 && RTStrICmpAscii(szIntName, g_aSupNtViBlacklistedDlls[i].psz) == 0)
1311 {
1312 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNDESIRABLE_MODULE,
1313 "The image '%ls' is listed as undesirable.", pwszName);
1314 break;
1315 }
1316 }
1317 if (RT_SUCCESS(rc))
1318 {
1319 for (unsigned i = 0; g_aSupNtViBlacklistedDlls[i].psz != NULL; i++)
1320 if (cwcName >= g_aSupNtViBlacklistedDlls[i].cch)
1321 {
1322 PCRTUTF16 pwszTmp = &pwszName[cwcName - g_aSupNtViBlacklistedDlls[i].cch];
1323 if ( ( cwcName == g_aSupNtViBlacklistedDlls[i].cch
1324 || pwszTmp[-1] == '\\'
1325 || pwszTmp[-1] == '/')
1326 && RTUtf16ICmpAscii(pwszTmp, g_aSupNtViBlacklistedDlls[i].psz) == 0)
1327 {
1328 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNDESIRABLE_MODULE,
1329 "The image '%ls' is listed as undesirable.", pwszName);
1330 break;
1331 }
1332 }
1333 }
1334 }
1335
1336#ifdef IN_SUP_HARDENED_R3
1337 /*
1338 * Hook for the LdrLoadDll code to schedule scanning of imports.
1339 */
1340 if (RT_SUCCESS(rc))
1341 supR3HardenedWinVerifyCacheScheduleImports(hLdrMod, pwszName);
1342#endif
1343
1344 return rc;
1345}
1346
1347
1348/**
1349 * Verifies the given executable image.
1350 *
1351 * @returns IPRT status code.
1352 * @param hFile File handle to the executable file.
1353 * @param pwszName Full NT path to the DLL in question, used for
1354 * dealing with unsigned system dlls as well as for
1355 * error/logging.
1356 * @param fFlags Flags, SUPHNTVI_F_XXX.
1357 * @param fAvoidWinVerifyTrust Whether to avoid WinVerifyTrust because of
1358 * deadlock or other loader related dangers.
1359 * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was used.
1360 * @param pErrInfo Pointer to error info structure. Optional.
1361 */
1362DECLHIDDEN(int) supHardenedWinVerifyImageByHandle(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, bool fAvoidWinVerifyTrust,
1363 bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
1364{
1365 /*
1366 * Create a reader instance.
1367 */
1368 PSUPHNTVIRDR pNtViRdr;
1369 int rc = supHardNtViRdrCreate(hFile, pwszName, fFlags, &pNtViRdr);
1370 if (RT_SUCCESS(rc))
1371 {
1372 /*
1373 * Open the image.
1374 */
1375 RTLDRMOD hLdrMod;
1376 RTLDRARCH enmArch = fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST;
1377 uint32_t fLdrFlags = RTLDR_O_FOR_VALIDATION | RTLDR_O_IGNORE_ARCH_IF_NO_CODE;
1378 if (fFlags & SUPHNTVI_F_IGNORE_ARCHITECTURE)
1379 fLdrFlags |= RTLDR_O_IGNORE_ARCH_IF_NO_CODE;
1380 rc = RTLdrOpenWithReader(&pNtViRdr->Core, fLdrFlags, enmArch, &hLdrMod, pErrInfo);
1381 if (RT_SUCCESS(rc))
1382 {
1383 /*
1384 * Verify it.
1385 */
1386 rc = supHardenedWinVerifyImageByLdrMod(hLdrMod, pwszName, pNtViRdr, fAvoidWinVerifyTrust, pfWinVerifyTrust, pErrInfo);
1387 int rc2 = RTLdrClose(hLdrMod); AssertRC(rc2);
1388 }
1389 else
1390 supHardNtViRdrDestroy(&pNtViRdr->Core);
1391 }
1392 SUP_DPRINTF(("supHardenedWinVerifyImageByHandle: -> %d (%ls)%s\n",
1393 rc, pwszName, pfWinVerifyTrust && *pfWinVerifyTrust ? " WinVerifyTrust" : ""));
1394 return rc;
1395}
1396
1397
1398#ifdef IN_RING3
1399/**
1400 * supHardenedWinVerifyImageByHandle version without the name.
1401 *
1402 * The name is derived from the handle.
1403 *
1404 * @returns IPRT status code.
1405 * @param hFile File handle to the executable file.
1406 * @param fFlags Flags, SUPHNTVI_F_XXX.
1407 * @param pErrInfo Pointer to error info structure. Optional.
1408 */
1409DECLHIDDEN(int) supHardenedWinVerifyImageByHandleNoName(HANDLE hFile, uint32_t fFlags, PRTERRINFO pErrInfo)
1410{
1411 /*
1412 * Determine the NT name and call the verification function.
1413 */
1414 union
1415 {
1416 UNICODE_STRING UniStr;
1417 uint8_t abBuffer[(MAX_PATH + 8 + 1) * 2];
1418 } uBuf;
1419
1420 ULONG cbIgn;
1421 NTSTATUS rcNt = NtQueryObject(hFile,
1422 ObjectNameInformation,
1423 &uBuf,
1424 sizeof(uBuf) - sizeof(WCHAR),
1425 &cbIgn);
1426 if (NT_SUCCESS(rcNt))
1427 uBuf.UniStr.Buffer[uBuf.UniStr.Length / sizeof(WCHAR)] = '\0';
1428 else
1429 uBuf.UniStr.Buffer = (WCHAR *)L"TODO3";
1430
1431 return supHardenedWinVerifyImageByHandle(hFile, uBuf.UniStr.Buffer, fFlags, false /*fAvoidWinVerifyTrust*/,
1432 NULL /*pfWinVerifyTrust*/, pErrInfo);
1433}
1434#endif /* IN_RING3 */
1435
1436
1437/**
1438 * Retrieves the full official path to the system root or one of it's sub
1439 * directories.
1440 *
1441 * This code is also used by the support driver.
1442 *
1443 * @returns VBox status code.
1444 * @param pvBuf The output buffer. This will contain a
1445 * UNICODE_STRING followed (at the kernel's
1446 * discretion) the string buffer.
1447 * @param cbBuf The size of the buffer @a pvBuf points to.
1448 * @param enmDir Which directory under the system root we're
1449 * interested in.
1450 * @param pErrInfo Pointer to error info structure. Optional.
1451 */
1452DECLHIDDEN(int) supHardNtGetSystemRootDir(void *pvBuf, uint32_t cbBuf, SUPHARDNTSYSROOTDIR enmDir, PRTERRINFO pErrInfo)
1453{
1454 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
1455 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1456
1457 UNICODE_STRING NtName;
1458 switch (enmDir)
1459 {
1460 case kSupHardNtSysRootDir_System32:
1461 {
1462 static const WCHAR s_wszNameSystem32[] = L"\\SystemRoot\\System32\\";
1463 NtName.Buffer = (PWSTR)s_wszNameSystem32;
1464 NtName.Length = sizeof(s_wszNameSystem32) - sizeof(WCHAR);
1465 NtName.MaximumLength = sizeof(s_wszNameSystem32);
1466 break;
1467 }
1468 case kSupHardNtSysRootDir_WinSxS:
1469 {
1470 static const WCHAR s_wszNameWinSxS[] = L"\\SystemRoot\\WinSxS\\";
1471 NtName.Buffer = (PWSTR)s_wszNameWinSxS;
1472 NtName.Length = sizeof(s_wszNameWinSxS) - sizeof(WCHAR);
1473 NtName.MaximumLength = sizeof(s_wszNameWinSxS);
1474 break;
1475 }
1476 default:
1477 AssertFailed();
1478 return VERR_INVALID_PARAMETER;
1479 }
1480
1481 OBJECT_ATTRIBUTES ObjAttr;
1482 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1483
1484 NTSTATUS rcNt = NtCreateFile(&hFile,
1485 FILE_READ_DATA | SYNCHRONIZE,
1486 &ObjAttr,
1487 &Ios,
1488 NULL /* Allocation Size*/,
1489 FILE_ATTRIBUTE_NORMAL,
1490 FILE_SHARE_READ | FILE_SHARE_WRITE,
1491 FILE_OPEN,
1492 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1493 NULL /*EaBuffer*/,
1494 0 /*EaLength*/);
1495 if (NT_SUCCESS(rcNt))
1496 rcNt = Ios.Status;
1497 if (NT_SUCCESS(rcNt))
1498 {
1499 ULONG cbIgn;
1500 rcNt = NtQueryObject(hFile,
1501 ObjectNameInformation,
1502 pvBuf,
1503 cbBuf - sizeof(WCHAR),
1504 &cbIgn);
1505 NtClose(hFile);
1506 if (NT_SUCCESS(rcNt))
1507 {
1508 PUNICODE_STRING pUniStr = (PUNICODE_STRING)pvBuf;
1509 if (pUniStr->Length > 0)
1510 {
1511 /* Make sure it's terminated so it can safely be printed.*/
1512 pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
1513 return VINF_SUCCESS;
1514 }
1515
1516 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH,
1517 "NtQueryObject returned an empty path for '%ls'", NtName.Buffer);
1518 }
1519 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "NtQueryObject failed on '%ls' dir: %#x", NtName.Buffer, rcNt);
1520 }
1521 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "Failure to open '%ls': %#x", NtName.Buffer, rcNt);
1522}
1523
1524
1525/**
1526 * Initialize one certificate entry.
1527 *
1528 * @returns VBox status code.
1529 * @param pCert The X.509 certificate representation to init.
1530 * @param pabCert The raw DER encoded certificate.
1531 * @param cbCert The size of the raw certificate.
1532 * @param pErrInfo Where to return extended error info. Optional.
1533 * @param pszErrorTag Error tag.
1534 */
1535static int supHardNtViCertInit(PRTCRX509CERTIFICATE pCert, unsigned char const *pabCert, unsigned cbCert,
1536 PRTERRINFO pErrInfo, const char *pszErrorTag)
1537{
1538 AssertReturn(cbCert > 16 && cbCert < _128K,
1539 RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, "%s: cbCert=%#x out of range", pszErrorTag, cbCert));
1540 AssertReturn(!RTCrX509Certificate_IsPresent(pCert),
1541 RTErrInfoSetF(pErrInfo, VERR_WRONG_ORDER, "%s: Certificate already decoded?", pszErrorTag));
1542
1543 RTASN1CURSORPRIMARY PrimaryCursor;
1544 RTAsn1CursorInitPrimary(&PrimaryCursor, pabCert, cbCert, pErrInfo, &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, NULL);
1545 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, pCert, pszErrorTag);
1546 if (RT_SUCCESS(rc))
1547 rc = RTCrX509Certificate_CheckSanity(pCert, 0, pErrInfo, pszErrorTag);
1548 return rc;
1549}
1550
1551
1552static int supHardNtViCertStoreAddArray(RTCRSTORE hStore, PCSUPTAENTRY paCerts, unsigned cCerts, PRTERRINFO pErrInfo)
1553{
1554 for (uint32_t i = 0; i < cCerts; i++)
1555 {
1556 int rc = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_TAF_DER, paCerts[i].pch, paCerts[i].cb, pErrInfo);
1557 if (RT_FAILURE(rc))
1558 return rc;
1559 }
1560 return VINF_SUCCESS;
1561}
1562
1563
1564/**
1565 * Initialize a certificate table.
1566 *
1567 * @param phStore Where to return the store pointer.
1568 * @param paCerts1 Pointer to the first certificate table.
1569 * @param cCerts1 Entries in the first certificate table.
1570 * @param paCerts2 Pointer to the second certificate table.
1571 * @param cCerts2 Entries in the second certificate table.
1572 * @param paCerts3 Pointer to the third certificate table.
1573 * @param cCerts3 Entries in the third certificate table.
1574 * @param pErrInfo Where to return extended error info. Optional.
1575 * @param pszErrorTag Error tag.
1576 */
1577static int supHardNtViCertStoreInit(PRTCRSTORE phStore,
1578 PCSUPTAENTRY paCerts1, unsigned cCerts1,
1579 PCSUPTAENTRY paCerts2, unsigned cCerts2,
1580 PCSUPTAENTRY paCerts3, unsigned cCerts3,
1581 PRTERRINFO pErrInfo, const char *pszErrorTag)
1582{
1583 AssertReturn(*phStore == NIL_RTCRSTORE, VERR_WRONG_ORDER);
1584 RT_NOREF1(pszErrorTag);
1585
1586 int rc = RTCrStoreCreateInMem(phStore, cCerts1 + cCerts2);
1587 if (RT_FAILURE(rc))
1588 return RTErrInfoSetF(pErrInfo, rc, "RTCrStoreCreateMemoryStore failed: %Rrc", rc);
1589
1590 rc = supHardNtViCertStoreAddArray(*phStore, paCerts1, cCerts1, pErrInfo);
1591 if (RT_SUCCESS(rc))
1592 rc = supHardNtViCertStoreAddArray(*phStore, paCerts2, cCerts2, pErrInfo);
1593 if (RT_SUCCESS(rc))
1594 rc = supHardNtViCertStoreAddArray(*phStore, paCerts3, cCerts3, pErrInfo);
1595 return rc;
1596}
1597
1598
1599#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
1600/**
1601 * Initializes the windows paths.
1602 */
1603static void supHardenedWinInitImageVerifierWinPaths(void)
1604{
1605 /*
1606 * Windows paths that we're interested in.
1607 */
1608 static const struct
1609 {
1610 SUPSYSROOTDIRBUF *pNtPath;
1611 WCHAR const *pwszRegValue;
1612 const char *pszLogName;
1613 } s_aPaths[] =
1614 {
1615 { &g_ProgramFilesNtPath, L"ProgramFilesDir", "ProgDir" },
1616 { &g_CommonFilesNtPath, L"CommonFilesDir", "ComDir" },
1617# ifdef RT_ARCH_AMD64
1618 { &g_ProgramFilesX86NtPath, L"ProgramFilesDir (x86)", "ProgDir32" },
1619 { &g_CommonFilesX86NtPath, L"CommonFilesDir (x86)", "ComDir32" },
1620# endif
1621 };
1622
1623 /*
1624 * Open the registry key containing the paths.
1625 */
1626 UNICODE_STRING NtName = RTNT_CONSTANT_UNISTR(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion");
1627 OBJECT_ATTRIBUTES ObjAttr;
1628 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1629 HANDLE hKey;
1630 NTSTATUS rcNt = NtOpenKey(&hKey, KEY_QUERY_VALUE, &ObjAttr);
1631 if (NT_SUCCESS(rcNt))
1632 {
1633 /*
1634 * Loop over the paths and resolve their NT paths.
1635 */
1636 for (uint32_t i = 0; i < RT_ELEMENTS(s_aPaths); i++)
1637 {
1638 /*
1639 * Query the value first.
1640 */
1641 UNICODE_STRING ValueName;
1642 ValueName.Buffer = (WCHAR *)s_aPaths[i].pwszRegValue;
1643 ValueName.Length = (USHORT)(RTUtf16Len(s_aPaths[i].pwszRegValue) * sizeof(WCHAR));
1644 ValueName.MaximumLength = ValueName.Length + sizeof(WCHAR);
1645
1646 union
1647 {
1648 KEY_VALUE_PARTIAL_INFORMATION PartialInfo;
1649 uint8_t abPadding[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(WCHAR) * 128];
1650 uint64_t uAlign;
1651 } uBuf;
1652
1653 ULONG cbActual = 0;
1654 rcNt = NtQueryValueKey(hKey, &ValueName, KeyValuePartialInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR), &cbActual);
1655 if (NT_SUCCESS(rcNt))
1656 {
1657 /*
1658 * Must be a simple string value, terminate it.
1659 */
1660 if ( uBuf.PartialInfo.Type == REG_EXPAND_SZ
1661 || uBuf.PartialInfo.Type == REG_SZ)
1662 {
1663 /*
1664 * Expand any environment variable references before opening it.
1665 * We use the result buffer as storage for the expaneded path,
1666 * reserving space for the windows name space prefix.
1667 */
1668 UNICODE_STRING Src;
1669 Src.Buffer = (WCHAR *)uBuf.PartialInfo.Data;
1670 Src.Length = uBuf.PartialInfo.DataLength;
1671 if (Src.Length >= sizeof(WCHAR) && Src.Buffer[Src.Length / sizeof(WCHAR) - 1] == '\0')
1672 Src.Length -= sizeof(WCHAR);
1673 Src.MaximumLength = Src.Length + sizeof(WCHAR);
1674 Src.Buffer[uBuf.PartialInfo.DataLength / sizeof(WCHAR)] = '\0';
1675
1676 s_aPaths[i].pNtPath->awcBuffer[0] = '\\';
1677 s_aPaths[i].pNtPath->awcBuffer[1] = '?';
1678 s_aPaths[i].pNtPath->awcBuffer[2] = '?';
1679 s_aPaths[i].pNtPath->awcBuffer[3] = '\\';
1680 UNICODE_STRING Dst;
1681 Dst.Buffer = &s_aPaths[i].pNtPath->awcBuffer[4];
1682 Dst.MaximumLength = sizeof(s_aPaths[i].pNtPath->awcBuffer) - sizeof(WCHAR) * 5;
1683 Dst.Length = Dst.MaximumLength;
1684
1685 if (uBuf.PartialInfo.Type == REG_EXPAND_SZ)
1686 rcNt = RtlExpandEnvironmentStrings_U(NULL, &Src, &Dst, NULL);
1687 else
1688 {
1689 memcpy(Dst.Buffer, Src.Buffer, Src.Length);
1690 Dst.Length = Src.Length;
1691 }
1692 if (NT_SUCCESS(rcNt))
1693 {
1694 Dst.Buffer[Dst.Length / sizeof(WCHAR)] = '\0';
1695
1696 /*
1697 * Include the \\??\\ prefix in the result and open the path.
1698 */
1699 Dst.Buffer -= 4;
1700 Dst.Length += 4 * sizeof(WCHAR);
1701 Dst.MaximumLength += 4 * sizeof(WCHAR);
1702 InitializeObjectAttributes(&ObjAttr, &Dst, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1703 HANDLE hFile = INVALID_HANDLE_VALUE;
1704 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1705 NTSTATUS rcNt = NtCreateFile(&hFile,
1706 FILE_READ_DATA | SYNCHRONIZE,
1707 &ObjAttr,
1708 &Ios,
1709 NULL /* Allocation Size*/,
1710 FILE_ATTRIBUTE_NORMAL,
1711 FILE_SHARE_READ | FILE_SHARE_WRITE,
1712 FILE_OPEN,
1713 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
1714 | FILE_SYNCHRONOUS_IO_NONALERT,
1715 NULL /*EaBuffer*/,
1716 0 /*EaLength*/);
1717 if (NT_SUCCESS(rcNt))
1718 rcNt = Ios.Status;
1719 if (NT_SUCCESS(rcNt))
1720 {
1721 /*
1722 * Query the real NT name.
1723 */
1724 ULONG cbIgn;
1725 rcNt = NtQueryObject(hFile,
1726 ObjectNameInformation,
1727 s_aPaths[i].pNtPath,
1728 sizeof(*s_aPaths[i].pNtPath) - sizeof(WCHAR),
1729 &cbIgn);
1730 if (NT_SUCCESS(rcNt))
1731 {
1732 if (s_aPaths[i].pNtPath->UniStr.Length > 0)
1733 {
1734 /* Make sure it's terminated.*/
1735 s_aPaths[i].pNtPath->UniStr.Buffer[s_aPaths[i].pNtPath->UniStr.Length / sizeof(WCHAR)] = '\0';
1736 SUP_DPRINTF(("%s:%*s %ls\n", s_aPaths[i].pszLogName, 9 - strlen(s_aPaths[i].pszLogName), "",
1737 s_aPaths[i].pNtPath->UniStr.Buffer));
1738 }
1739 else
1740 {
1741 SUP_DPRINTF(("%s: NtQueryObject returned empty string\n", s_aPaths[i].pszLogName));
1742 rcNt = STATUS_INVALID_PARAMETER;
1743 }
1744 }
1745 else
1746 SUP_DPRINTF(("%s: NtQueryObject failed: %#x\n", s_aPaths[i].pszLogName, rcNt));
1747 NtClose(hFile);
1748 }
1749 else
1750 SUP_DPRINTF(("%s: NtCreateFile failed: %#x (%ls)\n",
1751 s_aPaths[i].pszLogName, rcNt, Dst.Buffer));
1752 }
1753 else
1754 SUP_DPRINTF(("%s: RtlExpandEnvironmentStrings_U failed: %#x (%ls)\n",
1755 s_aPaths[i].pszLogName, rcNt, Src.Buffer));
1756 }
1757 else
1758 {
1759 SUP_DPRINTF(("%s: type mismatch: %#x\n", s_aPaths[i].pszLogName, uBuf.PartialInfo.Type));
1760 rcNt = STATUS_INVALID_PARAMETER;
1761 }
1762 }
1763 else
1764 SUP_DPRINTF(("%s: NtQueryValueKey failed: %#x\n", s_aPaths[i].pszLogName, rcNt));
1765
1766 /* Stub the entry on failure. */
1767 if (!NT_SUCCESS(rcNt))
1768 {
1769 s_aPaths[i].pNtPath->UniStr.Length = 0;
1770 s_aPaths[i].pNtPath->UniStr.Buffer = NULL;
1771 }
1772 }
1773 NtClose(hKey);
1774 }
1775 else
1776 {
1777 SUP_DPRINTF(("NtOpenKey(%ls) failed: %#x\n", NtName.Buffer, rcNt));
1778
1779 /* Stub all the entries on failure. */
1780 for (uint32_t i = 0; i < RT_ELEMENTS(s_aPaths); i++)
1781 {
1782 s_aPaths[i].pNtPath->UniStr.Length = 0;
1783 s_aPaths[i].pNtPath->UniStr.Buffer = NULL;
1784 }
1785 }
1786}
1787#endif /* IN_RING3 && !VBOX_PERMIT_EVEN_MORE */
1788
1789
1790/**
1791 * This initializes the certificates globals so we don't have to reparse them
1792 * every time we need to verify an image.
1793 *
1794 * @returns IPRT status code.
1795 * @param pErrInfo Where to return extended error info. Optional.
1796 */
1797DECLHIDDEN(int) supHardenedWinInitImageVerifier(PRTERRINFO pErrInfo)
1798{
1799 AssertReturn(!RTCrX509Certificate_IsPresent(&g_BuildX509Cert), VERR_WRONG_ORDER);
1800
1801 /*
1802 * Get the system root paths.
1803 */
1804 int rc = supHardNtGetSystemRootDir(&g_System32NtPath, sizeof(g_System32NtPath), kSupHardNtSysRootDir_System32, pErrInfo);
1805 if (RT_SUCCESS(rc))
1806 rc = supHardNtGetSystemRootDir(&g_WinSxSNtPath, sizeof(g_WinSxSNtPath), kSupHardNtSysRootDir_WinSxS, pErrInfo);
1807 if (RT_SUCCESS(rc))
1808 {
1809 SUP_DPRINTF(("System32: %ls\n", g_System32NtPath.UniStr.Buffer));
1810 SUP_DPRINTF(("WinSxS: %ls\n", g_WinSxSNtPath.UniStr.Buffer));
1811#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
1812 supHardenedWinInitImageVerifierWinPaths();
1813#endif
1814
1815 /*
1816 * Initialize it, leaving the cleanup to the termination call.
1817 */
1818 rc = supHardNtViCertInit(&g_BuildX509Cert, g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo, "BuildCertificate");
1819 if (RT_SUCCESS(rc))
1820 rc = supHardNtViCertStoreInit(&g_hSpcRootStore, g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
1821 NULL, 0, NULL, 0, pErrInfo, "SpcRoot");
1822 if (RT_SUCCESS(rc))
1823 rc = supHardNtViCertStoreInit(&g_hNtKernelRootStore, g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
1824 NULL, 0, NULL, 0, pErrInfo, "NtKernelRoot");
1825 if (RT_SUCCESS(rc))
1826 rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelRootStore,
1827 g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
1828 g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
1829 g_aSUPTimestampTAs, g_cSUPTimestampTAs,
1830 pErrInfo, "SpcAndNtKernelRoot");
1831 if (RT_SUCCESS(rc))
1832 rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelSuppStore,
1833 NULL, 0, NULL, 0, NULL, 0,
1834 pErrInfo, "SpcAndNtKernelSupplemental");
1835
1836#if 0 /* For the time being, always trust the build certificate. It bypasses the timestamp issues of CRT and SDL. */
1837 /* If the build certificate is a test singing certificate, it must be a
1838 trusted root or we'll fail to validate anything. */
1839 if ( RT_SUCCESS(rc)
1840 && RTCrX509Name_Compare(&g_BuildX509Cert.TbsCertificate.Subject, &g_BuildX509Cert.TbsCertificate.Issuer) == 0)
1841#else
1842 if (RT_SUCCESS(rc))
1843#endif
1844 rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
1845 g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo);
1846
1847 if (RT_SUCCESS(rc))
1848 {
1849 /*
1850 * Finally initialize known SIDs that we use.
1851 */
1852 SID_IDENTIFIER_AUTHORITY s_NtAuth = SECURITY_NT_AUTHORITY;
1853 NTSTATUS rcNt = RtlInitializeSid(&g_TrustedInstallerSid, &s_NtAuth, SECURITY_SERVICE_ID_RID_COUNT);
1854 if (NT_SUCCESS(rcNt))
1855 {
1856 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 0) = SECURITY_SERVICE_ID_BASE_RID;
1857 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 1) = 956008885;
1858 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 2) = 3418522649;
1859 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 3) = 1831038044;
1860 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 4) = 1853292631;
1861 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 5) = 2271478464;
1862
1863 rcNt = RtlInitializeSid(&g_LocalSystemSid, &s_NtAuth, 1);
1864 if (NT_SUCCESS(rcNt))
1865 {
1866 *RtlSubAuthoritySid(&g_LocalSystemSid, 0) = SECURITY_LOCAL_SYSTEM_RID;
1867
1868 rcNt = RtlInitializeSid(&g_AdminsGroupSid, &s_NtAuth, 2);
1869 if (NT_SUCCESS(rcNt))
1870 {
1871 *RtlSubAuthoritySid(&g_AdminsGroupSid, 0) = SECURITY_BUILTIN_DOMAIN_RID;
1872 *RtlSubAuthoritySid(&g_AdminsGroupSid, 1) = DOMAIN_ALIAS_RID_ADMINS;
1873 return VINF_SUCCESS;
1874 }
1875 }
1876 }
1877 rc = RTErrConvertFromNtStatus(rcNt);
1878 }
1879 supHardenedWinTermImageVerifier();
1880 }
1881 return rc;
1882}
1883
1884
1885/**
1886 * Releases resources allocated by supHardenedWinInitImageVerifier.
1887 */
1888DECLHIDDEN(void) supHardenedWinTermImageVerifier(void)
1889{
1890 if (RTCrX509Certificate_IsPresent(&g_BuildX509Cert))
1891 RTAsn1VtDelete(&g_BuildX509Cert.SeqCore.Asn1Core);
1892
1893 RTCrStoreRelease(g_hSpcAndNtKernelSuppStore);
1894 g_hSpcAndNtKernelSuppStore = NIL_RTCRSTORE;
1895 RTCrStoreRelease(g_hSpcAndNtKernelRootStore);
1896 g_hSpcAndNtKernelRootStore = NIL_RTCRSTORE;
1897
1898 RTCrStoreRelease(g_hNtKernelRootStore);
1899 g_hNtKernelRootStore = NIL_RTCRSTORE;
1900 RTCrStoreRelease(g_hSpcRootStore);
1901 g_hSpcRootStore = NIL_RTCRSTORE;
1902}
1903
1904#ifdef IN_RING3
1905
1906/**
1907 * This is a hardcoded list of certificates we thing we might need.
1908 *
1909 * @returns true if wanted, false if not.
1910 * @param pCert The certificate.
1911 */
1912static bool supR3HardenedWinIsDesiredRootCA(PCRTCRX509CERTIFICATE pCert)
1913{
1914 char szSubject[512];
1915 szSubject[sizeof(szSubject) - 1] = '\0';
1916 RTCrX509Name_FormatAsString(&pCert->TbsCertificate.Subject, szSubject, sizeof(szSubject) - 1, NULL);
1917
1918 /*
1919 * Check that it's a plausible root certificate.
1920 */
1921 if (!RTCrX509Certificate_IsSelfSigned(pCert))
1922 {
1923 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - not-self-signed: %s\n", szSubject));
1924 return false;
1925 }
1926
1927 if (RTAsn1Integer_UnsignedCompareWithU32(&pCert->TbsCertificate.T0.Version, 3) > 0)
1928 {
1929 if ( !(pCert->TbsCertificate.T3.fExtKeyUsage & RTCRX509CERT_KEY_USAGE_F_KEY_CERT_SIGN)
1930 && (pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE) )
1931 {
1932 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - non-cert-sign: %s\n", szSubject));
1933 return false;
1934 }
1935 if ( pCert->TbsCertificate.T3.pBasicConstraints
1936 && !pCert->TbsCertificate.T3.pBasicConstraints->CA.fValue)
1937 {
1938 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - non-CA: %s\n", szSubject));
1939 return false;
1940 }
1941 }
1942 if (pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits < 256) /* mostly for u64KeyId reading. */
1943 {
1944 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - key too small: %u bits %s\n",
1945 pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits, szSubject));
1946 return false;
1947 }
1948 uint64_t const u64KeyId = pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.uBits.pu64[1];
1949
1950# if 0
1951 /*
1952 * Whitelist - Array of names and key clues of the certificates we want.
1953 */
1954 static struct
1955 {
1956 uint64_t u64KeyId;
1957 const char *pszName;
1958 } const s_aWanted[] =
1959 {
1960 /* SPC */
1961 { UINT64_C(0xffffffffffffffff), "C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority" },
1962 { UINT64_C(0xffffffffffffffff), "L=Internet, O=VeriSign, Inc., OU=VeriSign Commercial Software Publishers CA" },
1963 { UINT64_C(0x491857ead79dde00), "C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority" },
1964
1965 /* TS */
1966 { UINT64_C(0xffffffffffffffff), "O=Microsoft Trust Network, OU=Microsoft Corporation, OU=Microsoft Time Stamping Service Root, OU=Copyright (c) 1997 Microsoft Corp." },
1967 { UINT64_C(0xffffffffffffffff), "O=VeriSign Trust Network, OU=VeriSign, Inc., OU=VeriSign Time Stamping Service Root, OU=NO LIABILITY ACCEPTED, (c)97 VeriSign, Inc." },
1968 { UINT64_C(0xffffffffffffffff), "C=ZA, ST=Western Cape, L=Durbanville, O=Thawte, OU=Thawte Certification, CN=Thawte Timestamping CA" },
1969
1970 /* Additional Windows 8.1 list: */
1971 { UINT64_C(0x5ad46780fa5df300), "DC=com, DC=microsoft, CN=Microsoft Root Certificate Authority" },
1972 { UINT64_C(0x3be670c1bd02a900), "OU=Copyright (c) 1997 Microsoft Corp., OU=Microsoft Corporation, CN=Microsoft Root Authority" },
1973 { UINT64_C(0x4d3835aa4180b200), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2011" },
1974 { UINT64_C(0x646e3fe3ba08df00), "C=US, O=MSFT, CN=Microsoft Authenticode(tm) Root Authority" },
1975 { UINT64_C(0xece4e4289e08b900), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010" },
1976 { UINT64_C(0x59faf1086271bf00), "C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2" },
1977 { UINT64_C(0x3d98ab22bb04a300), "C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root" },
1978 { UINT64_C(0x91e3728b8b40d000), "C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority" },
1979 { UINT64_C(0x61a3a33f81aace00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Object" },
1980 { 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]" },
1981 { UINT64_C(0xf4fd306318ccda00), "C=US, O=GeoTrust Inc., CN=GeoTrust Global CA" },
1982 { UINT64_C(0xa0ee62086758b15d), "C=US, O=Equifax, OU=Equifax Secure Certificate Authority" },
1983 { UINT64_C(0x8ff6fc03c1edbd00), "C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2" },
1984 { UINT64_C(0xa3ce8d99e60eda00), "C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA" },
1985 { UINT64_C(0xa671e9fec832b700), "C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority" },
1986 { UINT64_C(0xa8de7211e13be200), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA" },
1987 { 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" },
1988 { UINT64_C(0x7ae89c50f0b6a00f), "C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root" },
1989 { 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" },
1990 { 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]" },
1991 { UINT64_C(0x7c4fd32ec1b1ce00), "C=PL, O=Unizeto Sp. z o.o., CN=Certum CA" },
1992 { UINT64_C(0xd4fbe673e5ccc600), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA" },
1993 { 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" },
1994 { UINT64_C(0x6e2ba21058eedf00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN - DATACorp SGC" },
1995 { 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)" },
1996 { 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" },
1997 { UINT64_C(0x466cbc09db88c100), "C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority" },
1998 { UINT64_C(0x9259c8abe5ca713a), "L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com/, [email protected]" },
1999 { 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" },
2000 { UINT64_C(0x8043e4ce150ead00), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Assured ID Root CA" },
2001 { UINT64_C(0x00f2e6331af7b700), "C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root" },
2002 };
2003
2004
2005 uint32_t i = RT_ELEMENTS(s_aWanted);
2006 while (i-- > 0)
2007 if ( s_aWanted[i].u64KeyId == u64KeyId
2008 || s_aWanted[i].u64KeyId == UINT64_MAX)
2009 if (RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, s_aWanted[i].pszName))
2010 {
2011 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: Adding %#llx %s\n", u64KeyId, szSubject));
2012 return true;
2013 }
2014
2015 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping %#llx %s\n", u64KeyId, szSubject));
2016 return false;
2017# else
2018 /*
2019 * Blacklist approach.
2020 */
2021 static struct
2022 {
2023 uint64_t u64KeyId;
2024 const char *pszName;
2025 } const s_aUnwanted[] =
2026 {
2027 { UINT64_C(0xffffffffffffffff), "C=US, O=U.S. Robots and Mechanical Men, Inc., OU=V.I.K.I." }, /* dummy entry */
2028 };
2029
2030 uint32_t i = RT_ELEMENTS(s_aUnwanted);
2031 while (i-- > 0)
2032 if ( s_aUnwanted[i].u64KeyId == u64KeyId
2033 || s_aUnwanted[i].u64KeyId == UINT64_MAX)
2034 if (RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, s_aUnwanted[i].pszName))
2035 {
2036 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - blacklisted: %#llx %s\n", u64KeyId, szSubject));
2037 return false;
2038 }
2039
2040 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: Adding %#llx %s\n", u64KeyId, szSubject));
2041 return true;
2042# endif
2043}
2044
2045
2046/**
2047 * Loads a module in the system32 directory.
2048 *
2049 * @returns Module handle on success. Won't return on failure if fMandatory = true.
2050 * @param pszName The name of the DLL to load.
2051 * @param fMandatory Whether the library is mandatory.
2052 */
2053DECLHIDDEN(HMODULE) supR3HardenedWinLoadSystem32Dll(const char *pszName, bool fMandatory)
2054{
2055 WCHAR wszName[200+60];
2056 UINT cwcDir = GetSystemDirectoryW(wszName, RT_ELEMENTS(wszName) - 60);
2057 wszName[cwcDir] = '\\';
2058 RTUtf16CopyAscii(&wszName[cwcDir + 1], RT_ELEMENTS(wszName) - cwcDir, pszName);
2059
2060 DWORD fFlags = 0;
2061 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2062 fFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;
2063 HMODULE hMod = LoadLibraryExW(wszName, NULL, fFlags);
2064 if ( hMod == NULL
2065 && fFlags
2066 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
2067 && RtlGetLastWin32Error() == ERROR_INVALID_PARAMETER)
2068 {
2069 fFlags = 0;
2070 hMod = LoadLibraryExW(wszName, NULL, fFlags);
2071 }
2072 if ( hMod == NULL
2073 && fMandatory)
2074 supR3HardenedFatal("Error loading '%s': %u [%ls]", pszName, RtlGetLastWin32Error(), wszName);
2075 return hMod;
2076}
2077
2078
2079/**
2080 * Called by supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation to
2081 * import selected root CAs from the system certificate store.
2082 *
2083 * These certificates permits us to correctly validate third party DLLs.
2084 */
2085static void supR3HardenedWinRetrieveTrustedRootCAs(void)
2086{
2087 uint32_t cAdded = 0;
2088
2089 /*
2090 * Load crypt32.dll and resolve the APIs we need.
2091 */
2092 HMODULE hCrypt32 = supR3HardenedWinLoadSystem32Dll("crypt32.dll", true /*fMandatory*/);
2093
2094#define RESOLVE_CRYPT32_API(a_Name, a_pfnType) \
2095 a_pfnType pfn##a_Name = (a_pfnType)GetProcAddress(hCrypt32, #a_Name); \
2096 if (pfn##a_Name == NULL) supR3HardenedFatal("Error locating '" #a_Name "' in 'crypt32.dll': %u", RtlGetLastWin32Error())
2097 RESOLVE_CRYPT32_API(CertOpenStore, PFNCERTOPENSTORE);
2098 RESOLVE_CRYPT32_API(CertCloseStore, PFNCERTCLOSESTORE);
2099 RESOLVE_CRYPT32_API(CertEnumCertificatesInStore, PFNCERTENUMCERTIFICATESINSTORE);
2100#undef RESOLVE_CRYPT32_API
2101
2102 /*
2103 * Open the root store and look for the certificates we wish to use.
2104 */
2105 DWORD fOpenStore = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
2106 HCERTSTORE hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
2107 NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_LOCAL_MACHINE | fOpenStore, L"Root");
2108 if (!hStore)
2109 hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
2110 NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_CURRENT_USER | fOpenStore, L"Root");
2111 if (hStore)
2112 {
2113 PCCERT_CONTEXT pCurCtx = NULL;
2114 while ((pCurCtx = pfnCertEnumCertificatesInStore(hStore, pCurCtx)) != NULL)
2115 {
2116 if (pCurCtx->dwCertEncodingType & X509_ASN_ENCODING)
2117 {
2118 RTERRINFOSTATIC StaticErrInfo;
2119 RTASN1CURSORPRIMARY PrimaryCursor;
2120 RTAsn1CursorInitPrimary(&PrimaryCursor, pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded,
2121 RTErrInfoInitStatic(&StaticErrInfo),
2122 &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
2123 RTCRX509CERTIFICATE MyCert;
2124 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &MyCert, "Cert");
2125 if (RT_SUCCESS(rc))
2126 {
2127 if (supR3HardenedWinIsDesiredRootCA(&MyCert))
2128 {
2129 rc = RTCrStoreCertAddEncoded(g_hSpcRootStore, RTCRCERTCTX_F_ENC_X509_DER,
2130 pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
2131 AssertRC(rc);
2132
2133 rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
2134 pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
2135 AssertRC(rc);
2136 cAdded++;
2137 }
2138
2139 RTCrX509Certificate_Delete(&MyCert);
2140 }
2141 /* XP root certificate "C&W HKT SecureNet CA SGC Root" has non-standard validity
2142 timestamps, the UTC formatting isn't Zulu time but specifies timezone offsets.
2143 Ignore these failures and certificates. */
2144 else if (rc != VERR_ASN1_INVALID_UTC_TIME_ENCODING)
2145 AssertMsgFailed(("RTCrX509Certificate_DecodeAsn1 failed: rc=%#x: %s\n", rc, StaticErrInfo.szMsg));
2146 }
2147 }
2148 pfnCertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
2149 g_fHaveOtherRoots = true;
2150 }
2151 SUP_DPRINTF(("supR3HardenedWinRetrieveTrustedRootCAs: cAdded=%u\n", cAdded));
2152}
2153
2154
2155/**
2156 * Resolves the WinVerifyTrust API after the process has been verified and
2157 * installs a thread creation hook.
2158 *
2159 * The WinVerifyTrust API is used in addition our own Authenticode verification
2160 * code. If the image has the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag
2161 * set, it will be checked again by the kernel. All our image has this flag set
2162 * and we require all VBox extensions to have it set as well. In effect, the
2163 * authenticode signature will be checked two or three times.
2164 *
2165 * @param pszProgName The program name.
2166 */
2167DECLHIDDEN(void) supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(const char *pszProgName)
2168{
2169# ifdef IN_SUP_HARDENED_R3
2170 /*
2171 * Load our the support library DLL that does the thread hooking as the
2172 * security API may trigger the creation of COM worker threads (or
2173 * whatever they are).
2174 *
2175 * The thread creation hook makes the threads very slippery to debuggers by
2176 * irreversably disabling most (if not all) debug events for them.
2177 */
2178 char szPath[RTPATH_MAX];
2179 supR3HardenedPathAppSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxSupLib.DLL"));
2180 suplibHardenedStrCat(szPath, "/VBoxSupLib.DLL");
2181 HMODULE hSupLibMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, true /*fSystem32Only*/, 0 /*fMainFlags*/);
2182 if (hSupLibMod == NULL)
2183 supR3HardenedFatal("Error loading '%s': %u", szPath, RtlGetLastWin32Error());
2184# endif
2185
2186 /*
2187 * Allocate TLS entry for WinVerifyTrust recursion prevention.
2188 */
2189 DWORD iTls = TlsAlloc();
2190 if (iTls != TLS_OUT_OF_INDEXES)
2191 g_iTlsWinVerifyTrustRecursion = iTls;
2192 else
2193 supR3HardenedError(RtlGetLastWin32Error(), false /*fFatal*/, "TlsAlloc failed");
2194
2195 /*
2196 * Resolve the imports we need.
2197 */
2198 HMODULE hWintrust = supR3HardenedWinLoadSystem32Dll("Wintrust.dll", true /*fMandatory*/);
2199#define RESOLVE_CRYPT_API(a_Name, a_pfnType, a_uMinWinVer) \
2200 do { \
2201 g_pfn##a_Name = (a_pfnType)GetProcAddress(hWintrust, #a_Name); \
2202 if (g_pfn##a_Name == NULL && (a_uMinWinVer) < g_uNtVerCombined) \
2203 supR3HardenedFatal("Error locating '" #a_Name "' in 'Wintrust.dll': %u", RtlGetLastWin32Error()); \
2204 } while (0)
2205
2206 PFNWINVERIFYTRUST pfnWinVerifyTrust = (PFNWINVERIFYTRUST)GetProcAddress(hWintrust, "WinVerifyTrust");
2207 if (!pfnWinVerifyTrust)
2208 supR3HardenedFatal("Error locating 'WinVerifyTrust' in 'Wintrust.dll': %u", RtlGetLastWin32Error());
2209
2210 RESOLVE_CRYPT_API(CryptCATAdminAcquireContext, PFNCRYPTCATADMINACQUIRECONTEXT, 0);
2211 RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE, 0);
2212 RESOLVE_CRYPT_API(CryptCATAdminEnumCatalogFromHash, PFNCRYPTCATADMINENUMCATALOGFROMHASH, 0);
2213 RESOLVE_CRYPT_API(CryptCATAdminReleaseCatalogContext, PFNCRYPTCATADMINRELEASECATALOGCONTEXT, 0);
2214 RESOLVE_CRYPT_API(CryptCATAdminReleaseContext, PFNCRYPTCATDADMINRELEASECONTEXT, 0);
2215 RESOLVE_CRYPT_API(CryptCATCatalogInfoFromContext, PFNCRYPTCATCATALOGINFOFROMCONTEXT, 0);
2216
2217 RESOLVE_CRYPT_API(CryptCATAdminAcquireContext2, PFNCRYPTCATADMINACQUIRECONTEXT2, SUP_NT_VER_W80);
2218 RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle2, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2, SUP_NT_VER_W80);
2219
2220# ifdef IN_SUP_HARDENED_R3
2221 /*
2222 * Load bcrypt.dll and instantiate a few hashing and signing providers to
2223 * make sure the providers are cached for later us. Avoid recursion issues.
2224 */
2225 HMODULE hBCrypt = supR3HardenedWinLoadSystem32Dll("bcrypt.dll", false /*fMandatory*/);
2226 if (hBCrypt)
2227 {
2228 PFNBCRYPTOPENALGORTIHMPROVIDER pfnOpenAlgoProvider;
2229 pfnOpenAlgoProvider = (PFNBCRYPTOPENALGORTIHMPROVIDER)GetProcAddress(hBCrypt, "BCryptOpenAlgorithmProvider");
2230 if (pfnOpenAlgoProvider)
2231 {
2232 SUP_DPRINTF(("bcrypt.dll loaded at %p, BCryptOpenAlgorithmProvider at %p, preloading providers:\n",
2233 hBCrypt, pfnOpenAlgoProvider));
2234# define PRELOAD_ALGO_PROVIDER(a_Name) \
2235 do { \
2236 BCRYPT_ALG_HANDLE hAlgo = NULL; \
2237 NTSTATUS rcNt = pfnOpenAlgoProvider(&hAlgo, a_Name, NULL, 0); \
2238 SUP_DPRINTF(("%sBCryptOpenAlgorithmProvider(,'%ls',0,0) -> %#x (hAlgo=%p)\n", \
2239 NT_SUCCESS(rcNt) ? " " : "warning: ", a_Name, rcNt, hAlgo)); \
2240 } while (0)
2241 PRELOAD_ALGO_PROVIDER(BCRYPT_MD2_ALGORITHM);
2242 PRELOAD_ALGO_PROVIDER(BCRYPT_MD4_ALGORITHM);
2243 PRELOAD_ALGO_PROVIDER(BCRYPT_MD5_ALGORITHM);
2244 PRELOAD_ALGO_PROVIDER(BCRYPT_SHA1_ALGORITHM);
2245 PRELOAD_ALGO_PROVIDER(BCRYPT_SHA256_ALGORITHM);
2246 PRELOAD_ALGO_PROVIDER(BCRYPT_SHA512_ALGORITHM);
2247 PRELOAD_ALGO_PROVIDER(BCRYPT_RSA_ALGORITHM);
2248 PRELOAD_ALGO_PROVIDER(BCRYPT_DSA_ALGORITHM);
2249# undef PRELOAD_ALGO_PROVIDER
2250 }
2251 else
2252 SUP_DPRINTF(("Warning! Failed to find BCryptOpenAlgorithmProvider in bcrypt.dll\n"));
2253 }
2254 else
2255 SUP_DPRINTF(("Warning! Failed to load bcrypt.dll\n"));
2256
2257 /*
2258 * Call the verification API on ourselves and ntdll to make sure it works
2259 * and loads more stuff it needs, preventing any recursive fun we'd run
2260 * into after we set g_pfnWinVerifyTrust.
2261 */
2262 RTERRINFOSTATIC ErrInfoStatic;
2263 RTErrInfoInitStatic(&ErrInfoStatic);
2264 int rc = supR3HardNtViCallWinVerifyTrust(NULL, g_SupLibHardenedExeNtPath.UniStr.Buffer, 0,
2265 &ErrInfoStatic.Core, pfnWinVerifyTrust, NULL);
2266 if (RT_FAILURE(rc))
2267 supR3HardenedFatalMsg(pszProgName, kSupInitOp_Integrity, rc,
2268 "WinVerifyTrust failed on stub executable: %s", ErrInfoStatic.szMsg);
2269# else
2270 RT_NOREF1(pszProgName);
2271# endif
2272
2273 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. */
2274 supR3HardNtViCallWinVerifyTrust(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust, NULL);
2275 supR3HardNtViCallWinVerifyTrustCatFile(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
2276
2277 g_pfnWinVerifyTrust = pfnWinVerifyTrust;
2278 SUP_DPRINTF(("g_pfnWinVerifyTrust=%p\n", pfnWinVerifyTrust));
2279
2280# ifdef IN_SUP_HARDENED_R3
2281 /*
2282 * Load some problematic DLLs into the verifier cache to prevent
2283 * recursion trouble.
2284 */
2285 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\crypt32.dll");
2286 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\Wintrust.dll");
2287# endif
2288
2289 /*
2290 * Now, get trusted root CAs so we can verify a broader scope of signatures.
2291 */
2292 supR3HardenedWinRetrieveTrustedRootCAs();
2293}
2294
2295
2296static int supR3HardNtViNtToWinPath(PCRTUTF16 pwszNtName, PCRTUTF16 *ppwszWinPath,
2297 PRTUTF16 pwszWinPathBuf, size_t cwcWinPathBuf)
2298{
2299 static const RTUTF16 s_wszPrefix[] = L"\\\\.\\GLOBALROOT";
2300
2301 if (*pwszNtName != '\\' && *pwszNtName != '/')
2302 return VERR_PATH_DOES_NOT_START_WITH_ROOT;
2303
2304 size_t cwcNtName = RTUtf16Len(pwszNtName);
2305 if (RT_ELEMENTS(s_wszPrefix) + cwcNtName > cwcWinPathBuf)
2306 return VERR_FILENAME_TOO_LONG;
2307
2308 memcpy(pwszWinPathBuf, s_wszPrefix, sizeof(s_wszPrefix));
2309 memcpy(&pwszWinPathBuf[sizeof(s_wszPrefix) / sizeof(RTUTF16) - 1], pwszNtName, (cwcNtName + 1) * sizeof(RTUTF16));
2310 *ppwszWinPath = pwszWinPathBuf;
2311 return VINF_SUCCESS;
2312}
2313
2314
2315/**
2316 * Calls WinVerifyTrust to verify an PE image.
2317 *
2318 * @returns VBox status code.
2319 * @param hFile File handle to the executable file.
2320 * @param pwszName Full NT path to the DLL in question, used for
2321 * dealing with unsigned system dlls as well as for
2322 * error/logging.
2323 * @param fFlags Flags, SUPHNTVI_F_XXX.
2324 * @param pErrInfo Pointer to error info structure. Optional.
2325 * @param pfnWinVerifyTrust Pointer to the API.
2326 * @param phrcWinVerifyTrust Where to WinVerifyTrust error status on failure,
2327 * optional.
2328 */
2329static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
2330 PFNWINVERIFYTRUST pfnWinVerifyTrust, HRESULT *phrcWinVerifyTrust)
2331{
2332 RT_NOREF1(fFlags);
2333 if (phrcWinVerifyTrust)
2334 *phrcWinVerifyTrust = S_OK;
2335
2336 /*
2337 * Convert the name into a Windows name.
2338 */
2339 RTUTF16 wszWinPathBuf[MAX_PATH];
2340 PCRTUTF16 pwszWinPath;
2341 int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
2342 if (RT_FAILURE(rc))
2343 return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrust: rc=%Rrc '%ls'", rc, pwszName);
2344
2345 /*
2346 * Construct input parameters and call the API.
2347 */
2348 WINTRUST_FILE_INFO FileInfo;
2349 RT_ZERO(FileInfo);
2350 FileInfo.cbStruct = sizeof(FileInfo);
2351 FileInfo.pcwszFilePath = pwszWinPath;
2352 FileInfo.hFile = hFile;
2353
2354 GUID PolicyActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
2355
2356 WINTRUST_DATA TrustData;
2357 RT_ZERO(TrustData);
2358 TrustData.cbStruct = sizeof(TrustData);
2359 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
2360 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
2361 TrustData.dwUIChoice = WTD_UI_NONE;
2362 TrustData.dwProvFlags = 0;
2363 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2364 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
2365 else
2366 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
2367 TrustData.dwUnionChoice = WTD_CHOICE_FILE;
2368 TrustData.pFile = &FileInfo;
2369
2370 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
2371 if (hrc == S_OK)
2372 rc = VINF_SUCCESS;
2373 else
2374 {
2375 /*
2376 * Failed. Format a nice error message.
2377 */
2378# ifdef DEBUG_bird
2379 if (hrc != CERT_E_CHAINING /* Un-updated vistas, XPs, ++ */)
2380 __debugbreak();
2381# endif
2382 const char *pszErrConst = NULL;
2383 switch (hrc)
2384 {
2385 case TRUST_E_SYSTEM_ERROR: pszErrConst = "TRUST_E_SYSTEM_ERROR"; break;
2386 case TRUST_E_NO_SIGNER_CERT: pszErrConst = "TRUST_E_NO_SIGNER_CERT"; break;
2387 case TRUST_E_COUNTER_SIGNER: pszErrConst = "TRUST_E_COUNTER_SIGNER"; break;
2388 case TRUST_E_CERT_SIGNATURE: pszErrConst = "TRUST_E_CERT_SIGNATURE"; break;
2389 case TRUST_E_TIME_STAMP: pszErrConst = "TRUST_E_TIME_STAMP"; break;
2390 case TRUST_E_BAD_DIGEST: pszErrConst = "TRUST_E_BAD_DIGEST"; break;
2391 case TRUST_E_BASIC_CONSTRAINTS: pszErrConst = "TRUST_E_BASIC_CONSTRAINTS"; break;
2392 case TRUST_E_FINANCIAL_CRITERIA: pszErrConst = "TRUST_E_FINANCIAL_CRITERIA"; break;
2393 case TRUST_E_PROVIDER_UNKNOWN: pszErrConst = "TRUST_E_PROVIDER_UNKNOWN"; break;
2394 case TRUST_E_ACTION_UNKNOWN: pszErrConst = "TRUST_E_ACTION_UNKNOWN"; break;
2395 case TRUST_E_SUBJECT_FORM_UNKNOWN: pszErrConst = "TRUST_E_SUBJECT_FORM_UNKNOWN"; break;
2396 case TRUST_E_SUBJECT_NOT_TRUSTED: pszErrConst = "TRUST_E_SUBJECT_NOT_TRUSTED"; break;
2397 case TRUST_E_NOSIGNATURE: pszErrConst = "TRUST_E_NOSIGNATURE"; break;
2398 case TRUST_E_FAIL: pszErrConst = "TRUST_E_FAIL"; break;
2399 case TRUST_E_EXPLICIT_DISTRUST: pszErrConst = "TRUST_E_EXPLICIT_DISTRUST"; break;
2400 case CERT_E_EXPIRED: pszErrConst = "CERT_E_EXPIRED"; break;
2401 case CERT_E_VALIDITYPERIODNESTING: pszErrConst = "CERT_E_VALIDITYPERIODNESTING"; break;
2402 case CERT_E_ROLE: pszErrConst = "CERT_E_ROLE"; break;
2403 case CERT_E_PATHLENCONST: pszErrConst = "CERT_E_PATHLENCONST"; break;
2404 case CERT_E_CRITICAL: pszErrConst = "CERT_E_CRITICAL"; break;
2405 case CERT_E_PURPOSE: pszErrConst = "CERT_E_PURPOSE"; break;
2406 case CERT_E_ISSUERCHAINING: pszErrConst = "CERT_E_ISSUERCHAINING"; break;
2407 case CERT_E_MALFORMED: pszErrConst = "CERT_E_MALFORMED"; break;
2408 case CERT_E_UNTRUSTEDROOT: pszErrConst = "CERT_E_UNTRUSTEDROOT"; break;
2409 case CERT_E_CHAINING: pszErrConst = "CERT_E_CHAINING"; break;
2410 case CERT_E_REVOKED: pszErrConst = "CERT_E_REVOKED"; break;
2411 case CERT_E_UNTRUSTEDTESTROOT: pszErrConst = "CERT_E_UNTRUSTEDTESTROOT"; break;
2412 case CERT_E_REVOCATION_FAILURE: pszErrConst = "CERT_E_REVOCATION_FAILURE"; break;
2413 case CERT_E_CN_NO_MATCH: pszErrConst = "CERT_E_CN_NO_MATCH"; break;
2414 case CERT_E_WRONG_USAGE: pszErrConst = "CERT_E_WRONG_USAGE"; break;
2415 case CERT_E_UNTRUSTEDCA: pszErrConst = "CERT_E_UNTRUSTEDCA"; break;
2416 case CERT_E_INVALID_POLICY: pszErrConst = "CERT_E_INVALID_POLICY"; break;
2417 case CERT_E_INVALID_NAME: pszErrConst = "CERT_E_INVALID_NAME"; break;
2418 case CRYPT_E_FILE_ERROR: pszErrConst = "CRYPT_E_FILE_ERROR"; break;
2419 case CRYPT_E_REVOKED: pszErrConst = "CRYPT_E_REVOKED"; break;
2420 }
2421 if (pszErrConst)
2422 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
2423 "WinVerifyTrust failed with hrc=%s on '%ls'", pszErrConst, pwszName);
2424 else
2425 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
2426 "WinVerifyTrust failed with hrc=%Rhrc on '%ls'", hrc, pwszName);
2427 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrust: WinVerifyTrust failed with %#x (%s) on '%ls'\n",
2428 hrc, pszErrConst, pwszName));
2429 if (phrcWinVerifyTrust)
2430 *phrcWinVerifyTrust = hrc;
2431 }
2432
2433 /* clean up state data. */
2434 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
2435 FileInfo.hFile = NULL;
2436 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
2437
2438 return rc;
2439}
2440
2441
2442/**
2443 * Calls WinVerifyTrust to verify an PE image via catalog files.
2444 *
2445 * @returns VBox status code.
2446 * @param hFile File handle to the executable file.
2447 * @param pwszName Full NT path to the DLL in question, used for
2448 * dealing with unsigned system dlls as well as for
2449 * error/logging.
2450 * @param fFlags Flags, SUPHNTVI_F_XXX.
2451 * @param pErrInfo Pointer to error info structure. Optional.
2452 * @param pfnWinVerifyTrust Pointer to the API.
2453 */
2454static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
2455 PFNWINVERIFYTRUST pfnWinVerifyTrust)
2456{
2457 RT_NOREF1(fFlags);
2458 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hFile=%p pwszName=%ls\n", hFile, pwszName));
2459
2460 /*
2461 * Convert the name into a Windows name.
2462 */
2463 RTUTF16 wszWinPathBuf[MAX_PATH];
2464 PCRTUTF16 pwszWinPath;
2465 int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
2466 if (RT_FAILURE(rc))
2467 return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrustCatFile: rc=%Rrc '%ls'", rc, pwszName);
2468
2469 /*
2470 * Open the file if we didn't get a handle.
2471 */
2472 HANDLE hFileClose = NULL;
2473 if (hFile == RTNT_INVALID_HANDLE_VALUE || hFile == NULL)
2474 {
2475 hFile = RTNT_INVALID_HANDLE_VALUE;
2476 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2477
2478 UNICODE_STRING NtName;
2479 NtName.Buffer = (PWSTR)pwszName;
2480 NtName.Length = (USHORT)(RTUtf16Len(pwszName) * sizeof(WCHAR));
2481 NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
2482
2483 OBJECT_ATTRIBUTES ObjAttr;
2484 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
2485
2486 NTSTATUS rcNt = NtCreateFile(&hFile,
2487 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
2488 &ObjAttr,
2489 &Ios,
2490 NULL /* Allocation Size*/,
2491 FILE_ATTRIBUTE_NORMAL,
2492 FILE_SHARE_READ,
2493 FILE_OPEN,
2494 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
2495 NULL /*EaBuffer*/,
2496 0 /*EaLength*/);
2497 if (NT_SUCCESS(rcNt))
2498 rcNt = Ios.Status;
2499 if (!NT_SUCCESS(rcNt))
2500 return RTErrInfoSetF(pErrInfo, RTErrConvertFromNtStatus(rcNt),
2501 "NtCreateFile returned %#x opening '%ls'.", rcNt, pwszName);
2502 hFileClose = hFile;
2503 }
2504
2505 /*
2506 * On Windows 8.0 and later there are more than one digest choice.
2507 */
2508 int fNoSignedCatalogFound = -1;
2509 rc = VERR_LDRVI_NOT_SIGNED;
2510 static struct
2511 {
2512 /** The digest algorithm name. */
2513 const WCHAR *pszAlgorithm;
2514 /** Cached catalog admin handle. */
2515 HCATADMIN volatile hCachedCatAdmin;
2516 } s_aHashes[] =
2517 {
2518 { NULL, NULL },
2519 { L"SHA256", NULL },
2520 };
2521 for (uint32_t i = 0; i < RT_ELEMENTS(s_aHashes); i++)
2522 {
2523 /*
2524 * Another loop for dealing with different trust provider policies
2525 * required for successfully validating different catalog signatures.
2526 */
2527 bool fTryNextPolicy;
2528 uint32_t iPolicy = 0;
2529 static const GUID s_aPolicies[] =
2530 {
2531 DRIVER_ACTION_VERIFY, /* Works with microsoft bits. Most frequently used, thus first. */
2532 WINTRUST_ACTION_GENERIC_VERIFY_V2, /* Works with ATI and other SPC kernel-code signed stuff. */
2533 };
2534 do
2535 {
2536 /*
2537 * Create a context.
2538 */
2539 fTryNextPolicy = false;
2540 bool fFreshContext = false;
2541 BOOL fRc;
2542 HCATADMIN hCatAdmin = ASMAtomicXchgPtr(&s_aHashes[i].hCachedCatAdmin, NULL);
2543 if (hCatAdmin)
2544 {
2545 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Cached context %p\n", hCatAdmin));
2546 fFreshContext = false;
2547 fRc = TRUE;
2548 }
2549 else
2550 {
2551l_fresh_context:
2552 fFreshContext = true;
2553 if (g_pfnCryptCATAdminAcquireContext2)
2554 fRc = g_pfnCryptCATAdminAcquireContext2(&hCatAdmin, &s_aPolicies[iPolicy], s_aHashes[i].pszAlgorithm,
2555 NULL /*pStrongHashPolicy*/, 0 /*dwFlags*/);
2556 else
2557 fRc = g_pfnCryptCATAdminAcquireContext(&hCatAdmin, &s_aPolicies[iPolicy], 0 /*dwFlags*/);
2558 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: New context %p\n", hCatAdmin));
2559 }
2560 if (fRc)
2561 {
2562 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hCatAdmin=%p\n", hCatAdmin));
2563
2564 /*
2565 * Hash the file.
2566 */
2567 BYTE abHash[SUPHARDNTVI_MAX_CAT_HASH_SIZE];
2568 DWORD cbHash = sizeof(abHash);
2569 if (g_pfnCryptCATAdminCalcHashFromFileHandle2)
2570 fRc = g_pfnCryptCATAdminCalcHashFromFileHandle2(hCatAdmin, hFile, &cbHash, abHash, 0 /*dwFlags*/);
2571 else
2572 fRc = g_pfnCryptCATAdminCalcHashFromFileHandle(hFile, &cbHash, abHash, 0 /*dwFlags*/);
2573 if (fRc)
2574 {
2575 /* Produce a string version of it that we can pass to WinVerifyTrust. */
2576 RTUTF16 wszDigest[SUPHARDNTVI_MAX_CAT_HASH_SIZE * 2 + 1];
2577 int rc2 = RTUtf16PrintHexBytes(wszDigest, RT_ELEMENTS(wszDigest), abHash, cbHash, RTSTRPRINTHEXBYTES_F_UPPER);
2578 if (RT_SUCCESS(rc2))
2579 {
2580 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: cbHash=%u wszDigest=%ls\n", cbHash, wszDigest));
2581
2582 /*
2583 * Enumerate catalog information that matches the hash.
2584 */
2585 uint32_t iCat = 0;
2586 HCATINFO hCatInfoPrev = NULL;
2587 do
2588 {
2589 /* Get the next match. */
2590 HCATINFO hCatInfo = g_pfnCryptCATAdminEnumCatalogFromHash(hCatAdmin, abHash, cbHash, 0, &hCatInfoPrev);
2591 if (!hCatInfo)
2592 {
2593 if (!fFreshContext)
2594 {
2595 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Retrying with fresh context (CryptCATAdminEnumCatalogFromHash -> %u; iCat=%#x)\n", RtlGetLastWin32Error(), iCat));
2596 if (hCatInfoPrev != NULL)
2597 g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/);
2598 g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/);
2599 goto l_fresh_context;
2600 }
2601 ULONG ulErr = RtlGetLastWin32Error();
2602 fNoSignedCatalogFound = ulErr == ERROR_NOT_FOUND && fNoSignedCatalogFound != 0;
2603 if (iCat == 0)
2604 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed ERROR_NOT_FOUND (%u)\n", ulErr));
2605 else if (iCat == 0)
2606 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed %u\n", ulErr));
2607 break;
2608 }
2609 fNoSignedCatalogFound = 0;
2610 Assert(hCatInfoPrev == NULL);
2611 hCatInfoPrev = hCatInfo;
2612
2613 /*
2614 * Call WinVerifyTrust.
2615 */
2616 CATALOG_INFO CatInfo;
2617 CatInfo.cbStruct = sizeof(CatInfo);
2618 CatInfo.wszCatalogFile[0] = '\0';
2619 if (g_pfnCryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0 /*dwFlags*/))
2620 {
2621 WINTRUST_CATALOG_INFO WtCatInfo;
2622 RT_ZERO(WtCatInfo);
2623 WtCatInfo.cbStruct = sizeof(WtCatInfo);
2624 WtCatInfo.dwCatalogVersion = 0;
2625 WtCatInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
2626 WtCatInfo.pcwszMemberTag = wszDigest;
2627 WtCatInfo.pcwszMemberFilePath = pwszWinPath;
2628 WtCatInfo.pbCalculatedFileHash = abHash;
2629 WtCatInfo.cbCalculatedFileHash = cbHash;
2630 WtCatInfo.pcCatalogContext = NULL;
2631
2632 WINTRUST_DATA TrustData;
2633 RT_ZERO(TrustData);
2634 TrustData.cbStruct = sizeof(TrustData);
2635 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
2636 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
2637 TrustData.dwUIChoice = WTD_UI_NONE;
2638 TrustData.dwProvFlags = 0;
2639 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2640 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
2641 else
2642 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
2643 TrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
2644 TrustData.pCatalog = &WtCatInfo;
2645
2646 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
2647 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: WinVerifyTrust => %#x; cat='%ls'; file='%ls'\n",
2648 hrc, CatInfo.wszCatalogFile, pwszName));
2649
2650 if (SUCCEEDED(hrc))
2651 rc = VINF_SUCCESS;
2652 else if (hrc == TRUST_E_NOSIGNATURE)
2653 { /* ignore because it's useless. */ }
2654 else if (hrc == ERROR_INVALID_PARAMETER)
2655 { /* This is returned if the given file isn't found in the catalog, it seems. */ }
2656 else
2657 {
2658 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_WINTRUST_CAT_FAILURE,
2659 "WinVerifyTrust failed with hrc=%#x on '%ls' and .cat-file='%ls'.",
2660 hrc, pwszWinPath, CatInfo.wszCatalogFile);
2661 fTryNextPolicy |= (hrc == CERT_E_UNTRUSTEDROOT);
2662 }
2663
2664 /* clean up state data. */
2665 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
2666 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
2667 Assert(SUCCEEDED(hrc));
2668 }
2669 else
2670 {
2671 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2672 "CryptCATCatalogInfoFromContext failed: %d [file=%s]",
2673 RtlGetLastWin32Error(), pwszName);
2674 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATCatalogInfoFromContext failed\n"));
2675 }
2676 iCat++;
2677 } while (rc == VERR_LDRVI_NOT_SIGNED && iCat < 128);
2678
2679 if (hCatInfoPrev != NULL)
2680 if (!g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/))
2681 AssertFailed();
2682 }
2683 else
2684 rc = RTErrInfoSetF(pErrInfo, rc2, "RTUtf16PrintHexBytes failed: %Rrc", rc);
2685 }
2686 else
2687 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2688 "CryptCATAdminCalcHashFromFileHandle[2] failed: %d [file=%s]", RtlGetLastWin32Error(), pwszName);
2689
2690 if (!ASMAtomicCmpXchgPtr(&s_aHashes[i].hCachedCatAdmin, hCatAdmin, NULL))
2691 if (!g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/))
2692 AssertFailed();
2693 }
2694 else
2695 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
2696 "CryptCATAdminAcquireContext[2] failed: %d [file=%s]", RtlGetLastWin32Error(), pwszName);
2697 iPolicy++;
2698 } while ( fTryNextPolicy
2699 && iPolicy < RT_ELEMENTS(s_aPolicies));
2700
2701 /*
2702 * Only repeat if we've got g_pfnCryptCATAdminAcquireContext2 and can specify the hash algorithm.
2703 */
2704 if (!g_pfnCryptCATAdminAcquireContext2)
2705 break;
2706 if (rc != VERR_LDRVI_NOT_SIGNED)
2707 break;
2708 }
2709
2710 if (hFileClose != NULL)
2711 NtClose(hFileClose);
2712
2713 /*
2714 * DLLs that are likely candidates for local modifications.
2715 */
2716 if (rc == VERR_LDRVI_NOT_SIGNED)
2717 {
2718 bool fCoreSystemDll = false;
2719 PCRTUTF16 pwsz;
2720 uint32_t cwcName = (uint32_t)RTUtf16Len(pwszName);
2721 uint32_t cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR);
2722 if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_System32NtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
2723 {
2724 pwsz = pwszName + cwcOther + 1;
2725 if ( supHardViUtf16PathIsEqual(pwsz, "uxtheme.dll")
2726 || supHardViUtf16PathIsEqual(pwsz, "user32.dll")
2727 || supHardViUtf16PathIsEqual(pwsz, "gdi32.dll")
2728 || supHardViUtf16PathIsEqual(pwsz, "opengl32.dll")
2729 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "KernelBase.dll"))
2730 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "kernel32.dll"))
2731 || (fCoreSystemDll = supHardViUtf16PathIsEqual(pwsz, "ntdll.dll"))
2732 )
2733 {
2734 if (RTErrInfoIsSet(pErrInfo))
2735 RTErrInfoAdd(pErrInfo, rc, "\n");
2736 RTErrInfoAddF(pErrInfo, rc, "'%ls' is most likely modified.", pwszName);
2737 }
2738 }
2739
2740 /* Kludge for ancient windows versions we don't want to support but
2741 users still wants to use. Keep things as safe as possible without
2742 unnecessary effort. Problem is that 3rd party catalog files cannot
2743 easily be found. Showstopper for ATI users. */
2744 if ( fNoSignedCatalogFound == 1
2745 && g_uNtVerCombined < SUP_NT_VER_VISTA
2746 && !fCoreSystemDll)
2747 {
2748 rc = VINF_LDRVI_NOT_SIGNED;
2749 }
2750 }
2751
2752 return rc;
2753}
2754
2755
2756/**
2757 * Verifies the given image using WinVerifyTrust in some way.
2758 *
2759 * This is used by supHardenedWinVerifyImageByLdrMod as well as
2760 * supR3HardenedScreenImage.
2761 *
2762 * @returns IPRT status code, modified @a rc.
2763 * @param hFile Handle of the file to verify.
2764 * @param pwszName Full NT path to the DLL in question, used for
2765 * dealing with unsigned system dlls as well as for
2766 * error/logging.
2767 * @param fFlags SUPHNTVI_F_XXX.
2768 * @param rc The current status code.
2769 * @param pfWinVerifyTrust Where to return whether WinVerifyTrust was
2770 * actually used.
2771 * @param pErrInfo Pointer to error info structure. Optional.
2772 */
2773DECLHIDDEN(int) supHardenedWinVerifyImageTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, int rc,
2774 bool *pfWinVerifyTrust, PRTERRINFO pErrInfo)
2775{
2776 if (pfWinVerifyTrust)
2777 *pfWinVerifyTrust = false;
2778
2779 /*
2780 * Call the windows verify trust API if we've resolved it and aren't in
2781 * some obvious recursion.
2782 */
2783 if (g_pfnWinVerifyTrust != NULL)
2784 {
2785 uint32_t const idCurrentThread = RTNtCurrentThreadId();
2786
2787 /* Check if loader lock owner. */
2788 struct _RTL_CRITICAL_SECTION volatile *pLoaderLock = NtCurrentPeb()->LoaderLock;
2789 bool fOwnsLoaderLock = pLoaderLock
2790 && pLoaderLock->OwningThread == (HANDLE)(uintptr_t)idCurrentThread
2791 && pLoaderLock->RecursionCount > 0;
2792 if (!fOwnsLoaderLock)
2793 {
2794 /* Check for recursion. */
2795 bool fNoRecursion;
2796 if (g_iTlsWinVerifyTrustRecursion != UINT32_MAX)
2797 {
2798 fNoRecursion = TlsGetValue(g_iTlsWinVerifyTrustRecursion) == 0;
2799 if (fNoRecursion)
2800 TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)1);
2801 }
2802 else
2803 fNoRecursion = ASMAtomicCmpXchgU32(&g_idActiveThread, idCurrentThread, UINT32_MAX);
2804
2805 if (fNoRecursion && !fOwnsLoaderLock)
2806 {
2807 /* We can call WinVerifyTrust. */
2808 if (pfWinVerifyTrust)
2809 *pfWinVerifyTrust = true;
2810
2811 if (rc != VERR_LDRVI_NOT_SIGNED)
2812 {
2813 if (rc == VINF_LDRVI_NOT_SIGNED)
2814 {
2815 if (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION)
2816 {
2817 int rc2 = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo,
2818 g_pfnWinVerifyTrust);
2819 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (org %d)\n", rc2, rc));
2820 rc = rc2;
2821 }
2822 else
2823 {
2824 AssertFailed();
2825 rc = VERR_LDRVI_NOT_SIGNED;
2826 }
2827 }
2828 else if (RT_SUCCESS(rc))
2829 {
2830 HRESULT hrcWinVerifyTrust;
2831 rc = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust,
2832 &hrcWinVerifyTrust);
2833
2834 /* DLLs signed with special roots, like "Microsoft Digital Media Authority 2005",
2835 may fail here because the root cert is not in the normal certificate stores
2836 (if any). Our verification code has the basics of these certificates included
2837 and can verify them, which is why we end up here instead of in the
2838 VINF_LDRVI_NOT_SIGNED case above. Current workaround is to do as above.
2839 (Intel graphics driver DLLs, like igdusc64.dll. */
2840 if ( RT_FAILURE(rc)
2841 && hrcWinVerifyTrust == CERT_E_CHAINING
2842 && (fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION))
2843 {
2844 rc = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust);
2845 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (was CERT_E_CHAINING)\n", rc));
2846 }
2847 }
2848 else
2849 {
2850 int rc2 = supR3HardNtViCallWinVerifyTrust(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust, NULL);
2851 AssertMsg(RT_FAILURE_NP(rc2),
2852 ("rc=%Rrc, rc2=%Rrc %s", rc, rc2, pErrInfo ? pErrInfo->pszMsg : "<no-err-info>"));
2853 RT_NOREF_PV(rc2);
2854 }
2855 }
2856
2857 /* Unwind recursion. */
2858 if (g_iTlsWinVerifyTrustRecursion != UINT32_MAX)
2859 TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)0);
2860 else
2861 ASMAtomicWriteU32(&g_idActiveThread, UINT32_MAX);
2862 }
2863 /*
2864 * No can do.
2865 */
2866 else
2867 SUP_DPRINTF(("Detected WinVerifyTrust recursion: rc=%Rrc '%ls'.\n", rc, pwszName));
2868 }
2869 else
2870 SUP_DPRINTF(("Detected loader lock ownership: rc=%Rrc '%ls'.\n", rc, pwszName));
2871 }
2872 return rc;
2873}
2874
2875
2876/**
2877 * Checks if WinVerifyTrust is callable on the current thread.
2878 *
2879 * Used by the main code to figure whether it makes sense to try revalidate an
2880 * image that hasn't passed thru WinVerifyTrust yet.
2881 *
2882 * @returns true if callable on current thread, false if not.
2883 */
2884DECLHIDDEN(bool) supHardenedWinIsWinVerifyTrustCallable(void)
2885{
2886 return g_pfnWinVerifyTrust != NULL
2887 && ( g_iTlsWinVerifyTrustRecursion != UINT32_MAX
2888 ? (uintptr_t)TlsGetValue(g_iTlsWinVerifyTrustRecursion) == 0
2889 : g_idActiveThread != RTNtCurrentThreadId() );
2890}
2891
2892
2893
2894/**
2895 * Initializes g_uNtVerCombined and g_NtVerInfo.
2896 * Called from suplibHardenedWindowsMain and suplibOsInit.
2897 */
2898DECLHIDDEN(void) supR3HardenedWinInitVersion(bool fEarly)
2899{
2900 /*
2901 * Get the windows version. Use RtlGetVersion as GetVersionExW and
2902 * GetVersion might not be telling the whole truth (8.0 on 8.1 depending on
2903 * the application manifest).
2904 *
2905 * Note! Windows 10 build 14267+ touches BSS when calling RtlGetVersion, so we
2906 * have to use the fallback for the call from the early init code.
2907 */
2908 OSVERSIONINFOEXW NtVerInfo;
2909
2910 RT_ZERO(NtVerInfo);
2911 NtVerInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
2912 if ( fEarly
2913 || !NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&NtVerInfo)))
2914 {
2915 RT_ZERO(NtVerInfo);
2916 PPEB pPeb = NtCurrentPeb();
2917 NtVerInfo.dwMajorVersion = pPeb->OSMajorVersion;
2918 NtVerInfo.dwMinorVersion = pPeb->OSMinorVersion;
2919 NtVerInfo.dwBuildNumber = pPeb->OSBuildNumber;
2920 }
2921
2922 g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(NtVerInfo.dwMajorVersion, NtVerInfo.dwMinorVersion, NtVerInfo.dwBuildNumber,
2923 NtVerInfo.wServicePackMajor, NtVerInfo.wServicePackMinor);
2924}
2925
2926#endif /* IN_RING3 */
2927
Note: See TracBrowser for help on using the repository browser.

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