windows-nt/Source/XPSP1/NT/ds/security/gina/userenv/profile/hive.cpp
2020-09-26 16:20:57 +08:00

1008 lines
33 KiB
C++

//*************************************************************
//
// HKCR management routines
//
// hkcr.c
//
// Microsoft Confidential
// Copyright (c) Microsoft Corporation 1997
// All rights reserved
//
//*************************************************************
/*++
Abstract:
This module contains the code executed at logon for
creating a user classes hive and mapping it into the standard
user hive. The user classes hive and its machine classes
counterpart make up the registry subtree known as
HKEY_CLASSES_ROOT.
Author:
Zeke Odins-Lucas (zekel) 18-Oct-2000
changed from hkcr.c to hive.cpp
Adam P. Edwards (adamed) 10-Oct-1997
Gregory Jensenworth (gregjen) 1-Jul-1997
Key Functions:
LoadUserHives
UnloadUserHives
Notes:
Starting with NT5, the HKEY_CLASSES_ROOT key is per-user
instead of per-machine -- previously, HKCR was an alias for
HKLM\Software\Classes.
The per-user HKCR combines machine classes stored it the
traditional HKLM\Software\Classes location with classes
stored in HKCU\Software\Classes.
Certain keys, such as CLSID, will have subkeys that come
from both the machine and user locations. When there is a conflict
in key names, the user oriented key overrides the other one --
only the user key is seen in that case.
Originally, the code in this module was responsible for
creating this combined view. That responsibility has moved
to the win32 registry api's, so the main responsibility of
this module is the mapping of the user-specific classes into
the registry.
It should be noted that HKCU\Software\Classes is not the true
location of the user-only class data. If it were, all the class
data would be in ntuser.dat, which roams with the user. Since
class data can get very large, installation of a few apps
would cause HKCU (ntuser.dat) to grow from a few hundred thousand K
to several megabytes. Since user-only class data comes from
the directory, it does not need to roam and therefore it was
separated from HKCU (ntuser.dat) and stored in another hive
mounted under HKEY_USERS.
It is still desirable to allow access to this hive through
HKCU\Software\Classes, so we use some trickery (symlinks) to
make it seem as if the user class data exists there.
--*/
#include "uenv.h"
#include <malloc.h>
#define USER_CLASSES_HIVE_NAME TEXT("\\UsrClass.dat")
#define CLASSES_SUBTREE TEXT("Software\\Classes\\")
#define TEMPHIVE_FILENAME TEXT("TempClassesHive.dat")
#define CLASSES_CLSID_SUBTREE TEXT("Software\\Classes\\Clsid\\")
#define EXPLORER_CLASSES_SUBTREE TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Clsid\\")
#define LENGTH(x) (sizeof(x) - sizeof(WCHAR))
#define INIT_SPECIALKEY(x) x
typedef WCHAR* SpecialKey;
SpecialKey SpecialSubtrees[]= {
INIT_SPECIALKEY(L"*"),
INIT_SPECIALKEY(L"*\\shellex"),
INIT_SPECIALKEY(L"*\\shellex\\ContextMenuHandlers"),
INIT_SPECIALKEY(L"*\\shellex\\PropertyShellHandlers"),
INIT_SPECIALKEY(L"AppID"),
INIT_SPECIALKEY(L"ClsID"),
INIT_SPECIALKEY(L"Component Categories"),
INIT_SPECIALKEY(L"Drive"),
INIT_SPECIALKEY(L"Drive\\shellex"),
INIT_SPECIALKEY(L"Drive\\shellex\\ContextMenuHandlers"),
INIT_SPECIALKEY(L"Drive\\shellex\\PropertyShellHandlers"),
INIT_SPECIALKEY(L"FileType"),
INIT_SPECIALKEY(L"Folder"),
INIT_SPECIALKEY(L"Folder\\shellex"),
INIT_SPECIALKEY(L"Folder\\shellex\\ColumnHandler"),
INIT_SPECIALKEY(L"Folder\\shellex\\ContextMenuHandlers"),
INIT_SPECIALKEY(L"Folder\\shellex\\ExtShellFolderViews"),
INIT_SPECIALKEY(L"Folder\\shellex\\PropertySheetHandlers"),
INIT_SPECIALKEY(L"Installer\\Components"),
INIT_SPECIALKEY(L"Installer\\Features"),
INIT_SPECIALKEY(L"Installer\\Products"),
INIT_SPECIALKEY(L"Interface"),
INIT_SPECIALKEY(L"Mime"),
INIT_SPECIALKEY(L"Mime\\Database"),
INIT_SPECIALKEY(L"Mime\\Database\\Charset"),
INIT_SPECIALKEY(L"Mime\\Database\\Codepage"),
INIT_SPECIALKEY(L"Mime\\Database\\Content Type"),
INIT_SPECIALKEY(L"Typelib")
};
#define NUM_SPECIAL_SUBTREES (sizeof(SpecialSubtrees)/sizeof(*SpecialSubtrees))
//*************************************************************
//
// MoveUserClassesBeforeMerge
//
// Purpose: move HKCU\Software\Classes before
// MapUserClassesIntoUserHive() deletes it.
//
// Parameters: lpProfile - Profile information
// lpcszLocalHiveDir - Temp Hive location
//
// Return: ERROR_SUCCESS if successful,
// other error if not
//
// Comments:
//
// History: Date Author Comment
// 5/6/99 vtan Created
//
//*************************************************************
LONG MoveUserClassesBeforeMerge(
LPPROFILE lpProfile,
LPCTSTR lpcszLocalHiveDir)
{
LONG res;
HKEY hKeySource;
// Open HKCU\Software\Classes and see if there is a subkey.
// No subkeys would indicate that the move has already been
// done or there is no data to move.
res = RegOpenKeyEx(lpProfile->hKeyCurrentUser, CLASSES_CLSID_SUBTREE, 0, KEY_ALL_ACCESS, &hKeySource);
if (ERROR_SUCCESS == res)
{
DWORD dwSubKeyCount;
if ((ERROR_SUCCESS == RegQueryInfoKey(hKeySource, NULL, NULL, NULL, &dwSubKeyCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) &&
(dwSubKeyCount > 0))
{
LPTSTR pszLocalTempHive;
// Allocate enough space for the local hive directory and the temp hive filename.
pszLocalTempHive = (LPTSTR) alloca((lstrlen(lpcszLocalHiveDir) * sizeof(TCHAR)) +
sizeof(TEMPHIVE_FILENAME) +
(sizeof('\0') * sizeof(TCHAR)));
// Get a path to a file to save HKCU\Software\Classes into.
if (pszLocalTempHive != NULL)
{
HANDLE hToken = NULL;
BOOL bAdjustPriv = FALSE;
lstrcpy(pszLocalTempHive, lpcszLocalHiveDir);
lstrcat(pszLocalTempHive, TEMPHIVE_FILENAME);
// RegSaveKey() fails if the file exists so delete it first.
DeleteFile(pszLocalTempHive);
//
// Check to see if we are impersonating.
//
if(!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &hToken) || hToken == NULL) {
bAdjustPriv = TRUE;
}
else {
CloseHandle(hToken);
}
if(!bAdjustPriv) {
DWORD dwDisposition;
HKEY hKeyTarget;
BOOL fSavedHive;
// Save HKCU\Software\Classes into the temp hive
// and restore the state of SE_BACKUP_NAME privilege
res = RegSaveKey(hKeySource, pszLocalTempHive, NULL);
if (ERROR_SUCCESS == res)
{
res = RegCreateKeyEx(lpProfile->hKeyCurrentUser, EXPLORER_CLASSES_SUBTREE, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyTarget, &dwDisposition);
if (ERROR_SUCCESS == res)
{
// Restore temp hive to a new location at
// HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer
// This performs the upgrade from NT4 to NT5.
res = RegRestoreKey(hKeyTarget, pszLocalTempHive, 0);
if (ERROR_SUCCESS != res)
{
DebugMsg((DM_WARNING, TEXT("RegRestoreKey failed with error %d"), res));
}
RegCloseKey(hKeyTarget);
}
else
{
DebugMsg((DM_WARNING, TEXT("RegCreateKeyEx failed to create key %s with error %d"), EXPLORER_CLASSES_SUBTREE, res));
}
}
else
{
DebugMsg((DM_WARNING, TEXT("RegSaveKey failed with error %d"), res));
}
}
else {
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
DWORD dwReturnTokenPrivilegesSize;
TOKEN_PRIVILEGES oldTokenPrivileges, newTokenPrivileges;
// Enable SE_BACKUP_NAME privilege
if (LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &newTokenPrivileges.Privileges[0].Luid))
{
newTokenPrivileges.PrivilegeCount = 1;
newTokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (AdjustTokenPrivileges(hToken, FALSE, &newTokenPrivileges, sizeof(newTokenPrivileges), &oldTokenPrivileges, &dwReturnTokenPrivilegesSize))
{
BOOL fSavedHive;
// Save HKCU\Software\Classes into the temp hive
// and restore the state of SE_BACKUP_NAME privilege
res = RegSaveKey(hKeySource, pszLocalTempHive, NULL);
if (!AdjustTokenPrivileges(hToken, FALSE, &oldTokenPrivileges, 0, NULL, NULL))
{
DebugMsg((DM_WARNING, TEXT("AdjustTokenPrivileges failed to restore old privileges with error %d"), GetLastError()));
}
if (ERROR_SUCCESS == res)
{
// Enable SE_RESTORE_NAME privilege.
if (LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &newTokenPrivileges.Privileges[0].Luid))
{
newTokenPrivileges.PrivilegeCount = 1;
newTokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (AdjustTokenPrivileges(hToken, FALSE, &newTokenPrivileges, sizeof(newTokenPrivileges), &oldTokenPrivileges, &dwReturnTokenPrivilegesSize))
{
DWORD dwDisposition;
HKEY hKeyTarget;
res = RegCreateKeyEx(lpProfile->hKeyCurrentUser, EXPLORER_CLASSES_SUBTREE, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyTarget, &dwDisposition);
if (ERROR_SUCCESS == res)
{
// Restore temp hive to a new location at
// HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer
// This performs the upgrade from NT4 to NT5.
res = RegRestoreKey(hKeyTarget, pszLocalTempHive, 0);
if (ERROR_SUCCESS != res)
{
DebugMsg((DM_WARNING, TEXT("RegRestoreKey failed with error %d"), res));
}
RegCloseKey(hKeyTarget);
}
else
{
DebugMsg((DM_WARNING, TEXT("RegCreateKeyEx failed to create key %s with error %d"), EXPLORER_CLASSES_SUBTREE, res));
}
if (!AdjustTokenPrivileges(hToken, FALSE, &oldTokenPrivileges, 0, NULL, NULL))
{
DebugMsg((DM_WARNING, TEXT("AdjustTokenPrivileges failed to restore old privileges with error %d"), GetLastError()));
}
}
else
{
res = GetLastError();
DebugMsg((DM_WARNING, TEXT("AdjustTokenPrivileges failed with error %d"), res));
}
}
else
{
res = GetLastError();
DebugMsg((DM_WARNING, TEXT("LookupPrivilegeValue failed with error %d"), res));
}
}
else
{
DebugMsg((DM_WARNING, TEXT("RegSaveKey failed with error %d"), res));
}
}
else
{
res = GetLastError();
DebugMsg((DM_WARNING, TEXT("AdjustTokenPrivileges failed with error %d"), res));
}
}
else
{
res = GetLastError();
DebugMsg((DM_WARNING, TEXT("LookupPrivilegeValue failed with error %d"), res));
}
CloseHandle(hToken);
}
else
{
res = GetLastError();
DebugMsg((DM_WARNING, TEXT("OpenProcessToken failed to get token with error %d"), res));
}
} // if(!bAdjustPriv) else
// Delete local temporary hive file.
DeleteFile(pszLocalTempHive);
}
else
{
DebugMsg((DM_WARNING, TEXT("alloca failed to allocate temp hive path buffer")));
}
}
RegCloseKey(hKeySource);
}
else if (ERROR_FILE_NOT_FOUND == res)
{
res = ERROR_SUCCESS;
}
return res;
}
//*************************************************************
//
// CreateRegLink()
//
// Purpose: Create a link from the hkDest + SubKeyName
// pointing to lpSourceRootName
//
// if the key (link) already exists, do nothing
//
// Parameters: hkDest - root of destination
// SubKeyName - subkey of destination
// lpSourceName - target of link
//
// Return: ERROR_SUCCESS if successful
// other NTSTATUS if an error occurs
//
//*************************************************************/
LONG CreateRegLink(HKEY hkDest,
LPCTSTR SubKeyName,
LPCTSTR lpSourceName)
{
NTSTATUS Status;
UNICODE_STRING LinkTarget;
UNICODE_STRING SubKey;
OBJECT_ATTRIBUTES Attributes;
HANDLE hkInternal;
UNICODE_STRING SymbolicLinkValueName;
//
// Initialize special key value used to make symbolic links
//
RtlInitUnicodeString(&SymbolicLinkValueName, L"SymbolicLinkValue");
//
// Initialize unicode string for our in params
//
RtlInitUnicodeString(&LinkTarget, lpSourceName);
RtlInitUnicodeString(&SubKey, SubKeyName);
//
// See if this link already exists -- this is necessary because
// NtCreateKey fails with STATUS_OBJECT_NAME_COLLISION if a link
// already exists and will not return a handle to the existing
// link.
//
InitializeObjectAttributes(&Attributes,
&SubKey,
OBJ_CASE_INSENSITIVE | OBJ_OPENLINK,
hkDest,
NULL);
//
// If this call succeeds, we get a handle to the existing link
//
Status = NtOpenKey( &hkInternal,
MAXIMUM_ALLOWED,
&Attributes );
if (STATUS_OBJECT_NAME_NOT_FOUND == Status) {
//
// There is no existing link, so use NtCreateKey to make a new one
//
Status = NtCreateKey( &hkInternal,
KEY_CREATE_LINK | KEY_SET_VALUE,
&Attributes,
0,
NULL,
REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
NULL);
}
//
// Whether the link existed already or not, we should still set
// the value which determines the link target
//
if (NT_SUCCESS(Status)) {
Status = NtSetValueKey( hkInternal,
&SymbolicLinkValueName,
0,
REG_LINK,
LinkTarget.Buffer,
LinkTarget.Length);
NtClose(hkInternal);
}
return RtlNtStatusToDosError(Status);
}
//*************************************************************
//
// DeleteRegLink()
//
// Purpose: Deletes a registry key (or link) without
// using the advapi32 registry apis
//
//
// Parameters: hkRoot - parent key
// lpSubKey - subkey to delete
//
// Return: ERROR_SUCCESS if successful
// other error if not
//
// Comments:
//
// History: Date Author Comment
// 3/6/98 adamed Created
//
//*************************************************************
LONG DeleteRegLink(HKEY hkRoot, LPCTSTR lpSubKey)
{
OBJECT_ATTRIBUTES Attributes;
HANDLE hKey;
NTSTATUS Status;
UNICODE_STRING Subtree;
//
// Initialize string for lpSubKey param
//
RtlInitUnicodeString(&Subtree, lpSubKey);
InitializeObjectAttributes(&Attributes,
&Subtree,
OBJ_CASE_INSENSITIVE | OBJ_OPENLINK,
hkRoot,
NULL);
//
// Open the link
//
Status = NtOpenKey( &hKey,
MAXIMUM_ALLOWED,
&Attributes );
//
// If we succeeded in opening it, delete it
//
if (NT_SUCCESS(Status)) {
Status = NtDeleteKey(hKey);
NtClose(hKey);
}
return RtlNtStatusToDosError(Status);
}
#define HIVEENTRY(h, i, f, l, t) {h, i, f, l, t}
// table of hives to load
typedef struct
{
LPCTSTR pszHive;
int csidl;
LPCTSTR pszFile;
LPCTSTR pszLink;
LPCTSTR pszTargetSubkey;
} USERHIVE;
static USERHIVE s_hives[] =
{
HIVEENTRY(
USER_CLASSES_HIVE_SUFFIX,
CSIDL_LOCAL_APPDATA,
USER_CLASSES_HIVE_NAME,
CLASSES_SUBTREE,
NULL),
HIVEENTRY(
TEXT("_Windows_Local"),
CSIDL_LOCAL_APPDATA,
TEXT("UserWin.dat"),
TEXT("Software\\Microsoft\\Windows\\ShellNoRoam\\"),
TEXT("\\ShellNoRoam")),
HIVEENTRY(
TEXT("_Windows"),
CSIDL_APPDATA,
TEXT("UserWin.dat"),
TEXT("Software\\Microsoft\\Windows\\Shell\\"),
TEXT("\\Shell")),
};
#define CCH_TARGETHIVE MAX_PATH - (sizeof(USER_KEY_PREFIX) / sizeof(TCHAR)) + 1
class CUserHives
{
public:
CUserHives() : _pszFile(0) {}
~CUserHives();
LONG LoadHives(LPPROFILE pProfile, LPTSTR pszSid);
BOOL UnloadHives(LPCTSTR pszSid);
protected: // methods
BOOL _Init(USERPROFILE *pProfile, LPCTSTR pszSid);
LONG _LoadHive(USERHIVE *phive);
BOOL _UnloadHive(LPCTSTR pszHive);
BOOL _CreateHiveFolder(USERHIVE *phive);
BOOL _MakeTargetHive(LPCTSTR pszHive);
LONG _CreateUserHive(USERHIVE *phive);
LONG _PreLoadKey(BOOL fNewHive, LPCTSTR pszSub);
LONG _CreateHiveFile(HKEY hk);
LONG _PostLoadKey(BOOL fNewHive);
LONG _CreateLink(USERHIVE *phive);
LONG _DeleteLink(HKEY hk, LPCTSTR pszLink);
protected: // members
USERPROFILE *_pup;
LPCTSTR _sid;
LPTSTR _pszFile; // MAX_PATH;
LPTSTR _pszTargetObject;
LPTSTR _pszTargetHive; // points inside of _pszTargetObject;
};
BOOL _StrCatSafe(LPTSTR pszDest, LPCTSTR pszSrc, int cchDestBuffSize)
{
LPWSTR psz = pszDest;
// we walk forward till we find the end of pszDest, subtracting
// from cchDestBuffSize as we go.
while (*psz)
{
psz++;
cchDestBuffSize--;
}
if (cchDestBuffSize > 0)
{
lstrcpyn(psz, pszSrc, cchDestBuffSize);
return TRUE;
}
return FALSE;
}
LONG CUserHives::_DeleteLink(HKEY hk, LPCTSTR pszLink)
{
// delete the existing link
LONG Error = DeleteRegLink(hk, pszLink);
//
// It's ok if the deletion fails because the classes key, link or nonlink,
// doesn't exist. It's also ok if it fails because the key exists but is not
// a link and has children -- in this case, the key and its children will
// be eliminated by a subsequent call to RegDelNode.
//
if (ERROR_SUCCESS != Error) {
if ((ERROR_FILE_NOT_FOUND != Error) && (ERROR_ACCESS_DENIED != Error)) {
return Error;
}
}
//
// Just to be safe, destroy any existing HKCU\Software\Classes and children.
// This key may exist from previous unreleased versions of NT5, or from
// someone playing around with hive files and adding bogus keys
//
if (!RegDelnode (hk, (LPTSTR)pszLink)) {
Error = GetLastError();
//
// It's ok if this fails because the key doesn't exist, since
// nonexistence is our goal.
//
if (ERROR_FILE_NOT_FOUND != Error) {
return Error;
}
}
return ERROR_SUCCESS;
}
LONG CUserHives::_CreateLink(USERHIVE *phive)
{
LONG error = _DeleteLink(_pup->hKeyCurrentUser, phive->pszLink);
if (error == ERROR_SUCCESS)
{
//
// At this point, we know that no HKCU\Software\Classes exists, so we should
// be able to make a link there which points to the hive with the user class
// data.
//
if (!phive->pszTargetSubkey || _StrCatSafe(_pszTargetObject, phive->pszTargetSubkey, MAX_PATH))
error = CreateRegLink(_pup->hKeyCurrentUser, phive->pszLink, _pszTargetObject);
else
error = ERROR_NOT_ENOUGH_MEMORY;
}
return error;
}
//*************************************************************
//
// CreateClassesFolder()
//
// Purpose: Create the directory for the classes hives
//
//
// Parameters:
// pProfile - pointer to profile struct
// szLocalHiveDir - out param for location of
// classes hive folder.
//
// Return: ERROR_SUCCESS if successful
// other error if an error occurs
//
//
//*************************************************************
BOOL CUserHives::_CreateHiveFolder(USERHIVE *phive)
{
BOOL fRet = FALSE;
//
// Find out the correct shell location for our subdir --
// this call will create it if it doesn't exist.
// This is a subdir of the user profile which does not
// roam.
//
//
// Need to do this to fix up a localisation prob. in NT4
//
PatchLocalAppData(_pup->hTokenUser);
if (GetFolderPath(phive->csidl, _pup->hTokenUser, _pszFile))
{
// append our appdata sub-dir
_StrCatSafe(_pszFile, TEXT("\\Microsoft\\Windows\\"), MAX_PATH);
//
// We will now create our own subdir, CLASSES_SUBDIRECTORY,
// inside the local appdata subdir we just received above.
//
fRet = CreateNestedDirectory(_pszFile, NULL);
}
else
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
return fRet;
}
LONG CUserHives::_PostLoadKey(BOOL fNewHive)
{
HKEY hk;
LONG err = RegOpenKeyEx(HKEY_USERS, _pszTargetHive, 0,
WRITE_DAC | KEY_ENUMERATE_SUB_KEYS | READ_CONTROL,
&hk);
if (ERROR_SUCCESS == err)
{
if (fNewHive)
{
DebugMsg((DM_VERBOSE, TEXT("CreateClassHive: existing user classes hive not found")));
//
// An existing hive was not found before we created this hive, so we need
// to set security on the new hive
//
if (SetDefaultUserHiveSecurity(_pup, NULL, hk))
{
if (!SetFileAttributes (_pszFile, FILE_ATTRIBUTE_HIDDEN))
{
DebugMsg((DM_WARNING, TEXT("CreateClassHive: unable to set file attributes")
TEXT(" on classes hive %s with error %x"), _pszFile, GetLastError()));
}
}
else
err = GetLastError();
}
RegCloseKey(hk);
}
return err;
}
LONG CUserHives::_CreateHiveFile(HKEY hk)
{
LONG err = ERROR_PRIVILEGE_NOT_HELD;
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN fEnabled;
BOOL fAdjusted = FALSE;
HANDLE hToken;
if(!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &hToken)
|| hToken == NULL)
{
fAdjusted = TRUE;
status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &fEnabled);
}
else
{
CloseHandle(hToken);
}
if (NT_SUCCESS(status))
{
err = RegSaveKeyEx(hk, _pszFile, NULL, REG_LATEST_FORMAT);
if(fAdjusted)
{
RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, fEnabled, FALSE, &fEnabled);
}
}
return err;
}
LONG CUserHives::_PreLoadKey(BOOL fNewHive, LPCTSTR pszSub)
{
LONG err = ERROR_SUCCESS;
if (fNewHive)
{
// we need to create it using REG_LATEST_FORMAT
HKEY hk;
err = RegCreateKeyEx(_pup->hKeyCurrentUser, TEXT("UserTempHive"), 0, NULL,
REG_OPTION_NON_VOLATILE,
MAXIMUM_ALLOWED, NULL,
&hk, NULL);
if (ERROR_SUCCESS == err)
{
if (pszSub)
{
HKEY hkSub;
err = RegCreateKeyEx(hk, pszSub + 1, 0, NULL,
REG_OPTION_NON_VOLATILE,
MAXIMUM_ALLOWED, NULL,
&hkSub, NULL);
if (ERROR_SUCCESS == err)
{
RegCloseKey(hkSub);
}
else
{
DebugMsg((DM_WARNING, TEXT("CUserHives::_PreLoadKey() failed to create %s"), pszSub + 1));
}
}
RegFlushKey(hk);
if (ERROR_SUCCESS == err)
{
err = _CreateHiveFile(hk);
}
RegCloseKey(hk);
RegDelnode(_pup->hKeyCurrentUser, TEXT("UserTempHive"));
}
}
return err;
}
BOOL CUserHives::_MakeTargetHive(LPCTSTR pszHive)
{
// construct the hive name by appending the suffix to the sid
lstrcpyn(_pszTargetHive, _sid, CCH_TARGETHIVE);
return _StrCatSafe(_pszTargetHive, pszHive, CCH_TARGETHIVE);
}
LONG CUserHives::_CreateUserHive(USERHIVE *phive)
{
// need to do something special if it doesnt already exist
BOOL fNewHive = (-1 == GetFileAttributes(_pszFile));
LONG err = _PreLoadKey(fNewHive, phive->pszTargetSubkey);
// mount the hive
if (ERROR_SUCCESS == err)
{
err = MyRegLoadKey(HKEY_USERS, _pszTargetHive, _pszFile);
if (ERROR_SUCCESS == err)
{
err = _PostLoadKey(fNewHive);
if (ERROR_SUCCESS == err)
{
err = _CreateLink(phive);
}
}
else
{
DebugMsg((DM_VERBOSE, TEXT("CreateClassHive: existing user classes hive found")));
}
}
return err;
}
LONG CUserHives::_LoadHive(USERHIVE *phive)
{
// create the directory for the user-specific classes hive
LONG error = ERROR_NOT_ENOUGH_MEMORY;
if (_CreateHiveFolder(phive) && _MakeTargetHive(phive->pszHive))
{
if (0 == lstrcmp(phive->pszHive, USER_CLASSES_HIVE_SUFFIX))
{
// Move HKCU\Software\Classes before merging the two
// branches. Ignore any errors here as this branch is
// about to be deleted by the merge anyway.
// The reason for this move is because NT4 stores customized
// shell icons in HKCU\Software\Classes\CLSID\{CLSID_x} and
// NT5 stores this at HKCU\Software\Microsoft\Windows\
// CurrentVersion\Explorer\CLSID\{CLSID_x} and must be moved
// now before being deleted.
LONG error = MoveUserClassesBeforeMerge(_pup, _pszFile);
if (ERROR_SUCCESS != error)
{
DebugMsg((DM_WARNING, TEXT("MoveUserClassesBeforeMerge: Failed unexpectedly (%d)."),
error));
}
}
// append the file name to load/save to
if (_StrCatSafe(_pszFile, phive->pszFile, MAX_PATH))
{
error = _CreateUserHive(phive);
if (ERROR_SUCCESS != error)
{
DebugMsg((DM_WARNING, TEXT("LoadUserClasses: Failed to create user classes hive (%d)."), error));
}
}
}
else
error = GetLastError();
return error;
}
CUserHives::~CUserHives()
{
if (_pszFile)
LocalFree(_pszFile);
}
BOOL CUserHives::_Init(USERPROFILE *pProfile, LPCTSTR pszSid)
{
// unref'd vars!!!
_pup = pProfile;
_sid = pszSid;
_pszFile = (LPTSTR) LocalAlloc(LPTR, (MAX_PATH * sizeof(TCHAR)) * 2);
if (_pszFile)
{
//
// WARNING - we are being a little clever here
// we only allocate one buffer (_pszFile) that is MAX_PATH * 2
// we then split it up into two buffers of MAX_PATH (_pszFile & _pszTargetObject)
// _pszTargetObject has a constant prefix of USER_KEY_PREFIX ("\Registry\User\")
// we later append the key name that we will load under HKU.
// _pszTargetHive points to that value.
//
// ie _pszTargetObject = "\Registry\User\{SID}_Windows"
// _pszTargetHive = "{SID}_Windows"
//
_pszTargetObject = _pszFile + MAX_PATH;
lstrcpy(_pszTargetObject, USER_KEY_PREFIX);
_pszTargetHive = _pszTargetObject + sizeof(USER_KEY_PREFIX) / sizeof(TCHAR) - 1;
return TRUE;
}
return FALSE;
}
LONG CUserHives::LoadHives(USERPROFILE *pProfile, LPTSTR pszSid)
{
LONG error;
if (_Init(pProfile, pszSid))
{
// load each of the hives in turn
for (int i = 0; i < ARRAYSIZE(s_hives); i++)
{
error = _LoadHive(&s_hives[i]);
if (error != ERROR_SUCCESS)
break;
}
}
else
error = ERROR_NOT_ENOUGH_MEMORY;
return error;
}
LONG LoadUserHives(LPPROFILE pProfile, LPTSTR pszSid)
{
CUserHives hives;
return hives.LoadHives(pProfile, pszSid);
}
BOOL CUserHives::_UnloadHive(LPCTSTR pszHive)
{
if (_MakeTargetHive(pszHive))
{
HKEY hKey;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING usTargetObject;
//
// Initialize string for lpSubKey param
//
RtlInitUnicodeString(&usTargetObject, _pszTargetObject);
InitializeObjectAttributes(&oa,
&usTargetObject,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
NTSTATUS Status = NtOpenKey((PHANDLE)&hKey,
KEY_READ,
&oa);
if (NT_SUCCESS(Status))
{
//
// Make sure the hive is persisted properly
//
RegFlushKey(hKey);
RegCloseKey(hKey);
//
// Unmount the hive -- this should only fail if
// someone has a subkey of the hive open -- this
// should not normally happen and probably means there's a service
// that is leaking keys.
//
return MyRegUnLoadKey(HKEY_USERS, _pszTargetHive);
}
}
DebugMsg((DM_WARNING, TEXT("UnLoadClassHive: failed to unload user hive %s"), pszHive));
return FALSE;
}
BOOL CUserHives::UnloadHives(LPCTSTR pszSid)
{
if (_Init(NULL, pszSid))
{
// load each of the hives in turn
for (int i = 0; i < ARRAYSIZE(s_hives); i++)
{
if (!_UnloadHive(s_hives[i].pszHive))
return FALSE;
}
}
return TRUE;
}
BOOL UnloadUserHives(LPTSTR pszSid)
{
CUserHives hives;
// remove the implicit reference held by this process
RegCloseKey(HKEY_CLASSES_ROOT);
return hives.UnloadHives(pszSid);
}