VirtualBox

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

Revision 66525, 127.0 KB checked in by vboxsync, 6 months ago (diff)

Windows hardening: bugref:8750: blacklist scrobj.dll

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

www.oracle.com
ContactPrivacy policyTerms of Use