VirtualBox

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

Last change on this file since 106897 was 106893, checked in by vboxsync, 3 months ago

SUPHardNt: Rough and untested port of the C code to win.arm64 so the extpack builds. VBP-1447

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

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