/*++ Copyright (c) 1998 Microsoft Corporation Module Name: termutil.c Abstract: Terminal server support functions and inifile syncing/merging code Author: Revision History: --*/ #include "basedll.h" #include "regapi.h" #define TERMSRV_INIFILE_TIMES L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\IniFile Times" BOOL IsTerminalServerCompatible(VOID) { PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader( NtCurrentPeb()->ImageBaseAddress ); if ((NtHeader) && (NtHeader->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE)) { return TRUE; } else { return FALSE; } } BOOL IsTSAppCompatEnabled(VOID) { NTSTATUS NtStatus; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UniString; HKEY hKey = 0; ULONG ul, ulcbuf; PKEY_VALUE_PARTIAL_INFORMATION pKeyValInfo = NULL; BOOL retval = TRUE; RtlInitUnicodeString(&UniString,REG_NTAPI_CONTROL_TSERVER); // Determine the value info buffer size ulcbuf = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR) + sizeof(ULONG); pKeyValInfo = RtlAllocateHeap(RtlProcessHeap(), 0, ulcbuf); // Did everything initialize OK? if (UniString.Buffer && pKeyValInfo) { InitializeObjectAttributes(&ObjectAttributes, &UniString, OBJ_CASE_INSENSITIVE, NULL, NULL ); NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(NtStatus)) { RtlInitUnicodeString(&UniString, L"TSAppCompat"); NtStatus = NtQueryValueKey(hKey, &UniString, KeyValuePartialInformation, pKeyValInfo, ulcbuf, &ul); if (NT_SUCCESS(NtStatus) && (REG_DWORD == pKeyValInfo->Type)) { if ((*(PULONG)pKeyValInfo->Data) == 0) { retval = FALSE; } } NtClose(hKey); } } // Free up the buffers we allocated // Need to zero out the buffers, because some apps (MS Internet Assistant) // won't install if the heap is not zero filled. if (pKeyValInfo) { memset(pKeyValInfo, 0, ulcbuf); RtlFreeHeap( RtlProcessHeap(), 0, pKeyValInfo ); } return(retval); } BOOL IsSystemLUID(VOID) { HANDLE TokenHandle; UCHAR TokenInformation[ sizeof( TOKEN_STATISTICS ) ]; ULONG ReturnLength; LUID CurrentLUID = { 0, 0 }; LUID SystemLUID = SYSTEM_LUID; NTSTATUS Status; if ( CurrentLUID.LowPart == 0 && CurrentLUID.HighPart == 0 ) { Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_QUERY, &TokenHandle ); if ( !NT_SUCCESS( Status ) ) return(TRUE); NtQueryInformationToken( TokenHandle, TokenStatistics, &TokenInformation, sizeof(TokenInformation), &ReturnLength ); NtClose( TokenHandle ); RtlCopyLuid(&CurrentLUID, &(((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId)); } if (RtlEqualLuid(&CurrentLUID, &SystemLUID)) { return(TRUE); } else { return(FALSE ); } } void InitializeTermsrvFpns(void) { HANDLE dllHandle; if (IsTerminalServerCompatible() || (IsSystemLUID()) || (!IsTSAppCompatEnabled())) { return; } // // Load Terminal Server application compatibility dll // dllHandle = LoadLibraryW(L"tsappcmp.dll"); if (dllHandle) { gpTermsrvFormatObjectName = (PTERMSRVFORMATOBJECTNAME)GetProcAddress(dllHandle,"TermsrvFormatObjectName"); gpTermsrvGetComputerName = (PTERMSRVGETCOMPUTERNAME)GetProcAddress(dllHandle,"TermsrvGetComputerName"); gpTermsrvAdjustPhyMemLimits = (PTERMSRVADJUSTPHYMEMLIMITS)GetProcAddress(dllHandle,"TermsrvAdjustPhyMemLimits"); gpTermsrvGetWindowsDirectoryA = (PTERMSRVGETWINDOWSDIRECTORYA)GetProcAddress(dllHandle,"TermsrvGetWindowsDirectoryA"); gpTermsrvGetWindowsDirectoryW = (PTERMSRVGETWINDOWSDIRECTORYW)GetProcAddress(dllHandle,"TermsrvGetWindowsDirectoryW"); gpTermsrvConvertSysRootToUserDir = (PTERMSRVCONVERTSYSROOTTOUSERDIR)GetProcAddress(dllHandle,"TermsrvConvertSysRootToUserDir"); gpTermsrvBuildIniFileName = (PTERMSRVBUILDINIFILENAME)GetProcAddress(dllHandle,"TermsrvBuildIniFileName"); gpTermsrvCORIniFile = (PTERMSRVCORINIFILE)GetProcAddress(dllHandle,"TermsrvCORIniFile"); gpGetTermsrCompatFlags = (PGETTERMSRCOMPATFLAGS)GetProcAddress(dllHandle,"GetTermsrCompatFlags"); gpTermsrvBuildSysIniPath = (PTERMSRVBUILDSYSINIPATH)GetProcAddress(dllHandle,"TermsrvBuildSysIniPath"); gpTermsrvCopyIniFile = (PTERMSRVCOPYINIFILE)GetProcAddress(dllHandle,"TermsrvCopyIniFile"); gpTermsrvGetString = (PTERMSRVGETSTRING)GetProcAddress(dllHandle,"TermsrvGetString"); gpTermsrvLogInstallIniFile = (PTERMSRVLOGINSTALLINIFILE)GetProcAddress(dllHandle,"TermsrvLogInstallIniFile"); } } /***************************************************************************** * * TermsrvAppInstallMode * * Returns whether the system is in Install mode or not * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/ WINBASEAPI BOOL WINAPI TermsrvAppInstallMode( VOID ) { if ( BaseStaticServerData->fTermsrvAppInstallMode ) return( TRUE ); return( FALSE ); } /***************************************************************************** * * SetTermsrvAppInstallMode * * Turns App install mode on or off. Default is off * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/ WINBASEAPI BOOL WINAPI SetTermsrvAppInstallMode( BOOL bState ) { typedef BOOL ( APIENTRY Func_CheckTokenMembership )( HANDLE , PSID , PBOOL); BOOL rc; NTSTATUS Status; PSID pSid = NULL ; SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY; HINSTANCE dllHandle; if ( !( IsTerminalServer() && IsTSAppCompatEnabled() ) ) { return FALSE; } dllHandle = LoadLibraryW(L"advapi32.dll"); if (dllHandle) { Func_CheckTokenMembership *fPtr; fPtr = (Func_CheckTokenMembership * )GetProcAddress(dllHandle,"CheckTokenMembership"); if (fPtr) { Status = RtlAllocateAndInitializeSid( &SidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSid ); if (NT_SUCCESS(Status)) { BOOL FoundAdmin; if ( fPtr (NULL, pSid , &FoundAdmin)) { if (FoundAdmin) { // caller is admin, go ahead #if defined(BUILD_WOW6432) Status = CsrBasepSetTermsrvAppInstallMode(bState); #else BASE_API_MSG m; PBASE_SET_TERMSRVAPPINSTALLMODE c= (PBASE_SET_TERMSRVAPPINSTALLMODE)&m.u.SetTermsrvAppInstallMode; c->bState = bState; Status = CsrClientCallServer((PCSR_API_MSG)&m, NULL, CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetTermsrvAppInstallMode), sizeof( *c )); #endif if ( NT_SUCCESS( Status ) ) { // // Load tsappcmp.dll // if (gpTermsrvUpdateAllUserMenu == NULL) { HANDLE dllHandle; // // Load Terminal Server application compatibility dll // dllHandle = LoadLibraryW(L"tsappcmp.dll"); if (dllHandle) { gpTermsrvUpdateAllUserMenu = (PTERMSRVUPDATEALLUSERMENU)GetProcAddress(dllHandle,"TermsrvUpdateAllUserMenu"); } } if (gpTermsrvUpdateAllUserMenu) { gpTermsrvUpdateAllUserMenu( bState == TRUE ? 0 : 1 ); } rc = TRUE; } else { SetLastError( RtlNtStatusToDosError( Status ) ); rc = FALSE; } } else { // user is not admin SetLastError( ERROR_ACCESS_DENIED ); rc = FALSE; } } else { // call to CheckTokenMembership() failed, it set the last error rc = FALSE; } } else { // attempt to allocate and init SID failed. SetLastError( RtlNtStatusToDosError( Status ) ); rc = FALSE; } } else { // function not found, GetProc() set the last error. rc = FALSE; } FreeLibrary( dllHandle ); } else { // library not found, LoadLib() set the last error rc = FALSE; } if (pSid) { RtlFreeSid( pSid ); } return rc; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// /* Ini File syncing/merging code */ ////////////////////////////////////////////////////////////////////////////// /* External Functions */ NTSTATUS BaseDllOpenIniFileOnDisk( PINIFILE_PARAMETERS a ); NTSTATUS BaseDllWriteKeywordValue( IN PINIFILE_PARAMETERS a, IN PUNICODE_STRING VariableName OPTIONAL ); NTSTATUS BaseDllCloseIniFileOnDisk( IN PINIFILE_PARAMETERS a ); NTSTATUS BaseDllFindSection( IN PINIFILE_PARAMETERS a ); NTSTATUS BaseDllFindKeyword( IN PINIFILE_PARAMETERS a ); NTSTATUS TermsrvIniSyncLoop( HANDLE SrcHandle, PINIFILE_PARAMETERS a, PBOOLEAN pfIniUpdated ); NTSTATUS TermsrvGetSyncTime( PUNICODE_STRING pSysIniPath, PUNICODE_STRING pUserBasePath, PLARGE_INTEGER pLastSyncTime ); NTSTATUS TermsrvPutSyncTime( PUNICODE_STRING pSysIniPath, PUNICODE_STRING pUserBasePath, PLARGE_INTEGER pLastSyncTime ); /***************************************************************************** * * TermsrvGetSyncTime * * This routine will get the time of the system ini file that the user ini * file was last sync'd with. * * ENTRY: * PUNICODE_STRING pSysIniPath (In) - NT fully qualified system ini path * PUNICODE_STRING pUserBasePath (In) - NT fully qualified user directory path * PLARGE_INTEGER pLastSyncTime (OUT) - ptr to return last sync time * * EXIT: * STATUS_SUCCESS - successfully retrieved the last sync time from infile.upd * ****************************************************************************/ NTSTATUS TermsrvGetSyncTime( PUNICODE_STRING pSysIniPath, PUNICODE_STRING pUserBasePath, PLARGE_INTEGER pLastSyncTime) { NTSTATUS Status; HANDLE hUpdate = NULL; OBJECT_ATTRIBUTES ObjAUpd; IO_STATUS_BLOCK Iosb; FILE_STANDARD_INFORMATION StandardInfo; WCHAR wcUpdateFile[MAX_PATH+1]; UNICODE_STRING UniUpdateName = {0, sizeof(wcUpdateFile), wcUpdateFile}; PCHAR pBuff = NULL, pBuffEnd; PWCH pwch; SIZE_T ulBuffSize; LONG lresult; if (!pSysIniPath) { return STATUS_INVALID_PARAMETER_1; } if (!pUserBasePath) { return STATUS_INVALID_PARAMETER_2; } if (!pLastSyncTime) { return STATUS_INVALID_PARAMETER_3; } Status = RtlAppendUnicodeStringToString(&UniUpdateName, pUserBasePath); if (NT_SUCCESS(Status)) { Status = RtlAppendUnicodeToString(&UniUpdateName, L"\\inifile.upd"); } if (! NT_SUCCESS(Status)) { return Status; } pLastSyncTime->LowPart = 0; pLastSyncTime->HighPart = 0; InitializeObjectAttributes( &ObjAUpd, &UniUpdateName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // Open the update log Iosb.Status = STATUS_SUCCESS; Status = NtOpenFile( &hUpdate, FILE_GENERIC_READ, &ObjAUpd, &Iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT // OpenOptions ); // Get the size of the file if (NT_SUCCESS( Status )) { Status = NtQueryInformationFile( hUpdate, &Iosb, &StandardInfo, sizeof(StandardInfo), FileStandardInformation ); if (Status == STATUS_BUFFER_OVERFLOW) { Status = STATUS_SUCCESS; } #if DBG else if (!NT_SUCCESS( Status )) { DbgPrint( "TermsrvGetSyncTime: Unable to QueryInformation for %wZ - Status == %x\n", &UniUpdateName, Status ); } #endif } if (NT_SUCCESS( Status )) { ulBuffSize = StandardInfo.EndOfFile.LowPart + 4 * sizeof(WCHAR); Status = NtAllocateVirtualMemory( NtCurrentProcess(), &pBuff, 0, &ulBuffSize, MEM_RESERVE, PAGE_READWRITE ); } if (NT_SUCCESS( Status )) { Status = NtAllocateVirtualMemory( NtCurrentProcess(), &pBuff, 0, &ulBuffSize, MEM_COMMIT, PAGE_READWRITE ); } if (NT_SUCCESS( Status )) { Status = NtReadFile( hUpdate, NULL, NULL, NULL, &Iosb, pBuff, StandardInfo.EndOfFile.LowPart, NULL, NULL ); if ( Status == STATUS_PENDING ) { Status = NtWaitForSingleObject( hUpdate, FALSE, NULL ); } if ( NT_SUCCESS(Status) ) { // Get final I/O status Status = Iosb.Status; } } // Look for this ini file in the list if (NT_SUCCESS(Status)) { pwch = (PWCHAR)pBuff; pBuffEnd = pBuff + StandardInfo.EndOfFile.LowPart; // Look for the file in the sorted list while ((pwch < (PWCHAR)pBuffEnd) && ((lresult = _wcsicmp(pwch, pSysIniPath->Buffer)) < 0)) { pwch += wcslen(pwch) + sizeof(LARGE_INTEGER)/sizeof(WCHAR) + 1; } if ((pwch < (PWCHAR)pBuffEnd) && (lresult == 0)) { pwch += wcslen(pwch) + 1; pLastSyncTime->LowPart = ((PLARGE_INTEGER)pwch)->LowPart; pLastSyncTime->HighPart = ((PLARGE_INTEGER)pwch)->HighPart; } } if (NT_SUCCESS(Status) ) { // Get final I/O status Status = Iosb.Status; } if (pBuff) { NtFreeVirtualMemory( NtCurrentProcess(), &pBuff, &ulBuffSize, MEM_RELEASE ); } if (hUpdate) { Status = NtClose( hUpdate ); } return(Status); } /***************************************************************************** * * TermsrvPutSyncTime * * This routine will write the time of the system ini file that the user ini * file was last sync'd with. * * ENTRY: * PUNICODE_STRING pSysIniPath (In) - NT fully qualified system ini path * PUNICODE_STRING pUserBasePath (In) - NT fully qualified user directory path * PLARGE_INTEGER pLastSyncTime (OUT) - ptr to return last sync time * * EXIT: * STATUS_SUCCESS - successfully stored the last sync time in infile.upd * ****************************************************************************/ NTSTATUS TermsrvPutSyncTime( PUNICODE_STRING pSysIniPath, PUNICODE_STRING pUserBasePath, PLARGE_INTEGER pLastSyncTime) { NTSTATUS Status; HANDLE hUpdate = NULL; OBJECT_ATTRIBUTES ObjAUpd; IO_STATUS_BLOCK Iosb; FILE_STANDARD_INFORMATION StandardInfo; WCHAR wcUpdateFile[MAX_PATH+1]; UNICODE_STRING UniUpdateName = {0, sizeof(wcUpdateFile), wcUpdateFile}; PCHAR pBuff = NULL, pBuffEnd; PWCH pwch; SIZE_T ulBuffSize; ULONG ulLength; SIZE_T ulRegionSize; LONG lresult; LARGE_INTEGER FileLength; FILE_POSITION_INFORMATION CurrentPos; if (!pSysIniPath) { return STATUS_INVALID_PARAMETER_1; } if (!pUserBasePath) { return STATUS_INVALID_PARAMETER_2; } if (!pLastSyncTime) { return STATUS_INVALID_PARAMETER_3; } Status = RtlAppendUnicodeStringToString(&UniUpdateName, pUserBasePath); if (NT_SUCCESS(Status)) { Status = RtlAppendUnicodeToString(&UniUpdateName, L"\\inifile.upd"); } if (! NT_SUCCESS(Status)) { return Status; } InitializeObjectAttributes( &ObjAUpd, &UniUpdateName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // Open the update log Iosb.Status = STATUS_SUCCESS; Status = NtCreateFile( &hUpdate, FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE, &ObjAUpd, &Iosb, NULL, // Allocation size FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes FILE_SHARE_WRITE, // dwShareMode FILE_OPEN_IF, // CreateDisposition FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, // CreateFlags NULL, // EaBuffer 0 // EaLength ); if (NT_SUCCESS( Status )) { Status = NtQueryInformationFile( hUpdate, &Iosb, &StandardInfo, sizeof(StandardInfo), FileStandardInformation ); if (Status == STATUS_BUFFER_OVERFLOW) { Status = STATUS_SUCCESS; } #if DBG else if (!NT_SUCCESS( Status )) { DbgPrint( "TermsrvPutLastSyncTime: Unable to QueryInformation for %wZ - Status == %x\n", &UniUpdateName, Status ); } #endif } if (NT_SUCCESS( Status )) { ulBuffSize = StandardInfo.EndOfFile.LowPart + 4 * sizeof(WCHAR); ulRegionSize = ulBuffSize + 0x1000; // Room for 4K of growth Status = NtAllocateVirtualMemory( NtCurrentProcess(), &pBuff, 0, &ulRegionSize, MEM_RESERVE, PAGE_READWRITE ); } if (NT_SUCCESS( Status )) { Status = NtAllocateVirtualMemory( NtCurrentProcess(), &pBuff, 0, &ulBuffSize, MEM_COMMIT, PAGE_READWRITE ); } if (NT_SUCCESS( Status ) && StandardInfo.EndOfFile.LowPart) { Status = NtReadFile( hUpdate, NULL, NULL, NULL, &Iosb, pBuff, StandardInfo.EndOfFile.LowPart, NULL, NULL ); if ( Status == STATUS_PENDING ) { Status = NtWaitForSingleObject( hUpdate, FALSE, NULL ); } if ( NT_SUCCESS(Status) ) { // Get final I/O status Status = Iosb.Status; } } // Look for this ini file in the list if (NT_SUCCESS(Status)) { pwch = (PWCHAR)pBuff; pBuffEnd = pBuff + StandardInfo.EndOfFile.LowPart; // Look for the file in the list while ((pwch < (PWCHAR)pBuffEnd) && ((lresult = _wcsicmp(pwch, pSysIniPath->Buffer)) < 0)) { pwch += wcslen(pwch) + (sizeof(LARGE_INTEGER)/sizeof(WCHAR)) + 1; } // If the ini file is already in the file, just update the time if ((pwch < (PWCHAR)pBuffEnd) && (lresult == 0)) { pwch += wcslen(pwch) + 1; ((PLARGE_INTEGER)pwch)->LowPart = pLastSyncTime->LowPart; ((PLARGE_INTEGER)pwch)->HighPart = pLastSyncTime->HighPart; } else { // Ini file not in list // Figure out the size to grow the file ulLength = (pSysIniPath->Length + 2) + sizeof(LARGE_INTEGER); ulBuffSize += ulLength; // Grow the memory region Status = NtAllocateVirtualMemory( NtCurrentProcess(), &pBuff, 0, &ulBuffSize, MEM_COMMIT, PAGE_READWRITE ); if (NT_SUCCESS(Status)) { // figure out where the entry goes in the file if (pwch < (PWCHAR)pBuffEnd) { RtlMoveMemory( pwch+(ulLength/sizeof(WCHAR)), pwch, pBuffEnd - (PCHAR)pwch ); } pBuffEnd += ulLength; wcscpy(pwch, pSysIniPath->Buffer); pwch += (pSysIniPath->Length + 2)/sizeof(WCHAR); ((PLARGE_INTEGER)pwch)->LowPart = pLastSyncTime->LowPart; ((PLARGE_INTEGER)pwch)->HighPart = pLastSyncTime->HighPart; } } } if (NT_SUCCESS(Status)) { CurrentPos.CurrentByteOffset.LowPart = 0; CurrentPos.CurrentByteOffset.HighPart = 0; Status = NtSetInformationFile( hUpdate, &Iosb, &CurrentPos, sizeof(CurrentPos), FilePositionInformation ); Status = NtWriteFile( hUpdate, NULL, NULL, NULL, &Iosb, pBuff, (ULONG)(pBuffEnd - pBuff + 1), NULL, NULL ); if( Status == STATUS_PENDING ) { Status = NtWaitForSingleObject( hUpdate, FALSE, NULL ); } if( NT_SUCCESS(Status) ) { // Get final I/O status Status = Iosb.Status; } } if (NT_SUCCESS( Status )) { FileLength.LowPart = (ULONG)(pBuffEnd - pBuff); FileLength.HighPart = 0; Status = NtSetInformationFile( hUpdate, &Iosb, &FileLength, sizeof( FileLength ), FileEndOfFileInformation ); } if (pBuff) { NtFreeVirtualMemory( NtCurrentProcess(), &pBuff, &ulRegionSize, MEM_RELEASE ); } if (hUpdate) { Status = NtClose( hUpdate ); } return(Status); } /***************************************************************************** * * TermsrvCheckIniSync * * This routine will get the time of the system ini file that the user ini * file was last sync'd with. * * ENTRY: * PUNICODE_STRING pSysIniPath (In) - NT fully qualified system ini path * PUNICODE_STRING pUserBasePath (In) - NT fully qualified user directory path * BOOLEAN fGet (In) - TRUE means to get last sync time, FALSE means to write it * PLARGE_INTEGER pLastSyncTime (OUT) - ptr to return last sync time * * EXIT: * TRUE - User ini file should be sync'd * FALSE - User ini file should be sync'd * ****************************************************************************/ BOOLEAN TermsrvCheckIniSync( PUNICODE_STRING pSysIniPath, PUNICODE_STRING pUserBasePath) { LARGE_INTEGER LastSyncTime; OBJECT_ATTRIBUTES objaIni; FILE_NETWORK_OPEN_INFORMATION BasicInfo; NTSTATUS Status; // Get the last sync time of the ini file from the inifile.upd file TermsrvGetSyncTime(pSysIniPath, pUserBasePath, &LastSyncTime); // Get the last write time of the system ini file InitializeObjectAttributes( &objaIni, pSysIniPath, OBJ_CASE_INSENSITIVE, NULL, NULL ); // Now query it Status = NtQueryFullAttributesFile( &objaIni, &BasicInfo ); // If we couldn't get the time or the system ini file has been updated // since we last sync'd, return TRUE if (!NT_SUCCESS(Status) || ((BasicInfo.LastWriteTime.HighPart > LastSyncTime.HighPart) || ((BasicInfo.LastWriteTime.HighPart == LastSyncTime.HighPart) && (BasicInfo.LastWriteTime.LowPart > LastSyncTime.LowPart)))) { return(TRUE); } return(FALSE); } /***************************************************************************** * * TermsrvDoesFileExist * * Returns whether the file exists or not. * * Must use NT, not WIN32 pathnames. * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/ BOOL TermsrvDoesFileExist( PUNICODE_STRING pFileName ) { NTSTATUS Status; FILE_BASIC_INFORMATION BasicInfo; OBJECT_ATTRIBUTES Obja; InitializeObjectAttributes( &Obja, pFileName, OBJ_CASE_INSENSITIVE, NULL, NULL ); /* * Now query it */ Status = NtQueryAttributesFile( &Obja, &BasicInfo ); if( NT_SUCCESS( Status ) ) { return( TRUE ); } return( FALSE ); } /***************************************************************************** * * TermsrvSyncUserIniFile * * This routine will check that the user's ini file is "sync'd" with the * system version of the ini file. This means that it walks through the * system ini file and checks that there is a corresponding entry in the * user's ini file. * * ENTRY: * IN PINIFILE_PARAMETERS a - ptr to inifile structure * * EXIT: * True - Ini file updated * False - User Ini file was unchanged * ****************************************************************************/ BOOL TermsrvSyncUserIniFile(PINIFILE_PARAMETERS a) { WCHAR wcIniPath[MAX_PATH+1]; UNICODE_STRING IniFilePath = {MAX_PATH, MAX_PATH+1, wcIniPath}; PWCH pwch, pwcIniName; UNICODE_STRING UniSysPath; UNICODE_STRING UserBasePath; NTSTATUS Status; HANDLE SrcHandle; ULONG ulCompatFlags; OBJECT_ATTRIBUTES SrcObja; IO_STATUS_BLOCK SrcIosb; INIFILE_OPERATION OrigOperation; BOOLEAN OrigWrite, OrigMultiValue, OrigUnicode, OrigWriteOperation, fIniUpdated = FALSE; ANSI_STRING OrigAppName, OrigVarName; ULONG OrigResultChars, OrigResultMaxChars; LPSTR OrigResultBuffer; OBJECT_ATTRIBUTES objaIni; FILE_NETWORK_OPEN_INFORMATION BasicInfo; // If INI file mapping is not on, return if (IsSystemLUID() || TermsrvAppInstallMode()) { return(FALSE); } // Build full system path to the Ini file, and get BasePath to user dir if ((gpTermsrvBuildSysIniPath == NULL) || !(gpTermsrvBuildSysIniPath(&a->NtFileName, &UniSysPath, &UserBasePath))) { #if DBG //DbgPrint("TermsrvSyncUserIniFile: Error building Sys Ini Path!\n"); #endif return(FALSE); } // Get the ini file name pwch = wcsrchr(a->NtFileName.Buffer, L'\\') ; if (pwch == NULL) { return FALSE; } else{ pwch++; } pwcIniName = RtlAllocateHeap( RtlProcessHeap(), 0, (wcslen(pwch) + 1)*sizeof(WCHAR)); if (pwcIniName == NULL) { return FALSE; } wcscpy(pwcIniName, pwch); pwch = wcsrchr(pwcIniName, L'.'); if (pwch) { *pwch = L'\0'; } if (gpGetTermsrCompatFlags) { gpGetTermsrCompatFlags(pwcIniName, &ulCompatFlags, CompatibilityIniFile); } else { return FALSE; } // If the INISYNC compatibility flag is set in the registry and the // system version of the ini file exists, sync up the user version if (((ulCompatFlags & (TERMSRV_COMPAT_INISYNC | TERMSRV_COMPAT_WIN16)) == (TERMSRV_COMPAT_INISYNC | TERMSRV_COMPAT_WIN16)) && TermsrvDoesFileExist(&UniSysPath) && TermsrvCheckIniSync(&UniSysPath, &UserBasePath)) { // Create a backup copy of the original file (inifile.ctx) wcscpy(wcIniPath, UserBasePath.Buffer); if (UserBasePath.Buffer[UserBasePath.Length/sizeof(WCHAR) - 1] != L'\\') wcscat(wcIniPath, L"\\"); wcscat(wcIniPath, pwcIniName); wcscat(wcIniPath, L".ctx"); IniFilePath.Length = wcslen(wcIniPath)*sizeof(WCHAR); if (gpTermsrvCopyIniFile) { Status = gpTermsrvCopyIniFile(&a->NtFileName, NULL, &IniFilePath); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint("TermsrvSyncUserIniFile: Error 0x%x creating backup ini file %ws\n", Status, wcIniPath); } #endif } else { return FALSE; } // Check that each entry in the system version is in the user's version InitializeObjectAttributes(&SrcObja, &UniSysPath, OBJ_CASE_INSENSITIVE, NULL, NULL); // Open the src SrcIosb.Status = STATUS_SUCCESS; Status = NtOpenFile(&SrcHandle, FILE_GENERIC_READ, &SrcObja, &SrcIosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); if( NT_SUCCESS(Status) ) { // Get final I/O status Status = SrcIosb.Status; } if( !NT_SUCCESS(Status) ) { #if DBG DbgPrint("TermsrvSyncUserIniFile: Error 0x%x opening SrcFile %ws\n", Status, &UniSysPath.Buffer); #endif goto Cleanup; } // Save the original values OrigOperation = a->Operation; OrigMultiValue = a->MultiValueStrings; OrigAppName = a->ApplicationName; OrigVarName = a->VariableName; OrigResultChars = a->ResultChars; OrigResultMaxChars = a->ResultMaxChars; OrigResultBuffer = a->ResultBuffer; OrigUnicode = a->Unicode; OrigWriteOperation = a->WriteOperation; // Set up the open for writes a->WriteOperation = TRUE; a->Operation = WriteKeyValue; a->MultiValueStrings = FALSE; a->Unicode = FALSE; Status = BaseDllOpenIniFileOnDisk( a ); if( !NT_SUCCESS(Status) ) { #if DBG DbgPrint("TermsrvSyncUserIniFile: Error 0x%x opening DestFile %ws\n", Status, &a->NtFileName.Buffer); #endif NtClose( SrcHandle ); goto Cleanup; } // set the data up for writing a->TextEnd = (PCHAR)a->IniFile->BaseAddress + a->IniFile->EndOfFile; a->TextCurrent = a->IniFile->BaseAddress; // Make sure entries in system ini file are in user ini file Status = TermsrvIniSyncLoop( SrcHandle, a, &fIniUpdated ); #if DBG if( !NT_SUCCESS(Status) ) { DbgPrint("TermsrvSyncUserIniFile: Error 0x%x Doing sync loop\n",Status); } #endif // Close the file handles NtClose( SrcHandle ); BaseDllCloseIniFileOnDisk( a ); // Restore the variables in the ini file structure a->Operation = OrigOperation; a->MultiValueStrings = OrigMultiValue; a->ApplicationName = OrigAppName; a->VariableName = OrigVarName; a->ResultChars = OrigResultChars; a->ResultMaxChars = OrigResultMaxChars; a->ResultBuffer = OrigResultBuffer; a->WriteOperation = FALSE; a->Unicode = OrigUnicode; a->WriteOperation = OrigWriteOperation; // Get the last write time of the system ini file InitializeObjectAttributes( &objaIni, &UniSysPath, OBJ_CASE_INSENSITIVE, NULL, NULL ); // Now query it Status = NtQueryFullAttributesFile( &objaIni, &BasicInfo ); // Update the sync time in the inisync file if (NT_SUCCESS(Status)) { TermsrvPutSyncTime( &UniSysPath, &UserBasePath, &BasicInfo.LastWriteTime ); } } Cleanup: // Free the unicode buffers RtlFreeHeap( RtlProcessHeap(), 0, UniSysPath.Buffer ); RtlFreeHeap( RtlProcessHeap(), 0, UserBasePath.Buffer ); RtlFreeHeap( RtlProcessHeap(), 0, pwcIniName); return(fIniUpdated); } /***************************************************************************** * * TermsrvIniSyncLoop * * This routine will verify that there's a corresponding entry in the user's * ini file for each entry in the system ini file. * * ENTRY: * HANDLE SrcHandle (INPUT) - Handle to system ini file * PINIFILE_PARAMETERS a (INPUT) - pointer to current ini file structure * PBOOLEAN pfIniUpdated (OUTPUT) - Returns TRUE if user ini file is modified * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/ NTSTATUS TermsrvIniSyncLoop(HANDLE SrcHandle, PINIFILE_PARAMETERS a, PBOOLEAN pfIniUpdated) { PCHAR pStr; NTSTATUS Status; ULONG StringSize; CHAR IOBuf[512]; ULONG IOBufSize = 512; ULONG IOBufIndex = 0; ULONG IOBufFillSize = 0; ANSI_STRING AnsiSection; PCH pch; PVOID pSection, origbase; AnsiSection.Buffer = NULL; *pfIniUpdated = FALSE; while( 1 ) { pStr = NULL; StringSize = 0; if (gpTermsrvGetString == NULL) { return STATUS_UNSUCCESSFUL; } // Get a string from the source ini file Status = gpTermsrvGetString(SrcHandle, &pStr, &StringSize, IOBuf, IOBufSize, &IOBufIndex, &IOBufFillSize); if( !NT_SUCCESS(Status) ) { ASSERT( pStr == NULL ); if( Status == STATUS_END_OF_FILE ) { Status = STATUS_SUCCESS; } if (AnsiSection.Buffer) { RtlFreeHeap( RtlProcessHeap(), 0, AnsiSection.Buffer ); } a->IniFile->UpdateEndOffset = a->IniFile->EndOfFile; return( Status ); } // Make sure we got some actual data ASSERT( pStr != NULL ); // Is this a section name? if (*pStr == '[') { if (AnsiSection.Buffer) { RtlFreeHeap( RtlProcessHeap(), 0, AnsiSection.Buffer ); AnsiSection.Buffer = NULL; } pch = strrchr(pStr, ']'); if (pch) { AnsiSection.MaximumLength = (USHORT)(pch - pStr); *pch = '\0'; } else { AnsiSection.Length = (USHORT)strlen(pStr); } AnsiSection.Length = AnsiSection.MaximumLength - 1; AnsiSection.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, AnsiSection.MaximumLength); if (!AnsiSection.Buffer) { return STATUS_INSUFFICIENT_RESOURCES; } strcpy(AnsiSection.Buffer, pStr+1); a->ApplicationName = AnsiSection; a->TextCurrent = a->IniFile->BaseAddress; // reset file pointer // See if the section already exists, if so save the start of it Status = BaseDllFindSection( a ); if (NT_SUCCESS(Status)) { pSection = a->TextCurrent; } else { pSection = NULL; } // If it's not a comment, see if the entry is in the user's ini file } else if (*pStr != ';') { pch = strchr(pStr, '='); if (pch) { a->VariableName.Length = a->VariableName.MaximumLength = (USHORT)(pch - pStr); a->VariableName.Buffer = pStr; a->ValueBuffer = (++pch); a->ValueLength = 0; while (*pch && (*pch != 0xa) && (*pch != 0xd)) { pch++; a->ValueLength++; } // If the section exists, check for the keyword in user's ini if (pSection) { a->TextCurrent = pSection; Status = BaseDllFindKeyword( a ); } // If variable isn't found, write it out if (!pSection || !NT_SUCCESS( Status )) { origbase = a->TextCurrent = a->IniFile->BaseAddress; Status = BaseDllWriteKeywordValue( a, NULL ); a->TextEnd = (PCHAR)a->IniFile->BaseAddress + a->IniFile->EndOfFile; if (!NT_SUCCESS(Status)) { #if DBG DbgPrint("TermsrvIniSyncLoop: Error 0x%x write Key Value\n", Status); #endif a->IniFile->UpdateEndOffset = a->IniFile->EndOfFile; RtlFreeHeap( RtlProcessHeap(), 0, pStr ); if (AnsiSection.Buffer) { RtlFreeHeap(RtlProcessHeap(), 0, AnsiSection.Buffer); } return(Status); } *pfIniUpdated = TRUE; if (origbase != a->IniFile->BaseAddress) { a->TextCurrent = a->IniFile->BaseAddress; Status = BaseDllFindSection( a ); if (NT_SUCCESS(Status)) { pSection = a->TextCurrent; } else { pSection = NULL; } } } } } } // end while(1) } /****************************************************************************** * * GetPerUserWindowsDirectory * * * *****************************************************************************/ ULONG GetPerUserWindowsDirectory(PWCHAR TermSrvWindowsPath, ULONG len) { WCHAR Buf[MAX_PATH+1]; UNICODE_STRING BaseHomePathVariableName, BaseHomeDriveVariableName; NTSTATUS Status; UNICODE_STRING Path; if( !IsTSAppCompatEnabled() || IsSystemLUID() || (len < MAX_PATH) ) { return 0; } /* * Check for HOMEDRIVE and HOMEPATH */ RtlInitUnicodeString(&BaseHomeDriveVariableName,L"HOMEDRIVE"); RtlInitUnicodeString(&BaseHomePathVariableName,L"HOMEPATH"); Path.Buffer = Buf; Path.Length = 0; Path.MaximumLength = (MAX_PATH * sizeof(WCHAR)) - (9 * sizeof(WCHAR)); //MAX_PATH - wcslen(L"\\WINDOWS") + 1 if (NT_SUCCESS(Status = RtlQueryEnvironmentVariable_U( NULL, &BaseHomeDriveVariableName, &Path))) { Path.Buffer[Path.Length] = UNICODE_NULL; wcscpy(TermSrvWindowsPath,Path.Buffer); Path.MaximumLength -= Path.Length; if (NT_SUCCESS(Status = RtlQueryEnvironmentVariable_U( NULL, &BaseHomePathVariableName, &Path))) { Path.Buffer[Path.Length] = UNICODE_NULL; wcscat(TermSrvWindowsPath,Path.Buffer); } } if (!NT_SUCCESS(Status)) { #if DBG DbgPrint("GetPerUserWindowsDirectory Failed with Status %lx\n", Status); #endif return 0; } /* * Add a trailing backslash if one's not already there */ if (TermSrvWindowsPath[wcslen(TermSrvWindowsPath) -1 ] != L'\\') { wcscat(TermSrvWindowsPath,L"\\"); } wcscat(TermSrvWindowsPath,L"WINDOWS"); return( wcslen(TermSrvWindowsPath) ); }