5578 lines
195 KiB
C
5578 lines
195 KiB
C
/*++
|
|
|
|
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;
|
|
|
|
//
|
|
// 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)
|
|
|
|
|
|
//
|
|
// Global list of device setup classes subject to driver signing policy, along
|
|
// 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
|
|
);
|
|
|
|
//
|
|
// helper to determine log level to use
|
|
//
|
|
__inline
|
|
DWORD
|
|
GetCatLogLevel(DWORD Err)
|
|
{
|
|
switch(Err) {
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
case E_NOTIMPL:
|
|
return SETUP_LOG_VVERBOSE;
|
|
|
|
default:
|
|
return 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.
|
|
#ifdef UNICODE
|
|
If the file is ascii, it is assumed to be ANSI format and is converted
|
|
to Unicode.
|
|
#else
|
|
If the file is unicode, it will be converted to ascii using the ANSI
|
|
codepage.
|
|
|
|
NOTE: On Windows 95, the IsTextUnicode API is not implemented, thus
|
|
the unicode file must have a byte order mark (BOM) in order for us to
|
|
recognize it under Win95.
|
|
#endif
|
|
|
|
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;
|
|
#ifdef UNICODE
|
|
UINT SysCodePage = CP_ACP;
|
|
#endif
|
|
//
|
|
// 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.
|
|
//
|
|
#ifdef UNICODE
|
|
IsNativeChar = TRUE;
|
|
#else
|
|
IsNativeChar = FALSE;
|
|
#endif // UNICODE
|
|
|
|
((PWCHAR)TextStartAddress)++;
|
|
FileSize -= sizeof(WCHAR);
|
|
} else {
|
|
|
|
IsNativeChar = IsTextUnicode(TextStartAddress,FileSize,NULL);
|
|
#ifndef UNICODE
|
|
IsNativeChar = !IsNativeChar;
|
|
#endif // !UNICODE
|
|
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
rc = ERROR_READ_FAULT;
|
|
}
|
|
|
|
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.
|
|
#ifdef UNICODE
|
|
// 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.
|
|
#else
|
|
// The maximum size of the ANSI text is the same number
|
|
// of bytes that the Unicode text occupies.
|
|
//
|
|
#endif // UNICODE
|
|
|
|
if(Buffer = MyMalloc(FileSize * sizeof(TCHAR))) {
|
|
try {
|
|
#ifdef UNICODE
|
|
//
|
|
// RAID#397463-1999/09/01-JamieHun Implement ANSI Inf Language=xxxx
|
|
// 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;
|
|
NativeCharCount = MultiByteToWideChar(SysCodePage,
|
|
MB_PRECOMPOSED,
|
|
TextStartAddress,
|
|
FileSize,
|
|
Buffer,
|
|
FileSize
|
|
);
|
|
#else
|
|
NativeCharCount = WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
TextStartAddress,
|
|
FileSize / sizeof(WCHAR),
|
|
Buffer,
|
|
FileSize,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif // UNICODE
|
|
if(!NativeCharCount) {
|
|
rc = GetLastError();
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
rc = ERROR_READ_FAULT;
|
|
}
|
|
} 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;
|
|
VS_FIXEDFILEINFO *FixedVersionInfo;
|
|
UINT DataLength;
|
|
BOOL b;
|
|
PWORD Translation;
|
|
DWORD Ignored;
|
|
|
|
//
|
|
// Assume failure
|
|
//
|
|
b = FALSE;
|
|
|
|
//
|
|
// Get the size of version info block.
|
|
//
|
|
if(d = GetFileVersionInfoSize((PTSTR)FileName,&Ignored)) {
|
|
//
|
|
// Allocate memory block of sufficient size to hold version info block
|
|
//
|
|
VersionBlock = MyMalloc(d*sizeof(TCHAR));
|
|
if(VersionBlock) {
|
|
|
|
//
|
|
// Get the version block from the file.
|
|
//
|
|
if(GetFileVersionInfo((PTSTR)FileName,0,d*sizeof(TCHAR),VersionBlock)) {
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
DWORD
|
|
GetSetFileTimestamp(
|
|
IN PCTSTR FileName,
|
|
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.
|
|
|
|
Return Value:
|
|
|
|
If successful, returns NO_ERROR, otherwise returns the Win32 error
|
|
indicating the cause of failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE h;
|
|
DWORD d;
|
|
BOOL b;
|
|
|
|
h = CreateFile(
|
|
FileName,
|
|
Set ? GENERIC_WRITE : GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if(h == INVALID_HANDLE_VALUE) {
|
|
return(GetLastError());
|
|
}
|
|
|
|
b = Set
|
|
? SetFileTime(h,CreateTime,AccessTime,WriteTime)
|
|
: GetFileTime(h,CreateTime,AccessTime,WriteTime);
|
|
|
|
d = b ? NO_ERROR : GetLastError();
|
|
|
|
CloseHandle(h);
|
|
|
|
return(d);
|
|
}
|
|
|
|
|
|
DWORD
|
|
RetreiveFileSecurity(
|
|
IN PCTSTR FileName,
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retreive 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.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b;
|
|
DWORD d;
|
|
DWORD BytesRequired;
|
|
PSECURITY_DESCRIPTOR p;
|
|
|
|
|
|
BytesRequired = 1024;
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Allocate a buffer of the required size.
|
|
//
|
|
p = MyMalloc(BytesRequired);
|
|
if(!p) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
//
|
|
// Get the security.
|
|
//
|
|
b = GetFileSecurity(
|
|
FileName,
|
|
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
|
p,
|
|
BytesRequired,
|
|
&BytesRequired
|
|
);
|
|
|
|
//
|
|
// Return with sucess
|
|
//
|
|
if(b) {
|
|
*SecurityDescriptor = p;
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
//
|
|
// Return an error code, unless we just need a bigger buffer
|
|
//
|
|
MyFree(p);
|
|
d = GetLastError();
|
|
if(d != ERROR_INSUFFICIENT_BUFFER) {
|
|
return (d);
|
|
}
|
|
|
|
//
|
|
// There's a bug in GetFileSecurity that can cause it to ask for a
|
|
// REALLY big buffer. In that case, we return an error.
|
|
//
|
|
if (BytesRequired > 0xF0000000) {
|
|
return (ERROR_INVALID_DATA);
|
|
}
|
|
|
|
//
|
|
// Otherwise, we'll try again with a bigger buffer
|
|
//
|
|
}
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b;
|
|
|
|
b = SetFileSecurity(
|
|
FileName,
|
|
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
|
SecurityInfo
|
|
);
|
|
|
|
return(b ? NO_ERROR : GetLastError());
|
|
}
|
|
|
|
|
|
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 name of the file of which to take ownership.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b;
|
|
SECURITY_DESCRIPTOR SecurityDescriptor;
|
|
DWORD Err;
|
|
HANDLE Token;
|
|
DWORD BytesRequired;
|
|
PTOKEN_OWNER OwnerInfo;
|
|
|
|
//
|
|
// Open the process token.
|
|
//
|
|
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&Token)) {
|
|
Err = GetLastError();
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Get the current process's default owner sid.
|
|
//
|
|
GetTokenInformation(Token,TokenOwner,NULL,0,&BytesRequired);
|
|
Err = GetLastError();
|
|
if(Err != ERROR_INSUFFICIENT_BUFFER) {
|
|
goto clean1;
|
|
}
|
|
|
|
OwnerInfo = MyMalloc(BytesRequired);
|
|
if(!OwnerInfo) {
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto clean1;
|
|
}
|
|
|
|
b = GetTokenInformation(Token,TokenOwner,OwnerInfo,BytesRequired,&BytesRequired);
|
|
if(!b) {
|
|
Err = GetLastError();
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// Initialize the security descriptor.
|
|
//
|
|
if(!InitializeSecurityDescriptor(&SecurityDescriptor,SECURITY_DESCRIPTOR_REVISION)
|
|
|| !SetSecurityDescriptorOwner(&SecurityDescriptor,OwnerInfo->Owner,FALSE)) {
|
|
|
|
Err = GetLastError();
|
|
goto clean2;
|
|
}
|
|
|
|
//
|
|
// Set file security.
|
|
//
|
|
Err = SetFileSecurity(Filename,OWNER_SECURITY_INFORMATION,&SecurityDescriptor)
|
|
? NO_ERROR
|
|
: GetLastError();
|
|
|
|
//
|
|
// Not all filesystems support this operation.
|
|
//
|
|
if(Err == ERROR_NOT_SUPPORTED) {
|
|
Err = NO_ERROR;
|
|
}
|
|
|
|
clean2:
|
|
MyFree(OwnerInfo);
|
|
clean1:
|
|
CloseHandle(Token);
|
|
clean0:
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Now look for the INF in each path in our MultiSz list.
|
|
//
|
|
InfPathLocation = NULL;
|
|
d = NO_ERROR;
|
|
|
|
for(PathPtr = PathList; *PathPtr; PathPtr += (lstrlen(PathPtr) + 1)) {
|
|
//
|
|
// Concatenate the INF file name with the current search path.
|
|
//
|
|
lstrcpy(CurInfPath, PathPtr);
|
|
pSetupConcatenatePaths(CurInfPath,
|
|
InfName,
|
|
SIZECHARS(CurInfPath),
|
|
&PathLength
|
|
);
|
|
|
|
if(b = FileExists(CurInfPath, FindData)) {
|
|
if(!(FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
InfPathLocation = CurInfPath;
|
|
break;
|
|
}
|
|
} else {
|
|
//
|
|
// See if we got a 'real' error
|
|
//
|
|
d = GetLastError();
|
|
if((d == ERROR_NO_MORE_FILES) || (d == ERROR_FILE_NOT_FOUND) || (d == ERROR_PATH_NOT_FOUND)) {
|
|
//
|
|
// Not really an error--continue looking.
|
|
//
|
|
d = NO_ERROR;
|
|
|
|
} else {
|
|
//
|
|
// This is a 'real' error, abort the search.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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 an ERROR_* status code.
|
|
--*/
|
|
|
|
{
|
|
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 callers 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(';')
|
|
);
|
|
|
|
#if 0
|
|
if(RegDataType == REG_EXPAND_SZ) {
|
|
#endif
|
|
//
|
|
// 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;
|
|
goto clean0;
|
|
}
|
|
|
|
PathLength = 0;
|
|
for(Path1 = PathList;
|
|
*Path1;
|
|
Path1 += lstrlen(Path1) + 1) {
|
|
|
|
if(RegDataType == REG_EXPAND_SZ) {
|
|
PathLength += ExpandEnvironmentStrings(Path1,
|
|
PathBuffer + PathLength,
|
|
MAX_PATH
|
|
);
|
|
} else {
|
|
lstrcpy(PathBuffer + PathLength, Path1);
|
|
PathLength += lstrlen(Path1) + 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;
|
|
#if 0
|
|
}
|
|
#endif
|
|
|
|
} 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;
|
|
}
|
|
|
|
clean0: ; // nothing to do
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// 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] = 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 out-of-memory.
|
|
|
|
--*/
|
|
{
|
|
PTSTR PathListBuffer, TrimBuffer;
|
|
DWORD BufferSize;
|
|
DWORD Err;
|
|
|
|
//
|
|
// Start out with a buffer of MAX_PATH length, which should cover most cases.
|
|
//
|
|
BufferSize = MAX_PATH;
|
|
if(PathListBuffer = MyMalloc((BufferSize+2) * sizeof(TCHAR))) {
|
|
//
|
|
// Loop on a call to MultiSzFromSearchControl until we succeed or hit some
|
|
// error other than buffer-too-small. There are two reasons for this. 1st, it
|
|
// is possible that someone could have added a new path to the registry list
|
|
// between calls, and 2nd, 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(TRUE) {
|
|
|
|
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.)
|
|
//
|
|
if(TrimBuffer = MyRealloc(PathListBuffer, (BufferSize+2) * sizeof(TCHAR))) {
|
|
return TrimBuffer;
|
|
} else {
|
|
return PathListBuffer;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Free our current buffer before we find out what went wrong.
|
|
//
|
|
MyFree(PathListBuffer);
|
|
|
|
if((Err != ERROR_INSUFFICIENT_BUFFER) ||
|
|
!(PathListBuffer = MyMalloc((BufferSize+2) * sizeof(TCHAR)))) {
|
|
//
|
|
// We failed.
|
|
//
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DoMove(
|
|
IN PCTSTR CurrentName,
|
|
IN PCTSTR NewName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper for MoveFileEx on NT, DeleteFile followed by MoveFile on Win9x.
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
BOOL b;
|
|
|
|
//
|
|
// Try to be as efficient as possible on Windows NT.
|
|
//
|
|
if(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
|
|
b = MoveFileEx(CurrentName,NewName,MOVEFILE_REPLACE_EXISTING);
|
|
} else {
|
|
DeleteFile(NewName);
|
|
b = MoveFile(CurrentName,NewName);
|
|
}
|
|
|
|
return(b);
|
|
}
|
|
|
|
|
|
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(). On Win95 this means
|
|
using the wininit.ini mechanism.
|
|
|
|
It is assumed that the target file already exists. On Win95, we need
|
|
to know the short filename since the wininit.ini mechanism only understands
|
|
SFNs and we have to do a bunch of special crud to make this all work.
|
|
Note: we do NOT attempt to deal with the long filename of the target
|
|
when renaming on Win95. In other words, if the target name is not 8.3,
|
|
it will wind up as 8.3 after processing of wininit.ini is done!
|
|
If a referenced file does not exist we have no choice but to use
|
|
the name passed in, which better be 8.3!
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b;
|
|
DWORD err;
|
|
|
|
if(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
|
|
|
|
b = MoveFileEx(
|
|
CurrentName,
|
|
NewName,
|
|
MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT
|
|
);
|
|
|
|
} else {
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// The code below runs only on Win95 because of the version check
|
|
// just above. Thus we should never get here. The code below makes
|
|
// assumptions about maximum section lengths, etc, that are only
|
|
// valid on Win95. The SDK says that sections are limited to 32K,
|
|
// for example, and there are several hard-coded constants that take
|
|
// advantage of this.
|
|
//
|
|
b = FALSE;
|
|
err = ERROR_CALL_NOT_IMPLEMENTED;
|
|
#else
|
|
CHAR WininitFile[MAX_PATH];
|
|
CHAR NewSFN[MAX_PATH];
|
|
CHAR CurrentSFN[MAX_PATH];
|
|
CHAR *Buffer;
|
|
CHAR *p,*q;
|
|
DWORD Size;
|
|
DWORD d;
|
|
|
|
//
|
|
// Calculate full path of wininit.ini and get short filenames.
|
|
// We want to calculate the length of the data we're adding to
|
|
// wininit.ini up front, so we can leave room in the buffer
|
|
// (taking advantage of the 32K max section size on Win95).
|
|
// Trial and error dictates that when you get to that magic 32K
|
|
// number things can go really haywire unless you ensure you
|
|
// don't overflow a 16 bit signed 16-bit number, so we limit
|
|
// everything to 32767 to be safe.
|
|
//
|
|
// For renames, we add a line to delete the target file first.
|
|
// Not sure if this is really necessary, but it won't hurt anything.
|
|
//
|
|
if(Buffer = MyMalloc(32767)) {
|
|
|
|
lstrcpyn(WininitFile,WindowsDirectory,MAX_PATH);
|
|
pSetupConcatenatePaths(WininitFile,"WININIT.INI",MAX_PATH,NULL);
|
|
|
|
if(!GetShortPathName(CurrentName,CurrentSFN,MAX_PATH)) {
|
|
lstrcpyn(CurrentSFN,CurrentName,MAX_PATH);
|
|
}
|
|
if(NewName) {
|
|
if(!GetShortPathName(NewName,NewSFN,MAX_PATH)) {
|
|
lstrcpyn(NewSFN,NewName,MAX_PATH);
|
|
}
|
|
|
|
//
|
|
// NUL=NewSFN
|
|
// NewSFN=CurrentSFN
|
|
//
|
|
// plus terminating nul chars for each line.
|
|
//
|
|
Size = 3 + 1 + lstrlen(NewSFN) + 1
|
|
+ lstrlen(NewSFN) + 1 + lstrlen(CurrentSFN) + 1;
|
|
|
|
} else {
|
|
//
|
|
// NUL=CurrentSFN
|
|
//
|
|
// plus terminating nul char for the line
|
|
//
|
|
Size = 3 + 1 + lstrlen(CurrentSFN) + 1;
|
|
lstrcpy(NewSFN,"NUL");
|
|
}
|
|
|
|
//
|
|
// Fetch the section and see if the line is already in there.
|
|
// If so, we're done.
|
|
//
|
|
d = GetPrivateProfileSection("Rename",Buffer,32767,WininitFile);
|
|
for(p=Buffer; *p; p+=lstrlen(p)+1) {
|
|
if(q = _mbschr(p,'=')) {
|
|
*q = 0;
|
|
if(!lstrcmpi(p,NewSFN) && !lstrcmpi(q+1,CurrentSFN)) {
|
|
break;
|
|
}
|
|
*q = '=';
|
|
}
|
|
}
|
|
|
|
if((*p == 0) && (Size <= 32766-d)) {
|
|
//
|
|
// Add our line(s), and make sure we have that extra nul
|
|
// to terminate things properly. We guaranteed that there'd be
|
|
// enough room by the checks above.
|
|
//
|
|
if(NewName) {
|
|
d += wsprintf(Buffer+d,"NUL=%s",NewSFN) + 1;
|
|
d += wsprintf(Buffer+d,"%s=%s",NewSFN,CurrentSFN) + 1;
|
|
} else {
|
|
d += wsprintf(Buffer+d,"NUL=%s",CurrentSFN) + 1;
|
|
}
|
|
|
|
Buffer[d] = 0;
|
|
|
|
//
|
|
// Write the section back out.
|
|
//
|
|
b = WritePrivateProfileSection("Rename",Buffer,WininitFile);
|
|
err = b ? NO_ERROR : GetLastError();
|
|
} else {
|
|
//
|
|
// The section is full or the line was already there.
|
|
// Just ignore it (in the section full case there's nothing
|
|
// the user can do anyway).
|
|
//
|
|
b = TRUE;
|
|
err = NO_ERROR;
|
|
}
|
|
|
|
MyFree(Buffer);
|
|
} else {
|
|
b = FALSE;
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
#endif
|
|
SetLastError(err);
|
|
|
|
}
|
|
|
|
return(b);
|
|
}
|
|
|
|
|
|
DWORD
|
|
IsInstalledCatalogFromOem(
|
|
IN PCTSTR CatalogFile
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determin if a catalog is an OEM catalog.
|
|
|
|
Arguments:
|
|
|
|
CatalogFile - supplies name of catalog file.
|
|
|
|
Return Value:
|
|
|
|
BOOL. TRUE if the CatalogFile is an OEM Catalog file, and FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR p;
|
|
|
|
//
|
|
// First check that the first 3 characters are OEM
|
|
//
|
|
if (_tcsnicmp(CatalogFile, TEXT("oem"), 3)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Next verify that any characters after "oem" and before ".cat"
|
|
// are digits.
|
|
//
|
|
p = (PTSTR)CatalogFile;
|
|
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 ".cat"
|
|
//
|
|
if (lstrcmpi(p, TEXT(".cat"))) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This is an OEM catalog file
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupInstallCatalog(
|
|
IN LPCTSTR CatalogFullPath,
|
|
IN LPCTSTR NewBaseName, OPTIONAL
|
|
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 - optionally specifies the new base name to use when the
|
|
catalog file is copied into the catalog store. If not specified,
|
|
the basename will be generated by CryptCATAdminAddCatalog.
|
|
|
|
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 catalogFullPath;
|
|
LPWSTR newBaseName;
|
|
|
|
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;
|
|
catalogFullPath = NULL;
|
|
newBaseName = NULL;
|
|
|
|
if(!CryptCATAdminAcquireContext(&hCatAdmin,&DriverVerifyGuid,0)) {
|
|
Err = GetLastError();
|
|
MYASSERT(Err != NO_ERROR);
|
|
if(Err == NO_ERROR) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
} else {
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// We do this even for the Unicode case because CryptCATAdminAddCatalog
|
|
// takes non-const strings and I have no idea whatsoever whether this
|
|
// was just sloppiness or a real requirement, and no desire to mess
|
|
// around trying to figure it out.
|
|
//
|
|
if(catalogFullPath = DuplicateString(CatalogFullPath)) {
|
|
if(NewBaseName) {
|
|
newBaseName = DuplicateString(NewBaseName);
|
|
if(!newBaseName) {
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
} else {
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
#else
|
|
if(catalogFullPath = pSetupAnsiToUnicode(CatalogFullPath)) {
|
|
if(NewBaseName) {
|
|
newBaseName = pSetupAnsiToUnicode(NewBaseName);
|
|
if(!newBaseName) {
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
} else {
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
#endif
|
|
|
|
if(Err == NO_ERROR) {
|
|
|
|
hCatInfo = CryptCATAdminAddCatalog(hCatAdmin,catalogFullPath,newBaseName,0);
|
|
|
|
if(!hCatInfo) {
|
|
Err = GetLastError();
|
|
MYASSERT(Err != NO_ERROR);
|
|
if(Err == NO_ERROR) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
//
|
|
// We should never get this if we asked crypto to auto-
|
|
// generate a name for us...
|
|
//
|
|
MYASSERT(newBaseName);
|
|
|
|
if(NewCatalogFullPath) {
|
|
|
|
if(newBaseName) {
|
|
//
|
|
// Resolve the catalog base filename to a fully-
|
|
// qualified path.
|
|
//
|
|
CatalogInfo.cbStruct = sizeof(CATALOG_INFO);
|
|
|
|
if(CryptCATAdminResolveCatalogPath(hCatAdmin,
|
|
newBaseName,
|
|
&CatalogInfo,
|
|
0)) {
|
|
//
|
|
// We successfully resolved the filename, so we
|
|
// can report success
|
|
//
|
|
Err = NO_ERROR;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Caller isn't interested in finding out what pathname
|
|
// the catalog was installed under...
|
|
//
|
|
Err = NO_ERROR;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// If the caller wants to know the full path under which the
|
|
// catalog got installed, then find that out now.
|
|
//
|
|
if(NewCatalogFullPath) {
|
|
|
|
CatalogInfo.cbStruct = sizeof(CATALOG_INFO);
|
|
if(!CryptCATCatalogInfoFromContext(hCatInfo,&CatalogInfo,0)) {
|
|
Err = GetLastError();
|
|
MYASSERT(Err != NO_ERROR);
|
|
if(Err == NO_ERROR) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
}
|
|
|
|
CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 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) {
|
|
|
|
#ifdef UNICODE
|
|
lstrcpyn(NewCatalogFullPath, CatalogInfo.wszCatalogFile, MAX_PATH);
|
|
#else
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
CatalogInfo.wszCatalogFile,
|
|
-1,
|
|
NewCatalogFullPath,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
CryptCATAdminReleaseContext(hCatAdmin, 0);
|
|
}
|
|
|
|
if(catalogFullPath) {
|
|
MyFree(catalogFullPath);
|
|
}
|
|
if(newBaseName) {
|
|
MyFree(newBaseName);
|
|
}
|
|
|
|
return Err;
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupVerifyCatalogFile(
|
|
IN LPCTSTR CatalogFullPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine verifies a single catalog file. A catalog file is
|
|
"self-verifying" in that there is no additional file or data required
|
|
to verify it.
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
WINTRUST_DATA WintrustData;
|
|
WINTRUST_FILE_INFO WintrustFileInfo;
|
|
#ifndef UNICODE
|
|
WCHAR UnicodeBuffer[MAX_PATH];
|
|
#endif
|
|
|
|
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if(GlobalSetupFlags & PSPGF_AUTOFAIL_VERIFIES) {
|
|
return TRUST_E_FAIL;
|
|
}
|
|
|
|
ZeroMemory(&WintrustData, sizeof(WINTRUST_DATA));
|
|
WintrustData.cbStruct = sizeof(WINTRUST_DATA);
|
|
WintrustData.dwUIChoice = WTD_UI_NONE;
|
|
WintrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
|
|
WintrustData.dwUnionChoice = WTD_CHOICE_FILE;
|
|
WintrustData.pFile = &WintrustFileInfo;
|
|
WintrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
|
|
ZeroMemory(&WintrustFileInfo, sizeof(WINTRUST_FILE_INFO));
|
|
WintrustFileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
|
|
|
|
#ifdef UNICODE
|
|
WintrustFileInfo.pcwszFilePath = CatalogFullPath;
|
|
#else
|
|
MultiByteToWideChar(CP_ACP, 0, CatalogFullPath, -1, UnicodeBuffer, MAX_PATH);
|
|
WintrustFileInfo.pcwszFilePath = UnicodeBuffer;
|
|
#endif
|
|
|
|
return (DWORD)WinVerifyTrust(NULL, &DriverVerifyGuid, &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;
|
|
#ifdef UNICODE
|
|
//
|
|
// Presently, the crypto API CryptCATAdminRemoveCatalog defines its 2nd
|
|
// parameter as a PWSTR, even though it's an IN param that should be CONST.
|
|
// ReidK is going to fix this to be PCWSTR instead, so I'm safe in casting
|
|
// away the const-ness of my CatalogFilename arg here. (Once Reid's
|
|
// check-in happens, I can remove this cast.)
|
|
//
|
|
#define LocalCatName ((PWSTR)CatalogFilename)
|
|
#else
|
|
WCHAR LocalCatName[MAX_PATH];
|
|
|
|
//
|
|
// Crypto APIs are Unicode-only, so convert the catalog filename from
|
|
// ANSI to Unicode...
|
|
//
|
|
if(!MultiByteToWideChar(CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
CatalogFilename,
|
|
-1,
|
|
LocalCatName,
|
|
SIZECHARS(LocalCatName))) {
|
|
|
|
return GetLastError();
|
|
}
|
|
#endif
|
|
|
|
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
if(GlobalSetupFlags & PSPGF_AUTOFAIL_VERIFIES) {
|
|
return TRUST_E_FAIL;
|
|
}
|
|
|
|
Err = NO_ERROR;
|
|
|
|
if(!CryptCATAdminAcquireContext(&hCatAdmin, &DriverVerifyGuid, 0)) {
|
|
Err = GetLastError();
|
|
MYASSERT(Err != NO_ERROR);
|
|
if(Err == NO_ERROR) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
} else {
|
|
|
|
if(!CryptCATAdminRemoveCatalog(hCatAdmin, LocalCatName, 0)) {
|
|
Err = GetLastError();
|
|
MYASSERT(Err != NO_ERROR);
|
|
if(Err == NO_ERROR) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
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 any device is still using this INF, FALSE if no devices are using
|
|
this INF.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Err = NO_ERROR;
|
|
HDEVINFO DeviceInfoSet;
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
DWORD MemberIndex = 0;
|
|
HKEY hkey;
|
|
TCHAR CurrentDeviceInfFile[MAX_PATH];
|
|
TCHAR DeviceId[MAX_DEVICE_ID_LEN];
|
|
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) {
|
|
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) == 0)) {
|
|
//
|
|
// This key is using this INF file so the INF can't be
|
|
// deleted.
|
|
//
|
|
Err = ERROR_SHARING_VIOLATION;
|
|
|
|
if (LogContext) {
|
|
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);
|
|
}
|
|
}
|
|
} else {
|
|
Err = ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
return (Err != ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
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).
|
|
|
|
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;
|
|
|
|
if(InfDeleteErr) {
|
|
*InfDeleteErr = NO_ERROR;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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. We don't
|
|
// need to log anything at this point since we log every device using
|
|
// this INF in pAnyDevicesUsingInf().
|
|
//
|
|
*InfDeleteErr = GetLastError();
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Copy the caller-supplied INF name into a local buffer, so we can modify
|
|
// it when deleting the various files (INF, PNF, and CAT).
|
|
//
|
|
lstrcpyn(FileNameBuffer, InfFullPath, SIZECHARS(FileNameBuffer));
|
|
|
|
//
|
|
// 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('.'));
|
|
if(!ExtPtr) {
|
|
//
|
|
// not xxx.inf, so we know there is no catalog to delete
|
|
//
|
|
return;
|
|
}
|
|
lstrcpy(ExtPtr, pszCatSuffix);
|
|
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_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)...
|
|
//
|
|
lstrcpy(_tcsrchr(FileNameBuffer, TEXT('.')), pszPnfSuffix);
|
|
DeleteFile(FileNameBuffer);
|
|
|
|
//
|
|
// and finally the INF itself...
|
|
//
|
|
lstrcpy(_tcsrchr(FileNameBuffer, TEXT('.')), pszInfSuffix);
|
|
|
|
if(!DeleteFile(FileNameBuffer) && InfDeleteErr) {
|
|
*InfDeleteErr = GetLastError();
|
|
}
|
|
|
|
clean0:
|
|
if(FreeLogContext) {
|
|
DeleteLogContext(LogContext);
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
_VerifyFile(
|
|
IN PSETUP_LOG_CONTEXT LogContext,
|
|
IN OUT HCATADMIN *hCatAdmin, OPTIONAL
|
|
IN OUT HSDB *hSDBDrvMain, 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
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
hCatAdmin - optionally, supplies the address of an HCATADMIN handle. If
|
|
the handle pointed to is NULL, a handle will be acquired (if possible)
|
|
via CryptCATAdminAcquireContext and returned to the caller. If the
|
|
handle pointed to is non-NULL, then that handle will be used for any
|
|
validation done via this routine. If the pointer itself is NULL, then
|
|
an hCatAdmin will be acquired for the duration of this call, and
|
|
released before returning.
|
|
|
|
NOTE: it is the caller's responsibility to free the crypto context
|
|
handle returned by this routine by calling CryptCATAdminReleaseContext.
|
|
This handle may be opened in either success or failure cases, so the
|
|
caller must check for non-NULL returned handle in both cases.
|
|
|
|
hSDBDrvMain - optionally, supplies the address of a handle to the
|
|
drvmain.sdb database. If the handle pointed to is NULL, a handle will
|
|
be acquired (if possible) via SdbInitDatabase and returned to the
|
|
caller. If the handle pointed to is non-NULL, then that handle will be
|
|
used for bad driver database lookup (if requested) in this routine. If
|
|
the pointer itself is NULL, then an HSDB will be acquired for the
|
|
duration of this call (if needed), and released before returning.
|
|
|
|
NOTE: it is the caller's responsibility to free the handle returned by
|
|
this routine by calling SdbReleaseDatabase. This handle may be opened
|
|
in either success or failure cases, so the caller must check for
|
|
non-NULL returned handle in both cases.
|
|
|
|
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).
|
|
|
|
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_FAIL_COPIED_INFS - This flag is ignored. (It is only used
|
|
by the VerifySourceFile routine.)
|
|
|
|
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).
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPBYTE Hash;
|
|
DWORD HashSize;
|
|
CATALOG_INFO CatInfo;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
HCATINFO hCatInfo;
|
|
HCATINFO PrevCat;
|
|
DWORD Err;
|
|
WINTRUST_DATA WintrustData;
|
|
WINTRUST_CATALOG_INFO WintrustCatalogInfo;
|
|
WINTRUST_FILE_INFO WintrustFileInfo;
|
|
DRIVER_VER_INFO VersionInfo;
|
|
LPTSTR CatalogFullPath;
|
|
WCHAR UnicodeKey[MAX_PATH];
|
|
#ifndef UNICODE
|
|
CHAR AnsiBuffer[MAX_PATH];
|
|
#endif
|
|
WIN32_FILE_ATTRIBUTE_DATA FileAttribData;
|
|
BOOL FoundMatchingImage;
|
|
DWORD CurCatFileSize;
|
|
HANDLE CurCatFileHandle, CurCatMappingHandle;
|
|
PVOID CurCatBaseAddress;
|
|
BOOL LoggedWarning;
|
|
BOOL TrySelfSign;
|
|
DWORD AltPlatSlot;
|
|
TAGREF tagref = TAGREF_NULL;
|
|
HCATADMIN LocalhCatAdmin = NULL;
|
|
#ifdef UNICODE
|
|
HSDB LocalhSDBDrvMain = NULL;
|
|
#endif
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
if(GlobalSetupFlags & PSPGF_MINIMAL_EMBEDDED) {
|
|
//
|
|
// The existing behavior of this API in the ANSI case where the crypto
|
|
// APIs aren't available is 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);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
#ifdef ANSI_SETUPAPI
|
|
|
|
//
|
|
// If we are on a system that does not have crypt32.dll, then assume the
|
|
// file is valid.
|
|
//
|
|
if(Dyn_CryptCATAdminAcquireContext == Stub_CryptCATAdminAcquireContext) {
|
|
//
|
|
// Same asserts as above (minimal embedded) case apply here as well...
|
|
//
|
|
MYASSERT(!CatalogFileUsed);
|
|
MYASSERT(!NumCatalogsConsidered);
|
|
MYASSERT(!DigitalSigner);
|
|
MYASSERT(!SignerVersion);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
#endif
|
|
|
|
if(GlobalSetupFlags & PSPGF_AUTOFAIL_VERIFIES) {
|
|
if(Problem) {
|
|
*Problem = SetupapiVerifyAutoFailProblem;
|
|
lstrcpy(ProblemFile, FileFullPath);
|
|
}
|
|
return TRUST_E_FAIL;
|
|
}
|
|
|
|
Hash = NULL;
|
|
LoggedWarning = FALSE;
|
|
TrySelfSign = FALSE;
|
|
AltPlatSlot = 0;
|
|
|
|
//
|
|
// 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))));
|
|
|
|
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(hCatAdmin) {
|
|
//
|
|
// Caller supplied us with a pointer to a crypto context handle. If
|
|
// it's NULL, we must acquire it now...
|
|
//
|
|
if(*hCatAdmin) {
|
|
LocalhCatAdmin = *hCatAdmin;
|
|
} else {
|
|
if(CryptCATAdminAcquireContext(&LocalhCatAdmin, &DriverVerifyGuid, 0)) {
|
|
//
|
|
// We successfully acquired a context handle. Store it in the
|
|
// caller-supplied buffer.
|
|
//
|
|
*hCatAdmin = LocalhCatAdmin;
|
|
} else {
|
|
LocalhCatAdmin = NULL;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Acquire a temporary crypto context handle to be used only for the
|
|
// duration of this routine...
|
|
//
|
|
if(!CryptCATAdminAcquireContext(&LocalhCatAdmin, &DriverVerifyGuid, 0)) {
|
|
LocalhCatAdmin = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Calculate the hash value for the inf.
|
|
//
|
|
if(LocalhCatAdmin) {
|
|
|
|
hFile = CreateFile(FileFullPath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if(hFile == INVALID_HANDLE_VALUE) {
|
|
Err = GetLastError();
|
|
MYASSERT(Err != NO_ERROR);
|
|
if(Problem) {
|
|
*Problem = SetupapiVerifyFileProblem;
|
|
lstrcpy(ProblemFile, FileFullPath);
|
|
}
|
|
} else {
|
|
#ifdef UNICODE
|
|
//
|
|
// 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)) {
|
|
|
|
if(hSDBDrvMain) {
|
|
//
|
|
// Caller supplied us with a pointer to an SDB handle for
|
|
// the bad driver database. If it's NULL, we must acquire
|
|
// it now...
|
|
//
|
|
if(*hSDBDrvMain) {
|
|
LocalhSDBDrvMain = *hSDBDrvMain;
|
|
} else {
|
|
LocalhSDBDrvMain = SdbInitDatabase(SDB_DATABASE_MAIN_DRIVERS, NULL);
|
|
if(LocalhSDBDrvMain) {
|
|
*hSDBDrvMain = LocalhSDBDrvMain;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Acquire a temporary handle to drvmain.sdb to be used
|
|
// only for our call to SdbGetDatabaseMatch.
|
|
//
|
|
LocalhSDBDrvMain = SdbInitDatabase(SDB_DATABASE_MAIN_DRIVERS, NULL);
|
|
}
|
|
|
|
//
|
|
// 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 ? FileFullPath : TEXT("")
|
|
);
|
|
|
|
if(Problem) {
|
|
*Problem = SetupapiVerifyDriverBlocked;
|
|
lstrcpy(ProblemFile, FileFullPath);
|
|
}
|
|
LoggedWarning = TRUE;
|
|
Err = ERROR_DRIVER_BLOCKED;
|
|
goto clean0;
|
|
}
|
|
}
|
|
} else {
|
|
Err = GetLastError();
|
|
//
|
|
// Log an error that we couldn't access the bad driver
|
|
// database to check if this is a blocked driver. (We
|
|
// don't consider this a fatal error.)
|
|
//
|
|
WriteLogEntry(LogContext,
|
|
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
|
|
MSG_LOG_CANT_ACCESS_BDD,
|
|
NULL,
|
|
FileFullPath
|
|
);
|
|
WriteLogError(LogContext, SETUP_LOG_ERROR, Err);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// 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;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// 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.fdwRevocationChecks = WTD_REVOKE_NONE;
|
|
WintrustData.dwStateAction = WTD_STATEACTION_AUTO_CACHE;
|
|
WintrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Always 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;
|
|
|
|
|
|
//
|
|
// Start out with a hash buffer size that should be large enough for
|
|
// most requests.
|
|
//
|
|
HashSize = 100;
|
|
do {
|
|
Hash = MyMalloc(HashSize);
|
|
if(!Hash) {
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
if(CryptCATAdminCalcHashFromFileHandle(hFile, &HashSize, Hash, 0)) {
|
|
Err = NO_ERROR;
|
|
} else {
|
|
Err = GetLastError();
|
|
MYASSERT(Err != NO_ERROR);
|
|
//
|
|
// If this API did mess up and not set last error, go ahead
|
|
// and set something.
|
|
//
|
|
if(Err == NO_ERROR) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
|
|
MyFree(Hash);
|
|
if(Err != ERROR_INSUFFICIENT_BUFFER) {
|
|
//
|
|
// The API failed for some reason other than
|
|
// buffer-too-small. We will try to check if the file
|
|
// is self-signed.
|
|
//
|
|
Hash = NULL; // reset this so we won't try to free it
|
|
// later
|
|
hCatInfo = NULL;
|
|
TrySelfSign = TRUE;
|
|
WriteLogEntry(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
SETUP_LOG_VERBOSE|SETUP_LOG_BUFFER,
|
|
MSG_LOG_HASH_ERROR,
|
|
NULL,
|
|
FileFullPath ? FileFullPath : TEXT(""),
|
|
Catalog ? Catalog : TEXT(""),
|
|
Key
|
|
);
|
|
WriteLogError(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
SETUP_LOG_VERBOSE,
|
|
Err
|
|
);
|
|
LoggedWarning = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
} while(Err != NO_ERROR);
|
|
|
|
if(!TrySelfSign) {
|
|
|
|
if(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!
|
|
//
|
|
#ifdef UNICODE
|
|
//
|
|
// Copy the key to a writable Unicode character buffer so we
|
|
// can lower-case it.
|
|
//
|
|
lstrcpyn(UnicodeKey, Key, SIZECHARS(UnicodeKey));
|
|
CharLower(UnicodeKey);
|
|
#else
|
|
//
|
|
// Copy the key to a writable ANSI character buffer so we can
|
|
// lower-case it (prior to converting the string to Unicode).
|
|
//
|
|
lstrcpyn(AnsiBuffer, Key, SIZECHARS(AnsiBuffer));
|
|
CharLower(AnsiBuffer);
|
|
MultiByteToWideChar(CP_ACP, 0, AnsiBuffer, -1, UnicodeKey, SIZECHARS(UnicodeKey));
|
|
#endif
|
|
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...
|
|
//
|
|
#ifdef UNICODE
|
|
WintrustCatalogInfo.pcwszCatalogFilePath = Catalog;
|
|
#else
|
|
//
|
|
// Use the handy-dandy unicode catalog filename buffer
|
|
// provided for us by the CatInfo.wszCatalogFile field.
|
|
//
|
|
MultiByteToWideChar(CP_ACP, 0, Catalog, -1, CatInfo.wszCatalogFile, SIZECHARS(CatInfo.wszCatalogFile));
|
|
WintrustCatalogInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
|
|
#endif
|
|
//
|
|
// 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) {
|
|
|
|
DWORD dwStateAction;
|
|
|
|
//
|
|
// 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".
|
|
//
|
|
dwStateAction = WintrustData.dwStateAction;
|
|
|
|
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(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
|
|
MSG_LOG_CRYPTO_CACHE_FLUSH_FAILURE,
|
|
NULL,
|
|
Catalog
|
|
);
|
|
WriteLogError(
|
|
(PSETUP_LOG_CONTEXT)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 = dwStateAction;
|
|
|
|
Err = pSetupVerifyCatalogFile(Catalog);
|
|
}
|
|
|
|
if(Err != NO_ERROR) {
|
|
WriteLogEntry(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
GetCatLogLevel(Err)|SETUP_LOG_BUFFER,
|
|
MSG_LOG_VERIFYCAT_ERROR,
|
|
NULL,
|
|
Catalog ? Catalog : TEXT("")
|
|
);
|
|
WriteLogError(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
GetCatLogLevel(Err),
|
|
Err
|
|
);
|
|
LoggedWarning = TRUE;
|
|
if(Problem) {
|
|
*Problem = SetupapiVerifyCatalogProblem;
|
|
lstrcpy(ProblemFile, Catalog);
|
|
}
|
|
} else {
|
|
//
|
|
// Catalog was verified, now verify the file using that
|
|
// catalog.
|
|
//
|
|
if(CatalogFileUsed) {
|
|
lstrcpy(CatalogFileUsed, Catalog);
|
|
}
|
|
|
|
Err = (DWORD)WinVerifyTrust(NULL,
|
|
&DriverVerifyGuid,
|
|
&WintrustData
|
|
);
|
|
|
|
//
|
|
// Fill in the DigitalSigner and SignerVersion if
|
|
// they were passed in.
|
|
//
|
|
if (Err == NO_ERROR) {
|
|
if (DigitalSigner) {
|
|
#ifdef UNICODE
|
|
lstrcpy(DigitalSigner, VersionInfo.wszSignedBy);
|
|
#else
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
VersionInfo.wszSignedBy,
|
|
-1,
|
|
DigitalSigner,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif
|
|
}
|
|
|
|
if (SignerVersion) {
|
|
#ifdef UNICODE
|
|
lstrcpy(SignerVersion, VersionInfo.wszVersion);
|
|
#else
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
VersionInfo.wszVersion,
|
|
-1,
|
|
SignerVersion,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
|
|
TrySelfSign = TRUE;
|
|
|
|
WriteLogEntry(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
GetCatLogLevel(Err)|SETUP_LOG_BUFFER,
|
|
MSG_LOG_VERIFYFILE_ERROR,
|
|
NULL,
|
|
FileFullPath ? FileFullPath : TEXT(""),
|
|
Catalog ? Catalog : TEXT(""),
|
|
Key
|
|
);
|
|
WriteLogError(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
GetCatLogLevel(Err),
|
|
Err
|
|
);
|
|
LoggedWarning = TRUE;
|
|
} else {
|
|
WriteLogEntry(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
SETUP_LOG_VERBOSE,
|
|
MSG_LOG_VERIFYFILE_OK,
|
|
NULL,
|
|
FileFullPath ? FileFullPath : TEXT(""),
|
|
Catalog ? Catalog : TEXT(""),
|
|
Key
|
|
);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Search through installed catalogs looking for those that
|
|
// contain data for a file with the hash we just calculated.
|
|
//
|
|
PrevCat = NULL;
|
|
hCatInfo = CryptCATAdminEnumCatalogFromHash(LocalhCatAdmin,
|
|
Hash,
|
|
HashSize,
|
|
0,
|
|
&PrevCat
|
|
);
|
|
|
|
while(hCatInfo) {
|
|
|
|
CatInfo.cbStruct = sizeof(CATALOG_INFO);
|
|
if(CryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0)) {
|
|
#ifdef UNICODE
|
|
CatalogFullPath = CatInfo.wszCatalogFile;
|
|
#else
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
CatInfo.wszCatalogFile,
|
|
-1,
|
|
AnsiBuffer,
|
|
sizeof(AnsiBuffer),
|
|
NULL,
|
|
NULL
|
|
);
|
|
CatalogFullPath = AnsiBuffer;
|
|
#endif
|
|
//
|
|
// 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) ||
|
|
!IsInstalledCatalogFromOem(pSetupGetFileTitle(CatalogFullPath))) {
|
|
|
|
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(pSetupOpenAndMapFileForRead(CatalogFullPath,
|
|
&CurCatFileSize,
|
|
&CurCatFileHandle,
|
|
&CurCatMappingHandle,
|
|
&CurCatBaseAddress) == NO_ERROR) {
|
|
|
|
MYASSERT(CurCatFileSize == CatalogImageSize);
|
|
|
|
//
|
|
// Surround the following in try/except, in case we get an inpage error.
|
|
//
|
|
try {
|
|
//
|
|
// We've found a potential match.
|
|
//
|
|
FoundMatchingImage = !memcmp(
|
|
CatalogBaseAddress,
|
|
CurCatBaseAddress,
|
|
CatalogImageSize
|
|
);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
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.
|
|
//
|
|
// NOTE: Because we're using cached
|
|
// catalog information (i.e., the
|
|
// WTD_STATEACTION_AUTO_CACHE flag), we
|
|
// don't need to explicitly validate the
|
|
// catalog itself first.
|
|
//
|
|
WintrustCatalogInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
|
|
|
|
Err = (DWORD)WinVerifyTrust(NULL,
|
|
&DriverVerifyGuid,
|
|
&WintrustData
|
|
);
|
|
|
|
//
|
|
// Fill in the DigitalSigner and SignerVersion if
|
|
// they were passed in.
|
|
//
|
|
if (Err == NO_ERROR) {
|
|
if (DigitalSigner) {
|
|
#ifdef UNICODE
|
|
lstrcpy(DigitalSigner, VersionInfo.wszSignedBy);
|
|
#else
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
VersionInfo.wszSignedBy,
|
|
-1,
|
|
DigitalSigner,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif
|
|
}
|
|
|
|
if (SignerVersion) {
|
|
#ifdef UNICODE
|
|
lstrcpy(SignerVersion, VersionInfo.wszVersion);
|
|
#else
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
VersionInfo.wszVersion,
|
|
-1,
|
|
SignerVersion,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
GetCatLogLevel(Err)|SETUP_LOG_BUFFER,
|
|
MSG_LOG_VERIFYFILE_ERROR,
|
|
NULL,
|
|
FileFullPath ? FileFullPath : TEXT(""),
|
|
CatalogFullPath ? CatalogFullPath : TEXT(""),
|
|
Key
|
|
);
|
|
WriteLogError(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
GetCatLogLevel(Err),
|
|
Err
|
|
);
|
|
LoggedWarning = TRUE;
|
|
} else {
|
|
WriteLogEntry(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
SETUP_LOG_VERBOSE,
|
|
MSG_LOG_VERIFYFILE_OK,
|
|
NULL,
|
|
FileFullPath ? FileFullPath : TEXT(""),
|
|
CatalogFullPath ? CatalogFullPath : TEXT(""),
|
|
Key
|
|
);
|
|
}
|
|
|
|
if(Err == NO_ERROR) {
|
|
//
|
|
// We successfully verified the
|
|
// file--store the name of the
|
|
// catalog used, if the caller
|
|
// requested it.
|
|
//
|
|
if(CatalogFileUsed) {
|
|
lstrcpy(CatalogFileUsed, CatalogFullPath);
|
|
}
|
|
} else {
|
|
if(Catalog || CatalogBaseAddress) {
|
|
if(CatalogFileUsed) {
|
|
lstrcpy(CatalogFileUsed, CatalogFullPath);
|
|
}
|
|
if(Problem) {
|
|
*Problem = SetupapiVerifyFileProblem;
|
|
lstrcpy(ProblemFile, 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);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PrevCat = hCatInfo;
|
|
hCatInfo = CryptCATAdminEnumCatalogFromHash(LocalhCatAdmin, Hash, HashSize, 0, &PrevCat);
|
|
}
|
|
|
|
if(!hCatInfo) {
|
|
//
|
|
// We exhausted all the applicable catalogs without
|
|
// finding the one we needed.
|
|
//
|
|
Err = GetLastError();
|
|
MYASSERT(Err != NO_ERROR);
|
|
//
|
|
// Make sure we have a valid error code.
|
|
//
|
|
if(Err == NO_ERROR) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
TrySelfSign = TRUE;
|
|
//
|
|
// report failure if we haven't already done so
|
|
//
|
|
if(!LoggedWarning) {
|
|
WriteLogEntry((PSETUP_LOG_CONTEXT)LogContext,
|
|
GetCatLogLevel(Err)|SETUP_LOG_BUFFER,
|
|
MSG_LOG_VERIFYFILE_ERROR,
|
|
NULL,
|
|
FileFullPath ? FileFullPath : TEXT(""),
|
|
Catalog ? Catalog : TEXT(""),
|
|
Key
|
|
);
|
|
WriteLogError((PSETUP_LOG_CONTEXT)LogContext,GetCatLogLevel(Err),Err);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if(Problem) {
|
|
*Problem = SetupapiVerifyFileProblem;
|
|
lstrcpy(ProblemFile, FileFullPath);
|
|
}
|
|
}
|
|
}
|
|
if (TrySelfSign) {
|
|
if(!(Flags & VERIFY_FILE_IGNORE_SELFSIGNED)) {
|
|
//
|
|
// The file failed to validate using any catalogs
|
|
// See if the file validates without a
|
|
// catalog (i.e., the file contains its own
|
|
// signature).
|
|
//
|
|
WintrustData.dwUnionChoice = WTD_CHOICE_FILE;
|
|
WintrustData.pFile = &WintrustFileInfo;
|
|
ZeroMemory(&WintrustFileInfo, sizeof(WINTRUST_FILE_INFO));
|
|
WintrustFileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
|
|
#ifdef UNICODE
|
|
WintrustFileInfo.pcwszFilePath = FileFullPath;
|
|
#else
|
|
//
|
|
// Use the UnicodeKey buffer to hold the unicode
|
|
// version of the full pathname of the file to be
|
|
// verified.
|
|
//
|
|
MultiByteToWideChar(CP_ACP, 0, FileFullPath, -1, UnicodeKey, SIZECHARS(UnicodeKey));
|
|
WintrustFileInfo.pcwszFilePath = UnicodeKey;
|
|
#endif
|
|
Err = (DWORD)WinVerifyTrust(NULL,
|
|
&DriverVerifyGuid,
|
|
&WintrustData
|
|
);
|
|
|
|
//
|
|
// Fill in the DigitalSigner and SignerVersion if
|
|
// they were passed in.
|
|
//
|
|
if (Err == NO_ERROR) {
|
|
if (DigitalSigner) {
|
|
#ifdef UNICODE
|
|
lstrcpy(DigitalSigner, VersionInfo.wszSignedBy);
|
|
#else
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
VersionInfo.wszSignedBy,
|
|
-1,
|
|
DigitalSigner,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif
|
|
}
|
|
|
|
if (SignerVersion) {
|
|
#ifdef UNICODE
|
|
lstrcpy(SignerVersion, VersionInfo.wszVersion);
|
|
#else
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
VersionInfo.wszVersion,
|
|
-1,
|
|
SignerVersion,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
SETUP_LOG_VERBOSE|SETUP_LOG_BUFFER,
|
|
MSG_LOG_SELFSIGN_ERROR,
|
|
NULL,
|
|
FileFullPath ? FileFullPath : TEXT(""),
|
|
Key
|
|
);
|
|
WriteLogError(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
SETUP_LOG_VERBOSE,
|
|
Err
|
|
);
|
|
LoggedWarning = TRUE;
|
|
} else {
|
|
WriteLogEntry(
|
|
(PSETUP_LOG_CONTEXT)LogContext,
|
|
SETUP_LOG_VERBOSE,
|
|
MSG_LOG_SELFSIGN_OK,
|
|
NULL,
|
|
FileFullPath ? FileFullPath : TEXT(""),
|
|
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;
|
|
lstrcpy(ProblemFile, FileFullPath);
|
|
}
|
|
}
|
|
}
|
|
if (Hash) {
|
|
MyFree(Hash);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
Err = GetLastError();
|
|
MYASSERT(Err != NO_ERROR);
|
|
//
|
|
// Make sure we have a valid error code.
|
|
//
|
|
if(Err == NO_ERROR) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
|
|
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;
|
|
lstrcpy(ProblemFile, FileFullPath);
|
|
}
|
|
}
|
|
|
|
if (Err != NO_ERROR) {
|
|
//
|
|
// report failure if we haven't already done so
|
|
//
|
|
if(!LoggedWarning) {
|
|
WriteLogEntry(LogContext,
|
|
GetCatLogLevel(Err)|SETUP_LOG_BUFFER,
|
|
MSG_LOG_VERIFYFILE_ERROR,
|
|
NULL,
|
|
FileFullPath ? FileFullPath : TEXT(""),
|
|
Catalog ? Catalog : TEXT(""),
|
|
Key
|
|
);
|
|
WriteLogError((PSETUP_LOG_CONTEXT)LogContext,GetCatLogLevel(Err),Err);
|
|
}
|
|
}
|
|
|
|
clean0:
|
|
#ifdef UNICODE
|
|
if(!hSDBDrvMain && LocalhSDBDrvMain) {
|
|
//
|
|
// Don't need to return our HSDB to the caller, so free
|
|
// it now.
|
|
//
|
|
SdbReleaseDatabase(LocalhSDBDrvMain);
|
|
}
|
|
#endif
|
|
if(hFile != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(hFile);
|
|
}
|
|
if(AltPlatSlot) {
|
|
ReleaseLogInfoSlot(LogContext,AltPlatSlot);
|
|
}
|
|
if(!hCatAdmin && LocalhCatAdmin) {
|
|
CryptCATAdminReleaseContext(LocalhCatAdmin, 0);
|
|
}
|
|
SetLastError(Err);
|
|
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,
|
|
NULL,
|
|
Catalog,
|
|
CatalogBaseAddress,
|
|
CatalogImageSize,
|
|
Key,
|
|
FileFullPath,
|
|
Problem,
|
|
ProblemFile,
|
|
CatalogAlreadyVerified,
|
|
AltPlatformInfo,
|
|
VERIFY_FILE_USE_OEM_CATALOGS,
|
|
CatalogFileUsed,
|
|
NumCatalogsConsidered,
|
|
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
|
|
)
|
|
|
|
/*++
|
|
|
|
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).
|
|
|
|
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:
|
|
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if(PolicyToUse || ValidationPlatform) {
|
|
|
|
if(PolicyToUse) {
|
|
//
|
|
// Initialize policy to "Ignore"
|
|
//
|
|
*PolicyToUse = DRIVERSIGN_NONE;
|
|
}
|
|
|
|
ClassInDrvSignList = FALSE;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
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.
|
|
//
|
|
if(PolicyToUse || ValidationPlatform) {
|
|
//
|
|
// First, 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;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
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
|
|
}
|
|
|
|
if(PolicyToUse) {
|
|
//
|
|
// Unless we've already established that the policy to use is "Block"
|
|
// (because we found an exception INF), we should retrieve the
|
|
// applicable policy now...
|
|
//
|
|
if(*PolicyToUse != DRIVERSIGN_BLOCKING) {
|
|
|
|
*PolicyToUse =
|
|
pSetupGetCurrentDriverSigningPolicy(DeviceInfFound && ClassInDrvSignList);
|
|
}
|
|
}
|
|
|
|
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, and 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:
|
|
|
|
If the INF is a device INF, the return value is TRUE. Otherwise, it is
|
|
FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Policy;
|
|
|
|
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.
|
|
//
|
|
if(UseOriginalInfName) {
|
|
*UseOriginalInfName = FALSE;
|
|
}
|
|
if(ValidationPlatform) {
|
|
*ValidationPlatform = NULL;
|
|
}
|
|
return pSetupGetCurrentDriverSigningPolicy(FALSE);
|
|
}
|
|
|
|
IsInfForDeviceInstall(LogContext,
|
|
NULL,
|
|
(PLOADED_INF)InfHandle,
|
|
NULL,
|
|
ValidationPlatform,
|
|
&Policy,
|
|
UseOriginalInfName
|
|
);
|
|
|
|
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 due to
|
|
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).
|
|
|
|
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;
|
|
|
|
if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
(IsDeviceInstallation ? pszDrvSignPolicyPath
|
|
: pszNonDrvSignPolicyPath),
|
|
0,
|
|
KEY_READ,
|
|
&hKey))
|
|
{
|
|
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);
|
|
}
|
|
|
|
//
|
|
// 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))
|
|
{
|
|
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;
|
|
|
|
}
|
|
}
|
|
|
|
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
|
|
)
|
|
|
|
/*++
|
|
|
|
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 - 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.
|
|
|
|
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.
|
|
|
|
VERIFY_FILE_FAIL_COPIED_INFS - if this bit is set, then INFs destined
|
|
for %windir%\Inf will automatically
|
|
fail verification.
|
|
|
|
VERIFY_FILE_DRIVERBLOCKED_ONLY - Only check if the file is in the bad
|
|
driver database, don't do any digital
|
|
sigature validation.
|
|
|
|
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.
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is NO_ERROR;
|
|
If unsuccessful, the return value is the Win32 error code indicating the
|
|
cause of the failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc;
|
|
PCTSTR AltCatalogFile;
|
|
LPCTSTR InfFullPath;
|
|
|
|
MYASSERT(!QueueNode || Queue);
|
|
|
|
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);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// If we know the file's destination (i.e., we have a QueueNode), and we've
|
|
// been asked to fail any INFs headed for %windir%\Inf, we check for that
|
|
// 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.
|
|
//
|
|
if(QueueNode && (Flags & VERIFY_FILE_FAIL_COPIED_INFS) &&
|
|
!(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 && !lstrcmpi(p, pszInfSuffix)) {
|
|
//
|
|
// It's an INF. Construct the full target path to see where it's
|
|
// going.
|
|
//
|
|
lstrcpyn(
|
|
TargetPath,
|
|
pSetupStringTableStringFromId(Queue->StringTable, QueueNode->TargetDirectory),
|
|
SIZECHARS(TargetPath)
|
|
);
|
|
|
|
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;
|
|
lstrcpy(ProblemFile, 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 verfied this catalog previously. So
|
|
// all we need _VerifyFile to do is just verify the temporary
|
|
// (source) file against that catalog.
|
|
//
|
|
rc = _VerifyFile(LogContext,
|
|
&(Queue->hCatAdmin),
|
|
&(Queue->hSDBDrvMain),
|
|
QueueNode->CatalogInfo->CatalogFilenameOnSystem,
|
|
NULL,
|
|
0,
|
|
Key,
|
|
FileToVerifyFullPath,
|
|
Problem,
|
|
ProblemFile,
|
|
TRUE,
|
|
AltPlatformInfo,
|
|
Flags,
|
|
CatalogFileUsed,
|
|
NULL,
|
|
DigitalSigner,
|
|
SignerVersion
|
|
);
|
|
} else {
|
|
//
|
|
// If there's no error associated with this catalog info node, then
|
|
// that simply means that the INF didn't specify a CatalogFile=
|
|
// entry, thus we should 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->hCatAdmin),
|
|
&(Queue->hSDBDrvMain),
|
|
AltCatalogFile,
|
|
NULL,
|
|
0,
|
|
Key,
|
|
FileToVerifyFullPath,
|
|
Problem,
|
|
ProblemFile,
|
|
FALSE,
|
|
AltPlatformInfo,
|
|
Flags |
|
|
((QueueNode->CatalogInfo->VerificationFailureError == NO_ERROR)
|
|
? 0
|
|
: VERIFY_FILE_DRIVERBLOCKED_ONLY),
|
|
CatalogFileUsed,
|
|
NULL,
|
|
DigitalSigner,
|
|
SignerVersion
|
|
);
|
|
|
|
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
|
|
);
|
|
lstrcpy(ProblemFile, 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) {
|
|
lstrcpy(ProblemFile, 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.
|
|
//
|
|
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->hCatAdmin) : NULL,
|
|
Queue ? &(Queue->hSDBDrvMain) : NULL,
|
|
AltCatalogFile,
|
|
NULL,
|
|
0,
|
|
Key,
|
|
FileToVerifyFullPath,
|
|
Problem,
|
|
ProblemFile,
|
|
FALSE,
|
|
AltPlatformInfo,
|
|
Flags |
|
|
(InfIsBad ? VERIFY_FILE_DRIVERBLOCKED_ONLY : 0),
|
|
CatalogFileUsed,
|
|
NULL,
|
|
DigitalSigner,
|
|
SignerVersion
|
|
);
|
|
|
|
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;
|
|
lstrcpy(ProblemFile, 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) && OriginalSourceFileFullPath &&
|
|
((*Problem == SetupapiVerifyFileNotSigned) || (*Problem == SetupapiVerifyFileProblem))) {
|
|
|
|
lstrcpy(ProblemFile, OriginalSourceFileFullPath);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
BOOL
|
|
VerifyDeviceInfFile(
|
|
IN PSETUP_LOG_CONTEXT LogContext,
|
|
IN OUT HCATADMIN *hCatAdmin, 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
|
|
)
|
|
/*++
|
|
|
|
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.
|
|
|
|
hCatAdmin - optionally, supplies the address of an HCATADMIN handle. If
|
|
the handle pointed to is NULL, a handle will be acquired (if possible)
|
|
via CryptCATAdminAcquireContext and returned to the caller. If the
|
|
handle pointed to is non-NULL, then that handle will be used for any
|
|
validation done via this routine. If the pointer itself is NULL, then
|
|
an hCatAdmin will be acquired for the duration of this call, and
|
|
released before returning.
|
|
|
|
NOTE: it is the caller's responsibility to free the crypto context
|
|
handle returned by this routine by calling CryptCATAdminReleaseContext.
|
|
This handle may be opened in either success or failure cases, so the
|
|
caller must check for non-NULL returned handle in both cases.
|
|
|
|
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.
|
|
|
|
Return Value:
|
|
|
|
If the INF's signature is successfully verified, the return value is
|
|
non-zero (TRUE).
|
|
Otherwise, the return value is FALSE. GetLastError can be used to obtain
|
|
the Win32 error code indicating the cause of failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL DifferentOriginalName;
|
|
TCHAR OriginalCatalogName[MAX_PATH];
|
|
TCHAR CatalogPath[MAX_PATH];
|
|
TCHAR OriginalInfFileName[MAX_PATH];
|
|
DWORD Err;
|
|
PSP_ALTPLATFORM_INFO_V2 ValidationPlatform;
|
|
|
|
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);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if(GlobalSetupFlags & PSPGF_AUTOFAIL_VERIFIES) {
|
|
SetLastError(TRUST_E_FAIL);
|
|
return FALSE;
|
|
}
|
|
|
|
CatalogPath[0] = TEXT('\0');
|
|
|
|
Err = pGetInfOriginalNameAndCatalogFile(pInf,
|
|
NULL,
|
|
&DifferentOriginalName,
|
|
OriginalInfFileName,
|
|
SIZECHARS(OriginalInfFileName),
|
|
OriginalCatalogName,
|
|
SIZECHARS(OriginalCatalogName),
|
|
AltPlatformInfo
|
|
);
|
|
|
|
if(Err != NO_ERROR) {
|
|
SetLastError(Err);
|
|
return FALSE;
|
|
}
|
|
|
|
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) {
|
|
SetLastError(ERROR_NO_CATALOG_FOR_OEM_INF);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// ...and the CAT must reside in the same directory as the INF.
|
|
//
|
|
lstrcpy(CatalogPath, CurrentInfName);
|
|
lstrcpy((PTSTR)pSetupGetFileTitle(CatalogPath), OriginalCatalogName);
|
|
|
|
} 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) {
|
|
lstrcpy(CatalogPath, pSetupGetFileTitle(CurrentInfName));
|
|
lstrcpy(_tcsrchr(CatalogPath, TEXT('.')), pszCatSuffix);
|
|
}
|
|
}
|
|
|
|
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.
|
|
//
|
|
lstrcpy(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,
|
|
NULL,
|
|
NULL
|
|
);
|
|
} else {
|
|
ValidationPlatform = NULL;
|
|
}
|
|
|
|
Err = _VerifyFile(LogContext,
|
|
hCatAdmin,
|
|
NULL,
|
|
(*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);
|
|
|
|
if(ValidationPlatform) {
|
|
MyFree(ValidationPlatform);
|
|
}
|
|
|
|
SetLastError(Err);
|
|
|
|
return (Err == NO_ERROR);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
#ifdef UNICODE
|
|
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(EXCEPTION_EXECUTE_HANDLER) {
|
|
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);
|
|
}
|
|
|
|
#else // no file protection on Win9x
|
|
ret = FALSE;
|
|
#endif
|
|
|
|
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==NULL) {
|
|
Buffer[0] = TEXT('\0');
|
|
} else {
|
|
FilePart[0] = TEXT('\0');
|
|
}
|
|
return GetAnsiMuiSafeFilename(Buffer);
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
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 to current path name
|
|
//
|
|
lstrcpyn(Buffer,FilePath,MAX_PATH);
|
|
}
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
#else
|
|
|
|
PSTR
|
|
GetAnsiMuiSafeFilename(
|
|
IN PCTSTR FilePath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
See above, this is almost a no-op in ANSI
|
|
|
|
Arguments:
|
|
|
|
FilePath - supplies a context for logging the verify
|
|
|
|
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
|
|
|
|
--*/
|
|
{
|
|
PSTR res = DuplicateString(FilePath);
|
|
if(!res) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#endif
|
|
|
|
BOOL
|
|
pSetupAppendPath(
|
|
IN PCTSTR Path1,
|
|
IN PCTSTR Path2,
|
|
OUT PTSTR* Combined
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Call pSetupConcatenatePaths
|
|
dynamically modifying memory/pointer
|
|
|
|
Arguments:
|
|
|
|
Path1/Path2 - to concatenate
|
|
Combined - resultant path
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the full path fit in Target buffer. Otherwise the path
|
|
not created.
|
|
|
|
--*/
|
|
{
|
|
PTSTR FinalPath;
|
|
UINT Len;
|
|
|
|
if(!Path1 && !Path2) {
|
|
*Combined = NULL;
|
|
return TRUE;
|
|
}
|
|
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;
|
|
}
|
|
lstrcpy(FinalPath,Path1);
|
|
if(!pSetupConcatenatePaths(FinalPath,Path2,Len,NULL)) {
|
|
MyFree(FinalPath);
|
|
*Combined = NULL;
|
|
return FALSE;
|
|
}
|
|
*Combined = FinalPath;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pSetupApplyExtension(
|
|
IN PCTSTR Original,
|
|
IN PCTSTR Extension,
|
|
OUT PTSTR* NewName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Apply Extension onto Original to obtain NewName
|
|
|
|
Arguments:
|
|
|
|
Original - original name with old extension
|
|
Extension - new extension to apply (with or without dot)
|
|
NewName - allocated buffer containing new filename
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the full path fit in Target buffer. Otherwise the path
|
|
not created.
|
|
|
|
--*/
|
|
{
|
|
PCTSTR End = Original+lstrlen(Original);
|
|
PCTSTR OldExt = End;
|
|
PTSTR NewString = NULL;
|
|
TCHAR c;
|
|
UINT len;
|
|
UINT sublen;
|
|
if(Extension && (Extension[0] == 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 + lstrlen(Extension) + 2;
|
|
NewString = MyMalloc(len*sizeof(TCHAR));
|
|
if(!NewString) {
|
|
*NewName = NULL;
|
|
return FALSE;
|
|
}
|
|
memcpy(NewString,Original,(sublen)*sizeof(TCHAR));
|
|
NewString[sublen++] = Extension ? TEXT('.') : TEXT('\0');
|
|
if(Extension) {
|
|
lstrcpy(NewString+sublen,Extension);
|
|
}
|
|
*NewName = NewString;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ClassGuidInDrvSignPolicyList(
|
|
IN PSETUP_LOG_CONTEXT LogContext, 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) 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 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;
|
|
|
|
//
|
|
// 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)) {
|
|
|
|
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...
|
|
//
|
|
lstrcpyn(CertClassInfPath, InfDirectory,SIZECHARS(CertClassInfPath)-13);
|
|
lstrcat(CertClassInfPath, TEXT("\\certclas.inf"));
|
|
|
|
Err = _VerifyFile(LogContext,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
pSetupGetFileTitle(CertClassInfPath),
|
|
CertClassInfPath,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
NULL,
|
|
(VERIFY_FILE_IGNORE_SELFSIGNED | VERIFY_FILE_NO_DRIVERBLOCKED_CHECK),
|
|
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.
|
|
//
|
|
hCertClassInf = SetupOpenInfFile(CertClassInfPath,
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
&ErrorLine
|
|
);
|
|
|
|
if(hCertClassInf == INVALID_HANDLE_VALUE) {
|
|
//
|
|
// This failure is highly unlikely to occur, since we just got
|
|
// through validating the INF.
|
|
//
|
|
Err = GetLastError();
|
|
|
|
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;
|
|
|
|
} else {
|
|
//
|
|
// Certclas.inf validated, and we successfully opened it. Now
|
|
// retrieve the list contained therein.
|
|
//
|
|
LineCount = SetupGetLineCount(hCertClassInf,
|
|
pszDriverSigningClasses
|
|
);
|
|
|
|
if((LineCount > 0) &&
|
|
(NULL != (GlobalDrvSignPolicyList.Members = MyMalloc(LineCount * sizeof(DRVSIGN_CLASS_LIST_NODE))))) {
|
|
|
|
if(SetupFindFirstLine(hCertClassInf,
|
|
pszDriverSigningClasses,
|
|
NULL,
|
|
&InfContext)) {
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
SetupCloseInfFile(hCertClassInf);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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(_AXP64_)
|
|
PROCESSOR_ARCHITECTURE_ALPHA64;
|
|
#elif defined(_ALPHA_)
|
|
PROCESSOR_ARCHITECTURE_ALPHA;
|
|
#elif defined(_MIPS_)
|
|
PROCESSOR_ARCHITECTURE_MIPS;
|
|
#elif defined(_PPC_)
|
|
PROCESSOR_ARCHITECTURE_PPC;
|
|
#elif 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|