Windows2000/private/windows/screg/winreg/client/predefh.c
2020-09-30 17:12:32 +02:00

861 lines
20 KiB
C

/*++
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 <rpc.h>
#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
);
error_status_t
LocalOpenPerformanceText(
IN PREGISTRY_SERVER_NAME ServerName,
IN REGSAM samDesired,
OUT PRPC_HKEY phKey
);
error_status_t
LocalOpenPerformanceNlsText(
IN PREGISTRY_SERVER_NAME ServerName,
IN REGSAM samDesired,
OUT PRPC_HKEY phKey
);
// 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.
} PREDEFINED_HANDLE, *PPREDEFINED_HANDLE;
// Initialize predefined handle table.
PREDEFINED_HANDLE PredefinedHandleTable[ ] = {
NULL, LocalOpenClassesRoot, FALSE,
NULL, LocalOpenCurrentUser, FALSE,
NULL, LocalOpenLocalMachine, FALSE,
NULL, LocalOpenUsers, FALSE,
NULL, LocalOpenPerformanceData, FALSE,
NULL, LocalOpenPerformanceText, FALSE,
NULL, LocalOpenPerformanceNlsText, FALSE,
NULL, LocalOpenCurrentConfig, FALSE,
NULL, LocalOpenDynData, FALSE
};
#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 )
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 ) {
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;
leave_crit_sect:
#if DBG
{
NTSTATUS Status =
#endif DBG
RtlLeaveCriticalSection( &PredefinedHandleTableCriticalSection );
ASSERT( NT_SUCCESS( Status ) );
#if DBG
if ( !NT_SUCCESS( Status ) ) {
DbgPrint( "WINREG: RtlLeaveCriticalSection() on RemapPredefinedHandle() failed. Status = %lx \n", Status );
}
}
#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 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<sizeof(PredefinedHandleTable)/sizeof(PREDEFINED_HANDLE);i++) {
// consistency check
if( PredefinedHandleTable[ i ].Disabled == TRUE ) {
// predefined table is disabled for this key
// nobody is allowed to write here
ASSERT( PredefinedHandleTable[ i ].Handle == NULL );
} else if (PredefinedHandleTable[i].Handle != NULL) {
LocalBaseRegCloseKey(&PredefinedHandleTable[i].Handle);
PredefinedHandleTable[i].Handle = NULL;
}
}
Status = RtlLeaveCriticalSection( &PredefinedHandleTableCriticalSection );
ASSERT( NT_SUCCESS( Status ) );
#if DBG
if ( !NT_SUCCESS( Status ) ) {
DbgPrint( "WINREG: RtlLeaveCriticalSection() on CleanupPredefinedHandles() failed. Status = %lx \n", Status );
}
#endif
return(TRUE);
}
LONG
ClosePredefinedHandle(
IN RPC_HKEY Handle
)
/*++
Routine Description:
Zero out the predefined handles entry in the predefined handle table
so that subsequent opens will call the server.
Arguments:
Handle - Supplies a predefined handle.
Return Value:
None.
--*/
{
NTSTATUS Status;
HKEY hKey1;
LONG Error;
LONG Index;
ASSERT( IsPredefinedRegistryHandle( Handle ));
Status = RtlEnterCriticalSection( &PredefinedHandleTableCriticalSection );
ASSERT( NT_SUCCESS( Status ) );
#if DBG
if ( !NT_SUCCESS( Status ) ) {
DbgPrint( "WINREG: RtlEnterCriticalSection() on ClosePredefinedHandle() failed. Status = %lx \n", Status );
}
#endif
Index = MapPredefinedRegistryHandleToIndex( (ULONG)(ULONG_PTR)Handle );
ASSERT( Index != -1 );
hKey1 = PredefinedHandleTable[ Index ].Handle;
if( hKey1 == NULL ) {
// If the handle was already closed, then return ERROR_SUCCESS.
// This is because an application may already have called RegCloseKey
// on a predefined key, and is now callig RegOpenKey on the same
// predefined key. RegOpenKeyEx will try to close the predefined handle
// and open a new one, in order to re-impersonate the client. If we don't
// return ERROR_SUCCESS, then RegOpenKeyEx will not open a new predefined
// handle, and the API will fail.
Error = ERROR_SUCCESS;
} else {
// if there is a handle here, the predefined handle is not disabled.
ASSERT(PredefinedHandleTable[ Index ].Disabled == FALSE);
PredefinedHandleTable[ Index ].Handle = NULL;
}
Status = RtlLeaveCriticalSection( &PredefinedHandleTableCriticalSection );
ASSERT( NT_SUCCESS( Status ) );
#if DBG
if ( !NT_SUCCESS( Status ) ) {
DbgPrint( "WINREG: RtlLeaveCriticalSection() on ClosePredefinedHandle() failed. Status = %lx \n", Status );
}
#endif
if( hKey1 != NULL ) {
// close the key now (after leaving critical region to prevent deadlock
// with dumb heads calling reg apis from DllInit.
Error = ( LONG ) LocalBaseRegCloseKey( &hKey1 );
}
return( Error );
}
LONG
OpenPredefinedKeyForSpecialAccess(
IN RPC_HKEY Handle,
IN REGSAM AccessMask,
OUT PRPC_HKEY pKey
)
/*++
Routine Description:
Attempt to open a predefined key with SYSTEM_SECURITY_ACCESS.
Such an access is not included on MAXIMUM_ALLOWED, and is needed
by RegGetKeySecurity and RegSetKeySecurity, in order to retrieve
and save the SACL of a predefined key.
WHEN USING A HANDLE WITH SPECIAL ACCESS, IT IS IMPORTANT TO FOLLOW
THE RULES BELOW:
- HANDLES OPENED FOR SPECIAL ACCESS ARE NEVER SAVED ON THE
PredefinedHandleTable.
- SUCH HANDLES SHOULD BE USED ONLY INTERNALY TO THE CLIENT
SIDE OF WINREG APIs.
THEY SHOULD NEVER BE RETURNED TO THE OUTSIDE WORLD.
- IT IS THE RESPONSIBILITY OF THE CALLER OF THIS FUNCTION TO CLOSE
THE HANDLES OPENED BY THIS FUNCTION.
RegCloseKey() SHOULD BE USED TO CLOSE SUCH HANDLES.
This function should be called only by the following APIs:
RegGetKeySecurity -> 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 ) {
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;
}