You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
447 lines
11 KiB
447 lines
11 KiB
//
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1996-1999
|
|
//
|
|
// Author: AdamEd
|
|
// Date: October 1998
|
|
//
|
|
// Class Store path query / persistence
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------
|
|
|
|
#include "cstore.hxx"
|
|
#include "cspath.hxx"
|
|
|
|
HRESULT
|
|
GetAppmgmtIniFilePath(
|
|
PSID pSid,
|
|
LPWSTR* ppwszPath
|
|
)
|
|
{
|
|
UNICODE_STRING SidString;
|
|
PTOKEN_USER pTokenUser;
|
|
WCHAR wszPath[MAX_PATH];
|
|
WCHAR * pwszSystemDir = NULL;
|
|
DWORD AllocLength;
|
|
DWORD Length;
|
|
DWORD Size;
|
|
BOOL bStatus;
|
|
ULONG ulSize;
|
|
|
|
*ppwszPath = 0;
|
|
|
|
pwszSystemDir = wszPath;
|
|
AllocLength = sizeof(wszPath) / sizeof(WCHAR);
|
|
|
|
for (;;)
|
|
{
|
|
Length = GetSystemDirectory(
|
|
pwszSystemDir,
|
|
AllocLength );
|
|
|
|
if ( 0 == Length )
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if ( Length >= AllocLength )
|
|
{
|
|
AllocLength = Length + 1;
|
|
if (pwszSystemDir != wszPath)
|
|
{
|
|
LocalFree(pwszSystemDir);
|
|
}
|
|
|
|
pwszSystemDir = (WCHAR *) LocalAlloc( LPTR, AllocLength * sizeof(WCHAR) );
|
|
if (NULL == pwszSystemDir)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ( pSid )
|
|
{
|
|
NTSTATUS ntStatus;
|
|
DWORD dwStatus;
|
|
|
|
ntStatus = RtlConvertSidToUnicodeString(
|
|
&SidString,
|
|
pSid,
|
|
TRUE );
|
|
|
|
if (!NT_SUCCESS(ntStatus))
|
|
{
|
|
if (pwszSystemDir != wszPath)
|
|
{
|
|
LocalFree(pwszSystemDir);
|
|
}
|
|
|
|
dwStatus = RtlNtStatusToDosError(ntStatus);
|
|
return HRESULT_FROM_WIN32(dwStatus);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString( &SidString, L"MACHINE" );
|
|
}
|
|
|
|
HRESULT hr;
|
|
|
|
// System dir + \appmgmt\ + Sid \ + inifilename \ + null
|
|
|
|
ulSize = Length + 11 + (SidString.Length / 2) + (sizeof(APPMGMT_INI_FILENAME) / sizeof(WCHAR));
|
|
*ppwszPath = new WCHAR[ulSize];
|
|
|
|
if ( *ppwszPath )
|
|
{
|
|
hr = StringCchCopy( *ppwszPath, ulSize, pwszSystemDir );
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if ( pwszSystemDir[lstrlen(pwszSystemDir)-1] != L'\\' )
|
|
{
|
|
hr = StringCchCat( *ppwszPath, ulSize, L"\\" );
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCchCat( *ppwszPath, ulSize, L"appmgmt\\" );
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCchCat( *ppwszPath, ulSize, SidString.Buffer );
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCchCat( *ppwszPath, ulSize, APPMGMT_INI_FILENAME );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
if ( pSid )
|
|
RtlFreeUnicodeString( &SidString );
|
|
if (pwszSystemDir != wszPath)
|
|
{
|
|
LocalFree(pwszSystemDir);
|
|
}
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if ( pSid )
|
|
RtlFreeUnicodeString( &SidString );
|
|
|
|
if (pwszSystemDir != wszPath)
|
|
{
|
|
LocalFree(pwszSystemDir);
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
LONG
|
|
GetClassStorePathSize(
|
|
HANDLE hFile,
|
|
DWORD* pdwSize)
|
|
{
|
|
BOOL bStatus;
|
|
DWORD cbSizeHigh;
|
|
|
|
//
|
|
// Initialize the size to the length of an empty path -- this way
|
|
// if no file is passed in we will treat that as a class store path
|
|
// of zero length (excluding the terminator). This length must include
|
|
// the terminator, so we set our initial size to that of an empty string
|
|
//
|
|
*pdwSize = sizeof(L'\0');
|
|
|
|
//
|
|
// If we have an open file with data, determine the size of the data
|
|
//
|
|
if ( hFile )
|
|
{
|
|
//
|
|
// GetFileSize returns the logical size of the file, regardless
|
|
// of whether the logical size is the same as the physical
|
|
// size due to the vagaries of different file systems or compression
|
|
//
|
|
*pdwSize = GetFileSize(
|
|
hFile,
|
|
&cbSizeHigh);
|
|
|
|
//
|
|
// Check for a failure from the api
|
|
//
|
|
if ( -1 == *pdwSize )
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
//
|
|
// Check the size for validity -- a file of ridiculous
|
|
// size will be rejected
|
|
//
|
|
if ( cbSizeHigh )
|
|
{
|
|
//
|
|
// If the high dword is set, clearly this file
|
|
// contains an unreasonable amount of data
|
|
//
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
else if ( *pdwSize > MAX_CSPATH_SIZE )
|
|
{
|
|
//
|
|
// Again, the size should be within reasonable limits
|
|
//
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
LONG
|
|
ReadClassStorePathFromFile(
|
|
HANDLE hFile,
|
|
WCHAR* wszDestination,
|
|
DWORD cbSize)
|
|
{
|
|
DWORD cbRead;
|
|
BOOL bStatus;
|
|
|
|
//
|
|
// The format of the file is simple -- it is simply the stream of bytes
|
|
// that represent a unicode string terminated by a null unicode character --
|
|
// we can just read the data directly and copy it to a buffer referenced by
|
|
// a WCHAR* -- it will be legitimate unicode string once we read it in -- it includes
|
|
// the null unicode char as the last byte read
|
|
//
|
|
|
|
//
|
|
// Note that we've read nothing so far
|
|
//
|
|
cbRead = 0;
|
|
|
|
//
|
|
// If the file has data, read it
|
|
//
|
|
if ( cbSize )
|
|
{
|
|
//
|
|
// Read the data from the file into the buffer
|
|
//
|
|
bStatus = ReadFile(
|
|
hFile,
|
|
wszDestination,
|
|
cbSize,
|
|
&cbRead,
|
|
NULL);
|
|
|
|
if (!bStatus)
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
//
|
|
// Verify that the last character read was a null unicode character.
|
|
// If it wasn't, the file is corrupt -- return an error so we don't
|
|
// try to use the corrupt data and cause repeated errors or even crashes
|
|
//
|
|
if ( wszDestination[ cbRead / sizeof(*wszDestination) - 1 ] != L'\0' )
|
|
{
|
|
return ERROR_FILE_CORRUPT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For empty files, we simply return an empty unicode string
|
|
//
|
|
wszDestination[ 0 ] = L'\0';
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
HRESULT ReadClassStorePath(PSID pSid, LPWSTR* ppwszClassStorePath)
|
|
{
|
|
HRESULT hr;
|
|
LPWSTR wszIniFilePath;
|
|
DWORD cbSize;
|
|
WCHAR* wszClassStorePath;
|
|
LONG Status;
|
|
HANDLE hFile;
|
|
|
|
cbSize = 0;
|
|
|
|
//
|
|
// Notes: The file being read by this function was originally an
|
|
// ini file as generated by the WritePrivateProfileSection api.
|
|
// Unfortunately, the corresponding GetPrivateProfileString was horribly
|
|
// broken in the sense that it had a 32k limit on the size of values. This
|
|
// was not acceptable for our design, so we read the file directly
|
|
//
|
|
|
|
hr = GetAppmgmtIniFilePath(
|
|
pSid,
|
|
&wszIniFilePath);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Initialize our reference to conditionally freed data
|
|
//
|
|
wszClassStorePath = NULL;
|
|
|
|
//
|
|
// First, attempt to open the file, which should already exist. We
|
|
// generally do not expect concurrent readers, as there
|
|
// are only two processes from which this file is read: services
|
|
// and winlogon. Neither should be reading it at the same time, though
|
|
// a terminal server case where the same user was logged in to the machine
|
|
// twice could cause it
|
|
//
|
|
hFile = CreateFile(
|
|
wszIniFilePath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
|
|
//
|
|
// Handle the failure cases
|
|
//
|
|
if ( INVALID_HANDLE_VALUE == hFile )
|
|
{
|
|
//
|
|
// We want to use NULL to indicate the absence of the file
|
|
//
|
|
hFile = NULL;
|
|
|
|
Status = GetLastError();
|
|
|
|
//
|
|
// If the file did not exist, that's ok -- we interpret the
|
|
// absence of the file as a blank class store path -- we
|
|
// will not exit if the file didn't exist
|
|
//
|
|
if (ERROR_FILE_NOT_FOUND != Status)
|
|
{
|
|
goto cleanup_and_exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we don't have a file in existence, this means a zero length
|
|
// class store path
|
|
//
|
|
cbSize = 0;
|
|
}
|
|
|
|
//
|
|
// Now that we have access to the file or know that the file does
|
|
// not exist, we can calculate the size of the class store path
|
|
// from the file's size, as they have a 1-1 relationship. Note that if the
|
|
// file does not exist, then hFile will be NULL
|
|
//
|
|
if ( hFile )
|
|
{
|
|
Status = GetClassStorePathSize(
|
|
hFile,
|
|
&cbSize);
|
|
|
|
if ( ERROR_SUCCESS != Status )
|
|
{
|
|
goto cleanup_and_exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We know the size, so allocate space, treating the size
|
|
// as the length (in bytes) of the string including the terminator --
|
|
// to handle the case where cbSize is zero because of a nonexistent
|
|
// cs path, we will also add a terminating NULL since zero length
|
|
// strings are still 1 character in size
|
|
//
|
|
wszClassStorePath = new WCHAR [ cbSize / sizeof(*wszClassStorePath) + 1 ];
|
|
|
|
if ( wszClassStorePath )
|
|
{
|
|
//
|
|
// We have a buffer, so read the unicode string into
|
|
// the buffer
|
|
//
|
|
Status = ReadClassStorePathFromFile(
|
|
hFile,
|
|
wszClassStorePath,
|
|
cbSize);
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
cleanup_and_exit:
|
|
|
|
//
|
|
// We no longer need the path to the ini file -- free it
|
|
//
|
|
delete [] wszIniFilePath;
|
|
|
|
//
|
|
// Close the file if it's open
|
|
//
|
|
if ( hFile )
|
|
{
|
|
CloseHandle( hFile );
|
|
}
|
|
|
|
//
|
|
// On success, set the out param. On failure,
|
|
// free any buffer we allocated for the class store path
|
|
//
|
|
if (ERROR_SUCCESS == Status)
|
|
{
|
|
*ppwszClassStorePath = wszClassStorePath;
|
|
}
|
|
else
|
|
{
|
|
delete [] wszClassStorePath;
|
|
}
|
|
|
|
return HRESULT_FROM_WIN32(Status);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|