VirtualBox

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

Last change on this file since 52375 was 52375, checked in by vboxsync, 10 years ago

SUP: Some cleanup and bug hacking.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 102.2 KB
Line 
1/* $Id: SUPHardenedVerifyImage-win.cpp 52375 2014-08-14 01:25:12Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library/Driver - Hardened Image Verification, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2014 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* Header Files *
29*******************************************************************************/
30#ifdef IN_RING0
31# define IPRT_NT_MAP_TO_ZW
32# include <iprt/nt/nt.h>
33# include <ntimage.h>
34#else
35# include <iprt/nt/nt-and-windows.h>
36# include "Wintrust.h"
37# include "Softpub.h"
38# include "mscat.h"
39# ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
40# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
41# endif
42#endif
43
44#include <VBox/sup.h>
45#include <VBox/err.h>
46#include <iprt/ctype.h>
47#include <iprt/ldr.h>
48#include <iprt/log.h>
49#include <iprt/path.h>
50#include <iprt/string.h>
51#include <iprt/crypto/pkcs7.h>
52#include <iprt/crypto/store.h>
53
54#ifdef IN_RING0
55# include "SUPDrvInternal.h"
56#else
57# include "SUPLibInternal.h"
58#endif
59#include "win/SUPHardenedVerify-win.h"
60
61
62/*******************************************************************************
63* Defined Constants And Macros *
64*******************************************************************************/
65/** The size of static hash (output) buffers.
66 * Avoids dynamic allocations and cleanups for of small buffers as well as extra
67 * calls for getting the appropriate buffer size. The largest digest in regular
68 * use by current windows version is SHA-512, we double this and hope it's
69 * enough a good while. */
70#define SUPHARDNTVI_MAX_CAT_HASH_SIZE 128
71
72
73/*******************************************************************************
74* Structures and Typedefs *
75*******************************************************************************/
76
77#ifdef IN_RING3
78typedef LONG (WINAPI * PFNWINVERIFYTRUST)(HWND hwnd, GUID const *pgActionID, PVOID pWVTData);
79typedef BOOL (WINAPI * PFNCRYPTCATADMINACQUIRECONTEXT)(HCATADMIN *phCatAdmin, const GUID *pGuidSubsystem, DWORD dwFlags);
80typedef BOOL (WINAPI * PFNCRYPTCATADMINACQUIRECONTEXT2)(HCATADMIN *phCatAdmin, const GUID *pGuidSubsystem, PCWSTR pwszHashAlgorithm,
81 struct _CERT_STRONG_SIGN_PARA const *pStrongHashPolicy, DWORD dwFlags);
82typedef BOOL (WINAPI * PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE)(HANDLE hFile, DWORD *pcbHash, BYTE *pbHash, DWORD dwFlags);
83typedef BOOL (WINAPI * PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2)(HCATADMIN hCatAdmin, HANDLE hFile, DWORD *pcbHash,
84 BYTE *pbHash, DWORD dwFlags);
85typedef HCATINFO (WINAPI *PFNCRYPTCATADMINENUMCATALOGFROMHASH)(HCATADMIN hCatAdmin, BYTE *pbHash, DWORD cbHash,
86 DWORD dwFlags, HCATINFO *phPrevCatInfo);
87typedef BOOL (WINAPI * PFNCRYPTCATADMINRELEASECATALOGCONTEXT)(HCATADMIN hCatAdmin, HCATINFO hCatInfo, DWORD dwFlags);
88typedef BOOL (WINAPI * PFNCRYPTCATDADMINRELEASECONTEXT)(HCATADMIN hCatAdmin, DWORD dwFlags);
89typedef BOOL (WINAPI * PFNCRYPTCATCATALOGINFOFROMCONTEXT)(HCATINFO hCatInfo, CATALOG_INFO *psCatInfo, DWORD dwFlags);
90
91typedef HCERTSTORE (WINAPI *PFNCERTOPENSTORE)(PCSTR pszStoreProvider, DWORD dwEncodingType, HCRYPTPROV_LEGACY hCryptProv,
92 DWORD dwFlags, const void *pvParam);
93typedef BOOL (WINAPI *PFNCERTCLOSESTORE)(HCERTSTORE hCertStore, DWORD dwFlags);
94typedef PCCERT_CONTEXT (WINAPI *PFNCERTENUMCERTIFICATESINSTORE)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
95#endif
96
97
98/*******************************************************************************
99* Global Variables *
100*******************************************************************************/
101/** The build certificate. */
102static RTCRX509CERTIFICATE g_BuildX509Cert;
103
104/** Store for root software publisher certificates. */
105static RTCRSTORE g_hSpcRootStore = NIL_RTCRSTORE;
106/** Store for root NT kernel certificates. */
107static RTCRSTORE g_hNtKernelRootStore = NIL_RTCRSTORE;
108
109/** Store containing SPC, NT kernel signing, and timestamp root certificates. */
110static RTCRSTORE g_hSpcAndNtKernelRootStore = NIL_RTCRSTORE;
111/** Store for supplemental certificates for use with
112 * g_hSpcAndNtKernelRootStore. */
113static RTCRSTORE g_hSpcAndNtKernelSuppStore = NIL_RTCRSTORE;
114
115/** The full \\SystemRoot\\System32 path. */
116SUPSYSROOTDIRBUF g_System32NtPath;
117/** The full \\SystemRoot\\WinSxS path. */
118SUPSYSROOTDIRBUF g_WinSxSNtPath;
119#ifdef IN_RING3
120/** The full 'Program Files' path. */
121SUPSYSROOTDIRBUF g_ProgramFilesNtPath;
122# ifdef RT_ARCH_AMD64
123/** The full 'Program Files (x86)' path. */
124SUPSYSROOTDIRBUF g_ProgramFilesX86NtPath;
125# endif
126/** The full 'Common Files' path. */
127SUPSYSROOTDIRBUF g_CommonFilesNtPath;
128# ifdef RT_ARCH_AMD64
129/** The full 'Common Files (x86)' path. */
130SUPSYSROOTDIRBUF g_CommonFilesX86NtPath;
131# endif
132#endif /* IN_RING3 */
133
134/** The TrustedInstaller SID (Vista+). */
135static union
136{
137 SID Sid;
138 uint8_t abPadding[SECURITY_MAX_SID_SIZE];
139} g_TrustedInstallerSid;
140
141/** Set after we've retrived other SPC root certificates from the system. */
142static bool g_fHaveOtherRoots = false;
143
144#if defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
145/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED and
146 * SUP_MAKE_NT_VER_SIMPLE. */
147uint32_t g_uNtVerCombined;
148#endif
149
150#ifdef IN_RING3
151/** Timestamp hack working around issues with old DLLs that we ship.
152 * See supHardenedWinVerifyImageByHandle() for details. */
153static uint64_t g_uBuildTimestampHack = 0;
154#endif
155
156#ifdef IN_RING3
157/** Pointer to WinVerifyTrust. */
158PFNWINVERIFYTRUST g_pfnWinVerifyTrust;
159/** Pointer to CryptCATAdminAcquireContext. */
160PFNCRYPTCATADMINACQUIRECONTEXT g_pfnCryptCATAdminAcquireContext;
161/** Pointer to CryptCATAdminAcquireContext2 if available. */
162PFNCRYPTCATADMINACQUIRECONTEXT2 g_pfnCryptCATAdminAcquireContext2;
163/** Pointer to CryptCATAdminCalcHashFromFileHandle. */
164PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE g_pfnCryptCATAdminCalcHashFromFileHandle;
165/** Pointer to CryptCATAdminCalcHashFromFileHandle2. */
166PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2 g_pfnCryptCATAdminCalcHashFromFileHandle2;
167/** Pointer to CryptCATAdminEnumCatalogFromHash. */
168PFNCRYPTCATADMINENUMCATALOGFROMHASH g_pfnCryptCATAdminEnumCatalogFromHash;
169/** Pointer to CryptCATAdminReleaseCatalogContext. */
170PFNCRYPTCATADMINRELEASECATALOGCONTEXT g_pfnCryptCATAdminReleaseCatalogContext;
171/** Pointer to CryptCATAdminReleaseContext. */
172PFNCRYPTCATDADMINRELEASECONTEXT g_pfnCryptCATAdminReleaseContext;
173/** Pointer to CryptCATCatalogInfoFromContext. */
174PFNCRYPTCATCATALOGINFOFROMCONTEXT g_pfnCryptCATCatalogInfoFromContext;
175#endif
176
177
178/*******************************************************************************
179* Internal Functions *
180*******************************************************************************/
181#ifdef IN_RING3
182static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
183 PFNWINVERIFYTRUST pfnWinVerifyTrust);
184static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
185 PFNWINVERIFYTRUST pfnWinVerifyTrust);
186#endif
187
188
189
190
191/** @copydoc RTLDRREADER::pfnRead */
192static DECLCALLBACK(int) supHardNtViRdrRead(PRTLDRREADER pReader, void *pvBuf, size_t cb, RTFOFF off)
193{
194 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
195 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
196
197 if ((ULONG)cb != cb)
198 return VERR_OUT_OF_RANGE;
199
200
201 /*
202 * For some reason I'm getting occational read error in an XP VM with
203 * STATUS_FAILED_DRIVER_ENTRY. Redoing the call again works in the
204 * debugger, so try do that automatically.
205 */
206 for (uint32_t iTry = 0;; iTry++)
207 {
208 LARGE_INTEGER offNt;
209 offNt.QuadPart = off;
210
211 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
212 NTSTATUS rcNt = NtReadFile(pNtViRdr->hFile,
213 NULL /*hEvent*/,
214 NULL /*ApcRoutine*/,
215 NULL /*ApcContext*/,
216 &Ios,
217 pvBuf,
218 (ULONG)cb,
219 &offNt,
220 NULL);
221 if (NT_SUCCESS(rcNt))
222 rcNt = Ios.Status;
223 if (NT_SUCCESS(rcNt))
224 {
225 if (Ios.Information == cb)
226 {
227 pNtViRdr->off = off + cb;
228 return VINF_SUCCESS;
229 }
230#ifdef IN_RING3
231 supR3HardenedError(VERR_READ_ERROR, false,
232 "supHardNtViRdrRead: Only got %#zx bytes when requesting %#zx bytes at %#llx in '%s'.\n",
233 Ios.Information, off, cb, pNtViRdr->szFilename);
234#endif
235 pNtViRdr->off = -1;
236 return VERR_READ_ERROR;
237 }
238
239 /*
240 * Delay a little before we retry?
241 */
242#ifdef IN_RING3
243 if (iTry == 0)
244 NtYieldExecution();
245 else if (iTry >= 1)
246 {
247 LARGE_INTEGER Time;
248 Time.QuadPart = -1000000 / 100; /* 1ms in 100ns units, relative time. */
249 NtDelayExecution(TRUE, &Time);
250 }
251#endif
252 /*
253 * Before we give up, we'll try split up the request in case the
254 * kernel is low on memory or similar. For simplicity reasons, we do
255 * this in a recursion fashion.
256 */
257 if (iTry >= 2)
258 {
259 if (cb >= _8K)
260 {
261 size_t const cbBlock = RT_ALIGN_Z(cb / 4, 512);
262 while (cb > 0)
263 {
264 size_t cbThisRead = RT_MIN(cb, cbBlock);
265 int rc = supHardNtViRdrRead(&pNtViRdr->Core, pvBuf, cbThisRead, off);
266 if (RT_FAILURE(rc))
267 return rc;
268 off += cbThisRead;
269 cb -= cbThisRead;
270 pvBuf = (uint8_t *)pvBuf + cbThisRead;
271 }
272 return VINF_SUCCESS;
273 }
274
275#ifdef IN_RING3
276 supR3HardenedError(VERR_READ_ERROR, false, "supHardNtViRdrRead: Error %#x reading %#zx bytes at %#llx in '%s'.\n",
277 rcNt, off, cb, pNtViRdr->szFilename);
278#endif
279 pNtViRdr->off = -1;
280 return VERR_READ_ERROR;
281 }
282 }
283}
284
285
286/** @copydoc RTLDRREADER::pfnTell */
287static DECLCALLBACK(RTFOFF) supHardNtViRdrTell(PRTLDRREADER pReader)
288{
289 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
290 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
291 return pNtViRdr->off;
292}
293
294
295/** @copydoc RTLDRREADER::pfnSize */
296static DECLCALLBACK(RTFOFF) supHardNtViRdrSize(PRTLDRREADER pReader)
297{
298 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
299 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
300 return pNtViRdr->cbFile;
301}
302
303
304/** @copydoc RTLDRREADER::pfnLogName */
305static DECLCALLBACK(const char *) supHardNtViRdrLogName(PRTLDRREADER pReader)
306{
307 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
308 return pNtViRdr->szFilename;
309}
310
311
312/** @copydoc RTLDRREADER::pfnMap */
313static DECLCALLBACK(int) supHardNtViRdrMap(PRTLDRREADER pReader, const void **ppvBits)
314{
315 return VERR_NOT_SUPPORTED;
316}
317
318
319/** @copydoc RTLDRREADER::pfnUnmap */
320static DECLCALLBACK(int) supHardNtViRdrUnmap(PRTLDRREADER pReader, const void *pvBits)
321{
322 return VERR_NOT_SUPPORTED;
323}
324
325
326/** @copydoc RTLDRREADER::pfnDestroy */
327static DECLCALLBACK(int) supHardNtViRdrDestroy(PRTLDRREADER pReader)
328{
329 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pReader;
330 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
331
332 pNtViRdr->Core.uMagic = ~RTLDRREADER_MAGIC;
333 pNtViRdr->hFile = NULL;
334
335 RTMemFree(pNtViRdr);
336 return VINF_SUCCESS;
337}
338
339
340/**
341 * Creates a loader reader instance for the given NT file handle.
342 *
343 * @returns iprt status code.
344 * @param hFile Native NT file handle.
345 * @param pwszName Optional file name.
346 * @param fFlags Flags, SUPHNTVI_F_XXX.
347 * @param ppNtViRdr Where to store the reader instance on success.
348 */
349DECLHIDDEN(int) supHardNtViRdrCreate(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PSUPHNTVIRDR *ppNtViRdr)
350{
351 /*
352 * Try determine the size of the file.
353 */
354 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
355 FILE_STANDARD_INFORMATION StdInfo;
356 NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
357 if (!NT_SUCCESS(rcNt) || !NT_SUCCESS(Ios.Status))
358 return VERR_LDRVI_FILE_LENGTH_ERROR;
359
360 /*
361 * Calc the file name length and allocate memory for the reader instance.
362 */
363 size_t cchFilename = 0;
364 if (pwszName)
365 cchFilename = RTUtf16CalcUtf8Len(pwszName);
366
367 int rc = VERR_NO_MEMORY;
368 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)RTMemAllocZ(sizeof(*pNtViRdr) + cchFilename);
369 if (!pNtViRdr)
370 return VERR_NO_MEMORY;
371
372 /*
373 * Initialize the structure.
374 */
375 if (cchFilename)
376 {
377 char *pszName = &pNtViRdr->szFilename[0];
378 rc = RTUtf16ToUtf8Ex(pwszName, RTSTR_MAX, &pszName, cchFilename + 1, NULL);
379 AssertStmt(RT_SUCCESS(rc), pNtViRdr->szFilename[0] = '\0');
380 }
381 else
382 pNtViRdr->szFilename[0] = '\0';
383
384 pNtViRdr->Core.uMagic = RTLDRREADER_MAGIC;
385 pNtViRdr->Core.pfnRead = supHardNtViRdrRead;
386 pNtViRdr->Core.pfnTell = supHardNtViRdrTell;
387 pNtViRdr->Core.pfnSize = supHardNtViRdrSize;
388 pNtViRdr->Core.pfnLogName = supHardNtViRdrLogName;
389 pNtViRdr->Core.pfnMap = supHardNtViRdrMap;
390 pNtViRdr->Core.pfnUnmap = supHardNtViRdrUnmap;
391 pNtViRdr->Core.pfnDestroy = supHardNtViRdrDestroy;
392 pNtViRdr->hFile = hFile;
393 pNtViRdr->off = 0;
394 pNtViRdr->cbFile = StdInfo.EndOfFile.QuadPart;
395 pNtViRdr->fFlags = fFlags;
396 *ppNtViRdr = pNtViRdr;
397 return VINF_SUCCESS;
398}
399
400
401/**
402 * Checks if the file is owned by TrustedInstaller on Vista and later.
403 *
404 * @returns true if owned by TrustedInstaller of pre-Vista, false if not.
405 *
406 * @param hFile The handle to the file.
407 * @param pwszName The name of the file.
408 */
409static bool supHardNtViCheckIsOwnedByTrustedInstaller(HANDLE hFile, PCRTUTF16 pwszName)
410{
411 if (g_uNtVerCombined < SUP_NT_VER_VISTA)
412 return true;
413
414 /*
415 * Get the ownership information.
416 */
417 union
418 {
419 SECURITY_DESCRIPTOR_RELATIVE Rel;
420 SECURITY_DESCRIPTOR Abs;
421 uint8_t abView[256];
422 } uBuf;
423 ULONG cbActual;
424 NTSTATUS rcNt = NtQuerySecurityObject(hFile, OWNER_SECURITY_INFORMATION, &uBuf.Abs, sizeof(uBuf), &cbActual);
425 if (!NT_SUCCESS(rcNt))
426 {
427 SUP_DPRINTF(("NtQuerySecurityObject failed with rcNt=%#x on '%ls'\n", rcNt, pwszName));
428 return false;
429 }
430
431 /*
432 * Check the owner.
433 */
434 PSID pOwner = uBuf.Rel.Control & SE_SELF_RELATIVE ? &uBuf.abView[uBuf.Rel.Owner] : uBuf.Abs.Owner;
435 Assert((uintptr_t)pOwner - (uintptr_t)&uBuf < sizeof(uBuf) - sizeof(SID));
436 if (RtlEqualSid(pOwner, &g_TrustedInstallerSid))
437 return true;
438
439 SUP_DPRINTF(("%ls: Owner is not trusted installer (%.*Rhxs)\n",
440 pwszName, ((uint8_t *)pOwner)[1] /*SubAuthorityCount*/ * sizeof(ULONG) + 8, pOwner));
441 return false;
442}
443
444
445/**
446 * Simple case insensitive UTF-16 / ASCII path compare.
447 *
448 * @returns true if equal, false if not.
449 * @param pwszLeft The UTF-16 path string.
450 * @param pszRight The ascii string.
451 */
452static bool supHardViUtf16PathIsEqual(PCRTUTF16 pwszLeft, const char *pszRight)
453{
454 for (;;)
455 {
456 RTUTF16 wc = *pwszLeft++;
457 uint8_t b = *pszRight++;
458 if (b != wc)
459 {
460 if (wc >= 0x80)
461 return false;
462 wc = RT_C_TO_LOWER(wc);
463 if (wc != b)
464 {
465 b = RT_C_TO_LOWER(b);
466 if (wc != b)
467 {
468 if (wc == '/')
469 wc = '\\';
470 if (b == '/')
471 b = '\\';
472 if (wc != b)
473 return false;
474 }
475 }
476 }
477 if (!b)
478 return true;
479 }
480}
481
482
483/**
484 * Simple case insensitive UTF-16 / ASCII ends-with path predicate.
485 *
486 * @returns true if equal, false if not.
487 * @param pwsz The UTF-16 path string.
488 * @param pszSuffix The ascii suffix string.
489 */
490static bool supHardViUtf16PathEndsWith(PCRTUTF16 pwsz, const char *pszSuffix)
491{
492 size_t cwc = RTUtf16Len(pwsz);
493 size_t cchSuffix = strlen(pszSuffix);
494 if (cwc >= cchSuffix)
495 return supHardViUtf16PathIsEqual(pwsz + cwc - cchSuffix, pszSuffix);
496 return false;
497}
498
499
500/**
501 * Simple case insensitive UTF-16 / ASCII starts-with path predicate.
502 *
503 * @returns true if starts with given string, false if not.
504 * @param pwsz The UTF-16 path string.
505 * @param pszPrefix The ascii prefix string.
506 */
507static bool supHardViUtf16PathStartsWithAscii(PCRTUTF16 pwszLeft, const char *pszRight)
508{
509 for (;;)
510 {
511 RTUTF16 wc = *pwszLeft++;
512 uint8_t b = *pszRight++;
513 if (b != wc)
514 {
515 if (!b)
516 return true;
517 if (wc >= 0x80 || wc == 0)
518 return false;
519 wc = RT_C_TO_LOWER(wc);
520 if (wc != b)
521 {
522 b = RT_C_TO_LOWER(b);
523 if (wc != b)
524 {
525 if (wc == '/')
526 wc = '\\';
527 if (b == '/')
528 b = '\\';
529 if (wc != b)
530 return false;
531 }
532 }
533 }
534 }
535}
536
537
538/**
539 * Simple case insensitive UNICODE_STRING starts-with path predicate.
540 *
541 * @returns true if starts with given string, false if not.
542 * @param pwszLeft The path to check.
543 * @param cwcLeft The length of @a pwszLeft
544 * @param pwszRight The starts-with path.
545 * @param cwcRight The length of @a pwszRight.
546 * @param fCheckSlash Check for a slash following the prefix.
547 */
548DECLHIDDEN(bool) supHardViUtf16PathStartsWithEx(PCRTUTF16 pwszLeft, uint32_t cwcLeft,
549 PCRTUTF16 pwszRight, uint32_t cwcRight, bool fCheckSlash)
550{
551 if (cwcLeft < cwcRight || !cwcRight || !pwszRight)
552 return false;
553
554 /* See if we can get away with a case sensitive compare first. */
555 if (memcmp(pwszLeft, pwszRight, cwcRight) == 0)
556 pwszLeft += cwcRight;
557 else
558 {
559 /* No luck, do a slow case insensitive comapre. */
560 uint32_t cLeft = cwcRight;
561 while (cLeft-- > 0)
562 {
563 RTUTF16 wcLeft = *pwszLeft++;
564 RTUTF16 wcRight = *pwszRight++;
565 if (wcLeft != wcRight)
566 {
567 wcLeft = wcLeft < 0x80 ? wcLeft == '/' ? '\\' : RT_C_TO_LOWER(wcLeft) : wcLeft;
568 wcRight = wcRight < 0x80 ? wcRight == '/' ? '\\' : RT_C_TO_LOWER(wcRight) : wcRight;
569 if (wcLeft != wcRight)
570 return false;
571 }
572 }
573 }
574
575 /* Check for slash following the prefix, if request. */
576 if ( !fCheckSlash
577 || *pwszLeft == '\\'
578 || *pwszLeft == '/')
579 return true;
580 return false;
581}
582
583
584/**
585 * Simple case insensitive UNICODE_STRING starts-with path predicate.
586 *
587 * @returns true if starts with given string, false if not.
588 * @param pUniStrLeft The path to check.
589 * @param pUniStrRight The starts-with path.
590 * @param fCheckSlash Check for a slash following the prefix.
591 */
592DECLHIDDEN(bool) supHardViUniStrPathStartsWithUniStr(UNICODE_STRING const *pUniStrLeft, UNICODE_STRING const *pUniStrRight,
593 bool fCheckSlash)
594{
595 return supHardViUtf16PathStartsWithEx(pUniStrLeft->Buffer, pUniStrLeft->Length / sizeof(WCHAR),
596 pUniStrRight->Buffer, pUniStrRight->Length / sizeof(WCHAR), fCheckSlash);
597}
598
599
600
601/**
602 * Counts slashes in the given UTF-8 path string.
603 *
604 * @returns Number of slashes.
605 * @param pwsz The UTF-16 path string.
606 */
607static uint32_t supHardViUtf16PathCountSlashes(PCRTUTF16 pwsz)
608{
609 uint32_t cSlashes = 0;
610 RTUTF16 wc;
611 while ((wc = *pwsz++) != '\0')
612 if (wc == '/' || wc == '\\')
613 cSlashes++;
614 return cSlashes;
615}
616
617
618#ifdef VBOX_PERMIT_MORE
619/**
620 * Checks if the path goes into %windir%\apppatch\.
621 *
622 * @returns true if apppatch, false if not.
623 * @param pwszPath The path to examine.
624 */
625DECLHIDDEN(bool) supHardViIsAppPatchDir(PCRTUTF16 pwszPath, uint32_t cwcName)
626{
627 uint32_t cwcWinDir = (g_System32NtPath.UniStr.Length - sizeof(L"System32")) / sizeof(WCHAR);
628
629 if (cwcName <= cwcWinDir + sizeof("AppPatch"))
630 return false;
631
632 if (memcmp(pwszPath, g_System32NtPath.UniStr.Buffer, cwcWinDir * sizeof(WCHAR)))
633 return false;
634
635 if (!supHardViUtf16PathStartsWithAscii(&pwszPath[cwcWinDir], "\\AppPatch\\"))
636 return false;
637
638 return g_uNtVerCombined >= SUP_NT_VER_VISTA;
639}
640#else
641# error should not get here..
642#endif
643
644
645
646/**
647 * Checks if the unsigned DLL is fine or not.
648 *
649 * @returns VINF_LDRVI_NOT_SIGNED or @a rc.
650 * @param hLdrMod The loader module handle.
651 * @param pwszName The NT name of the DLL/EXE.
652 * @param fFlags Flags.
653 * @param hFile The file handle.
654 * @param rc The status code..
655 */
656static int supHardNtViCheckIfNotSignedOk(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, uint32_t fFlags, HANDLE hFile, int rc)
657{
658 if (fFlags & (SUPHNTVI_F_REQUIRE_BUILD_CERT | SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING))
659 return rc;
660
661 /*
662 * Version macros.
663 */
664 uint32_t const uNtVer = g_uNtVerCombined;
665#define IS_XP() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(5, 1) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(5, 2) )
666#define IS_W2K3() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(5, 2) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(5, 3) )
667#define IS_VISTA() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 0) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 1) )
668#define IS_W70() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 1) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 2) )
669#define IS_W80() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 2) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 3) )
670#define IS_W81() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 3) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) )
671
672 /*
673 * The System32 directory.
674 *
675 * System32 is full of unsigned DLLs shipped by microsoft, graphics
676 * hardware vendors, input device/method vendors and whatnot else that
677 * actually needs to be loaded into a process for it to work correctly.
678 * We have to ASSUME that anything our process attempts to load from
679 * System32 is trustworthy and that the Windows system with the help of
680 * anti-virus software make sure there is nothing evil lurking in System32
681 * or being loaded from it.
682 *
683 * A small measure of protection is to list DLLs we know should be signed
684 * and decline loading unsigned versions of them, assuming they have been
685 * replaced by an adversary with evil intentions.
686 */
687 PCRTUTF16 pwsz;
688 uint32_t cwcName = (uint32_t)RTUtf16Len(pwszName);
689 uint32_t cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR);
690 if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_System32NtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
691 {
692 pwsz = pwszName + cwcOther + 1;
693
694 /* Must be owned by trusted installer. */
695 if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
696 && !supHardNtViCheckIsOwnedByTrustedInstaller(hFile, pwszName))
697 return rc;
698
699 /* Core DLLs. */
700 if (supHardViUtf16PathIsEqual(pwsz, "ntdll.dll"))
701 return uNtVer < SUP_NT_VER_VISTA ? VINF_LDRVI_NOT_SIGNED : rc;
702 if (supHardViUtf16PathIsEqual(pwsz, "kernel32.dll"))
703 return uNtVer < SUP_NT_VER_W81 ? VINF_LDRVI_NOT_SIGNED : rc;
704 if (supHardViUtf16PathIsEqual(pwsz, "kernelbase.dll"))
705 return IS_W80() || IS_W70() ? VINF_LDRVI_NOT_SIGNED : rc;
706 if (supHardViUtf16PathIsEqual(pwsz, "apisetschema.dll"))
707 return IS_W70() ? VINF_LDRVI_NOT_SIGNED : rc;
708 if (supHardViUtf16PathIsEqual(pwsz, "apphelp.dll"))
709 return uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) ? VINF_LDRVI_NOT_SIGNED : rc;
710#ifdef VBOX_PERMIT_MORE
711 if (uNtVer >= SUP_NT_VER_W70) /* hard limit: user32.dll is unwanted prior to w7. */
712 {
713 if (supHardViUtf16PathIsEqual(pwsz, "sfc.dll"))
714 return uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) ? VINF_LDRVI_NOT_SIGNED : rc;
715 if (supHardViUtf16PathIsEqual(pwsz, "sfc_os.dll"))
716 return uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) ? VINF_LDRVI_NOT_SIGNED : rc;
717 if (supHardViUtf16PathIsEqual(pwsz, "user32.dll"))
718 return uNtVer < SUP_NT_VER_W81 ? VINF_LDRVI_NOT_SIGNED : rc;
719 }
720#endif
721
722#ifndef IN_RING0
723 /* Check that this DLL isn't supposed to be signed on this windows
724 version. If it should, it's likely to be a fake. */
725 /** @todo list of signed dlls for various windows versions. */
726SUP_DPRINTF(("supHardNtViCheckIfNotSignedOk: VINF_LDRVI_NOT_SIGNED\n"));
727 return VINF_LDRVI_NOT_SIGNED;
728#else
729 return rc;
730#endif /* IN_RING0 */
731 }
732
733#ifndef IN_RING0
734 /*
735 * The WinSxS white list.
736 *
737 * Just like with System32 there are potentially a number of DLLs that
738 * could be required from WinSxS.
739 */
740 cwcOther = g_WinSxSNtPath.UniStr.Length / sizeof(WCHAR);
741 if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_WinSxSNtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
742 {
743 pwsz = pwszName + cwcOther + 1;
744 cwcName -= cwcOther + 1;
745
746 /* The WinSxS layout means everything worth loading is exactly one level down. */
747 uint32_t cSlashes = supHardViUtf16PathCountSlashes(pwsz);
748 if (cSlashes != 1)
749 return rc;
750
751 if ( (fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
752 && supHardNtViCheckIsOwnedByTrustedInstaller(hFile, pwszName))
753 return VINF_LDRVI_NOT_SIGNED;
754 return rc;
755 }
756#endif /* !IN_RING0 */
757
758#ifdef VBOX_PERMIT_MORE
759 /*
760 * AppPatch whitelist.
761 */
762 if (supHardViIsAppPatchDir(pwszName, cwcName))
763 {
764 cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR); /* ASSUMES System32 is called System32. */
765 pwsz = pwszName + cwcOther + 1;
766
767 if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
768 && !supHardNtViCheckIsOwnedByTrustedInstaller(hFile, pwszName))
769 return rc;
770
771 if (supHardViUtf16PathIsEqual(pwsz, "acres.dll"))
772 return VINF_LDRVI_NOT_SIGNED;
773
774# ifdef RT_ARCH_AMD64
775 if (supHardViUtf16PathIsEqual(pwsz, "AppPatch64\\AcGenral.dll"))
776 return VINF_LDRVI_NOT_SIGNED;
777# elif defined(RT_ARCH_X86)
778 if (supHardViUtf16PathIsEqual(pwsz, "AcGenral.dll"))
779 return VINF_LDRVI_NOT_SIGNED;
780# endif
781
782# ifndef IN_RING0
783 return VINF_LDRVI_NOT_SIGNED;
784# else
785 return rc;
786# endif
787 }
788#endif /* VBOX_PERMIT_MORE */
789
790#if !defined(IN_RING0) && defined(VBOX_PERMIT_MORE)
791 /*
792 * Program files and common files.
793 * Permit anything that's signed and correctly installed.
794 */
795 if ( supHardViUtf16PathStartsWithEx(pwszName, cwcName,
796 g_ProgramFilesNtPath.UniStr.Buffer, g_ProgramFilesNtPath.UniStr.Length,
797 true /*fCheckSlash*/)
798 || supHardViUtf16PathStartsWithEx(pwszName, cwcName,
799 g_CommonFilesNtPath.UniStr.Buffer, g_CommonFilesNtPath.UniStr.Length,
800 true /*fCheckSlash*/)
801# ifdef RT_ARCH_AMD64
802 || supHardViUtf16PathStartsWithEx(pwszName, cwcName,
803 g_ProgramFilesX86NtPath.UniStr.Buffer, g_ProgramFilesX86NtPath.UniStr.Length,
804 true /*fCheckSlash*/)
805 || supHardViUtf16PathStartsWithEx(pwszName, cwcName,
806 g_CommonFilesX86NtPath.UniStr.Buffer, g_CommonFilesX86NtPath.UniStr.Length,
807 true /*fCheckSlash*/)
808# endif
809 )
810 {
811 if ( (fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
812 && supHardNtViCheckIsOwnedByTrustedInstaller(hFile, pwszName))
813 return VINF_LDRVI_NOT_SIGNED;
814 return rc;
815 }
816#endif /* !IN_RING0 && VBOX_PERMIT_MORE*/
817
818 return rc;
819}
820
821
822/**
823 * @callback_method_impl{RTCRPKCS7VERIFYCERTCALLBACK,
824 * Standard code signing. Use this for Microsoft SPC.}
825 */
826static DECLCALLBACK(int) supHardNtViCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths,
827 void *pvUser, PRTERRINFO pErrInfo)
828{
829 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pvUser;
830 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
831
832 /*
833 * If there is no certificate path build & validator associated with this
834 * callback, it must be because of the build certificate. We trust the
835 * build certificate without any second thoughts.
836 */
837 if (hCertPaths == NIL_RTCRX509CERTPATHS)
838 {
839 if (RTCrX509Certificate_Compare(pCert, &g_BuildX509Cert) == 0) /* healthy paranoia */
840 return VINF_SUCCESS;
841 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_BUILD_CERT_IPE, "Not valid kernel code signature.");
842 }
843
844 /*
845 * Standard code signing capabilites required.
846 */
847 int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, NULL, pErrInfo);
848 if (RT_SUCCESS(rc))
849 {
850 /*
851 * If kernel signing, a valid certificate path must be anchored by the
852 * microsoft kernel signing root certificate.
853 */
854 if (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING)
855 {
856 uint32_t cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
857 uint32_t cFound = 0;
858 uint32_t cValid = 0;
859 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
860 {
861 bool fTrusted;
862 PCRTCRX509NAME pSubject;
863 PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
864 int rcVerify;
865 rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
866 NULL, NULL /*pCertCtx*/, &rcVerify);
867 AssertRCBreak(rc);
868
869 if (RT_SUCCESS(rcVerify))
870 {
871 Assert(fTrusted);
872 cValid++;
873
874 /*
875 * Search the kernel signing root store for a matching anchor.
876 */
877 RTCRSTORECERTSEARCH Search;
878 rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(g_hNtKernelRootStore, pSubject, &Search);
879 AssertRCBreak(rc);
880
881 PCRTCRCERTCTX pCertCtx;
882 while ((pCertCtx = RTCrStoreCertSearchNext(g_hNtKernelRootStore, &Search)) != NULL)
883 {
884 PCRTCRX509SUBJECTPUBLICKEYINFO pCertPubKeyInfo = NULL;
885 if (pCertCtx->pCert)
886 pCertPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
887 else if (pCertCtx->pTaInfo)
888 pCertPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
889 else
890 pCertPubKeyInfo = NULL;
891 if ( pCertPubKeyInfo
892 && RTCrX509SubjectPublicKeyInfo_Compare(pCertPubKeyInfo, pPublicKeyInfo) == 0)
893 cFound++;
894 RTCrCertCtxRelease(pCertCtx);
895 }
896
897 int rc2 = RTCrStoreCertSearchDestroy(g_hNtKernelRootStore, &Search); AssertRC(rc2);
898 }
899 }
900 if (RT_SUCCESS(rc) && cFound == 0)
901 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_VALID_KERNEL_CODE_SIGNATURE, "Not valid kernel code signature.");
902 if (RT_SUCCESS(rc) && cValid < 2 && g_fHaveOtherRoots)
903 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_UNEXPECTED_VALID_PATH_COUNT,
904 "Expected at least %u valid paths, not %u.", 2, cValid);
905 }
906 }
907
908 /*
909 * More requirements? NT5 build lab?
910 */
911
912 return rc;
913}
914
915
916static DECLCALLBACK(int) supHardNtViCallback(RTLDRMOD hLdrMod, RTLDRSIGNATURETYPE enmSignature,
917 void const *pvSignature, size_t cbSignature,
918 PRTERRINFO pErrInfo, void *pvUser)
919{
920 /*
921 * Check out the input.
922 */
923 PSUPHNTVIRDR pNtViRdr = (PSUPHNTVIRDR)pvUser;
924 Assert(pNtViRdr->Core.uMagic == RTLDRREADER_MAGIC);
925
926 AssertReturn(cbSignature == sizeof(RTCRPKCS7CONTENTINFO), VERR_INTERNAL_ERROR_5);
927 PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pvSignature;
928 AssertReturn(RTCrPkcs7ContentInfo_IsSignedData(pContentInfo), VERR_INTERNAL_ERROR_5);
929 AssertReturn(pContentInfo->u.pSignedData->SignerInfos.cItems == 1, VERR_INTERNAL_ERROR_5);
930 PCRTCRPKCS7SIGNERINFO pSignerInfo = &pContentInfo->u.pSignedData->SignerInfos.paItems[0];
931
932 /*
933 * If special certificate requirements, check them out before validating
934 * the signature.
935 */
936 if (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT)
937 {
938 if (!RTCrX509Certificate_MatchIssuerAndSerialNumber(&g_BuildX509Cert,
939 &pSignerInfo->IssuerAndSerialNumber.Name,
940 &pSignerInfo->IssuerAndSerialNumber.SerialNumber))
941 return RTErrInfoSet(pErrInfo, VERR_SUP_VP_NOT_SIGNED_WITH_BUILD_CERT, "Not signed with the build certificate.");
942 }
943
944 /*
945 * Verify the signature.
946 */
947 RTTIMESPEC ValidationTime;
948 RTTimeSpecSetSeconds(&ValidationTime, pNtViRdr->uTimestamp);
949
950 return RTCrPkcs7VerifySignedData(pContentInfo, 0, g_hSpcAndNtKernelSuppStore, g_hSpcAndNtKernelRootStore, &ValidationTime,
951 supHardNtViCertVerifyCallback, pNtViRdr, pErrInfo);
952}
953
954
955/**
956 * Verifies the given loader image.
957 *
958 * @returns IPRT status code.
959 * @param hLdrMod File handle to the executable file.
960 * @param pwszName Full NT path to the DLL in question, used for dealing
961 * with unsigned system dlls as well as for error/logging.
962 * @param pNtViRdr The reader instance /w flags.
963 * @param pfCacheable Where to return whether the result can be cached. A
964 * valid value is always returned. Optional.
965 * @param pErrInfo Pointer to error info structure. Optional.
966 */
967DECLHIDDEN(int) supHardenedWinVerifyImageByLdrMod(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, PSUPHNTVIRDR pNtViRdr,
968 bool *pfCacheable, PRTERRINFO pErrInfo)
969{
970#ifdef IN_RING3
971 /* Check that the caller has performed the necessary library initialization. */
972 if (!RTCrX509Certificate_IsPresent(&g_BuildX509Cert))
973 return RTErrInfoSet(pErrInfo, VERR_WRONG_ORDER,
974 "supHardenedWinVerifyImageByHandle: supHardenedWinInitImageVerifier was not called.");
975#endif
976
977 /*
978 * Check the trusted installer bit first, if requested as it's somewhat
979 * cheaper than the rest.
980 */
981 if ( (pNtViRdr->fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
982 && !supHardNtViCheckIsOwnedByTrustedInstaller(pNtViRdr->hFile, pwszName))
983 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_OWNED_BY_TRUSTED_INSTALLER,
984 "supHardenedWinVerifyImageByHandle: TrustedInstaller is not the owner of '%ls'.", pwszName);
985
986 /*
987 * Verify it.
988 *
989 * The PKCS #7 SignedData signature is checked in the callback. Any
990 * signing certificate restrictions are also enforced there.
991 *
992 * For the time being, we use the executable timestamp as the
993 * certificate validation date. We must query that first to avoid
994 * potential issues re-entering the loader code from the callback.
995 *
996 * Update: Save the first timestamp we validate with build cert and
997 * use this as a minimum timestamp for further build cert
998 * validations. This works around issues with old DLLs that
999 * we sign against with our certificate (crt, sdl, qt).
1000 */
1001 int rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &pNtViRdr->uTimestamp, sizeof(pNtViRdr->uTimestamp));
1002 if (RT_SUCCESS(rc))
1003 {
1004#ifdef IN_RING3 /* Hack alert! (see above) */
1005 if ( (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING)
1006 && (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT)
1007 && pNtViRdr->uTimestamp < g_uBuildTimestampHack)
1008 pNtViRdr->uTimestamp = g_uBuildTimestampHack;
1009#endif
1010
1011 rc = RTLdrVerifySignature(hLdrMod, supHardNtViCallback, pNtViRdr, pErrInfo);
1012
1013#ifdef IN_RING3 /* Hack alert! (see above) */
1014 if ((pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT) && g_uBuildTimestampHack == 0 && RT_SUCCESS(rc))
1015 g_uBuildTimestampHack = pNtViRdr->uTimestamp;
1016#endif
1017
1018 /*
1019 * Microsoft doesn't sign a whole bunch of DLLs, so we have to
1020 * ASSUME that a bunch of system DLLs are fine.
1021 */
1022 if (rc == VERR_LDRVI_NOT_SIGNED)
1023 rc = supHardNtViCheckIfNotSignedOk(hLdrMod, pwszName, pNtViRdr->fFlags, pNtViRdr->hFile, rc);
1024 if (RT_FAILURE(rc))
1025 RTErrInfoAddF(pErrInfo, rc, ": %ls", pwszName);
1026
1027 /*
1028 * Check for the signature checking enforcement, if requested to do so.
1029 */
1030 if (RT_SUCCESS(rc) && (pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_SIGNATURE_ENFORCEMENT))
1031 {
1032 bool fEnforced = false;
1033 int rc2 = RTLdrQueryProp(hLdrMod, RTLDRPROP_SIGNATURE_CHECKS_ENFORCED, &fEnforced, sizeof(fEnforced));
1034 if (RT_FAILURE(rc2))
1035 rc = RTErrInfoSetF(pErrInfo, rc2, "Querying RTLDRPROP_SIGNATURE_CHECKS_ENFORCED failed on %ls: %Rrc.",
1036 pwszName, rc2);
1037 else if (!fEnforced)
1038 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SIGNATURE_CHECKS_NOT_ENFORCED,
1039 "The image '%ls' was not linked with /IntegrityCheck.", pwszName);
1040 }
1041 }
1042 else
1043 RTErrInfoSetF(pErrInfo, rc, "RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on %ls: %Rrc", pwszName, rc);
1044
1045#ifdef IN_RING3
1046 /*
1047 * Call the windows verify trust API if we've resolved it and aren't in
1048 * some obvious recursion. Assumes the loader semaphore will reduce the
1049 * risk of concurrency here, so no TLS, only a single static variable.
1050 */
1051 if (g_pfnWinVerifyTrust)
1052 {
1053 static uint32_t volatile s_idActiveThread = UINT32_MAX;
1054 uint32_t const idCurrentThread = GetCurrentThreadId();
1055 if (s_idActiveThread != idCurrentThread)
1056 {
1057 ASMAtomicCmpXchgU32(&s_idActiveThread, idCurrentThread, UINT32_MAX);
1058
1059 if (pfCacheable)
1060 *pfCacheable = g_pfnWinVerifyTrust != NULL;
1061 if (rc != VERR_LDRVI_NOT_SIGNED)
1062 {
1063 if (rc == VINF_LDRVI_NOT_SIGNED)
1064 {
1065 if (pNtViRdr->fFlags & SUPHNTVI_F_ALLOW_CAT_FILE_VERIFICATION)
1066 {
1067 int rc2 = supR3HardNtViCallWinVerifyTrustCatFile(pNtViRdr->hFile, pwszName, pNtViRdr->fFlags, pErrInfo,
1068 g_pfnWinVerifyTrust);
1069 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile -> %d (org %d)\n", rc2, rc));
1070 rc = rc2;
1071 }
1072 else
1073 {
1074 AssertFailed();
1075 rc = VERR_LDRVI_NOT_SIGNED;
1076 }
1077 }
1078 else if (RT_SUCCESS(rc))
1079 {
1080 /** @todo having trouble with a 32-bit windows box when letting these calls thru */
1081 rc = supR3HardNtViCallWinVerifyTrust(pNtViRdr->hFile, pwszName, pNtViRdr->fFlags, pErrInfo,
1082 g_pfnWinVerifyTrust);
1083 }
1084 else
1085 {
1086 int rc2 = supR3HardNtViCallWinVerifyTrust(pNtViRdr->hFile, pwszName, pNtViRdr->fFlags, pErrInfo,
1087 g_pfnWinVerifyTrust);
1088 AssertMsg(RT_FAILURE_NP(rc2),
1089 ("rc=%Rrc, rc2=%Rrc %s", rc, rc2, pErrInfo ? pErrInfo->pszMsg : "<no-err-info>"));
1090 }
1091 }
1092
1093 ASMAtomicCmpXchgU32(&s_idActiveThread, UINT32_MAX, idCurrentThread);
1094 }
1095 else
1096 SUP_DPRINTF(("Detected WinVerifyTrust recursion: rc=%Rrc '%ls'.\n", rc, pwszName));
1097 }
1098#else /* !IN_RING3 */
1099 if (pfCacheable)
1100 *pfCacheable = true;
1101#endif /* !IN_RING3 */
1102
1103 return rc;
1104}
1105
1106
1107/**
1108 * Verifies the given executable image.
1109 *
1110 * @returns IPRT status code.
1111 * @param hFile File handle to the executable file.
1112 * @param pwszName Full NT path to the DLL in question, used for dealing
1113 * with unsigned system dlls as well as for error/logging.
1114 * @param fFlags Flags, SUPHNTVI_F_XXX.
1115 * @param pfCacheable Where to return whether the result can be cached. A
1116 * valid value is always returned. Optional.
1117 * @param pErrInfo Pointer to error info structure. Optional.
1118 */
1119DECLHIDDEN(int) supHardenedWinVerifyImageByHandle(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags,
1120 bool *pfCacheable, PRTERRINFO pErrInfo)
1121{
1122 /* Clear the cacheable indicator as it needs to be valid in all return paths. */
1123 if (pfCacheable)
1124 *pfCacheable = false;
1125
1126 /*
1127 * Create a reader instance.
1128 */
1129 PSUPHNTVIRDR pNtViRdr;
1130 int rc = supHardNtViRdrCreate(hFile, pwszName, fFlags, &pNtViRdr);
1131 if (RT_SUCCESS(rc))
1132 {
1133 /*
1134 * Open the image.
1135 */
1136 RTLDRMOD hLdrMod;
1137 RTLDRARCH enmArch = fFlags & SUPHNTVI_F_RC_IMAGE ? RTLDRARCH_X86_32 : RTLDRARCH_HOST;
1138 if (fFlags & SUPHNTVI_F_RESOURCE_IMAGE)
1139 enmArch = RTLDRARCH_WHATEVER;
1140 rc = RTLdrOpenWithReader(&pNtViRdr->Core, RTLDR_O_FOR_VALIDATION, enmArch, &hLdrMod, pErrInfo);
1141 if (RT_SUCCESS(rc))
1142 {
1143 /*
1144 * Verify it.
1145 */
1146 rc = supHardenedWinVerifyImageByLdrMod(hLdrMod, pwszName, pNtViRdr, pfCacheable, pErrInfo);
1147 int rc2 = RTLdrClose(hLdrMod); AssertRC(rc2);
1148 }
1149 else
1150 supHardNtViRdrDestroy(&pNtViRdr->Core);
1151 }
1152 SUP_DPRINTF(("supHardenedWinVerifyImageByHandle: -> %d %s(%ls)\n", rc, pfCacheable && *pfCacheable ? "cacheable ": "", pwszName));
1153 return rc;
1154}
1155
1156
1157#ifdef IN_RING3
1158/**
1159 * supHardenedWinVerifyImageByHandle version without the name.
1160 *
1161 * The name is derived from the handle.
1162 *
1163 * @returns IPRT status code.
1164 * @param hFile File handle to the executable file.
1165 * @param fFlags Flags, SUPHNTVI_F_XXX.
1166 * @param pErrInfo Pointer to error info structure. Optional.
1167 */
1168DECLHIDDEN(int) supHardenedWinVerifyImageByHandleNoName(HANDLE hFile, uint32_t fFlags, PRTERRINFO pErrInfo)
1169{
1170 /*
1171 * Determine the NT name and call the verification function.
1172 */
1173 union
1174 {
1175 UNICODE_STRING UniStr;
1176 uint8_t abBuffer[(MAX_PATH + 8 + 1) * 2];
1177 } uBuf;
1178
1179 ULONG cbIgn;
1180 NTSTATUS rcNt = NtQueryObject(hFile,
1181 ObjectNameInformation,
1182 &uBuf,
1183 sizeof(uBuf) - sizeof(WCHAR),
1184 &cbIgn);
1185 if (NT_SUCCESS(rcNt))
1186 uBuf.UniStr.Buffer[uBuf.UniStr.Length / sizeof(WCHAR)] = '\0';
1187 else
1188 uBuf.UniStr.Buffer = (WCHAR *)L"TODO3";
1189
1190 return supHardenedWinVerifyImageByHandle(hFile, uBuf.UniStr.Buffer, fFlags, NULL /*pfCacheable*/, pErrInfo);
1191}
1192#endif /* IN_RING3 */
1193
1194
1195/**
1196 * Retrieves the full official path to the system root or one of it's sub
1197 * directories.
1198 *
1199 * This code is also used by the support driver.
1200 *
1201 * @returns VBox status code.
1202 * @param pvBuf The output buffer. This will contain a
1203 * UNICODE_STRING followed (at the kernel's
1204 * discretion) the string buffer.
1205 * @param cbBuf The size of the buffer @a pvBuf points to.
1206 * @param enmDir Which directory under the system root we're
1207 * interested in.
1208 * @param pErrInfo Pointer to error info structure. Optional.
1209 */
1210DECLHIDDEN(int) supHardNtGetSystemRootDir(void *pvBuf, uint32_t cbBuf, SUPHARDNTSYSROOTDIR enmDir, PRTERRINFO pErrInfo)
1211{
1212 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
1213 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1214
1215 UNICODE_STRING NtName;
1216 switch (enmDir)
1217 {
1218 case kSupHardNtSysRootDir_System32:
1219 {
1220 static const WCHAR s_wszNameSystem32[] = L"\\SystemRoot\\System32\\";
1221 NtName.Buffer = (PWSTR)s_wszNameSystem32;
1222 NtName.Length = sizeof(s_wszNameSystem32) - sizeof(WCHAR);
1223 NtName.MaximumLength = sizeof(s_wszNameSystem32);
1224 break;
1225 }
1226 case kSupHardNtSysRootDir_WinSxS:
1227 {
1228 static const WCHAR s_wszNameWinSxS[] = L"\\SystemRoot\\WinSxS\\";
1229 NtName.Buffer = (PWSTR)s_wszNameWinSxS;
1230 NtName.Length = sizeof(s_wszNameWinSxS) - sizeof(WCHAR);
1231 NtName.MaximumLength = sizeof(s_wszNameWinSxS);
1232 break;
1233 }
1234 default:
1235 AssertFailed();
1236 return VERR_INVALID_PARAMETER;
1237 }
1238
1239 OBJECT_ATTRIBUTES ObjAttr;
1240 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1241
1242 NTSTATUS rcNt = NtCreateFile(&hFile,
1243 FILE_READ_DATA | SYNCHRONIZE,
1244 &ObjAttr,
1245 &Ios,
1246 NULL /* Allocation Size*/,
1247 FILE_ATTRIBUTE_NORMAL,
1248 FILE_SHARE_READ | FILE_SHARE_WRITE,
1249 FILE_OPEN,
1250 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
1251 NULL /*EaBuffer*/,
1252 0 /*EaLength*/);
1253 if (NT_SUCCESS(rcNt))
1254 rcNt = Ios.Status;
1255 if (NT_SUCCESS(rcNt))
1256 {
1257 ULONG cbIgn;
1258 rcNt = NtQueryObject(hFile,
1259 ObjectNameInformation,
1260 pvBuf,
1261 cbBuf - sizeof(WCHAR),
1262 &cbIgn);
1263 NtClose(hFile);
1264 if (NT_SUCCESS(rcNt))
1265 {
1266 PUNICODE_STRING pUniStr = (PUNICODE_STRING)pvBuf;
1267 if (pUniStr->Length > 0)
1268 {
1269 /* Make sure it's terminated so it can safely be printed.*/
1270 pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0';
1271 return VINF_SUCCESS;
1272 }
1273
1274 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH,
1275 "NtQueryObject returned an empty path for '%ls'", NtName.Buffer);
1276 }
1277 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "NtQueryObject failed on '%ls' dir: %#x", NtName.Buffer, rcNt);
1278 }
1279 return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "Failure to open '%ls': %#x", NtName.Buffer, rcNt);
1280}
1281
1282
1283/**
1284 * Initialize one certificate entry.
1285 *
1286 * @returns VBox status code.
1287 * @param pCert The X.509 certificate representation to init.
1288 * @param pabCert The raw DER encoded certificate.
1289 * @param cbCert The size of the raw certificate.
1290 * @param pErrInfo Where to return extended error info. Optional.
1291 * @param pszErrorTag Error tag.
1292 */
1293static int supHardNtViCertInit(PRTCRX509CERTIFICATE pCert, unsigned char const *pabCert, unsigned cbCert,
1294 PRTERRINFO pErrInfo, const char *pszErrorTag)
1295{
1296 AssertReturn(cbCert > 16 && cbCert < _128K,
1297 RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, "%s: cbCert=%#x out of range", pszErrorTag, cbCert));
1298 AssertReturn(!RTCrX509Certificate_IsPresent(pCert),
1299 RTErrInfoSetF(pErrInfo, VERR_WRONG_ORDER, "%s: Certificate already decoded?", pszErrorTag));
1300
1301 RTASN1CURSORPRIMARY PrimaryCursor;
1302 RTAsn1CursorInitPrimary(&PrimaryCursor, pabCert, cbCert, pErrInfo, &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, NULL);
1303 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, pCert, pszErrorTag);
1304 if (RT_SUCCESS(rc))
1305 rc = RTCrX509Certificate_CheckSanity(pCert, 0, pErrInfo, pszErrorTag);
1306 return rc;
1307}
1308
1309
1310static int supHardNtViCertStoreAddArray(RTCRSTORE hStore, PCSUPTAENTRY paCerts, unsigned cCerts, PRTERRINFO pErrInfo)
1311{
1312 for (uint32_t i = 0; i < cCerts; i++)
1313 {
1314 int rc = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_TAF_DER, paCerts[i].pch, paCerts[i].cb, pErrInfo);
1315 if (RT_FAILURE(rc))
1316 return rc;
1317 }
1318 return VINF_SUCCESS;
1319}
1320
1321
1322/**
1323 * Initialize a certificate table.
1324 *
1325 * @param phStore Where to return the store pointer.
1326 * @param paCerts1 Pointer to the first certificate table.
1327 * @param cCerts1 Entries in the first certificate table.
1328 * @param paCerts2 Pointer to the second certificate table.
1329 * @param cCerts2 Entries in the second certificate table.
1330 * @param paCerts3 Pointer to the third certificate table.
1331 * @param cCerts3 Entries in the third certificate table.
1332 * @param pErrInfo Where to return extended error info. Optional.
1333 * @param pszErrorTag Error tag.
1334 */
1335static int supHardNtViCertStoreInit(PRTCRSTORE phStore,
1336 PCSUPTAENTRY paCerts1, unsigned cCerts1,
1337 PCSUPTAENTRY paCerts2, unsigned cCerts2,
1338 PCSUPTAENTRY paCerts3, unsigned cCerts3,
1339 PRTERRINFO pErrInfo, const char *pszErrorTag)
1340{
1341 AssertReturn(*phStore == NIL_RTCRSTORE, VERR_WRONG_ORDER);
1342
1343 int rc = RTCrStoreCreateInMem(phStore, cCerts1 + cCerts2);
1344 if (RT_FAILURE(rc))
1345 return RTErrInfoSetF(pErrInfo, rc, "RTCrStoreCreateMemoryStore failed: %Rrc", rc);
1346
1347 rc = supHardNtViCertStoreAddArray(*phStore, paCerts1, cCerts1, pErrInfo);
1348 if (RT_SUCCESS(rc))
1349 rc = supHardNtViCertStoreAddArray(*phStore, paCerts2, cCerts2, pErrInfo);
1350 if (RT_SUCCESS(rc))
1351 rc = supHardNtViCertStoreAddArray(*phStore, paCerts3, cCerts3, pErrInfo);
1352 return rc;
1353}
1354
1355
1356
1357#ifdef IN_RING3
1358/**
1359 * Initializes the windows paths.
1360 */
1361static void supHardenedWinInitImageVerifierWinPaths(void)
1362{
1363 /*
1364 * Windows paths that we're interested in.
1365 */
1366 static const struct
1367 {
1368 SUPSYSROOTDIRBUF *pNtPath;
1369 WCHAR const *pwszRegValue;
1370 const char *pszLogName;
1371 } s_aPaths[] =
1372 {
1373 { &g_ProgramFilesNtPath, L"ProgramFilesDir", "ProgDir" },
1374 { &g_CommonFilesNtPath, L"CommonFilesDir", "ComDir" },
1375# ifdef RT_ARCH_AMD64
1376 { &g_ProgramFilesX86NtPath, L"ProgramFilesDir (x86)", "ProgDir32" },
1377 { &g_CommonFilesX86NtPath, L"CommonFilesDir (x86)", "ComDir32" },
1378# endif
1379 };
1380
1381 /*
1382 * Open the registry key containing the paths.
1383 */
1384 UNICODE_STRING NtName = RTNT_CONSTANT_UNISTR(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion");
1385 OBJECT_ATTRIBUTES ObjAttr;
1386 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1387 HANDLE hKey;
1388 NTSTATUS rcNt = NtOpenKey(&hKey, KEY_QUERY_VALUE, &ObjAttr);
1389 if (NT_SUCCESS(rcNt))
1390 {
1391 /*
1392 * Loop over the paths and resolve their NT paths.
1393 */
1394 for (uint32_t i = 0; i < RT_ELEMENTS(s_aPaths); i++)
1395 {
1396 /*
1397 * Query the value first.
1398 */
1399 UNICODE_STRING ValueName;
1400 ValueName.Buffer = (WCHAR *)s_aPaths[i].pwszRegValue;
1401 ValueName.Length = (USHORT)(RTUtf16Len(s_aPaths[i].pwszRegValue) * sizeof(WCHAR));
1402 ValueName.MaximumLength = ValueName.Length + sizeof(WCHAR);
1403
1404 union
1405 {
1406 KEY_VALUE_PARTIAL_INFORMATION PartialInfo;
1407 uint8_t abPadding[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(WCHAR) * 128];
1408 uint64_t uAlign;
1409 } uBuf;
1410
1411 ULONG cbActual = 0;
1412 rcNt = NtQueryValueKey(hKey, &ValueName, KeyValuePartialInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR), &cbActual);
1413 if (NT_SUCCESS(rcNt))
1414 {
1415 /*
1416 * Must be a simple string value, terminate it.
1417 */
1418 if ( uBuf.PartialInfo.Type == REG_EXPAND_SZ
1419 || uBuf.PartialInfo.Type == REG_SZ)
1420 {
1421 /*
1422 * Expand any environment variable references before opening it.
1423 * We use the result buffer as storage for the expaneded path,
1424 * reserving space for the windows name space prefix.
1425 */
1426 UNICODE_STRING Src;
1427 Src.Buffer = (WCHAR *)uBuf.PartialInfo.Data;
1428 Src.Length = uBuf.PartialInfo.DataLength;
1429 if (Src.Length >= sizeof(WCHAR) && Src.Buffer[Src.Length / sizeof(WCHAR) - 1] == '\0')
1430 Src.Length -= sizeof(WCHAR);
1431 Src.MaximumLength = Src.Length + sizeof(WCHAR);
1432 Src.Buffer[uBuf.PartialInfo.DataLength / sizeof(WCHAR)] = '\0';
1433
1434 s_aPaths[i].pNtPath->awcBuffer[0] = '\\';
1435 s_aPaths[i].pNtPath->awcBuffer[1] = '?';
1436 s_aPaths[i].pNtPath->awcBuffer[2] = '?';
1437 s_aPaths[i].pNtPath->awcBuffer[3] = '\\';
1438 UNICODE_STRING Dst;
1439 Dst.Buffer = &s_aPaths[i].pNtPath->awcBuffer[4];
1440 Dst.MaximumLength = sizeof(s_aPaths[i].pNtPath->awcBuffer) - sizeof(WCHAR) * 5;
1441 Dst.Length = Dst.MaximumLength;
1442
1443 if (uBuf.PartialInfo.Type == REG_EXPAND_SZ)
1444 rcNt = RtlExpandEnvironmentStrings_U(NULL, &Src, &Dst, NULL);
1445 else
1446 {
1447 memcpy(Dst.Buffer, Src.Buffer, Src.Length);
1448 Dst.Length = Src.Length;
1449 }
1450 if (NT_SUCCESS(rcNt))
1451 {
1452 Dst.Buffer[Dst.Length / sizeof(WCHAR)] = '\0';
1453
1454 /*
1455 * Include the \\??\\ prefix in the result and open the path.
1456 */
1457 Dst.Buffer -= 4;
1458 Dst.Length += 4 * sizeof(WCHAR);
1459 Dst.MaximumLength += 4 * sizeof(WCHAR);
1460 InitializeObjectAttributes(&ObjAttr, &Dst, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1461 HANDLE hFile = INVALID_HANDLE_VALUE;
1462 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1463 NTSTATUS rcNt = NtCreateFile(&hFile,
1464 FILE_READ_DATA | SYNCHRONIZE,
1465 &ObjAttr,
1466 &Ios,
1467 NULL /* Allocation Size*/,
1468 FILE_ATTRIBUTE_NORMAL,
1469 FILE_SHARE_READ | FILE_SHARE_WRITE,
1470 FILE_OPEN,
1471 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
1472 | FILE_SYNCHRONOUS_IO_NONALERT,
1473 NULL /*EaBuffer*/,
1474 0 /*EaLength*/);
1475 if (NT_SUCCESS(rcNt))
1476 rcNt = Ios.Status;
1477 if (NT_SUCCESS(rcNt))
1478 {
1479 /*
1480 * Query the real NT name.
1481 */
1482 ULONG cbIgn;
1483 rcNt = NtQueryObject(hFile,
1484 ObjectNameInformation,
1485 s_aPaths[i].pNtPath,
1486 sizeof(*s_aPaths[i].pNtPath) - sizeof(WCHAR),
1487 &cbIgn);
1488 if (NT_SUCCESS(rcNt))
1489 {
1490 if (s_aPaths[i].pNtPath->UniStr.Length > 0)
1491 {
1492 /* Make sure it's terminated.*/
1493 s_aPaths[i].pNtPath->UniStr.Buffer[s_aPaths[i].pNtPath->UniStr.Length / sizeof(WCHAR)] = '\0';
1494 SUP_DPRINTF(("%s:%*s %ls\n", s_aPaths[i].pszLogName, 9 - strlen(s_aPaths[i].pszLogName), "",
1495 s_aPaths[i].pNtPath->UniStr.Buffer));
1496 }
1497 else
1498 {
1499 SUP_DPRINTF(("%s: NtQueryObject returned empty string\n", s_aPaths[i].pszLogName));
1500 rcNt = STATUS_INVALID_PARAMETER;
1501 }
1502 }
1503 else
1504 SUP_DPRINTF(("%s: NtQueryObject failed: %#x\n", s_aPaths[i].pszLogName, rcNt));
1505 NtClose(hFile);
1506 }
1507 else
1508 SUP_DPRINTF(("%s: NtCreateFile failed: %#x (%ls)\n",
1509 s_aPaths[i].pszLogName, rcNt, Dst.Buffer));
1510 }
1511 else
1512 SUP_DPRINTF(("%s: RtlExpandEnvironmentStrings_U failed: %#x (%ls)\n",
1513 s_aPaths[i].pszLogName, rcNt, Src.Buffer));
1514 }
1515 else
1516 {
1517 SUP_DPRINTF(("%s: type mismatch: %#x\n", s_aPaths[i].pszLogName, uBuf.PartialInfo.Type));
1518 rcNt = STATUS_INVALID_PARAMETER;
1519 }
1520 }
1521 else
1522 SUP_DPRINTF(("%s: NtQueryValueKey failed: %#x\n", s_aPaths[i].pszLogName, rcNt));
1523
1524 /* Stub the entry on failure. */
1525 if (!NT_SUCCESS(rcNt))
1526 {
1527 s_aPaths[i].pNtPath->UniStr.Length = 0;
1528 s_aPaths[i].pNtPath->UniStr.Buffer = NULL;
1529 }
1530 }
1531 NtClose(hKey);
1532 }
1533 else
1534 {
1535 SUP_DPRINTF(("NtOpenKey(%ls) failed: %#x\n", NtName.Buffer, rcNt));
1536
1537 /* Stub all the entries on failure. */
1538 for (uint32_t i = 0; i < RT_ELEMENTS(s_aPaths); i++)
1539 {
1540 s_aPaths[i].pNtPath->UniStr.Length = 0;
1541 s_aPaths[i].pNtPath->UniStr.Buffer = NULL;
1542 }
1543 }
1544}
1545#endif /* IN_RING3 */
1546
1547
1548/**
1549 * This initializes the certificates globals so we don't have to reparse them
1550 * every time we need to verify an image.
1551 *
1552 * @returns IPRT status code.
1553 * @param pErrInfo Where to return extended error info. Optional.
1554 */
1555DECLHIDDEN(int) supHardenedWinInitImageVerifier(PRTERRINFO pErrInfo)
1556{
1557 AssertReturn(!RTCrX509Certificate_IsPresent(&g_BuildX509Cert), VERR_WRONG_ORDER);
1558
1559 /*
1560 * Get the system root paths.
1561 */
1562 int rc = supHardNtGetSystemRootDir(&g_System32NtPath, sizeof(g_System32NtPath), kSupHardNtSysRootDir_System32, pErrInfo);
1563 if (RT_SUCCESS(rc))
1564 rc = supHardNtGetSystemRootDir(&g_WinSxSNtPath, sizeof(g_WinSxSNtPath), kSupHardNtSysRootDir_WinSxS, pErrInfo);
1565 if (RT_SUCCESS(rc))
1566 {
1567 SUP_DPRINTF(("System32: %ls\n", g_System32NtPath.UniStr.Buffer));
1568 SUP_DPRINTF(("WinSxS: %ls\n", g_WinSxSNtPath.UniStr.Buffer));
1569#ifdef IN_RING3
1570 supHardenedWinInitImageVerifierWinPaths();
1571#endif
1572
1573 /*
1574 * Initialize it, leaving the cleanup to the termination call.
1575 */
1576 rc = supHardNtViCertInit(&g_BuildX509Cert, g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo, "BuildCertificate");
1577 if (RT_SUCCESS(rc))
1578 rc = supHardNtViCertStoreInit(&g_hSpcRootStore, g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
1579 NULL, 0, NULL, 0, pErrInfo, "SpcRoot");
1580 if (RT_SUCCESS(rc))
1581 rc = supHardNtViCertStoreInit(&g_hNtKernelRootStore, g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
1582 NULL, 0, NULL, 0, pErrInfo, "NtKernelRoot");
1583 if (RT_SUCCESS(rc))
1584 rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelRootStore,
1585 g_aSUPSpcRootTAs, g_cSUPSpcRootTAs,
1586 g_aSUPNtKernelRootTAs, g_cSUPNtKernelRootTAs,
1587 g_aSUPTimestampTAs, g_cSUPTimestampTAs,
1588 pErrInfo, "SpcAndNtKernelRoot");
1589 if (RT_SUCCESS(rc))
1590 rc = supHardNtViCertStoreInit(&g_hSpcAndNtKernelSuppStore,
1591 NULL, 0, NULL, 0, NULL, 0,
1592 pErrInfo, "SpcAndNtKernelSupplemental");
1593
1594#if 0 /* For the time being, always trust the build certificate. It bypasses the timestamp issues of CRT and SDL. */
1595 /* If the build certificate is a test singing certificate, it must be a
1596 trusted root or we'll fail to validate anything. */
1597 if ( RT_SUCCESS(rc)
1598 && RTCrX509Name_Compare(&g_BuildX509Cert.TbsCertificate.Subject, &g_BuildX509Cert.TbsCertificate.Issuer) == 0)
1599#else
1600 if (RT_SUCCESS(rc))
1601#endif
1602 rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
1603 g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo);
1604
1605 if (RT_SUCCESS(rc))
1606 {
1607 /*
1608 * Finally initialize known SIDs that we use.
1609 */
1610 SID_IDENTIFIER_AUTHORITY s_NtAuth = SECURITY_NT_AUTHORITY;
1611 NTSTATUS rcNt = RtlInitializeSid(&g_TrustedInstallerSid, &s_NtAuth, SECURITY_SERVICE_ID_RID_COUNT);
1612 if (NT_SUCCESS(rcNt))
1613 {
1614 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 0) = SECURITY_SERVICE_ID_BASE_RID;
1615 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 1) = 956008885;
1616 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 2) = 3418522649;
1617 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 3) = 1831038044;
1618 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 4) = 1853292631;
1619 *RtlSubAuthoritySid(&g_TrustedInstallerSid, 5) = 2271478464;
1620 return VINF_SUCCESS;
1621 }
1622 rc = RTErrConvertFromNtStatus(rcNt);
1623 }
1624 supHardenedWinTermImageVerifier();
1625 }
1626 return rc;
1627}
1628
1629
1630/**
1631 * Releases resources allocated by supHardenedWinInitImageVerifier.
1632 */
1633DECLHIDDEN(void) supHardenedWinTermImageVerifier(void)
1634{
1635 if (RTCrX509Certificate_IsPresent(&g_BuildX509Cert))
1636 RTAsn1VtDelete(&g_BuildX509Cert.SeqCore.Asn1Core);
1637
1638 RTCrStoreRelease(g_hSpcAndNtKernelSuppStore);
1639 g_hSpcAndNtKernelSuppStore = NIL_RTCRSTORE;
1640 RTCrStoreRelease(g_hSpcAndNtKernelRootStore);
1641 g_hSpcAndNtKernelRootStore = NIL_RTCRSTORE;
1642
1643 RTCrStoreRelease(g_hNtKernelRootStore);
1644 g_hNtKernelRootStore = NIL_RTCRSTORE;
1645 RTCrStoreRelease(g_hSpcRootStore);
1646 g_hSpcRootStore = NIL_RTCRSTORE;
1647}
1648
1649#ifdef IN_RING3
1650
1651/**
1652 * This is a hardcoded list of certificates we thing we might need.
1653 *
1654 * @returns true if wanted, false if not.
1655 * @param pCert The certificate.
1656 */
1657static bool supR3HardenedWinIsDesiredRootCA(PCRTCRX509CERTIFICATE pCert)
1658{
1659 /*
1660 * Check that it's a plausible root certificate.
1661 */
1662 if (!RTCrX509Certificate_IsSelfSigned(pCert))
1663 return false;
1664 if (RTAsn1Integer_UnsignedCompareWithU32(&pCert->TbsCertificate.T0.Version, 3) > 0)
1665 {
1666 if ( !(pCert->TbsCertificate.T3.fExtKeyUsage & RTCRX509CERT_KEY_USAGE_F_KEY_CERT_SIGN)
1667 && (pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE) )
1668 return false;
1669 if ( pCert->TbsCertificate.T3.pBasicConstraints
1670 && !pCert->TbsCertificate.T3.pBasicConstraints->CA.fValue)
1671 return false;
1672 }
1673 if (pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits < 256) /* mostly for u64KeyId reading. */
1674 return false;
1675
1676 /*
1677 * Array of names and key clues of the certificates we want.
1678 */
1679 static struct
1680 {
1681 uint64_t u64KeyId;
1682 const char *pszName;
1683 } const s_aWanted[] =
1684 {
1685 /* SPC */
1686 { UINT64_C(0xffffffffffffffff), "C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority" },
1687 { UINT64_C(0xffffffffffffffff), "L=Internet, O=VeriSign, Inc., OU=VeriSign Commercial Software Publishers CA" },
1688 { UINT64_C(0x491857ead79dde00), "C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority" },
1689
1690 /* TS */
1691 { UINT64_C(0xffffffffffffffff), "O=Microsoft Trust Network, OU=Microsoft Corporation, OU=Microsoft Time Stamping Service Root, OU=Copyright (c) 1997 Microsoft Corp." },
1692 { UINT64_C(0xffffffffffffffff), "O=VeriSign Trust Network, OU=VeriSign, Inc., OU=VeriSign Time Stamping Service Root, OU=NO LIABILITY ACCEPTED, (c)97 VeriSign, Inc." },
1693 { UINT64_C(0xffffffffffffffff), "C=ZA, ST=Western Cape, L=Durbanville, O=Thawte, OU=Thawte Certification, CN=Thawte Timestamping CA" },
1694
1695 /* Additional Windows 8.1 list: */
1696 { UINT64_C(0x5ad46780fa5df300), "DC=com, DC=microsoft, CN=Microsoft Root Certificate Authority" },
1697 { UINT64_C(0x3be670c1bd02a900), "OU=Copyright (c) 1997 Microsoft Corp., OU=Microsoft Corporation, CN=Microsoft Root Authority" },
1698 { UINT64_C(0x4d3835aa4180b200), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2011" },
1699 { UINT64_C(0x646e3fe3ba08df00), "C=US, O=MSFT, CN=Microsoft Authenticode(tm) Root Authority" },
1700 { UINT64_C(0xece4e4289e08b900), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010" },
1701 { UINT64_C(0x59faf1086271bf00), "C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2" },
1702 { UINT64_C(0x3d98ab22bb04a300), "C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root" },
1703 { UINT64_C(0x91e3728b8b40d000), "C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority" },
1704 { UINT64_C(0x61a3a33f81aace00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Object" },
1705 { 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]" },
1706 { UINT64_C(0xf4fd306318ccda00), "C=US, O=GeoTrust Inc., CN=GeoTrust Global CA" },
1707 { UINT64_C(0xa0ee62086758b15d), "C=US, O=Equifax, OU=Equifax Secure Certificate Authority" },
1708 { UINT64_C(0x8ff6fc03c1edbd00), "C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2" },
1709 { UINT64_C(0xa3ce8d99e60eda00), "C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA" },
1710 { UINT64_C(0xa671e9fec832b700), "C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority" },
1711 { UINT64_C(0xa8de7211e13be200), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA" },
1712 { 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" },
1713 { UINT64_C(0x7ae89c50f0b6a00f), "C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root" },
1714 { 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" },
1715 { 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]" },
1716 { UINT64_C(0x7c4fd32ec1b1ce00), "C=PL, O=Unizeto Sp. z o.o., CN=Certum CA" },
1717 { UINT64_C(0xd4fbe673e5ccc600), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA" },
1718 { 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" },
1719 { UINT64_C(0x6e2ba21058eedf00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN - DATACorp SGC" },
1720 { 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)" },
1721 { 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" },
1722 { UINT64_C(0x466cbc09db88c100), "C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority" },
1723 { UINT64_C(0x9259c8abe5ca713a), "L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com/, [email protected]" },
1724 { 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" },
1725 { UINT64_C(0x8043e4ce150ead00), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Assured ID Root CA" },
1726 { UINT64_C(0x00f2e6331af7b700), "C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root" },
1727 };
1728
1729
1730 uint64_t const u64KeyId = pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.uBits.pu64[1];
1731 uint32_t i = RT_ELEMENTS(s_aWanted);
1732 while (i-- > 0)
1733 if ( s_aWanted[i].u64KeyId == u64KeyId
1734 || s_aWanted[i].u64KeyId == UINT64_MAX)
1735 if (RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, s_aWanted[i].pszName))
1736 return true;
1737
1738#ifdef DEBUG_bird
1739 char szTmp[512];
1740 szTmp[sizeof(szTmp) - 1] = '\0';
1741 RTCrX509Name_FormatAsString(&pCert->TbsCertificate.Issuer, szTmp, sizeof(szTmp) - 1, NULL);
1742 SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: %#llx %s\n", u64KeyId, szTmp));
1743#endif
1744 return false;
1745}
1746
1747
1748/**
1749 * Loads a module in the system32 directory.
1750 *
1751 * @returns Module handle on success. Won't return on faliure.
1752 * @param pszName The name of the DLL to load.
1753 */
1754DECLHIDDEN(HMODULE) supR3HardenedWinLoadSystem32Dll(const char *pszName)
1755{
1756 WCHAR wszName[200+60];
1757 UINT cwcDir = GetSystemWindowsDirectoryW(wszName, RT_ELEMENTS(wszName) - 60);
1758 memcpy(&wszName[cwcDir], RT_STR_TUPLE(L"\\System32\\"));
1759 RTUtf16CopyAscii(&wszName[cwcDir + sizeof("\\System32\\") - 1], RT_ELEMENTS(wszName) - cwcDir, pszName);
1760
1761 DWORD fFlags = 0;
1762 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
1763 fFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;
1764 HMODULE hMod = LoadLibraryExW(wszName, NULL, fFlags);
1765 if ( hMod == NULL
1766 && fFlags
1767 && g_uNtVerCombined < SUP_MAKE_NT_VER_SIMPLE(6, 2)
1768 && GetLastError() == ERROR_INVALID_PARAMETER)
1769 {
1770 fFlags = 0;
1771 hMod = LoadLibraryExW(wszName, NULL, fFlags);
1772 }
1773 if (hMod == NULL)
1774 supR3HardenedFatal("Error loading '%s': %u [%ls]", pszName, GetLastError(), wszName);
1775 return hMod;
1776}
1777
1778
1779/**
1780 * Called by supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation to
1781 * import selected root CAs from the system certificate store.
1782 *
1783 * These certificates permits us to correctly validate third party DLLs.
1784 */
1785static void supR3HardenedWinRetrieveTrustedRootCAs(void)
1786{
1787 uint32_t cAdded = 0;
1788
1789 /*
1790 * Load crypt32.dll and resolve the APIs we need.
1791 */
1792 HMODULE hCrypt32 = supR3HardenedWinLoadSystem32Dll("crypt32.dll");
1793
1794#define RESOLVE_CRYPT32_API(a_Name, a_pfnType) \
1795 a_pfnType pfn##a_Name = (a_pfnType)GetProcAddress(hCrypt32, #a_Name); \
1796 if (pfn##a_Name == NULL) supR3HardenedFatal("Error locating '" #a_Name "' in 'crypt32.dll': %u", GetLastError())
1797 RESOLVE_CRYPT32_API(CertOpenStore, PFNCERTOPENSTORE);
1798 RESOLVE_CRYPT32_API(CertCloseStore, PFNCERTCLOSESTORE);
1799 RESOLVE_CRYPT32_API(CertEnumCertificatesInStore, PFNCERTENUMCERTIFICATESINSTORE);
1800#undef RESOLVE_CRYPT32_API
1801
1802 /*
1803 * Open the root store and look for the certificates we wish to use.
1804 */
1805 DWORD fOpenStore = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
1806 HCERTSTORE hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1807 NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_LOCAL_MACHINE | fOpenStore, L"Root");
1808 if (!hStore)
1809 hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1810 NULL /* hCryptProv = default */, CERT_SYSTEM_STORE_CURRENT_USER | fOpenStore, L"Root");
1811 if (hStore)
1812 {
1813 PCCERT_CONTEXT pCurCtx = NULL;
1814 while ((pCurCtx = pfnCertEnumCertificatesInStore(hStore, pCurCtx)) != NULL)
1815 {
1816 if (pCurCtx->dwCertEncodingType & X509_ASN_ENCODING)
1817 {
1818 RTERRINFOSTATIC StaticErrInfo;
1819 RTASN1CURSORPRIMARY PrimaryCursor;
1820 RTAsn1CursorInitPrimary(&PrimaryCursor, pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded,
1821 RTErrInfoInitStatic(&StaticErrInfo),
1822 &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
1823 RTCRX509CERTIFICATE MyCert;
1824 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &MyCert, "Cert");
1825 if (RT_SUCCESS(rc))
1826 {
1827 if (supR3HardenedWinIsDesiredRootCA(&MyCert))
1828 {
1829 rc = RTCrStoreCertAddEncoded(g_hSpcRootStore, RTCRCERTCTX_F_ENC_X509_DER,
1830 pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
1831 AssertRC(rc);
1832
1833 rc = RTCrStoreCertAddEncoded(g_hSpcAndNtKernelRootStore, RTCRCERTCTX_F_ENC_X509_DER,
1834 pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, NULL /*pErrInfo*/);
1835 AssertRC(rc);
1836 cAdded++;
1837 }
1838
1839 RTCrX509Certificate_Delete(&MyCert);
1840 }
1841 /* XP root certificate "C&W HKT SecureNet CA SGC Root" has non-standard validity
1842 timestamps, the UTC formatting isn't Zulu time but specifies timezone offsets.
1843 Ignore these failures and certificates. */
1844 else if (rc != VERR_ASN1_INVALID_UTC_TIME_ENCODING)
1845 AssertMsgFailed(("RTCrX509Certificate_DecodeAsn1 failed: rc=%#x: %s\n", rc, StaticErrInfo.szMsg));
1846 }
1847 }
1848 pfnCertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
1849 g_fHaveOtherRoots = true;
1850 }
1851 SUP_DPRINTF(("supR3HardenedWinRetrieveTrustedRootCAs: cAdded=%u\n", cAdded));
1852}
1853
1854
1855/**
1856 * Resolves the WinVerifyTrust API after the process has been verified and
1857 * installs a thread creation hook.
1858 *
1859 * The WinVerifyTrust API is used in addition our own Authenticode verification
1860 * code. If the image has the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag
1861 * set, it will be checked again by the kernel. All our image has this flag set
1862 * and we require all VBox extensions to have it set as well. In effect, the
1863 * authenticode signature will be checked two or three times.
1864 *
1865 * @param pszProgName The program name.
1866 */
1867DECLHIDDEN(void) supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(const char *pszProgName)
1868{
1869# ifdef IN_SUP_HARDENED_R3
1870 /*
1871 * Load our the support library DLL that does the thread hooking as the
1872 * security API may trigger the creation of COM worker threads (or
1873 * whatever they are).
1874 *
1875 * The thread creation hook makes the threads very slippery to debuggers by
1876 * irreversably disabling most (if not all) debug events for them.
1877 */
1878 char szPath[RTPATH_MAX];
1879 supR3HardenedPathSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxSupLib.DLL"));
1880 suplibHardenedStrCat(szPath, "/VBoxSupLib.DLL");
1881 HMODULE hSupLibMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, true /*fSystem32Only*/);
1882 if (hSupLibMod == NULL)
1883 supR3HardenedFatal("Error loading '%s': %u", szPath, GetLastError());
1884# endif
1885
1886 /*
1887 * Resolve it.
1888 */
1889 HMODULE hWintrust = supR3HardenedWinLoadSystem32Dll("Wintrust.dll");
1890#define RESOLVE_CRYPT_API(a_Name, a_pfnType, a_uMinWinVer) \
1891 do { \
1892 g_pfn##a_Name = (a_pfnType)GetProcAddress(hWintrust, #a_Name); \
1893 if (g_pfn##a_Name == NULL && (a_uMinWinVer) < g_uNtVerCombined) \
1894 supR3HardenedFatal("Error locating '" #a_Name "' in 'Wintrust.dll': %u", GetLastError()); \
1895 } while (0)
1896
1897 PFNWINVERIFYTRUST pfnWinVerifyTrust = (PFNWINVERIFYTRUST)GetProcAddress(hWintrust, "WinVerifyTrust");
1898 if (!pfnWinVerifyTrust)
1899 supR3HardenedFatal("Error locating 'WinVerifyTrust' in 'Wintrust.dll': %u", GetLastError());
1900
1901 RESOLVE_CRYPT_API(CryptCATAdminAcquireContext, PFNCRYPTCATADMINACQUIRECONTEXT, 0);
1902 RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE, 0);
1903 RESOLVE_CRYPT_API(CryptCATAdminEnumCatalogFromHash, PFNCRYPTCATADMINENUMCATALOGFROMHASH, 0);
1904 RESOLVE_CRYPT_API(CryptCATAdminReleaseCatalogContext, PFNCRYPTCATADMINRELEASECATALOGCONTEXT, 0);
1905 RESOLVE_CRYPT_API(CryptCATAdminReleaseContext, PFNCRYPTCATDADMINRELEASECONTEXT, 0);
1906 RESOLVE_CRYPT_API(CryptCATCatalogInfoFromContext, PFNCRYPTCATCATALOGINFOFROMCONTEXT, 0);
1907
1908 RESOLVE_CRYPT_API(CryptCATAdminAcquireContext2, PFNCRYPTCATADMINACQUIRECONTEXT2, SUP_NT_VER_W80);
1909 RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle2, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2, SUP_NT_VER_W80);
1910
1911 /*
1912 * Call it on ourselves and ntdll to make sure it loads all the providers
1913 * now, we would otherwise geting into recursive trouble in the
1914 * NtCreateSection hook.
1915 */
1916# ifdef IN_SUP_HARDENED_R3
1917 RTERRINFOSTATIC ErrInfoStatic;
1918 RTErrInfoInitStatic(&ErrInfoStatic);
1919 int rc = supR3HardNtViCallWinVerifyTrust(NULL, g_SupLibHardenedExeNtPath.UniStr.Buffer, 0,
1920 &ErrInfoStatic.Core, pfnWinVerifyTrust);
1921 if (RT_FAILURE(rc))
1922 supR3HardenedFatalMsg(pszProgName, kSupInitOp_Integrity, rc,
1923 "WinVerifyTrust failed on stub executable: %s", ErrInfoStatic.szMsg);
1924# endif
1925
1926 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. */
1927 supR3HardNtViCallWinVerifyTrust(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
1928 supR3HardNtViCallWinVerifyTrustCatFile(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
1929
1930 g_pfnWinVerifyTrust = pfnWinVerifyTrust;
1931 SUP_DPRINTF(("g_pfnWinVerifyTrust=%p\n", pfnWinVerifyTrust));
1932
1933# ifdef IN_SUP_HARDENED_R3
1934 /*
1935 * Load some problematic DLLs into the verifier cache to prevent
1936 * recursion trouble.
1937 */
1938 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\crypt32.dll");
1939 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\Wintrust.dll");
1940# endif
1941
1942 /*
1943 * Now, get trusted root CAs so we can verify a broader scope of signatures.
1944 */
1945 supR3HardenedWinRetrieveTrustedRootCAs();
1946
1947# ifdef IN_SUP_HARDENED_R3
1948 /*
1949 * Do some verify cache preloading. The MS Visual C++ CRT DLLs works
1950 * around recursion issues with WinVerifyTrust on 32-bit windows 7.
1951 */
1952 SUP_DPRINTF(("preloading part 2...\n"));
1953# if 0 /* Seeing if this helps with the later Win7/32 issue... apparently not :-/ */
1954 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\cfgmgr32.dll");
1955 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\devobj.dll");
1956 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\setupapi.dll");
1957 supR3HardenedWinLoadSystem32Dll("setupapi.dll");
1958 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\rsaenh.dll");
1959 supR3HardenedWinLoadSystem32Dll("rsaenh.dll");
1960# endif
1961
1962 WCHAR wszPath[260+16];
1963 supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\apphelp.dll");
1964 memcpy(wszPath, g_SupLibHardenedExeNtPath.UniStr.Buffer, g_SupLibHardenedExeNtPath.UniStr.Length);
1965 static char const *s_apszAppDlls[] =
1966 {
1967 NULL,
1968 "VBoxRT.dll",
1969# if _MSC_VER < 1600
1970 "msvcr90.dll", "msvcp90.dll",
1971# elif _MSC_VER < 1700
1972 "msvcr100.dll", "msvcp100.dll",
1973# elif _MSC_VER < 1800
1974 "msvcr110.dll", "msvcp110.dll",
1975# elif _MSC_VER < 1900
1976 "msvcr120.dll", "msvcp120.dll",
1977# elif _MSC_VER < 1900
1978 "msvcr130.dll", "msvcp130.dll",
1979# else
1980# error "Unsupported compiler version."
1981# endif
1982 };
1983 s_apszAppDlls[0] = pszProgName;
1984 for (uint32_t i = 0; i < RT_ELEMENTS(s_apszAppDlls); i++)
1985 {
1986 RTUtf16CopyAscii(&wszPath[g_offSupLibHardenedExeNtName], 300 - g_offSupLibHardenedExeNtName, s_apszAppDlls[i]);
1987 supR3HardenedWinVerifyCachePreload(wszPath);
1988 }
1989 SUP_DPRINTF(("preloading part 2 - done.\n"));
1990# endif
1991}
1992
1993
1994static int supR3HardNtViNtToWinPath(PCRTUTF16 pwszNtName, PCRTUTF16 *ppwszWinPath,
1995 PRTUTF16 pwszWinPathBuf, size_t cwcWinPathBuf)
1996{
1997 static const RTUTF16 s_wszPrefix[] = L"\\\\.\\GLOBALROOT";
1998
1999 if (*pwszNtName != '\\' && *pwszNtName != '/')
2000 return VERR_PATH_DOES_NOT_START_WITH_ROOT;
2001
2002 size_t cwcNtName = RTUtf16Len(pwszNtName);
2003 if (RT_ELEMENTS(s_wszPrefix) + cwcNtName > cwcWinPathBuf)
2004 return VERR_FILENAME_TOO_LONG;
2005
2006 memcpy(pwszWinPathBuf, s_wszPrefix, sizeof(s_wszPrefix));
2007 memcpy(&pwszWinPathBuf[sizeof(s_wszPrefix) / sizeof(RTUTF16) - 1], pwszNtName, (cwcNtName + 1) * sizeof(RTUTF16));
2008 *ppwszWinPath = pwszWinPathBuf;
2009 return VINF_SUCCESS;
2010}
2011
2012
2013/**
2014 * Calls WinVerifyTrust to verify an PE image.
2015 *
2016 * @returns VBox status code.
2017 * @param hFile File handle to the executable file.
2018 * @param pwszName Full NT path to the DLL in question, used for
2019 * dealing with unsigned system dlls as well as for
2020 * error/logging.
2021 * @param fFlags Flags, SUPHNTVI_F_XXX.
2022 * @param pErrInfo Pointer to error info structure. Optional.
2023 * @param pfnWinVerifyTrust Pointer to the API.
2024 */
2025static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
2026 PFNWINVERIFYTRUST pfnWinVerifyTrust)
2027{
2028 /*
2029 * Convert the name into a Windows name.
2030 */
2031 RTUTF16 wszWinPathBuf[MAX_PATH];
2032 PCRTUTF16 pwszWinPath;
2033 int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
2034 if (RT_FAILURE(rc))
2035 return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrust: rc=%Rrc '%ls'", rc, pwszName);
2036
2037 /*
2038 * Construct input parameters and call the API.
2039 */
2040 WINTRUST_FILE_INFO FileInfo;
2041 RT_ZERO(FileInfo);
2042 FileInfo.cbStruct = sizeof(FileInfo);
2043 FileInfo.pcwszFilePath = pwszWinPath;
2044 FileInfo.hFile = hFile;
2045
2046 GUID PolicyActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
2047
2048 WINTRUST_DATA TrustData;
2049 RT_ZERO(TrustData);
2050 TrustData.cbStruct = sizeof(TrustData);
2051 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
2052 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
2053 TrustData.dwUIChoice = WTD_UI_NONE;
2054 TrustData.dwProvFlags = 0;
2055 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2056 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
2057 else
2058 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
2059 TrustData.dwUnionChoice = WTD_CHOICE_FILE;
2060 TrustData.pFile = &FileInfo;
2061
2062 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
2063 if (hrc == S_OK)
2064 rc = VINF_SUCCESS;
2065 else
2066 {
2067 /*
2068 * Failed. Format a nice error message.
2069 */
2070# ifdef DEBUG_bird
2071 if (hrc != CERT_E_CHAINING /* Un-updated vistas, XPs, ++ */)
2072 __debugbreak();
2073# endif
2074 const char *pszErrConst = NULL;
2075 switch (hrc)
2076 {
2077 case TRUST_E_SYSTEM_ERROR: pszErrConst = "TRUST_E_SYSTEM_ERROR"; break;
2078 case TRUST_E_NO_SIGNER_CERT: pszErrConst = "TRUST_E_NO_SIGNER_CERT"; break;
2079 case TRUST_E_COUNTER_SIGNER: pszErrConst = "TRUST_E_COUNTER_SIGNER"; break;
2080 case TRUST_E_CERT_SIGNATURE: pszErrConst = "TRUST_E_CERT_SIGNATURE"; break;
2081 case TRUST_E_TIME_STAMP: pszErrConst = "TRUST_E_TIME_STAMP"; break;
2082 case TRUST_E_BAD_DIGEST: pszErrConst = "TRUST_E_BAD_DIGEST"; break;
2083 case TRUST_E_BASIC_CONSTRAINTS: pszErrConst = "TRUST_E_BASIC_CONSTRAINTS"; break;
2084 case TRUST_E_FINANCIAL_CRITERIA: pszErrConst = "TRUST_E_FINANCIAL_CRITERIA"; break;
2085 case TRUST_E_PROVIDER_UNKNOWN: pszErrConst = "TRUST_E_PROVIDER_UNKNOWN"; break;
2086 case TRUST_E_ACTION_UNKNOWN: pszErrConst = "TRUST_E_ACTION_UNKNOWN"; break;
2087 case TRUST_E_SUBJECT_FORM_UNKNOWN: pszErrConst = "TRUST_E_SUBJECT_FORM_UNKNOWN"; break;
2088 case TRUST_E_SUBJECT_NOT_TRUSTED: pszErrConst = "TRUST_E_SUBJECT_NOT_TRUSTED"; break;
2089 case TRUST_E_NOSIGNATURE: pszErrConst = "TRUST_E_NOSIGNATURE"; break;
2090 case TRUST_E_FAIL: pszErrConst = "TRUST_E_FAIL"; break;
2091 case TRUST_E_EXPLICIT_DISTRUST: pszErrConst = "TRUST_E_EXPLICIT_DISTRUST"; break;
2092 case CERT_E_CHAINING: pszErrConst = "CERT_E_CHAINING"; break;
2093 case CERT_E_REVOCATION_FAILURE: pszErrConst = "CERT_E_REVOCATION_FAILURE"; break;
2094 }
2095 if (pszErrConst)
2096 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
2097 "WinVerifyTrust failed with hrc=%s on '%ls'", pszErrConst, pwszName);
2098 else
2099 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_UNSUPPORTED_ARCH,
2100 "WinVerifyTrust failed with hrc=%Rhrc on '%ls'", hrc, pwszName);
2101 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrust: WinVerifyTrust failed with %#x (%s) on '%ls'\n",
2102 hrc, pszErrConst, pwszName));
2103 }
2104
2105 /* clean up state data. */
2106 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
2107 FileInfo.hFile = NULL;
2108 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &PolicyActionGuid, &TrustData);
2109
2110 return rc;
2111}
2112
2113
2114/**
2115 * Calls WinVerifyTrust to verify an PE image via catalog files.
2116 *
2117 * @returns VBox status code.
2118 * @param hFile File handle to the executable file.
2119 * @param pwszName Full NT path to the DLL in question, used for
2120 * dealing with unsigned system dlls as well as for
2121 * error/logging.
2122 * @param fFlags Flags, SUPHNTVI_F_XXX.
2123 * @param pErrInfo Pointer to error info structure. Optional.
2124 * @param pfnWinVerifyTrust Pointer to the API.
2125 */
2126static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
2127 PFNWINVERIFYTRUST pfnWinVerifyTrust)
2128{
2129 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hFile=%p pwszName=%ls\n", hFile, pwszName));
2130
2131 /*
2132 * Convert the name into a Windows name.
2133 */
2134 RTUTF16 wszWinPathBuf[MAX_PATH];
2135 PCRTUTF16 pwszWinPath;
2136 int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
2137 if (RT_FAILURE(rc))
2138 return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrustCatFile: rc=%Rrc '%ls'", rc, pwszName);
2139
2140 /*
2141 * Open the file if we didn't get a handle.
2142 */
2143 HANDLE hFileClose = NULL;
2144 if (hFile == RTNT_INVALID_HANDLE_VALUE || hFile == NULL)
2145 {
2146 hFile = RTNT_INVALID_HANDLE_VALUE;
2147 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2148
2149 UNICODE_STRING NtName;
2150 NtName.Buffer = (PWSTR)pwszName;
2151 NtName.Length = (USHORT)(RTUtf16Len(pwszName) * sizeof(WCHAR));
2152 NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
2153
2154 OBJECT_ATTRIBUTES ObjAttr;
2155 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
2156
2157 NTSTATUS rcNt = NtCreateFile(&hFile,
2158 FILE_READ_DATA | READ_CONTROL | SYNCHRONIZE,
2159 &ObjAttr,
2160 &Ios,
2161 NULL /* Allocation Size*/,
2162 FILE_ATTRIBUTE_NORMAL,
2163 FILE_SHARE_READ,
2164 FILE_OPEN,
2165 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
2166 NULL /*EaBuffer*/,
2167 0 /*EaLength*/);
2168 if (NT_SUCCESS(rcNt))
2169 rcNt = Ios.Status;
2170 if (!NT_SUCCESS(rcNt))
2171 return RTErrInfoSetF(pErrInfo, RTErrConvertFromNtStatus(rcNt),
2172 "NtCreateFile returned %#x opening '%ls'.", rcNt, pwszName);
2173 hFileClose = hFile;
2174 }
2175
2176 /*
2177 * On Windows 8.0 and later there are more than one digest choice.
2178 */
2179 rc = VERR_LDRVI_NOT_SIGNED;
2180 static struct
2181 {
2182 /** The digest algorithm name. */
2183 const WCHAR *pszAlgorithm;
2184 /** Cached catalog admin handle. */
2185 HCATADMIN volatile hCachedCatAdmin;
2186 } s_aHashes[] =
2187 {
2188 { NULL, NULL },
2189 { L"SHA256", NULL },
2190 };
2191 for (uint32_t i = 0; i < RT_ELEMENTS(s_aHashes); i++)
2192 {
2193 /*
2194 * Another loop for dealing with different trust provider policies
2195 * required for successfully validating different catalog signatures.
2196 */
2197 bool fTryNextPolicy;
2198 uint32_t iPolicy = 0;
2199 static const GUID s_aPolicies[] =
2200 {
2201 DRIVER_ACTION_VERIFY, /* Works with microsoft bits. Most frequently used, thus first. */
2202 WINTRUST_ACTION_GENERIC_VERIFY_V2, /* Works with ATI and other SPC kernel-code signed stuff. */
2203 };
2204 do
2205 {
2206 /*
2207 * Create a context.
2208 */
2209 fTryNextPolicy = false;
2210 BOOL fRc;
2211 HCATADMIN hCatAdmin = ASMAtomicXchgPtr(&s_aHashes[i].hCachedCatAdmin, NULL);
2212 if (hCatAdmin)
2213 fRc = TRUE;
2214 else if (g_pfnCryptCATAdminAcquireContext2)
2215 fRc = g_pfnCryptCATAdminAcquireContext2(&hCatAdmin, &s_aPolicies[iPolicy], s_aHashes[i].pszAlgorithm,
2216 NULL /*pStrongHashPolicy*/, 0 /*dwFlags*/);
2217 else
2218 fRc = g_pfnCryptCATAdminAcquireContext(&hCatAdmin, &s_aPolicies[iPolicy], 0 /*dwFlags*/);
2219 if (fRc)
2220 {
2221 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: hCatAdmin=%p\n", hCatAdmin));
2222
2223 /*
2224 * Hash the file.
2225 */
2226 BYTE abHash[SUPHARDNTVI_MAX_CAT_HASH_SIZE];
2227 DWORD cbHash = sizeof(abHash);
2228 if (g_pfnCryptCATAdminCalcHashFromFileHandle2)
2229 fRc = g_pfnCryptCATAdminCalcHashFromFileHandle2(hCatAdmin, hFile, &cbHash, abHash, 0 /*dwFlags*/);
2230 else
2231 fRc = g_pfnCryptCATAdminCalcHashFromFileHandle(hFile, &cbHash, abHash, 0 /*dwFlags*/);
2232 if (fRc)
2233 {
2234 /* Produce a string version of it that we can pass to WinVerifyTrust. */
2235 RTUTF16 wszDigest[SUPHARDNTVI_MAX_CAT_HASH_SIZE * 2 + 1];
2236 int rc2 = RTUtf16PrintHexBytes(wszDigest, RT_ELEMENTS(wszDigest), abHash, cbHash, RTSTRPRINTHEXBYTES_F_UPPER);
2237 if (RT_SUCCESS(rc2))
2238 {
2239 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: cbHash=%u wszDigest=%ls\n", cbHash, wszDigest));
2240
2241 /*
2242 * Enumerate catalog information that matches the hash.
2243 */
2244 uint32_t iCat = 0;
2245 HCATINFO hCatInfoPrev = NULL;
2246 do
2247 {
2248 /* Get the next match. */
2249 HCATINFO hCatInfo = g_pfnCryptCATAdminEnumCatalogFromHash(hCatAdmin, abHash, cbHash, 0, &hCatInfoPrev);
2250 if (!hCatInfo)
2251 {
2252 if (iCat == 0)
2253 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed %u\n", GetLastError()));
2254 break;
2255 }
2256 Assert(hCatInfoPrev == NULL);
2257 hCatInfoPrev = hCatInfo;
2258
2259 /*
2260 * Call WinVerifyTrust.
2261 */
2262 CATALOG_INFO CatInfo;
2263 CatInfo.cbStruct = sizeof(CatInfo);
2264 CatInfo.wszCatalogFile[0] = '\0';
2265 if (g_pfnCryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0 /*dwFlags*/))
2266 {
2267 WINTRUST_CATALOG_INFO WtCatInfo;
2268 RT_ZERO(WtCatInfo);
2269 WtCatInfo.cbStruct = sizeof(WtCatInfo);
2270 WtCatInfo.dwCatalogVersion = 0;
2271 WtCatInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
2272 WtCatInfo.pcwszMemberTag = wszDigest;
2273 WtCatInfo.pcwszMemberFilePath = pwszWinPath;
2274 WtCatInfo.pbCalculatedFileHash = abHash;
2275 WtCatInfo.cbCalculatedFileHash = cbHash;
2276 WtCatInfo.pcCatalogContext = NULL;
2277
2278 WINTRUST_DATA TrustData;
2279 RT_ZERO(TrustData);
2280 TrustData.cbStruct = sizeof(TrustData);
2281 TrustData.fdwRevocationChecks = WTD_REVOKE_NONE; /* Keep simple for now. */
2282 TrustData.dwStateAction = WTD_STATEACTION_VERIFY;
2283 TrustData.dwUIChoice = WTD_UI_NONE;
2284 TrustData.dwProvFlags = 0;
2285 if (g_uNtVerCombined >= SUP_MAKE_NT_VER_SIMPLE(6, 0))
2286 TrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
2287 else
2288 TrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
2289 TrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
2290 TrustData.pCatalog = &WtCatInfo;
2291
2292 HRESULT hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
2293 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: WinVerifyTrust => %#x; cat='%ls'; file='%ls'\n",
2294 hrc, CatInfo.wszCatalogFile, pwszName));
2295
2296 if (SUCCEEDED(hrc))
2297 rc = VINF_SUCCESS;
2298 else if (hrc == TRUST_E_NOSIGNATURE)
2299 { /* ignore because it's useless. */ }
2300 else if (hrc == ERROR_INVALID_PARAMETER)
2301 { /* This is returned if the given file isn't found in the catalog, it seems. */ }
2302 else
2303 {
2304 rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_WINTRUST_CAT_FAILURE,
2305 "WinVerifyTrust failed with hrc=%#x on '%ls' and .cat-file='%ls'.",
2306 hrc, pwszWinPath, CatInfo.wszCatalogFile);
2307 fTryNextPolicy = (hrc == CERT_E_UNTRUSTEDROOT);
2308 }
2309
2310 /* clean up state data. */
2311 TrustData.dwStateAction = WTD_STATEACTION_CLOSE;
2312 hrc = pfnWinVerifyTrust(NULL /*hwnd*/, &s_aPolicies[iPolicy], &TrustData);
2313 Assert(SUCCEEDED(hrc));
2314 }
2315 else
2316 {
2317 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(GetLastError()),
2318 "CryptCATCatalogInfoFromContext failed: %d [file=%s]",
2319 GetLastError(), pwszName);
2320 SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATCatalogInfoFromContext failed\n"));
2321 }
2322 iCat++;
2323 } while (rc == VERR_LDRVI_NOT_SIGNED && iCat < 128);
2324
2325 if (hCatInfoPrev != NULL)
2326 if (!g_pfnCryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfoPrev, 0 /*dwFlags*/))
2327 AssertFailed();
2328 }
2329 else
2330 rc = RTErrInfoSetF(pErrInfo, rc2, "RTUtf16PrintHexBytes failed: %Rrc", rc);
2331 }
2332 else
2333 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(GetLastError()),
2334 "CryptCATAdminCalcHashFromFileHandle[2] failed: %d [file=%s]", GetLastError(), pwszName);
2335
2336 if (!ASMAtomicCmpXchgPtr(&s_aHashes[i].hCachedCatAdmin, hCatAdmin, NULL))
2337 if (!g_pfnCryptCATAdminReleaseContext(hCatAdmin, 0 /*dwFlags*/))
2338 AssertFailed();
2339 }
2340 else
2341 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(GetLastError()),
2342 "CryptCATAdminAcquireContext[2] failed: %d [file=%s]", GetLastError(), pwszName);
2343 iPolicy++;
2344 } while ( fTryNextPolicy
2345 && iPolicy < RT_ELEMENTS(s_aPolicies));
2346
2347 /*
2348 * Only repeat if we've got g_pfnCryptCATAdminAcquireContext2 and can specify the hash algorithm.
2349 */
2350 if (!g_pfnCryptCATAdminAcquireContext2)
2351 break;
2352 if (rc != VERR_LDRVI_NOT_SIGNED)
2353 break;
2354 }
2355
2356 if (hFileClose != NULL)
2357 NtClose(hFileClose);
2358
2359 return rc;
2360}
2361
2362
2363/**
2364 * Initializes g_uNtVerCombined and g_NtVerInfo.
2365 * Called from suplibHardenedWindowsMain and suplibOsInit.
2366 */
2367DECLHIDDEN(void) supR3HardenedWinInitVersion(void)
2368{
2369 /*
2370 * Get the windows version. Use RtlGetVersion as GetVersionExW and
2371 * GetVersion might not be telling the whole truth (8.0 on 8.1 depending on
2372 * the application manifest).
2373 */
2374 OSVERSIONINFOEXW NtVerInfo;
2375
2376 RT_ZERO(NtVerInfo);
2377 NtVerInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
2378 if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&NtVerInfo)))
2379 {
2380 RT_ZERO(NtVerInfo);
2381 PPEB pPeb = NtCurrentPeb();
2382 NtVerInfo.dwMajorVersion = pPeb->OSMajorVersion;
2383 NtVerInfo.dwMinorVersion = pPeb->OSMinorVersion;
2384 NtVerInfo.dwBuildNumber = pPeb->OSPlatformId;
2385 }
2386
2387 g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(NtVerInfo.dwMajorVersion, NtVerInfo.dwMinorVersion, NtVerInfo.dwBuildNumber,
2388 NtVerInfo.wServicePackMajor, NtVerInfo.wServicePackMinor);
2389}
2390
2391#endif /* IN_RING3 */
2392
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