VirtualBox

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

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

IPRT/ldr/asn1/pkcs7: Ironed out issues in decoding indefinite ASN.1 length records and successfully verified the first Mach-O signature. bugref:9232 [bug fix]

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

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