// // // 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); }