/*++ Copyright (c) 1992 Microsoft Corporation Module Name: regtool.c Abstract: This file contains functions for supporting the registry tools REGINI, REGDMP, REGDIR and REGFIND Author: Steve Wood (stevewo) 15-Nov-1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop LONG RegLoadHive( IN PREG_CONTEXT RegistryContext, IN PWSTR HiveFileName, IN PWSTR HiveRootName ); void RegUnloadHive( IN PREG_CONTEXT RegistryContext ); BOOLEAN PrivilegeEnabled; BOOLEAN RestoreWasEnabled; BOOLEAN BackupWasEnabled; BOOLEAN RTEnableBackupRestorePrivilege( void ) { NTSTATUS Status; // // Try to enable backup and restore privileges // Status = RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, TRUE, // Enable FALSE, // Not impersonating &RestoreWasEnabled // previous state ); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlAdjustPrivilege( SE_BACKUP_PRIVILEGE, TRUE, // Enable FALSE, // Not impersonating &BackupWasEnabled // previous state ); if (!NT_SUCCESS( Status )) { return FALSE; } PrivilegeEnabled = TRUE; return TRUE; } void RTDisableBackupRestorePrivilege( void ) { // // Restore privileges to what they were // RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, RestoreWasEnabled, FALSE, &RestoreWasEnabled ); RtlAdjustPrivilege( SE_BACKUP_PRIVILEGE, BackupWasEnabled, FALSE, &BackupWasEnabled ); PrivilegeEnabled = FALSE; return; } LONG RTConnectToRegistry( IN PWSTR MachineName, IN PWSTR HiveFileName, IN PWSTR HiveRootName, OUT PWSTR *DefaultRootKeyName, OUT PREG_CONTEXT RegistryContext ) { LONG Error; // // This code comes from a library that can support remote machine name // for our use though we never support remote machine name, so that // bit of code is commented out. // UNREFERENCED_PARAMETER( MachineName ); #if 0 if (MachineName != NULL) { if (HiveRootName || HiveFileName ) { return ERROR_INVALID_PARAMETER; } Error = RegConnectRegistry( MachineName, HKEY_LOCAL_MACHINE, (PHKEY)&RegistryContext->MachineRoot ); if (Error == NO_ERROR) { Error = RegConnectRegistry( MachineName, HKEY_USERS, (PHKEY)&RegistryContext->UsersRoot ); if (Error == NO_ERROR) { Error = RegOpenKey( RegistryContext->UsersRoot, L".Default", &RegistryContext->CurrentUserRoot ); } } if (Error != NO_ERROR) { if (RegistryContext->MachineRoot != NULL) { RegCloseKey( RegistryContext->MachineRoot ); RegistryContext->MachineRoot = NULL; } if (RegistryContext->UsersRoot != NULL) { RegCloseKey( RegistryContext->UsersRoot ); RegistryContext->UsersRoot = NULL; } return Error; } wcsncpy( RegistryContext->MachinePath, L"\\Registry\\Machine", MAX_PATH ); RegistryContext->MachinePath[MAX_PATH-1] = L'\0'; wcsncpy( RegistryContext->UsersPath, L"\\Registry\\Users", MAX_PATH ); RegistryContext->UsersPath[MAX_PATH-1] = L'\0'; wcsncpy( RegistryContext->CurrentUserPath, L"\\Registry\\Users\\.Default",MAX_PATH ); RegistryContext->CurrentUserPath[MAX_PATH-1] = L'\0'; RegistryContext->Target = REG_TARGET_REMOTE_REGISTRY; } else #endif if (HiveRootName != NULL || HiveFileName != NULL) { // If they sent us one, they need to send us both. if (HiveRootName == NULL || HiveFileName == NULL ) { return ERROR_INVALID_PARAMETER; } if (!PrivilegeEnabled && !RTEnableBackupRestorePrivilege()) { return ERROR_PRIVILEGE_NOT_HELD; } RegistryContext->MachineRoot = NULL; RegistryContext->UsersRoot = NULL; RegistryContext->CurrentUserRoot = NULL; Error = RegLoadHive( RegistryContext, HiveFileName, HiveRootName ); if (Error != NO_ERROR) { return Error; } if (DefaultRootKeyName != NULL && *DefaultRootKeyName == NULL) { *DefaultRootKeyName = HiveRootName; } RegistryContext->Target = REG_TARGET_HIVE_REGISTRY; } else { NTSTATUS Status; UNICODE_STRING CurrentUserKeyPath; RegistryContext->MachineRoot = HKEY_LOCAL_MACHINE; RegistryContext->UsersRoot = HKEY_USERS; RegistryContext->CurrentUserRoot = HKEY_CURRENT_USER; wcsncpy( RegistryContext->MachinePath, L"\\Registry\\Machine",MAX_PATH ); RegistryContext->MachinePath[MAX_PATH-1] = L'\0'; wcsncpy( RegistryContext->UsersPath, L"\\Registry\\Users",MAX_PATH ); RegistryContext->UsersPath[MAX_PATH-1] = L'\0'; Status = RtlFormatCurrentUserKeyPath( &CurrentUserKeyPath ); if (!NT_SUCCESS( Status )) { SetLastError( RtlNtStatusToDosError( Status ) ); return FALSE; } wcsncpy( RegistryContext->CurrentUserPath, CurrentUserKeyPath.Buffer, MAX_PATH ); RegistryContext->CurrentUserPath[MAX_PATH-1] = L'\0'; RtlFreeUnicodeString( &CurrentUserKeyPath ); RegistryContext->Target = REG_TARGET_LOCAL_REGISTRY; } if (DefaultRootKeyName != NULL && *DefaultRootKeyName == NULL) { *DefaultRootKeyName = L"\\Registry"; } RegistryContext->MachinePathLength = (DWORD)wcslen( RegistryContext->MachinePath ); RegistryContext->UsersPathLength = (DWORD)wcslen( RegistryContext->UsersPath ); RegistryContext->CurrentUserPathLength = (DWORD)wcslen( RegistryContext->CurrentUserPath ); return NO_ERROR; } LONG RTDisconnectFromRegistry( IN PREG_CONTEXT RegistryContext ) { switch ( RegistryContext->Target ) { case REG_TARGET_DISCONNECTED: break; case REG_TARGET_LOCAL_REGISTRY: break; case REG_TARGET_REMOTE_REGISTRY: break; case REG_TARGET_HIVE_REGISTRY: RegUnloadHive( RegistryContext ); break; } if (PrivilegeEnabled) { RTDisableBackupRestorePrivilege(); } RegistryContext->Target = REG_TARGET_DISCONNECTED; return NO_ERROR; } UNICODE_STRING RegHiveRootName; LONG RegLoadHive( IN PREG_CONTEXT RegistryContext, IN PWSTR HiveFileName, IN PWSTR HiveRootName ) { NTSTATUS Status; UNICODE_STRING NtFileName; OBJECT_ATTRIBUTES File; if (!RtlDosPathNameToNtPathName_U( HiveFileName, &NtFileName, NULL, NULL ) ) { return ERROR_BAD_PATHNAME; } InitializeObjectAttributes( &File, &NtFileName, OBJ_CASE_INSENSITIVE, NULL, NULL ); RtlInitUnicodeString( &RegHiveRootName, L"\\Registry"); InitializeObjectAttributes( &RegistryContext->HiveRootKey, &RegHiveRootName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey( &RegistryContext->HiveRootHandle, MAXIMUM_ALLOWED, &RegistryContext->HiveRootKey ); RtlFreeHeap(RtlProcessHeap(), 0, NtFileName.Buffer); if (!NT_SUCCESS(Status)) { return RtlNtStatusToDosError( Status ); } RtlInitUnicodeString( &RegHiveRootName, HiveRootName ); InitializeObjectAttributes( &RegistryContext->HiveRootKey, &RegHiveRootName, OBJ_CASE_INSENSITIVE, RegistryContext->HiveRootHandle, NULL ); NtUnloadKey( &RegistryContext->HiveRootKey ); Status = NtLoadKey( &RegistryContext->HiveRootKey, &File ); if (!NT_SUCCESS( Status )) { return RtlNtStatusToDosError( Status ); } return NO_ERROR; } void RegUnloadHive( IN PREG_CONTEXT RegistryContext ) { NTSTATUS Status; HANDLE Handle; PREG_CONTEXT_OPEN_HIVE_KEY p, p1; Status = NtOpenKey( &Handle, MAXIMUM_ALLOWED, &RegistryContext->HiveRootKey ); if (NT_SUCCESS( Status )) { NtFlushKey( Handle ); NtClose( Handle ); } p = RegistryContext->OpenHiveKeys; while (p) { RegCloseKey( p->KeyHandle ); p1 = p; p = p->Next; HeapFree( GetProcessHeap(), 0, p1 ); }; do { Status = NtUnloadKey( &RegistryContext->HiveRootKey ); } while (NT_SUCCESS( Status ) ); NtClose( RegistryContext->HiveRootHandle ); return; } DWORD RegRememberOpenKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle ) { PREG_CONTEXT_OPEN_HIVE_KEY p, *pp; DWORD RetVal = ERROR_SUCCESS; pp = &RegistryContext->OpenHiveKeys; while ((p = *pp) != NULL) { if (p->KeyHandle == KeyHandle) { p->ReferenceCount += 1; RetVal = ERROR_SUCCESS; goto exit; } else { pp = &p->Next; } } p = HeapAlloc( GetProcessHeap(), 0, sizeof( *p ) ); if (p != NULL) { p->KeyHandle = KeyHandle; p->ReferenceCount = 1; p->Next = NULL; *pp = p; RetVal = ERROR_SUCCESS; } else { RetVal = ERROR_OUTOFMEMORY; } exit: return(RetVal); } void RegForgetOpenKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle ) { PREG_CONTEXT_OPEN_HIVE_KEY p, *pp; pp = &RegistryContext->OpenHiveKeys; while ((p = *pp) != NULL) { if (p->KeyHandle == KeyHandle) { p->ReferenceCount -= 1; if (p->ReferenceCount == 0) { *pp = p->Next; HeapFree( GetProcessHeap(), 0, p ); return; } } else { pp = &p->Next; } } return; } BOOLEAN RegCheckPrefix( IN OUT PCWSTR *s, IN PCWSTR Prefix, IN ULONG PrefixLength ) { if (PrefixLength == 0) { return FALSE; } if (!_wcsnicmp( *s, Prefix, PrefixLength )) { *s += PrefixLength; return TRUE; } return FALSE; } BOOLEAN RegValidateKeyPath( IN PREG_CONTEXT RegistryContext, IN OUT PHKEY RootKeyHandle, IN OUT PCWSTR *SubKeyName ) { PCWSTR s; s = *SubKeyName; if (*RootKeyHandle == NULL) { if (RegCheckPrefix( &s, L"USER:", 5 ) || RegCheckPrefix( &s, L"HKEY_CURRENT_USER", 17 ) ) { if (RegistryContext->CurrentUserRoot == NULL) { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } if (*s == L'\\') { s += 1; } else if (s[-1] != L':' && *s != UNICODE_NULL) { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } *RootKeyHandle = RegistryContext->CurrentUserRoot; } else if (RegCheckPrefix( &s, L"HKEY_LOCAL_MACHINE", 18 )) { if (*s == L'\\') { s += 1; } else if (*s != UNICODE_NULL) { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } *RootKeyHandle = RegistryContext->MachineRoot; } else if (RegCheckPrefix( &s, L"HKEY_USERS", 10 )) { if (*s == L'\\') { s += 1; } else if (*s != UNICODE_NULL) { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } *RootKeyHandle = RegistryContext->UsersRoot; } else if (*s != L'\\') { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } else if (RegCheckPrefix( &s, RegistryContext->MachinePath, RegistryContext->MachinePathLength )) { *RootKeyHandle = RegistryContext->MachineRoot; if (*s == L'\\') { s += 1; } } else if (RegCheckPrefix( &s, RegistryContext->UsersPath, RegistryContext->UsersPathLength )) { *RootKeyHandle = RegistryContext->UsersRoot; if (*s == L'\\') { s += 1; } } else if (RegCheckPrefix( &s, RegistryContext->CurrentUserPath, RegistryContext->CurrentUserPathLength )) { *RootKeyHandle = RegistryContext->CurrentUserRoot; if (*s == L'\\') { s += 1; } } else if (!_wcsicmp( *SubKeyName, L"\\Registry" )) { *RootKeyHandle = NULL; } else { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } } else if (*s == L'\\') { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } *SubKeyName = s; return TRUE; } LONG RTCreateKey( IN PREG_CONTEXT RegistryContext, IN HKEY RootKeyHandle, IN PCWSTR SubKeyName, IN ACCESS_MASK DesiredAccess, IN ULONG CreateOptions, IN PVOID SecurityDescriptor, OUT PHKEY ReturnedKeyHandle, OUT PULONG Disposition ) { LONG Error; SECURITY_ATTRIBUTES SecurityAttributes; if (!RegValidateKeyPath( RegistryContext, &RootKeyHandle, &SubKeyName )) { return GetLastError(); } if (RootKeyHandle == NULL) { *Disposition = REG_OPENED_EXISTING_KEY; *ReturnedKeyHandle = HKEY_REGISTRY_ROOT; return NO_ERROR; } else if (RootKeyHandle == HKEY_REGISTRY_ROOT) { *ReturnedKeyHandle = NULL; if (!_wcsicmp( SubKeyName, L"Machine" )) { *ReturnedKeyHandle = RegistryContext->MachineRoot; } else if (!_wcsicmp( SubKeyName, L"Users" )) { *ReturnedKeyHandle = RegistryContext->UsersRoot; } if (*ReturnedKeyHandle != NULL) { return NO_ERROR; } else { return ERROR_PATH_NOT_FOUND; } } SecurityAttributes.nLength = sizeof( SecurityAttributes ); SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor; SecurityAttributes.bInheritHandle = FALSE; Error = RegCreateKeyEx( RootKeyHandle, SubKeyName, 0, NULL, CreateOptions, (REGSAM)DesiredAccess, &SecurityAttributes, ReturnedKeyHandle, Disposition ); if (Error == NO_ERROR && RegistryContext->Target == REG_TARGET_HIVE_REGISTRY ) { Error = RegRememberOpenKey( RegistryContext, *ReturnedKeyHandle ); } if (Error == NO_ERROR && *Disposition == REG_OPENED_EXISTING_KEY && SecurityDescriptor != NULL ) { RegSetKeySecurity( *ReturnedKeyHandle, DACL_SECURITY_INFORMATION, SecurityDescriptor ); } return Error; } LONG RTOpenKey( IN PREG_CONTEXT RegistryContext, IN HKEY RootKeyHandle, IN PCWSTR SubKeyName, IN ACCESS_MASK DesiredAccess, IN ULONG OpenOptions, OUT PHKEY ReturnedKeyHandle ) { LONG Error; if (!RegValidateKeyPath( RegistryContext, &RootKeyHandle, &SubKeyName )) { return GetLastError(); } if (RootKeyHandle == NULL) { *ReturnedKeyHandle = HKEY_REGISTRY_ROOT; return NO_ERROR; } else if (RootKeyHandle == HKEY_REGISTRY_ROOT) { *ReturnedKeyHandle = NULL; if (!_wcsicmp( SubKeyName, L"Machine" )) { *ReturnedKeyHandle = RegistryContext->MachineRoot; } else if (!_wcsicmp( SubKeyName, L"Users" )) { *ReturnedKeyHandle = RegistryContext->UsersRoot; } if (*ReturnedKeyHandle != NULL) { return NO_ERROR; } else { return ERROR_PATH_NOT_FOUND; } } Error = RegOpenKeyEx( RootKeyHandle, SubKeyName, OpenOptions, DesiredAccess, ReturnedKeyHandle ); if (Error == NO_ERROR && RegistryContext->Target == REG_TARGET_HIVE_REGISTRY ) { RegRememberOpenKey( RegistryContext, *ReturnedKeyHandle ); } return Error; } LONG RTCloseKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle ) { LONG Error; if (KeyHandle == HKEY_REGISTRY_ROOT) { return NO_ERROR; } else { Error = RegCloseKey( KeyHandle ); if (Error == NO_ERROR && RegistryContext->Target == REG_TARGET_HIVE_REGISTRY ) { RegForgetOpenKey( RegistryContext, KeyHandle ); } return Error; } } LONG RTEnumerateValueKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle, IN ULONG Index, OUT PULONG ValueType, IN OUT PULONG ValueNameLength, OUT PWSTR ValueName, IN OUT PULONG ValueDataLength, OUT PVOID ValueData ) { ULONG Error; UNREFERENCED_PARAMETER(RegistryContext); if (KeyHandle == HKEY_REGISTRY_ROOT) { return ERROR_NO_MORE_ITEMS; } else { Error = RegEnumValue( KeyHandle, Index, ValueName, ValueNameLength, NULL, ValueType, ValueData, ValueDataLength ); if (Error == NO_ERROR) { RtlZeroMemory( (PCHAR)ValueData + *ValueDataLength, 4 - (*ValueDataLength & 3) ); } return Error; } } LONG RTQueryValueKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle, IN PWSTR ValueName, OUT PULONG ValueType, IN OUT PULONG ValueDataLength, OUT PVOID ValueData ) { LONG Error; UNREFERENCED_PARAMETER(RegistryContext); Error = RegQueryValueEx( KeyHandle, ValueName, NULL, ValueType, ValueData, ValueDataLength ); if (Error == NO_ERROR) { RtlZeroMemory( (PCHAR)ValueData + *ValueDataLength, 4 - (*ValueDataLength & 3) ); } return Error; }