/****************************************************************************** * * FILE.C * * This file contains routines based off the User Profile Editor utility. * * Copyright Citrix Systems, Inc. 1997 * Copyright (c) 1998-1999 Microsoft Corporation * * Author: Brad Anderson 1/20/97 * * $Log: M:\nt\private\utils\citrix\cprofile\VCS\file.c $ * * Rev 1.3 Jun 26 1997 18:18:38 billm * move to WF40 tree * * Rev 1.2 23 Jun 1997 16:13:20 butchd * update * * Rev 1.1 28 Jan 1997 20:06:34 BradA * Fixed up some problems related to WF 2.0 changes * * Rev 1.0 27 Jan 1997 20:03:44 BradA * Initial Version * ******************************************************************************/ /****************************** Module Header ******************************\ * Module Name: upesave.c * * Copyright (c) 1992, Microsoft Corporation * * Handles OPening and saving of Profiles: default, system, current and user * profiles. * \***************************************************************************/ #include "precomp.h" #pragma hdrstop #ifndef RC_INVOKED #include #include #include #include #include #endif #include "cprofile.h" HKEY hkeyCurrentUser; PSID gSystemSid; // Initialized in 'InitializeGlobalSids' PSID gAdminsLocalGroup; // Initialized in 'InitializeGlobalSids SID_IDENTIFIER_AUTHORITY gNtAuthority = SECURITY_NT_AUTHORITY; #define SYSTEM_DEFAULT_SUBKEY TEXT(".DEFAULT") #define TEMP_USER_SUBKEY TEXT("TEMP_USER") #define TEMP_USER_HIVE_PATH TEXT("%systemroot%\\system32\\config\\") #define TEMP_SAVE_HIVE TEXT("%systemroot%\\system32\\config\\HiveSave") #define CITRIX_CLASSES L"\\Registry\\Machine\\Software\\Classes" LPTSTR lpTempUserHive = NULL; LPTSTR lpTempUserHivePath = NULL; LPTSTR lpTempHiveKey; extern TCHAR szDefExt[]; extern PSID gSystemSid; BOOL AllocAndExpandEnvironmentStrings(LPTSTR String, LPTSTR *lpExpandedString); VOID GetRegistryKeyFromPath(LPTSTR lpPath, LPTSTR *lpKey); NTSTATUS CtxDeleteKeyTree( HANDLE hKeyRoot, PKEY_BASIC_INFORMATION pKeyInfo, ULONG ulInfoSize ); PSECURITY_DESCRIPTOR GetSecurityInfo( LPTSTR File ); void FreeSecurityInfo(PSECURITY_DESCRIPTOR); /***************************************************************************\ * ClearTempUserProfile * * Purpose : unloads the temp user profile loaded from a file, and deletes * the temp file * * History: * 11-20-92 JohanneC Created. \***************************************************************************/ BOOL APIENTRY ClearTempUserProfile() { BOOL bRet; if (hkeyCurrentUser == HKEY_CURRENT_USER) return(TRUE); // // Close registry keys. // if (hkeyCurrentUser) { RegCloseKey(hkeyCurrentUser); } hkeyCurrentUser = HKEY_CURRENT_USER; bRet = (RegUnLoadKey(HKEY_USERS, lpTempHiveKey) == ERROR_SUCCESS); if (*lpTempUserHive) { DeleteFile(lpTempUserHive); lstrcat(lpTempUserHive, TEXT(".log")); DeleteFile(lpTempUserHive); LocalFree(lpTempUserHive); lpTempUserHive = NULL; } return(bRet); } /***************************************************************************\ * OpenUserProfile * * Purpose : Load an existing profile in the registry and unload previously * loaded profile (and delete its tmp file). * * History: * 11-20-92 JohanneC Created. \***************************************************************************/ BOOL APIENTRY OpenUserProfile(LPTSTR szFilePath, PSID *pUserSid) { DWORD err; // // Copy the profile to a temp hive before loading it in the registry. // if (!lpTempUserHivePath) { if (!AllocAndExpandEnvironmentStrings(TEMP_USER_HIVE_PATH, &lpTempUserHivePath)) return(FALSE); } lpTempUserHive = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * (lstrlen(lpTempUserHivePath) + 17)); if (!lpTempUserHive) { return(FALSE); } if (!GetTempFileName(lpTempUserHivePath, TEXT("tmp"), 0, lpTempUserHive)) { lstrcpy(lpTempUserHive, lpTempUserHivePath); lstrcat(lpTempUserHive, TEXT("\\HiveOpen")); } if (CopyFile(szFilePath, lpTempUserHive, FALSE)) { GetRegistryKeyFromPath(lpTempUserHive, &lpTempHiveKey); if ((err = RegLoadKey(HKEY_USERS, lpTempHiveKey, lpTempUserHive)) == ERROR_SUCCESS) { if ((err = RegOpenKeyEx(HKEY_USERS, lpTempHiveKey, 0, MAXIMUM_ALLOWED, &hkeyCurrentUser)) != ERROR_SUCCESS) { // // Error, do not have access to the profile. // ErrorPrintf(IDS_ERROR_PROFILE_LOAD_ERR, err); ClearTempUserProfile(); return(FALSE); } } else { DeleteFile(lpTempUserHive); lstrcat(lpTempUserHive, TEXT(".log")); DeleteFile(lpTempUserHive); LocalFree(lpTempUserHive); // // Could not load the user profile, check the error code // if (err == ERROR_BADDB) { // bad format: not a profile registry file ErrorPrintf(IDS_ERROR_BAD_PROFILE); return(FALSE); } else { // generic error message : Failed to load profile ErrorPrintf(IDS_ERROR_PROFILE_LOAD_ERR, err); return(FALSE); } } } else { // // An error occured trying to load the profile. // DeleteFile(lpTempUserHive); switch ( (err = GetLastError()) ) { case ERROR_SHARING_VIOLATION: ErrorPrintf(IDS_ERROR_PROFILE_INUSE); break; default: ErrorPrintf(IDS_ERROR_PROFILE_LOAD_ERR, err); break; } return(FALSE); } // // Get the permitted user // *pUserSid = NULL; return(TRUE); } /***************************************************************************\ * SaveUserProfile * * Purpose : Save the loaded profile as a file. The registry should already * have the existing ACL's already set so nothing needs to change. The * file ACL's do need to be copied from the original and applied to the * saved file. This function assumes the orignal file exists. * \***************************************************************************/ BOOL APIENTRY SaveUserProfile(PSID pUserSid, LPTSTR lpFilePath) { LPTSTR lpTmpHive = NULL; BOOL err = FALSE; // // Save the profile to a temp hive then copy it over. // if ( AllocAndExpandEnvironmentStrings(TEMP_SAVE_HIVE, &lpTmpHive) ) { if( lpTmpHive != NULL ) { DeleteFile(lpTmpHive); if(RegSaveKey(hkeyCurrentUser, lpTmpHive, NULL) != ERROR_SUCCESS) { LocalFree(lpTmpHive); lpTmpHive = NULL; err = TRUE; } else { PSECURITY_DESCRIPTOR pSecDesc; DWORD Attrib = GetFileAttributes(lpFilePath); pSecDesc = GetSecurityInfo(lpFilePath); SetFileAttributes(lpFilePath,FILE_ATTRIBUTE_ARCHIVE); if(CopyFile(lpTmpHive, lpFilePath, FALSE)) { DeleteFile(lpTmpHive); LocalFree(lpTmpHive); lpTmpHive = NULL; if (pSecDesc) { SetFileSecurity(lpFilePath, DACL_SECURITY_INFORMATION, pSecDesc); FreeSecurityInfo(pSecDesc); } } else { if(pSecDesc) { FreeSecurityInfo(pSecDesc); } err = TRUE; } if(0xffffffff != Attrib) { SetFileAttributes(lpFilePath,Attrib); } } } } else { err = TRUE; } if( lpTmpHive != NULL ) { LocalFree(lpTmpHive); } return(!err); } /***************************************************************************\ * EnablePrivilege * * Enables/disabled the specified well-known privilege in the * current process context * * Returns TRUE on success, FALSE on failure * * History: * 12-05-91 Davidc Created \***************************************************************************/ BOOL EnablePrivilege( DWORD Privilege, BOOL Enable ) { NTSTATUS Status; #if 0 BOOL WasEnabled; Status = RtlAdjustPrivilege(Privilege, Enable, TRUE, (PBOOLEAN)&WasEnabled); return(NT_SUCCESS(Status)); #else HANDLE ProcessToken; LUID LuidPrivilege; PTOKEN_PRIVILEGES NewPrivileges; DWORD Length; // // Open our own token // Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &ProcessToken ); if (!NT_SUCCESS(Status)) { return(FALSE); } // // Initialize the privilege adjustment structure // LuidPrivilege = RtlConvertLongToLuid(Privilege); NewPrivileges = (PTOKEN_PRIVILEGES) LocalAlloc(LPTR, sizeof(TOKEN_PRIVILEGES) + (1 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES)); if (NewPrivileges == NULL) { NtClose(ProcessToken); return(FALSE); } NewPrivileges->PrivilegeCount = 1; NewPrivileges->Privileges[0].Luid = LuidPrivilege; // // WORKAROUND: because of a bug in NtAdjustPrivileges which // returns an error when you try to enable a privilege // that is already enabled, we first try to disable it. // to be removed when api is fixed. // NewPrivileges->Privileges[0].Attributes = 0; Status = NtAdjustPrivilegesToken( ProcessToken, // TokenHandle (BOOLEAN)FALSE, // DisableAllPrivileges NewPrivileges, // NewPrivileges 0, // BufferLength NULL, // PreviousState (OPTIONAL) &Length // ReturnLength ); NewPrivileges->Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0; // // Enable the privilege // Status = NtAdjustPrivilegesToken( ProcessToken, // TokenHandle (BOOLEAN)FALSE, // DisableAllPrivileges NewPrivileges, // NewPrivileges 0, // BufferLength NULL, // PreviousState (OPTIONAL) &Length // ReturnLength ); LocalFree(NewPrivileges); NtClose(ProcessToken); if (Status) { return(FALSE); } return(TRUE); #endif } BOOL AllocAndExpandEnvironmentStrings(LPTSTR String, LPTSTR *lpExpandedString) { LPTSTR lptmp = NULL; DWORD cchBuffer; // Get the number of characters needed. cchBuffer = ExpandEnvironmentStrings(String, lptmp, 0); if (cchBuffer) { cchBuffer++; // for NULL terminator lptmp = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * cchBuffer); if (!lptmp) { return(FALSE); } cchBuffer = ExpandEnvironmentStrings(String, lptmp, cchBuffer); } *lpExpandedString = lptmp; return(TRUE); } VOID GetRegistryKeyFromPath(LPTSTR lpPath, LPTSTR *lpKey) { LPTSTR lptmp; *lpKey = lpPath; for (lptmp = lpPath; *lptmp; lptmp++) { if (*lptmp == TEXT('\\')) { *lpKey = lptmp+1; } } } /***************************************************************************\ * InitializeGlobalSids * * Initializes the various global Sids used in this module. * * History: * 04-28-93 JohanneC Created \***************************************************************************/ VOID InitializeGlobalSids() { NTSTATUS Status; // // Build the admins local group SID // Status = RtlAllocateAndInitializeSid( &gNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &gAdminsLocalGroup ); // // create System Sid // Status = RtlAllocateAndInitializeSid( &gNtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &gSystemSid ); } /***************************************************************************** * * ClearDisabledClasses * * This routine will check the compatibility flags for the user's Classes * registry key, and remove the keys if mapping is disabled. * * ENTRY: * * EXIT: * No return value. * ****************************************************************************/ void ClearDisabledClasses(void) { NTSTATUS Status = STATUS_SUCCESS; ULONG ulcnt = 0, ultemp = 0; UNICODE_STRING UniPath; OBJECT_ATTRIBUTES ObjAttr; PKEY_BASIC_INFORMATION pKeyUserInfo = NULL; HANDLE hKeyUser = NULL; NTSTATUS Status2; HANDLE hClassesKey; WCHAR wcuser[MAX_PATH]; if ( ! hkeyCurrentUser) { return; } GetTermsrCompatFlags(CITRIX_CLASSES, &ultemp, CompatibilityRegEntry); if ( (ultemp & (TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) != (TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) { return; } // Get a buffer for the key info ulcnt = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH*sizeof(WCHAR) + sizeof(WCHAR); pKeyUserInfo = RtlAllocateHeap(RtlProcessHeap(), 0, ulcnt); if (!pKeyUserInfo) { Status = STATUS_NO_MEMORY; } // We have the necessary buffers, start checking the keys if (NT_SUCCESS(Status)) { // Build up a string for this user's software section wcscpy(wcuser, L"Software"); // Create a unicode string for the user key path RtlInitUnicodeString(&UniPath, wcuser); InitializeObjectAttributes(&ObjAttr, &UniPath, OBJ_CASE_INSENSITIVE, hkeyCurrentUser, NULL); Status = NtOpenKey(&hKeyUser, KEY_READ | DELETE, &ObjAttr); RtlInitUnicodeString(&UniPath, L"Classes"); InitializeObjectAttributes(&ObjAttr, &UniPath, OBJ_CASE_INSENSITIVE, hKeyUser, NULL); Status2 = NtOpenKey(&hClassesKey, KEY_READ | DELETE, &ObjAttr); if ( NT_SUCCESS(Status2) ) { Status2 = CtxDeleteKeyTree(hClassesKey, pKeyUserInfo, ulcnt); if ( !NT_SUCCESS(Status2)) { } NtClose(hClassesKey); } // If we allocated the system key, close it if (hKeyUser) { NtClose(hKeyUser); } } // Free up any memory we allocated if (pKeyUserInfo) { RtlFreeHeap( RtlProcessHeap(), 0, pKeyUserInfo); } } /***************************************************************************** * * CtxDeleteKeyTree * * Delete a subtree of registry keys * * ENTRY: * hKeyRoot is a handle to the root key that will be deleted along with * its children * pKeyInfo is a pointer to a KEY_BASIC_INFORMATION buffer that is * large enough to hold a MAX_PATH WCHAR string. It is * reused and destroyed by each recursive call. * ulInfoSize is the size of the pKeyInfo buffer * * EXIT: * Status * ****************************************************************************/ NTSTATUS CtxDeleteKeyTree( HANDLE hKeyRoot, PKEY_BASIC_INFORMATION pKeyInfo, ULONG ulInfoSize ) { NTSTATUS Status = STATUS_SUCCESS, Status2; UNICODE_STRING UniPath; OBJECT_ATTRIBUTES ObjAttr; ULONG ulcnt = 0; ULONG ultemp; HANDLE hKey; // Go through each of the subkeys while (NT_SUCCESS(Status)) { Status = NtEnumerateKey(hKeyRoot, ulcnt, KeyBasicInformation, pKeyInfo, ulInfoSize, &ultemp); // Delete sub keys if (NT_SUCCESS(Status)) { // Null terminate the key name pKeyInfo->Name[pKeyInfo->NameLength/sizeof(WCHAR)] = L'\0'; // Create a unicode string for the key name RtlInitUnicodeString(&UniPath, pKeyInfo->Name); InitializeObjectAttributes(&ObjAttr, &UniPath, OBJ_CASE_INSENSITIVE, hKeyRoot, NULL); // Open up the child key Status2 = NtOpenKey(&hKey, MAXIMUM_ALLOWED, &ObjAttr); if ( NT_SUCCESS(Status2) ) { Status2 = CtxDeleteKeyTree ( hKey, pKeyInfo, ulInfoSize ); NtClose(hKey); // If the key was not successfully deleted, we need // to increment the enumerate index to guarantee // that the alogorithm will complete. if ( !NT_SUCCESS(Status2) ) { ++ulcnt; } } } } // If we deleted all the sub-keys delete the curent key if ( !ulcnt ) { Status = NtDeleteKey(hKeyRoot); } else { Status = STATUS_CANNOT_DELETE; } return ( Status ); } PSECURITY_DESCRIPTOR GetSecurityInfo(LPTSTR lpFilePath) { int SizeReq = 0; PSECURITY_DESCRIPTOR pSecDesc = NULL; GetFileSecurity(lpFilePath, DACL_SECURITY_INFORMATION, pSecDesc, 0, &SizeReq); if ( !SizeReq ) { return (NULL); } pSecDesc = LocalAlloc(LPTR, SizeReq); if ( pSecDesc ) { if ( !GetFileSecurity(lpFilePath, DACL_SECURITY_INFORMATION, pSecDesc, SizeReq, &SizeReq) ) { LocalFree(pSecDesc); pSecDesc = NULL; } } return (pSecDesc); } void FreeSecurityInfo(PSECURITY_DESCRIPTOR pSecDesc) { LocalFree(pSecDesc); }