text: /*++
  
  
  Copyright (c) 1991 Microsoft Corporation
  
  Module Name:
  
      Predefh.c
  
  Abstract:
  
      This module contains routines for opening the Win32 Registry API's
      predefined handles.
  
      A predefined handle is used as a root to an absolute or relative
      sub-tree in the real Nt Registry. An absolute predefined handle maps
      to a specific key within the Registry. A relative predefined handle
      maps to a key relative to some additional information such as the
      current user.
  
      Predefined handles are strictly part of the Win32 Registry API. The
      Nt Registry API knows nothing about them.
  
      A predefined handle can be used anywhere that a non-predefined handle
      (i.e. one returned from RegCreateKey(), RegOpenKey() or
      RegConnectRegistry()) can be used.
  
  Author:
  
      David J. Gilman (davegi) 15-Nov-1991
  
  --*/
  
  #include <rpc.h>
  #include "regrpc.h"
  #include "localreg.h"
  #include "regclass.h"
  #include "ntconreg.h"
  #include "regsec.h"
  #ifdef LOCAL
  #include "tsappcmp.h"
  
  #if defined(LEAK_TRACK) 
  #include "regleak.h"
  #endif // LEAK_TRACK
  #endif
  
  //
  // Determine the length of a Unicode string w/o the trailing NULL.
  //
  
  #define LENGTH( str )   ( sizeof( str ) - sizeof( UNICODE_NULL ))
  
  //
  // Nt Registry name space.
  //
  
  #define MACHINE         L"\\REGISTRY\\MACHINE"
  
  #define USER            L"\\REGISTRY\\USER"
  
  #define CLASSES         L"\\REGISTRY\\MACHINE\\SOFTWARE\\CLASSES"
  
  #define CURRENTCONFIG   L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\HARDWARE PROFILES\\CURRENT"
  
  extern const
  UNICODE_STRING          MachineStringKey = {
                              LENGTH( MACHINE ),
                              LENGTH( MACHINE ),
                              MACHINE
                              };
  
  extern const
  UNICODE_STRING          UserStringKey = {
                              LENGTH( USER ),
                              LENGTH( USER ),
                              USER
                          };
  
  extern const
  UNICODE_STRING          ClassesStringKey = {
                              LENGTH( CLASSES ),
                              LENGTH( CLASSES ),
                              CLASSES
                          };
  
  extern const
  UNICODE_STRING          CurrentConfigStringKey = {
                              LENGTH( CURRENTCONFIG ),
                              LENGTH( CURRENTCONFIG ),
                              CURRENTCONFIG
                          };
  
  
  HANDLE	RestrictedMachineHandle = NULL;
  
  NTSTATUS
  InitSecurityAcls(PSECURITY_DESCRIPTOR *SecurityDescriptor)
  /*++
  
  Routine Description:
  
      Gives GENERIC_ALL to admins and denies WRITE_OWNER | WRITE_DAC  from everyone
  
  Arguments:
  
  
  Return Value:
  
  
  --*/
  {
      SID_IDENTIFIER_AUTHORITY    NtAuthority = SECURITY_NT_AUTHORITY;
      SID_IDENTIFIER_AUTHORITY    WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
      PSID                        BuiltInAdministrators = NULL;
      PSID                        Everyone = NULL;
      NTSTATUS                    Status;
      ULONG                       AclSize;
      ACL                         *Acl;
  
      *SecurityDescriptor = NULL;
  
      Status = RtlAllocateAndInitializeSid(
                &WorldAuthority,
                1,
                SECURITY_WORLD_RID,
                0,0,0,0,0,0,0,
                &Everyone );
      if( !NT_SUCCESS(Status) ) {
          goto Exit;
      }
  
      Status = RtlAllocateAndInitializeSid(
                &NtAuthority,
                2,
                SECURITY_BUILTIN_DOMAIN_RID,
                DOMAIN_ALIAS_RID_ADMINS,
                0,0,0,0,0,0,
                &BuiltInAdministrators );
  
      if( !NT_SUCCESS(Status) ) {
          goto Exit;
      }
  
  
  
      AclSize = sizeof (ACL) +
          (2 * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (ULONG))) +
          GetLengthSid(BuiltInAdministrators) +
          GetLengthSid(Everyone);
  
      *SecurityDescriptor = (PSECURITY_DESCRIPTOR)RtlAllocateHeap( RtlProcessHeap(), 0, SECURITY_DESCRIPTOR_MIN_LENGTH + AclSize);
      if (!*SecurityDescriptor) {
          Status = STATUS_INSUFFICIENT_RESOURCES;
          goto Exit;
      } 
  
      Acl = (ACL *)((BYTE *)(*SecurityDescriptor) + SECURITY_DESCRIPTOR_MIN_LENGTH);
  
      Status = RtlCreateAcl(  Acl,
                              AclSize,
                              ACL_REVISION);
      if( !NT_SUCCESS(Status) ) {
          goto Exit;
      }
  
      Status = RtlAddAccessAllowedAce(Acl,
                                      ACL_REVISION,
                                      (KEY_ALL_ACCESS & ~(WRITE_OWNER | WRITE_DAC)),
                                      Everyone);
      if( !NT_SUCCESS(Status) ) {
          goto Exit;
      }
  
      Status = RtlAddAccessAllowedAce(Acl,
                                      ACL_REVISION,
                                      GENERIC_ALL,
                                      BuiltInAdministrators);
      if( !NT_SUCCESS(Status) ) {
          goto Exit;
      }
  
      Status = RtlCreateSecurityDescriptor(
                  *SecurityDescriptor,
                  SECURITY_DESCRIPTOR_REVISION
                  );
      if( !NT_SUCCESS(Status) ) {
          goto Exit;
      }
  
      Status = RtlSetDaclSecurityDescriptor(  *SecurityDescriptor,
                                              TRUE,
                                              Acl,
                                              FALSE);
  
  Exit:
      if( Everyone ) {
          RtlFreeSid( Everyone );
      }
      
      if( BuiltInAdministrators ) {
          RtlFreeSid( BuiltInAdministrators );
      }
  
      return Status;
  }
  
  
  error_status_t
  OpenClassesRoot(
      IN PREGISTRY_SERVER_NAME ServerName,
      IN REGSAM samDesired,
      OUT PRPC_HKEY phKey
      )
  
  /*++
  
  Routine Description:
  
      Attempts to open the the HKEY_CLASSES_ROOT predefined handle.
  
  Arguments:
  
      ServerName - Not used.
      samDesired - This access mask describes the desired security access
                   for the key.
      phKey - Returns a handle to the key \REGISTRY\MACHINE\SOFTWARE\CLASSES.
  
  Return Value:
  
      Returns ERROR_SUCCESS (0) for success; error-code for failure.
  
  --*/
  
  {
      PSECURITY_DESCRIPTOR     SecurityDescriptor = NULL;
      OBJECT_ATTRIBUTES       Obja;
      NTSTATUS                Status;
      UNICODE_STRING          UsersHive;
      UNICODE_STRING          UsersMergedHive;
  
      UNREFERENCED_PARAMETER( ServerName );
  
      //
      // Impersonate the client.
      //
  
      RPC_IMPERSONATE_CLIENT( NULL );
  
  #ifdef LOCAL
      //
      // Multiuser CLASSES key so each user has their own key.  If opening
      // CLASSES in execute mode - open it under HKEY_CURRENT_USER else
      // just let it fall thru here and open the global one.
      //
      if (gpfnTermsrvOpenUserClasses) {
          Status = gpfnTermsrvOpenUserClasses(samDesired,phKey);
      } else {
          *phKey = NULL;
      }
      if (!(*phKey)) {
  #endif // LOCAL
  
  
      //
      // Initialize the SECURITY_DESCRIPTOR.
      //
      Status = InitSecurityAcls(&SecurityDescriptor);
  
      if( ! NT_SUCCESS( Status )) {
          goto error_exit;
      }
  
  
  #ifdef LOCAL
  
      if (gbCombinedClasses) {
          // first try for a per-user HKCR
          Status = OpenCombinedClassesRoot( samDesired, phKey );
  
          if ( NT_SUCCESS( Status ) ) {
              goto error_exit;
          }
      }
  #endif
  
      //
      // Initialize the OBJECT_ATTRIBUTES structure so that it creates
      // (opens) the key "\REGISTRY\MACHINE\SOFTWARE\CLASSES" with a Security
      // Descriptor that allows everyone complete access.
      //
  
      InitializeObjectAttributes(
          &Obja,
          (PUNICODE_STRING)&ClassesStringKey,
          OBJ_CASE_INSENSITIVE,
          NULL,
          SecurityDescriptor
          );
  
      Status = NtCreateKey(
                  phKey,
                  samDesired, // MAXIMUM_ALLOWED,
                  &Obja,
                  0,
                  NULL,
                  REG_OPTION_NON_VOLATILE,
                  NULL
                  );
  #ifdef LOCAL
      }
  #endif // LOCAL
  
  #if DBG
          if( ! NT_SUCCESS( Status )) {
              DbgPrint(
                  "Winreg Server: "
                  "Creating HKEY_CLASSES_ROOT failed, status = 0x%x\n",
                  Status
                  );
          }
  #endif
  
  error_exit:
      
      if( SecurityDescriptor != NULL ) {
  	RtlFreeHeap( RtlProcessHeap(), 0, SecurityDescriptor );
      }
      RPC_REVERT_TO_SELF();
      return (error_status_t)RtlNtStatusToDosError( Status );
  }
  
  error_status_t
  OpenCurrentUser(
      IN PREGISTRY_SERVER_NAME ServerName,
      IN REGSAM samDesired,
      OUT PRPC_HKEY phKey
      )
  
  /*++
  
  Routine Description:
  
      Attempts to open the the HKEY_CURRENT_USER predefined handle.
  
  Arguments:
  
      ServerName - Not used.
      samDesired - This access mask describes the desired security access
                   for the key.
      phKey - Returns a handle to the key \REGISTRY\USER\*.
  
  Return Value:
  
      Returns ERROR_SUCCESS (0) for success; error-code for failure.
  
  --*/
  
  {
      NTSTATUS            Status;
  
      UNREFERENCED_PARAMETER( ServerName );
  
      //
      // Impersonate the client.
      //
  
      RPC_IMPERSONATE_CLIENT( NULL );
  
      //
      // Open the registry key.
      //
  
      Status = RtlOpenCurrentUser( samDesired, /* MAXIMUM_ALLOWED, */ phKey );
  
      RPC_REVERT_TO_SELF();
      //
      // Map the returned status
      //
  
      return (error_status_t)RtlNtStatusToDosError( Status );
  }
  
  error_status_t
  OpenLocalMachine(
      IN PREGISTRY_SERVER_NAME ServerName,
      IN REGSAM samDesired,
      OUT PRPC_HKEY phKey
      )
  
  /*++
  
  Routine Description:
  
      Attempt to open the the HKEY_LOCAL_MACHINE predefined handle.
  
  Arguments:
  
      ServerName - Not used.
      samDesired - This access mask describes the desired security access
                   for the key.
      phKey - Returns a handle to the key \REGISTRY\MACHINE.
  
  Return Value:
  
      Returns ERROR_SUCCESS (0) for success; error-code for failure.
  
  --*/
  
  {
      OBJECT_ATTRIBUTES   Obja;
      NTSTATUS            Status;
  
      UNREFERENCED_PARAMETER( ServerName );
  
      //
      // Impersonate the client.
      //
  
      RPC_IMPERSONATE_CLIENT( NULL );
  
      InitializeObjectAttributes(
          &Obja,
          (PUNICODE_STRING)&MachineStringKey,
          OBJ_CASE_INSENSITIVE,
          NULL,
          NULL
          );
  
      Status = NtOpenKey(
                  phKey,
                  samDesired, // MAXIMUM_ALLOWED,
                  &Obja
                  );
  #if DBG
          if( ! NT_SUCCESS( Status )) {
              DbgPrint(
                  "Winreg Server: "
                  "Opening HKEY_LOCAL_MACHINE failed, status = 0x%x\n",
                  Status
                  );
          }
  #endif
  
      if ( NT_SUCCESS( Status ) )
      {
          if (! REGSEC_CHECK_REMOTE( phKey ) )
          {
              ASSERT( RestrictedMachineHandle != NULL );
              NtClose(*phKey);
              *phKey = REGSEC_FLAG_HANDLE( RestrictedMachineHandle, CHECK_MACHINE_PATHS );
          }
      }
  
      RPC_REVERT_TO_SELF();
  
      return (error_status_t)RtlNtStatusToDosError( Status );
  }
  
  error_status_t
  OpenUsers(
      IN PREGISTRY_SERVER_NAME ServerName,
      IN REGSAM samDesired,
      OUT PRPC_HKEY phKey
      )
  
  /*++
  
  Routine Description:
  
      Attempts to open the the HKEY_USERS predefined handle.
  
  Arguments:
  
      ServerName - Not used.
      samDesired - This access mask describes the desired security access
                   for the key.
      phKey - Returns a handle to the key \REGISTRY\USER.
  
  Return Value:
  
      Returns ERROR_SUCCESS (0) for success; error-code for failure.
  
  --*/
  
  {
      OBJECT_ATTRIBUTES   Obja;
      NTSTATUS            Status;
  
      UNREFERENCED_PARAMETER( ServerName );
  
      //
      // Impersonate the client.
      //
  
      RPC_IMPERSONATE_CLIENT( NULL );
  
      InitializeObjectAttributes(
          &Obja,
          (PUNICODE_STRING)&UserStringKey,
          OBJ_CASE_INSENSITIVE,
          NULL,
          NULL
          );
  
      Status = NtOpenKey(
                  phKey,
                  samDesired, // MAXIMUM_ALLOWED,
                  &Obja
                  );
  #if DBG
          if( ! NT_SUCCESS( Status )) {
              DbgPrint(
                  "Winreg Server: "
                  "Opening HKEY_USERS failed, status = 0x%x\n",
                  Status
                  );
          }
  #endif
  
  /*
      if ( NT_SUCCESS( Status ) )
      {
          if (! REGSEC_CHECK_REMOTE( phKey ) )
          {
              *phKey = REGSEC_FLAG_HANDLE( *phKey, CHECK_USER_PATHS );
          }
      }
  */
      RPC_REVERT_TO_SELF();
  
      return (error_status_t)RtlNtStatusToDosError( Status );
  }
  
  error_status_t
  OpenCurrentConfig(
      IN PREGISTRY_SERVER_NAME ServerName,
      IN REGSAM samDesired,
      OUT PRPC_HKEY phKey
      )
  
  /*++
  
  Routine Description:
  
      Attempts to open the the HKEY_CURRENT_CONFIG predefined handle.
  
  Arguments:
  
      ServerName - Not used.
      samDesired - This access mask describes the desired security access
                   for the key.
      phKey - Returns a handle to the key \REGISTRY\MACHINE\SYSTEM\CURRENTCONTROLSET\HARDWARE PROFILES\CURRENT
  
  Return Value:
  
      Returns ERROR_SUCCESS (0) for success; error-code for failure.
  
  --*/
  
  {
      OBJECT_ATTRIBUTES   Obja;
      NTSTATUS            Status;
  
      UNREFERENCED_PARAMETER( ServerName );
  
      //
      // Impersonate the client.
      //
  
      RPC_IMPERSONATE_CLIENT( NULL );
  
      InitializeObjectAttributes(
          &Obja,
          (PUNICODE_STRING)&CurrentConfigStringKey,
          OBJ_CASE_INSENSITIVE,
          NULL,
          NULL
          );
  
      Status = NtOpenKey(
                  phKey,
                  samDesired, // MAXIMUM_ALLOWED,
                  &Obja
                  );
  #if DBG
          if( ! NT_SUCCESS( Status )) {
              DbgPrint(
                  "Winreg Server: "
                  "Opening HKEY_CURRENT_CONFIG failed, status = 0x%x\n",
                  Status
                  );
          }
  #endif
      RPC_REVERT_TO_SELF();
  
      return (error_status_t)RtlNtStatusToDosError( Status );
  }
  error_status_t
  OpenPerformanceData(
      IN PREGISTRY_SERVER_NAME ServerName,
      IN REGSAM samDesired,
      OUT PRPC_HKEY phKey
      )
  
  /*++
  
  Routine Description:
  
      Attempts to open the the HKEY_PERFORMANCE_DATA predefined handle.
  
  Arguments:
  
      ServerName - Not used.
      samDesired - Not used.
      phKey - Returns a the predefined handle HKEY_PERFORMANCE_DATA.
  
  Return Value:
  
      Returns ERROR_SUCCESS (0) for success;
      or a DOS (not NT) error-code for failure.
  
  --*/
  
  {
      NTSTATUS status;
  
      if ( 0 ) {
          DBG_UNREFERENCED_PARAMETER(ServerName);
          DBG_UNREFERENCED_PARAMETER(samDesired);
      }
  
      //
      // Impersonate the client.
      //
  
      RPC_IMPERSONATE_CLIENT( NULL );
  
      if ( ! REGSEC_CHECK_PERF( phKey ) )
      {
          RPC_REVERT_TO_SELF();
          return( ERROR_ACCESS_DENIED );
      }
  
      // Move lodctr/unlodctr access check to part of PerfOpenKey()
  
      status  = PerfOpenKey( HKEY_PERFORMANCE_DATA );
  
      RPC_REVERT_TO_SELF();
  
      *phKey = (RPC_HKEY) HKEY_PERFORMANCE_DATA;
      return ERROR_SUCCESS;
  
  }
  
  error_status_t
  OpenPerformanceText(
      IN PREGISTRY_SERVER_NAME ServerName,
      IN REGSAM samDesired,
      OUT PRPC_HKEY phKey
      )
  
  /*++
  
  Routine Description:
  
      Attempts to open the the HKEY_PERFORMANCE_TEXT predefined handle.
  
  Arguments:
  
      ServerName - Not used.
      samDesired - Not used.
      phKey - Returns the predefined handle HKEY_PERFORMANCE_TEXT.
  
  Return Value:
  
      Returns ERROR_SUCCESS (0) for success;
      or a DOS (not NT) error-code for failure.
  
  --*/
  
  {
      error_status_t Status = ERROR_SUCCESS;
  
  // No need to call OpenPerformanceData for getting text (HWC 4/1994)
  //    Status = OpenPerformanceData(ServerName, samDesired, phKey);
  //    if (Status==ERROR_SUCCESS) {
          *phKey = HKEY_PERFORMANCE_TEXT;
  //    }
      return(Status);
  }
  
  error_status_t
  OpenPerformanceNlsText(
      IN PREGISTRY_SERVER_NAME ServerName,
      IN REGSAM samDesired,
      OUT PRPC_HKEY phKey
      )
  
  /*++
  
  Routine Description:
  
      Attempts to open the the HKEY_PERFORMANCE_TEXT predefined handle.
  
  Arguments:
  
      ServerName - Not used.
      samDesired - Not used.
      phKey - Returns the predefined handle HKEY_PERFORMANCE_NLSTEXT.
  
  Return Value:
  
      Returns ERROR_SUCCESS (0) for success;
      or a DOS (not NT) error-code for failure.
  
  --*/
  
  {
      error_status_t Status = ERROR_SUCCESS;
  
  // No need to call OpenPerformanceData for getting text (HWC 4/1994)
  //    Status = OpenPerformanceData(ServerName, samDesired, phKey);
  //    if (Status==ERROR_SUCCESS) {
          *phKey = HKEY_PERFORMANCE_NLSTEXT;
  //    }
      return(Status);
  }
  
  
  error_status_t
  OpenDynData(
      IN PREGISTRY_SERVER_NAME ServerName,
      IN REGSAM samDesired,
      OUT PRPC_HKEY phKey
      )
  /*++
  
  Routine Description:
  
      Attempts to open the the HKEY_DYN_DATA predefined handle.
  
      There is currently no HKEY_DYN_DATA on NT, thus this
      function always returns ERROR_CALL_NOT_IMPLEMENTED.
  
  Arguments:
  
      ServerName - Not used.
      samDesired - This access mask describes the desired security access
                   for the key.
      phKey - Returns a handle to the key HKEY_DYN_DATA
  
  Return Value:
  
      Returns ERROR_SUCCESS (0) for success; error-code for failure.
  
  --*/
  
  {
      return((error_status_t)ERROR_CALL_NOT_IMPLEMENTED);
  }
  
  //
  // Default ("fake") handle returned to all check_machine_paths-like connections
  //
  NTSTATUS
  OpenMachineKey(PHANDLE phKey)
  {
      OBJECT_ATTRIBUTES   Obja;
      NTSTATUS            Status;
  
      InitializeObjectAttributes(
                              &Obja,
                              (PUNICODE_STRING)&MachineStringKey,
                              OBJ_CASE_INSENSITIVE,
                              NULL,
                              NULL
                              );
  
      Status = NtOpenKey(
                          phKey,
                          MAXIMUM_ALLOWED,
                          &Obja
                          );
  
      return Status;
  }
  
  NTSTATUS
  InitRestrictedMachineHandle()
  {
      return OpenMachineKey(&RestrictedMachineHandle);
  }
  
  VOID
  CleanupRestrictedMachineHandle()
  {
      if( RestrictedMachineHandle != NULL ) {
          NtClose(RestrictedMachineHandle);
          RestrictedMachineHandle = NULL;
      }
  }
  