Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

7116 lines
256 KiB

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
fileutil.c
Abstract:
File-related functions for Windows NT Setup API dll.
Author:
Ted Miller (tedm) 11-Jan-1995
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include <ntverp.h>
//
// This guid is used for file signing/verification.
//
GUID DriverVerifyGuid = DRIVER_ACTION_VERIFY;
GUID AuthenticodeVerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
//
// Instantiate exception class GUID.
//
#include <initguid.h>
DEFINE_GUID( GUID_DEVCLASS_WINDOWS_COMPONENT_PUBLISHER, 0xF5776D81L, 0xAE53, 0x4935, 0x8E, 0x84, 0xB0, 0xB2, 0x83, 0xD8, 0xBC, 0xEF );
// Bit 0 indicates policy for filters (0 = critical, 1 = non-critical)
#define DDB_DRIVER_POLICY_CRITICAL_BIT (1 << 0)
// Bit 1 indicates policy for user-mode setup blocking (0 = block, 1 = no-block)
#define DDB_DRIVER_POLICY_SETUP_NO_BLOCK_BIT (1 << 1)
#define MIN_HASH_LEN 16
#define MAX_HASH_LEN 20
//
// Global list of device setup classes subject to driver signing policy, along
// with validation platform overrides (where applicable).
//
DRVSIGN_POLICY_LIST GlobalDrvSignPolicyList;
//
// private function prototypes
//
BOOL
ClassGuidInDrvSignPolicyList(
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
IN CONST GUID *DeviceSetupClassGuid, OPTIONAL
OUT PSP_ALTPLATFORM_INFO_V2 *ValidationPlatform OPTIONAL
);
DWORD
_VerifyCatalogFile(
IN PSETUP_LOG_CONTEXT LogContext,
IN LPCTSTR CatalogFullPath,
IN PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo, OPTIONAL
IN BOOL UseAuthenticodePolicy,
IN OUT HCERTSTORE *hStoreTrustedPublisher, OPTIONAL
OUT HANDLE *hWVTStateData OPTIONAL
);
BOOL
pSetupIsAuthenticodePublisherTrusted(
IN PCCERT_CONTEXT pcSignerCertContext,
IN OUT HCERTSTORE *hStoreTrustedPublisher OPTIONAL
);
BOOL
IsAutoCertInstallAllowed(
VOID
);
DWORD
pSetupInstallCertificate(
IN PCCERT_CONTEXT pcSignerCertContext
);
//
// helper to determine log level to use
//
__inline
DWORD
GetCatLogLevel(
IN DWORD Err,
IN BOOL DriverLevel
)
{
switch(Err) {
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case E_NOTIMPL:
return (DriverLevel ? DRIVER_LOG_VVERBOSE : SETUP_LOG_VVERBOSE);
default:
return (DriverLevel ? DRIVER_LOG_INFO : SETUP_LOG_INFO);
}
}
DWORD
ReadAsciiOrUnicodeTextFile(
IN HANDLE FileHandle,
OUT PTEXTFILE_READ_BUFFER Result,
IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
)
/*++
Routine Description:
Read in a text file that may be in either ascii or unicode format.
If the file is ascii, it is assumed to be ANSI format and is converted
to Unicode.
Arguments:
FileHandle - Supplies the handle of the text file to be read.
Result - supplies the address of a TEXTFILE_READ_BUFFER structure that
receives information about the text file buffer read. The structure
is defined as follows:
typedef struct _TEXTFILE_READ_BUFFER {
PCTSTR TextBuffer;
DWORD TextBufferSize;
HANDLE FileHandle;
HANDLE MappingHandle;
PVOID ViewAddress;
} TEXTFILE_READ_BUFFER, *PTEXTFILE_READ_BUFFER;
TextBuffer - pointer to the read-only character string containing
the entire text of the file.
(NOTE: If the file is a Unicode file with a Byte Order Mark
prefix, this Unicode character is not included in the returned
buffer.)
TextBufferSize - size of the TextBuffer (in characters).
FileHandle - If this is a valid handle (i.e., it's not equal to
INVALID_HANDLE_VALUE), then the file was already the native
character type, so the TextBuffer is simply the mapped-in image
of the file. This field is reserved for use by the
DestroyTextFileReadBuffer routine, and should not be accessed.
MappingHandle - If FileHandle is valid, then this contains the
mapping handle for the file image mapping.
This field is reserved for use by the DestroyTextFileReadBuffer
routine, and should not be accessed.
ViewAddress - If FileHandle is valid, then this contains the
starting memory address where the file image was mapped in.
This field is reserved for use by the DestroyTextFileReadBuffer
routine, and should not be accessed.
LogContext - for logging of errors/tracing
Return Value:
Win32 error value indicating the outcome.
Remarks:
Upon return from this routine, the caller MUST NOT attempt to close
FileHandle. This routine with either close the handle itself (after it's
finished with it, or upon error), or it will store the handle away in the
TEXTFILE_READ_BUFFER struct, to be later closed via
DestroyTextFileReadBuffer().
--*/
{
DWORD rc;
DWORD FileSize;
HANDLE MappingHandle;
PVOID ViewAddress, TextStartAddress;
BOOL IsNativeChar;
UINT SysCodePage = CP_ACP;
//
// Map the file for read access.
//
rc = pSetupMapFileForRead(
FileHandle,
&FileSize,
&MappingHandle,
&ViewAddress
);
if(rc != NO_ERROR) {
//
// We couldn't map the file--close the file handle now.
//
CloseHandle(FileHandle);
} else {
//
// Determine whether the file is unicode. Guard with try/except in
// case we get an inpage error.
//
try {
//
// Check to see if the file starts with a Unicode Byte Order Mark
// (BOM) character (0xFEFF). If so, then we know that the file
// is Unicode, and don't have to go through the slow process of
// trying to figure it out.
//
TextStartAddress = ViewAddress;
if((FileSize >= sizeof(WCHAR)) && (*(PWCHAR)TextStartAddress == 0xFEFF)) {
//
// The file has the BOM prefix. Adjust the pointer to the
// start of the text, so that we don't include the marker
// in the text buffer we return.
//
IsNativeChar = TRUE;
((PWCHAR)TextStartAddress)++;
FileSize -= sizeof(WCHAR);
} else {
IsNativeChar = IsTextUnicode(TextStartAddress,FileSize,NULL);
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_READ_FAULT, &rc);
}
if(rc == NO_ERROR) {
if(IsNativeChar) {
//
// No conversion is required--we'll just use the mapped-in
// image in memory.
//
Result->TextBuffer = TextStartAddress;
Result->TextBufferSize = FileSize / sizeof(TCHAR);
Result->FileHandle = FileHandle;
Result->MappingHandle = MappingHandle;
Result->ViewAddress = ViewAddress;
} else {
DWORD NativeCharCount;
PTCHAR Buffer;
//
// Need to convert the file to the native character type.
// Allocate a buffer that is maximally sized.
// The maximum size of the unicode text is
// double the size of the oem text, and would occur
// when each oem character is single-byte.
//
if(Buffer = MyMalloc(FileSize * sizeof(TCHAR))) {
try {
//
// FUTURE-1999/09/01-JamieHun -- Implement ANSI Inf Language=xxxx
// Need to come up with a better way of determining
// what code-page to interpret INF file under.
// Currently we use the install-base.
//
SysCodePage = CP_ACP;
rc = GLE_FN_CALL(0,
NativeCharCount = MultiByteToWideChar(
SysCodePage,
MB_PRECOMPOSED,
TextStartAddress,
FileSize,
Buffer,
FileSize)
);
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(),
ERROR_READ_FAULT,
&rc
);
}
} else {
rc = ERROR_NOT_ENOUGH_MEMORY;
}
if(rc == NO_ERROR) {
//
// If the converted buffer doesn't require the entire block
// we allocated, attempt to reallocate the buffer to its
// correct size. We don't care if this fails, since the
// buffer we have is perfectly fine (just bigger than we
// need).
//
if(!(Result->TextBuffer = MyRealloc(Buffer, NativeCharCount * sizeof(TCHAR)))) {
Result->TextBuffer = Buffer;
}
Result->TextBufferSize = NativeCharCount;
Result->FileHandle = INVALID_HANDLE_VALUE;
} else {
//
// Free the buffer, if it was previously allocated.
//
if(Buffer) {
MyFree(Buffer);
}
}
}
}
//
// If the file was already in native character form and we didn't
// enounter any errors, then we don't want to close it, because we
// use the mapped-in view directly.
//
if((rc != NO_ERROR) || !IsNativeChar) {
pSetupUnmapAndCloseFile(FileHandle, MappingHandle, ViewAddress);
}
}
return rc;
}
BOOL
DestroyTextFileReadBuffer(
IN PTEXTFILE_READ_BUFFER ReadBuffer
)
/*++
Routine Description:
Destroy a textfile read buffer created by ReadAsciiOrUnicodeTextFile.
Arguments:
ReadBuffer - supplies the address of a TEXTFILE_READ_BUFFER structure
for the buffer to be destroyed.
Return Value:
BOOLean value indicating success or failure.
--*/
{
//
// If our ReadBuffer structure has a valid FileHandle, then we must
// unmap and close the file, otherwise, we simply need to free the
// allocated buffer.
//
if(ReadBuffer->FileHandle != INVALID_HANDLE_VALUE) {
return pSetupUnmapAndCloseFile(ReadBuffer->FileHandle,
ReadBuffer->MappingHandle,
ReadBuffer->ViewAddress
);
} else {
MyFree(ReadBuffer->TextBuffer);
return TRUE;
}
}
BOOL
GetVersionInfoFromImage(
IN PCTSTR FileName,
OUT PDWORDLONG Version,
OUT LANGID *Language
)
/*++
Routine Description:
Retrieve file version and language info from a file.
The version is specified in the dwFileVersionMS and dwFileVersionLS fields
of a VS_FIXEDFILEINFO, as filled in by the win32 version APIs. For the
language we look at the translation table in the version resources and
assume that the first langid/codepage pair specifies the language.
If the file is not a coff image or does not have version resources,
the function fails. The function does not fail if we are able to retrieve
the version but not the language.
Arguments:
FileName - supplies the full path of the file whose version data is desired.
Version - receives the version stamp of the file. If the file is not a coff
image or does not contain the appropriate version resource data, the
function fails.
Language - receives the language id of the file. If the file is not a coff
image or does not contain the appropriate version resource data, this
will be 0 and the function succeeds.
Return Value:
TRUE if we were able to retreive at least the version stamp.
FALSE otherwise.
--*/
{
DWORD d;
PVOID VersionBlock = NULL;
VS_FIXEDFILEINFO *FixedVersionInfo;
UINT DataLength;
BOOL b;
PWORD Translation;
DWORD Ignored;
//
// Assume failure
//
b = FALSE;
try {
//
// Get the size of version info block.
//
d = GetFileVersionInfoSize((PTSTR)FileName, &Ignored);
if(!d) {
leave;
}
//
// Allocate memory block of sufficient size to hold version info block
//
VersionBlock = MyMalloc(d);
if(!VersionBlock) {
leave;
}
//
// Get the version block from the file.
//
if(!GetFileVersionInfo((PTSTR)FileName, 0, d, VersionBlock)) {
leave;
}
//
// Get fixed version info.
//
if(VerQueryValue(VersionBlock,
TEXT("\\"),
&FixedVersionInfo,
&DataLength)) {
//
// If we get here, we declare success, even if there is no
// language.
//
b = TRUE;
//
// Return version to caller.
//
*Version = (((DWORDLONG)FixedVersionInfo->dwFileVersionMS) << 32)
+ FixedVersionInfo->dwFileVersionLS;
//
// Attempt to get language of file. We'll simply ask for the
// translation table and use the first language id we find in there
// as *the* language of the file.
//
// The translation table consists of LANGID/Codepage pairs.
//
if(VerQueryValue(VersionBlock,
TEXT("\\VarFileInfo\\Translation"),
&Translation,
&DataLength)
&& (DataLength >= (2*sizeof(WORD)))) {
*Language = Translation[0];
} else {
//
// No language
//
*Language = 0;
}
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_READ_FAULT, NULL);
b = FALSE;
}
if(VersionBlock) {
MyFree(VersionBlock);
}
return b;
}
BOOL
pSetupGetVersionInfoFromImage(
IN PCTSTR FileName,
OUT PULARGE_INTEGER Version,
OUT LANGID *Language
)
/*++
Routine Description:
See GetVersionInfoFromImage for description
Semi-public version that uses the more friendly ULARGE_INTEGER
Arguments:
FileName - supplies the full path of the file whose version data is
desired.
Version - receives the version stamp of the file. If the file is not a coff
image or does not contain the appropriate version resource data, the
function fails.
Language - receives the language id of the file. If the file is not a coff
image or does not contain the appropriate version resource data, this
will be 0 and the function succeeds.
Return Value:
TRUE if we were able to retreive at least the version stamp.
FALSE otherwise.
--*/
{
DWORDLONG privateVersion=0;
BOOL result;
result = GetVersionInfoFromImage(FileName, &privateVersion, Language);
if(result && Version) {
Version->QuadPart = privateVersion;
}
return result;
}
BOOL
AddFileTimeSeconds(
IN const FILETIME *Base,
OUT FILETIME *Target,
IN INT Seconds
)
/*++
Routine Description:
Bias the filetime by specified number of seconds
Arguments:
Base - original file time
Target - new file time
Seconds - number of seconds to bias it by
Return Value:
If successful, returns TRUE
If out of bounds, returns FALSE and Target set to be same as Base
--*/
{
ULARGE_INTEGER Fuddle;
//
// bias off FileTimeThen as it's the greater time
//
Fuddle.LowPart = Base->dwLowDateTime;
Fuddle.HighPart = Base->dwHighDateTime;
Fuddle.QuadPart += 10000000i64 * Seconds;
if(Fuddle.HighPart < 0x80000000) {
Target->dwHighDateTime = Fuddle.HighPart;
Target->dwLowDateTime = Fuddle.LowPart;
return TRUE;
} else {
*Target = *Base;
return FALSE;
}
}
DWORD
GetSetFileTimestamp(
IN PCTSTR FileName,
IN OUT FILETIME *CreateTime, OPTIONAL
OUT FILETIME *AccessTime, OPTIONAL
OUT FILETIME *WriteTime, OPTIONAL
IN BOOL Set
)
/*++
Routine Description:
Get or set a file's timestamp values.
Arguments:
FileName - supplies full path of file to get or set timestamps
CreateTime - if specified and the underlying filesystem supports it,
receives the creation time of the file.
AccessTime - if specified and the underlying filesystem supports it,
receives the last access time of the file.
WriteTime - if specified, receives the last write time of the file.
Set - if TRUE, set the file's timestamp(s) to the caller-supplied value(s).
Otherwise, simply retrieve the value(s).
Return Value:
If successful, returns NO_ERROR, otherwise returns the Win32 error
indicating the cause of failure.
--*/
{
HANDLE h = INVALID_HANDLE_VALUE;
DWORD d;
try {
d = GLE_FN_CALL(INVALID_HANDLE_VALUE,
h = CreateFile(FileName,
Set ? GENERIC_WRITE : GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL)
);
if(d != NO_ERROR) {
leave;
}
if(Set) {
d = GLE_FN_CALL(FALSE,
SetFileTime(h, CreateTime, AccessTime, WriteTime)
);
} else {
d = GLE_FN_CALL(FALSE,
GetFileTime(h, CreateTime, AccessTime, WriteTime)
);
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_READ_FAULT, &d);
}
if(h != INVALID_HANDLE_VALUE) {
CloseHandle(h);
}
return d;
}
DWORD
RetreiveFileSecurity(
IN PCTSTR FileName,
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
)
/*++
Routine Description:
Retrieve security information from a file and place it into a buffer.
Arguments:
FileName - supplies name of file whose security information is desired.
SecurityDescriptor - If the function is successful, receives pointer
to buffer containing security information for the file. The pointer
may be NULL, indicating that there is no security information
associated with the file or that the underlying filesystem does not
support file security.
Return Value:
Win32 error code indicating outcome. If NO_ERROR check the value returned
in SecurityDescriptor.
The caller can free the buffer with MyFree() when done with it.
--*/
{
DWORD d;
DWORD BytesRequired;
PSECURITY_DESCRIPTOR p = NULL;
try {
BytesRequired = 1024; // start out with a reasonably-sized buffer
while(NULL != (p = MyMalloc(BytesRequired))) {
//
// Get the security.
//
d = GLE_FN_CALL(FALSE,
GetFileSecurity(
FileName,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
p,
BytesRequired,
&BytesRequired)
);
if(d == NO_ERROR) {
*SecurityDescriptor = p;
p = NULL; // so we won't try to free it later
leave;
}
//
// Return an error code, unless we just need a bigger buffer
//
MyFree(p);
p = NULL;
if(d != ERROR_INSUFFICIENT_BUFFER) {
leave;
}
//
// Otherwise, we'll try again with a bigger buffer
//
}
//
// If we get to here, then we failed due to insufficient memory.
//
d = ERROR_NOT_ENOUGH_MEMORY;
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_READ_FAULT, &d);
}
if(p) {
MyFree(p);
}
return d;
}
DWORD
StampFileSecurity(
IN PCTSTR FileName,
IN PSECURITY_DESCRIPTOR SecurityInfo
)
/*++
Routine Description:
Set security information on a file.
Arguments:
FileName - supplies name of file whose security information is desired.
SecurityDescriptor - supplies pointer to buffer containing security
information for the file. This buffer should have been returned by a
call to RetreiveFileSecurity. If the underlying filesystem does not
support file security, the function fails.
Return Value:
Win32 error code indicating outcome.
--*/
{
return GLE_FN_CALL(FALSE,
SetFileSecurity(FileName,
(OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION),
SecurityInfo)
);
}
DWORD
TakeOwnershipOfFile(
IN PCTSTR Filename
)
/*++
Routine Description:
Sets the owner of a given file to the default owner specified in
the current process token.
Arguments:
FileName - supplies fully-qualified path of the file of which to take
ownership.
Return Value:
Win32 error code indicating outcome.
--*/
{
SECURITY_DESCRIPTOR SecurityDescriptor;
DWORD Err;
HANDLE Token;
DWORD BytesRequired;
PTOKEN_OWNER OwnerInfo = NULL;
//
// Open the process token.
//
Err = GLE_FN_CALL(FALSE,
OpenProcessToken(GetCurrentProcess(),
TOKEN_QUERY,
&Token)
);
if(Err != NO_ERROR) {
return Err;
}
try {
//
// Get the current process's default owner sid.
//
Err = GLE_FN_CALL(FALSE,
GetTokenInformation(Token,
TokenOwner,
NULL,
0,
&BytesRequired)
);
MYASSERT(Err != NO_ERROR);
if(Err != ERROR_INSUFFICIENT_BUFFER) {
leave;
}
OwnerInfo = MyMalloc(BytesRequired);
if(!OwnerInfo) {
Err = ERROR_NOT_ENOUGH_MEMORY;
leave;
}
Err = GLE_FN_CALL(FALSE,
GetTokenInformation(Token,
TokenOwner,
OwnerInfo,
BytesRequired,
&BytesRequired)
);
if(Err != NO_ERROR) {
leave;
}
//
// Initialize the security descriptor.
//
Err = GLE_FN_CALL(FALSE,
InitializeSecurityDescriptor(
&SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION)
);
if(Err != NO_ERROR) {
leave;
}
Err = GLE_FN_CALL(FALSE,
SetSecurityDescriptorOwner(&SecurityDescriptor,
OwnerInfo->Owner,
FALSE)
);
if(Err != NO_ERROR) {
leave;
}
//
// Set file security.
//
Err = GLE_FN_CALL(FALSE,
SetFileSecurity(Filename,
OWNER_SECURITY_INFORMATION,
&SecurityDescriptor)
);
//
// Not all filesystems support this operation.
//
if(Err == ERROR_NOT_SUPPORTED) {
Err = NO_ERROR;
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_READ_FAULT, &Err);
}
if(OwnerInfo) {
MyFree(OwnerInfo);
}
CloseHandle(Token);
return Err;
}
DWORD
SearchForInfFile(
IN PCTSTR InfName,
OUT LPWIN32_FIND_DATA FindData,
IN DWORD SearchControl,
OUT PTSTR FullInfPath,
IN UINT FullInfPathSize,
OUT PUINT RequiredSize OPTIONAL
)
/*++
Routine Description:
This routine searches for an INF file in the manner specified
by the SearchControl parameter. If the file is found, its
full path is returned.
Arguments:
InfName - Supplies name of INF to search for. This name is simply
appended to the two search directory paths, so if the name
contains directories, the file will searched for in the
subdirectory under the search directory. I.e.:
\foo\bar.inf
will be searched for as %windir%\inf\foo\bar.inf and
%windir%\system32\foo\bar.inf.
FindData - Supplies the address of a Win32 Find Data structure that
receives information about the file specified (if it is found).
SearchControl - Specifies the order in which directories should
be searched:
INFINFO_DEFAULT_SEARCH : search %windir%\inf, then %windir%\system32
INFINFO_REVERSE_DEFAULT_SEARCH : reverse of the above
INFINFO_INF_PATH_LIST_SEARCH : search for the INF in each of the
directories listed in the DevicePath value entry under:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion.
FullInfPath - If the file is found, receives the full path of the INF.
FullInfPathSize - Supplies the size of the FullInfPath buffer (in
characters).
RequiredSize - Optionally, receives the number of characters (including
terminating NULL) required to store the FullInfPath.
Return Value:
Win32 error code indicating whether the function was successful. Common
return values are:
NO_ERROR if the file was found, and the INF file path returned
successfully.
ERROR_INSUFFICIENT_BUFFER if the supplied buffer was not large enough
to hold the full INF path (RequiredSize will indicated how large
the buffer needs to be)
ERROR_FILE_NOT_FOUND if the file was not found.
ERROR_INVALID_PARAMETER if the SearchControl parameter is invalid.
--*/
{
PCTSTR PathList;
TCHAR CurInfPath[MAX_PATH];
PCTSTR PathPtr, InfPathLocation;
DWORD PathLength;
BOOL b, FreePathList;
DWORD d;
//
// Retrieve the path list.
//
if(SearchControl == INFINFO_INF_PATH_LIST_SEARCH) {
//
// Just use our global list of INF search paths.
//
PathList = InfSearchPaths;
FreePathList = FALSE;
} else {
if(!(PathList = AllocAndReturnDriverSearchList(SearchControl))) {
return ERROR_NOT_ENOUGH_MEMORY;
}
FreePathList = TRUE;
}
d = NO_ERROR;
InfPathLocation = NULL;
try {
//
// Now look for the INF in each path in our MultiSz list.
//
for(PathPtr = PathList; *PathPtr; PathPtr += (lstrlen(PathPtr) + 1)) {
//
// Concatenate the INF file name with the current search path.
//
if(FAILED(StringCchCopy(CurInfPath, SIZECHARS(CurInfPath), PathPtr))) {
//
// not a valid path, don't bother trying to do FileExists
//
continue;
}
if(!pSetupConcatenatePaths(CurInfPath,
InfName,
SIZECHARS(CurInfPath),
&PathLength)) {
//
// not a valid path, don't bother trying to do FileExists
//
continue;
}
d = GLE_FN_CALL(FALSE, FileExists(CurInfPath, FindData));
if(d == NO_ERROR) {
if(!(FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
InfPathLocation = CurInfPath;
break;
}
} else {
//
// See if we got a 'real' error
//
if((d != ERROR_NO_MORE_FILES) &&
(d != ERROR_FILE_NOT_FOUND) &&
(d != ERROR_PATH_NOT_FOUND)) {
//
// This is a 'real' error, abort the search.
//
break;
}
//
// reset error to NO_ERROR and continue search
//
d = NO_ERROR;
}
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &d);
}
//
// Whatever the outcome, we're through with the PathList buffer.
//
if(FreePathList) {
MyFree(PathList);
}
if(d != NO_ERROR) {
return d;
} else if(!InfPathLocation) {
return ERROR_FILE_NOT_FOUND;
}
if(RequiredSize) {
*RequiredSize = PathLength;
}
if(PathLength > FullInfPathSize) {
return ERROR_INSUFFICIENT_BUFFER;
}
CopyMemory(FullInfPath,
InfPathLocation,
PathLength * sizeof(TCHAR)
);
return NO_ERROR;
}
DWORD
MultiSzFromSearchControl(
IN DWORD SearchControl,
OUT PTCHAR PathList,
IN DWORD PathListSize,
OUT PDWORD RequiredSize OPTIONAL
)
/*++
Routine Description:
This routine takes a search control ordinal and builds a MultiSz list
based on the search list it specifies.
Arguments:
SearchControl - Specifies the directory list to be built. May be one
of the following values:
INFINFO_DEFAULT_SEARCH : %windir%\inf, then %windir%\system32
INFINFO_REVERSE_DEFAULT_SEARCH : reverse of the above
INFINFO_INF_PATH_LIST_SEARCH : Each of the directories listed in
the DevicePath value entry under:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion.
PathList - Supplies the address of a character buffer that will receive
the MultiSz list.
PathListSize - Supplies the size, in characters, of the PathList buffer.
RequiredSize - Optionally, receives the number of characters required
to store the MultiSz PathList.
(NOTE: The user-supplied buffer is used to retrieve the value entry
from the registry. Therefore, if the value is a REG_EXPAND_SZ entry,
the RequiredSize parameter may be set too small on an
ERROR_INSUFFICIENT_BUFFER error. This will happen if the buffer was
too small to retrieve the value entry, before expansion. In this case,
calling the API again with a buffer sized according to the RequiredSize
output may fail with an ERROR_INSUFFICIENT_BUFFER yet again, since
expansion may require an even larger buffer.)
Return Value:
If successful, returns NO_ERROR.
If failure, returns a Win32 error code indicating the cause of the failure.
--*/
{
HKEY hk;
PCTSTR Path1, Path2;
PTSTR PathBuffer;
DWORD RegDataType, PathLength, PathLength1, PathLength2;
DWORD NumPaths, Err;
BOOL UseDefaultDevicePath;
if(PathList) {
Err = NO_ERROR; // assume success.
} else {
return ERROR_INVALID_PARAMETER;
}
UseDefaultDevicePath = FALSE;
if(SearchControl == INFINFO_INF_PATH_LIST_SEARCH) {
//
// Retrieve the INF search path list from the registry.
//
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
pszPathSetup,
0,
KEY_READ,
&hk) != ERROR_SUCCESS) {
//
// Fall back to default (just the Inf directory).
//
UseDefaultDevicePath = TRUE;
} else {
PathBuffer = NULL;
try {
//
// Get the DevicePath value entry. Support REG_SZ or
// REG_EXPAND_SZ data.
//
PathLength = PathListSize * sizeof(TCHAR);
Err = RegQueryValueEx(hk,
pszDevicePath,
NULL,
&RegDataType,
(LPBYTE)PathList,
&PathLength
);
//
// Need path length in characters from now on.
//
PathLength /= sizeof(TCHAR);
if(Err == ERROR_SUCCESS) {
//
// Check if the caller's buffer has room for extra NULL
// terminator.
//
if(PathLength >= PathListSize) {
PathLength++;
Err = ERROR_INSUFFICIENT_BUFFER;
} else if((RegDataType == REG_SZ) || (RegDataType == REG_EXPAND_SZ)) {
//
// Convert this semicolon-delimited list to a
// REG_MULTI_SZ.
//
NumPaths = DelimStringToMultiSz(PathList,
PathLength,
TEXT(';')
);
//
// Allocate a temporary buffer large enough to hold the
// number of paths in the MULTI_SZ list, each having
// maximum length (plus an extra terminating NULL at
// the end).
//
if(!(PathBuffer = MyMalloc((NumPaths * MAX_PATH * sizeof(TCHAR))
+ sizeof(TCHAR)))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
leave;
}
PathLength = 0;
for(Path1 = PathList;
*Path1;
Path1 += lstrlen(Path1) + 1) {
if(RegDataType == REG_EXPAND_SZ) {
DWORD SubPathLength;
SubPathLength = ExpandEnvironmentStrings(
Path1,
PathBuffer + PathLength,
MAX_PATH
);
if(SubPathLength <= MAX_PATH) {
PathLength += lstrlen(PathBuffer+PathLength) + 1;
}
} else {
if(SUCCEEDED(StringCchCopy(PathBuffer + PathLength,
MAX_PATH,
Path1))) {
PathLength += lstrlen(PathBuffer+PathLength) + 1;
}
}
//
// If the last character in this path is a
// backslash, then strip it off.
// PathLength at this point includes terminating
// NULL char at PathBuffer[PathLength-1] is (or
// should be) NULL char at PathBuffer[PathLength-2]
// may be '\'
//
if(*CharPrev(PathBuffer, PathBuffer + PathLength - 1) == TEXT('\\')) {
*(PathBuffer + PathLength - 2) = TEXT('\0');
PathLength--;
}
}
//
// Add additional terminating NULL at the end.
//
*(PathBuffer + PathLength) = TEXT('\0');
if(++PathLength > PathListSize) {
Err = ERROR_INSUFFICIENT_BUFFER;
} else {
CopyMemory(PathList,
PathBuffer,
PathLength * sizeof(TCHAR)
);
}
MyFree(PathBuffer);
PathBuffer = NULL;
} else {
//
// Bad data type--just use the Inf directory.
//
UseDefaultDevicePath = TRUE;
}
} else if(Err == ERROR_MORE_DATA){
Err = ERROR_INSUFFICIENT_BUFFER;
} else {
//
// Fall back to default (just the Inf directory).
//
UseDefaultDevicePath = TRUE;
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
//
// Fall back to default (just the Inf directory).
//
UseDefaultDevicePath = TRUE;
if(PathBuffer) {
MyFree(PathBuffer);
}
}
RegCloseKey(hk);
}
}
if(UseDefaultDevicePath) {
PathLength = lstrlen(InfDirectory) + 2;
if(PathLength > PathListSize) {
Err = ERROR_INSUFFICIENT_BUFFER;
} else {
Err = NO_ERROR;
CopyMemory(PathList, InfDirectory, (PathLength - 1) * sizeof(TCHAR));
//
// Add extra NULL to terminate the list.
//
PathList[PathLength - 1] = TEXT('\0');
}
} else if((Err == NO_ERROR) && (SearchControl != INFINFO_INF_PATH_LIST_SEARCH)) {
switch(SearchControl) {
case INFINFO_DEFAULT_SEARCH :
Path1 = InfDirectory;
Path2 = SystemDirectory;
break;
case INFINFO_REVERSE_DEFAULT_SEARCH :
Path1 = SystemDirectory;
Path2 = InfDirectory;
break;
default :
return ERROR_INVALID_PARAMETER;
}
PathLength1 = lstrlen(Path1) + 1;
PathLength2 = lstrlen(Path2) + 1;
PathLength = PathLength1 + PathLength2 + 1;
if(PathLength > PathListSize) {
Err = ERROR_INSUFFICIENT_BUFFER;
} else {
CopyMemory(PathList, Path1, PathLength1 * sizeof(TCHAR));
CopyMemory(&(PathList[PathLength1]), Path2, PathLength2 * sizeof(TCHAR));
//
// Add additional terminating NULL at the end.
//
PathList[PathLength - 1] = TEXT('\0');
}
}
if(((Err == NO_ERROR) || (Err == ERROR_INSUFFICIENT_BUFFER)) && RequiredSize) {
*RequiredSize = PathLength;
}
return Err;
}
PTSTR
AllocAndReturnDriverSearchList(
IN DWORD SearchControl
)
/*++
Routine Description:
This routine returns a buffer contains a multi-sz list of all directory
paths in our driver search path list.
The buffer returned must be freed with MyFree().
Arguments:
SearchControl - Specifies the directory list to be retrieved. May be one
of the following values:
INFINFO_DEFAULT_SEARCH : %windir%\inf, then %windir%\system32
INFINFO_REVERSE_DEFAULT_SEARCH : reverse of the above
INFINFO_INF_PATH_LIST_SEARCH : Each of the directories listed in
the DevicePath value entry under:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion.
Returns:
Pointer to the allocated buffer containing the list, or NULL if a failure
was encountered (typically, due to out-of-memory).
--*/
{
PTSTR PathListBuffer = NULL, TrimBuffer = NULL;
DWORD BufferSize;
DWORD Err;
try {
//
// Start out with a buffer of MAX_PATH length, which should cover most cases.
//
BufferSize = MAX_PATH;
//
// Loop on a call to MultiSzFromSearchControl until we succeed or hit
// some error other than buffer-too-small. There are two reasons for
// this. First, it is possible that someone could have added a new
// path to the registry list between calls, and second, since that
// routine uses our buffer to retrieve the original (non-expanded)
// list, it can only report the size it needs to retrieve the
// unexpanded list. After it is given enough space to retrieve it,
// _then_ it can tell us how much space we really need.
//
// With all that said, we'll almost never see this call made more than
// once.
//
while(NULL != (PathListBuffer = MyMalloc((BufferSize+2) * sizeof(TCHAR)))) {
if((Err = MultiSzFromSearchControl(SearchControl,
PathListBuffer,
BufferSize,
&BufferSize)) == NO_ERROR) {
//
// We've successfully retrieved the path list. If the list is
// larger than necessary (the normal case), then trim it down
// before returning. (If this fails it's no big deal--we'll
// just keep on using the original buffer.)
//
TrimBuffer = MyRealloc(PathListBuffer,
(BufferSize+2) * sizeof(TCHAR)
);
if(TrimBuffer) {
PathListBuffer = TrimBuffer;
}
//
// We succeeded--break out of the loop.
//
break;
} else {
//
// Free our current buffer before we find out what went wrong.
//
MyFree(PathListBuffer);
PathListBuffer = NULL;
if(Err != ERROR_INSUFFICIENT_BUFFER) {
//
// We failed.
//
leave;
}
}
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
if(TrimBuffer) {
MyFree(TrimBuffer);
} else if(PathListBuffer) {
MyFree(PathListBuffer);
}
PathListBuffer = NULL;
}
return PathListBuffer;
}
BOOL
DoMove(
IN PCTSTR CurrentName,
IN PCTSTR NewName
)
/*++
Routine Description:
Wrapper for MoveFileEx on NT
Arguments:
CurrentName - supplies the name of the file as it exists currently.
NewName - supplies the new name
Returns:
Boolean value indicating outcome. If failure, last error is set.
--*/
{
return MoveFileEx(CurrentName, NewName, MOVEFILE_REPLACE_EXISTING);
}
BOOL
DelayedMove(
IN PCTSTR CurrentName,
IN PCTSTR NewName OPTIONAL
)
/*++
Routine Description:
Queue a file for copy or delete on next reboot.
On Windows NT this means using MoveFileEx().
Arguments:
CurrentName - supplies the name of the file as it exists currently.
NewName - if specified supplies the new name. If not specified
then the file named by CurrentName is deleted on next reboot.
Returns:
Boolean value indicating outcome. If failure, last error is set.
--*/
{
return MoveFileEx(CurrentName,
NewName,
MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT
);
}
typedef enum _OEM_FILE_TYPE {
OemFiletypeInf,
OemFiletypeCat
} OEM_FILE_TYPE, *POEM_FILE_TYPE;
BOOL
IsInstalledFileFromOem(
IN PCTSTR Filename,
IN OEM_FILE_TYPE Filetype
)
/*++
Routine Description:
Determine whether a file has the proper format for an (installed) OEM INF
or catalog.
Arguments:
Filename - supplies filename (sans path) to be checked.
Filetype - specifies the type of file indicating how validation should be
performed for the caller-supplied Filename. May be one of the
following values:
OemFiletypeInf - file must be OEM INF filename format (i.e., OEM<n>.INF)
OemFiletypeCat - file must be OEM CAT filename format (i.e., OEM<n>.CAT)
Return Value:
If the file conforms to the format for an installed OEM file of the
specified type, the return is non-zero. Otherwise, it is FALSE.
--*/
{
PTSTR p;
BOOL b;
//
// Catalog filename must not contain path...
//
MYASSERT(pSetupGetFileTitle(Filename) == Filename);
//
// First check that the first 3 characters are OEM
//
if(_tcsnicmp(Filename, TEXT("oem"), 3)) {
return FALSE;
}
//
// Next verify that any characters after "oem" and before ".cat"
// are digits.
//
p = (PTSTR)Filename;
p = CharNext(p);
p = CharNext(p);
p = CharNext(p);
while((*p != TEXT('\0')) && (*p != TEXT('.'))) {
if((*p < TEXT('0')) || (*p > TEXT('9'))) {
return FALSE;
}
p = CharNext(p);
}
//
// Finally, verify that the last 4 characters are either ".inf" or ".cat",
// depending on what type of file the caller specified.
//
switch(Filetype) {
case OemFiletypeInf :
b = !_tcsicmp(p, TEXT(".INF"));
break;
case OemFiletypeCat :
b = !_tcsicmp(p, TEXT(".CAT"));
break;
default :
MYASSERT(FALSE);
return FALSE;
}
return b;
}
DWORD
pSetupInstallCatalog(
IN LPCTSTR CatalogFullPath,
IN LPCTSTR NewBaseName,
OUT LPTSTR NewCatalogFullPath OPTIONAL
)
/*++
Routine Description:
This routine installs a catalog file. The file is copied by the system
into a special directory, and is optionally renamed.
Arguments:
CatalogFullPath - supplies the fully-qualified win32 path of the catalog
to be installed on the system.
NewBaseName - specifies the new base name to use when the catalog file is
copied into the catalog store.
NewCatalogFullPath - optionally receives the fully-qualified path of the
catalog file within the catalog store. This buffer should be at least
MAX_PATH bytes (ANSI version) or chars (Unicode version).
** NOTE: If we're running in "minimal embedded" mode, then we don't **
** actually call any of the Crypto APIs, and instead always simply **
** report success. In this case, the caller had better not have **
** specified an OUT buffer for NewCatalogFullPath, because we won't **
** have a path to report. If we run into this case, we'll instead **
** report failure. What this really says is that nobody other than **
** setupapi should ever be passing a non-NULL value for this arg. **
Return Value:
If successful, the return value is NO_ERROR.
If failure, the return value is a Win32 error code indicating the cause of
the failure.
--*/
{
DWORD Err;
HCATADMIN hCatAdmin;
HCATINFO hCatInfo;
CATALOG_INFO CatalogInfo;
LPWSTR LocalCatalogFullPath;
LPWSTR LocalNewBaseName;
MYASSERT(NewBaseName);
if(!NewBaseName) {
return ERROR_INVALID_PARAMETER;
}
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
//
// If someone called us expecting the new catalog's full path to be
// returned, they're outta luck...
//
MYASSERT(!NewCatalogFullPath);
if(NewCatalogFullPath) {
//
// In minimal embedded mode, a non-NULL NewCatalogFullPath arg is
// an invalid parameter...
//
return ERROR_INVALID_PARAMETER;
} else {
//
// Simply report success.
//
return NO_ERROR;
}
}
if(GlobalSetupFlags & PSPGF_AUTOFAIL_VERIFIES) {
return TRUST_E_FAIL;
}
Err = NO_ERROR;
LocalCatalogFullPath = NULL;
LocalNewBaseName = NULL;
hCatInfo = NULL;
Err = GLE_FN_CALL(FALSE, CryptCATAdminAcquireContext(&hCatAdmin,
&DriverVerifyGuid,
0)
);
if(Err != NO_ERROR) {
return Err;
}
try {
//
// Duplicate our catalog pathname and basename since the
// CryptCATAdminAddCatalog prototype doesn't specify these arguments as
// being const strings.
//
LocalCatalogFullPath = DuplicateString(CatalogFullPath);
LocalNewBaseName = DuplicateString(NewBaseName);
if(!LocalCatalogFullPath || !LocalNewBaseName) {
Err = ERROR_NOT_ENOUGH_MEMORY;
leave;
}
Err = GLE_FN_CALL(NULL,
hCatInfo = CryptCATAdminAddCatalog(
hCatAdmin,
LocalCatalogFullPath,
LocalNewBaseName,
0)
);
if(Err != NO_ERROR) {
//
// If the error we received is ERROR_ALREADY_EXISTS, then that
// indicates that the exact same catalog was already present
// (and installed under the same name). Treat this as a
// success (assuming we can get the full pathname of the
// existing catalog).
//
if(Err == ERROR_ALREADY_EXISTS) {
if(NewCatalogFullPath) {
//
// Resolve the catalog base filename to a fully-
// qualified path.
//
CatalogInfo.cbStruct = sizeof(CATALOG_INFO);
Err = GLE_FN_CALL(FALSE,
CryptCATAdminResolveCatalogPath(
hCatAdmin,
LocalNewBaseName,
&CatalogInfo,
0)
);
} else {
//
// Caller isn't interested in finding out what pathname
// the catalog was installed under...
//
Err = NO_ERROR;
}
}
} else if(NewCatalogFullPath) {
//
// The caller wants to know the full path under which the catalog
// got installed.
//
CatalogInfo.cbStruct = sizeof(CATALOG_INFO);
Err = GLE_FN_CALL(FALSE,
CryptCATCatalogInfoFromContext(hCatInfo,
&CatalogInfo,
0)
);
}
//
// If we succeeded in retrieving the installed catalog's full path
// (and the caller requested it), fill in the caller's buffer now.
//
if((Err == NO_ERROR) && NewCatalogFullPath) {
MYVERIFY(SUCCEEDED(StringCchCopy(NewCatalogFullPath,
MAX_PATH,
CatalogInfo.wszCatalogFile)));
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
}
if(hCatInfo) {
CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0);
}
CryptCATAdminReleaseContext(hCatAdmin, 0);
if(LocalCatalogFullPath) {
MyFree(LocalCatalogFullPath);
}
if(LocalNewBaseName) {
MyFree(LocalNewBaseName);
}
return Err;
}
DWORD
pSetupVerifyCatalogFile(
IN LPCTSTR CatalogFullPath
)
/*++
Routine Description:
This routine verifies a single catalog file using standard OS codesigning
(i.e., driver signing) policy.
Arguments:
CatalogFullPath - supplies the fully-qualified Win32 path of
the catalog file to be verified.
Return Value:
If successful, the return value is ERROR_SUCCESS.
If failure, the return value is the error returned from WinVerifyTrust.
--*/
{
return _VerifyCatalogFile(NULL, CatalogFullPath, NULL, FALSE, NULL, NULL);
}
DWORD
_VerifyCatalogFile(
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
IN LPCTSTR CatalogFullPath,
IN PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo, OPTIONAL
IN BOOL UseAuthenticodePolicy,
IN OUT HCERTSTORE *hStoreTrustedPublisher, OPTIONAL
OUT HANDLE *hWVTStateData OPTIONAL
)
/*++
Routine Description:
This routine verifies a single catalog file using the specified policy.
Arguments:
LogContext - optionally, supplies the context to be used when logging
information about the routine's activities.
CatalogFullPath - supplies the fully-qualified Win32 path of the catalog
file to be verified.
AltPlatformInfo - optionally, supplies alternate platform information used
to fill in a DRIVER_VER_INFO structure (defined in sdk\inc\softpub.h)
that is passed to WinVerifyTrust.
** NOTE: This structure _must_ have its cbSize field set to **
** sizeof(SP_ALTPLATFORM_INFO_V2) -- validation on client-supplied **
** buffer is the responsibility of the caller. **
UseAuthenticodePolicy - if TRUE, verification is to be done using
Authenticode policy instead of standard driver signing policy.
hStoreTrustedPublisher - optionally, supplies the address of a certificate
store handle. If the handle pointed to is NULL, a handle will be
acquired (if possible) via CertOpenStore and returned to the caller.
If the handle pointed to is non-NULL, then that handle will be used by
this routine. If the pointer itself is NULL, then an HCERTSTORE will
be acquired for the duration of this call, and released before
returning.
NOTE: it is the caller's responsibility to free the certificate store
handle returned by this routine by calling CertCloseStore. This handle
may be opened in either success or failure cases, so the caller must
check for non-NULL returned handle in both cases.
hWVTStateData - if supplied, this parameter points to a buffer that
receives a handle to WinVerifyTrust state data. This handle will be
returned only when validation was successfully performed using
Authenticode policy. This handle may be used, for example, to retrieve
signer info when prompting the user about whether they trust the
publisher. (The status code returned will indicate whether or not this
is necessary, see "Return Value" section below.)
This parameter should only be supplied if UseAuthenticodePolicy is
TRUE. If the routine fails, then this handle will be set to NULL.
It is the caller's responsibility to close this handle when they're
finished with it by calling pSetupCloseWVTStateData().
Return Value:
If the catalog was successfully validated via driver signing policy, then
the return value is NO_ERROR.
If the catalog was successfully validated via Authenticode policy, and the
publisher was in the TrustedPublisher store, then the return value is
ERROR_AUTHENTICODE_TRUSTED_PUBLISHER.
If the catalog was successfully validated via Authenticode policy, and the
publisher was not in the TrustedPublisher store (hence we must prompt the
user to establish their trust of the publisher), then the return value is
ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED
If a failure occurred, the return value is a Win32 error code indicating
the cause of the failure.
Remarks:
If we're in initial system setup (i.e., GUI-mode setup or mini-setup), we
automatically install the certificates for any Authenticode-signed packages
we encounter. This is done to make OEM and corporate deployment as
painless as possible. In order to avoid being spoofed into thinking we're
in system setup when we're not, we check to see if we're in LocalSystem
security context (which both GUI setup and mini-setup are), and that we're
on an interactive windowstation (which umpnpmgr is not).
--*/
{
WINTRUST_DATA WintrustData;
WINTRUST_FILE_INFO WintrustFileInfo;
DRIVER_VER_INFO VersionInfo;
DWORD Err, CertAutoInstallErr;
PCRYPT_PROVIDER_DATA ProviderData;
PCRYPT_PROVIDER_SGNR ProviderSigner;
PCRYPT_PROVIDER_CERT ProviderCert;
TCHAR PublisherName[MAX_PATH];
//
// If the caller requested that we return WinVerifyTrust state data upon
// successful Authenticode validation, then they'd better have actually
// requested Authenticode validation!
//
MYASSERT(!hWVTStateData || UseAuthenticodePolicy);
if(hWVTStateData) {
*hWVTStateData = NULL;
}
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
//
// Not valid to call us requesting Authenticode validation!
//
MYASSERT(!UseAuthenticodePolicy);
return ERROR_SUCCESS;
}
if(GlobalSetupFlags & PSPGF_AUTOFAIL_VERIFIES) {
return TRUST_E_FAIL;
}
if (!FileExists(CatalogFullPath, NULL)) {
return ERROR_NO_CATALOG_FOR_OEM_INF;
}
ZeroMemory(&WintrustData, sizeof(WINTRUST_DATA));
WintrustData.cbStruct = sizeof(WINTRUST_DATA);
WintrustData.dwUIChoice = WTD_UI_NONE;
WintrustData.dwUnionChoice = WTD_CHOICE_FILE;
WintrustData.pFile = &WintrustFileInfo;
WintrustData.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT |
WTD_CACHE_ONLY_URL_RETRIEVAL;
ZeroMemory(&WintrustFileInfo, sizeof(WINTRUST_FILE_INFO));
WintrustFileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
WintrustFileInfo.pcwszFilePath = CatalogFullPath;
if(UseAuthenticodePolicy) {
//
// We want WinVerifyTrust to return a handle to its state data, so we
// can retrieve the publisher's cert...
//
WintrustData.dwStateAction = WTD_STATEACTION_VERIFY;
} else {
//
// Specify driver version info structure so that we can control the
// range of OS versions against which this catalog should validate.
//
ZeroMemory(&VersionInfo, sizeof(DRIVER_VER_INFO));
VersionInfo.cbStruct = sizeof(DRIVER_VER_INFO);
if(AltPlatformInfo) {
MYASSERT(AltPlatformInfo->cbSize == sizeof(SP_ALTPLATFORM_INFO_V2));
//
// Caller wants the file validated for an alternate platform, so we
// must fill in a DRIVER_VER_INFO structure to be passed to the policy
// module.
//
VersionInfo.dwPlatform = AltPlatformInfo->Platform;
VersionInfo.dwVersion = AltPlatformInfo->MajorVersion;
VersionInfo.sOSVersionLow.dwMajor = AltPlatformInfo->FirstValidatedMajorVersion;
VersionInfo.sOSVersionLow.dwMinor = AltPlatformInfo->FirstValidatedMinorVersion;
VersionInfo.sOSVersionHigh.dwMajor = AltPlatformInfo->MajorVersion;
VersionInfo.sOSVersionHigh.dwMinor = AltPlatformInfo->MinorVersion;
} else {
//
// If an AltPlatformInfo was not passed in then set the
// WTD_USE_DEFAULT_OSVER_CHECK flag. This flag tells WinVerifyTrust to
// use its default osversion checking, even though a DRIVER_VER_INFO
// structure was passed in.
//
WintrustData.dwProvFlags |= WTD_USE_DEFAULT_OSVER_CHECK;
}
//
// Specify a DRIVER_VER_INFO structure so we can get back signer
// information about the catalog.
//
WintrustData.pPolicyCallbackData = (PVOID)&VersionInfo;
}
//
// Our call to WinVerifyTrust may allocate a resource we need to free
// (namely, the signer cert context). Wrap the following in try/except
// so we won't leak resources in case of exception.
//
try {
Err = (DWORD)WinVerifyTrust(NULL,
(UseAuthenticodePolicy
? &AuthenticodeVerifyGuid
: &DriverVerifyGuid),
&WintrustData
);
if((Err != NO_ERROR) || !UseAuthenticodePolicy) {
//
// If we're using driver signing policy, and we failed because of
// an osattribute mismatch, then convert this error to a specific
// error (with more sensible text).
//
if(!UseAuthenticodePolicy && (Err == ERROR_APP_WRONG_OS)) {
Err = ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH;
}
leave;
}
//
// If we get to this point, we successfully validated the catalog via
// Authenticode policy, and we have a handle to the WinVerifyTrust
// state data we need in order to: (a) return the handle to the caller
// (if requested), and (b) get at the certificate we need to search for
// in the TrustedPublisher certificate store.
//
MYASSERT(WintrustData.hWVTStateData);
if(!WintrustData.hWVTStateData) {
Err = ERROR_UNIDENTIFIED_ERROR;
leave;
}
//
// Now we need to ascertain whether the publisher is already trusted,
// or whether we must prompt the user.
//
ProviderData = WTHelperProvDataFromStateData(WintrustData.hWVTStateData);
MYASSERT(ProviderData);
if(!ProviderData) {
Err = ERROR_UNIDENTIFIED_ERROR;
leave;
}
ProviderSigner = WTHelperGetProvSignerFromChain(ProviderData,
0,
FALSE,
0
);
MYASSERT(ProviderSigner);
if(!ProviderSigner) {
Err = ERROR_UNIDENTIFIED_ERROR;
leave;
}
ProviderCert = WTHelperGetProvCertFromChain(ProviderSigner, 0);
MYASSERT(ProviderCert);
if(!ProviderCert) {
Err = ERROR_UNIDENTIFIED_ERROR;
leave;
}
if(pSetupIsAuthenticodePublisherTrusted(ProviderCert->pCert,
hStoreTrustedPublisher)) {
Err = ERROR_AUTHENTICODE_TRUSTED_PUBLISHER;
} else {
//
// If we're running in a context where it's acceptable to auto-
// install the cert, then do that now. Otherwise, we report a
// status code that informs the caller they must prompt the user
// about whether this publisher is to be trusted.
//
if(IsAutoCertInstallAllowed()) {
//
// Retrieve the publisher's name, so we can include it when
// logging either success or failure of the certificate
// installation.
//
MYVERIFY(1 <= CertGetNameString(
ProviderCert->pCert,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
PublisherName,
SIZECHARS(PublisherName))
);
CertAutoInstallErr = pSetupInstallCertificate(ProviderCert->pCert);
if(CertAutoInstallErr == NO_ERROR) {
//
// Log the fact that we auto-installed a certificate.
//
WriteLogEntry(LogContext,
DRIVER_LOG_INFO,
MSG_LOG_AUTHENTICODE_CERT_AUTOINSTALLED,
NULL,
PublisherName
);
//
// Now publisher is trusted, so return status indicating
// that.
//
Err = ERROR_AUTHENTICODE_TRUSTED_PUBLISHER;
} else {
//
// Log the fact that we couldn't install the certificate.
// Don't treat this as a fatal error, however.
//
WriteLogEntry(LogContext,
DRIVER_LOG_WARNING | SETUP_LOG_BUFFER,
MSG_LOG_AUTHENTICODE_CERT_AUTOINSTALL_FAILED,
NULL,
PublisherName
);
WriteLogError(LogContext,
DRIVER_LOG_WARNING,
CertAutoInstallErr
);
//
// Report status indicating that the user must be prompted
// to establish trust of this publisher.
//
Err = ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED;
}
} else {
Err = ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED;
}
}
//
// If we get to here, then we've successfully verified the catalog, and
// ascertained whether the certificate should be implicitly trusted.
// If the caller requested that we return the WinVerifyTrust state data
// to them, then we can store that in their output buffer now.
//
if(hWVTStateData) {
*hWVTStateData = WintrustData.hWVTStateData;
//
// WinVerifyTrust state data handle successfully transferred to
// caller's output buffer. Clear it out of the WintrustData
// structure so we won't end up trying to free it twice in case of
// error.
//
WintrustData.hWVTStateData = NULL;
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
}
if(!UseAuthenticodePolicy && VersionInfo.pcSignerCertContext) {
CertFreeCertificateContext(VersionInfo.pcSignerCertContext);
}
if((Err != ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) &&
(Err != ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
//
// If our error is NO_ERROR, then we shouldn't have WinVerifyTrust
// state data, because that indicates we validated using standard
// driver signing policy, not Authenticode policy.
//
MYASSERT((Err != NO_ERROR) || !hWVTStateData || !*hWVTStateData);
if(hWVTStateData && *hWVTStateData) {
pSetupCloseWVTStateData(*hWVTStateData);
*hWVTStateData = NULL;
//
// We'd better not also have a WinVerifyTrust state data handle in
// the WintrustData structure...
//
MYASSERT(!WintrustData.hWVTStateData);
}
}
if(WintrustData.hWVTStateData) {
pSetupCloseWVTStateData(WintrustData.hWVTStateData);
}
return Err;
}
VOID
pSetupCloseWVTStateData(
IN HANDLE hWVTStateData
)
/*++
Routine Description:
This routine closes the WinVerifyTrust state data handle returned from
certain routines (e.g., _VerifyCatalogFile) for prompting the user to
establish trust of an Authenticode publisher.
Arguments:
hWVTStateData - supplies the WinVerifyTrust state data handle to be closed.
Return Value:
None.
--*/
{
WINTRUST_DATA WintrustData;
ZeroMemory(&WintrustData, sizeof(WINTRUST_DATA));
WintrustData.cbStruct = sizeof(WINTRUST_DATA);
WintrustData.dwStateAction = WTD_STATEACTION_CLOSE;
WintrustData.hWVTStateData = hWVTStateData;
MYVERIFY(NO_ERROR == WinVerifyTrust(NULL,
&AuthenticodeVerifyGuid,
&WintrustData));
}
DWORD
pSetupUninstallCatalog(
IN LPCTSTR CatalogFilename
)
/*++
Routine Description:
This routine uninstalls a catalog, so it can no longer be used to validate
digital signatures.
Arguments:
CatalogFilename - supplies the simple filename of the catalog to be
uninstalled.
Return Value:
If successful, the return value is NO_ERROR.
If failure, the return value is a Win32 error code indicating the cause of
the failure.
--*/
{
DWORD Err;
HCATADMIN hCatAdmin;
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
return NO_ERROR;
}
if(GlobalSetupFlags & PSPGF_AUTOFAIL_VERIFIES) {
return TRUST_E_FAIL;
}
Err = GLE_FN_CALL(FALSE,
CryptCATAdminAcquireContext(&hCatAdmin,
&DriverVerifyGuid,
0)
);
if(Err != NO_ERROR) {
return Err;
}
try {
Err = GLE_FN_CALL(FALSE,
CryptCATAdminRemoveCatalog(hCatAdmin,
CatalogFilename,
0)
);
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
}
CryptCATAdminReleaseContext(hCatAdmin, 0);
return Err;
}
BOOL
pAnyDeviceUsingInf(
IN LPCTSTR InfFullPath,
IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
)
/*++
Routine Description:
This routine checks if any device, live or phantom, is using this INF file,
and logs if they are.
Arguments:
InfFullPath - supplies the full path of the INF.
LogContext - optionally, supplies the log context to be used if a device
using this INF is encountered.
Return Value:
TRUE if this INF is being used by any device, or if an error occurred (if
an error was encountered, we don't want to end up deleting the INF
erroneously.
FALSE if no devices are using this INF (and no errors were encountered).
--*/
{
DWORD Err;
HDEVINFO DeviceInfoSet;
SP_DEVINFO_DATA DeviceInfoData;
DWORD MemberIndex = 0;
HKEY hkey = INVALID_HANDLE_VALUE;
TCHAR CurrentDeviceInfFile[MAX_PATH];
DWORD cbSize, dwType;
PTSTR pInfFile;
//
// If we are passed a NULL InfFullPath or an enpty string then just return
// FALSE since nobody is using this.
//
if(!InfFullPath || (InfFullPath[0] == TEXT('\0'))) {
return FALSE;
}
pInfFile = (PTSTR)pSetupGetFileTitle(InfFullPath);
DeviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES);
if(DeviceInfoSet == INVALID_HANDLE_VALUE) {
//
// We can't retrieve a list of devices, hence we cannot make a
// determination of whether this inf is in-use. Safe thing to do is
// assume it is in-use...
//
return TRUE;
}
Err = NO_ERROR; // assume we won't find any devices using this INF.
try {
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
while(SetupDiEnumDeviceInfo(DeviceInfoSet,
MemberIndex++,
&DeviceInfoData)) {
//
// Open the 'driver' key for this device.
//
hkey = SetupDiOpenDevRegKey(DeviceInfoSet,
&DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_READ);
if(hkey != INVALID_HANDLE_VALUE) {
cbSize = sizeof(CurrentDeviceInfFile);
dwType = REG_SZ;
if ((RegQueryValueEx(hkey,
pszInfPath,
NULL,
&dwType,
(LPBYTE)CurrentDeviceInfFile,
&cbSize) == ERROR_SUCCESS) &&
!lstrcmpi(CurrentDeviceInfFile, pInfFile)) {
//
// This key is using this INF file so the INF can't be
// deleted.
//
Err = ERROR_SHARING_VIOLATION; // any error will do
if(LogContext) {
TCHAR DeviceId[MAX_DEVICE_ID_LEN];
if(CM_Get_Device_ID(DeviceInfoData.DevInst,
DeviceId,
SIZECHARS(DeviceId),
0
) != CR_SUCCESS) {
DeviceId[0] = TEXT('\0');
}
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_INF_IN_USE,
NULL,
pInfFile,
DeviceId
);
}
}
RegCloseKey(hkey);
hkey = INVALID_HANDLE_VALUE;
}
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
}
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
if(hkey != INVALID_HANDLE_VALUE) {
RegCloseKey(hkey);
}
return (Err != NO_ERROR);
}
VOID
pSetupUninstallOEMInf(
IN LPCTSTR InfFullPath,
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
IN DWORD Flags,
OUT PDWORD InfDeleteErr OPTIONAL
)
/*++
Routine Description:
This routine uninstalls a 3rd-party INF, PNF, and CAT (if one exists). It
can also be used to uninstall an exception package INF, PNF, and CAT.
By default this routine will first verify that there aren't any other
device's, live and phantom, that are pointing their InfPath to this
INF. This behavior can be overridden by the SUOI_FORCEDELETE flag.
Arguments:
InfFullPath - supplies the full path of the INF to be uninstalled.
LogContext - optionally, supplies the log context to be used if we
encounter an error when attempting to delete the catalog.
Flags - the following flags are supported:
SUOI_FORCEDELETE - delete the INF even if other driver keys are
have their InfPath pointing to it.
InfDeleteErr - optionally, supplies the address of a variable that receives
the error (if any) encountered when attempting to delete the INF.
Note that we delete the INF last (to avoid race conditions), so the
corresponding CAT and PNF may have already been deleted at this point.
Return Value:
None.
--*/
{
DWORD Err;
TCHAR FileNameBuffer[MAX_PATH+4]; // +4 in case filename is blahblah. not blahblah.inf
BOOL FreeLogContext = FALSE;
LPTSTR ExtPtr= NULL;
HINF hInf = INVALID_HANDLE_VALUE;
if(!LogContext) {
if(NO_ERROR == CreateLogContext(NULL, TRUE, &LogContext)) {
//
// Remember that we created this log context locally, so we can
// free it when we're done with this routine.
//
FreeLogContext = TRUE;
} else {
//
// Ensure LogContext is still NULL so we won't try to use it.
//
LogContext = NULL;
}
}
try {
//
// Make sure the specified INF is in %windir%\Inf, and that it's an OEM
// INF (i.e, filename is OEM<n>.INF).
//
if(pSetupInfIsFromOemLocation(InfFullPath, TRUE)) {
Err = ERROR_NOT_AN_INSTALLED_OEM_INF;
goto LogAnyUninstallErrors;
} else if(!IsInstalledFileFromOem(pSetupGetFileTitle(InfFullPath), OemFiletypeInf)) {
BOOL IsExceptionInf = FALSE;
GUID ClassGuid;
//
// The INF is in %windir%\Inf, but is not of the form oem<n>.inf.
// It may still be OK to uninstall it, if it's an exception INF...
//
hInf = SetupOpenInfFile(InfFullPath, NULL, INF_STYLE_WIN4, NULL);
if(hInf != INVALID_HANDLE_VALUE) {
//
// We don't need to lock the INF because it'll never be
// accessible outside of this routine.
//
if(ClassGuidFromInfVersionNode(&(((PLOADED_INF)hInf)->VersionBlock), &ClassGuid)
&& IsEqualGUID(&ClassGuid, &GUID_DEVCLASS_WINDOWS_COMPONENT_PUBLISHER)) {
IsExceptionInf = TRUE;
}
//
// Close the INF handle now so we won't still have it open if
// we end up deleting the INF.
//
SetupCloseInfFile(hInf);
hInf = INVALID_HANDLE_VALUE; // no need to close if we hit an exception
}
if(!IsExceptionInf) {
Err = ERROR_NOT_AN_INSTALLED_OEM_INF;
goto LogAnyUninstallErrors;
}
}
//
// Unless the caller passed in the SUOI_FORCEDELETE flag first check
// that no devices are using this INF file.
//
if(!(Flags & SUOI_FORCEDELETE) &&
pAnyDeviceUsingInf(InfFullPath, LogContext)) {
//
// Some device is still using this INF so we can't delete it.
//
Err = ERROR_INF_IN_USE_BY_DEVICES;
goto LogAnyUninstallErrors;
}
//
// Copy the caller-supplied INF name into a local buffer, so we can
// modify it when deleting the various files (INF, PNF, and CAT).
//
if(FAILED(StringCchCopy(FileNameBuffer, SIZECHARS(FileNameBuffer), InfFullPath))) {
//
// This error would be returned by DeleteFile if we let it go
// through.
//
Err = ERROR_PATH_NOT_FOUND;
goto LogAnyUninstallErrors;
}
//
// Uninstall the catalog (if any) first, because as soon as we delete
// the INF, that slot is "open" for use by another INF, and we wouldn't
// want to inadvertently delete someone else's catalog due to a race
// condition.
//
ExtPtr = _tcsrchr(FileNameBuffer, TEXT('.'));
MYASSERT(ExtPtr); // we already validated the filename's format
if(FAILED(StringCchCopy(ExtPtr,
SIZECHARS(FileNameBuffer)-(ExtPtr-FileNameBuffer),
pszCatSuffix))) {
Err = ERROR_BUFFER_OVERFLOW;
} else {
Err = pSetupUninstallCatalog(pSetupGetFileTitle(FileNameBuffer));
}
if((Err != NO_ERROR) && LogContext) {
//
// It's kinda important that we were unable to delete the catalog,
// but not important enough to fail the routine. Log this fact to
// setupapi.log...
//
WriteLogEntry(LogContext,
DEL_ERR_LOG_LEVEL(Err) | SETUP_LOG_BUFFER,
MSG_LOG_OEM_CAT_UNINSTALL_FAILED,
NULL,
pSetupGetFileTitle(FileNameBuffer)
);
WriteLogError(LogContext,
DEL_ERR_LOG_LEVEL(Err),
Err
);
}
//
// Now delete the PNF (we don't care so much if this succeeds or
// fails)...
//
if(SUCCEEDED(StringCchCopy(ExtPtr,
SIZECHARS(FileNameBuffer)-(ExtPtr-FileNameBuffer),
pszPnfSuffix))) {
SetFileAttributes(FileNameBuffer, FILE_ATTRIBUTE_NORMAL);
DeleteFile(FileNameBuffer);
}
//
// and finally the INF itself...
//
SetFileAttributes(InfFullPath, FILE_ATTRIBUTE_NORMAL);
Err = GLE_FN_CALL(FALSE, DeleteFile(InfFullPath));
LogAnyUninstallErrors:
if((Err != NO_ERROR) && LogContext) {
WriteLogEntry(LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_OEM_INF_UNINSTALL_FAILED,
NULL,
InfFullPath
);
WriteLogError(LogContext,
SETUP_LOG_ERROR,
Err
);
}
if(InfDeleteErr) {
*InfDeleteErr = Err;
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(),
ERROR_INVALID_PARAMETER,
InfDeleteErr
);
if(hInf != INVALID_HANDLE_VALUE) {
SetupCloseInfFile(hInf);
}
}
if(FreeLogContext) {
DeleteLogContext(LogContext);
}
}
DWORD
_VerifyFile(
IN PSETUP_LOG_CONTEXT LogContext,
IN OUT PVERIFY_CONTEXT VerifyContext, OPTIONAL
IN LPCTSTR Catalog, OPTIONAL
IN PVOID CatalogBaseAddress, OPTIONAL
IN DWORD CatalogImageSize,
IN LPCTSTR Key,
IN LPCTSTR FileFullPath,
OUT SetupapiVerifyProblem *Problem, OPTIONAL
OUT LPTSTR ProblemFile, OPTIONAL
IN BOOL CatalogAlreadyVerified,
IN PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo, OPTIONAL
IN DWORD Flags, OPTIONAL
OUT LPTSTR CatalogFileUsed, OPTIONAL
OUT PDWORD NumCatalogsConsidered, OPTIONAL
OUT LPTSTR DigitalSigner, OPTIONAL
OUT LPTSTR SignerVersion, OPTIONAL
OUT HANDLE *hWVTStateData OPTIONAL
)
/*++
Routine Description:
This routine verifies a single file against a particular catalog file, or
against any installed catalog file.
Arguments:
LogContext - supplies the context to be used when logging information about
the routine's activities.
VerifyContext - optionally, supplies the address of a structure that caches
various verification context handles. These handles may be NULL (if
not previously acquired, and they may be filled in upon return (in
either success or failure) if they were acquired during the processing
of this verification request. It is the caller's responsibility to
free these various context handles when they are no longer needed by
calling pSetupFreeVerifyContextMembers.
Catalog - optionally, supplies the path of the catalog file to be used for
the verification. If this argument is not specified, then this routine
will attempt to find a catalog that can verify it from among all
catalogs installed in the system.
If this path is a simple filename (no path components), then we'll look
up that catalog file in the CatAdmin's list of installed catalogs, else
we'll use the name as-is.
CatalogBaseAddress - optionally, supplies the address of a buffer
containing the entire catalog image with which our enumerated catalog
must match before being considered a correct validation. This is used
when copying OEM INFs, for example, when there may be multiple
installed catalogs that can validate an INF, but we want to make sure
that we pick _the_ catalog that matches the one we're contemplating
installing before we'll consider our INF/CAT files to be duplicates of
the previously-existing files.
This parameter (and its partner, CatalogImageSize) are only used when
Catalog doesn't specify a file path.
CatalogImageSize - if CatalogBaseAddress is specified, this parameter give
the size, in bytes, of that buffer.
Key - supplies a value that "indexes" the catalog, telling the verify APIs
which signature datum within the catalog it should use. Typically
the key is the (original) filename of a file.
FileFullPath - supplies the full path of the file to be verified.
Problem - if supplied, this parameter points to a variable that will be
filled in upon unsuccessful return with the cause of failure. If this
parameter is not supplied, then the ProblemFile parameter is ignored.
ProblemFile - if supplied, this parameter points to a character buffer of
at least MAX_PATH characters that receives the name of the file for
which a verification error occurred (the contents of this buffer are
undefined if verification succeeds.
If the Problem parameter is supplied, then the ProblemFile parameter
must also be specified.
CatalogAlreadyVerified - if TRUE, then verification won't be done on the
catalog--we'll just use that catalog to validate the file of interest.
If this is TRUE, then Catalog must be specified, must contain the path
to the catalog file (i.e., it can't be a simple filename).
** This flag is ignored when validating via Authenticode policy--the **
** catalog is always verified. **
AltPlatformInfo - optionally, supplies alternate platform information used
to fill in a DRIVER_VER_INFO structure (defined in sdk\inc\softpub.h)
that is passed to WinVerifyTrust.
** NOTE: This structure _must_ have its cbSize field set to **
** sizeof(SP_ALTPLATFORM_INFO_V2) -- validation on client-supplied **
** buffer is the responsibility of the caller. **
Flags - supplies flags that alter that behavior of this routine. May be a
combination of the following values:
VERIFY_FILE_IGNORE_SELFSIGNED - if this bit is set, then this routine
will fail validation for self-signed
binaries.
VERIFY_FILE_USE_OEM_CATALOGS - if this bit is set, then all catalogs
installed in the system will be scanned
to verify the given file. Otherwise,
OEM (3rd party) catalogs will NOT be
scanned to verify the given file. This
is only applicable if a catalog is not
specified.
VERIFY_FILE_USE_AUTHENTICODE_CATALOG - Validate the file using a
catalog signed with Authenticode
policy. If this flag is set,
we'll _only_ check for
Authenticode signatures, so if
the caller wants to first try
validating a file for OS code-
signing usage, then falling back
to Authenticode, they'll have to
call this routine twice.
If this flag is set, then the
caller may also supply the
hWVTStateData output parameter,
which can be used to prompt user
in order to establish that the
publisher should be trusted.
_VerifyFile will return one of
two error codes upon successful
validation via Authenticode
policy. Refer to the "Return
Value" section for details.
VERIFY_FILE_DRIVERBLOCKED_ONLY - Only check if the file is in the bad
driver database, don't do any digital
sigature validation.
VERIFY_FILE_NO_DRIVERBLOCKED_CHECK - Don't check if the file is blocked
via the Bad Driver Database.
CatalogFileUsed - if supplied, this parameter points to a character buffer
at least MAX_PATH characters big that receives the name of the catalog
file used to verify the specified file. This is only filled in upon
successful return, or when the Problem is SetupapiVerifyFileProblem
(i.e., the catalog verified, but the file did not). If this buffer is
set to the empty string upon a SetupapiVerifyFileProblem failure, then
we didn't find any applicable catalogs to use for validation.
Also, this buffer will contain an empty string upon successful return
if the file was validated without using a catalog (i.e., the file
contains its own signature).
NumCatalogsConsidered - if supplied, this parameter receives a count of the
number of catalogs against which verification was attempted. Of course,
if Catalog is specified, this number will always be either zero or one.
DigitalSigner - if supplied, this parameter points to a character buffer of
at least MAX_PATH characters that receives the name of who digitally
signed the specified file. This value is only set if the Key is
correctly signed (i.e. the function returns NO_ERROR).
** This parameter should not be supplied when validating using **
** Authenticode policy. Information about signer, date, etc., may **
** be acquired from the hWVTStateData in that case. **
SignerVersion - if supplied, this parameter points to a character buffer of
at least MAX_PATH characters that receives the signer version as
returned in the szwVerion field of the DRIVER_VER_INFO structure in our
call to WinVerifyTrust.
** This parameter should not be supplied when validating using **
** Authenticode policy. Information about signer, date, etc., may **
** be acquired from the hWVTStateData in that case. **
hWVTStateData - if supplied, this parameter points to a buffer that
receives a handle to WinVerifyTrust state data. This handle will be
returned only when validation was successfully performed using
Authenticode policy. This handle may be used, for example, to retrieve
signer info when prompting the user about whether they trust the
publisher. (The status code returned will indicate whether or not this
is necessary, see "Return Value" section below.)
This parameter should only be supplied if the
VERIFY_FILE_USE_AUTHENTICODE_CATALOG bit is set in the supplied Flags.
If the routine fails, then this handle will be set to NULL.
It is the caller's responsibility to close this handle when they're
finished with it by calling pSetupCloseWVTStateData().
Return Value:
If the file was successfully validated via driver signing policy (or we
didn't perform digital signature verification and everything else
succeeded), then the return value is NO_ERROR.
If the file was successfully validated via Authenticode policy, and the
publisher was in the TrustedPublisher store, then the return value is
ERROR_AUTHENTICODE_TRUSTED_PUBLISHER.
If the file was successfully validated via Authenticode policy, and the
publisher was not in the TrustedPublisher store (hence we must prompt the
user to establish their trust of the publisher), then the return value is
ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED
If a failure occurred, the return value is a Win32 error code indicating
the cause of the failure.
--*/
{
LPBYTE Hash;
DWORD HashSize;
CATALOG_INFO CatInfo;
HANDLE hFile;
HCATINFO hCatInfo;
HCATINFO PrevCat;
DWORD Err, AuthenticodeErr;
WINTRUST_DATA WintrustData;
WINTRUST_CATALOG_INFO WintrustCatalogInfo;
WINTRUST_FILE_INFO WintrustFileInfo;
DRIVER_VER_INFO VersionInfo;
LPTSTR CatalogFullPath;
WCHAR UnicodeKey[MAX_PATH];
WIN32_FILE_ATTRIBUTE_DATA FileAttribData;
BOOL FoundMatchingImage;
DWORD CurCatFileSize;
HANDLE CurCatFileHandle, CurCatMappingHandle;
PVOID CurCatBaseAddress;
BOOL LoggedWarning;
BOOL TrySelfSign;
DWORD AltPlatSlot;
TAGREF tagref;
HCATADMIN LocalhCatAdmin;
HSDB LocalhSDBDrvMain;
BOOL UseAuthenticodePolicy = Flags & VERIFY_FILE_USE_AUTHENTICODE_CATALOG;
//
// Initialize the CatalogFileUsed parameter to an empty string (i.e., no
// applicable catalog at this point).
//
if(CatalogFileUsed) {
*CatalogFileUsed = TEXT('\0');
}
//
// Initialize the output counter indicating the number of catalog files we
// processed during the attempted verification.
//
if(NumCatalogsConsidered) {
*NumCatalogsConsidered = 0;
}
//
// Initialize the output handle for WinVerifyTrust state data.
//
if(hWVTStateData) {
*hWVTStateData = NULL;
}
//
// If Authenticode validation is requested, then the caller shouldn't have
// passed in the DigitalSigner and SignerVersion output parameters.
//
MYASSERT(!UseAuthenticodePolicy || (!DigitalSigner && !SignerVersion));
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
//
// The old behavior of this API in the ANSI case where the crypto
// APIs weren't available was to set the CatalogFileUsed OUT param to
// an empty string and report NO_ERROR. We'll do the same thing here
// (but we'll also assert, because no external callers should care, and
// if they do, an empty string probably isn't going to make them very
// happy).
//
MYASSERT(!CatalogFileUsed);
MYASSERT(!NumCatalogsConsidered);
//
// We'd better not be called in minimal embedded scenarios where we're
// asked to provide signer info...
//
MYASSERT(!DigitalSigner);
MYASSERT(!SignerVersion);
//
// Likewise, we'd better not be called asking to validate using
// Authenticode policy (and hence, to return WinVerifyTrust state data
// regarding the signing certificate).
//
MYASSERT(!UseAuthenticodePolicy);
return NO_ERROR;
}
//
// If the caller requested that we return WinVerifyTrust state data upon
// successful Authenticode validation, then they'd better have actually
// requested Authenticode validation!
//
MYASSERT(!hWVTStateData || UseAuthenticodePolicy);
//
// Doesn't make sense to have both these flags set!
//
MYASSERT((Flags & (VERIFY_FILE_DRIVERBLOCKED_ONLY | VERIFY_FILE_NO_DRIVERBLOCKED_CHECK))
!= (VERIFY_FILE_DRIVERBLOCKED_ONLY | VERIFY_FILE_NO_DRIVERBLOCKED_CHECK)
);
//
// If Problem is supplied, then ProblemFile must also be supplied.
//
MYASSERT(!Problem || ProblemFile);
//
// If the caller claims to have already verified the catalog file, make
// sure they passed us the full path to one.
//
MYASSERT(!CatalogAlreadyVerified || (Catalog && (Catalog != pSetupGetFileTitle(Catalog))));
//
// If a catalog image is specified, we'd better have been given a size.
//
MYASSERT((CatalogBaseAddress && CatalogImageSize) ||
!(CatalogBaseAddress || CatalogImageSize));
//
// If a catalog image was supplied for comparison, there shouldn't be a
// file path specified in the Catalog parameter.
//
MYASSERT(!CatalogBaseAddress || !(Catalog && (Catalog != pSetupGetFileTitle(Catalog))));
//
// OK, preliminary checking is over--prepare to enter try/except block...
//
hFile = INVALID_HANDLE_VALUE;
Hash = NULL;
LoggedWarning = FALSE;
TrySelfSign = FALSE;
AltPlatSlot = 0;
tagref = TAGREF_NULL;
LocalhCatAdmin = NULL;
LocalhSDBDrvMain = NULL;
hCatInfo = NULL;
AuthenticodeErr = NO_ERROR;
//
// Go ahead and initialize the VersionInfo structure as well.
// WinVerifyTrust will sometimes fill in the pcSignerCertContext field with
// a cert context that must be freed, and we don't want to introduce the
// possibility of leaking this if we hit an exception at an inopportune
// moment. (We don't need to do this for Authenticode verification,
// because Authenticode pays no attention to the driver version info.)
//
if(!UseAuthenticodePolicy) {
ZeroMemory(&VersionInfo, sizeof(DRIVER_VER_INFO));
VersionInfo.cbStruct = sizeof(DRIVER_VER_INFO);
}
try {
if(GlobalSetupFlags & PSPGF_AUTOFAIL_VERIFIES) {
if(Problem) {
*Problem = SetupapiVerifyAutoFailProblem;
StringCchCopy(ProblemFile, MAX_PATH, FileFullPath);
}
Err = TRUST_E_FAIL;
leave;
}
if(AltPlatformInfo) {
AltPlatSlot = AllocLogInfoSlotOrLevel(LogContext,SETUP_LOG_VERBOSE,FALSE);
WriteLogEntry(LogContext,
AltPlatSlot,
MSG_LOG_VERIFYFILE_ALTPLATFORM,
NULL, // text message
AltPlatformInfo->Platform,
AltPlatformInfo->MajorVersion,
AltPlatformInfo->MinorVersion,
AltPlatformInfo->FirstValidatedMajorVersion,
AltPlatformInfo->FirstValidatedMinorVersion
);
}
if(VerifyContext && VerifyContext->hCatAdmin) {
LocalhCatAdmin = VerifyContext->hCatAdmin;
} else {
Err = GLE_FN_CALL(FALSE,
CryptCATAdminAcquireContext(&LocalhCatAdmin,
&DriverVerifyGuid,
0)
);
if(Err != NO_ERROR) {
//
// Failure encountered--ensure local handle is still NULL.
//
LocalhCatAdmin = NULL;
if(Problem) {
//
// We failed too early to blame the file as the problem,
// but it's the only filename we currently have to return
// as the problematic file.
//
*Problem = SetupapiVerifyFileProblem;
StringCchCopy(ProblemFile, MAX_PATH, FileFullPath);
}
leave;
}
//
// If requested, store the handle to be returned to the caller.
//
if(VerifyContext) {
VerifyContext->hCatAdmin = LocalhCatAdmin;
}
}
//
// Calculate the hash value for the inf.
//
Err = GLE_FN_CALL(INVALID_HANDLE_VALUE,
hFile = CreateFile(FileFullPath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL)
);
if(Err != NO_ERROR) {
if(Problem) {
*Problem = SetupapiVerifyFileProblem;
StringCchCopy(ProblemFile, MAX_PATH, FileFullPath);
}
leave;
}
//
// Only check if the driver is in the defective driver database if we
// are NOT in GUI setup, and the caller has NOT passed in the
// VERIFY_FILE_NO_DRIVERBLOCKED_CHECK flag.
//
if(!GuiSetupInProgress &&
!(Flags & VERIFY_FILE_NO_DRIVERBLOCKED_CHECK)) {
//
// Shim database APIs have been known to crash from time to time,
// so guard ourselves against that because failure to do a lookup
// in the bad driver database should not result in a verification
// failure.
//
try {
if(VerifyContext && VerifyContext->hSDBDrvMain) {
LocalhSDBDrvMain = VerifyContext->hSDBDrvMain;
} else {
LocalhSDBDrvMain = SdbInitDatabaseEx(SDB_DATABASE_MAIN_DRIVERS,
NULL,
DEFAULT_IMAGE
);
if(LocalhSDBDrvMain) {
//
// If requested, store the handle to be returned to the
// caller.
//
if(VerifyContext) {
VerifyContext->hSDBDrvMain = LocalhSDBDrvMain;
}
} else {
//
// Log a warning that we couldn't access the bad driver
// database to check if this is a blocked driver.
// (SdbInitDatabase doesn't set last error, so we don't
// know why this failed--only that it did.)
//
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_CANT_ACCESS_BDD,
NULL,
FileFullPath
);
}
}
//
// Check the bad driver database to see if this file is blocked.
//
if(LocalhSDBDrvMain) {
tagref = SdbGetDatabaseMatch(LocalhSDBDrvMain,
Key,
hFile,
NULL,
0);
if(tagref != TAGREF_NULL) {
//
// Read the driver policy to see if this should be
// blocked by usermode or not.
// If the 1st bit is set then this should NOT be blocked
// by usermode.
//
ULONG type, size, policy;
size = sizeof(policy);
policy = 0;
type = REG_DWORD;
if(SdbQueryDriverInformation(LocalhSDBDrvMain,
tagref,
TEXT("Policy"),
&type,
&policy,
&size) != ERROR_SUCCESS) {
//
// If we can't read the policy then default to 0.
// This means we will block in usermode!
//
policy = 0;
}
if(!(policy & DDB_DRIVER_POLICY_SETUP_NO_BLOCK_BIT)) {
//
// This driver is in the database and needs to be
// blocked!
//
WriteLogEntry(LogContext,
SETUP_LOG_VERBOSE,
MSG_LOG_DRIVER_BLOCKED_ERROR,
NULL,
FileFullPath
);
LoggedWarning = TRUE;
if(Problem) {
*Problem = SetupapiVerifyDriverBlocked;
StringCchCopy(ProblemFile, MAX_PATH, FileFullPath);
}
Err = ERROR_DRIVER_BLOCKED;
leave;
}
}
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(),
ERROR_INVALID_PARAMETER,
&Err
);
WriteLogEntry(LogContext,
SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
MSG_LOG_CANT_ACCESS_BDD_EXCEPTION,
NULL,
FileFullPath
);
WriteLogError(LogContext, SETUP_LOG_WARNING, Err);
}
}
//
// If the caller only wanted to check if the file was in the bad driver
// database then we are done.
//
if(Flags & VERIFY_FILE_DRIVERBLOCKED_ONLY) {
Err = NO_ERROR;
leave;
}
//
// Initialize some of the structures that will be used later on
// in calls to WinVerifyTrust. We don't know if we're verifying
// against a catalog or against a file yet.
//
ZeroMemory(&WintrustData, sizeof(WINTRUST_DATA));
WintrustData.cbStruct = sizeof(WINTRUST_DATA);
WintrustData.dwUIChoice = WTD_UI_NONE;
WintrustData.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT |
WTD_CACHE_ONLY_URL_RETRIEVAL;
if(!UseAuthenticodePolicy) {
//
// We only supply a driver version info structure if we're doing
// validation via driver signing policy. Authenticode completely
// ignores this...
//
if(AltPlatformInfo) {
MYASSERT(AltPlatformInfo->cbSize == sizeof(SP_ALTPLATFORM_INFO_V2));
//
// Caller wants the file validated for an alternate
// platform, so we must fill in a DRIVER_VER_INFO structure
// to be passed to the policy module.
//
VersionInfo.dwPlatform = AltPlatformInfo->Platform;
VersionInfo.dwVersion = AltPlatformInfo->MajorVersion;
VersionInfo.sOSVersionLow.dwMajor = AltPlatformInfo->FirstValidatedMajorVersion;
VersionInfo.sOSVersionLow.dwMinor = AltPlatformInfo->FirstValidatedMinorVersion;
VersionInfo.sOSVersionHigh.dwMajor = AltPlatformInfo->MajorVersion;
VersionInfo.sOSVersionHigh.dwMinor = AltPlatformInfo->MinorVersion;
} else {
//
// If an AltPlatformInfo was not passed in then set the
// WTD_USE_DEFAULT_OSVER_CHECK flag. This flag tells
// WinVerifyTrust to use its default osversion checking, even
// though a DRIVER_VER_INFO structure was passed in.
//
WintrustData.dwProvFlags |= WTD_USE_DEFAULT_OSVER_CHECK;
}
//
// Specify a DRIVER_VER_INFO structure so we can get back
// who signed the file and the signer version information.
// If we don't have an AltPlatformInfo then set the
// WTD_USE_DEFAULT_OSVER_CHECK flag so that WinVerifyTrust will do
// its default checking, just as if a DRIVER_VER_INFO structure
// was not passed in.
//
WintrustData.pPolicyCallbackData = (PVOID)&VersionInfo;
//
// "auto-cache" feature only works for driver signing policy...
//
WintrustData.dwStateAction = WTD_STATEACTION_AUTO_CACHE;
}
//
// Compute the cryptographic hash for the file. If we fail to compute
// this hash, we want to bail immediately. We don't want to attempt to
// fallback to self-signed binaries, because in the future the hash may
// be used to revoke a signature, and we wouldn't want to automatically
// trust a self-signed binary in that case.
//
HashSize = 100; // start out with a reasonable size for most requests.
do {
Hash = MyMalloc(HashSize);
if(!Hash) {
Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
Err = GLE_FN_CALL(FALSE,
CryptCATAdminCalcHashFromFileHandle(hFile,
&HashSize,
Hash,
0)
);
}
if(Err != NO_ERROR) {
if(Hash) {
MyFree(Hash);
Hash = NULL;
}
if(Err != ERROR_INSUFFICIENT_BUFFER) {
//
// We failed for some reason other than buffer-too-small.
//
WriteLogEntry(LogContext,
SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
MSG_LOG_HASH_ERROR,
NULL,
FileFullPath,
Catalog ? Catalog : TEXT(""),
Key
);
WriteLogError(LogContext, SETUP_LOG_WARNING, Err);
LoggedWarning = TRUE;
if(Problem) {
*Problem = SetupapiVerifyFileProblem;
StringCchCopy(ProblemFile, MAX_PATH, FileFullPath);
}
leave;
}
}
} while(Err != NO_ERROR);
//
// Now we have the file's hash. Initialize the structures that will be
// used later on in calls to WinVerifyTrust.
//
WintrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
WintrustData.pCatalog = &WintrustCatalogInfo;
ZeroMemory(&WintrustCatalogInfo, sizeof(WINTRUST_CATALOG_INFO));
WintrustCatalogInfo.cbStruct = sizeof(WINTRUST_CATALOG_INFO);
WintrustCatalogInfo.pbCalculatedFileHash = Hash;
WintrustCatalogInfo.cbCalculatedFileHash = HashSize;
//
// WinVerifyTrust is case-sensitive, so ensure that the key being used
// is all lower-case. (We copy the key to a writable Unicode character
// buffer so we can lower-case it.)
//
StringCchCopy(UnicodeKey, SIZECHARS(UnicodeKey), Key);
CharLower(UnicodeKey);
WintrustCatalogInfo.pcwszMemberTag = UnicodeKey;
if(Catalog && (Catalog != pSetupGetFileTitle(Catalog))) {
//
// We know in this case we're always going to examine exactly one
// catalog.
//
if(NumCatalogsConsidered) {
*NumCatalogsConsidered = 1;
}
//
// Fill in the catalog information since we know which catalog
// we're going to be using...
//
WintrustCatalogInfo.pcwszCatalogFilePath = Catalog;
//
// The caller supplied the path to the catalog file to be used for
// verification--we're ready to go! First, verify the catalog
// (unless the caller already did it), and if that succeeds, then
// verify the file.
//
if(!CatalogAlreadyVerified || UseAuthenticodePolicy) {
//
// Before validating the catalog, we'll flush the crypto cache.
// Otherwise, it can get fooled when validating against a
// catalog at a specific location, because that catalog can
// change "behind its back".
//
if(WintrustData.dwStateAction == WTD_STATEACTION_AUTO_CACHE) {
WintrustData.dwStateAction = WTD_STATEACTION_AUTO_CACHE_FLUSH;
Err = (DWORD)WinVerifyTrust(NULL,
&DriverVerifyGuid,
&WintrustData
);
if(Err != NO_ERROR) {
//
// This shouldn't fail, but log a warning if it does...
//
WriteLogEntry(LogContext,
SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
MSG_LOG_CRYPTO_CACHE_FLUSH_FAILURE,
NULL,
Catalog
);
WriteLogError(LogContext,
SETUP_LOG_WARNING,
Err
);
//
// treat this error as non-fatal
//
}
//
// When flushing the cache, crypto isn't supposed to be
// allocating a pcSignerCertContext...
//
MYASSERT(!VersionInfo.pcSignerCertContext);
if(VersionInfo.pcSignerCertContext) {
CertFreeCertificateContext(VersionInfo.pcSignerCertContext);
VersionInfo.pcSignerCertContext = NULL;
}
//
// Now back to our regularly-scheduled programming...
//
WintrustData.dwStateAction = WTD_STATEACTION_AUTO_CACHE;
}
//
// NTRAID#NTBUG9-705286-2002/09/17-LonnyM superfluous validation of catalog
// Apparently, WinVerifyTrust validates both the catalog, and
// the file vouched thereby. We should clean this up, although
// the performance delta is probably insignificant.
//
Err = _VerifyCatalogFile(LogContext,
Catalog,
AltPlatformInfo,
UseAuthenticodePolicy,
(VerifyContext
? &(VerifyContext->hStoreTrustedPublisher)
: NULL),
hWVTStateData
);
//
// If we got one of the two "successful" errors when validating
// via Authenticode policy, then reset our error, and save the
// Authenticode error for use later, if we don't subsequently
// encounter some other failure.
//
if((Err == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(Err == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
MYASSERT(UseAuthenticodePolicy);
MYASSERT(!hWVTStateData || *hWVTStateData);
AuthenticodeErr = Err;
Err = NO_ERROR;
}
}
if(Err != NO_ERROR) {
MYASSERT(!hWVTStateData || !*hWVTStateData);
WriteLogEntry(LogContext,
GetCatLogLevel(Err, UseAuthenticodePolicy) | SETUP_LOG_BUFFER,
(UseAuthenticodePolicy
? MSG_LOG_VERIFYCAT_VIA_AUTHENTICODE_ERROR
: MSG_LOG_VERIFYCAT_ERROR),
NULL,
Catalog
);
WriteLogError(LogContext,
GetCatLogLevel(Err, UseAuthenticodePolicy),
Err
);
LoggedWarning = TRUE;
if(Problem) {
*Problem = SetupapiVerifyCatalogProblem;
StringCchCopy(ProblemFile, MAX_PATH, Catalog);
}
leave;
}
//
// Catalog was verified, now verify the file using that catalog.
//
if(CatalogFileUsed) {
StringCchCopy(CatalogFileUsed, MAX_PATH, Catalog);
}
Err = (DWORD)WinVerifyTrust(NULL,
(UseAuthenticodePolicy
? &AuthenticodeVerifyGuid
: &DriverVerifyGuid),
&WintrustData
);
//
// Fill in the DigitalSigner and SignerVersion if they were passed
// in.
//
if(Err == NO_ERROR) {
if(DigitalSigner) {
StringCchCopy(DigitalSigner, MAX_PATH, VersionInfo.wszSignedBy);
}
if(SignerVersion) {
StringCchCopy(SignerVersion, MAX_PATH, VersionInfo.wszVersion);
}
} else if(!UseAuthenticodePolicy && (Err == ERROR_APP_WRONG_OS)) {
//
// We failed validation via driver signing policy because of an
// osattribute mismatch. Translate this error into something
// with more understandable text.
//
Err = ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH;
}
if(Err != NO_ERROR) {
WriteLogEntry(LogContext,
GetCatLogLevel(Err, UseAuthenticodePolicy) | SETUP_LOG_BUFFER,
(UseAuthenticodePolicy
? MSG_LOG_VERIFYFILE_AUTHENTICODE_AGAINST_FULLCATPATH_ERROR
: MSG_LOG_VERIFYFILE_AGAINST_FULLCATPATH_ERROR),
NULL,
FileFullPath,
Catalog,
Key
);
WriteLogError(LogContext,
GetCatLogLevel(Err, UseAuthenticodePolicy),
Err
);
LoggedWarning = TRUE;
if(Problem) {
*Problem = SetupapiVerifyFileProblem;
StringCchCopy(ProblemFile, MAX_PATH, FileFullPath);
}
} else {
//
// Log the successful validation against the caller-specified
// catalog.
//
if(UseAuthenticodePolicy) {
WriteLogEntry(LogContext,
DRIVER_LOG_INFO | SETUP_LOG_BUFFER,
MSG_LOG_VERIFYFILE_AUTHENTICODE_AGAINST_FULLCATPATH_OK,
NULL,
FileFullPath,
Catalog,
Key
);
WriteLogError(LogContext, DRIVER_LOG_INFO, AuthenticodeErr);
} else {
WriteLogEntry(LogContext,
SETUP_LOG_VERBOSE,
MSG_LOG_VERIFYFILE_AGAINST_FULLCATPATH_OK,
NULL,
FileFullPath,
Catalog,
Key
);
}
}
//
// We don't attempt to fallback to self-contained signatures in
// cases where the catalog is specifically specified. Thus, we're
// done, regardless of whether or not we were successful.
//
leave;
}
//
// We'd better have a catalog name (i.e., not be doing global
// validation) if we're supposed to be verifying via Authenticode
// policy!
//
MYASSERT(!UseAuthenticodePolicy || Catalog);
//
// Search through installed catalogs looking for those that contain
// data for a file with the hash we just calculated (aka, "global
// validation").
//
PrevCat = NULL;
Err = GLE_FN_CALL(NULL,
hCatInfo = CryptCATAdminEnumCatalogFromHash(
LocalhCatAdmin,
Hash,
HashSize,
0,
&PrevCat)
);
while(hCatInfo) {
CatInfo.cbStruct = sizeof(CATALOG_INFO);
if(CryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0)) {
CatalogFullPath = CatInfo.wszCatalogFile;
//
// If we have a catalog name we're looking for, see if the
// current catalog matches. If the caller didn't specify a
// catalog, then just attempt to validate against each
// catalog we enumerate. Note that the catalog file info
// we get back gives us a fully qualified path.
//
if(Catalog) {
FoundMatchingImage = !lstrcmpi(
pSetupGetFileTitle(CatalogFullPath),
Catalog
);
} else {
if((Flags & VERIFY_FILE_USE_OEM_CATALOGS) ||
!IsInstalledFileFromOem(pSetupGetFileTitle(CatalogFullPath),
OemFiletypeCat)) {
FoundMatchingImage = TRUE;
} else {
FoundMatchingImage = FALSE;
}
}
if(FoundMatchingImage) {
//
// Increment our counter of how many catalogs we've
// considered.
//
if(NumCatalogsConsidered) {
(*NumCatalogsConsidered)++;
}
//
// If the caller supplied a mapped-in image of the
// catalog we're looking for, then check to see if this
// catalog matches by doing a binary compare.
//
if(CatalogBaseAddress) {
FoundMatchingImage = GetFileAttributesEx(
CatalogFullPath,
GetFileExInfoStandard,
&FileAttribData
);
//
// Check to see if the catalog we're looking
// at is the same size as the one we're
// verifying.
//
if(FoundMatchingImage &&
(FileAttribData.nFileSizeLow != CatalogImageSize)) {
FoundMatchingImage = FALSE;
}
if(FoundMatchingImage) {
if(NO_ERROR == pSetupOpenAndMapFileForRead(
CatalogFullPath,
&CurCatFileSize,
&CurCatFileHandle,
&CurCatMappingHandle,
&CurCatBaseAddress)) {
MYASSERT(CurCatFileSize == CatalogImageSize);
//
// Wrap this binary compare in its own try/
// except block, because if we run across a
// catalog we can't read for some reason,
// we don't want this to abort our search.
//
try {
FoundMatchingImage =
!memcmp(CatalogBaseAddress,
CurCatBaseAddress,
CatalogImageSize
);
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(),
ERROR_READ_FAULT,
NULL);
FoundMatchingImage = FALSE;
}
pSetupUnmapAndCloseFile(CurCatFileHandle,
CurCatMappingHandle,
CurCatBaseAddress
);
} else {
FoundMatchingImage = FALSE;
}
}
} else {
//
// Since there was no catalog image supplied to
// match against, the catalog we're currently
// looking at is considered a valid match
// candidate.
//
FoundMatchingImage = TRUE;
}
if(FoundMatchingImage) {
//
// We found an applicable catalog, now validate the
// file against that catalog (this also validates the
// catalog itself).
//
WintrustCatalogInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
Err = (DWORD)WinVerifyTrust(NULL,
(UseAuthenticodePolicy
? &AuthenticodeVerifyGuid
: &DriverVerifyGuid),
&WintrustData
);
//
// Fill in the DigitalSigner and SignerVersion if
// they were passed in.
//
if(Err == NO_ERROR) {
if(DigitalSigner) {
StringCchCopy(DigitalSigner, MAX_PATH, VersionInfo.wszSignedBy);
}
if(SignerVersion) {
StringCchCopy(SignerVersion, MAX_PATH, VersionInfo.wszVersion);
}
} else if(!UseAuthenticodePolicy && (Err == ERROR_APP_WRONG_OS)) {
//
// We failed validation via driver signing policy
// because of an osattribute mismatch. Translate
// this error into something with more
// understandable text.
//
// NOTE: Unfortunately, the crypto APIs report
// ERROR_APP_WRONG_OS in two quite different cases:
//
// 1. Valid driver signing catalog has an
// osattribute mismatch.
// 2. Authenticode catalog is being validated
// using driver signing policy.
//
// We want to translate the error in the first case
// but not in the second. The only way to
// distinguish these two cases is to re-attempt
// verification against _all_ os versions. Thus,
// any failure encountered must be due to #2.
//
//
// The DRIVER_VER_INFO structure was filled in
// during our previous call to WinVerifyTrust with
// a pointer that we must free!
//
if(VersionInfo.pcSignerCertContext) {
CertFreeCertificateContext(VersionInfo.pcSignerCertContext);
}
//
// Now reset the structure and clear the flag so
// that we'll validate against _any_ OS version
// (irrespective of the catalog's osattributes).
//
ZeroMemory(&VersionInfo, sizeof(DRIVER_VER_INFO));
VersionInfo.cbStruct = sizeof(DRIVER_VER_INFO);
WintrustData.dwProvFlags &= ~WTD_USE_DEFAULT_OSVER_CHECK;
if(NO_ERROR == WinVerifyTrust(NULL,
&DriverVerifyGuid,
&WintrustData)) {
//
// The catalog is a valid driver signing
// catalog (i.e., case #1 discussed above).
// Translate this into more specific/meaningful
// error.
//
Err = ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH;
}
}
//
// If we successfully validated via Authenticode
// policy, then we need to determine whether the
// catalog's publisher is in the TrustedPublisher cert
// store.
//
if((Err == NO_ERROR) && UseAuthenticodePolicy) {
AuthenticodeErr = _VerifyCatalogFile(
LogContext,
WintrustCatalogInfo.pcwszCatalogFilePath,
NULL,
TRUE,
(VerifyContext
? &(VerifyContext->hStoreTrustedPublisher)
: NULL),
hWVTStateData
);
MYASSERT((AuthenticodeErr == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(AuthenticodeErr == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED));
if((AuthenticodeErr != ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) &&
(AuthenticodeErr != ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
//
// This shouldn't have failed, but since it
// did, we'll propagate this over to our error
// status so we treat it appropriately.
//
Err = AuthenticodeErr;
}
}
if(!UseAuthenticodePolicy) {
//
// The DRIVER_VER_INFO structure was filled in with
// a pointer that we must free!
//
if(VersionInfo.pcSignerCertContext) {
CertFreeCertificateContext(VersionInfo.pcSignerCertContext);
VersionInfo.pcSignerCertContext = NULL;
}
}
if(Err != NO_ERROR) {
WriteLogEntry(LogContext,
GetCatLogLevel(Err, UseAuthenticodePolicy) | SETUP_LOG_BUFFER,
(UseAuthenticodePolicy
? MSG_LOG_VERIFYFILE_AUTHENTICODE_GLOBAL_VALIDATION_ERROR
: MSG_LOG_VERIFYFILE_GLOBAL_VALIDATION_ERROR),
NULL,
FileFullPath,
CatInfo.wszCatalogFile,
Key
);
WriteLogError(LogContext,
GetCatLogLevel(Err, UseAuthenticodePolicy),
Err
);
LoggedWarning = TRUE;
} else {
if(UseAuthenticodePolicy) {
WriteLogEntry(LogContext,
DRIVER_LOG_INFO | SETUP_LOG_BUFFER,
MSG_LOG_VERIFYFILE_AUTHENTICODE_GLOBAL_VALIDATION_OK,
NULL,
FileFullPath,
CatInfo.wszCatalogFile,
Key
);
WriteLogError(LogContext,
DRIVER_LOG_INFO,
AuthenticodeErr
);
} else {
WriteLogEntry(LogContext,
SETUP_LOG_VERBOSE,
MSG_LOG_VERIFYFILE_GLOBAL_VALIDATION_OK,
NULL,
FileFullPath,
CatInfo.wszCatalogFile,
Key
);
}
}
if(Err == NO_ERROR) {
//
// We successfully verified the file--store the
// name of the catalog used, if the caller
// requested it.
//
if(CatalogFileUsed) {
StringCchCopy(CatalogFileUsed, MAX_PATH, CatalogFullPath);
}
} else {
if(Catalog || CatalogBaseAddress) {
//
// We only want to return the catalog file used
// in cases where we believe the catalog to be
// valid. In this situation, the only case we
// can tell for certain is when we encounter an
// osattribute mismatch.
//
if(CatalogFileUsed && (Err == ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH)) {
StringCchCopy(CatalogFileUsed, MAX_PATH, CatalogFullPath);
}
if(Problem) {
*Problem = SetupapiVerifyFileProblem;
StringCchCopy(ProblemFile, MAX_PATH, FileFullPath);
}
}
}
//
// If the result of the above validations is success,
// then we're done. If not, and we're looking for a
// relevant catalog file (i.e., the INF didn't specify
// one), then we move on to the next catalog.
// Otherwise, we've failed.
//
if((Err == NO_ERROR) || Catalog || CatalogBaseAddress) {
CryptCATAdminReleaseCatalogContext(LocalhCatAdmin, hCatInfo, 0);
hCatInfo = NULL;
break;
}
}
}
}
PrevCat = hCatInfo;
Err = GLE_FN_CALL(NULL,
hCatInfo = CryptCATAdminEnumCatalogFromHash(
LocalhCatAdmin,
Hash,
HashSize,
0,
&PrevCat)
);
}
if(Err == NO_ERROR) {
//
// We successfully validated the file--we're done!
//
leave;
}
//
// report failure if we haven't already done so
//
if(!LoggedWarning) {
WriteLogEntry(LogContext,
GetCatLogLevel(Err, UseAuthenticodePolicy) | SETUP_LOG_BUFFER,
(UseAuthenticodePolicy
? MSG_LOG_VERIFYFILE_AUTHENTICODE_GLOBAL_VALIDATION_NO_CATS_FOUND
: MSG_LOG_VERIFYFILE_GLOBAL_VALIDATION_NO_CATS_FOUND),
NULL,
FileFullPath,
Catalog ? Catalog : TEXT(""),
Key
);
WriteLogError(LogContext,
GetCatLogLevel(Err, UseAuthenticodePolicy),
Err
);
LoggedWarning = TRUE;
}
if(!(Flags & (VERIFY_FILE_IGNORE_SELFSIGNED | VERIFY_FILE_USE_AUTHENTICODE_CATALOG))) {
//
// We've been instructed to allow self-signed files to be
// considered valid, so check for self-contained signature now.
//
WintrustData.dwUnionChoice = WTD_CHOICE_FILE;
WintrustData.pFile = &WintrustFileInfo;
ZeroMemory(&WintrustFileInfo, sizeof(WINTRUST_FILE_INFO));
WintrustFileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
WintrustFileInfo.pcwszFilePath = FileFullPath;
Err = (DWORD)WinVerifyTrust(NULL,
&DriverVerifyGuid,
&WintrustData
);
//
// Fill in the DigitalSigner and SignerVersion if
// they were passed in.
//
if(Err == NO_ERROR) {
if(DigitalSigner) {
StringCchCopy(DigitalSigner, MAX_PATH, VersionInfo.wszSignedBy);
}
if(SignerVersion) {
StringCchCopy(SignerVersion, MAX_PATH, VersionInfo.wszVersion);
}
}
//
// The DRIVER_VER_INFO structure was filled in with a pointer that
// we must free!
//
if(VersionInfo.pcSignerCertContext) {
CertFreeCertificateContext(VersionInfo.pcSignerCertContext);
VersionInfo.pcSignerCertContext = NULL;
}
if(Err != NO_ERROR) {
WriteLogEntry(LogContext,
SETUP_LOG_VERBOSE | SETUP_LOG_BUFFER,
MSG_LOG_SELFSIGN_ERROR,
NULL,
FileFullPath,
Key
);
WriteLogError(LogContext, SETUP_LOG_VERBOSE, Err);
LoggedWarning = TRUE;
} else {
WriteLogEntry(LogContext,
SETUP_LOG_VERBOSE,
MSG_LOG_SELFSIGN_OK,
NULL,
FileFullPath,
Key
);
}
}
if(Err == NO_ERROR) {
//
// The file validated without a catalog. Store an empty string
// in the CatalogFileUsed buffer (if supplied).
//
if(CatalogFileUsed) {
*CatalogFileUsed = TEXT('\0');
}
} else {
//
// report error prior to Self-Sign check
//
if(Problem) {
*Problem = SetupapiVerifyFileProblem;
StringCchCopy(ProblemFile, MAX_PATH, FileFullPath);
}
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
}
if(Err == NO_ERROR) {
//
// If we successfully validated via Authenticode policy, then we should
// have squirrelled away one of two "special" errors to be returned to
// the caller...
//
MYASSERT((AuthenticodeErr == NO_ERROR) || UseAuthenticodePolicy);
Err = AuthenticodeErr;
} else {
//
// If we failed, and we haven't already logged a message indicating the
// cause of the failure, log a generic message now.
//
if(!LoggedWarning) {
WriteLogEntry(LogContext,
GetCatLogLevel(Err, UseAuthenticodePolicy) | SETUP_LOG_BUFFER,
(UseAuthenticodePolicy
? MSG_LOG_VERIFYFILE_AUTHENTICODE_ERROR
: MSG_LOG_VERIFYFILE_ERROR),
NULL,
FileFullPath,
Catalog ? Catalog : TEXT(""),
Key
);
WriteLogError(LogContext,
GetCatLogLevel(Err, UseAuthenticodePolicy),
Err
);
}
if(hWVTStateData && *hWVTStateData) {
//
// We must've hit an exception after retrieving the WinVerifyTrust
// state data. This is extremely unlikely, but if this happens,
// we need to free this here and now.
//
pSetupCloseWVTStateData(*hWVTStateData);
*hWVTStateData = NULL;
}
}
if(!VerifyContext && LocalhSDBDrvMain) {
//
// Don't need to return our HSDB to the caller, so free it now.
//
SdbReleaseDatabase(LocalhSDBDrvMain);
}
if(hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
}
if(AltPlatSlot) {
ReleaseLogInfoSlot(LogContext, AltPlatSlot);
}
if(Hash) {
MyFree(Hash);
}
if(!UseAuthenticodePolicy && VersionInfo.pcSignerCertContext) {
CertFreeCertificateContext(VersionInfo.pcSignerCertContext);
}
if(hCatInfo) {
MYASSERT(LocalhCatAdmin);
CryptCATAdminReleaseCatalogContext(LocalhCatAdmin, hCatInfo, 0);
}
if(!VerifyContext && LocalhCatAdmin) {
CryptCATAdminReleaseContext(LocalhCatAdmin, 0);
}
return Err;
}
DWORD
pSetupVerifyFile(
IN PSETUP_LOG_CONTEXT LogContext,
IN LPCTSTR Catalog, OPTIONAL
IN PVOID CatalogBaseAddress, OPTIONAL
IN DWORD CatalogImageSize,
IN LPCTSTR Key,
IN LPCTSTR FileFullPath,
OUT SetupapiVerifyProblem *Problem, OPTIONAL
OUT LPTSTR ProblemFile, OPTIONAL
IN BOOL CatalogAlreadyVerified,
IN PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo, OPTIONAL
OUT LPTSTR CatalogFileUsed, OPTIONAL
OUT PDWORD NumCatalogsConsidered OPTIONAL
)
/*++
Routine Description:
See _VerifyFile
Since this private API is exported for use by other system components
(e.g., syssetup), we make a check to ensure that the AltPlatformInfo, if
specified, is of the correct version.
--*/
{
if(AltPlatformInfo) {
if(AltPlatformInfo->cbSize != sizeof(SP_ALTPLATFORM_INFO_V2)) {
return ERROR_INVALID_PARAMETER;
}
if(!(AltPlatformInfo->Flags & SP_ALTPLATFORM_FLAGS_VERSION_RANGE)) {
//
// this flag must be set to indicate the version range fields are
// valid
//
return ERROR_INVALID_PARAMETER;
}
}
return _VerifyFile(LogContext,
NULL,
Catalog,
CatalogBaseAddress,
CatalogImageSize,
Key,
FileFullPath,
Problem,
ProblemFile,
CatalogAlreadyVerified,
AltPlatformInfo,
0,
CatalogFileUsed,
NumCatalogsConsidered,
NULL,
NULL,
NULL
);
}
BOOL
IsInfForDeviceInstall(
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
IN CONST GUID *DeviceSetupClassGuid, OPTIONAL
IN PLOADED_INF LoadedInf, OPTIONAL
OUT PTSTR *DeviceDesc, OPTIONAL
OUT PSP_ALTPLATFORM_INFO_V2 *ValidationPlatform, OPTIONAL
OUT PDWORD PolicyToUse, OPTIONAL
OUT PBOOL UseOriginalInfName, OPTIONAL
IN BOOL ForceNonDrvSignPolicy
)
/*++
Routine Description:
This routine determines whether the specified INF is a device INF. If so,
it returns a generic string to use in identifying the device installation
when there is no device description available (e.g., installing a printer).
It can also return the appropriate platform parameters to be used in
digital signature verification for this INF, as well as the codesigning
policy to employ should a digital signature validation failure occur.
Finally, this routine can indicate whether the INF should be installed
under its original name (i.e., because it's an exception package INF).
Arguments:
LogContext - Optionally, supplies the log context for any log entries that
might be generated by this routine.
DeviceSetupClassGuid - Optionally, supplies the address of a GUID that
indicates which device setup class is to be used in determining
information such as description, validation platform, and policy to
use. If this parameter is NULL, then the GUID is retrieved from the
INF list supplied via the LoadedInf parameter.
LoadedInf - Optionally, supplies the address of a loaded INF list we need
to examine to see if any of the members therein are device INFs. An
INF is a device INF if it specifies a class association (via either
Class= or ClassGUID= entries) in its [Version] section. If the
DeviceSetupClassGuid parameter is supplied (i.e., non-NULL), then this
parameter is ignored. If this parameter is also NULL, then it is
assumed the installation is not device-related.
The presence of a device INF anywhere in this list will cause us to
consider this a device installation. However, the _first_ INF we
encounter having a class association is what will be used in
determining the device description (see below).
** NOTE: The caller is responsible for locking the loaded INF **
** list prior to calling this routine! **
DeviceDesc - Optionally, supplies the address of a string pointer that will
be filled in upon return with a (newly-allocated) descriptive string to
be used when referring to this installation (e.g., for doing driver
signing failure popups). We will first try to retrieve the friendly
name for this INF's class (whose determination is described above). If
that's not possible (e.g., the class isn't yet installed), then we'll
return the class name. If that's not possible, then we'll return a
(localized) generic string such as "Unknown driver software package".
This output parameter (if supplied) will only ever be set to a non-NULL
value when the routine returns TRUE. The caller is responsible for
freeing this character buffer. If an out-of-memory failure is
encountered when trying to allocate memory for this buffer, the
DeviceDesc pointer will simply be set to NULL. It will also be set to
NULL if we're dealing with an exception package (since we don't want
to treat this like a "hardware install" for purposes of codesigning
blocking UI).
ValidationPlatform - Optionally, supplies the address of a (version 2)
altplatform info pointer that is filled in upon return with a newly-
allocated structure specifying the appropriate parameters to be passed
to WinVerifyTrust when validating this INF. These parameters are
retrieved from certclas.inf for the relevant device setup class GUID.
If no special parameters are specified for this class (or if the INF
has no class at all), then this pointer is returned as NULL, which
causes us to use WinVerifyTrust's default validation. Note that if
we fail to allocate this structure due to low-memory, the pointer will
be returned as NULL in that case as well. This is OK, because this
simply means we'll do default validation in that case.
By this mechanism, we can easily deal with the different validation
policies in effect for the various device classes we have in the
system.
The caller is responsible for freeing the memory allocated for this
structure.
PolicyToUse - Optionally, supplies the address of a dword variable that is
set upon return to indicate what codesigning policy (i.e., Ignore,
Warn, or Block) should be used for this INF. This determination is
made based on whether any INF in the list is of a class that WHQL has a
certification program for (as specified in %windir%\Inf\certclas.inf).
Additionally, if any INF in the list is of the exception class, then
the policy is automatically set to Block (i.e., it's not configurable
via driver signing or non-driver-signing policy/preference settings).
If it's appropriate to allow for a fallback to an Authenticode
signature, then the high bit of the policy value will be set (i.e.,
DRIVERSIGN_ALLOW_AUTHENTICODE).
UseOriginalInfName - Optionally, supplies the address of a boolean variable
that is set upon return to indicate whether the INF should be installed
into %windir%\Inf under its original name. This will only be true if
the INF is an exception INF.
ForceNonDrvSignPolicy - if TRUE, then we'll use non-driver signing policy,
regardless of whether the INF has an associated device setup class
GUID. (Note: this override won't work if that class GUID is in the
WHQL logo-able list contained in certclas.inf--in that case, we always
want to use driver signing policy.)
This override will also cause us to report that the INF is _not_ a
device INF (assuming it's not a WHQL class, as described above).
Return Value:
If the INF is a device INF, the return value is TRUE. Otherwise, it is
FALSE.
--*/
{
PLOADED_INF CurInf, NextInf;
GUID ClassGuid;
BOOL DeviceInfFound, ClassInDrvSignList;
TCHAR ClassDescBuffer[LINE_LEN];
PCTSTR ClassDesc;
DWORD Err;
BOOL IsExceptionInf = FALSE;
if(DeviceDesc) {
*DeviceDesc = NULL;
}
if(ValidationPlatform) {
*ValidationPlatform = NULL;
}
if(UseOriginalInfName) {
*UseOriginalInfName = FALSE;
}
if(!DeviceSetupClassGuid && !LoadedInf) {
//
// Not a whole lot to do here. Assume non-driver-signing policy and
// return.
//
if(PolicyToUse) {
*PolicyToUse = pSetupGetCurrentDriverSigningPolicy(FALSE);
//
// Non-device installs are always candidates for Authenticode
// signatures.
//
*PolicyToUse |= DRIVERSIGN_ALLOW_AUTHENTICODE;
}
return FALSE;
}
if(PolicyToUse) {
*PolicyToUse = DRIVERSIGN_NONE;
}
//
// If DeviceSetupClassGuid was specified, then retrieve information
// pertaining to that class. Otherwise, traverse the individual INFs in
// the LOADED_INF list, examining each one to see if it's a device INF.
//
DeviceInfFound = FALSE;
ClassInDrvSignList = FALSE;
try {
for(CurInf = LoadedInf; CurInf || DeviceSetupClassGuid; CurInf = NextInf) {
//
// Setup a "NextInf" pointer so we won't dereference NULL when we
// go back through the loop in the case where we have a
// DeviceSetupClassGuid instead of a LoadedInf list.
//
NextInf = CurInf ? CurInf->Next : NULL;
if(!DeviceSetupClassGuid) {
if(ClassGuidFromInfVersionNode(&(CurInf->VersionBlock), &ClassGuid)) {
DeviceSetupClassGuid = &ClassGuid;
} else {
//
// This INF doesn't have an associated device setup class
// GUID, so skip it and continue on with our search for a
// device INF.
//
continue;
}
}
//
// NOTE: From this point forward, you must reset the
// DeviceSetupClasGuid pointer to NULL before making another pass
// through the loop. Otherwise, we'll enter an infinite loop,
// since we can enter the loop if that pointer is non-NULL.
//
if(IsEqualGUID(DeviceSetupClassGuid, &GUID_NULL)) {
//
// The INF specified a ClassGUID of GUID_NULL (e.g., like some
// of our non-device system INFs such as layout.inf do). Skip
// it, and continue on with our search for a device INF.
//
DeviceSetupClassGuid = NULL;
continue;
}
//
// If we get to this point, we have a device setup class GUID. If
// this is the first device INF we've encountered (or our first and
// only time through the loop when the caller passed us in a
// DeviceSetupClassGuid), then do our best to retrieve a
// description for it (if we've been asked to do so). We do not do
// this for exception packages, because we don't want them to be
// referred to as "hardware installs" in the Block dialog if a
// signature verification failure occurs.
//
if(!DeviceInfFound) {
DeviceInfFound = TRUE;
if(DeviceDesc) {
if(!IsEqualGUID(DeviceSetupClassGuid, &GUID_DEVCLASS_WINDOWS_COMPONENT_PUBLISHER)) {
//
// First, try to retrieve the class's friendly name.
//
if(SetupDiGetClassDescription(DeviceSetupClassGuid,
ClassDescBuffer,
SIZECHARS(ClassDescBuffer),
NULL)) {
ClassDesc = ClassDescBuffer;
} else if(CurInf) {
//
// OK, so the class isn't installed yet. Retrieve
// the class name from the INF itself.
//
ClassDesc = pSetupGetVersionDatum(&(CurInf->VersionBlock),
pszClass
);
} else {
//
// The caller passed us in a device setup class
// GUID instead of an INF. The class hasn't been
// installed yet, so we have no idea what to call
// it.
//
ClassDesc = NULL;
}
if(!ClassDesc) {
//
// We have a non-installed class, either with no
// INF, or with an INF that specifies a ClassGUID=
// entry, but no Class= entry in its [Version]
// section. If we tried to actually install a
// device from such an INF, we'd get a failure in
// SetupDiInstallClass because the class name is
// required when installing the class. However,
// this INF might never be used in a device
// installation, but it definitely is a device INF.
// Therefore, we just give it a generic
// description.
//
if(LoadString(MyDllModuleHandle,
IDS_UNKNOWN_DRIVER,
ClassDescBuffer,
SIZECHARS(ClassDescBuffer))) {
ClassDesc = ClassDescBuffer;
} else {
ClassDesc = NULL;
}
}
//
// OK, we have a description for this device (unless we
// hit some weird error).
//
if(ClassDesc) {
*DeviceDesc = DuplicateString(ClassDesc);
}
}
}
}
//
// If we get to this point, we know that: (a) we have a device
// setup class GUID and (b) we've retrieved a device description,
// if necessary/possible.
//
// Now, check to see if this is an exception class--if it is, then
// policy is Block, and we want to install the INF and CAT files
// using their original names.
//
if(IsEqualGUID(DeviceSetupClassGuid, &GUID_DEVCLASS_WINDOWS_COMPONENT_PUBLISHER)) {
if(PolicyToUse) {
*PolicyToUse = DRIVERSIGN_BLOCKING;
}
if(UseOriginalInfName) {
*UseOriginalInfName = TRUE;
}
IsExceptionInf = TRUE;
}
if(PolicyToUse || ValidationPlatform || ForceNonDrvSignPolicy) {
//
// Now check to see if this class is in our list of classes
// that WHQL has certification programs for (hence should be
// subject to driver signing policy).
//
// NOTE: We may also find the exception class GUID in this
// list. This may be used as an override mechanism, in case we
// decide to allow 5.0-signed exception packages install on
// 5.1, for example. This should never be the case, since an
// exception package destined for multiple OS versions should
// have an OS attribute explicitly calling out each OS version
// for which it is applicable. The fact that we check for the
// exception package override in certclas.inf is simply an
// "escape hatch" for such an unfortunate eventuality.
//
ClassInDrvSignList = ClassGuidInDrvSignPolicyList(
LogContext,
DeviceSetupClassGuid,
ValidationPlatform
);
if(ClassInDrvSignList) {
//
// Once we encounter a device INF whose class is in our
// driver signing policy list, we can stop looking...
//
break;
}
} else {
//
// The caller doesn't care about whether this class is subject
// to driver signing policy. Since we've already retrieved the
// info they care about, we can get out of this loop.
//
break;
}
DeviceSetupClassGuid = NULL; // break out in no-INF case
}
//
// Unless we've already established that the policy to use is "Block"
// (i.e., because we found an exception INF), we should retrieve the
// applicable policy now...
//
if(PolicyToUse && (*PolicyToUse != DRIVERSIGN_BLOCKING)) {
if(ForceNonDrvSignPolicy) {
//
// We want to use non-driver signing policy unless the INF's
// class is in our WHQL-approved list.
//
if(ClassInDrvSignList) {
*PolicyToUse = pSetupGetCurrentDriverSigningPolicy(TRUE);
} else {
*PolicyToUse = pSetupGetCurrentDriverSigningPolicy(FALSE)
| DRIVERSIGN_ALLOW_AUTHENTICODE;
}
} else {
//
// If we have a device INF, we want to use driver signing
// policy. Otherwise, we want "non-driver signing" policy.
//
*PolicyToUse = pSetupGetCurrentDriverSigningPolicy(DeviceInfFound);
//
// If we have a device setup class, and that class is in our
// WHQL-approved list, then we should not accept Authenticode
// signatures.
//
if(!ClassInDrvSignList) {
*PolicyToUse |= DRIVERSIGN_ALLOW_AUTHENTICODE;
}
}
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
}
if(ForceNonDrvSignPolicy) {
return (ClassInDrvSignList || IsExceptionInf);
} else {
return DeviceInfFound;
}
}
DWORD
GetCodeSigningPolicyForInf(
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
IN HINF InfHandle,
OUT PSP_ALTPLATFORM_INFO_V2 *ValidationPlatform, OPTIONAL
OUT PBOOL UseOriginalInfName OPTIONAL
)
/*++
Routine Description:
This routine returns a value indicating the appropriate policy to be
employed should a digital signature verification failure arise from some
operation initiated by that INF. It figures out whether the INF is subject
to driver signing or non-driver signing policy (based on the INF's class
affiliation), as well as whether or not Authenticode signatures should be
allowed (based on the presence of an applicable WHQL program). It also can
return an altplatform info structure indicating how validation should be
done (i.e., if certclas.inf indicates that a range of OSATTR versions
should be considered valid).
Arguments:
LogContext - Optionally, supplies the log context for any log entries that
might be generated by this routine.
InfHandle - Supplies the handle of the INF for which policy is to be
retrieved.
ValidationPlatform - See preamble of IsInfForDeviceInstall routine for
details.
UseOriginalInfName - Optionally, supplies the address of a boolean variable
that is set upon return to indicate whether the INF should be installed
into %windir%\Inf under its original name. This will only be true if
the INF is an exception INF.
Return Value:
DWORD indicating the policy to be used...
DRIVERSIGN_NONE
DRIVERSIGN_WARNING
DRIVERSIGN_BLOCKING
...potentially OR'ed with the DRIVERSIGN_ALLOW_AUTHENTICODE flag, if it's
acceptable to check for Authenticode signatures.
--*/
{
DWORD Policy;
if(UseOriginalInfName) {
*UseOriginalInfName = FALSE;
}
if(ValidationPlatform) {
*ValidationPlatform = NULL;
}
if(!LockInf((PLOADED_INF)InfHandle)) {
//
// This is an internal-only routine, and all callers should be passing
// in valid INF handles.
//
MYASSERT(FALSE);
//
// If this does happen, just assume this isn't a device INF (but don't
// allow Authenticode signatures).
//
return pSetupGetCurrentDriverSigningPolicy(FALSE);
}
try {
IsInfForDeviceInstall(LogContext,
NULL,
(PLOADED_INF)InfHandle,
NULL,
ValidationPlatform,
&Policy,
UseOriginalInfName,
TRUE // use non-driver signing policy unless it's a WHQL class
);
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
//
// We have to return a policy so we'll default to warn (w/o allowing
// use of Authenticode signatures).
//
Policy = DRIVERSIGN_WARNING;
}
UnlockInf((PLOADED_INF)InfHandle);
return Policy;
}
VOID
pSetupGetRealSystemTime(
OUT LPSYSTEMTIME RealSystemTime
);
DWORD
pSetupGetCurrentDriverSigningPolicy(
IN BOOL IsDeviceInstallation
)
/*++
Routine Description:
(The following description describes the strategy behind the selection of
policy. The implementation, however, follows a few twists and turns in
order to thwart unscupulous individuals who would subvert digital signature
UI in order to avoid having to get their packages signed...)
This routine returns a value indicating what action should be taken when a
digital signature verification failure is encountered. Separate "policies"
are maintained for "DriverSigning" (i.e., device installer activities) and
"NonDriverSigning" (i.e., everything else).
** NOTE: presently, an INF that doesn't identify itself as a device INF **
** (i.e., because it doesn't include a non-NULL ClassGuid entry in its **
** [Version] section) will always fall under "non-driver signing" **
** policy, even though it's possible the INF may be making driver- **
** related changes (e.g., copying new driver files to **
** %windir%\system32\drivers, adding services via either AddReg or **
** AddService, etc.). **
For driver signing, there are actually 3 sources of policy:
1. HKLM\Software\Microsoft\Driver Signing : Policy : REG_BINARY (REG_DWORD also supported)
This is a Windows 98-compatible value that specifies the default
behavior which applies to all users of the machine.
2. HKCU\Software\Microsoft\Driver Signing : Policy : REG_DWORD
This specifies the user's preference for what behavior to employ
upon verification failure.
3. HKCU\Software\Policies\Microsoft\Windows NT\Driver Signing : BehaviorOnFailedVerify : REG_DWORD
This specifies the administrator-mandated policy on what behavior
to employ upon verification failure. This policy, if specified,
overrides the user's preference.
The algorithm for deciding on the behavior to employ is as follows:
if (3) is specified {
policy = (3)
} else {
policy = (2)
}
policy = MAX(policy, (1))
For non-driver signing, the algorithm is the same, except that values (1),
(2), and (3) come from the following registry locations:
1. HKLM\Software\Microsoft\Non-Driver Signing : Policy : REG_BINARY (REG_DWORD also supported)
2. HKCU\Software\Microsoft\Non-Driver Signing : Policy : REG_DWORD
3. HKCU\Software\Policies\Microsoft\Windows NT\Non-Driver Signing : BehaviorOnFailedVerify : REG_DWORD
NOTE: If we're in non-interactive mode, policy is always Block, so we
don't even bother trying to retrieve any of these registry settings.
Another reason to avoid doing so is to keep from jumping to the
wrong conclusion that someone has tampered with policy when in
reality, we're in a service that loaded in GUI setup prior to the
time when the policy values were fully initialized.
Arguments:
IsDeviceInstallation - If non-zero, then driver signing policy should be
retrieved. Otherwise, non-driver signing policy should be used.
Return Value:
Value indicating the policy in effect. May be one of the following three
values:
DRIVERSIGN_NONE - silently succeed installation of unsigned/
incorrectly-signed files. A PSS log entry will
be generated, however (as it will for all 3
types)
DRIVERSIGN_WARNING - warn the user, but let them choose whether or not
they still want to install the problematic file
DRIVERSIGN_BLOCKING - do not allow the file to be installed
(If policy can't be retrieved from any of the sources described above, the
default is DRIVERSIGN_NONE.)
--*/
{
SYSTEMTIME RealSystemTime;
DWORD PolicyFromReg, PolicyFromDS, RegDataType, RegDataSize;
HKEY hKey;
BOOL UserPolicyRetrieved = FALSE;
WORD w;
if(GlobalSetupFlags & PSPGF_NONINTERACTIVE) {
return DRIVERSIGN_BLOCKING;
}
w = IsDeviceInstallation?1:0;
RealSystemTime.wDayOfWeek = (LOWORD(&hKey)&~4)|(w<<2);
pSetupGetRealSystemTime(&RealSystemTime);
PolicyFromReg = (((RealSystemTime.wMilliseconds+2)&15)^8)/4;
MYASSERT(PolicyFromReg <= DRIVERSIGN_BLOCKING);
//
// Retrieve the user policy. If policy isn't set for this user, then
// retrieve the user's preference, instead.
//
PolicyFromDS = DRIVERSIGN_NONE;
hKey = INVALID_HANDLE_VALUE;
try {
if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER,
(IsDeviceInstallation ? pszDrvSignPolicyPath
: pszNonDrvSignPolicyPath),
0,
KEY_READ,
&hKey)) {
//
// Ensure hKey is still invalid so we won't try to free it.
//
hKey = INVALID_HANDLE_VALUE;
} else {
RegDataSize = sizeof(PolicyFromDS);
if(ERROR_SUCCESS == RegQueryValueEx(
hKey,
pszDrvSignBehaviorOnFailedVerifyDS,
NULL,
&RegDataType,
(PBYTE)&PolicyFromDS,
&RegDataSize)) {
if((RegDataType == REG_DWORD) &&
(RegDataSize == sizeof(DWORD)) &&
((PolicyFromDS == DRIVERSIGN_NONE) || (PolicyFromDS == DRIVERSIGN_WARNING) || (PolicyFromDS == DRIVERSIGN_BLOCKING)))
{
//
// We successfully retrieved user policy, so we won't need
// to retrieve user preference.
//
UserPolicyRetrieved = TRUE;
}
}
RegCloseKey(hKey);
hKey = INVALID_HANDLE_VALUE;
}
//
// If we didn't find a user policy, then retrieve the user preference.
//
if(!UserPolicyRetrieved) {
if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER,
(IsDeviceInstallation ? pszDrvSignPath
: pszNonDrvSignPath),
0,
KEY_READ,
&hKey)) {
//
// Ensure hKey is still invalid so we won't try to free it.
//
hKey = INVALID_HANDLE_VALUE;
} else {
RegDataSize = sizeof(PolicyFromDS);
if(ERROR_SUCCESS == RegQueryValueEx(hKey,
pszDrvSignPolicyValue,
NULL,
&RegDataType,
(PBYTE)&PolicyFromDS,
&RegDataSize))
{
if((RegDataType != REG_DWORD) ||
(RegDataSize != sizeof(DWORD)) ||
!((PolicyFromDS == DRIVERSIGN_NONE) || (PolicyFromDS == DRIVERSIGN_WARNING) || (PolicyFromDS == DRIVERSIGN_BLOCKING)))
{
//
// Bogus entry for user preference--ignore it.
//
PolicyFromDS = DRIVERSIGN_NONE;
}
}
//
// No need to close the registry key handle--it'll get closed
// after we exit the try/except block.
//
}
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
}
if(hKey != INVALID_HANDLE_VALUE) {
RegCloseKey(hKey);
}
//
// Now return the more restrictive of the two policies.
//
if(PolicyFromDS > PolicyFromReg) {
return PolicyFromDS;
} else {
return PolicyFromReg;
}
}
DWORD
VerifySourceFile(
IN PSETUP_LOG_CONTEXT LogContext,
IN PSP_FILE_QUEUE Queue, OPTIONAL
IN PSP_FILE_QUEUE_NODE QueueNode, OPTIONAL
IN PCTSTR Key,
IN PCTSTR FileToVerifyFullPath,
IN PCTSTR OriginalSourceFileFullPath, OPTIONAL
IN PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo, OPTIONAL
IN DWORD Flags,
OUT SetupapiVerifyProblem *Problem,
OUT LPTSTR ProblemFile,
OUT LPTSTR CatalogFileUsed, OPTIONAL
OUT LPTSTR DigitalSigner, OPTIONAL
OUT LPTSTR SignerVersion, OPTIONAL
OUT HANDLE *hWVTStateData OPTIONAL
)
/*++
Routine Description:
This routine verifies the digital signature of the specified file either
globally (i.e., using all catalogs), or based on the catalog file specified
in the supplied queue node.
Arguments:
LogContext - Supplies a context for logging the verify
Queue - Optionally, supplies pointer to the queue structure. This contains
information about the default verification method to use when the file
isn't associated with a particular catalog.
QueueNode - Optionally, supplies the queue node containing catalog
information to be used when verifying the file's signature. If not
supplied, then the file will be verified using all applicable installed
catalogs. If this pointer is supplied, then so must the Queue
parameter.
Key - Supplies a value that "indexes" the catalog, telling the verify APIs
which signature datum within the catalog it should use. Typically
the key is the name of the destination file (sans path) that the source
file is to be copied to.
FileToVerifyFullPath - Supplies the full path of the file to be verified.
OriginalSourceFileFullPath - Optionally, supplies the original source
file's name, to be returned in the ProblemFile buffer when an error
occurs. If this parameter is not specified, then the source file's
original name is assumed to be the same as the filename we're
verifying, and the path supplied in FileToVerifyFullPath will be
returned in the ProblemFile buffer in case of error.
AltPlatformInfo - optionally, supplies alternate platform information used
to fill in a DRIVER_VER_INFO structure (defined in sdk\inc\softpub.h)
that is passed to WinVerifyTrust.
** NOTE: This structure _must_ have its cbSize field set to **
** sizeof(SP_ALTPLATFORM_INFO_V2) -- validation on client-supplied **
** buffer is the responsibility of the caller. **
Flags - supplies flags that alter that behavior of this routine. May be a
combination of the following values:
VERIFY_FILE_IGNORE_SELFSIGNED - if this bit is set, then this routine
will fail validation for self-signed
binaries. NOTE: when validating via
Authenticode policy, we always ignore
self-signed binaries.
VERIFY_FILE_USE_OEM_CATALOGS - if this bit is set, then all catalogs
installed in the system will be scanned
to verify the given file. Otherwise,
OEM (3rd party) catalogs will NOT be
scanned to verify the given file. This
is only applicable if a QueueNode
specifying a specific catalog is not
given. NOTE: this flag should not be
specified when requesting validation
via Authenticode policy.
VERIFY_FILE_USE_AUTHENTICODE_CATALOG - Validate the file using a
catalog signed with Authenticode
policy. If this flag is set,
we'll _only_ check for
Authenticode signatures, so if
the caller wants to first try
validating a file for OS code-
signing usage, then falling back
to Authenticode, they'll have to
call this routine twice.
If this flag is set, then the
caller may also supply the
hWVTStateData output parameter,
which can be used to prompt user
in order to establish that the
publisher should be trusted.
VerifySourceFile will return one
of two error codes upon
successful validation via
Authenticode policy. Refer to
the "Return Value" section for
details.
VERIFY_FILE_DRIVERBLOCKED_ONLY - Only check if the file is in the bad
driver database, don't do any digital
sigature validation. NOTE: this flag
should not be specified when
requesting validation via Authenticode
policy.
VERIFY_FILE_NO_DRIVERBLOCKED_CHECK - Don't check if the file is blocked
via the Bad Driver Database.
NOTE: this flag has no effect when
validating via Authenticode policy
because we never check the bad
driver database in that case.
Problem - Points to a variable that will be filled in upon unsuccessful
return with the cause of failure.
ProblemFile - Supplies the address of a character buffer that will be
filled in upon unsuccessful return to indicate the file that failed
verification. This may be the name of the file we're verifying (or
it's original name, if supplied), or it may be the name of the catalog
used for verification, if the catalog itself isn't properly signed.
(The type of file can be ascertained from the value returned in the
Problem output parameter.)
CatalogFileUsed - if supplied, this parameter points to a character buffer
at least MAX_PATH characters big that receives the name of the catalog
file used to verify the specified file. This is only filled in upon
successful return, or when the Problem is SetupapiVerifyFileProblem
(i.e., the catalog verified, but the file did not). If this buffer is
set to the empty string upon a SetupapiVerifyFileProblem failure, then
we didn't find any applicable catalogs to use for validation.
Also, this buffer will contain an empty string upon successful return
if the file was validated without using a catalog (i.e., the file
contains its own signature).
DigitalSigner - if supplied, this parameter points to a character buffer of
at least MAX_PATH characters that receives the name of who digitally
signed the specified file. This value is only set if the Key is
correctly signed (i.e. the function returns NO_ERROR).
SignerVersion - if supplied, this parameter points to a character buffer of
at least MAX_PATH characters that receives the the signer version as
returned in the szwVerion field of the DRIVER_VER_INFO structure in
our call to WinVerifyTrust.
hWVTStateData - if supplied, this parameter points to a buffer that
receives a handle to WinVerifyTrust state data. This handle will be
returned only when validation was successfully performed using
Authenticode policy. This handle may be used, for example, to retrieve
signer info when prompting the user about whether they trust the
publisher. (The status code returned will indicate whether or not this
is necessary, see "Return Value" section below.)
This parameter should only be supplied if the
VERIFY_FILE_USE_AUTHENTICODE_CATALOG bit is set in the supplied Flags.
If the routine fails, then this handle will be set to NULL.
It is the caller's responsibility to close this handle when they're
finished with it by calling pSetupCloseWVTStateData().
Return Value:
If the file was successfully validated via driver signing policy (or we
didn't perform digital signature verification and everything else
succeeded), then the return value is NO_ERROR.
If the file was successfully validated via Authenticode policy, and the
publisher was in the TrustedPublisher store, then the return value is
ERROR_AUTHENTICODE_TRUSTED_PUBLISHER.
If the file was successfully validated via Authenticode policy, and the
publisher was not in the TrustedPublisher store (hence we must prompt the
user to establish their trust of the publisher), then the return value is
ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED
If a failure occurred, the return value is a Win32 error code indicating
the cause of the failure.
--*/
{
DWORD rc;
PCTSTR AltCatalogFile;
LPCTSTR InfFullPath;
MYASSERT(!QueueNode || Queue);
//
// Initialize the output handle for WinVerifyTrust state data.
//
if(hWVTStateData) {
*hWVTStateData = NULL;
}
//
// If the caller requested that we return WinVerifyTrust state data upon
// successful Authenticode validation, then they'd better have actually
// requested Authenticode validation!
//
MYASSERT(!hWVTStateData || (Flags & VERIFY_FILE_USE_AUTHENTICODE_CATALOG));
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
//
// Nobody had better be calling this expecting to get back any info
// about a successful verification!
//
MYASSERT(!CatalogFileUsed);
MYASSERT(!DigitalSigner);
MYASSERT(!SignerVersion);
//
// Likewise, we'd better not be called asking to validate using
// Authenticode policy (and hence, to return WinVerifyTrust state data
// regarding the signing certificate).
//
MYASSERT(!(Flags & VERIFY_FILE_USE_AUTHENTICODE_CATALOG));
return NO_ERROR;
}
//
// We only support a subset of flags when validating via Authenticode
// policy. Make sure no illegal flags are set.
//
MYASSERT(!(Flags & VERIFY_FILE_USE_AUTHENTICODE_CATALOG) ||
!(Flags & (VERIFY_FILE_USE_OEM_CATALOGS | VERIFY_FILE_DRIVERBLOCKED_ONLY)));
//
// If we know the file's destination (i.e., we have a QueueNode), and the
// INF is headed for %windir%\Inf, we want to catch and disallow that right
// up front. We don't do this for exception packages, since it's assumed
// (whether correctly or incorrectly) that they're "part of the OS", and as
// such, they know what they're doing.
//
// We also unfortunately can't do this for Authenticode-signed packages,
// because there are some IExpress packages out there that copy the INF to
// the INF directory, and use an Authenticode signature. If we treat this
// as a signature failure, then this will cause us to bail out of queue
// committal. Since our Authenticode logic is meant to prevent spoofing/
// tampering (and that's not the issue in this case), we just have to keep
// quiet about this. :-(
//
if(!(Flags & VERIFY_FILE_USE_AUTHENTICODE_CATALOG) &&
(QueueNode && !(Queue->Flags & FQF_KEEP_INF_AND_CAT_ORIGINAL_NAMES))) {
TCHAR TargetPath[MAX_PATH];
LPCTSTR TargetFilename, p;
//
// Is the target file an INF?
//
TargetFilename = pSetupStringTableStringFromId(Queue->StringTable,
QueueNode->TargetFilename
);
p = _tcsrchr(TargetFilename, TEXT('.'));
if(p && !_tcsicmp(p, pszInfSuffix)) {
//
// It's an INF. Construct the full target path to see where it's
// going.
//
StringCchCopy(
TargetPath,
SIZECHARS(TargetPath),
pSetupStringTableStringFromId(Queue->StringTable, QueueNode->TargetDirectory)
);
pSetupConcatenatePaths(TargetPath,
TargetFilename,
SIZECHARS(TargetPath),
NULL
);
if(!pSetupInfIsFromOemLocation(TargetPath, TRUE)) {
//
// It is invalid to copy an INF into %windir%\Inf via a file
// queue. Report this file as unsigned...
//
*Problem = SetupapiVerifyIncorrectlyCopiedInf;
StringCchCopy(ProblemFile, MAX_PATH, FileToVerifyFullPath);
return ERROR_INCORRECTLY_COPIED_INF;
}
}
}
//
// Check to see if the source file is signed.
//
if(QueueNode && QueueNode->CatalogInfo) {
//
// We should never have the IQF_FROM_BAD_OEM_INF internal flag set in
// this case.
//
MYASSERT(!(QueueNode->InternalFlags & IQF_FROM_BAD_OEM_INF));
if(*(QueueNode->CatalogInfo->CatalogFilenameOnSystem)) {
//
// The fact that our catalog info node has a filename filled in
// means we successfully verified this catalog previously. Now we
// simply need to verify the temporary (source) file against the
// catalog.
//
//
// If our catalog is an Authenticode catalog, we don't want to use
// WinVerifyTrust to validate using driver signing policy. That's
// because it will return the same error we get when we have an
// osattribute mismatch, and this will confuse this routine's
// callers.
//
if(!(Flags & VERIFY_FILE_USE_AUTHENTICODE_CATALOG) &&
(QueueNode->CatalogInfo->Flags &
(CATINFO_FLAG_AUTHENTICODE_SIGNED | CATINFO_FLAG_PROMPT_FOR_TRUST))) {
*Problem = SetupapiVerifyFileProblem;
StringCchCopy(ProblemFile, MAX_PATH, FileToVerifyFullPath);
return ERROR_ONLY_VALIDATE_VIA_AUTHENTICODE;
} else {
//
// The caller shouldn't have requested verification using
// Authenticode policy unless they already knew the catalog was
// Authenticode signed. We'll do the right thing regardless, but
// this is a sanity check.
//
MYASSERT(!(Flags & VERIFY_FILE_USE_AUTHENTICODE_CATALOG) ||
(QueueNode->CatalogInfo->Flags &
(CATINFO_FLAG_AUTHENTICODE_SIGNED | CATINFO_FLAG_PROMPT_FOR_TRUST))
);
rc = _VerifyFile(LogContext,
&(Queue->VerifyContext),
QueueNode->CatalogInfo->CatalogFilenameOnSystem,
NULL,
0,
Key,
FileToVerifyFullPath,
Problem,
ProblemFile,
TRUE,
AltPlatformInfo,
Flags,
CatalogFileUsed,
NULL,
DigitalSigner,
SignerVersion,
hWVTStateData
);
}
} else {
//
// The INF didn't specify a Catalog= entry. This indicates we
// should perform global validation, except when we're doing
// Authenticode-based verification (which must be performed in
// the context of a specific catalog explicitly identified by the
// INF).
//
if(Flags & VERIFY_FILE_USE_AUTHENTICODE_CATALOG) {
if(QueueNode->CatalogInfo->VerificationFailureError != NO_ERROR) {
rc = QueueNode->CatalogInfo->VerificationFailureError;
} else {
rc = ERROR_NO_AUTHENTICODE_CATALOG;
}
if((rc == ERROR_NO_CATALOG_FOR_OEM_INF) ||
(rc == ERROR_NO_AUTHENTICODE_CATALOG)) {
//
// The failure is the INF's fault (it's an OEM INF that
// copies files without specifying a catalog). Blame
// the INF, not the file being copied.
//
*Problem = SetupapiVerifyInfProblem;
MYASSERT(QueueNode->CatalogInfo->InfFullPath != -1);
InfFullPath = pSetupStringTableStringFromId(
Queue->StringTable,
QueueNode->CatalogInfo->InfFullPath
);
MYASSERT(InfFullPath);
StringCchCopy(ProblemFile, MAX_PATH, InfFullPath);
} else {
//
// We previously failed to validate the catalog file
// associated with this queue node.
//
*Problem = SetupapiVerifyFileNotSigned;
//
// If the caller didn't supply us with an original
// source filepath (which will be taken care of later),
// go ahead and copy the path of the file that was to
// be verified.
//
if(!OriginalSourceFileFullPath) {
StringCchCopy(ProblemFile, MAX_PATH, FileToVerifyFullPath);
}
}
} else {
//
// If there's no error associated with this catalog info node,
// then we simply need to do global validation. If there is an
// error then we still need to check if the driver is in the
// bad driver database.
//
// If the queue has an alternate default catalog file
// associated with it, then retrieve that catalog's name for
// use later.
//
AltCatalogFile = (Queue->AltCatalogFile != -1)
? pSetupStringTableStringFromId(Queue->StringTable, Queue->AltCatalogFile)
: NULL;
rc = _VerifyFile(LogContext,
&(Queue->VerifyContext),
AltCatalogFile,
NULL,
0,
Key,
FileToVerifyFullPath,
Problem,
ProblemFile,
FALSE,
AltPlatformInfo,
Flags |
((QueueNode->CatalogInfo->VerificationFailureError == NO_ERROR)
? 0
: VERIFY_FILE_DRIVERBLOCKED_ONLY),
CatalogFileUsed,
NULL,
DigitalSigner,
SignerVersion,
NULL
);
if((rc == NO_ERROR) &&
(QueueNode->CatalogInfo->VerificationFailureError != NO_ERROR)) {
//
// If there is an error associated with this catalog info
// node and the file was not in the bad driver database
// then return the error.
//
rc = QueueNode->CatalogInfo->VerificationFailureError;
if(rc == ERROR_NO_CATALOG_FOR_OEM_INF) {
//
// The failure is the INF's fault (it's an OEM INF that
// copies files without specifying a catalog). Blame
// the INF, not the file being copied.
//
*Problem = SetupapiVerifyInfProblem;
MYASSERT(QueueNode->CatalogInfo->InfFullPath != -1);
InfFullPath = pSetupStringTableStringFromId(
Queue->StringTable,
QueueNode->CatalogInfo->InfFullPath
);
StringCchCopy(ProblemFile, MAX_PATH, InfFullPath);
} else {
//
// We previously failed to validate the catalog file
// associated with this queue node.
//
*Problem = SetupapiVerifyFileNotSigned;
//
// If the caller didn't supply us with an original
// source filepath (which will be taken care of later),
// go ahead and copy the path of the file that was to
// be verified.
//
if(!OriginalSourceFileFullPath) {
StringCchCopy(ProblemFile, MAX_PATH, FileToVerifyFullPath);
}
}
}
}
}
} else {
//
// We have no queue, or we couldn't associate this source file back
// to a catalog info node that tells us exactly which catalog to use
// for verification. Thus, we'll have to settle for global validation.
// (Except in the Authenticode case, where global validation isn't
// allowed.)
//
if(Flags & VERIFY_FILE_USE_AUTHENTICODE_CATALOG) {
rc = ERROR_NO_AUTHENTICODE_CATALOG;
//
// In this case, we have to blame the file, because we don't have
// an INF to blame.
//
*Problem = SetupapiVerifyFileNotSigned;
//
// If the caller didn't supply us with an original source filepath
// (which will be taken care of later), go ahead and copy the path
// of the file that was to be verified.
//
if(!OriginalSourceFileFullPath) {
StringCchCopy(ProblemFile, MAX_PATH, FileToVerifyFullPath);
}
} else {
BOOL InfIsBad = FALSE;
rc = NO_ERROR;
if(Queue) {
if(Queue->AltCatalogFile == -1) {
if(QueueNode && (QueueNode->InternalFlags & IQF_FROM_BAD_OEM_INF)) {
InfIsBad = TRUE;
}
AltCatalogFile = NULL;
} else {
//
// We have an alternate catalog file to use instead of global
// validation.
//
AltCatalogFile = pSetupStringTableStringFromId(Queue->StringTable, Queue->AltCatalogFile);
}
} else {
AltCatalogFile = NULL;
}
rc = _VerifyFile(LogContext,
Queue ? &(Queue->VerifyContext) : NULL,
AltCatalogFile,
NULL,
0,
Key,
FileToVerifyFullPath,
Problem,
ProblemFile,
FALSE,
AltPlatformInfo,
Flags |
(InfIsBad ? VERIFY_FILE_DRIVERBLOCKED_ONLY : 0),
CatalogFileUsed,
NULL,
DigitalSigner,
SignerVersion,
NULL
);
if(rc == NO_ERROR) {
if(InfIsBad) {
//
// The driver file was not blocked, but the INF was bad so
// set the appropriate error and problem values.
//
rc = ERROR_NO_CATALOG_FOR_OEM_INF;
*Problem = SetupapiVerifyFileProblem;
StringCchCopy(ProblemFile, MAX_PATH, FileToVerifyFullPath);
}
}
}
}
//
// If the problem was with the file (as opposed to with the catalog), then
// use the real source name, if supplied, as opposed to the temporary
// filename we passed into _VerifyFile.
//
if((rc != NO_ERROR) &&
(rc != ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) &&
(rc != ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
if(OriginalSourceFileFullPath &&
((*Problem == SetupapiVerifyFileNotSigned) || (*Problem == SetupapiVerifyFileProblem))) {
StringCchCopy(ProblemFile, MAX_PATH, OriginalSourceFileFullPath);
}
}
return rc;
}
DWORD
VerifyDeviceInfFile(
IN PSETUP_LOG_CONTEXT LogContext,
IN OUT PVERIFY_CONTEXT VerifyContext, OPTIONAL
IN LPCTSTR CurrentInfName,
IN PLOADED_INF pInf,
IN PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo, OPTIONAL
OUT LPTSTR CatalogFileUsed, OPTIONAL
OUT LPTSTR DigitalSigner, OPTIONAL
OUT LPTSTR SignerVersion, OPTIONAL
IN DWORD Flags,
OUT HANDLE *hWVTStateData OPTIONAL
)
/*++
Routine Description:
This routine performs a digital signature verification on the specified
INF file.
Arguments:
LogContext - supplies the log context to be used in logging an error if
we encounter an error.
VerifyContext - optionally, supplies the address of a structure that caches
various verification context handles. These handles may be NULL (if
not previously acquired, and they may be filled in upon return (in
either success or failure) if they were acquired during the processing
of this verification request. It is the caller's responsibility to
free these various context handles when they are no longer needed by
calling pSetupFreeVerifyContextMembers.
CurrentInfName - supplies the full path to the INF to be validated
pInf - supplies a pointer to the LOADED_INF structure corresponding to this
INF.
AltPlatformInfo - optionally, supplies alternate platform information to
be used when validating this INF.
CatalogFileUsed - optionally, supplies a character buffer that must be at
least MAX_PATH characters in size. Upon successful return, this buffer
will be filled in with the catalog file used to validate the INF.
DigitalSigner - optionally, supplies a character buffer that must be at
least MAX_PATH characters in size. Upon successful return, this buffer
will be filled in with the name of the signer.
SignerVersion - optionally, supplies a character buffer that must be at
least MAX_PATH characters in size. Upon successful return, this buffer
will be filled in with the signer version information.
Flags - supplies flags that alter the behavior of this routine. May be a
combination of the following values:
VERIFY_INF_USE_AUTHENTICODE_CATALOG - Validate the file using a
catalog signed with Authenticode
policy. If this flag is set,
we'll _only_ check for
Authenticode signatures, so if
the caller wants to first try
validating a file for OS code-
signing usage, then falling back
to Authenticode, they'll have to
call this routine twice.
If this flag is set, then the
caller may also supply the
hWVTStateData output parameter,
which can be used to prompt user
in order to establish that the
publisher should be trusted.
VerifyDeviceInfFile will return
one of two error codes upon
successful validation via
Authenticode policy. Refer to
the "Return Value" section for
details.
hWVTStateData - if supplied, this parameter points to a buffer that
receives a handle to WinVerifyTrust state data. This handle will be
returned only when validation was successfully performed using
Authenticode policy. This handle may be used, for example, to retrieve
signer info when prompting the user about whether they trust the
publisher. (The status code returned will indicate whether or not this
is necessary, see "Return Value" section below.)
This parameter should only be supplied if the
VERIFY_INF_USE_AUTHENTICODE_CATALOG bit is set in the supplied Flags.
If the routine fails, then this handle will be set to NULL.
It is the caller's responsibility to close this handle when they're
finished with it by calling pSetupCloseWVTStateData().
Return Value:
If the INF was successfully validated via driver signing policy, then the
return value is NO_ERROR.
If the INF was successfully validated via Authenticode policy, and the
publisher was in the TrustedPublisher store, then the return value is
ERROR_AUTHENTICODE_TRUSTED_PUBLISHER.
If the INF was successfully validated via Authenticode policy, and the
publisher was not in the TrustedPublisher store (hence we must prompt the
user to establish their trust of the publisher), then the return value is
ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED
If a failure occurred, the return value is a Win32 error code indicating
the cause of the failure.
--*/
{
BOOL DifferentOriginalName;
TCHAR OriginalCatalogName[MAX_PATH];
TCHAR CatalogPath[MAX_PATH];
TCHAR OriginalInfFileName[MAX_PATH];
PTSTR p;
DWORD Err;
PSP_ALTPLATFORM_INFO_V2 ValidationPlatform;
DWORD VerificationPolicyToUse;
//
// Initialize the output handle for WinVerifyTrust state data.
//
if(hWVTStateData) {
*hWVTStateData = NULL;
}
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
//
// Nobody had better be calling this expecting to get back any info
// about a successful verification!
//
MYASSERT(!CatalogFileUsed);
MYASSERT(!DigitalSigner);
MYASSERT(!SignerVersion);
//
// Likewise, we'd better not be called asking to validate using
// Authenticode policy (and hence, to return WinVerifyTrust state data
// regarding the signing certificate).
//
MYASSERT(!(Flags & VERIFY_INF_USE_AUTHENTICODE_CATALOG));
return NO_ERROR;
}
//
// If the caller requested that we return WinVerifyTrust state data upon
// successful Authenticode validation, then they'd better have actually
// requested Authenticode validation!
//
MYASSERT(!hWVTStateData || (Flags & VERIFY_INF_USE_AUTHENTICODE_CATALOG));
if(GlobalSetupFlags & PSPGF_AUTOFAIL_VERIFIES) {
return TRUST_E_FAIL;
}
CatalogPath[0] = TEXT('\0');
Err = pGetInfOriginalNameAndCatalogFile(pInf,
NULL,
&DifferentOriginalName,
OriginalInfFileName,
SIZECHARS(OriginalInfFileName),
OriginalCatalogName,
SIZECHARS(OriginalCatalogName),
AltPlatformInfo
);
if(Err != NO_ERROR) {
return Err;
}
if(pSetupInfIsFromOemLocation(CurrentInfName, TRUE)) {
//
// INF isn't in %windir%\Inf (i.e., it's 3rd-party), so it had better
// specify a catalog file...
//
if(!*OriginalCatalogName) {
return ERROR_NO_CATALOG_FOR_OEM_INF;
}
//
// ...and the CAT must reside in the same directory as the INF.
//
if(FAILED(StringCchCopy(CatalogPath, SIZECHARS(CatalogPath), CurrentInfName))) {
return ERROR_PATH_NOT_FOUND;
}
p = (PTSTR)pSetupGetFileTitle(CatalogPath);
if(FAILED(StringCchCopy(p, SIZECHARS(CatalogPath)-(p-CatalogPath), OriginalCatalogName))) {
return ERROR_PATH_NOT_FOUND;
}
} else {
//
// The INF lives in %windir%\Inf.
// If it is a 3rd party INF then we want to set the CatalogPath to
// the current INF name with .CAT at the end instead of .INF (e.g
// oem1.cat). If this is not an OEM catalog then we won't set the
// CatalogPath so we can search all of the catalogs in the system.
//
// We will assume that if the INF had a different original name.
//
if(DifferentOriginalName) {
p = (PTSTR)pSetupGetFileTitle(CurrentInfName);
if(FAILED(StringCchCopy(CatalogPath, SIZECHARS(CatalogPath), p))) {
return ERROR_PATH_NOT_FOUND;
}
p = _tcsrchr(CatalogPath, TEXT('.'));
if(!p) {
p = CatalogPath+lstrlen(CatalogPath);
}
if(FAILED(StringCchCopy(p,SIZECHARS(CatalogPath)-(p-CatalogPath),pszCatSuffix))) {
return ERROR_PATH_NOT_FOUND;
}
}
}
if(DifferentOriginalName) {
MYASSERT(*OriginalInfFileName);
} else {
//
// INF's current name is the same as its original name, so store the
// simple filename (sans path) for use as the validation key in the
// upcoming call to _VerifyFile.
//
StringCchCopy(OriginalInfFileName, SIZECHARS(OriginalInfFileName),pSetupGetFileTitle(CurrentInfName));
}
//
// If the caller didn't supply alternate platform information, we need to
// check and see whether a range of OSATTR versions should be considered
// valid for this INF's class.
//
if(!AltPlatformInfo) {
IsInfForDeviceInstall(LogContext,
NULL,
pInf,
NULL,
&ValidationPlatform,
&VerificationPolicyToUse,
NULL,
FALSE
);
} else {
ValidationPlatform = NULL;
//
// We still need to find out what verification policy is in effect, in
// order to determine whether we can use Authenticode policy.
//
IsInfForDeviceInstall(LogContext,
NULL,
pInf,
NULL,
NULL,
&VerificationPolicyToUse,
NULL,
FALSE
);
}
try {
if(Flags & VERIFY_INF_USE_AUTHENTICODE_CATALOG) {
if(!(VerificationPolicyToUse & DRIVERSIGN_ALLOW_AUTHENTICODE)) {
//
// Authenticode policy can't be used for this INF!
//
Err = ERROR_AUTHENTICODE_DISALLOWED;
leave;
}
if(!*CatalogPath) {
//
// Authenticode-signed INFs must expicitly reference a catalog.
//
Err = ERROR_NO_AUTHENTICODE_CATALOG;
leave;
}
Err = _VerifyFile(LogContext,
VerifyContext,
CatalogPath,
NULL,
0,
OriginalInfFileName,
CurrentInfName,
NULL,
NULL,
FALSE,
(AltPlatformInfo ? AltPlatformInfo : ValidationPlatform),
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK
| VERIFY_FILE_USE_AUTHENTICODE_CATALOG),
CatalogFileUsed,
NULL,
DigitalSigner,
SignerVersion,
hWVTStateData
);
} else {
//
// Perform standard drivers signing verification.
//
Err = _VerifyFile(LogContext,
VerifyContext,
(*CatalogPath ? CatalogPath : NULL),
NULL,
0,
OriginalInfFileName,
CurrentInfName,
NULL,
NULL,
FALSE,
(AltPlatformInfo ? AltPlatformInfo : ValidationPlatform),
(VERIFY_FILE_IGNORE_SELFSIGNED
| VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
CatalogFileUsed,
NULL,
DigitalSigner,
SignerVersion,
NULL
);
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
}
if(ValidationPlatform) {
MyFree(ValidationPlatform);
}
return Err;
}
BOOL
IsFileProtected(
IN LPCTSTR FileFullPath,
IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
OUT PHANDLE phSfp OPTIONAL
)
/*++
Routine Description:
This routine determines whether the specified file is a protected system
file.
Arguments:
FileFullPath - supplies the full path to the file of interest
LogContext - supplies the log context to be used in logging an error if
we're unable to open an SFC handle.
phSfp - optionally, supplies the address of a handle variable that will be
filled in with a handle to the SFC server. This will only be supplied
when the routine returns TRUE (i.e., the file is SFP-protected).
Return Value:
If the file is protected, the return value is TRUE, otherwise it is FALSE.
--*/
{
BOOL ret;
HANDLE hSfp;
hSfp = SfcConnectToServer(NULL);
if(!hSfp) {
//
// This ain't good...
//
WriteLogEntry(LogContext,
SETUP_LOG_ERROR,
MSG_LOG_SFC_CONNECT_FAILED,
NULL
);
return FALSE;
}
try {
ret = SfcIsFileProtected(hSfp, FileFullPath);
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
ret = FALSE;
}
//
// If the file _is_ protected, and the caller wants the SFP handle (e.g.,
// to subsequently exempt an unsigned replacement operation), then save
// the handle in the caller-supplied buffer. Otherwise, close the handle.
//
if(ret && phSfp) {
*phSfp = hSfp;
} else {
SfcClose(hSfp);
}
return ret;
}
PSTR
GetAnsiMuiSafePathname(
IN PCTSTR FilePath
)
/*++
Routine Description:
Remove filename portion of FilePath
and convert rest of path to be MUI parse safe
Note that the returned pathname is such that the FileName can be cat'd
so for "E:\i386\myfile.dl_" FilePath = "E:\i386\" and
FileName = "myfile.dl_"
*This is required* (it also happens to make this code easier)
Arguments:
FilePath - path+filename to convert
Return Value:
If successful, the return value is pointer to ANSI filepath (memory
allocated by pSetupMalloc)
If unsuccessful, the return value is NULL and GetLastError returns error
--*/
{
TCHAR Buffer[MAX_PATH];
LPTSTR FilePart;
DWORD actsz;
actsz = GetFullPathName(FilePath,MAX_PATH,Buffer,&FilePart);
if(actsz == 0) {
//
// GetLastError has error
//
return NULL;
}
if(actsz >= MAX_PATH) {
//
// can't do anything with this path
//
SetLastError(ERROR_INVALID_DATA);
return NULL;
}
if(!FilePart) {
//
// Since GetFullPathName couldn't find the beginning of the filename,
// assume this means we were handed a simple filename (sans path).
//
*Buffer = TEXT('\0');
} else {
//
// Strip the filename from the path.
//
*FilePart = TEXT('\0');
}
return GetAnsiMuiSafeFilename(Buffer);
}
PSTR
GetAnsiMuiSafeFilename(
IN PCTSTR FilePath
)
/*++
Routine Description:
Convert FilePath, which is a native file path to one that is safe to parse
by ansi API's in an MUI environment.
returned pointer is allocated and should be free'd
Arguments:
FilePath - path to convert (may be an empty string)
Return Value:
If successful, the return value is pointer to ANSI filepath (memory
allocated by pSetupMalloc)
If unsuccessful, the return value is NULL and GetLastError returns error
--*/
{
TCHAR Buffer[MAX_PATH];
PTSTR p;
PSTR ansiPath;
DWORD actsz;
DWORD err;
//
// NTRAID#NTBUG9-644041-2002/04/12-lonnym -- logic fails if short pathname support is turned off
//
actsz = GetShortPathName(FilePath,Buffer,MAX_PATH);
if(actsz >= MAX_PATH) {
//
// file path too big
//
SetLastError(ERROR_INVALID_DATA);
return NULL;
}
if(!actsz) {
//
// some other error - resort back the current path name
//
if(FAILED(StringCchCopy(Buffer,MAX_PATH,FilePath))) {
SetLastError(ERROR_INVALID_DATA);
return NULL;
}
}
//
// convert to ansi now we've (if we can) converted to short path name
//
ansiPath = pSetupUnicodeToAnsi(Buffer);
if(!ansiPath) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
return ansiPath;
}
BOOL
pSetupAppendPath(
IN PCTSTR Path1, OPTIONAL
IN PCTSTR Path2, OPTIONAL
OUT PTSTR *Combined
)
/*++
Routine Description:
Call pSetupConcatenatePaths dynamically modifying memory/pointer
Arguments:
Path1/Path2 - Optionally, supplies paths to concatenate
Combined - resultant path (must be freed via MyFree)
Return Value:
TRUE if we successfully concatenated the paths into a newly-allocated
buffer.
FALSE otherwise.
Notes:
If both Path1 and Path2 are NULL, we will return a buffer containing a
single NULL character (i.e., an empty string).
--*/
{
PTSTR FinalPath;
UINT Len;
BOOL b;
if(!Path1 && !Path2) {
*Combined = MyMalloc(sizeof(TCHAR));
if(*Combined) {
**Combined = TEXT('\0');
return TRUE;
} else {
return FALSE;
}
}
if(!Path1) {
*Combined = DuplicateString(Path2);
return *Combined ? TRUE : FALSE;
}
if(!Path2) {
*Combined = DuplicateString(Path1);
return *Combined ? TRUE : FALSE;
}
Len = lstrlen(Path1)+lstrlen(Path2)+2; // slash and null
FinalPath = MyMalloc(Len*sizeof(TCHAR));
if(!FinalPath) {
*Combined = NULL;
return FALSE;
}
try {
StringCchCopy(FinalPath, Len, Path1);
b = pSetupConcatenatePaths(FinalPath, Path2, Len, NULL);
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
b = FALSE;
}
if(!b) {
MyFree(FinalPath);
FinalPath = NULL;
}
*Combined = FinalPath;
return b;
}
BOOL
pSetupApplyExtension(
IN PCTSTR Original,
IN PCTSTR Extension, OPTIONAL
OUT PTSTR* NewName
)
/*++
Routine Description:
Apply Extension onto Original to obtain NewName
Arguments:
Original - original filename (may include path) with old extension
Extension - new extension to apply (with or without dot), if NULL, deletes
NewName - allocated buffer containing new filename (must be freed via
MyFree)
Return Value:
TRUE if modified filename was successfully returned in newly-allocated
buffer.
FALSE otherwise.
--*/
{
PCTSTR End = Original+lstrlen(Original);
PCTSTR OldExt = End;
PTSTR NewString = NULL;
TCHAR c;
UINT len;
UINT sublen;
if(Extension && (*Extension == TEXT('.'))) {
Extension++;
}
while(End != Original) {
End = CharPrev(Original, End);
if((*End == TEXT('/')) || (*End == TEXT('\\'))) {
break;
}
if(*End == TEXT('.')) {
OldExt = End;
break;
}
}
sublen = (UINT)(OldExt-Original);
len = sublen + (Extension ? lstrlen(Extension) : 0) + 2;
NewString = MyMalloc(len*sizeof(TCHAR));
if(!NewString) {
*NewName = NULL;
return FALSE;
}
try {
CopyMemory(NewString, Original, sublen * sizeof(TCHAR));
NewString[sublen++] = Extension ? TEXT('.') : TEXT('\0');
if(Extension) {
MYVERIFY(SUCCEEDED(StringCchCopy(NewString + sublen,
len - sublen,
Extension))
);
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
MyFree(NewString);
NewString = NULL;
}
*NewName = NewString;
return (NewString != NULL);
}
BOOL
ClassGuidInDrvSignPolicyList(
IN PSETUP_LOG_CONTEXT OptLogContext, OPTIONAL
IN CONST GUID *DeviceSetupClassGuid,
OUT PSP_ALTPLATFORM_INFO_V2 *ValidationPlatform OPTIONAL
)
/*++
Routine Description:
This routine determines whether the specified device setup class is among
the list of classes for which driver signing policy is applicable (i.e., as
indicated by the class's inclusion in the [DriverSigningClasses] section of
%windir%\Inf\certclas.inf). Additionally, if an non-native signature
validation lower-bound is applicable, a newly-allocated alternate platform
info structure is returned to the caller (if requested) to be used in
subsequent validation attempts associated with this class.
Arguments:
LogContext - Optionally, supplies the log context for any log entries that
might be generated by this routine.
DeviceSetupClassGuid - Supplies the address of the GUID we're attempting to
find in our driver signing policy list.
ValidationPlatform - Optionally, supplies the address of a (version 2)
altplatform info pointer (initialized to NULL) that is filled in upon
return with a newly-allocated structure specifying the appropriate
parameters to be passed to WinVerifyTrust when validating this INF.
These parameters are retrieved from certclas.inf for the relevant
device setup class GUID. If no special parameters are specified for
this class (or if the INF has no class at all), then this pointer is
not modified (i.e., left as NULL) causing us to use WinVerifyTrust's
default validation. Note that if we fail to allocate this structure
due to low-memory, the pointer will be left as NULL in that case as
well. This is OK, because this simply means we'll do default
validation in that case.
The caller is responsible for freeing the memory allocated for this
structure.
Return Value:
If the device setup class is in our driver signing policy list, the return
value is non-zero (TRUE). Otherwise, it is FALSE.
--*/
{
DWORD Err;
BOOL UseDrvSignPolicy;
INT i;
TCHAR CertClassInfPath[MAX_PATH];
HINF hCertClassInf = INVALID_HANDLE_VALUE;
INFCONTEXT InfContext;
UINT ErrorLine;
LONG LineCount;
PCTSTR GuidString;
PSETUP_LOG_CONTEXT LogContext = NULL;
//
// Default is to lump all device installs under driver signing policy
//
UseDrvSignPolicy = TRUE;
//
// If the caller supplied the ValidationPlatform parameter it must be
// pointing to a NULL pointer...
//
MYASSERT(!ValidationPlatform || !*ValidationPlatform);
if(!LockDrvSignPolicyList(&GlobalDrvSignPolicyList)) {
return UseDrvSignPolicy;
}
try {
InheritLogContext(OptLogContext, &LogContext); // want to group logging
if(GlobalDrvSignPolicyList.NumMembers == -1) {
//
// We've not yet retrieved the list from certclas.inf. First,
// verify the INF to make sure no one has tampered with it...
//
LPTSTR strp = CertClassInfPath;
size_t strl = SIZECHARS(CertClassInfPath);
if(FAILED(StringCchCopyEx(strp,strl,InfDirectory,&strp,&strl,0)) ||
FAILED(StringCchCopy(strp,strl,TEXT("\\certclas.inf")))) {
Err = ERROR_PATH_NOT_FOUND;
} else {
Err = _VerifyFile(LogContext,
NULL,
NULL,
NULL,
0,
pSetupGetFileTitle(CertClassInfPath),
CertClassInfPath,
NULL,
NULL,
FALSE,
NULL,
(VERIFY_FILE_IGNORE_SELFSIGNED | VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
NULL,
NULL,
NULL,
NULL,
NULL
);
}
if(Err == NO_ERROR) {
//
// Open up driver signing class list INF for use when examining
// the individual INFs in the LOADED_INF list below.
//
Err = GLE_FN_CALL(INVALID_HANDLE_VALUE,
hCertClassInf = SetupOpenInfFile(
CertClassInfPath,
NULL,
INF_STYLE_WIN4,
&ErrorLine)
);
if(Err != NO_ERROR) {
//
// This failure is highly unlikely to occur, since we just
// got through validating the INF.
//
WriteLogEntry(LogContext,
SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
MSG_LOG_CERTCLASS_LOAD_FAILED,
NULL,
CertClassInfPath,
ErrorLine
);
}
} else {
WriteLogEntry(LogContext,
SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
MSG_LOG_CERTCLASS_INVALID,
NULL,
CertClassInfPath
);
}
if(Err != NO_ERROR) {
//
// Somebody mucked with/deleted certclas.inf! (Or, much less
// likely, we encountered some other failure whilst trying to
// load the INF.) Since we don't know which classes are
// subject to driver signing policy, we assume they all are.
//
WriteLogError(LogContext,
SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
Err
);
WriteLogEntry(LogContext,
SETUP_LOG_WARNING,
MSG_LOG_DRIVER_SIGNING_FOR_ALL_CLASSES,
NULL
);
//
// Set the NumMembers field to zero, so we'll know we
// previously attempted (and failed) to retrieve the list. We
// do this so we don't keep re-trying to get this list.
//
GlobalDrvSignPolicyList.NumMembers = 0;
leave;
}
//
// Certclas.inf validated, and we successfully opened it. Now
// retrieve the list contained therein.
//
LineCount = SetupGetLineCount(hCertClassInf,
pszDriverSigningClasses
);
MYASSERT(LineCount > 0);
if((LineCount <= 0) ||
(NULL == (GlobalDrvSignPolicyList.Members = MyMalloc(LineCount * sizeof(DRVSIGN_CLASS_LIST_NODE))))) {
leave;
}
if(!SetupFindFirstLine(hCertClassInf,
pszDriverSigningClasses,
NULL,
&InfContext)) {
MYASSERT(FALSE);
leave;
}
i = 0;
do {
MYASSERT(i < LineCount);
//
// The format of a line in the [DriverSigningClasses]
// section is as follows:
//
// {GUID} [= FirstValidatedMajorVersion, FirstValidatedMinorVersion]
//
GuidString = pSetupGetField(&InfContext, 0);
if(GuidString &&
(NO_ERROR == pSetupGuidFromString(GuidString, &(GlobalDrvSignPolicyList.Members[i].DeviceSetupClassGuid)))) {
if(SetupGetIntField(&InfContext, 1, &(GlobalDrvSignPolicyList.Members[i].MajorVerLB)) &&
SetupGetIntField(&InfContext, 2, &(GlobalDrvSignPolicyList.Members[i].MinorVerLB))) {
//
// We successfully retrieved major/minor
// version info for validation lower-bound.
// Do a sanity-check on these.
//
if(GlobalDrvSignPolicyList.Members[i].MajorVerLB <= 0) {
GlobalDrvSignPolicyList.Members[i].MajorVerLB = -1;
GlobalDrvSignPolicyList.Members[i].MinorVerLB = -1;
}
} else {
//
// Set major/minor version info to -1 to
// indicate there's no validation platform
// override.
//
GlobalDrvSignPolicyList.Members[i].MajorVerLB = -1;
GlobalDrvSignPolicyList.Members[i].MinorVerLB = -1;
}
i++;
}
} while(SetupFindNextLine(&InfContext, &InfContext));
//
// Update NumMembers field in our list to indicate the
// number of class GUID entries we actually found.
//
GlobalDrvSignPolicyList.NumMembers = i;
}
//
// We now have a list. If the list is empty, this means we
// encountered some problem retrieving the list, thus all device
// classes should be subject to driver signing policy. Otherwise,
// try to find the caller-specified class in our list.
//
if(GlobalDrvSignPolicyList.NumMembers) {
//
// OK, we know we have a valid list--now default to non-driver
// signing policy unless our list search proves fruitful.
//
UseDrvSignPolicy = FALSE;
for(i = 0; i < GlobalDrvSignPolicyList.NumMembers; i++) {
if(!memcmp(DeviceSetupClassGuid,
&(GlobalDrvSignPolicyList.Members[i].DeviceSetupClassGuid),
sizeof(GUID))) {
//
// We found a match!
//
UseDrvSignPolicy = TRUE;
//
// Now, check to see if we have any validation platform
// override info...
//
if(ValidationPlatform &&
(GlobalDrvSignPolicyList.Members[i].MajorVerLB != -1)) {
MYASSERT(GlobalDrvSignPolicyList.Members[i].MinorVerLB != -1);
*ValidationPlatform = MyMalloc(sizeof(SP_ALTPLATFORM_INFO_V2));
//
// If the memory allocation fails, we just won't report
// the altplatform info, so the validation will be done
// based on the current OS version (instead of widening
// it up to allow a range of valid versions).
//
if(*ValidationPlatform) {
ZeroMemory(*ValidationPlatform, sizeof(SP_ALTPLATFORM_INFO_V2));
(*ValidationPlatform)->cbSize = sizeof(SP_ALTPLATFORM_INFO_V2);
(*ValidationPlatform)->Platform = VER_PLATFORM_WIN32_NT;
(*ValidationPlatform)->Flags = SP_ALTPLATFORM_FLAGS_VERSION_RANGE;
(*ValidationPlatform)->MajorVersion = VER_PRODUCTMAJORVERSION;
(*ValidationPlatform)->MinorVersion = VER_PRODUCTMINORVERSION;
(*ValidationPlatform)->ProcessorArchitecture =
#if defined(_X86_)
PROCESSOR_ARCHITECTURE_INTEL;
#elif defined(_IA64_)
PROCESSOR_ARCHITECTURE_IA64;
#elif defined(_AMD64_)
PROCESSOR_ARCHITECTURE_AMD64;
#else
#error "no target architecture"
#endif
(*ValidationPlatform)->FirstValidatedMajorVersion
= (DWORD)(GlobalDrvSignPolicyList.Members[i].MajorVerLB);
(*ValidationPlatform)->FirstValidatedMinorVersion
= (DWORD)(GlobalDrvSignPolicyList.Members[i].MinorVerLB);
}
}
//
// Since we've found a match, we can break out of the loop.
//
break;
}
}
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
//
// If we hit an exception, don't trust information in newly-allocated
// validation platform buffer (if any)
//
if(ValidationPlatform && *ValidationPlatform) {
MyFree(*ValidationPlatform);
*ValidationPlatform = NULL;
}
}
if(hCertClassInf != INVALID_HANDLE_VALUE) {
SetupCloseInfFile(hCertClassInf);
}
if(LogContext) {
DeleteLogContext(LogContext);
}
UnlockDrvSignPolicyList(&GlobalDrvSignPolicyList);
return UseDrvSignPolicy;
}
BOOL
InitDrvSignPolicyList(
VOID
)
/*++
Routine Description:
This routine initializes the global "Driver Signing Policy" list that is
retrieved (on first use) from %windir%\Inf\certclas.inf.
Arguments:
None
Return Value:
If success, the return value is TRUE, otherwise, it is FALSE.
--*/
{
ZeroMemory(&GlobalDrvSignPolicyList, sizeof(DRVSIGN_POLICY_LIST));
GlobalDrvSignPolicyList.NumMembers = -1;
return InitializeSynchronizedAccess(&GlobalDrvSignPolicyList.Lock);
}
VOID
DestroyDrvSignPolicyList(
VOID
)
/*++
Routine Description:
This routine destroys the global "Driver Signing Policy" list that is
retrieved (on first use) from %windir%\Inf\certclas.inf.
Arguments:
None
Return Value:
None
--*/
{
if(LockDrvSignPolicyList(&GlobalDrvSignPolicyList)) {
if(GlobalDrvSignPolicyList.Members) {
MyFree(GlobalDrvSignPolicyList.Members);
}
DestroySynchronizedAccess(&GlobalDrvSignPolicyList.Lock);
}
}
VOID
pSetupFreeVerifyContextMembers(
IN PVERIFY_CONTEXT VerifyContext
)
/*++
Routine Description:
This routine frees the various context handles contained in the specified
VerifyContext structure.
Arguments:
VerifyContext - supplies a pointer to a verification context structure
whose non-NULL members are to be freed.
Return Value:
None
--*/
{
//
// Release the crypto context (if there is one)
//
if(VerifyContext->hCatAdmin) {
CryptCATAdminReleaseContext(VerifyContext->hCatAdmin, 0);
}
//
// Release the handle to the bad driver database (if there is one)
//
if(VerifyContext->hSDBDrvMain) {
SdbReleaseDatabase(VerifyContext->hSDBDrvMain);
}
//
// Release the handle to the trusted publisher cert store (if there is
// one)
//
if(VerifyContext->hStoreTrustedPublisher) {
CertCloseStore(VerifyContext->hStoreTrustedPublisher, 0);
}
}
BOOL
pSetupIsAuthenticodePublisherTrusted(
IN PCCERT_CONTEXT CertContext,
IN OUT HCERTSTORE *hStoreTrustedPublisher OPTIONAL
)
/*++
Routine Description:
This routine checks to see whether the specified certificate is in the
"TrustedPublisher" certificate store.
Arguments:
CertContext - supplies the certificate context to look for in the
TrustedPublisher certificate store.
hStoreTrustedPublisher - optionally, supplies the address of a certificate
store handle. If the handle pointed to is NULL, a handle will be
acquired (if possible) via CertOpenStore and returned to the caller.
If the handle pointed to is non-NULL, then that handle will be used by
this routine. If the pointer itself is NULL, then an HCERTSTORE will
be acquired for the duration of this call, and released before
returning.
NOTE: it is the caller's responsibility to free the certificate store
handle returned by this routine by calling CertCloseStore. This handle
may be opened in either success or failure cases, so the caller must
check for non-NULL returned handle in both cases.
Return Value:
If the certificate was located in the "TrustedPublisher" certificate store,
the return value is non-zero (i.e., TRUE).
Otherwise, the return value is FALSE.
--*/
{
BYTE rgbHash[MAX_HASH_LEN];
CRYPT_DATA_BLOB HashBlob;
PCCERT_CONTEXT pFoundCert = NULL;
BOOL IsPublisherTrusted = FALSE;
HCERTSTORE LocalhStore = NULL;
try {
HashBlob.pbData = rgbHash;
HashBlob.cbData = sizeof(rgbHash);
if(!CertGetCertificateContextProperty(CertContext,
CERT_SIGNATURE_HASH_PROP_ID,
rgbHash,
&HashBlob.cbData)
|| (MIN_HASH_LEN > HashBlob.cbData))
{
leave;
}
//
// Check if trusted publisher
//
if(hStoreTrustedPublisher && *hStoreTrustedPublisher) {
LocalhStore = *hStoreTrustedPublisher;
} else {
LocalhStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM_W,
0,
(HCRYPTPROV)NULL,
(CERT_SYSTEM_STORE_CURRENT_USER |
CERT_STORE_MAXIMUM_ALLOWED_FLAG |
CERT_STORE_SHARE_CONTEXT_FLAG),
(const void *) L"TrustedPublisher"
);
if(!LocalhStore) {
leave;
}
//
// Try to setup for auto-resync, but it's not a critical failure if
// we can't do this...
//
CertControlStore(LocalhStore,
0,
CERT_STORE_CTRL_AUTO_RESYNC,
NULL
);
if(hStoreTrustedPublisher) {
*hStoreTrustedPublisher = LocalhStore;
}
}
pFoundCert = CertFindCertificateInStore(LocalhStore,
0,
0,
CERT_FIND_SIGNATURE_HASH,
(const void *) &HashBlob,
NULL
);
if(pFoundCert) {
IsPublisherTrusted = TRUE;
}
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, NULL);
}
if(pFoundCert) {
CertFreeCertificateContext(pFoundCert);
}
if(!hStoreTrustedPublisher && LocalhStore) {
CertCloseStore(LocalhStore, 0);
}
return IsPublisherTrusted;
}
BOOL
IsAutoCertInstallAllowed(
VOID
)
/*++
Routine Description:
This routine indicates whether a certificate should automatically be
installed. The criteria it uses are:
1. Must be in GUI-mode setup or mini-setup.
2. Must be on an interactive windowstation
3. Must be in LocalSystem security context.
(#2 & #3) are to prevent spoofing of registry values in #1.)
Arguments:
None.
Return Value:
If the certificate should be auto-installed, the return value is TRUE.
Otherwise, it is FALSE.
--*/
{
//
// Only auto-install certificates if we're in GUI-mode setup (or
// mini-setup)...
//
if(!GuiSetupInProgress) {
return FALSE;
}
//
// ...and if we're on an interactive windowstation...
//
if(!IsInteractiveWindowStation()) {
return FALSE;
}
//
// ...and if we're in LocalSystem security context.
//
if(!pSetupIsLocalSystem()) {
return FALSE;
}
return TRUE;
}
DWORD
pSetupInstallCertificate(
IN PCCERT_CONTEXT CertContext
)
/*++
Routine Description:
This routine will install the specified certificate into the
TrustedPublisher certificate store.
Arguments:
CertContext - supplies the context for the certificate to be installed.
Return Value:
If the certificate was successfully installed, the return value is
NO_ERROR.
Otherwise, it is a Win32 error indicating the cause of the failure.
--*/
{
DWORD Err;
HCERTSTORE hCertStore;
Err = GLE_FN_CALL(NULL,
hCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM_W,
0,
(HCRYPTPROV)NULL,
(CERT_SYSTEM_STORE_LOCAL_MACHINE
| CERT_STORE_OPEN_EXISTING_FLAG),
(const void *) L"TrustedPublisher")
);
if(Err != NO_ERROR) {
return Err;
}
try {
Err = GLE_FN_CALL(FALSE,
CertAddCertificateContextToStore(hCertStore,
CertContext,
CERT_STORE_ADD_USE_EXISTING,
NULL)
);
} except(pSetupExceptionFilter(GetExceptionCode())) {
pSetupExceptionHandler(GetExceptionCode(), ERROR_INVALID_PARAMETER, &Err);
}
if(hCertStore) {
CertCloseStore(hCertStore, 0);
}
return Err;
}