VirtualBox

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

Last change on this file since 86513 was 85127, checked in by vboxsync, 5 years ago

iprt/cdefs.h: Made DECLHIDDEN imply nothrow and restrict its use to function (just like the newly added DECL_HIDDEN_NOTHROW). Added DECL_HIDDEN_ONLY and DECL_HIDDEN_THROW to accompany it. bugref:9794

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