/*++ Copyright (c) 1992 Microsoft Corporation Module Name: Reglukey.c Abstract: This module contains the server side Win32 Registry APIs to load, unload and replace keys. That is: - BaseRegLoadKeyA - BaseRegLoadKeyW - BaseRegUnLoadKeyA - BaseRegUnLoadKeyW - BaseRegReplaceKeyA - BaseRegReplaceKeyW Author: Ramon J. San Andres (ramonsa) 16-Apr-1992 --*/ #include #include "regrpc.h" #include "localreg.h" error_status_t BaseRegLoadKey( IN HKEY hKey, IN PUNICODE_STRING lpSubKey OPTIONAL, IN PUNICODE_STRING lpFile ) /*++ Routine Description: Load the tree in the supplied file into the key referenced by the supplied key handle and sub-key. The loaded tree will overwrite all of the contents of the supplied sub-key except for its name. Pictorially, if the file contains: A / \ / \ B C and the supplied key refers to a key name X, the resultant tree would look like: X / \ / \ B C Arguments: hKey - Supplies the predefined handle HKEY_USERS or HKEY_LOCAL_MACHINE. lpSubKey is relative to this handle. lpSubKey - Supplies a path name to a new (i.e. non-existant) key where the supplied file will be loaded. lpFile - Supplies a pointer to an existing file name whose contents was created with RegSaveKey. The file name may not have an extension. Return Value: Returns ERROR_SUCCESS (0) for success; error-code for failure. Notes: The difference between RegRestoreKey and RegLoadKey is that in the latter case the supplied file is used as the actual backing store whereas in the former case the information in the file is copied into the Registry. RegLoadKey requires SeRestorePrivilege. --*/ { OBJECT_ATTRIBUTES ObjaKey; OBJECT_ATTRIBUTES ObjaFile; BOOLEAN ErrorFlag; UNICODE_STRING FileName; RTL_RELATIVE_NAME RelativeName; PVOID FreeBuffer; NTSTATUS NtStatus; PUNICODE_STRING SubKey; #if DBG //OutputDebugString( "WINREG: Entering BaseRegLoadKey\n" ); #endif ASSERT( (hKey != NULL) && (lpFile != NULL) && (lpFile->Buffer != NULL)); if ( (hKey == NULL) || (lpFile == NULL) || (lpFile->Buffer == NULL) ) { return ERROR_INVALID_PARAMETER; } // // check for oddly formed UNICODE_STRINGs passed by malicious clients // check also for zero-length strings // if ((!lpFile->Length) || (lpFile->Length & 1) || (lpFile->Buffer[(lpFile->Length-1)/sizeof(WCHAR)] != UNICODE_NULL)) { return ERROR_INVALID_PARAMETER; } if ((lpSubKey) && ((!lpSubKey->Length) || (lpSubKey->Length & 1) || (lpSubKey->Buffer[(lpSubKey->Length-1)/sizeof(WCHAR)] != UNICODE_NULL))) { return ERROR_INVALID_PARAMETER; } RPC_IMPERSONATE_CLIENT( NULL ); // // Remove terminating NULLs from Length counts. These were added // on the client side so that RPC would transmit the whole thing. // if ( lpSubKey && lpSubKey->Length > 0 ) { lpSubKey->Length -= sizeof( UNICODE_NULL ); SubKey = lpSubKey; } else { SubKey = NULL; } if ( lpFile->Length > 0 ) { lpFile->Length -= sizeof( UNICODE_NULL ); } InitializeObjectAttributes( &ObjaKey, SubKey, OBJ_CASE_INSENSITIVE, hKey, NULL ); // // Convert the DOS path name to a canonical Nt path name. // ErrorFlag = RtlDosPathNameToNtPathName_U( lpFile->Buffer, &FileName, NULL, &RelativeName ); // // If the name was not succesfully converted assume it was invalid. // if ( !ErrorFlag ) { RPC_REVERT_TO_SELF(); return ERROR_INVALID_PARAMETER; } // // Remember the buffer allocatted by RtlDosPathNameToNtPathName_U. // FreeBuffer = FileName.Buffer; // // If a relative name and directory handle will work, use those. // if ( RelativeName.RelativeName.Length ) { // // Replace the full path with the relative path. // FileName = *( PUNICODE_STRING ) &RelativeName.RelativeName; } else { // // Using the full path - no containing directory. // RelativeName.ContainingDirectory = NULL; } // // Initialize the Obja structure for the file. // InitializeObjectAttributes( &ObjaFile, &FileName, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, NULL ); #if DBG //OutputDebugString( "WINREG: Before NtLoadKey\n" ); #endif NtStatus = NtLoadKey( &ObjaKey, &ObjaFile ); #if DBG //OutputDebugString( "WINREG: After RegLoadKey\n" ); #endif RPC_REVERT_TO_SELF(); // // Free the buffer allocatted by RtlDosPathNameToNtPathName_U. // RtlFreeHeap( RtlProcessHeap( ), 0, FreeBuffer ); #if DBG //OutputDebugString( "WINREG: Leaving BaseRegLoadKey\n" ); #endif return (error_status_t)RtlNtStatusToDosError( NtStatus ); } error_status_t BaseRegUnLoadKey( IN HKEY hKey, IN PUNICODE_STRING lpSubKey OPTIONAL ) /*++ Routine Description: Unload the specified tree (hive) from the Registry. Arguments: hKey - Supplies a handle to an open key. lpSubKey is relative to this handle. lpSubKey - Supplies a path name to the key that is to be unloaded. The combination of hKey and lpSubKey must refer to a hive in the Registry created with RegRestoreKey or RegLoadKey. This parameter may be NULL. Return Value: Returns ERROR_SUCCESS (0) for success; error-code for failure. RegUnLoadKey requires SeRestorePrivilege. --*/ { OBJECT_ATTRIBUTES ObjaKey; NTSTATUS NtStatus; ASSERT( hKey != NULL ); if ( hKey == NULL ) { return ERROR_INVALID_PARAMETER; } RPC_IMPERSONATE_CLIENT( NULL ); // // Remove terminating NULLs from Length counts. These were added // on the client side so that RPC would transmit the whole thing. // if ( lpSubKey && lpSubKey->Length > 0 ) { lpSubKey->Length -= sizeof( UNICODE_NULL ); } InitializeObjectAttributes( &ObjaKey, lpSubKey, OBJ_CASE_INSENSITIVE, hKey, NULL ); NtStatus = NtUnloadKey( &ObjaKey ); RPC_REVERT_TO_SELF(); return (error_status_t)RtlNtStatusToDosError( NtStatus ); } error_status_t BaseRegReplaceKey( HKEY hKey, PUNICODE_STRING lpSubKey, PUNICODE_STRING lpNewFile, PUNICODE_STRING lpOldFile ) /*++ Routine Description: Replace an existing tree (hive) in the Registry. The new tree will take effect the next time the system is rebooted. Arguments: hKey - Supplies a handle to an open key. lpSubKey is relative to this handle. lpSubKey - Supplies a path name to the key that is to be replaced. The combination of hKey and lpSubKey must refer to a hive in the Registry. This parameter may be NULL. lpNewFile - Supplies a file name for the new hive file. lpOldFile - Supplies a backup file name for the old (existing) hive file. Return Value: Returns ERROR_SUCCESS (0) for success; error-code for failure. Notes: lpNewFile will remain open until after the system is rebooted. RegUnLoadKey requires SeRestorePrivilege. --*/ { UNICODE_STRING NewFileName; UNICODE_STRING OldFileName; RTL_RELATIVE_NAME RelativeName; PVOID NewFreeBuffer; PVOID OldFreeBuffer; HANDLE HiveHandle; OBJECT_ATTRIBUTES ObjaKey; OBJECT_ATTRIBUTES ObjaNewFile; OBJECT_ATTRIBUTES ObjaOldFile; BOOLEAN ErrorFlag; NTSTATUS NtStatus; ErrorFlag = (BOOLEAN)( (hKey == NULL) || (lpNewFile == NULL) || (lpNewFile->Buffer == NULL) || (lpOldFile == NULL) || (lpOldFile->Buffer == NULL) ); ASSERT( !ErrorFlag ); if ( ErrorFlag ) { return ERROR_INVALID_PARAMETER; } RPC_IMPERSONATE_CLIENT( NULL ); // // Remove terminating NULLs from Length counts. These were added // on the client side so that RPC would transmit the whole thing. // if ( lpSubKey && lpSubKey->Length > 0 ) { lpSubKey->Length -= sizeof( UNICODE_NULL ); } if ( lpNewFile->Length > 0 ) { lpNewFile->Length -= sizeof( UNICODE_NULL ); } if ( lpOldFile->Length > 0 ) { lpOldFile->Length -= sizeof( UNICODE_NULL ); } InitializeObjectAttributes( &ObjaKey, lpSubKey, OBJ_CASE_INSENSITIVE, hKey, NULL ); // // Get a handle to the hive root // NtStatus = NtCreateKey( &HiveHandle, MAXIMUM_ALLOWED, &ObjaKey, 0, NULL, REG_OPTION_BACKUP_RESTORE, NULL ); if ( !NT_SUCCESS( NtStatus ) ) { RPC_REVERT_TO_SELF(); return (error_status_t)RtlNtStatusToDosError( NtStatus ); } // // Convert the new DOS path name to a canonical Nt path name. // ErrorFlag = RtlDosPathNameToNtPathName_U( lpNewFile->Buffer, &NewFileName, NULL, &RelativeName ); // // If the name was not succesfully converted assume it was invalid. // if ( !ErrorFlag ) { NtClose( HiveHandle ); RPC_REVERT_TO_SELF(); return ERROR_INVALID_PARAMETER; } // // Remember the buffer allocatted by RtlDosPathNameToNtPathName_U. // NewFreeBuffer = NewFileName.Buffer; // // If a relative name and directory handle will work, use those. // if ( RelativeName.RelativeName.Length ) { // // Replace the full path with the relative path. // NewFileName = *( PUNICODE_STRING ) &RelativeName.RelativeName; } else { // // Using the full path - no containing directory. // RelativeName.ContainingDirectory = NULL; } // // Initialize the Obja structure for the new file. // InitializeObjectAttributes( &ObjaNewFile, &NewFileName, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, NULL ); // // Convert the old DOS path name to a canonical Nt path name. // ErrorFlag = RtlDosPathNameToNtPathName_U( lpOldFile->Buffer, &OldFileName, NULL, &RelativeName ); // // If the name was not succesfully converted assume it was invalid. // if ( !ErrorFlag ) { RtlFreeHeap( RtlProcessHeap( ), 0, NewFreeBuffer ); NtClose( HiveHandle ); RPC_REVERT_TO_SELF(); return ERROR_INVALID_PARAMETER; } // // Remember the buffer allocatted by RtlDosPathNameToNtPathName_U. // OldFreeBuffer = OldFileName.Buffer; // // If a relative name and directory handle will work, use those. // if ( RelativeName.RelativeName.Length ) { // // Replace the full path with the relative path. // OldFileName = *( PUNICODE_STRING ) &RelativeName.RelativeName; } else { // // Using the full path - no containing directory. // RelativeName.ContainingDirectory = NULL; } // // Initialize the Obja structure for the new file. // InitializeObjectAttributes( &ObjaOldFile, &OldFileName, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, NULL ); NtStatus = NtReplaceKey( &ObjaNewFile, HiveHandle, &ObjaOldFile ); // // Free the buffers allocatted by RtlDosPathNameToNtPathName_U. // RtlFreeHeap( RtlProcessHeap( ), 0, NewFreeBuffer ); RtlFreeHeap( RtlProcessHeap( ), 0, OldFreeBuffer ); NtClose( HiveHandle ); RPC_REVERT_TO_SELF(); return (error_status_t)RtlNtStatusToDosError( NtStatus ); }