|
|
/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
validate.c
Abstract:
Code to validate an uninstall image
Author:
Jim Schmidt (jimschm) 19-Jan-2001
Revision History:
<alias> <date> <comments>
--*/
#include "pch.h"
#include "undop.h"
#include "file.h"
#include "persist.h"
#include "uninstall.h"
//
// Contants
//
#define MAX_BACKUP_FILES 3
#define _SECOND ((__int64) 10000000)
#define _MINUTE (60 * _SECOND)
#define _HOUR (60 * _MINUTE)
#define _DAY (24 * _HOUR)
PERSISTENCE_IMPLEMENTATION(DRIVE_LAYOUT_INFORMATION_EX_PERSISTENCE); PERSISTENCE_IMPLEMENTATION(DISKINFO_PERSISTENCE); PERSISTENCE_IMPLEMENTATION(DRIVEINFO_PERSISTENCE); PERSISTENCE_IMPLEMENTATION(FILEINTEGRITYINFO_PERSISTENCE); PERSISTENCE_IMPLEMENTATION(BACKUPIMAGEINFO_PERSISTENCE);
//
// Code
//
PCTSTR GetUndoDirPath ( VOID )
/*++
Routine Description:
GetUndoDirPath queries the registry and obtains the stored backup path.
Arguments:
None.
Return Value:
The backup path, which must be freed with MemFree, or NULL if the backup path is not stored in the registry.
--*/
{ PCTSTR backUpPath = NULL; HKEY key;
key = OpenRegKeyStr (S_REGKEY_WIN_SETUP); if (key) { backUpPath = GetRegValueString (key, S_REG_KEY_UNDO_PATH); CloseRegKey (key); }
return backUpPath; }
BOOL pIsUserAdmin ( VOID )
/*++
Routine Description:
This routine returns TRUE if the caller's process is a member of the Administrators local group.
Caller is NOT expected to be impersonating anyone and IS expected to be able to open their own process and process token.
Arguments:
None.
Return Value:
TRUE - Caller has Administrators local group.
FALSE - Caller does not have Administrators local group.
--*/
{ HANDLE Token; UINT BytesRequired; PTOKEN_GROUPS Groups; BOOL b; UINT i; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; PSID AdministratorsGroup;
//
// On non-NT platforms the user is administrator.
//
if(!ISNT()) { return(TRUE); }
//
// Open the process token.
//
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&Token)) { return(FALSE); }
b = FALSE; Groups = NULL;
//
// Get group information.
//
if(!GetTokenInformation(Token,TokenGroups,NULL,0,&BytesRequired) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && (Groups = (PTOKEN_GROUPS)LocalAlloc(LPTR,BytesRequired)) && GetTokenInformation(Token,TokenGroups,Groups,BytesRequired,&BytesRequired)) {
b = AllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup );
if(b) {
//
// See if the user has the administrator group.
//
b = FALSE; for(i=0; i<Groups->GroupCount; i++) { if(EqualSid(Groups->Groups[i].Sid,AdministratorsGroup)) { b = TRUE; break; } }
FreeSid(AdministratorsGroup); } }
//
// Clean up and return.
//
if(Groups) { LocalFree((HLOCAL)Groups); }
CloseHandle(Token);
return(b); }
PBACKUPIMAGEINFO pReadUndoFileIntegrityInfo( VOID )
/*++
Routine Description:
pReadUndoFileIntegrityInfo reads the uninstall registry info that was written by setup. This info tells undo what files are in the backup image and details about those files.
Arguments:
None.
Return Value:
A pointer to a BACKUPIMAGEINFO which must be freed with MemFree structure if successful, NULL otherwise.
--*/
{ static BACKUPIMAGEINFO backupInfo; BYTE * filesIntegrityPtr = NULL; UINT sizeOfBuffer = 0; UINT typeOfRegKey; HKEY key; LONG rc; BOOL bResult = FALSE;
key = OpenRegKeyStr (S_REGKEY_WIN_SETUP);
if (key) { rc = RegQueryValueEx ( key, S_REG_KEY_UNDO_INTEGRITY, NULL, &typeOfRegKey, NULL, &sizeOfBuffer );
if(rc == ERROR_SUCCESS && sizeOfBuffer){ filesIntegrityPtr = MemAlloc(g_hHeap, 0, sizeOfBuffer);
if(filesIntegrityPtr){ rc = RegQueryValueEx ( key, S_REG_KEY_UNDO_INTEGRITY, NULL, &typeOfRegKey, (PBYTE)filesIntegrityPtr, &sizeOfBuffer );
if (rc != ERROR_SUCCESS) { MemFree(g_hHeap, 0, filesIntegrityPtr); filesIntegrityPtr = NULL;
DEBUGMSG ((DBG_ERROR, "File integrity info struct is not the expected size")); } } }
CloseRegKey (key); }
if(filesIntegrityPtr){ if(Persist_Success == PERSIST_LOAD(filesIntegrityPtr, sizeOfBuffer, BACKUPIMAGEINFO, BACKUPIMAGEINFO_VERSION, &backupInfo)){ bResult = TRUE; } MemFree(g_hHeap, 0, filesIntegrityPtr); }
return bResult? &backupInfo: NULL; }
VOID pReleaseMemOfUndoFileIntegrityInfo( BACKUPIMAGEINFO * pBackupImageInfo )
/*++
Routine Description:
pReleaseMemOfUndoFileIntegrityInfo releases memory of BACKUPIMAGEINFO structure, that was allocated previously by pReadUndoFileIntegrityInfo function
Arguments:
None.
Return Value:
None.
--*/
{ if(!pBackupImageInfo){ return; }
PERSIST_RELEASE_STRUCT_MEMORY(BACKUPIMAGEINFO, BACKUPIMAGEINFO_VERSION, pBackupImageInfo); }
BOOL pIsEnoughDiskSpace( IN TCHAR Drive, IN PULARGE_INTEGER NeedDiskSpacePtr ) { ULARGE_INTEGER TotalNumberOfFreeBytes; TCHAR drive[] = TEXT("?:\\");
if(!NeedDiskSpacePtr){ MYASSERT(FALSE); return FALSE; }
drive[0] = Drive;
if(!GetDiskFreeSpaceEx(drive, NULL, NULL, &TotalNumberOfFreeBytes)){ LOG ((LOG_ERROR, "Unable to get %c drive free space information", Drive)); return FALSE; }
if(TotalNumberOfFreeBytes.QuadPart < NeedDiskSpacePtr->QuadPart){ LOG (( LOG_ERROR, "No enough space on windir drive %c:\\. Free: %d MB Need: %d MB", Drive, (UINT)TotalNumberOfFreeBytes.QuadPart>>20, (UINT)NeedDiskSpacePtr->QuadPart>>20) ); return FALSE; }
return TRUE; }
UNINSTALLSTATUS pDiskInfoComparationStatusToUninstallStatus( IN DISKINFO_COMPARATION_STATUS diskInfoCmpStatus ) { switch(diskInfoCmpStatus) { case DiskInfoCmp_DifferentLetter: case DiskInfoCmp_DriveMountPointHasChanged: return Uninstall_DifferentDriveLetter; case DiskInfoCmp_FileSystemHasChanged: return Uninstall_DifferentDriveFileSystem; case DiskInfoCmp_GeometryHasChanged: return Uninstall_DifferentDriveGeometry; case DiskInfoCmp_PartitionPlaceHasChanged: case DiskInfoCmp_PartitionLengthHasChanged: case DiskInfoCmp_PartitionTypeHasChanged: case DiskInfoCmp_PartitionStyleHasChanged: case DiskInfoCmp_PartitionCountHasChanged: case DiskInfoCmp_PartitionNumberHasChanged: case DiskInfoCmp_RewritePartitionHasChanged: case DiskInfoCmp_PartitionAttributesHasChanged: return Uninstall_DifferentDrivePartitionInfo; ; }; return Uninstall_WrongDrive; }
UNINSTALLSTATUS SanityCheck ( IN SANITYFLAGS Flags, IN PCWSTR VolumeRestriction, OPTIONAL OUT PULONGLONG DiskSpace OPTIONAL ) { PCTSTR path = NULL; UINT attribs; UINT version; UINT i; UINT j; WIN32_FILE_ATTRIBUTE_DATA fileDetails; PCTSTR backUpPath = NULL; PBACKUPIMAGEINFO imageInfo = NULL; UNINSTALLSTATUS result; OSVERSIONINFOEX osVersion; ULONGLONG condition; SYSTEMTIME st; FILETIME ft; ULARGE_INTEGER backupFileTime; ULARGE_INTEGER timeDifference; PCWSTR unicodePath; BOOL restricted; WCHAR winDir[MAX_PATH]; ULARGE_INTEGER TotalNumberOfFreeBytes; ULARGE_INTEGER FileSize; UINT drivesNumber; DRIVEINFO drivesInfo[MAX_DRIVE_NUMBER]; UINT disksNumber; DISKINFO * disksInfo = NULL; WCHAR * FileSystemName = NULL; WCHAR * VolumeNTPath = NULL; BOOL oldImage = FALSE; DISKINFO_COMPARATION_STATUS DiskCmpStatus;
__try {
if (DiskSpace) { *DiskSpace = 0; }
//
// Check OS version. Use the Windows 2000 VerifyVersionInfo API so we
// always get good results, even in the future. We support NT 5.1 and
// above.
//
condition = VerSetConditionMask (0, VER_MAJORVERSION, VER_GREATER_EQUAL); condition = VerSetConditionMask (condition, VER_MINORVERSION, VER_GREATER_EQUAL); condition = VerSetConditionMask (condition, VER_PLATFORMID, VER_EQUAL);
ZeroMemory (&osVersion, sizeof (osVersion)); osVersion.dwOSVersionInfoSize = sizeof (osVersion); osVersion.dwMajorVersion = 5; osVersion.dwMinorVersion = 1; osVersion.dwPlatformId = VER_PLATFORM_WIN32_NT;
if (!VerifyVersionInfo ( &osVersion, VER_MAJORVERSION|VER_MINORVERSION|VER_PLATFORMID, condition )) { DEBUGMSG ((DBG_ERROR, "VerifyVersionInfo says this is not the OS we support")); result = Uninstall_InvalidOsVersion; __leave; }
//
// Validate security
//
if (!pIsUserAdmin()) { result = Uninstall_NotEnoughPrivileges; DEBUGMSG ((DBG_WARNING, "User is not an administrator")); __leave; }
//
// Get info setup wrote to the registry
//
DEBUGMSG ((DBG_NAUSEA, "Getting registry info"));
backUpPath = GetUndoDirPath(); imageInfo = pReadUndoFileIntegrityInfo();
if(!backUpPath || !imageInfo) { result = Uninstall_DidNotFindRegistryEntries; LOG ((LOG_WARNING, "Uninstall: Failed to retrieve registry entries")); __leave; }
//
// Verify backup subdirectory exists
//
DEBUGMSG ((DBG_NAUSEA, "Validating undo subdirectory"));
attribs = GetFileAttributes (backUpPath); if (attribs == INVALID_ATTRIBUTES || !(attribs & FILE_ATTRIBUTE_DIRECTORY)) { DEBUGMSG ((DBG_VERBOSE, "%s not found", backUpPath)); result = Uninstall_DidNotFindDirOrFiles; __leave; }
//
// Compute disk space used by image
//
if (DiskSpace) { for (i = 0; i < imageInfo->NumberOfFiles; i++) {
path = JoinPaths (backUpPath, imageInfo->FilesInfo[i].FileName);
DEBUGMSG ((DBG_NAUSEA, "Getting disk space for %s", path));
if (VolumeRestriction) { DEBUGMSG ((DBG_NAUSEA, "Validating volume restriction for %s", path));
unicodePath = CreateUnicode (path); restricted = !StringIPrefixW (unicodePath, VolumeRestriction); if (restricted) { DEBUGMSGW (( DBG_VERBOSE, "%s is being skipped because it is not on volume %s", unicodePath, VolumeRestriction )); } DestroyUnicode (unicodePath);
if (restricted) { FreePathString (path); path = NULL; continue; } }
if (GetFileAttributesEx (path, GetFileExInfoStandard, &fileDetails) && fileDetails.dwFileAttributes != INVALID_ATTRIBUTES && !(fileDetails.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
DEBUGMSG (( DBG_NAUSEA, "Adding %I64u bytes for %s", (ULONGLONG) fileDetails.nFileSizeLow + ((ULONGLONG) fileDetails.nFileSizeHigh << (ULONGLONG) 32), path )); *DiskSpace += (ULONGLONG) fileDetails.nFileSizeLow + ((ULONGLONG) fileDetails.nFileSizeHigh << (ULONGLONG) 32); }
FreePathString (path); path = NULL; } }
//
// Validate each file in the backup subdirectory
//
for (i = 0; i < imageInfo->NumberOfFiles; i++) {
path = JoinPaths (backUpPath, imageInfo->FilesInfo[i].FileName);
DEBUGMSG ((DBG_NAUSEA, "Validating %s", path));
if (VolumeRestriction) { DEBUGMSG ((DBG_NAUSEA, "Validating volume restriction for %s", path));
unicodePath = CreateUnicode (path); restricted = !StringIPrefixW (unicodePath, VolumeRestriction); if (restricted) { DEBUGMSGW (( DBG_VERBOSE, "%s is being skipped because it is not on volume %s", unicodePath, VolumeRestriction )); } DestroyUnicode (unicodePath);
if (restricted) { FreePathString (path); path = NULL; continue; } }
if (!GetFileAttributesEx (path, GetFileExInfoStandard, &fileDetails) || fileDetails.dwFileAttributes == INVALID_ATTRIBUTES || (fileDetails.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) { DEBUGMSG ((DBG_VERBOSE, "%s not found", path)); result = Uninstall_DidNotFindDirOrFiles; __leave; }
DEBUGMSG ((DBG_NAUSEA, "Validating time for %s", path));
//
// Get the current FILETIME and transfer file time into a ULONGLONG
//
backupFileTime.LowPart = fileDetails.ftLastWriteTime.dwLowDateTime; backupFileTime.HighPart = fileDetails.ftLastWriteTime.dwHighDateTime;
GetSystemTime (&st); SystemTimeToFileTime (&st, &ft); timeDifference.LowPart = ft.dwLowDateTime; timeDifference.HighPart = ft.dwHighDateTime;
//
// If time is messed up, then fail
//
if (timeDifference.QuadPart < backupFileTime.QuadPart) { DEBUGMSG ((DBG_VERBOSE, "File time of %s is in the future according to current clock", path)); result = Uninstall_NewImage; __leave; }
//
// Subtract the original write time from the current time. If
// the result is less than 7 days, then stop.
//
timeDifference.QuadPart -= backupFileTime.QuadPart;
if (Flags & FAIL_IF_NOT_OLD) {
if (timeDifference.QuadPart < 7 * _DAY) { DEBUGMSG ((DBG_VERBOSE, "Image is less than 7 days old", path)); result = Uninstall_NewImage; __leave; } }
//
// Check if the image is more than 30 days old. If so, stop.
//
if (timeDifference.QuadPart >= (31 * _DAY)) { DEBUGMSG ((DBG_VERBOSE, "Image is more than 30 days old", path)); oldImage = TRUE; }
//
// Check file size
//
FileSize.LowPart = fileDetails.nFileSizeLow; FileSize.HighPart = fileDetails.nFileSizeHigh;
if(FileSize.QuadPart != imageInfo->FilesInfo[i].FileSize.QuadPart){ DEBUGMSG ((DBG_VERBOSE, "%s was changed", path)); result = Uninstall_FileWasModified; __leave; }
if (Flags & VERIFY_CAB) { if (imageInfo->FilesInfo[i].IsCab) { if (!CheckCabForAllFilesAvailability (path)){ result = Uninstall_FileWasModified; __leave; } } }
FreePathString (path); path = NULL; } DEBUGMSG ((DBG_VERBOSE, "Undo image is valid"));
//
// Validate disk geometry and partition info
//
path = JoinPaths (backUpPath, TEXT("boot.cab")); if(!GetBootDrive(backUpPath, path)){ LOG ((LOG_WARNING, "Uninstall Validate: Unable to open %s", path)); result = Uninstall_FileWasModified; __leave; }
if(!GetWindowsDirectoryW(winDir, ARRAYSIZE(winDir))){ LOG ((LOG_WARNING, "Uninstall Validate: Unable to get Windows dir")); result = Uninstall_CantRetrieveSystemInfo; __leave; }
//
// compare disk information
//
FileSystemName = MemAlloc(g_hHeap, 0, MAX_DRIVE_NUMBER * MAX_PATH); if(!FileSystemName){ LOG ((LOG_WARNING, "Uninstall Validate: Unable to allocate memory for FileSystemName")); result = Uninstall_NotEnoughMemory; __leave; }
VolumeNTPath = MemAlloc(g_hHeap, 0, MAX_DRIVE_NUMBER * MAX_PATH); if(!VolumeNTPath){ LOG ((LOG_WARNING, "Uninstall Validate: Unable to allocate memory for VolumeNTPath")); result = Uninstall_NotEnoughMemory; __leave; }
memset(drivesInfo, 0, sizeof(drivesInfo)); for(j = 0; j < ARRAYSIZE(drivesInfo); j++){ drivesInfo[j].FileSystemName = &FileSystemName[j * MAX_PATH]; drivesInfo[j].VolumeNTPath = &VolumeNTPath[j * MAX_PATH]; }
if(!GetUndoDrivesInfo(drivesInfo, &drivesNumber, g_BootDrv, winDir[0], backUpPath[0])){ LOG ((LOG_WARNING, "Uninstall Validate: Unable to get disk drives information")); result = Uninstall_CantRetrieveSystemInfo; __leave; }
if(drivesNumber != imageInfo->NumberOfDrives){ LOG ((LOG_WARNING, "Uninstall Validate: Different number of drive %d, was %d", drivesNumber, imageInfo->NumberOfDrives)); result = Uninstall_DifferentNumberOfDrives; __leave; }
if(!CompareDrivesInfo(drivesInfo, imageInfo->DrivesInfo, drivesNumber, &DiskCmpStatus, NULL)){ LOG ((LOG_WARNING, "Uninstall Validate: Different drives layout")); result = pDiskInfoComparationStatusToUninstallStatus(DiskCmpStatus); __leave; }
if(!GetDisksInfo(&disksInfo, &disksNumber)){ LOG ((LOG_WARNING, "Uninstall Validate: Unable to get physical disk information")); result = Uninstall_CantRetrieveSystemInfo; __leave; }
if(disksNumber != imageInfo->NumberOfDisks){ LOG ((LOG_WARNING, "Uninstall Validate: Different number of disks %d, was %d", disksNumber, imageInfo->NumberOfDisks)); result = Uninstall_DifferentNumberOfDrives; __leave; }
if(!CompareDisksInfo(disksInfo, imageInfo->DisksInfo, disksNumber, &DiskCmpStatus, NULL)){ LOG ((LOG_WARNING, "Uninstall Validate: Different disks layout")); result = pDiskInfoComparationStatusToUninstallStatus(DiskCmpStatus); __leave; } //
// validate free disk space
//
if(towlower(backUpPath[0]) == towlower(winDir[0]) || towlower(backUpPath[0]) == towlower(g_BootDrv)){ if(towlower(backUpPath[0]) == towlower(winDir[0])){ imageInfo->BackupFilesDiskSpace.QuadPart += imageInfo->UndoFilesDiskSpace.QuadPart; } else{ imageInfo->BootFilesDiskSpace.QuadPart += imageInfo->UndoFilesDiskSpace.QuadPart; } } else{ if(!pIsEnoughDiskSpace(backUpPath[0], &imageInfo->UndoFilesDiskSpace)){ result = Uninstall_NotEnoughSpace; __leave; } }
if(towlower(g_BootDrv) == towlower(winDir[0])){ imageInfo->BackupFilesDiskSpace.QuadPart += imageInfo->BootFilesDiskSpace.QuadPart; } else { if(!pIsEnoughDiskSpace(g_BootDrv, &imageInfo->BootFilesDiskSpace)){ result = Uninstall_NotEnoughSpace; __leave; } }
if(!pIsEnoughDiskSpace(winDir[0], &imageInfo->BackupFilesDiskSpace)){ result = Uninstall_NotEnoughSpace; __leave; }
//
// Uninstall backup is valid & uninstall is possible. Now process warnings.
//
if (oldImage) { result = Uninstall_OldImage; } else { result = Uninstall_Valid; } } __finally { if (path) { FreePathString (path); } if(backUpPath){ MemFree(g_hHeap, 0, backUpPath); }
if(imageInfo){ pReleaseMemOfUndoFileIntegrityInfo(imageInfo); }
if(disksInfo){ FreeDisksInfo(disksInfo, disksNumber); }
if(VolumeNTPath){ MemFree(g_hHeap, 0, VolumeNTPath); }
if(FileSystemName){ MemFree(g_hHeap, 0, FileSystemName); } }
return result; }
|