VirtualBox

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

Last change on this file since 64883 was 64883, checked in by vboxsync, 7 years ago

IPRT/ASN.1: Refactored array handling (SET OF, SEQUENCE OF) to use a pointer array instead of an object instance array. The old approach would move objects around in memory after they'd be initialized/decoded, making certain core optimziations involving pointers to object members impossible, as well as causing potentially causing trouble when modifying structures that takes down pointers after decoding. Fixed validation bug in rtCrX509Name_CheckSanityExtra where it didn't check that the RDNs had subitems but instead checked the parent twice (slight risk).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use