VirtualBox

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

Last change on this file since 92270 was 91984, checked in by vboxsync, 3 years ago

SUPHardNt: Just continue validating signatures upon encountering VERR_CR_X509_CPV_UNKNOWN_CRITICAL_EXTENSION and VERR_SUP_VP_NOT_VALID_KERNEL_CODE_SIGNATURE. Fixes igdumdim64.dll problem. bugref:10130

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