VirtualBox

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

Last change on this file was 104384, checked in by vboxsync, 7 days ago

/Config.kmk,SUPHardNt: s/VBOX_WITHOUT_HARDENING_INTEGRITY_CHECK/VBOX_WITHOUT_WINDOWS_KERNEL_CODE_SIGNING_CERT/; s/SUPHNTVI_F_TRUSTED_INSTALLER_OWNER/SUPHNTVI_F_TRUSTED_INSTALLER_OR_SIMILAR_OWNER/; some more comments, a @todo and dialing back the changes a little. bugref:10657

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

© 2023 Oracle
ContactPrivacy policyTerms of Use