SUPHardenedVerifyImage-win.cpp revision 8ce864006b017481247823fbc15e45bd30d98811
/* $Id$ */
/** @file
*/
/*
* Copyright (C) 2006-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#ifdef IN_RING0
# define IPRT_NT_MAP_TO_ZW
# include <ntimage.h>
#else
# include "Wintrust.h"
# include "Softpub.h"
# include "mscat.h"
# ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
# endif
#endif
#ifdef IN_RING0
# include "SUPDrvInternal.h"
#else
# include "SUPLibInternal.h"
#endif
#include "win/SUPHardenedVerify-win.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The size of static hash (output) buffers.
* Avoids dynamic allocations and cleanups for of small buffers as well as extra
* calls for getting the appropriate buffer size. The largest digest in regular
* use by current windows version is SHA-512, we double this and hope it's
* enough a good while. */
#define SUPHARDNTVI_MAX_CAT_HASH_SIZE 128
#if defined(VBOX_PERMIT_EVEN_MORE) && !defined(VBOX_PERMIT_MORE)
# error "VBOX_PERMIT_EVEN_MORE without VBOX_PERMIT_MORE!"
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
#ifdef IN_RING3
typedef BOOL (WINAPI * PFNCRYPTCATADMINACQUIRECONTEXT)(HCATADMIN *phCatAdmin, const GUID *pGuidSubsystem, DWORD dwFlags);
typedef BOOL (WINAPI * PFNCRYPTCATADMINACQUIRECONTEXT2)(HCATADMIN *phCatAdmin, const GUID *pGuidSubsystem, PCWSTR pwszHashAlgorithm,
typedef BOOL (WINAPI * PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE)(HANDLE hFile, DWORD *pcbHash, BYTE *pbHash, DWORD dwFlags);
typedef BOOL (WINAPI * PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2)(HCATADMIN hCatAdmin, HANDLE hFile, DWORD *pcbHash,
typedef HCATINFO (WINAPI *PFNCRYPTCATADMINENUMCATALOGFROMHASH)(HCATADMIN hCatAdmin, BYTE *pbHash, DWORD cbHash,
typedef BOOL (WINAPI * PFNCRYPTCATADMINRELEASECATALOGCONTEXT)(HCATADMIN hCatAdmin, HCATINFO hCatInfo, DWORD dwFlags);
typedef BOOL (WINAPI * PFNCRYPTCATCATALOGINFOFROMCONTEXT)(HCATINFO hCatInfo, CATALOG_INFO *psCatInfo, DWORD dwFlags);
typedef HCERTSTORE (WINAPI *PFNCERTOPENSTORE)(PCSTR pszStoreProvider, DWORD dwEncodingType, HCRYPTPROV_LEGACY hCryptProv,
typedef PCCERT_CONTEXT (WINAPI *PFNCERTENUMCERTIFICATESINSTORE)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The build certificate. */
static RTCRX509CERTIFICATE g_BuildX509Cert;
/** Store for root software publisher certificates. */
/** Store for root NT kernel certificates. */
/** Store containing SPC, NT kernel signing, and timestamp root certificates. */
/** Store for supplemental certificates for use with
* g_hSpcAndNtKernelRootStore. */
/** The full \\SystemRoot\\System32 path. */
/** The full \\SystemRoot\\WinSxS path. */
#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
/** The full 'Program Files' path. */
# ifdef RT_ARCH_AMD64
/** The full 'Program Files (x86)' path. */
# endif
/** The full 'Common Files' path. */
# ifdef RT_ARCH_AMD64
/** The full 'Common Files (x86)' path. */
# endif
#endif /* IN_RING3 && !VBOX_PERMIT_MORE*/
static union
{
}
/** The TrustedInstaller SID (Vista+). */
/** Local system ID (S-1-5-21). */
/** Builtin Administrators group alias (S-1-5-32-544). */
/** Set after we've retrived other SPC root certificates from the system. */
static bool g_fHaveOtherRoots = false;
#if defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
/** Combined windows NT version number. See SUP_MAKE_NT_VER_COMBINED and
* SUP_MAKE_NT_VER_SIMPLE. */
#endif
#ifdef IN_RING3
/** Timestamp hack working around issues with old DLLs that we ship.
* See supHardenedWinVerifyImageByHandle() for details. */
static uint64_t g_uBuildTimestampHack = 0;
#endif
#ifdef IN_RING3
/** Pointer to WinVerifyTrust. */
/** Pointer to CryptCATAdminAcquireContext. */
/** Pointer to CryptCATAdminAcquireContext2 if available. */
/** Pointer to CryptCATAdminCalcHashFromFileHandle. */
/** Pointer to CryptCATAdminCalcHashFromFileHandle2. */
/** Pointer to CryptCATAdminEnumCatalogFromHash. */
/** Pointer to CryptCATAdminReleaseCatalogContext. */
/** Pointer to CryptCATAdminReleaseContext. */
/** Pointer to CryptCATCatalogInfoFromContext. */
/** Where we store the TLS entry for detecting WinVerifyTrustRecursion. */
/** Fallback WinVerifyTrust recursion protection. */
#endif
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifdef IN_RING3
static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
#endif
/** @copydoc RTLDRREADER::pfnRead */
static DECLCALLBACK(int) supHardNtViRdrRead(PRTLDRREADER pReader, void *pvBuf, size_t cb, RTFOFF off)
{
return VERR_OUT_OF_RANGE;
/*
* For some reason I'm getting occational read error in an XP VM with
* STATUS_FAILED_DRIVER_ENTRY. Redoing the call again works in the
* debugger, so try do that automatically.
*/
{
NULL /*hEvent*/,
NULL /*ApcRoutine*/,
NULL /*ApcContext*/,
&Ios,
&offNt,
NULL);
if (NT_SUCCESS(rcNt))
if (NT_SUCCESS(rcNt))
{
{
return VINF_SUCCESS;
}
#ifdef IN_RING3
supR3HardenedError(VERR_READ_ERROR, false,
"supHardNtViRdrRead: Only got %#zx bytes when requesting %#zx bytes at %#llx in '%s'.\n",
#endif
return VERR_READ_ERROR;
}
/** @todo This nonsense is probably due to missing FILE_SYNCHRONOUS_IO_NONALERT
* flags in early code stage. Should clean this up, but leave code
* for handling STATUS_PENDING as we don't know what callers of
* NtCreateSection might've been passing to their NtCreateFile calls.
* In ring-0, this code is mostly pointless, I think. */
/*
* Delay a little before we retry?
*/
#ifdef IN_RING3
if (iTry == 0)
else if (iTry >= 1)
{
}
#endif
/*
* Before we give up, we'll try split up the request in case the
* kernel is low on memory or similar. For simplicity reasons, we do
* this in a recursion fashion.
*/
if (iTry >= 2)
{
{
while (cb > 0)
{
if (RT_FAILURE(rc))
return rc;
off += cbThisRead;
cb -= cbThisRead;
}
return VINF_SUCCESS;
}
#ifdef IN_RING3
supR3HardenedError(VERR_READ_ERROR, false, "supHardNtViRdrRead: Error %#x reading %#zx bytes at %#llx in '%s'.\n",
#endif
return VERR_READ_ERROR;
}
}
}
/** @copydoc RTLDRREADER::pfnTell */
{
}
/** @copydoc RTLDRREADER::pfnSize */
{
}
/** @copydoc RTLDRREADER::pfnLogName */
{
return pNtViRdr->szFilename;
}
/** @copydoc RTLDRREADER::pfnMap */
{
return VERR_NOT_SUPPORTED;
}
/** @copydoc RTLDRREADER::pfnUnmap */
{
return VERR_NOT_SUPPORTED;
}
/** @copydoc RTLDRREADER::pfnDestroy */
{
return VINF_SUCCESS;
}
/**
* Creates a loader reader instance for the given NT file handle.
*
* @returns iprt status code.
* @param hFile Native NT file handle.
* @param pwszName Optional file name.
* @param fFlags Flags, SUPHNTVI_F_XXX.
* @param ppNtViRdr Where to store the reader instance on success.
*/
DECLHIDDEN(int) supHardNtViRdrCreate(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PSUPHNTVIRDR *ppNtViRdr)
{
/*
* Try determine the size of the file.
*/
NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
return VERR_LDRVI_FILE_LENGTH_ERROR;
/*
* Calc the file name length and allocate memory for the reader instance.
*/
size_t cchFilename = 0;
if (pwszName)
int rc = VERR_NO_MEMORY;
if (!pNtViRdr)
return VERR_NO_MEMORY;
/*
* Initialize the structure.
*/
if (cchFilename)
{
}
else
return VINF_SUCCESS;
}
/**
* Checks if the file is owned by TrustedInstaller (Vista+) or similar.
*
* @returns true if owned by TrustedInstaller of pre-Vista, false if not.
*
* @param hFile The handle to the file.
* @param pwszName The name of the file.
*/
{
if (g_uNtVerCombined < SUP_NT_VER_VISTA)
return true;
/*
* Get the ownership information.
*/
union
{
} uBuf;
NTSTATUS rcNt = NtQuerySecurityObject(hFile, OWNER_SECURITY_INFORMATION, &uBuf.Abs, sizeof(uBuf), &cbActual);
if (!NT_SUCCESS(rcNt))
{
return false;
}
/*
* Check the owner.
*
* Initially we wished to only allow TrustedInstaller. But a Windows CAPI
* plugin "Program Files\Tumbleweed\Desktop Validator\tmwdcapiclient.dll"
* turned up owned by the local system user, and we cannot operate without
* the plugin loaded once it's installed (WinVerityTrust fails).
*
* We'd like to avoid allowing Builtin\Administrators here since it's the
* default owner of anything an admin user creates (at least when elevated).
* Seems windows update or someone ends up installing or modifying system
* DLL ownership to this group, so for system32 and winsxs it's unavoidable.
* And, not surprise, a bunch of products, including AV, firewalls and similar
* ends up with their files installed with this group as owner. For instance
* if we wish to have NAT continue working, we need to allow this.
*
* Hopefully, we can limit the allowed files to these owners though, so
* we won't be subject to ordinary (non-admin, or not elevated) users
* downloading or be tricked into putting evil DLLs around the place...
*/
return true;
return true;
{
return true;
}
SUP_DPRINTF(("%ls: Owner is not trusted installer (%.*Rhxs)\n",
return false;
}
/**
* Simple case insensitive UTF-16 / ASCII path compare.
*
* @returns true if equal, false if not.
* @param pawcLeft The UTF-16 path string, not necessarily null
* terminated.
* @param cwcLeft The number of chars in the left string,
* RTSTR_MAX if unknown but terminated.
* @param pszRight The ascii string.
*/
DECLHIDDEN(bool) supHardViUtf16PathIsEqualEx(PCRTUTF16 pawcLeft, size_t cwcLeft, const char *pszRight)
{
for (;;)
{
if (cwcLeft-- > 0)
else
wc = 0;
if (b != wc)
{
if (wc >= 0x80)
return false;
if (wc != b)
{
b = RT_C_TO_LOWER(b);
if (wc != b)
{
if (wc == '/')
wc = '\\';
if (b == '/')
b = '\\';
if (wc != b)
return false;
}
}
}
if (!b)
return true;
}
}
/**
* Simple case insensitive UTF-16 / ASCII path compare.
*
* @returns true if equal, false if not.
* @param pwszLeft The UTF-16 path string.
* @param pszRight The ascii string.
*/
{
}
/**
* Simple case insensitive UTF-16 / ASCII ends-with path predicate.
*
* @returns true if equal, false if not.
* @param pwsz The UTF-16 path string.
* @param pszSuffix The ascii suffix string.
*/
{
return false;
}
/**
* Simple case insensitive UTF-16 / ASCII starts-with path predicate.
*
* @returns true if starts with given string, false if not.
* @param pwsz The UTF-16 path string.
* @param pszPrefix The ascii prefix string.
*/
{
for (;;)
{
if (b != wc)
{
if (!b)
return true;
return false;
if (wc != b)
{
b = RT_C_TO_LOWER(b);
if (wc != b)
{
if (wc == '/')
wc = '\\';
if (b == '/')
b = '\\';
if (wc != b)
return false;
}
}
}
}
}
/**
* Simple case insensitive UNICODE_STRING starts-with path predicate.
*
* @returns true if starts with given string, false if not.
* @param pwszLeft The path to check.
* @param cwcLeft The length of @a pwszLeft
* @param pwszRight The starts-with path.
* @param cwcRight The length of @a pwszRight.
* @param fCheckSlash Check for a slash following the prefix.
*/
{
return false;
/* See if we can get away with a case sensitive compare first. */
else
{
/* No luck, do a slow case insensitive comapre. */
while (cLeft-- > 0)
{
{
return false;
}
}
}
/* Check for slash following the prefix, if request. */
if ( !fCheckSlash
|| *pwszLeft == '\\'
|| *pwszLeft == '/')
return true;
return false;
}
/**
* Simple case insensitive UNICODE_STRING starts-with path predicate.
*
* @returns true if starts with given string, false if not.
* @param pUniStrLeft The path to check.
* @param pUniStrRight The starts-with path.
* @param fCheckSlash Check for a slash following the prefix.
*/
DECLHIDDEN(bool) supHardViUniStrPathStartsWithUniStr(UNICODE_STRING const *pUniStrLeft, UNICODE_STRING const *pUniStrRight,
bool fCheckSlash)
{
}
/**
* Counts slashes in the given UTF-8 path string.
*
* @returns Number of slashes.
* @param pwsz The UTF-16 path string.
*/
{
cSlashes++;
return cSlashes;
}
#ifdef VBOX_PERMIT_MORE
/**
* Checks if the path goes into %windir%\apppatch\.
*
* @returns true if apppatch, false if not.
* @param pwszPath The path to examine.
*/
{
return false;
return false;
return false;
return g_uNtVerCombined >= SUP_NT_VER_VISTA;
}
#else
#endif
/**
* Checks if the unsigned DLL is fine or not.
*
* @returns VINF_LDRVI_NOT_SIGNED or @a rc.
* @param hLdrMod The loader module handle.
* @param fFlags Flags.
* @param hFile The file handle.
* @param rc The status code..
*/
static int supHardNtViCheckIfNotSignedOk(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, uint32_t fFlags, HANDLE hFile, int rc)
{
return rc;
/*
* Version macros.
*/
#define IS_W2K3() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(5, 2) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(5, 3) )
#define IS_VISTA() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 0) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 1) )
#define IS_W70() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 1) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 2) )
#define IS_W80() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 2) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 3) )
#define IS_W81() ( uNtVer >= SUP_MAKE_NT_VER_SIMPLE(6, 3) && uNtVer < SUP_MAKE_NT_VER_SIMPLE(6, 4) )
/*
* The System32 directory.
*
* System32 is full of unsigned DLLs shipped by microsoft, graphics
* actually needs to be loaded into a process for it to work correctly.
* We have to ASSUME that anything our process attempts to load from
* System32 is trustworthy and that the Windows system with the help of
* anti-virus software make sure there is nothing evil lurking in System32
* or being loaded from it.
*
* A small measure of protection is to list DLLs we know should be signed
* and decline loading unsigned versions of them, assuming they have been
* replaced by an adversary with evil intentions.
*/
if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_System32NtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
{
/* Must be owned by trusted installer. (This test is superfuous, thus no relaxation here.) */
if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
return rc;
/* Core DLLs. */
#ifdef VBOX_PERMIT_VERIFIER_DLL
#endif
#ifdef VBOX_PERMIT_MORE
{
}
#endif
#ifndef IN_RING0
/* Check that this DLL isn't supposed to be signed on this windows
version. If it should, it's likely to be a fake. */
/** @todo list of signed dlls for various windows versions. */
return VINF_LDRVI_NOT_SIGNED;
#else
return rc;
#endif /* IN_RING0 */
}
#ifndef IN_RING0
/*
* The WinSxS white list.
*
* Just like with System32 there are potentially a number of DLLs that
* could be required from WinSxS.
*/
if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_WinSxSNtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
{
/* The WinSxS layout means everything worth loading is exactly one level down. */
if (cSlashes != 1)
return rc;
/* Must be owned by trusted installer. */
if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
return rc;
return VINF_LDRVI_NOT_SIGNED;
}
#endif /* !IN_RING0 */
#ifdef VBOX_PERMIT_MORE
/*
* AppPatch whitelist.
*/
{
cwcOther = g_System32NtPath.UniStr.Length / sizeof(WCHAR); /* ASSUMES System32 is called System32. */
if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
return rc;
# ifndef VBOX_PERMIT_EVEN_MORE
return VINF_LDRVI_NOT_SIGNED;
# ifdef RT_ARCH_AMD64
return VINF_LDRVI_NOT_SIGNED;
# elif defined(RT_ARCH_X86)
return VINF_LDRVI_NOT_SIGNED;
# endif
# endif /* !VBOX_PERMIT_EVEN_MORE */
# ifdef IN_RING0
return rc;
# else
return VINF_LDRVI_NOT_SIGNED;
# endif
}
#endif /* VBOX_PERMIT_MORE */
#ifndef IN_RING0
# if defined(VBOX_PERMIT_MORE) && !defined(VBOX_PERMIT_EVEN_MORE)
/*
* Program files and common files.
* Permit anything that's signed and correctly installed.
*/
true /*fCheckSlash*/)
true /*fCheckSlash*/)
# ifdef RT_ARCH_AMD64
true /*fCheckSlash*/)
true /*fCheckSlash*/)
# endif
)
{
if ( !(fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
return rc;
return VINF_LDRVI_NOT_SIGNED;
}
/*
* Anything that's owned by the trusted installer.
*/
if ( (fFlags & SUPHNTVI_F_TRUSTED_INSTALLER_OWNER)
return VINF_LDRVI_NOT_SIGNED;
# endif
#endif /* !IN_RING0 */
/*
* Not permitted.
*/
return rc;
}
/**
* @callback_method_impl{RTCRPKCS7VERIFYCERTCALLBACK,
* Standard code signing. Use this for Microsoft SPC.}
*/
static DECLCALLBACK(int) supHardNtViCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths,
{
/*
* If there is no certificate path build & validator associated with this
* callback, it must be because of the build certificate. We trust the
* build certificate without any second thoughts.
*/
if (hCertPaths == NIL_RTCRX509CERTPATHS)
{
return VINF_SUCCESS;
}
/*
* Standard code signing capabilites required.
*/
if ( RT_SUCCESS(rc)
&& (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
{
/*
* If kernel signing, a valid certificate path must be anchored by the
* microsoft kernel signing root certificate.
*/
{
{
bool fTrusted;
int rcVerify;
rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
if (RT_SUCCESS(rcVerify))
{
cValid++;
/*
* Search the kernel signing root store for a matching anchor.
*/
{
else
if ( pCertPubKeyInfo
cFound++;
}
}
}
rc = RTErrInfoSetF(pErrInfo, VERR_SUP_VP_NOT_VALID_KERNEL_CODE_SIGNATURE, "Not valid kernel code signature.");
}
}
/*
* More requirements? NT5 build lab?
*/
return rc;
}
{
/*
* Check out the input.
*/
/*
* If special certificate requirements, check them out before validating
* the signature.
*/
{
return RTErrInfoSet(pErrInfo, VERR_SUP_VP_NOT_SIGNED_WITH_BUILD_CERT, "Not signed with the build certificate.");
}
/*
* Verify the signature. We instruct the verifier to use the signing time
* counter signature present when present, falling back on the timestamp
* planted by the linker when absent. In ring-0 we don't have all the
* necessary timestamp server root certificate info, so we have to allow
* using counter signatures unverified there. Ditto for the early period
* of ring-3 hardened stub execution.
*/
#ifndef IN_RING0
if (!g_fHaveOtherRoots)
#endif
fFlags |= RTCRPKCS7VERIFY_SD_F_USE_SIGNING_TIME_UNVERIFIED | RTCRPKCS7VERIFY_SD_F_USE_MS_TIMESTAMP_UNVERIFIED;
return RTCrPkcs7VerifySignedData(pContentInfo, fFlags, g_hSpcAndNtKernelSuppStore, g_hSpcAndNtKernelRootStore,
}
/**
* Verifies the given loader image.
*
* @returns IPRT status code.
* @param hLdrMod File handle to the executable file.
* @param pwszName Full NT path to the DLL in question, used for
* dealing with unsigned system dlls as well as for
* @param pNtViRdr The reader instance /w flags.
* @param fAvoidWinVerifyTrust Whether to avoid WinVerifyTrust because of
* deadlock or other loader related dangers.
* @param pfWinVerifyTrust Where to return whether WinVerifyTrust was used.
* @param pErrInfo Pointer to error info structure. Optional.
*/
DECLHIDDEN(int) supHardenedWinVerifyImageByLdrMod(RTLDRMOD hLdrMod, PCRTUTF16 pwszName, PSUPHNTVIRDR pNtViRdr,
{
if (pfWinVerifyTrust)
*pfWinVerifyTrust = false;
#ifdef IN_RING3
/* Check that the caller has performed the necessary library initialization. */
"supHardenedWinVerifyImageByHandle: supHardenedWinInitImageVerifier was not called.");
#endif
/*
* Check the trusted installer bit first, if requested as it's somewhat
* cheaper than the rest.
*
* We relax this for system32 and a little for WinSxS, like we used to, as
* there are apparently some systems out there where the user, admin, or
* someone has changed the ownership of core windows DLLs like user32.dll
* and comctl32.dll. Since we need user32.dll and will be checking it's
* digital signature, it's reasonably safe to let this thru. (The report
* was of SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS
* owning user32.dll, see public ticket 13187, VBoxStartup.3.log.)
*
* We've also had problems with graphics driver components like ig75icd64.dll
* and atig6pxx.dll not being owned by TrustedInstaller, with the result
* that 3D got broken (mod by zero issue in test build 5). These were also
* SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS.
*
* In one report by 'thor' the WinSxS resident comctl32.dll was owned by
* SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS (with 4.3.16).
*/
/** @todo Since we're now allowing Builtin\Administrators after all, perhaps we
* could drop these system32 + winsxs hacks?? */
{
true /*fCheckSlash*/))
SUP_DPRINTF(("%ls: Relaxing the TrustedInstaller requirement for this DLL (it's in system32).\n", pwszName));
true /*fCheckSlash*/))
SUP_DPRINTF(("%ls: Relaxing the TrustedInstaller requirement for this DLL (it's in WinSxS).\n", pwszName));
else
"supHardenedWinVerifyImageByHandle: TrustedInstaller is not the owner of '%ls'.", pwszName);
}
/*
* Verify it.
*
* The PKCS #7 SignedData signature is checked in the callback. Any
* signing certificate restrictions are also enforced there.
*
* For the time being, we use the executable timestamp as the
* certificate validation date. We must query that first to avoid
* potential issues re-entering the loader code from the callback.
*
* Update: Save the first timestamp we validate with build cert and
* use this as a minimum timestamp for further build cert
* validations. This works around issues with old DLLs that
* we sign against with our certificate (crt, sdl, qt).
*/
int rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &pNtViRdr->uTimestamp, sizeof(pNtViRdr->uTimestamp));
if (RT_SUCCESS(rc))
{
#ifdef IN_RING3 /* Hack alert! (see above) */
#endif
#ifdef IN_RING3 /* Hack alert! (see above) */
if ((pNtViRdr->fFlags & SUPHNTVI_F_REQUIRE_BUILD_CERT) && g_uBuildTimestampHack == 0 && RT_SUCCESS(rc))
#endif
/*
* Microsoft doesn't sign a whole bunch of DLLs, so we have to
* ASSUME that a bunch of system DLLs are fine.
*/
if (rc == VERR_LDRVI_NOT_SIGNED)
if (RT_FAILURE(rc))
/*
* Check for the signature checking enforcement, if requested to do so.
*/
{
bool fEnforced = false;
int rc2 = RTLdrQueryProp(hLdrMod, RTLDRPROP_SIGNATURE_CHECKS_ENFORCED, &fEnforced, sizeof(fEnforced));
if (RT_FAILURE(rc2))
rc = RTErrInfoSetF(pErrInfo, rc2, "Querying RTLDRPROP_SIGNATURE_CHECKS_ENFORCED failed on %ls: %Rrc.",
else if (!fEnforced)
"The image '%ls' was not linked with /IntegrityCheck.", pwszName);
}
}
else
RTErrInfoSetF(pErrInfo, rc, "RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on %ls: %Rrc", pwszName, rc);
#ifdef IN_RING3
/*
* Pass it thru WinVerifyTrust when possible.
*/
if (!fAvoidWinVerifyTrust)
rc = supHardenedWinVerifyImageTrust(pNtViRdr->hFile, pwszName, pNtViRdr->fFlags, rc, pfWinVerifyTrust, pErrInfo);
#endif
#ifdef IN_SUP_HARDENED_R3
/*
* Hook for the LdrLoadDll code to schedule scanning of imports.
*/
if (RT_SUCCESS(rc))
#endif
return rc;
}
/**
* Verifies the given executable image.
*
* @returns IPRT status code.
* @param hFile File handle to the executable file.
* @param pwszName Full NT path to the DLL in question, used for
* dealing with unsigned system dlls as well as for
* @param fFlags Flags, SUPHNTVI_F_XXX.
* @param fAvoidWinVerifyTrust Whether to avoid WinVerifyTrust because of
* deadlock or other loader related dangers.
* @param pfWinVerifyTrust Where to return whether WinVerifyTrust was used.
* @param pErrInfo Pointer to error info structure. Optional.
*/
DECLHIDDEN(int) supHardenedWinVerifyImageByHandle(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, bool fAvoidWinVerifyTrust,
{
/*
* Create a reader instance.
*/
if (RT_SUCCESS(rc))
{
/*
* Open the image.
*/
if (fFlags & SUPHNTVI_F_RESOURCE_IMAGE)
if (RT_SUCCESS(rc))
{
/*
* Verify it.
*/
rc = supHardenedWinVerifyImageByLdrMod(hLdrMod, pwszName, pNtViRdr, fAvoidWinVerifyTrust, pfWinVerifyTrust, pErrInfo);
}
else
}
SUP_DPRINTF(("supHardenedWinVerifyImageByHandle: -> %d (%ls)%s\n",
return rc;
}
#ifdef IN_RING3
/**
* supHardenedWinVerifyImageByHandle version without the name.
*
* The name is derived from the handle.
*
* @returns IPRT status code.
* @param hFile File handle to the executable file.
* @param fFlags Flags, SUPHNTVI_F_XXX.
* @param pErrInfo Pointer to error info structure. Optional.
*/
DECLHIDDEN(int) supHardenedWinVerifyImageByHandleNoName(HANDLE hFile, uint32_t fFlags, PRTERRINFO pErrInfo)
{
/*
* Determine the NT name and call the verification function.
*/
union
{
} uBuf;
&uBuf,
&cbIgn);
if (NT_SUCCESS(rcNt))
else
return supHardenedWinVerifyImageByHandle(hFile, uBuf.UniStr.Buffer, fFlags, false /*fAvoidWinVerifyTrust*/,
}
#endif /* IN_RING3 */
/**
* Retrieves the full official path to the system root or one of it's sub
* directories.
*
* This code is also used by the support driver.
*
* @returns VBox status code.
* @param pvBuf The output buffer. This will contain a
* UNICODE_STRING followed (at the kernel's
* discretion) the string buffer.
* @param cbBuf The size of the buffer @a pvBuf points to.
* @param enmDir Which directory under the system root we're
* interested in.
* @param pErrInfo Pointer to error info structure. Optional.
*/
DECLHIDDEN(int) supHardNtGetSystemRootDir(void *pvBuf, uint32_t cbBuf, SUPHARDNTSYSROOTDIR enmDir, PRTERRINFO pErrInfo)
{
switch (enmDir)
{
{
break;
}
{
break;
}
default:
AssertFailed();
return VERR_INVALID_PARAMETER;
}
InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
if (NT_SUCCESS(rcNt))
{
&cbIgn);
if (NT_SUCCESS(rcNt))
{
{
/* Make sure it's terminated so it can safely be printed.*/
return VINF_SUCCESS;
}
}
return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "NtQueryObject failed on '%ls' dir: %#x", NtName.Buffer, rcNt);
}
return RTErrInfoSetF(pErrInfo, VERR_SUP_VP_SYSTEM32_PATH, "Failure to open '%ls': %#x", NtName.Buffer, rcNt);
}
/**
* Initialize one certificate entry.
*
* @returns VBox status code.
* @param pCert The X.509 certificate representation to init.
* @param pabCert The raw DER encoded certificate.
* @param cbCert The size of the raw certificate.
* @param pErrInfo Where to return extended error info. Optional.
* @param pszErrorTag Error tag.
*/
static int supHardNtViCertInit(PRTCRX509CERTIFICATE pCert, unsigned char const *pabCert, unsigned cbCert,
{
RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, "%s: cbCert=%#x out of range", pszErrorTag, cbCert));
RTAsn1CursorInitPrimary(&PrimaryCursor, pabCert, cbCert, pErrInfo, &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, NULL);
if (RT_SUCCESS(rc))
return rc;
}
static int supHardNtViCertStoreAddArray(RTCRSTORE hStore, PCSUPTAENTRY paCerts, unsigned cCerts, PRTERRINFO pErrInfo)
{
{
int rc = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_TAF_DER, paCerts[i].pch, paCerts[i].cb, pErrInfo);
if (RT_FAILURE(rc))
return rc;
}
return VINF_SUCCESS;
}
/**
* Initialize a certificate table.
*
* @param phStore Where to return the store pointer.
* @param paCerts1 Pointer to the first certificate table.
* @param cCerts1 Entries in the first certificate table.
* @param paCerts2 Pointer to the second certificate table.
* @param cCerts2 Entries in the second certificate table.
* @param paCerts3 Pointer to the third certificate table.
* @param cCerts3 Entries in the third certificate table.
* @param pErrInfo Where to return extended error info. Optional.
* @param pszErrorTag Error tag.
*/
{
if (RT_FAILURE(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
return rc;
}
#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
/**
* Initializes the windows paths.
*/
static void supHardenedWinInitImageVerifierWinPaths(void)
{
/*
* Windows paths that we're interested in.
*/
static const struct
{
WCHAR const *pwszRegValue;
const char *pszLogName;
} s_aPaths[] =
{
# ifdef RT_ARCH_AMD64
# endif
};
/*
* Open the registry key containing the paths.
*/
UNICODE_STRING NtName = RTNT_CONSTANT_UNISTR(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion");
InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
if (NT_SUCCESS(rcNt))
{
/*
* Loop over the paths and resolve their NT paths.
*/
{
/*
* Query the value first.
*/
union
{
} uBuf;
rcNt = NtQueryValueKey(hKey, &ValueName, KeyValuePartialInformation, &uBuf, sizeof(uBuf) - sizeof(WCHAR), &cbActual);
if (NT_SUCCESS(rcNt))
{
/*
* Must be a simple string value, terminate it.
*/
{
/*
* Expand any environment variable references before opening it.
* We use the result buffer as storage for the expaneded path,
* reserving space for the windows name space prefix.
*/
else
{
}
if (NT_SUCCESS(rcNt))
{
/*
* Include the \\??\\ prefix in the result and open the path.
*/
InitializeObjectAttributes(&ObjAttr, &Dst, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
if (NT_SUCCESS(rcNt))
{
/*
* Query the real NT name.
*/
&cbIgn);
if (NT_SUCCESS(rcNt))
{
{
/* Make sure it's terminated.*/
}
else
{
}
}
else
}
else
SUP_DPRINTF(("%s: NtCreateFile failed: %#x (%ls)\n",
}
else
SUP_DPRINTF(("%s: RtlExpandEnvironmentStrings_U failed: %#x (%ls)\n",
}
else
{
}
}
else
/* Stub the entry on failure. */
if (!NT_SUCCESS(rcNt))
{
}
}
}
else
{
/* Stub all the entries on failure. */
{
}
}
}
#endif /* IN_RING3 && !VBOX_PERMIT_EVEN_MORE */
/**
* This initializes the certificates globals so we don't have to reparse them
* every time we need to verify an image.
*
* @returns IPRT status code.
* @param pErrInfo Where to return extended error info. Optional.
*/
{
/*
* Get the system root paths.
*/
int rc = supHardNtGetSystemRootDir(&g_System32NtPath, sizeof(g_System32NtPath), kSupHardNtSysRootDir_System32, pErrInfo);
if (RT_SUCCESS(rc))
rc = supHardNtGetSystemRootDir(&g_WinSxSNtPath, sizeof(g_WinSxSNtPath), kSupHardNtSysRootDir_WinSxS, pErrInfo);
if (RT_SUCCESS(rc))
{
#if defined(IN_RING3) && !defined(VBOX_PERMIT_EVEN_MORE)
#endif
/*
* Initialize it, leaving the cleanup to the termination call.
*/
rc = supHardNtViCertInit(&g_BuildX509Cert, g_abSUPBuildCert, g_cbSUPBuildCert, pErrInfo, "BuildCertificate");
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
pErrInfo, "SpcAndNtKernelRoot");
if (RT_SUCCESS(rc))
pErrInfo, "SpcAndNtKernelSupplemental");
#if 0 /* For the time being, always trust the build certificate. It bypasses the timestamp issues of CRT and SDL. */
/* If the build certificate is a test singing certificate, it must be a
trusted root or we'll fail to validate anything. */
if ( RT_SUCCESS(rc)
&& RTCrX509Name_Compare(&g_BuildX509Cert.TbsCertificate.Subject, &g_BuildX509Cert.TbsCertificate.Issuer) == 0)
#else
if (RT_SUCCESS(rc))
#endif
if (RT_SUCCESS(rc))
{
/*
* Finally initialize known SIDs that we use.
*/
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(rcNt))
{
if (NT_SUCCESS(rcNt))
{
return VINF_SUCCESS;
}
}
}
}
}
return rc;
}
/**
* Releases resources allocated by supHardenedWinInitImageVerifier.
*/
DECLHIDDEN(void) supHardenedWinTermImageVerifier(void)
{
}
#ifdef IN_RING3
/**
* This is a hardcoded list of certificates we thing we might need.
*
* @returns true if wanted, false if not.
* @param pCert The certificate.
*/
{
char szSubject[512];
RTCrX509Name_FormatAsString(&pCert->TbsCertificate.Subject, szSubject, sizeof(szSubject) - 1, NULL);
/*
* Check that it's a plausible root certificate.
*/
{
return false;
}
{
{
return false;
}
{
return false;
}
}
if (pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.cBits < 256) /* mostly for u64KeyId reading. */
{
SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - key too small: %u bits %s\n",
return false;
}
uint64_t const u64KeyId = pCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.uBits.pu64[1];
# if 0
/*
* Whitelist - Array of names and key clues of the certificates we want.
*/
static struct
{
const char *pszName;
} const s_aWanted[] =
{
/* SPC */
{ UINT64_C(0xffffffffffffffff), "C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority" },
{ UINT64_C(0xffffffffffffffff), "L=Internet, O=VeriSign, Inc., OU=VeriSign Commercial Software Publishers CA" },
{ UINT64_C(0x491857ead79dde00), "C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority" },
/* TS */
{ UINT64_C(0xffffffffffffffff), "O=Microsoft Trust Network, OU=Microsoft Corporation, OU=Microsoft Time Stamping Service Root, OU=Copyright (c) 1997 Microsoft Corp." },
{ UINT64_C(0xffffffffffffffff), "O=VeriSign Trust Network, OU=VeriSign, Inc., OU=VeriSign Time Stamping Service Root, OU=NO LIABILITY ACCEPTED, (c)97 VeriSign, Inc." },
{ UINT64_C(0xffffffffffffffff), "C=ZA, ST=Western Cape, L=Durbanville, O=Thawte, OU=Thawte Certification, CN=Thawte Timestamping CA" },
/* Additional Windows 8.1 list: */
{ UINT64_C(0x3be670c1bd02a900), "OU=Copyright (c) 1997 Microsoft Corp., OU=Microsoft Corporation, CN=Microsoft Root Authority" },
{ UINT64_C(0x4d3835aa4180b200), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2011" },
{ UINT64_C(0xece4e4289e08b900), "C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010" },
{ UINT64_C(0x59faf1086271bf00), "C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2" },
{ UINT64_C(0x91e3728b8b40d000), "C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority" },
{ UINT64_C(0x61a3a33f81aace00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Object" },
{ 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" },
{ UINT64_C(0x8ff6fc03c1edbd00), "C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2" },
{ UINT64_C(0xa671e9fec832b700), "C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority" },
{ UINT64_C(0xa8de7211e13be200), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA" },
{ 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" },
{ UINT64_C(0x7ae89c50f0b6a00f), "C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root" },
{ 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" },
{ 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" },
{ UINT64_C(0xd4fbe673e5ccc600), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA" },
{ 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" },
{ UINT64_C(0x6e2ba21058eedf00), "C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN - DATACorp SGC" },
{ 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)" },
{ 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" },
{ UINT64_C(0x466cbc09db88c100), "C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority" },
{ 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" },
{ 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" },
{ UINT64_C(0x8043e4ce150ead00), "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Assured ID Root CA" },
{ UINT64_C(0x00f2e6331af7b700), "C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root" },
};
while (i-- > 0)
{
return true;
}
return false;
# else
/*
* Blacklist approach.
*/
static struct
{
const char *pszName;
} const s_aUnwanted[] =
{
{ UINT64_C(0xffffffffffffffff), "C=US, O=U.S. Robots and Mechanical Men, Inc., OU=V.I.K.I." }, /* dummy entry */
};
while (i-- > 0)
{
SUP_DPRINTF(("supR3HardenedWinIsDesiredRootCA: skipping - blacklisted: %#llx %s\n", u64KeyId, szSubject));
return false;
}
return true;
# endif
}
/**
* Loads a module in the system32 directory.
*
* @returns Module handle on success. Won't return on faliure.
* @param pszName The name of the DLL to load.
*/
{
&& fFlags
{
fFlags = 0;
}
return hMod;
}
/**
* Called by supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation to
* import selected root CAs from the system certificate store.
*
* These certificates permits us to correctly validate third party DLLs.
*/
static void supR3HardenedWinRetrieveTrustedRootCAs(void)
{
/*
* Load crypt32.dll and resolve the APIs we need.
*/
if (pfn##a_Name == NULL) supR3HardenedFatal("Error locating '" #a_Name "' in 'crypt32.dll': %u", RtlGetLastWin32Error())
/*
* Open the root store and look for the certificates we wish to use.
*/
HCERTSTORE hStore = pfnCertOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
if (!hStore)
if (hStore)
{
{
{
if (RT_SUCCESS(rc))
{
{
cAdded++;
}
}
/* XP root certificate "C&W HKT SecureNet CA SGC Root" has non-standard validity
timestamps, the UTC formatting isn't Zulu time but specifies timezone offsets.
Ignore these failures and certificates. */
else if (rc != VERR_ASN1_INVALID_UTC_TIME_ENCODING)
}
}
g_fHaveOtherRoots = true;
}
}
/**
* Resolves the WinVerifyTrust API after the process has been verified and
* installs a thread creation hook.
*
* The WinVerifyTrust API is used in addition our own Authenticode verification
* code. If the image has the IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY flag
* set, it will be checked again by the kernel. All our image has this flag set
* and we require all VBox extensions to have it set as well. In effect, the
* authenticode signature will be checked two or three times.
*
* @param pszProgName The program name.
*/
DECLHIDDEN(void) supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(const char *pszProgName)
{
# ifdef IN_SUP_HARDENED_R3
/*
* Load our the support library DLL that does the thread hooking as the
* security API may trigger the creation of COM worker threads (or
* whatever they are).
*
* The thread creation hook makes the threads very slippery to debuggers by
* irreversably disabling most (if not all) debug events for them.
*/
char szPath[RTPATH_MAX];
if (hSupLibMod == NULL)
# endif
/*
* Allocate TLS entry for WinVerifyTrust recursion prevention.
*/
if (iTls != TLS_OUT_OF_INDEXES)
else
/*
* Resolve it.
*/
do { \
} while (0)
PFNWINVERIFYTRUST pfnWinVerifyTrust = (PFNWINVERIFYTRUST)GetProcAddress(hWintrust, "WinVerifyTrust");
if (!pfnWinVerifyTrust)
supR3HardenedFatal("Error locating 'WinVerifyTrust' in 'Wintrust.dll': %u", RtlGetLastWin32Error());
RESOLVE_CRYPT_API(CryptCATAdminCalcHashFromFileHandle2, PFNCRYPTCATADMINCALCHASHFROMFILEHANDLE2, SUP_NT_VER_W80);
/*
* Call it on ourselves and ntdll to make sure it loads all the providers
* now, we would otherwise geting into recursive trouble in the
* NtCreateSection hook.
*/
# ifdef IN_SUP_HARDENED_R3
if (RT_FAILURE(rc))
# endif
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. */
supR3HardNtViCallWinVerifyTrust(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
supR3HardNtViCallWinVerifyTrustCatFile(NULL, L"\\SystemRoot\\System32\\ntdll.dll", 0, NULL, pfnWinVerifyTrust);
# ifdef IN_SUP_HARDENED_R3
/*
* Load some problematic DLLs into the verifier cache to prevent
* recursion trouble.
*/
supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\crypt32.dll");
supR3HardenedWinVerifyCachePreload(L"\\SystemRoot\\System32\\Wintrust.dll");
# endif
/*
* Now, get trusted root CAs so we can verify a broader scope of signatures.
*/
}
{
return VERR_FILENAME_TOO_LONG;
memcpy(&pwszWinPathBuf[sizeof(s_wszPrefix) / sizeof(RTUTF16) - 1], pwszNtName, (cwcNtName + 1) * sizeof(RTUTF16));
return VINF_SUCCESS;
}
/**
* Calls WinVerifyTrust to verify an PE image.
*
* @returns VBox status code.
* @param hFile File handle to the executable file.
* @param pwszName Full NT path to the DLL in question, used for
* dealing with unsigned system dlls as well as for
* @param fFlags Flags, SUPHNTVI_F_XXX.
* @param pErrInfo Pointer to error info structure. Optional.
* @param pfnWinVerifyTrust Pointer to the API.
*/
static int supR3HardNtViCallWinVerifyTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
{
/*
* Convert the name into a Windows name.
*/
int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
if (RT_FAILURE(rc))
return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrust: rc=%Rrc '%ls'", rc, pwszName);
/*
* Construct input parameters and call the API.
*/
TrustData.dwProvFlags = 0;
else
rc = VINF_SUCCESS;
else
{
/*
* Failed. Format a nice error message.
*/
# ifdef DEBUG_bird
__debugbreak();
# endif
const char *pszErrConst = NULL;
switch (hrc)
{
}
if (pszErrConst)
else
SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrust: WinVerifyTrust failed with %#x (%s) on '%ls'\n",
}
/* clean up state data. */
return rc;
}
/**
* Calls WinVerifyTrust to verify an PE image via catalog files.
*
* @returns VBox status code.
* @param hFile File handle to the executable file.
* @param pwszName Full NT path to the DLL in question, used for
* dealing with unsigned system dlls as well as for
* @param fFlags Flags, SUPHNTVI_F_XXX.
* @param pErrInfo Pointer to error info structure. Optional.
* @param pfnWinVerifyTrust Pointer to the API.
*/
static int supR3HardNtViCallWinVerifyTrustCatFile(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, PRTERRINFO pErrInfo,
{
/*
* Convert the name into a Windows name.
*/
int rc = supR3HardNtViNtToWinPath(pwszName, &pwszWinPath, wszWinPathBuf, RT_ELEMENTS(wszWinPathBuf));
if (RT_FAILURE(rc))
return RTErrInfoSetF(pErrInfo, rc, "Bad path passed to supR3HardNtViCallWinVerifyTrustCatFile: rc=%Rrc '%ls'", rc, pwszName);
/*
* Open the file if we didn't get a handle.
*/
{
InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
&ObjAttr,
&Ios,
NULL /* Allocation Size*/,
NULL /*EaBuffer*/,
0 /*EaLength*/);
if (NT_SUCCESS(rcNt))
if (!NT_SUCCESS(rcNt))
hFileClose = hFile;
}
/*
* On Windows 8.0 and later there are more than one digest choice.
*/
int fNoSignedCatalogFound = -1;
static struct
{
/** The digest algorithm name. */
const WCHAR *pszAlgorithm;
/** Cached catalog admin handle. */
HCATADMIN volatile hCachedCatAdmin;
} s_aHashes[] =
{
{ L"SHA256", NULL },
};
{
/*
* Another loop for dealing with different trust provider policies
* required for successfully validating different catalog signatures.
*/
bool fTryNextPolicy;
static const GUID s_aPolicies[] =
{
DRIVER_ACTION_VERIFY, /* Works with microsoft bits. Most frequently used, thus first. */
WINTRUST_ACTION_GENERIC_VERIFY_V2, /* Works with ATI and other SPC kernel-code signed stuff. */
};
do
{
/*
* Create a context.
*/
fTryNextPolicy = false;
bool fFreshContext = false;
if (hCatAdmin)
{
fFreshContext = false;
}
else
{
fFreshContext = true;
fRc = g_pfnCryptCATAdminAcquireContext2(&hCatAdmin, &s_aPolicies[iPolicy], s_aHashes[i].pszAlgorithm,
else
}
if (fRc)
{
/*
* Hash the file.
*/
else
if (fRc)
{
/* Produce a string version of it that we can pass to WinVerifyTrust. */
int rc2 = RTUtf16PrintHexBytes(wszDigest, RT_ELEMENTS(wszDigest), abHash, cbHash, RTSTRPRINTHEXBYTES_F_UPPER);
if (RT_SUCCESS(rc2))
{
SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: cbHash=%u wszDigest=%ls\n", cbHash, wszDigest));
/*
* Enumerate catalog information that matches the hash.
*/
do
{
/* Get the next match. */
HCATINFO hCatInfo = g_pfnCryptCATAdminEnumCatalogFromHash(hCatAdmin, abHash, cbHash, 0, &hCatInfoPrev);
if (!hCatInfo)
{
if (!fFreshContext)
{
SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: Retrying with fresh context (CryptCATAdminEnumCatalogFromHash -> %u; iCat=%#x)\n", RtlGetLastWin32Error(), iCat));
if (hCatInfoPrev != NULL)
goto l_fresh_context;
}
if (iCat == 0)
SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed ERRROR_NOT_FOUND (%u)\n", ulErr));
else if (iCat == 0)
SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATAdminEnumCatalogFromHash failed %u\n", ulErr));
break;
}
/*
* Call WinVerifyTrust.
*/
{
TrustData.dwProvFlags = 0;
else
SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: WinVerifyTrust => %#x; cat='%ls'; file='%ls'\n",
rc = VINF_SUCCESS;
else if (hrc == TRUST_E_NOSIGNATURE)
{ /* ignore because it's useless. */ }
else if (hrc == ERROR_INVALID_PARAMETER)
{ /* This is returned if the given file isn't found in the catalog, it seems. */ }
else
{
"WinVerifyTrust failed with hrc=%#x on '%ls' and .cat-file='%ls'.",
}
/* clean up state data. */
}
else
{
"CryptCATCatalogInfoFromContext failed: %d [file=%s]",
SUP_DPRINTF(("supR3HardNtViCallWinVerifyTrustCatFile: CryptCATCatalogInfoFromContext failed\n"));
}
iCat++;
if (hCatInfoPrev != NULL)
AssertFailed();
}
else
}
else
AssertFailed();
}
else
iPolicy++;
} while ( fTryNextPolicy
/*
* Only repeat if we've got g_pfnCryptCATAdminAcquireContext2 and can specify the hash algorithm.
*/
break;
if (rc != VERR_LDRVI_NOT_SIGNED)
break;
}
if (hFileClose != NULL)
/*
* DLLs that are likely candidates for local modifications.
*/
if (rc == VERR_LDRVI_NOT_SIGNED)
{
bool fCoreSystemDll = false;
if (supHardViUtf16PathStartsWithEx(pwszName, cwcName, g_System32NtPath.UniStr.Buffer, cwcOther, true /*fCheckSlash*/))
{
)
{
if (RTErrInfoIsSet(pErrInfo))
}
}
/* Kludge for ancient windows versions we don't want to support but
users still wants to use. Keep things as safe as possible without
unnecessary effort. Problem is that 3rd party catalog files cannot
easily be found. Showstopper for ATI users. */
if ( fNoSignedCatalogFound == 1
&& !fCoreSystemDll)
{
}
}
return rc;
}
/**
* Verifies the given image using WinVerifyTrust in some way.
*
* This is used by supHardenedWinVerifyImageByLdrMod as well as
* supR3HardenedScreenImage.
*
* @returns IPRT status code.
* @param hFile Handle of the file to verify.
* @param pwszName Full NT path to the DLL in question, used for
* dealing with unsigned system dlls as well as for
* @param pfWinVerifyTrust Where to return whether WinVerifyTrust was
* actually used.
* @param pErrInfo Pointer to error info structure. Optional.
*/
DECLHIDDEN(int) supHardenedWinVerifyImageTrust(HANDLE hFile, PCRTUTF16 pwszName, uint32_t fFlags, int rc,
{
if (pfWinVerifyTrust)
*pfWinVerifyTrust = false;
/*
* Call the windows verify trust API if we've resolved it and aren't in
* some obvious recursion.
*/
if (g_pfnWinVerifyTrust != NULL)
{
/* Check for recursion. */
bool fNoRecursion;
{
if (fNoRecursion)
}
else
{
}
if (fNoRecursion)
{
/* We can call WinVerifyTrust. */
if (pfWinVerifyTrust)
*pfWinVerifyTrust = true;
if (rc != VERR_LDRVI_NOT_SIGNED)
{
if (rc == VINF_LDRVI_NOT_SIGNED)
{
{
int rc2 = supR3HardNtViCallWinVerifyTrustCatFile(hFile, pwszName, fFlags, pErrInfo, g_pfnWinVerifyTrust);
}
else
{
AssertFailed();
}
}
else if (RT_SUCCESS(rc))
{
/** @todo having trouble with a 32-bit windows box when letting these calls thru */
}
else
{
}
}
/* Unwind recursion. */
TlsSetValue(g_iTlsWinVerifyTrustRecursion, (void *)0);
else
}
else
}
return rc;
}
/**
* Checks if WinVerifyTrust is callable on the current thread.
*
* Used by the main code to figure whether it makes sense to try revalidate an
* image that hasn't passed thru WinVerifyTrust yet.
*
* @returns true if callable on current thread, false if not.
*/
DECLHIDDEN(bool) supHardenedWinIsWinVerifyTrustCallable(void)
{
return g_pfnWinVerifyTrust != NULL
: g_idActiveThread != RTNtCurrentThreadId() );
}
/**
* Initializes g_uNtVerCombined and g_NtVerInfo.
* Called from suplibHardenedWindowsMain and suplibOsInit.
*/
DECLHIDDEN(void) supR3HardenedWinInitVersion(void)
{
/*
* Get the windows version. Use RtlGetVersion as GetVersionExW and
* GetVersion might not be telling the whole truth (8.0 on 8.1 depending on
* the application manifest).
*/
{
}
g_uNtVerCombined = SUP_MAKE_NT_VER_COMBINED(NtVerInfo.dwMajorVersion, NtVerInfo.dwMinorVersion, NtVerInfo.dwBuildNumber,
}
#endif /* IN_RING3 */