Microsoft KB Archive/199190

From BetaArchive Wiki

HOWTO: Reference Another User's Hive Through HKEY_CURRENT_USER

Q199190



The information in this article applies to:


  • Microsoft Win32 Application Programming Interface (API), included with:
    • Microsoft Windows NT Server version 4.0
    • Microsoft Windows NT Workstation version 4.0
    • the operating system: Microsoft Windows 2000





SUMMARY

After loading a user's hive programmatically, a reference to HKEY_CURRENT_USER does not correctly map to the loaded hive.

On Windows 2000 use can use the RegOpenCurrentUser API to properly set HKEY_CURRENT_USER instead of the technique mentioned below.

For more information on loading a user's hive, please see the following article in the Microsoft Knowledge Base:


Q168877 HOWTO: Load a User's Hive Programmatically



MORE INFORMATION

The registry key HKEY_CURRENT_USER is a mapping to the HKEY_USERS\<User SID> key. The system will perform the mapping based on the security context of the running thread accessing HKEY_CURRENT_USER, but the mapping is not dynamic. In other words, as a thread changes security contexts through impersonation, the mapping of HKEY_CURRENT_USER to HKEY_USERS\<User SID> does not change automatically.

This non-dynamic nature of HKEY_CURRENT_USER can sometimes cause problems when multiple users are impersonated within the same application. The best solution is to directly access the HKEY_USERS\<User SID> rather than using HKEY_CURRENT_USER. This approach does not depend on the mapping and thus you can be sure of exactly which user's hive you are referencing. This approach is highly recommended for service applications that may impersonate many people.

Application developers frequently don't have full control over all code. In this case, to force HKEY_CURRENT_USER to access the correct user's hive, a running thread can call RegCloseKey(HKEY_CURRENT_USER) to close the current mapping of HKEY_CURRENT_USER before impersonating another user. The very next call to RegOpenKey that references HKEY_CURRENT_USER will re-map to point to the new users hive.

NOTE: Care must be taken to ensure that no other running thread is still accessing the current mapping of HKEY_CURRENT_USER when RegCloseKey() is called, otherwise there is a race condition and the thread accessing HKEY_CURRENT_USER could cause the HKEY_CURRENT_USER -> HKEY_USERS mapping to access the wrong user's hive or even worse, access a closed user's hive.

The sample code below shows how to use RegCloseKey() to correctly map HKEY_CURRENT_USER to the appropriate user's hives.

This sample will print out all values under HKEY_CURRENT_USER\software\Microsoft\WindowsNT\CurrentVersion\PrinterPorts\ to the console window. You will need the supporting code from Q168877 to compile this sample code.

   ///////////// 
   // 
   // EnumPrinterPorts
   // 
   BOOL EnumPrinterPorts (void)
   {
      int i;
      HKEY hKey;
      BOOL bResult = TRUE;
      DWORD lResult;
      lResult = RegOpenKeyEx(HKEY_CURRENT_USER,
         "Software\\Microsoft\\Windows NT\\CurrentVersion\\PrinterPorts\\",
         0,
         KEY_QUERY_VALUE, &hKey);
      if (lResult != ERROR_SUCCESS)
      {
         return FALSE;
      }

      for (i = 0; ; i++)
      {
         TCHAR szValueName[_MAX_PATH + 1];
         DWORD dwValueName = sizeof(szValueName);
         DWORD dwValueType;<BR/>

         lResult = RegEnumValue(hKey,
                                i,
                                szValueName,
                                &dwValueName,
                                NULL,
                                &dwValueType,
                                NULL,
                                NULL);
         if (lResult != ERROR_SUCCESS)
         {
            if (lResult != ERROR_NO_MORE_ITEMS)
            {
               bResult = FALSE;
            }
            break;
         }
         szValueName[dwValueName] = (TCHAR) '\n';
         _tprintf(TEXT("Printer #%d: %s\n"), i + 1, szValueName);
      }

      if (i == 0)
      {
         _tprintf(TEXT("No printer port found.\n"));
      }

      _tprintf(TEXT("\n"));
      RegCloseKey(hKey);
      return bResult;
   }

   int _tmain(int argc, TCHAR *argv[])
   {
      TCHAR  szComputerName[256];
      TCHAR  szProfilePath[256];
      TCHAR  szSid[256];
      DWORD  dwBufferLen;
      DWORD  dwComputerLen;
      HANDLE hToken = NULL;
      int    iSuccess = RTN_ERROR;

      __try
      {
         if (argc != 3)
         {
            _tprintf(TEXT("Usage: %s UserName Password\n"), argv[0]);
            __leave;
         }

         dwComputerLen = sizeof(szComputerName) / sizeof(TCHAR);
         if (!GetComputerName(szComputerName, &dwComputerLen))
         {
            __leave;
         }

         // 
         // Logon user.
         // 
         if (!LogonUser(argv[1],
                        szComputerName,
                        argv[2],
                        LOGON32_LOGON_INTERACTIVE,
                        LOGON32_PROVIDER_DEFAULT,
                        &hToken))
         {
            _tprintf(TEXT("Error [%d]: LogonUser() failed.\n"),
                     GetLastError());
            __leave;
         }

         // 
         // Initialize buffers.
         // 
         ZeroMemory(szSid, sizeof(szSid) / sizeof(TCHAR));
         ZeroMemory(szProfilePath, sizeof(szProfilePath) / sizeof(TCHAR));
         dwBufferLen = sizeof(szSid) / sizeof(TCHAR);

         // 
         // Obtain the sid.
         // 
         if (!ObtainSid(hToken, szSid, &dwBufferLen))
         {
            __leave;
         }

         // 
         // Obtain profile path.
         // 
         if (!ObtainProfilePath(szSid,
                                szProfilePath,
                                sizeof(szProfilePath) / sizeof(TCHAR)))
         {
            __leave;
         }

         // 
         // Enable privilege.
         // 
         if (!Privilege(SE_RESTORE_NAME, TRUE))
         {
            __leave;
         }

         // 
         // Print out printer port registry values.
         // 
         _tprintf(TEXT("Printer port(s) for original logged on user:\n"));

         if (!EnumPrinterPorts())
         {
            __leave;
         }

         // 
         // Close the HKEY_CURRENT_USER mapping.
         // 
         if (RegCloseKey(HKEY_CURRENT_USER) != ERROR_SUCCESS)
         {
            __leave;
         }

         // 
         // At this point, HEKY_CURRENT_KEY is not mapped. The next time
         // HKEY_CURRENT_USER is referenced again, the system will
         // re-establish the mapping based on the security context of
         // the running thread.

         // 
         // Load the hive.
         // 
         if (!Hive(szSid, szProfilePath, TRUE))
         {
            __leave;
         }

         // 
         // Impersonate logged on user.
         // 
         ImpersonateLoggedOnUser(hToken);

         // 
         // When HKEY_CURRENT_USER is referenced again in the following
         // call to EnumPrinterPorts(), the system will remap the key
         // based on the impersonated security contex.
         // 

         // 
         // Print out printer port registry values.
         // 
         _tprintf(TEXT("Printer port(s) for impersonated user:\n"));

         EnumPrinterPorts();

         // 
         // Close the HKEY_CURRENT_USER mapping.
         // 
         if (RegCloseKey(HKEY_CURRENT_USER) != ERROR_SUCCESS)
         {
            __leave;
         }

         // 
         // Revert to self.
         // 
         RevertToSelf();

         // 
         // Unload the hive.
         // 
         if (!Hive(szSid, szProfilePath, FALSE))
         {
            __leave;
         }

         // 
         // Disable the privilege.
         // 
         if (!Privilege(SE_RESTORE_NAME, FALSE))
         {
            __leave;
         }

         iSuccess = RTN_OK;
      }

      __finally
      {
         if (hToken != NULL)
         {
            CloseHandle(hToken);
         }
      }

      return iSuccess;
   } 



REFERENCES

For more information on loading a user's hive, please see the following article in the Microsoft Knowledge Base:


Q168877 HOWTO: Load a User's Hive Programmatically

Additional query words: kbDSupport

Keywords : kbKernBase kbOSWinNT400 kbOSWin2000 kbRegistry kbSDKPlatform kbSDKWin32 kbSecurity kbFAQ kbDSupport kbGrpDSKernBase
Issue type : kbhowto
Technology : kbAudDeveloper kbWin32sSearch kbWin32API


Last Reviewed: October 22, 2000
© 2001 Microsoft Corporation. All rights reserved. Terms of Use.