VirtualBox

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

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

Windows hardening: bugref:8750: blacklist scrobj.dll

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

© 2023 Oracle
ContactPrivacy policyTerms of Use