/*++ Copyright (c) 1992 Microsoft Corporation Module Name: Predefh.c Abstract: This module contains the client side support for managing the Win32 Registry API's predefined handles. This support is supplied via a table, which maps (a) predefined handles to real handles and (b) the server side routine which opens the handle. Author: David J. Gilman (davegi) 15-Nov-1991 Notes: See the notes in server\predefh.c. --*/ #include #include "regrpc.h" #include "client.h" #if defined(LEAK_TRACK) NTSTATUS TrackObject(HKEY hKey); #endif // LEAK_TRACK RTL_CRITICAL_SECTION PredefinedHandleTableCriticalSection; // // For each predefined handle an entry is maintained in an array. Each of // these structures contains a real (context) handle and a pointer to a // function that knows how to map the predefined handle to the Registry name // space. // // // Pointer to function to open predefined handles. // typedef error_status_t ( *OPEN_FUNCTION ) ( PREGISTRY_SERVER_NAME, REGSAM, PRPC_HKEY ); // // Table entry for a predefined handle. // typedef struct _PRDEFINED_HANDLE { RPC_HKEY Handle; OPEN_FUNCTION OpenFunc; BOOLEAN Disabled; // tells whether the handle should be cached or not. #if DBG ULONG Callers; PVOID CallerAddress[10]; #endif } PREDEFINED_HANDLE, *PPREDEFINED_HANDLE; // // Initialize predefined handle table. // PREDEFINED_HANDLE PredefinedHandleTable[ ] = { NULL, LocalOpenClassesRoot, FALSE #if DBG , 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } #endif , NULL, LocalOpenCurrentUser, FALSE #if DBG , 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } #endif , NULL, LocalOpenLocalMachine, FALSE #if DBG , 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } #endif , NULL, LocalOpenUsers, FALSE #if DBG , 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } #endif , NULL, LocalOpenPerformanceData, FALSE #if DBG , 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } #endif , NULL, LocalOpenPerformanceText, FALSE #if DBG , 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } #endif , NULL, LocalOpenPerformanceNlsText, FALSE #if DBG , 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } #endif , NULL, LocalOpenCurrentConfig, FALSE #if DBG , 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } #endif , NULL, LocalOpenDynData, FALSE #if DBG , 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } #endif }; #define MAX_PREDEFINED_HANDLES \ ( sizeof( PredefinedHandleTable ) / sizeof( PREDEFINED_HANDLE )) // // Predefined HKEY values are defined in Winreg.x. They MUST be kept in // synch with the following constants and macros. // // // Mark Registry handles so that we can recognize predefined handles. // #define PREDEFINED_REGISTRY_HANDLE_SIGNATURE ( 0x80000000 ) NTSTATUS SetHandleProtection( IN HANDLE Handle, IN LONG Index, IN BOOLEAN Protect ) /*++ Routine Description: Changes the handle ProtectFromClose attribute. To be used for predefined handles, to prevent abnormal closure. Arguments: Handle - Supplies the handle who's protection to be changed. Index - Index in the predefined handle table Return Value: Status of the NtSetInformationObject call --*/ { NTSTATUS Status; OBJECT_HANDLE_FLAG_INFORMATION Ohfi = { FALSE, FALSE }; ULONG PredefHandle; PredefHandle = ((ULONG)Index) | PREDEFINED_REGISTRY_HANDLE_SIGNATURE; switch (PredefHandle) { case (ULONG)((ULONG_PTR)HKEY_CLASSES_ROOT): case (ULONG)((ULONG_PTR)HKEY_CURRENT_USER): case (ULONG)((ULONG_PTR)HKEY_LOCAL_MACHINE): case (ULONG)((ULONG_PTR)HKEY_USERS): // // go change the protection // break; default: // // The supplied handle might not be a real handle // return STATUS_INVALID_HANDLE; } Ohfi.ProtectFromClose = Protect; Status = NtSetInformationObject(Handle, ObjectHandleFlagInformation, &Ohfi, sizeof (OBJECT_HANDLE_FLAG_INFORMATION)); #if DBG if (!NT_SUCCESS(Status)) { DbgPrint( "WINREG: SetHandleProtection (%u) on %lx failed. Status = %lx \n",Protect, Handle, Status ); } #endif return Status; } LONG MapPredefinedRegistryHandleToIndex( IN ULONG Handle ) /*++ Routine Description: Maps a predefined handle to an index into the predefined handle table. Arguments: Handle - Supplies the handle to be mapped. Return Value: An index into the predefined handle table -1 if the handle is not a predefined handle --*/ { LONG Index; switch (Handle) { case (ULONG)((ULONG_PTR)HKEY_CLASSES_ROOT): case (ULONG)((ULONG_PTR)HKEY_CURRENT_USER): case (ULONG)((ULONG_PTR)HKEY_LOCAL_MACHINE): case (ULONG)((ULONG_PTR)HKEY_USERS): case (ULONG)((ULONG_PTR)HKEY_PERFORMANCE_DATA): Index = (Handle & ~PREDEFINED_REGISTRY_HANDLE_SIGNATURE); break; case (ULONG)((ULONG_PTR)HKEY_PERFORMANCE_TEXT): Index = 5; break; case (ULONG)((ULONG_PTR)HKEY_PERFORMANCE_NLSTEXT): Index = 6; break; case (ULONG)((ULONG_PTR)HKEY_CURRENT_CONFIG): Index = 7; break; case (ULONG)((ULONG_PTR)HKEY_DYN_DATA): Index = 8; break; default: // // The supplied handle is not predefined, so return it. // Index = -1; break; } return(Index); } NTSTATUS RemapPredefinedHandle( IN RPC_HKEY Handle, IN RPC_HKEY NewHandle ) /*++ Routine Description: Override the current predefined handle. If it is already open, close it, then set the new handle Arguments: Handle - Supplies a handle which must be a predefined handle NewHandle - an already open registry key to override the special key Return Value: ERROR_SUCCESS - no problems --*/ { LONG Index; LONG Error; NTSTATUS Status; HANDLE hCurrentProcess; HKEY hkTableHandle = NULL; // // If the high bit is not set, we know it's not a predefined handle // so take a quick out. // if (((ULONG_PTR)Handle & 0x80000000) == 0) { return(STATUS_INVALID_HANDLE); } Status = RtlEnterCriticalSection( &PredefinedHandleTableCriticalSection ); ASSERT( NT_SUCCESS( Status ) ); if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "WINREG: RtlEnterCriticalSection() on RemapPredefinedHandle() failed. Status = %lx \n", Status ); #endif Status = ERROR_INVALID_HANDLE; goto cleanup_and_exit; } Index = MapPredefinedRegistryHandleToIndex((ULONG)(ULONG_PTR)Handle); if (Index == -1) { Status = STATUS_INVALID_HANDLE; goto leave_crit_sect; } ASSERT(( 0 <= Index ) && ( Index < MAX_PREDEFINED_HANDLES )); if( PredefinedHandleTable[ Index ].Disabled == TRUE ) { // // predefined table is disabled for this key // // nobody is allowed to write here ASSERT( PredefinedHandleTable[ Index ].Handle == NULL ); // refuse the request Status = STATUS_INVALID_HANDLE; goto leave_crit_sect; } hCurrentProcess = NtCurrentProcess(); // // see if we can duplicate this handle so we can place it // in the table // if (NewHandle && !NT_SUCCESS(Status = NtDuplicateObject (hCurrentProcess, NewHandle, hCurrentProcess, &hkTableHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))) { goto leave_crit_sect; } if (NewHandle && IsSpecialClassesHandle(NewHandle)) { TagSpecialClassesHandle( &hkTableHandle ); } // // If the predefined handle has already been opened try // and close the key now. // if( PredefinedHandleTable[ Index ].Handle != NULL ) { // make sure the handle CAN be closed. SetHandleProtection(PredefinedHandleTable[ Index ].Handle,Index,FALSE); #if DBG PredefinedHandleTable[ Index ].Callers = RtlWalkFrameChain(&(PredefinedHandleTable[ Index ].CallerAddress[0]), 10, 0); #endif Error = (LONG) RegCloseKey( PredefinedHandleTable[ Index ].Handle ); #if DBG if ( Error != ERROR_SUCCESS ) { DbgPrint( "Winreg.dll: Cannot close predefined handle\n" ); DbgPrint( " Handle: 0x%x Index: %d Error: %d\n", Handle, Index, Error ); } #endif } PredefinedHandleTable[ Index ].Handle = hkTableHandle; // make sure the handle CANNOT be closed. SetHandleProtection(PredefinedHandleTable[ Index ].Handle,Index,TRUE); leave_crit_sect: #if DBG { NTSTATUS Status2 = #endif DBG RtlLeaveCriticalSection( &PredefinedHandleTableCriticalSection ); ASSERT( NT_SUCCESS( Status2 ) ); #if DBG if ( !NT_SUCCESS( Status2 ) ) { DbgPrint( "WINREG: RtlLeaveCriticalSection() on RemapPredefinedHandle() failed. Status = %lx \n", Status2 ); } } #endif cleanup_and_exit: if (!NT_SUCCESS(Status) && hkTableHandle) { RegCloseKey(hkTableHandle); } return( Status ); } RPC_HKEY MapPredefinedHandle( IN RPC_HKEY Handle, OUT PRPC_HKEY HandleToClose ) /*++ Routine Description: Attempt to map a predefined handle to a RPC context handle. This in turn will map to a real Nt Registry handle. Arguments: Handle - Supplies a handle which may be a predefined handle or a handle returned from a previous call to any flavour of RegCreateKey, RegOpenKey or RegConnectRegistry. HandleToClose - When not NULL, this is the same as the result. Used to implement the DisablePredefinedCache feature. Return Value: RPC_HKEY- Returns the supplied handle argument if it not predefined, a RPC context handle if possible (i.e. it was previously opened or can be opened now), NULL otherwise. --*/ { LONG Index; LONG Error; NTSTATUS Status; HANDLE ResultHandle; *HandleToClose = NULL; // reject outrageous calls if( Handle == INVALID_HANDLE_VALUE ) { return( NULL ); } // // If the high bit is not set, we know it's not a predefined handle // so take a quick out. // if (((ULONG_PTR)Handle & 0x80000000) == 0) { return(Handle); } Index = MapPredefinedRegistryHandleToIndex((ULONG)(ULONG_PTR)Handle); if (Index == -1) { return(Handle); } ASSERT(( 0 <= Index ) && ( Index < MAX_PREDEFINED_HANDLES )); Status = RtlEnterCriticalSection( &PredefinedHandleTableCriticalSection ); ASSERT( NT_SUCCESS( Status ) ); if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "WINREG: RtlEnterCriticalSection() on MapPredefinedHandle() failed. Status = %lx \n", Status ); #endif return( NULL ); } if( PredefinedHandleTable[ Index ].Disabled == TRUE ) { // // for this handle the predefined feature has been disabled // // nobody is allowed to write here ASSERT( PredefinedHandleTable[ Index ].Handle == NULL ); // // open a new handle for this request and store it in "toClose" // argument so the caller knows that should close it // ( *PredefinedHandleTable[ Index ].OpenFunc )( NULL, MAXIMUM_ALLOWED, HandleToClose ); // return the new handle to the caller ResultHandle = *HandleToClose; } else { // // If the predefined handle has not already been openend try // and open the key now. // if( PredefinedHandleTable[ Index ].Handle == NULL ) { Error = (LONG)( *PredefinedHandleTable[ Index ].OpenFunc )( NULL, MAXIMUM_ALLOWED, &PredefinedHandleTable[ Index ].Handle ); if( Error == ERROR_SUCCESS ) { // make sure the handle CANNOT be closed. SetHandleProtection(PredefinedHandleTable[ Index ].Handle,Index,TRUE); } #if defined(LEAK_TRACK) if (g_RegLeakTraceInfo.bEnableLeakTrack) { (void) TrackObject(PredefinedHandleTable[ Index ].Handle); } #endif // defined(LEAK_TRACK) #if DBG if ( Error != ERROR_SUCCESS ) { DbgPrint( "Winreg.dll: Cannot map predefined handle\n" ); DbgPrint( " Handle: 0x%x Index: %d Error: %d\n", Handle, Index, Error ); } else { ASSERT( IsLocalHandle( PredefinedHandleTable[ Index ].Handle )); } #endif } // // Map the predefined handle to a real handle (may be NULL // if key could not be opened). // ResultHandle = PredefinedHandleTable[ Index ].Handle; ASSERT(*HandleToClose == NULL); } Status = RtlLeaveCriticalSection( &PredefinedHandleTableCriticalSection ); ASSERT( NT_SUCCESS( Status ) ); #if DBG if ( !NT_SUCCESS( Status ) ) { DbgPrint( "WINREG: RtlLeaveCriticalSection() on MapPredefinedHandle() failed. Status = %lx \n", Status ); } #endif return( ResultHandle ); } BOOL CleanupPredefinedHandles( VOID ) /*++ Routine Description: Runs down the list of predefined handles and closes any that have been opened. Arguments: None. Return Value: TRUE - success FALSE - failure --*/ { LONG i; NTSTATUS Status; Status = RtlEnterCriticalSection( &PredefinedHandleTableCriticalSection ); ASSERT( NT_SUCCESS( Status ) ); #if DBG if ( !NT_SUCCESS( Status ) ) { DbgPrint( "WINREG: RtlEnterCriticalSection() on CleanupPredefinedHandles() failed. Status = %lx \n", Status ); } #endif for (i=0;i So that it can retrieve the SACL of a predefined key RegSetKeySecurity -> So that it can save the SACL of a predefined key RegOpenKeyEx -> So that it can determine wheteher or not the caller of RegOpenKeyEx is able to save and restore SACL of predefined keys. Arguments: Handle - Supplies one of the predefined handle of the local machine. AccessMask - Suplies an access mask that contains the special access desired (the ones not included in MAXIMUM_ALLOWED). On NT 1.0, ACCESS_SYSTEM_SECURITY is the only one of such access. pKey - Pointer to the variable that will contain the handle opened with the special access. Return Value: LONG - Returns a DosErrorCode (ERROR_SUCCESS if the operation succeeds). --*/ { LONG Index; LONG Error; ASSERT( pKey ); ASSERT( AccessMask & ACCESS_SYSTEM_SECURITY ); ASSERT( IsPredefinedRegistryHandle( Handle ) ); // // Check if the Handle is a predefined handle. // if( IsPredefinedRegistryHandle( Handle )) { if( ( ( AccessMask & ACCESS_SYSTEM_SECURITY ) == 0 ) || ( pKey == NULL ) ) { return( ERROR_INVALID_PARAMETER ); } // // Convert the handle to an index. // Index = MapPredefinedRegistryHandleToIndex( (ULONG)(ULONG_PTR)Handle ); ASSERT(( 0 <= Index ) && ( Index < MAX_PREDEFINED_HANDLES )); // // If the predefined handle has not already been openend try // and open the key now. // Error = (LONG)( *PredefinedHandleTable[ Index ].OpenFunc )( NULL, AccessMask, pKey ); /* #if DBG if ( Error != ERROR_SUCCESS ) { DbgPrint( "Winreg.dll: Cannot map predefined handle\n" ); DbgPrint( " Handle: 0x%x Index: %d Error: %d\n", Handle, Index, Error ); } else { ASSERT( IsLocalHandle( PredefinedHandleTable[ Index ].Handle )); } #endif */ return Error; } else { return( ERROR_BADKEY ); } } // #endif BOOL InitializePredefinedHandlesTable( ) /*++ Routine Description: Initialize the critical section used by the functions that access PredefinedHandleTable. This critical section is needed to avoid that a thread closes a predefined key, while other threads are accessing the predefined key Arguments: None. Return Value: Returns TRUE if the initialization succeeds. --*/ { NTSTATUS NtStatus; NtStatus = RtlInitializeCriticalSection( &PredefinedHandleTableCriticalSection ); ASSERT( NT_SUCCESS( NtStatus ) ); if ( !NT_SUCCESS( NtStatus ) ) { return FALSE; } return( TRUE ); } BOOL CleanupPredefinedHandlesTable( ) /*++ Routine Description: Delete the critical section used by the functions that access the PredefinedHandleTable. Arguments: None. Return Value: Returns TRUE if the cleanup succeeds. --*/ { NTSTATUS NtStatus; // // Delete the critical section // NtStatus = RtlDeleteCriticalSection( &PredefinedHandleTableCriticalSection ); ASSERT( NT_SUCCESS( NtStatus ) ); if ( !NT_SUCCESS( NtStatus ) ) { return FALSE; } return( TRUE ); } NTSTATUS DisablePredefinedHandleTable( HKEY Handle ) /*++ Routine Description: Disables the predefined handle table for the current user key. Eventually closes the handle in predefined handle, if already open Arguments: Handle - predefined handle for which to disable (now only current user) Return Value: --*/ { NTSTATUS Status; LONG Index; if( Handle != HKEY_CURRENT_USER ) { // // feature enabled only for current user at this time // return STATUS_INVALID_HANDLE; } Status = RtlEnterCriticalSection( &PredefinedHandleTableCriticalSection ); ASSERT( NT_SUCCESS( Status ) ); #if DBG if ( !NT_SUCCESS( Status ) ) { DbgPrint( "WINREG: RtlEnterCriticalSection() on DisablePredefinedHandleTable() failed. Status = %lx \n", Status ); } #endif Index = MapPredefinedRegistryHandleToIndex( (ULONG)(ULONG_PTR)Handle ); ASSERT( Index != -1 ); if(PredefinedHandleTable[ Index ].Disabled == TRUE) { // already called ASSERT( PredefinedHandleTable[ Index ].Handle == NULL ); } else { if( PredefinedHandleTable[ Index ].Handle != NULL ) { // make sure the handle CAN be closed. SetHandleProtection(PredefinedHandleTable[ Index ].Handle,Index,FALSE); #if DBG PredefinedHandleTable[ Index ].Callers = RtlWalkFrameChain(&(PredefinedHandleTable[ Index ].CallerAddress[0]), 10, 0); #endif LocalBaseRegCloseKey( &(PredefinedHandleTable[ Index ].Handle) ); } PredefinedHandleTable[ Index ].Handle = NULL; PredefinedHandleTable[ Index ].Disabled = TRUE; } Status = RtlLeaveCriticalSection( &PredefinedHandleTableCriticalSection ); ASSERT( NT_SUCCESS( Status ) ); #if DBG if ( !NT_SUCCESS( Status ) ) { DbgPrint( "WINREG: RtlLeaveCriticalSection() on ClosePredefinedHandle() failed. Status = %lx \n", Status ); } #endif return Status; }