VirtualBox

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

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

scm --update-copyright-year

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

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