VirtualBox

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

Last change on this file since 104381 was 104381, checked in by vboxsync, 3 weeks ago

/Config.kmk, HostDrivers/Support/win: Disable integrity check due to changes in signing. bugref:10657

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

© 2023 Oracle
ContactPrivacy policyTerms of Use