HOWTO: Reference Another User's Hive Through HKEY_CURRENT_USERID: Q199190
|
After loading a user's hive programmatically, a reference to HKEY_CURRENT_USER does not correctly map to the loaded hive.
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
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;
}
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 kbNTOS400 kbRegistry kbSDKPlatform kbSDKWin32
Version : winnt:4.0
Platform : winnt
Issue type : kbhowto
Last Reviewed: May 21, 1999