1935 lines
48 KiB
C
1935 lines
48 KiB
C
|
#include "setupp.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
|
||
|
//
|
||
|
// Safe boot strings
|
||
|
//
|
||
|
|
||
|
#define SAFEBOOT_OPTION_KEY TEXT("System\\CurrentControlSet\\Control\\SafeBoot\\Option")
|
||
|
#define OPTION_VALUE TEXT("OptionValue")
|
||
|
|
||
|
VOID
|
||
|
SetUpProductTypeName(
|
||
|
OUT PWSTR ProductTypeString,
|
||
|
IN UINT BufferSizeChars
|
||
|
)
|
||
|
{
|
||
|
switch(ProductType) {
|
||
|
case PRODUCT_WORKSTATION:
|
||
|
lstrcpyn(ProductTypeString,L"WinNT",BufferSizeChars);
|
||
|
break;
|
||
|
case PRODUCT_SERVER_PRIMARY:
|
||
|
lstrcpyn(ProductTypeString,L"LanmanNT",BufferSizeChars);
|
||
|
break;
|
||
|
case PRODUCT_SERVER_STANDALONE:
|
||
|
lstrcpyn(ProductTypeString,L"ServerNT",BufferSizeChars);
|
||
|
break;
|
||
|
default:
|
||
|
LoadString(MyModuleHandle,IDS_UNKNOWN,ProductTypeString,BufferSizeChars);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
HMODULE
|
||
|
MyLoadLibraryWithSignatureCheck(
|
||
|
IN PWSTR ModuleName
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
verifies the signature for a dll and if it's ok, then load the dll
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
ModuleName - filename to be loaded.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
an HMODULE on success, else NULL
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
WCHAR FullModuleName[MAX_PATH];
|
||
|
PWSTR p;
|
||
|
DWORD error;
|
||
|
|
||
|
if (!GetFullPathName(ModuleName,MAX_PATH,FullModuleName,&p)) {
|
||
|
//
|
||
|
// couldn't get full path to file
|
||
|
//
|
||
|
SetupDebugPrint1( L"Setup: MyLoadLibraryWithSignatureCheck failed GetFullPathName, le = %d\n",
|
||
|
GetLastError() );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
error = pSetupVerifyFile(
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
0,
|
||
|
pSetupGetFileTitle(FullModuleName),
|
||
|
FullModuleName,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
FALSE,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL );
|
||
|
|
||
|
if (NO_ERROR != error) {
|
||
|
//
|
||
|
// signing problem
|
||
|
//
|
||
|
SetupDebugPrint1( L"Setup: MyLoadLibraryWithSignatureCheck failed pSetupVerifyFile, le = %x\n",
|
||
|
error );
|
||
|
SetLastError(error);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return (LoadLibrary(FullModuleName));
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
UINT
|
||
|
MyGetDriveType(
|
||
|
IN WCHAR Drive
|
||
|
)
|
||
|
{
|
||
|
WCHAR DriveNameNt[] = L"\\\\.\\?:";
|
||
|
WCHAR DriveName[] = L"?:\\";
|
||
|
HANDLE hDisk;
|
||
|
BOOL b;
|
||
|
UINT rc;
|
||
|
DWORD DataSize;
|
||
|
DISK_GEOMETRY MediaInfo;
|
||
|
|
||
|
//
|
||
|
// First, get the win32 drive type. If it tells us DRIVE_REMOVABLE,
|
||
|
// then we need to see whether it's a floppy or hard disk. Otherwise
|
||
|
// just believe the api.
|
||
|
//
|
||
|
DriveName[0] = Drive;
|
||
|
if((rc = GetDriveType(DriveName)) == DRIVE_REMOVABLE) {
|
||
|
|
||
|
DriveNameNt[4] = Drive;
|
||
|
|
||
|
hDisk = CreateFile(
|
||
|
DriveNameNt,
|
||
|
FILE_READ_ATTRIBUTES,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
0,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if(hDisk != INVALID_HANDLE_VALUE) {
|
||
|
|
||
|
b = DeviceIoControl(
|
||
|
hDisk,
|
||
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
||
|
NULL,
|
||
|
0,
|
||
|
&MediaInfo,
|
||
|
sizeof(MediaInfo),
|
||
|
&DataSize,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// It's really a hard disk if the media type is removable.
|
||
|
//
|
||
|
if(b && (MediaInfo.MediaType == RemovableMedia)) {
|
||
|
rc = DRIVE_FIXED;
|
||
|
}
|
||
|
|
||
|
CloseHandle(hDisk);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(rc);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
GetPartitionInfo(
|
||
|
IN WCHAR Drive,
|
||
|
OUT PPARTITION_INFORMATION PartitionInfo
|
||
|
)
|
||
|
{
|
||
|
WCHAR DriveName[] = L"\\\\.\\?:";
|
||
|
HANDLE hDisk;
|
||
|
BOOL b;
|
||
|
DWORD DataSize;
|
||
|
|
||
|
DriveName[4] = Drive;
|
||
|
|
||
|
hDisk = CreateFile(
|
||
|
DriveName,
|
||
|
GENERIC_READ,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
0,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if(hDisk == INVALID_HANDLE_VALUE) {
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
b = DeviceIoControl(
|
||
|
hDisk,
|
||
|
IOCTL_DISK_GET_PARTITION_INFO,
|
||
|
NULL,
|
||
|
0,
|
||
|
PartitionInfo,
|
||
|
sizeof(PARTITION_INFORMATION),
|
||
|
&DataSize,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
CloseHandle(hDisk);
|
||
|
|
||
|
return(b);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
IsErrorLogEmpty (
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Checks to see if the error log is empty.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
TRUE if the error log size is zero.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
HANDLE ErrorLog;
|
||
|
WCHAR LogName[MAX_PATH];
|
||
|
DWORD Size = 0;
|
||
|
|
||
|
if( GetWindowsDirectory (LogName, MAX_PATH) ) {
|
||
|
pSetupConcatenatePaths (LogName, SETUPLOG_ERROR_FILENAME, MAX_PATH, NULL);
|
||
|
|
||
|
ErrorLog = CreateFile (
|
||
|
LogName,
|
||
|
GENERIC_READ,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if (ErrorLog != INVALID_HANDLE_VALUE) {
|
||
|
Size = GetFileSize (ErrorLog, NULL);
|
||
|
CloseHandle (ErrorLog);
|
||
|
}
|
||
|
}
|
||
|
return Size == 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
PumpMessageQueue(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
MSG msg;
|
||
|
|
||
|
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
|
||
|
DispatchMessage(&msg);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
PVOID
|
||
|
InitSysSetupQueueCallbackEx(
|
||
|
IN HWND OwnerWindow,
|
||
|
IN HWND AlternateProgressWindow, OPTIONAL
|
||
|
IN UINT ProgressMessage,
|
||
|
IN DWORD Reserved1,
|
||
|
IN PVOID Reserved2
|
||
|
)
|
||
|
{
|
||
|
PSYSSETUP_QUEUE_CONTEXT SysSetupContext;
|
||
|
|
||
|
SysSetupContext = MyMalloc(sizeof(SYSSETUP_QUEUE_CONTEXT));
|
||
|
|
||
|
if(SysSetupContext) {
|
||
|
|
||
|
SysSetupContext->Skipped = FALSE;
|
||
|
|
||
|
SysSetupContext->DefaultContext = SetupInitDefaultQueueCallbackEx(
|
||
|
OwnerWindow,
|
||
|
AlternateProgressWindow,
|
||
|
ProgressMessage,
|
||
|
Reserved1,
|
||
|
Reserved2
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return SysSetupContext;
|
||
|
}
|
||
|
|
||
|
|
||
|
PVOID
|
||
|
InitSysSetupQueueCallback(
|
||
|
IN HWND OwnerWindow
|
||
|
)
|
||
|
{
|
||
|
return(InitSysSetupQueueCallbackEx(OwnerWindow,NULL,0,0,NULL));
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
TermSysSetupQueueCallback(
|
||
|
IN PVOID SysSetupContext
|
||
|
)
|
||
|
{
|
||
|
PSYSSETUP_QUEUE_CONTEXT Context = SysSetupContext;
|
||
|
|
||
|
try {
|
||
|
if(Context->DefaultContext) {
|
||
|
SetupTermDefaultQueueCallback(Context->DefaultContext);
|
||
|
}
|
||
|
MyFree(Context);
|
||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
UINT
|
||
|
VersionCheckQueueCallback(
|
||
|
IN PVOID Context,
|
||
|
IN UINT Notification,
|
||
|
IN UINT_PTR Param1,
|
||
|
IN UINT_PTR Param2
|
||
|
)
|
||
|
{
|
||
|
PFILEPATHS FilePaths = (PFILEPATHS)Param1;
|
||
|
|
||
|
//
|
||
|
// If we're being notified that a version mismatch was found,
|
||
|
// indicate that the file shouldn't be copied. Otherwise,
|
||
|
// pass the notification on.
|
||
|
//
|
||
|
if((Notification & (SPFILENOTIFY_LANGMISMATCH |
|
||
|
SPFILENOTIFY_TARGETNEWER |
|
||
|
SPFILENOTIFY_TARGETEXISTS)) != 0) {
|
||
|
|
||
|
SetuplogError(
|
||
|
LogSevInformation,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
, // MSG_LOG_VERSION_MISMATCH, This message is no longer appropriate.
|
||
|
FilePaths->Source,
|
||
|
FilePaths->Target,
|
||
|
NULL,NULL);
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Want default processing.
|
||
|
//
|
||
|
return(SysSetupQueueCallback(Context,Notification,Param1,Param2));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
UINT
|
||
|
SysSetupQueueCallback(
|
||
|
IN PVOID Context,
|
||
|
IN UINT Notification,
|
||
|
IN UINT_PTR Param1,
|
||
|
IN UINT_PTR Param2
|
||
|
)
|
||
|
{
|
||
|
UINT Status;
|
||
|
PSYSSETUP_QUEUE_CONTEXT SysSetupContext = Context;
|
||
|
PFILEPATHS FilePaths = (PFILEPATHS)Param1;
|
||
|
PSOURCE_MEDIA SourceMedia = (PSOURCE_MEDIA)Param1;
|
||
|
REGISTRATION_CONTEXT RegistrationContext;
|
||
|
|
||
|
|
||
|
//
|
||
|
// If we're being notified that a file is missing and we're supposed
|
||
|
// to skip missing files, then return skip. Otherwise pass it on
|
||
|
// to the default callback routine.
|
||
|
//
|
||
|
if(( (Notification == SPFILENOTIFY_COPYERROR) || (Notification == SPFILENOTIFY_NEEDMEDIA) ) && SkipMissingFiles) {
|
||
|
|
||
|
if((FilePaths->Win32Error == ERROR_FILE_NOT_FOUND)
|
||
|
|| (FilePaths->Win32Error == ERROR_PATH_NOT_FOUND)) {
|
||
|
|
||
|
if(Notification == SPFILENOTIFY_COPYERROR)
|
||
|
SetuplogError(
|
||
|
LogSevWarning,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_FILE_COPY_ERROR,
|
||
|
FilePaths->Source,
|
||
|
FilePaths->Target, NULL,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
FilePaths->Win32Error,
|
||
|
NULL,NULL);
|
||
|
else
|
||
|
SetuplogError(
|
||
|
LogSevError,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_NEEDMEDIA_SKIP,
|
||
|
SourceMedia->SourceFile,
|
||
|
SourceMedia->SourcePath,
|
||
|
NULL,NULL);
|
||
|
|
||
|
|
||
|
return(FILEOP_SKIP);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((Notification == SPFILENOTIFY_COPYERROR
|
||
|
|| Notification == SPFILENOTIFY_RENAMEERROR
|
||
|
|| Notification == SPFILENOTIFY_DELETEERROR) &&
|
||
|
(FilePaths->Win32Error == ERROR_DIRECTORY)) {
|
||
|
WCHAR Buffer[MAX_PATH];
|
||
|
PWSTR p;
|
||
|
//
|
||
|
// The target directory has been converted into a file by autochk.
|
||
|
// just delete it -- we might be in trouble if the target directory was
|
||
|
// really important, but it's worth trying
|
||
|
//
|
||
|
|
||
|
wcscpy( Buffer,FilePaths->Target);
|
||
|
p = wcsrchr(Buffer,L'\\');
|
||
|
if (p) {
|
||
|
*p = (WCHAR)NULL;
|
||
|
}
|
||
|
if (FileExists(Buffer,NULL)) {
|
||
|
DeleteFile( Buffer );
|
||
|
SetupDebugPrint1(L"autochk turned directory %s into file, delete file and retry\n", Buffer);
|
||
|
return(FILEOP_RETRY);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we're being notified that a version mismatch was found,
|
||
|
// silently overwrite the file. Otherwise, pass the notification on.
|
||
|
//
|
||
|
if((Notification & (SPFILENOTIFY_LANGMISMATCH |
|
||
|
SPFILENOTIFY_TARGETNEWER |
|
||
|
SPFILENOTIFY_TARGETEXISTS)) != 0) {
|
||
|
|
||
|
SetuplogError(
|
||
|
LogSevInformation,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_VERSION_MISMATCH,
|
||
|
FilePaths->Source,
|
||
|
FilePaths->Target,
|
||
|
NULL,NULL);
|
||
|
|
||
|
return(FILEOP_DOIT);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Use default processing, then check for errors.
|
||
|
//
|
||
|
Status = SetupDefaultQueueCallback(
|
||
|
SysSetupContext->DefaultContext,Notification,Param1,Param2);
|
||
|
|
||
|
switch(Notification) {
|
||
|
|
||
|
case SPFILENOTIFY_STARTQUEUE:
|
||
|
case SPFILENOTIFY_STARTSUBQUEUE:
|
||
|
case SPFILENOTIFY_ENDSUBQUEUE:
|
||
|
//
|
||
|
// Nothing is logged in this case.
|
||
|
//
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_ENDQUEUE:
|
||
|
|
||
|
if(!Param1) {
|
||
|
SetuplogError(
|
||
|
LogSevInformation,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_QUEUE_ABORT, NULL,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
GetLastError(),
|
||
|
NULL,NULL);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_STARTRENAME:
|
||
|
|
||
|
if(Status == FILEOP_SKIP) {
|
||
|
SysSetupContext->Skipped = TRUE;
|
||
|
} else {
|
||
|
SysSetupContext->Skipped = FALSE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_ENDRENAME:
|
||
|
|
||
|
if(FilePaths->Win32Error == NO_ERROR &&
|
||
|
!SysSetupContext->Skipped) {
|
||
|
|
||
|
SetuplogError(
|
||
|
LogSevInformation,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_FILE_RENAMED,
|
||
|
FilePaths->Source,
|
||
|
FilePaths->Target,
|
||
|
NULL,NULL);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
SetuplogError(
|
||
|
LogSevError,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_FILE_RENAME_ERROR,
|
||
|
FilePaths->Source,
|
||
|
FilePaths->Target, NULL,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
FilePaths->Win32Error == NO_ERROR ?
|
||
|
MSG_LOG_USER_SKIP :
|
||
|
FilePaths->Win32Error,
|
||
|
NULL,NULL);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_RENAMEERROR:
|
||
|
|
||
|
if(Status == FILEOP_SKIP) {
|
||
|
SysSetupContext->Skipped = TRUE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_STARTDELETE:
|
||
|
|
||
|
if(Status == FILEOP_SKIP) {
|
||
|
SysSetupContext->Skipped = TRUE;
|
||
|
} else {
|
||
|
SysSetupContext->Skipped = FALSE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_ENDDELETE:
|
||
|
|
||
|
if(FilePaths->Win32Error == NO_ERROR &&
|
||
|
!SysSetupContext->Skipped) {
|
||
|
|
||
|
SetuplogError(
|
||
|
LogSevInformation,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_FILE_DELETED,
|
||
|
FilePaths->Target,
|
||
|
NULL,NULL);
|
||
|
|
||
|
} else if(FilePaths->Win32Error == ERROR_FILE_NOT_FOUND ||
|
||
|
FilePaths->Win32Error == ERROR_PATH_NOT_FOUND) {
|
||
|
//
|
||
|
// This failure is not important.
|
||
|
//
|
||
|
SetuplogError(
|
||
|
LogSevInformation,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_FILE_DELETE_ERROR,
|
||
|
FilePaths->Target, NULL,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
FilePaths->Win32Error,
|
||
|
NULL,NULL);
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// Here we have an actual error.
|
||
|
//
|
||
|
SetuplogError(
|
||
|
LogSevError,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_FILE_DELETE_ERROR,
|
||
|
FilePaths->Target, NULL,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
FilePaths->Win32Error == NO_ERROR ?
|
||
|
MSG_LOG_USER_SKIP :
|
||
|
FilePaths->Win32Error,
|
||
|
NULL,NULL);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_DELETEERROR:
|
||
|
|
||
|
if(Status == FILEOP_SKIP) {
|
||
|
SysSetupContext->Skipped = TRUE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_STARTCOPY:
|
||
|
if(Status == FILEOP_SKIP) {
|
||
|
SysSetupContext->Skipped = TRUE;
|
||
|
} else {
|
||
|
SysSetupContext->Skipped = FALSE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_ENDCOPY:
|
||
|
|
||
|
if(FilePaths->Win32Error == NO_ERROR &&
|
||
|
!SysSetupContext->Skipped) {
|
||
|
|
||
|
LogRepairInfo(
|
||
|
FilePaths->Source,
|
||
|
FilePaths->Target
|
||
|
);
|
||
|
|
||
|
SetuplogError(
|
||
|
LogSevInformation,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_FILE_COPIED,
|
||
|
FilePaths->Source,
|
||
|
FilePaths->Target,
|
||
|
NULL,NULL);
|
||
|
|
||
|
//
|
||
|
// clear the file's readonly attribute that it may have gotten
|
||
|
// from the cdrom.
|
||
|
//
|
||
|
SetFileAttributes(
|
||
|
FilePaths->Target,
|
||
|
GetFileAttributes(FilePaths->Target) & ~FILE_ATTRIBUTE_READONLY );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
SetuplogError(
|
||
|
LogSevError,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_FILE_COPY_ERROR,
|
||
|
FilePaths->Source,
|
||
|
FilePaths->Target, NULL,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
FilePaths->Win32Error == NO_ERROR ?
|
||
|
MSG_LOG_USER_SKIP :
|
||
|
FilePaths->Win32Error,
|
||
|
NULL,NULL);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_COPYERROR:
|
||
|
|
||
|
if(Status == FILEOP_SKIP) {
|
||
|
SysSetupContext->Skipped = TRUE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_NEEDMEDIA:
|
||
|
|
||
|
if(Status == FILEOP_SKIP) {
|
||
|
|
||
|
SetuplogError(
|
||
|
LogSevError,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_NEEDMEDIA_SKIP,
|
||
|
SourceMedia->SourceFile,
|
||
|
SourceMedia->SourcePath,
|
||
|
NULL,NULL);
|
||
|
|
||
|
SysSetupContext->Skipped = TRUE;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case SPFILENOTIFY_STARTREGISTRATION:
|
||
|
case SPFILENOTIFY_ENDREGISTRATION:
|
||
|
RtlZeroMemory(&RegistrationContext,sizeof(RegistrationContext));
|
||
|
RegistrationQueueCallback(
|
||
|
&RegistrationContext,
|
||
|
Notification,
|
||
|
Param1,
|
||
|
Param2);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
PSID
|
||
|
GetAdminAccountSid(
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
===============================================================================
|
||
|
Routine Description:
|
||
|
|
||
|
This routine gets the Adminstrator's SID
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE - success.
|
||
|
|
||
|
FALSE - failed.
|
||
|
|
||
|
===============================================================================
|
||
|
--*/
|
||
|
{
|
||
|
BOOL b = TRUE;
|
||
|
LSA_HANDLE hPolicy;
|
||
|
NTSTATUS ntStatus;
|
||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
|
||
|
UCHAR SubAuthCount;
|
||
|
DWORD sidlen;
|
||
|
PSID psid = NULL;
|
||
|
|
||
|
|
||
|
InitializeObjectAttributes( &ObjectAttributes,
|
||
|
NULL,
|
||
|
0L,
|
||
|
NULL,
|
||
|
NULL );
|
||
|
|
||
|
ntStatus = LsaOpenPolicy( NULL,
|
||
|
&ObjectAttributes,
|
||
|
POLICY_ALL_ACCESS,
|
||
|
&hPolicy );
|
||
|
|
||
|
if (!NT_SUCCESS(ntStatus)) {
|
||
|
LsaClose(hPolicy);
|
||
|
b = FALSE;
|
||
|
}
|
||
|
|
||
|
if( b ) {
|
||
|
ntStatus = LsaQueryInformationPolicy( hPolicy,
|
||
|
PolicyAccountDomainInformation,
|
||
|
(PVOID *) &AccountDomainInfo );
|
||
|
|
||
|
LsaClose( hPolicy );
|
||
|
|
||
|
if (!NT_SUCCESS(ntStatus)) {
|
||
|
if ( AccountDomainInfo != NULL ) {
|
||
|
(VOID) LsaFreeMemory( AccountDomainInfo );
|
||
|
}
|
||
|
b = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( b ) {
|
||
|
//
|
||
|
// calculate the size of a new sid with one more SubAuthority
|
||
|
//
|
||
|
SubAuthCount = *(GetSidSubAuthorityCount ( AccountDomainInfo->DomainSid ));
|
||
|
SubAuthCount++; // for admin
|
||
|
sidlen = GetSidLengthRequired ( SubAuthCount );
|
||
|
|
||
|
//
|
||
|
// allocate and copy the new new sid from the Domain SID
|
||
|
//
|
||
|
psid = (PSID)malloc(sidlen);
|
||
|
|
||
|
if( psid ) {
|
||
|
|
||
|
memcpy(psid, AccountDomainInfo->DomainSid, GetLengthSid(AccountDomainInfo->DomainSid) );
|
||
|
|
||
|
//
|
||
|
// increment SubAuthority count and add Domain Admin RID
|
||
|
//
|
||
|
*(GetSidSubAuthorityCount( psid )) = SubAuthCount;
|
||
|
*(GetSidSubAuthority( psid, SubAuthCount-1 )) = DOMAIN_USER_RID_ADMIN;
|
||
|
|
||
|
if ( AccountDomainInfo != NULL ) {
|
||
|
(VOID) LsaFreeMemory( AccountDomainInfo );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return psid;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
GetAdminAccountName(
|
||
|
PWSTR AccountName
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
===============================================================================
|
||
|
Routine Description:
|
||
|
|
||
|
This routine sets the Adminstrator Password
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE - success.
|
||
|
|
||
|
FALSE - failed.
|
||
|
|
||
|
===============================================================================
|
||
|
--*/
|
||
|
{
|
||
|
BOOL b = TRUE;
|
||
|
LSA_HANDLE hPolicy;
|
||
|
NTSTATUS ntStatus;
|
||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
|
||
|
UCHAR SubAuthCount;
|
||
|
DWORD sidlen;
|
||
|
PSID psid;
|
||
|
WCHAR adminname[512];
|
||
|
WCHAR domainname[512];
|
||
|
DWORD adminlen=512; // address of size account string
|
||
|
DWORD domlen=512; // address of size account string
|
||
|
SID_NAME_USE sidtype;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Initialize the administrator's account name.
|
||
|
//
|
||
|
LoadString(MyModuleHandle,IDS_ADMINISTRATOR,adminname,MAX_USERNAME+1);
|
||
|
|
||
|
InitializeObjectAttributes( &ObjectAttributes,
|
||
|
NULL,
|
||
|
0L,
|
||
|
NULL,
|
||
|
NULL );
|
||
|
|
||
|
ntStatus = LsaOpenPolicy( NULL,
|
||
|
&ObjectAttributes,
|
||
|
POLICY_ALL_ACCESS,
|
||
|
&hPolicy );
|
||
|
|
||
|
if (!NT_SUCCESS(ntStatus)) {
|
||
|
LsaClose(hPolicy);
|
||
|
b = FALSE;
|
||
|
}
|
||
|
|
||
|
if( b ) {
|
||
|
ntStatus = LsaQueryInformationPolicy( hPolicy,
|
||
|
PolicyAccountDomainInformation,
|
||
|
(PVOID *) &AccountDomainInfo );
|
||
|
|
||
|
LsaClose( hPolicy );
|
||
|
|
||
|
if (!NT_SUCCESS(ntStatus)) {
|
||
|
if ( AccountDomainInfo != NULL ) {
|
||
|
(VOID) LsaFreeMemory( AccountDomainInfo );
|
||
|
}
|
||
|
b = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( b ) {
|
||
|
//
|
||
|
// calculate the size of a new sid with one more SubAuthority
|
||
|
//
|
||
|
SubAuthCount = *(GetSidSubAuthorityCount ( AccountDomainInfo->DomainSid ));
|
||
|
SubAuthCount++; // for admin
|
||
|
sidlen = GetSidLengthRequired ( SubAuthCount );
|
||
|
|
||
|
//
|
||
|
// allocate and copy the new new sid from the Domain SID
|
||
|
//
|
||
|
psid = (PSID)malloc(sidlen);
|
||
|
if (psid) {
|
||
|
memcpy(psid, AccountDomainInfo->DomainSid, GetLengthSid(AccountDomainInfo->DomainSid) );
|
||
|
|
||
|
//
|
||
|
// increment SubAuthority count and add Domain Admin RID
|
||
|
//
|
||
|
*(GetSidSubAuthorityCount( psid )) = SubAuthCount;
|
||
|
*(GetSidSubAuthority( psid, SubAuthCount-1 )) = DOMAIN_USER_RID_ADMIN;
|
||
|
|
||
|
if ( AccountDomainInfo != NULL ) {
|
||
|
(VOID) LsaFreeMemory( AccountDomainInfo );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// get the admin account name from the new SID
|
||
|
//
|
||
|
LookupAccountSid( NULL,
|
||
|
psid,
|
||
|
adminname,
|
||
|
&adminlen,
|
||
|
domainname,
|
||
|
&domlen,
|
||
|
&sidtype );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
lstrcpy( AccountName, adminname );
|
||
|
|
||
|
if (psid) {
|
||
|
free(psid);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
GetBatteryTag (HANDLE DriverHandle)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
IO_STATUS_BLOCK IOSB;
|
||
|
ULONG BatteryTag;
|
||
|
|
||
|
Status = NtDeviceIoControlFile(
|
||
|
DriverHandle,
|
||
|
(HANDLE) NULL, // event
|
||
|
(PIO_APC_ROUTINE) NULL,
|
||
|
(PVOID) NULL,
|
||
|
&IOSB,
|
||
|
IOCTL_BATTERY_QUERY_TAG,
|
||
|
NULL, // input buffer
|
||
|
0,
|
||
|
&BatteryTag, // output buffer
|
||
|
sizeof (BatteryTag)
|
||
|
);
|
||
|
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
BatteryTag = BATTERY_TAG_INVALID;
|
||
|
if (Status == STATUS_NO_SUCH_DEVICE) {
|
||
|
SetupDebugPrint(L"(Battery is not physically present or is not connected)\n");
|
||
|
} else {
|
||
|
SetupDebugPrint1(L"Query Battery tag failed: Status = %x\n", Status);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return BatteryTag;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
GetBatteryInfo (
|
||
|
HANDLE DriverHandle,
|
||
|
ULONG BatteryTag,
|
||
|
IN BATTERY_QUERY_INFORMATION_LEVEL Level,
|
||
|
OUT PVOID Buffer,
|
||
|
IN ULONG BufferLength
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
IO_STATUS_BLOCK IOSB;
|
||
|
BATTERY_QUERY_INFORMATION BInfo;
|
||
|
|
||
|
memset (Buffer, 0, BufferLength);
|
||
|
BInfo.BatteryTag = BatteryTag;
|
||
|
BInfo.InformationLevel = Level;
|
||
|
BInfo.AtRate = 0; // This is needed for reading estimated time correctly.
|
||
|
|
||
|
Status = NtDeviceIoControlFile(
|
||
|
DriverHandle,
|
||
|
(HANDLE) NULL, // event
|
||
|
(PIO_APC_ROUTINE) NULL,
|
||
|
(PVOID) NULL,
|
||
|
&IOSB,
|
||
|
IOCTL_BATTERY_QUERY_INFORMATION,
|
||
|
&BInfo, // input buffer
|
||
|
sizeof (BInfo),
|
||
|
Buffer, // output buffer
|
||
|
BufferLength
|
||
|
);
|
||
|
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
|
||
|
if ((Status == STATUS_INVALID_PARAMETER) ||
|
||
|
(Status == STATUS_INVALID_DEVICE_REQUEST) ||
|
||
|
(Status == STATUS_NOT_SUPPORTED)) {
|
||
|
|
||
|
SetupDebugPrint2(L"Not Supported by Battery, Level %x, Status: %x\n", Level, Status);
|
||
|
} else {
|
||
|
SetupDebugPrint2(L"Query failed: Level %x, Status = %x\n", Level, Status);
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
IsLongTermBattery(
|
||
|
PCWSTR BatteryName
|
||
|
)
|
||
|
{
|
||
|
HANDLE driverHandle;
|
||
|
ULONG batteryTag;
|
||
|
BATTERY_INFORMATION BInfo;
|
||
|
BOOLEAN Ret;
|
||
|
|
||
|
|
||
|
driverHandle = CreateFile (BatteryName,
|
||
|
GENERIC_READ | GENERIC_WRITE,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL,
|
||
|
NULL);
|
||
|
|
||
|
if (INVALID_HANDLE_VALUE == driverHandle) {
|
||
|
|
||
|
SetupDebugPrint2(L"Error opening %ws: GetLastError = 0x%08lx \n",
|
||
|
BatteryName, GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
batteryTag = GetBatteryTag (driverHandle);
|
||
|
if (batteryTag == BATTERY_TAG_INVALID) {
|
||
|
NtClose(driverHandle);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (GetBatteryInfo (driverHandle, batteryTag, BatteryInformation, &BInfo, sizeof(BInfo))) {
|
||
|
|
||
|
Ret = !(BInfo.Capabilities & BATTERY_IS_SHORT_TERM);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
Ret = FALSE;
|
||
|
}
|
||
|
|
||
|
NtClose(driverHandle);
|
||
|
return(Ret);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
IsLaptop(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
HDEVINFO devInfo;
|
||
|
SP_INTERFACE_DEVICE_DATA interfaceDevData;
|
||
|
PSP_INTERFACE_DEVICE_DETAIL_DATA funcClassDevData;
|
||
|
UCHAR index;
|
||
|
DWORD reqSize;
|
||
|
BOOLEAN b = FALSE;
|
||
|
|
||
|
|
||
|
devInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVICE_BATTERY, NULL, NULL,
|
||
|
DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
|
||
|
if (devInfo == INVALID_HANDLE_VALUE) {
|
||
|
SetupDebugPrint1(L"SetupDiGetClassDevs on GUID_DEVICE_BATTERY, failed: %d\n", GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
interfaceDevData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||
|
|
||
|
index = 0;
|
||
|
while(1) {
|
||
|
if (SetupDiEnumInterfaceDevice(devInfo,
|
||
|
0,
|
||
|
(LPGUID)&GUID_DEVICE_BATTERY,
|
||
|
index,
|
||
|
&interfaceDevData)) {
|
||
|
|
||
|
// Get the required size of the function class device data.
|
||
|
SetupDiGetInterfaceDeviceDetail(devInfo,
|
||
|
&interfaceDevData,
|
||
|
NULL,
|
||
|
0,
|
||
|
&reqSize,
|
||
|
NULL);
|
||
|
|
||
|
funcClassDevData = MyMalloc(reqSize);
|
||
|
if (funcClassDevData != NULL) {
|
||
|
funcClassDevData->cbSize =
|
||
|
sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
|
||
|
|
||
|
if (SetupDiGetInterfaceDeviceDetail(devInfo,
|
||
|
&interfaceDevData,
|
||
|
funcClassDevData,
|
||
|
reqSize,
|
||
|
&reqSize,
|
||
|
NULL)) {
|
||
|
|
||
|
b = IsLongTermBattery(funcClassDevData->DevicePath);
|
||
|
}
|
||
|
else {
|
||
|
SetupDebugPrint1(L"SetupDiGetInterfaceDeviceDetail, failed: %d\n", GetLastError());
|
||
|
}
|
||
|
|
||
|
MyFree(funcClassDevData);
|
||
|
if (b) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if (ERROR_NO_MORE_ITEMS == GetLastError()) {
|
||
|
break;
|
||
|
}
|
||
|
else {
|
||
|
SetupDebugPrint1(L"SetupDiEnumInterfaceDevice, failed: %d\n", GetLastError());
|
||
|
}
|
||
|
}
|
||
|
index++;
|
||
|
}
|
||
|
|
||
|
SetupDiDestroyDeviceInfoList(devInfo);
|
||
|
return b;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SaveInstallInfoIntoEventLog(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
Routine Description:
|
||
|
|
||
|
This routine will store information into the event log regarding
|
||
|
- if we upgraded or cleaninstall
|
||
|
- what build did the install originate from
|
||
|
- what build are we?
|
||
|
- were there errors during Setup
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
#define AnswerBufLen (64)
|
||
|
WCHAR AnswerFile[MAX_PATH];
|
||
|
WCHAR Answer[AnswerBufLen];
|
||
|
WCHAR OrigVersion[AnswerBufLen];
|
||
|
WCHAR NewVersion[AnswerBufLen];
|
||
|
HANDLE hEventSrc;
|
||
|
PCWSTR MyArgs[2];
|
||
|
PCWSTR ErrorArgs[1];
|
||
|
DWORD MessageID;
|
||
|
WORD MyArgCount;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Go get the starting information out of $winnt$.sif
|
||
|
//
|
||
|
OrigVersion[0] = L'0';
|
||
|
OrigVersion[1] = L'\0';
|
||
|
GetSystemDirectory(AnswerFile,MAX_PATH);
|
||
|
pSetupConcatenatePaths(AnswerFile,WINNT_GUI_FILE,MAX_PATH,NULL);
|
||
|
if( GetPrivateProfileString( WINNT_DATA,
|
||
|
WINNT_D_WIN32_VER,
|
||
|
pwNull,
|
||
|
Answer,
|
||
|
AnswerBufLen,
|
||
|
AnswerFile ) ) {
|
||
|
|
||
|
if( lstrcmp( pwNull, Answer ) ) {
|
||
|
|
||
|
wsprintf( OrigVersion, L"%d", HIWORD(wcstoul( Answer, NULL, 16 )) );
|
||
|
}
|
||
|
}
|
||
|
MyArgs[1] = OrigVersion;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Get the new version information.
|
||
|
//
|
||
|
wsprintf( NewVersion, L"%d", HIWORD(GetVersion()) );
|
||
|
MyArgs[0] = NewVersion;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// See if we're an NT upgrade?
|
||
|
//
|
||
|
MessageID = 0;
|
||
|
if( GetPrivateProfileString( WINNT_DATA,
|
||
|
WINNT_D_NTUPGRADE,
|
||
|
pwNo,
|
||
|
Answer,
|
||
|
AnswerBufLen,
|
||
|
AnswerFile ) ) {
|
||
|
if( !lstrcmp( pwYes, Answer ) ) {
|
||
|
|
||
|
MessageID = MSG_NTUPGRADE_SUCCESS;
|
||
|
MyArgCount = 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// See if we're a Win9X upgrade.
|
||
|
//
|
||
|
if( (!MessageID) &&
|
||
|
GetPrivateProfileString( WINNT_DATA,
|
||
|
WINNT_D_WIN95UPGRADE,
|
||
|
pwNo,
|
||
|
Answer,
|
||
|
AnswerBufLen,
|
||
|
AnswerFile ) ) {
|
||
|
if( !lstrcmp( pwYes, Answer ) ) {
|
||
|
|
||
|
MessageID = MSG_WIN9XUPGRADE_SUCCESS;
|
||
|
MyArgCount = 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Clean install.
|
||
|
//
|
||
|
if( (!MessageID) ) {
|
||
|
MessageID = MSG_CLEANINSTALL_SUCCESS;
|
||
|
MyArgCount = 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// If this is anything but an NT upgrade, then
|
||
|
// we need to go try and manually start the eventlog
|
||
|
// service.
|
||
|
//
|
||
|
if( MessageID != MSG_NTUPGRADE_SUCCESS ) {
|
||
|
SetupStartService( L"Eventlog", TRUE );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Get a handle to the eventlog.
|
||
|
//
|
||
|
hEventSrc = RegisterEventSource( NULL, L"Setup" );
|
||
|
|
||
|
if( (hEventSrc == NULL) ||
|
||
|
(hEventSrc == INVALID_HANDLE_VALUE) ) {
|
||
|
|
||
|
//
|
||
|
// Fail quietly.
|
||
|
//
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Log event message for failure of SceSetupRootSecurity
|
||
|
//
|
||
|
if( !bSceSetupRootSecurityComplete) {
|
||
|
ErrorArgs[0] = L"%windir%";
|
||
|
ReportEvent( hEventSrc,
|
||
|
EVENTLOG_WARNING_TYPE,
|
||
|
0,
|
||
|
MSG_LOG_SCE_SETUPROOT_ERROR,
|
||
|
NULL,
|
||
|
1,
|
||
|
0,
|
||
|
ErrorArgs,
|
||
|
NULL );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Log event if there were errors during Setup.
|
||
|
//
|
||
|
if ( !IsErrorLogEmpty() ) {
|
||
|
ReportEvent( hEventSrc,
|
||
|
EVENTLOG_ERROR_TYPE,
|
||
|
0,
|
||
|
MSG_NONFATAL_ERRORS,
|
||
|
NULL,
|
||
|
0,
|
||
|
0,
|
||
|
NULL,
|
||
|
NULL );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Build the event log message.
|
||
|
//
|
||
|
ReportEvent( hEventSrc,
|
||
|
EVENTLOG_INFORMATION_TYPE,
|
||
|
0,
|
||
|
MessageID,
|
||
|
NULL,
|
||
|
MyArgCount,
|
||
|
0,
|
||
|
MyArgs,
|
||
|
NULL );
|
||
|
|
||
|
|
||
|
DeregisterEventSource( hEventSrc );
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
IsEncryptedAdminPasswordPresent( VOID )
|
||
|
{
|
||
|
|
||
|
#define MD4HASHLEN ((2*(LM_OWF_PASSWORD_LENGTH + NT_OWF_PASSWORD_LENGTH))+2)
|
||
|
WCHAR AnswerFile[MAX_PATH+2];
|
||
|
WCHAR Answer[MD4HASHLEN];
|
||
|
|
||
|
//
|
||
|
// Get EncryptedAdminPassword from the Answer file
|
||
|
//
|
||
|
GetSystemDirectory(AnswerFile,MAX_PATH);
|
||
|
pSetupConcatenatePaths(AnswerFile,WINNT_GUI_FILE,MAX_PATH,NULL);
|
||
|
|
||
|
if( GetPrivateProfileString( WINNT_GUIUNATTENDED,
|
||
|
WINNT_US_ENCRYPTEDADMINPASS,
|
||
|
pwNull,
|
||
|
Answer,
|
||
|
MD4HASHLEN,
|
||
|
AnswerFile ) ) {
|
||
|
|
||
|
//
|
||
|
// See if we have an encrypted password. Now interpret the Admin Password differently
|
||
|
//
|
||
|
|
||
|
if( !lstrcmpi( WINNT_A_YES, Answer ) )
|
||
|
return TRUE;
|
||
|
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
ProcessEncryptedAdminPassword( PCWSTR AdminAccountName )
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine looks in the unattend file to see if there is an encrypted password and if present
|
||
|
sets the admin password to that.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
AdminAccountName - Name of the administrator account whose password you want to set.
|
||
|
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Returns TRUE if it succeeds, FALSE on failure
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
#define MD4HASHLEN ((2*(LM_OWF_PASSWORD_LENGTH + NT_OWF_PASSWORD_LENGTH))+2)
|
||
|
WCHAR AnswerFile[MAX_PATH+2];
|
||
|
WCHAR Answer[MD4HASHLEN];
|
||
|
DWORD Err = NO_ERROR;
|
||
|
WCHAR adminName[MAX_USERNAME+1];
|
||
|
BOOLEAN ret = FALSE;
|
||
|
|
||
|
//
|
||
|
// Pickup the answer file.
|
||
|
//
|
||
|
GetSystemDirectory(AnswerFile,MAX_PATH);
|
||
|
pSetupConcatenatePaths(AnswerFile,WINNT_GUI_FILE,MAX_PATH,NULL);
|
||
|
|
||
|
|
||
|
//
|
||
|
// we look for the following keys in the [GUIUnattended] section:
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// EncryptedAdminPassword = Yes | No
|
||
|
// AdminPassword = <MD4 Hash value>
|
||
|
//
|
||
|
|
||
|
|
||
|
if( IsEncryptedAdminPasswordPresent() ) {
|
||
|
|
||
|
|
||
|
//Fetch the Encrypted Admin Password
|
||
|
|
||
|
if( GetPrivateProfileString( WINNT_GUIUNATTENDED,
|
||
|
WINNT_US_ADMINPASS,
|
||
|
pwNull,
|
||
|
Answer,
|
||
|
MD4HASHLEN,
|
||
|
AnswerFile ) == (MD4HASHLEN-2) ) {
|
||
|
|
||
|
Err = SetLocalUserEncryptedPassword( AdminAccountName, L"", FALSE, Answer, TRUE );
|
||
|
|
||
|
if( Err == ERROR_SUCCESS) {
|
||
|
ret = TRUE;
|
||
|
}else{
|
||
|
|
||
|
//Log the error - MSG_LOG_CHANGING_ENCRYPT_PW_FAIL
|
||
|
|
||
|
SetuplogError(
|
||
|
LogSevWarning,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_CHANGING_ENCRYPT_PW_FAIL,
|
||
|
AdminAccountName, NULL,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_X_PARAM_RETURNED_WINERR,
|
||
|
L"SetLocalUserEncryptedPassword",
|
||
|
Err,
|
||
|
AdminAccountName,
|
||
|
NULL,NULL);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
}else{
|
||
|
|
||
|
//Log that we had a bad encrypted password
|
||
|
|
||
|
SetuplogError(
|
||
|
LogSevError,
|
||
|
SETUPLOG_USE_MESSAGEID,
|
||
|
MSG_LOG_BAD_UNATTEND_PARAM,
|
||
|
WINNT_US_ADMINPASS,
|
||
|
WINNT_GUIUNATTENDED,
|
||
|
NULL,NULL);
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
FileExists(
|
||
|
IN PCTSTR FileName,
|
||
|
OUT PWIN32_FIND_DATA FindData OPTIONAL
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Determine if a file exists and is accessible.
|
||
|
Errormode is set (and then restored) so the user will not see
|
||
|
any pop-ups.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
FileName - supplies full path of file to check for existance.
|
||
|
|
||
|
FindData - if specified, receives find data for the file.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if the file exists and is accessible.
|
||
|
FALSE if not. GetLastError() returns extended error info.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
WIN32_FIND_DATA findData;
|
||
|
HANDLE FindHandle;
|
||
|
UINT OldMode;
|
||
|
DWORD Error;
|
||
|
|
||
|
OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
||
|
|
||
|
FindHandle = FindFirstFile(FileName,&findData);
|
||
|
if(FindHandle == INVALID_HANDLE_VALUE) {
|
||
|
Error = GetLastError();
|
||
|
} else {
|
||
|
FindClose(FindHandle);
|
||
|
if(FindData) {
|
||
|
*FindData = findData;
|
||
|
}
|
||
|
Error = NO_ERROR;
|
||
|
}
|
||
|
|
||
|
SetErrorMode(OldMode);
|
||
|
|
||
|
SetLastError(Error);
|
||
|
return (Error == NO_ERROR);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL IsSafeMode(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
LONG lStatus;
|
||
|
HKEY hk;
|
||
|
DWORD dwVal;
|
||
|
DWORD dwType;
|
||
|
DWORD dwSize;
|
||
|
|
||
|
lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, SAFEBOOT_OPTION_KEY, 0, KEY_QUERY_VALUE, &hk);
|
||
|
|
||
|
if(lStatus != ERROR_SUCCESS)
|
||
|
return FALSE;
|
||
|
|
||
|
dwSize = sizeof(dwVal);
|
||
|
lStatus = RegQueryValueExW(hk, OPTION_VALUE, NULL, &dwType, (LPBYTE) &dwVal, &dwSize);
|
||
|
RegCloseKey(hk);
|
||
|
return ERROR_SUCCESS == lStatus && REG_DWORD == dwType && dwVal != 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
SetupCrashRecovery(
|
||
|
VOID
|
||
|
)
|
||
|
/*
|
||
|
Setup the Crash Recovery stuff. This is implemented as RTL APIS
|
||
|
This call sets up the tracking file etc. Crash Recovery tracks boot and
|
||
|
shutdown and in the event of failures in either it will by default pick the
|
||
|
right advanced boot option.
|
||
|
|
||
|
*/
|
||
|
{
|
||
|
|
||
|
HANDLE BootStatusData;
|
||
|
NTSTATUS Status;
|
||
|
BOOLEAN Enabled;
|
||
|
UCHAR Timeout = 30; //default = 30 sec
|
||
|
WCHAR Buffer[10];
|
||
|
PSTR AnsiBuffer;
|
||
|
int p = 0;
|
||
|
|
||
|
//
|
||
|
// We enable the feature for Pro and Per. On Server SKUs
|
||
|
// we create the file but don't enable the feature by default.
|
||
|
//
|
||
|
|
||
|
|
||
|
if( ProductType == PRODUCT_WORKSTATION ){
|
||
|
Enabled = TRUE;
|
||
|
}else{
|
||
|
Enabled = FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For the fresh install case create the boot status data file
|
||
|
// and setup the default settings. In the case of an upgrade
|
||
|
// we will set the previous values if we find them in $winnt$.inf
|
||
|
// as textmode setup would have stored this away for us. If not we
|
||
|
// proceed as if we were fresh.
|
||
|
//
|
||
|
|
||
|
|
||
|
if( Upgrade ){
|
||
|
|
||
|
//Look for settings in $winnt$.inf
|
||
|
|
||
|
if( SpSetupLoadParameter( WINNT_D_CRASHRECOVERYENABLED, Buffer, sizeof(Buffer)/sizeof(WCHAR))){
|
||
|
if (_wcsicmp(Buffer, L"NO") == 0) {
|
||
|
Enabled = FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
//We do the below check also as we might have to migrate settings on server SKUs
|
||
|
//that have this enabled. By default they are disabled.
|
||
|
//
|
||
|
|
||
|
if (_wcsicmp(Buffer, L"YES") == 0) {
|
||
|
Enabled = TRUE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
Status = RtlLockBootStatusData( &BootStatusData );
|
||
|
|
||
|
// This is the first time or there was no file. Create it
|
||
|
|
||
|
if( !NT_SUCCESS( Status )){
|
||
|
Status = RtlCreateBootStatusDataFile();
|
||
|
|
||
|
if( !NT_SUCCESS( Status )){
|
||
|
SetuplogError(
|
||
|
LogSevWarning,
|
||
|
L"Setup: (non-critical error) Could not lock the Crash Recovery status file - (%1!x!)\n",
|
||
|
0,Status,NULL,NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Lock the file
|
||
|
|
||
|
Status = RtlLockBootStatusData( &BootStatusData );
|
||
|
if( !NT_SUCCESS( Status )){
|
||
|
SetupDebugPrint1( L"Setup: (non-critical error) Could not lock the Crash Recovery status file - (%x)\n", Status );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Status = RtlGetSetBootStatusData(
|
||
|
BootStatusData,
|
||
|
FALSE,
|
||
|
RtlBsdItemAabTimeout,
|
||
|
&Timeout,
|
||
|
sizeof(UCHAR),
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if( !NT_SUCCESS( Status )){
|
||
|
SetupDebugPrint1( L"Setup: (non-critical error) Could not set the Crash Recovery timeout - (%x)\n", Status );
|
||
|
goto SCR_done;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
Status = RtlGetSetBootStatusData(
|
||
|
BootStatusData,
|
||
|
FALSE,
|
||
|
RtlBsdItemAabEnabled,
|
||
|
&Enabled,
|
||
|
sizeof(BOOLEAN),
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if( !NT_SUCCESS( Status )){
|
||
|
SetupDebugPrint1( L"Setup: (non-critical error) Could not enable Crash Recovery - (%x)\n", Status );
|
||
|
}
|
||
|
|
||
|
SCR_done:
|
||
|
|
||
|
RtlUnlockBootStatusData( BootStatusData );
|
||
|
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
BuildFileListFromDir(
|
||
|
IN PCTSTR PathBase,
|
||
|
IN PCTSTR Directory OPTIONAL,
|
||
|
IN DWORD MustHaveAttrs OPTIONAL,
|
||
|
IN DWORD MustNotHaveAttrs OPTIONAL,
|
||
|
IN PFN_BUILD_FILE_LIST_CALLBACK Callback OPTIONAL,
|
||
|
OUT PLIST_ENTRY ListHead
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Builds a linked list containing the names (without path) of files present in a specified directory.
|
||
|
The files must meed a set of conditions as specified by the arguments.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
PathBase - The path to the directory to enumerate files in.
|
||
|
Directory - If not NULL or empty, it is appended to PathBase to form a complete path to the directory.
|
||
|
MustHaveAttrs - A set of attributes a file must have to be included on the list.
|
||
|
MustNotHaveAttrs - A set of attributes a file must not have to be included on the list.
|
||
|
Callback - If not NULL, this function will be called and the file will be included on the list only
|
||
|
if it returns TRUE.
|
||
|
ListHead - Pointer to the head of the list to be filled in.
|
||
|
|
||
|
Return value:
|
||
|
|
||
|
ERROR_SUCCESS on success, otherwise a Win32 error code. The list may not be empty even if the function fails; the caller
|
||
|
must always empty the list.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD Error = ERROR_SUCCESS;
|
||
|
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||
|
PTSTR szPath = NULL;
|
||
|
WIN32_FIND_DATA fd;
|
||
|
|
||
|
if(ListHead != NULL) {
|
||
|
InitializeListHead(ListHead);
|
||
|
}
|
||
|
|
||
|
if(NULL == PathBase || 0 == PathBase[0] || NULL == ListHead) {
|
||
|
Error = ERROR_INVALID_PARAMETER;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
szPath = (PTSTR) MyMalloc(MAX_PATH * sizeof(TCHAR));
|
||
|
|
||
|
if(NULL == szPath) {
|
||
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
_tcsncpy(szPath, PathBase, MAX_PATH - 1);
|
||
|
szPath[MAX_PATH - 1] = 0;
|
||
|
|
||
|
if(Directory != NULL && Directory[0] != 0) {
|
||
|
pSetupConcatenatePaths(szPath, Directory, MAX_PATH, NULL);
|
||
|
}
|
||
|
|
||
|
if(!pSetupConcatenatePaths(szPath, TEXT("\\*"), MAX_PATH, NULL)) {
|
||
|
Error = ERROR_BAD_PATHNAME;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
hFind = FindFirstFile(szPath, &fd);
|
||
|
|
||
|
if(INVALID_HANDLE_VALUE == hFind) {
|
||
|
Error = GetLastError();
|
||
|
|
||
|
if(ERROR_FILE_NOT_FOUND == Error || ERROR_PATH_NOT_FOUND == Error) {
|
||
|
Error = ERROR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We might need the dir later
|
||
|
//
|
||
|
(_tcsrchr(szPath, L'\\'))[0] = 0;
|
||
|
|
||
|
do {
|
||
|
if(_tcscmp(fd.cFileName, _T(".")) != 0 &&
|
||
|
_tcscmp(fd.cFileName, _T("..")) != 0 &&
|
||
|
(MustHaveAttrs & fd.dwFileAttributes) == MustHaveAttrs &&
|
||
|
0 == (MustNotHaveAttrs & fd.dwFileAttributes) &&
|
||
|
(NULL == Callback || Callback(szPath, fd.cFileName))) {
|
||
|
|
||
|
ULONG uLen;
|
||
|
PSTRING_LIST_ENTRY pElem = (PSTRING_LIST_ENTRY) MyMalloc(sizeof(STRING_LIST_ENTRY));
|
||
|
|
||
|
if(NULL == pElem) {
|
||
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
uLen = (_tcslen(fd.cFileName) + 1) * sizeof(TCHAR);
|
||
|
pElem->String = (PTSTR) MyMalloc(uLen);
|
||
|
|
||
|
if(NULL == pElem->String) {
|
||
|
MyFree(pElem);
|
||
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(pElem->String, fd.cFileName, uLen);
|
||
|
InsertTailList(ListHead, &pElem->Entry);
|
||
|
}
|
||
|
}
|
||
|
while(FindNextFile(hFind, &fd));
|
||
|
|
||
|
Error = GetLastError();
|
||
|
|
||
|
if(ERROR_NO_MORE_FILES == Error) {
|
||
|
Error = ERROR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
if(hFind != INVALID_HANDLE_VALUE) {
|
||
|
FindClose(hFind);
|
||
|
}
|
||
|
|
||
|
if(szPath != NULL) {
|
||
|
MyFree(szPath);
|
||
|
}
|
||
|
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
PSTRING_LIST_ENTRY
|
||
|
SearchStringInList(
|
||
|
IN PLIST_ENTRY ListHead,
|
||
|
IN PCTSTR String,
|
||
|
BOOL CaseSensitive
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine searches for a string in a string list. The string can be NULL or empty, in which
|
||
|
case the function returns the first entry in the list with a NULL or empty string.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
ListHead - Pointer to the head of the list to be searched.
|
||
|
String - Specifies the string to search for.
|
||
|
CaseSensitive - If TRUE, the search will be case sensitive.
|
||
|
|
||
|
Return value:
|
||
|
|
||
|
A pointer to the first entry containing the string, if found, or NULL otherwise.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if(ListHead != NULL)
|
||
|
{
|
||
|
PLIST_ENTRY pEntry;
|
||
|
ULONG uLen1 = (String != NULL ? _tcslen(String) : 0);
|
||
|
|
||
|
for(pEntry = ListHead->Flink; pEntry != ListHead; pEntry = pEntry->Flink) {
|
||
|
PSTRING_LIST_ENTRY pStringEntry;
|
||
|
ULONG uLen2;
|
||
|
pStringEntry = CONTAINING_RECORD(pEntry, STRING_LIST_ENTRY, Entry);
|
||
|
uLen2 = (pStringEntry->String != NULL ? _tcslen(pStringEntry->String) : 0);
|
||
|
|
||
|
if(uLen1 == uLen2) {
|
||
|
if(0 == uLen1 || 0 == (CaseSensitive ? _tcscmp : _tcsicmp)(String, pStringEntry->String)) {
|
||
|
return pStringEntry;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
LookupCatalogAttribute(
|
||
|
IN PCWSTR CatalogName,
|
||
|
IN PCWSTR Directory OPTIONAL,
|
||
|
IN PCWSTR AttributeName OPTIONAL,
|
||
|
IN PCWSTR AttributeValue OPTIONAL,
|
||
|
PBOOL Found
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function searches if a catalog has the specified attribute with the specified value.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
CatalogName - Name of the catalog to search. A path can be specified.
|
||
|
Directory - If specified, it is prepended to CatalogName to form the path to the catalog.
|
||
|
AttributeName - See AttributeValue.
|
||
|
AttributeValue - If AttributeName and AttributeValue are not specified, the catalog meets the condition.
|
||
|
If AttributeName is specified and AttributeValue isn't, the catalog meets the condition if
|
||
|
it contains an attribute with AttributeName name and any value. If AttributeName is not
|
||
|
specified and AttributeValue is, the catalog meets the condition if it contains an attribute
|
||
|
with AttributeValue value and any name. If both AttributeName and AttributeValue are
|
||
|
specified, the catalog meets the condition if it contains an attribute with AttributeName name
|
||
|
and AttributeValue value.
|
||
|
Found - Pointer to a variable that receives TRUE if the catalog meets the condition, or FALSE otherwise.
|
||
|
|
||
|
Return value:
|
||
|
|
||
|
ERROR_SUCCESS if successful, otherwise a Win32 error code.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD Error = ERROR_SUCCESS;
|
||
|
HANDLE hCat = INVALID_HANDLE_VALUE;
|
||
|
PWSTR szCatPath = NULL;
|
||
|
CRYPTCATATTRIBUTE* pAttr;
|
||
|
|
||
|
if(Found != NULL) {
|
||
|
*Found = FALSE;
|
||
|
}
|
||
|
|
||
|
if(NULL == CatalogName || 0 == CatalogName[0] || NULL == Found) {
|
||
|
Error = ERROR_INVALID_PARAMETER;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
if(Directory != NULL && Directory[0] != 0) {
|
||
|
szCatPath = MyMalloc(MAX_PATH * sizeof(WCHAR));
|
||
|
|
||
|
if(NULL == szCatPath) {
|
||
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
_tcsncpy(szCatPath, Directory, MAX_PATH - 1);
|
||
|
szCatPath[MAX_PATH - 1] = 0;
|
||
|
pSetupConcatenatePaths(szCatPath, CatalogName, MAX_PATH, NULL);
|
||
|
CatalogName = szCatPath;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This is easier to test
|
||
|
//
|
||
|
if(AttributeName != NULL && 0 == AttributeName[0]) {
|
||
|
AttributeName = NULL;
|
||
|
}
|
||
|
|
||
|
if(AttributeValue != NULL && 0 == AttributeValue[0]) {
|
||
|
AttributeValue = NULL;
|
||
|
}
|
||
|
|
||
|
if(NULL == AttributeName && NULL == AttributeValue) {
|
||
|
//
|
||
|
// If attribute name and value are not specified, any catalog is a match
|
||
|
//
|
||
|
*Found = TRUE;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
hCat = CryptCATOpen((PWSTR) CatalogName, CRYPTCAT_OPEN_EXISTING, 0, 0, 0);
|
||
|
|
||
|
if(INVALID_HANDLE_VALUE == hCat) {
|
||
|
Error = GetLastError();
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
pAttr = CryptCATEnumerateCatAttr(hCat, NULL);
|
||
|
|
||
|
while(pAttr != NULL) {
|
||
|
*Found = (NULL == AttributeName || 0 == _wcsicmp(AttributeName, pAttr->pwszReferenceTag)) &&
|
||
|
(NULL == AttributeValue || 0 == _wcsicmp(AttributeName, (PCWSTR) pAttr->pbValue));
|
||
|
|
||
|
if(*Found) {
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
pAttr = CryptCATEnumerateCatAttr(hCat, pAttr);
|
||
|
}
|
||
|
|
||
|
Error = GetLastError();
|
||
|
|
||
|
if(CRYPT_E_NOT_FOUND == Error) {
|
||
|
Error = ERROR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
if(szCatPath != NULL) {
|
||
|
MyFree(szCatPath);
|
||
|
}
|
||
|
|
||
|
if(hCat != INVALID_HANDLE_VALUE) {
|
||
|
CryptCATClose(hCat);
|
||
|
}
|
||
|
|
||
|
return Error;
|
||
|
}
|