VirtualBox

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

最後變更 在這個檔案是 107183,由 vboxsync 提交於 4 月 前

Support/win/SUPHardenedVerifyImage-win.cpp: Fixed a warning found by Parfait (assignment is unused).

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

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