VirtualBox

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

Last change on this file since 86549 was 86549, checked in by vboxsync, 4 years ago

SUPHardNt,IPRT: If there are nested signatures (i.e. more than one signature), don't get grumpy if there are time or cert path issues with some of them, as long as one or more checks out perfectly. (Mind, all the signature data must check out, it's just the cert path or signing time we're relaxing here.) ticketref:19743 bugref:3103

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

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