Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1302 lines
43 KiB

//*************************************************************
//
// 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:
Adam P. Edwards (adamed) 10-Oct-1997
Gregory Jensenworth (gregjen) 1-Jul-1997
Key Functions:
LoadUserClasses
UnloadClasses
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>
#include <wow64reg.h>
#include "strsafe.h"
#define USER_CLASSES_HIVE_NAME TEXT("\\UsrClass.dat")
#define CLASSES_SUBTREE TEXT("Software\\Classes\\")
#define CLASSES_SUBDIRECTORY TEXT("\\Microsoft\\Windows\\")
#define MAX_HIVE_DIR_CCH (MAX_PATH + 1 + lstrlen(CLASSES_SUBDIRECTORY))
#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))
//*************************************************************
//
// 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,
LPTSTR SubKeyName,
LPTSTR 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, LPTSTR lpSubKey)
{
OBJECT_ATTRIBUTES Attributes;
HKEY 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);
}
//*************************************************************
//
// MapUserClassesIntoUserHive()
//
// Purpose: Makes HKCU\\Software\\Classes point to
// the user classes hive. This is done by using
// a symbolic link, a feature of the kernel's
// object manager. We use this to make
// HKCU\Software\Classes poing to another hive
// where the classes exist physically.
// If there is an existing HKCU\\Software\\Classes,
// it is deleted along with everything below it.
//
//
// Parameters: lpProfile - user's profile
// lpSidString - string representing user's sid
//
// Return: ERROR_SUCCESS if successful
// other error if not
//
// Comments:
//
// History: Date Author Comment
// 3/6/98 adamed Created
//
//*************************************************************
LONG MapUserClassesIntoUserHive(
LPPROFILE lpProfile,
LPTSTR lpSidString)
{
LONG Error;
LPTSTR lpClassesKeyName = NULL;
DWORD cchClassesKeyName;
//
// get memory for user classes keyname
//
cchClassesKeyName = lstrlen(lpSidString) +
lstrlen(USER_CLASSES_HIVE_SUFFIX) +
lstrlen(USER_KEY_PREFIX) +
1;
lpClassesKeyName = (LPTSTR) LocalAlloc(LPTR, cchClassesKeyName * sizeof(TCHAR));
//
// Can't continue if no memory;
//
if ( !lpClassesKeyName ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Exit;
}
//
// concoct user classes keyname
//
StringCchCopy( lpClassesKeyName, cchClassesKeyName, USER_KEY_PREFIX);
StringCchCat ( lpClassesKeyName, cchClassesKeyName, lpSidString );
StringCchCat ( lpClassesKeyName, cchClassesKeyName, USER_CLASSES_HIVE_SUFFIX);
//
// Eliminate any existing form of HKCU\Software\Classes
//
//
// First, delete existing link
//
Error = DeleteRegLink(lpProfile->hKeyCurrentUser, CLASSES_SUBTREE);
//
// 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)) {
goto Exit;
}
}
//
// 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 ((Error = RegDelnode (lpProfile->hKeyCurrentUser, CLASSES_SUBTREE)) != ERROR_SUCCESS) {
//
// It's ok if this fails because the key doesn't exist, since
// nonexistence is our goal.
//
if (ERROR_FILE_NOT_FOUND != Error) {
goto Exit;
}
}
//
// 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.
//
Error = CreateRegLink(lpProfile->hKeyCurrentUser,
CLASSES_SUBTREE,
lpClassesKeyName);
Exit:
if (lpClassesKeyName)
LocalFree(lpClassesKeyName);
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.
// cchLocalHiveDir - size of the buffer, in TCHARs
//
// Return: ERROR_SUCCESS if successful
// other error if an error occurs
//
//
//*************************************************************
LONG CreateClassesFolder(LPPROFILE pProfile, LPTSTR szLocalHiveDir, DWORD cchLocalHiveDir)
{
BOOL fGotLocalData;
BOOL fCreatedSubdirectory;
//
// 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(pProfile->hTokenUser);
fGotLocalData = GetFolderPath (
CSIDL_LOCAL_APPDATA,
pProfile->hTokenUser,
szLocalHiveDir);
if (!fGotLocalData) {
// a bogus error, check the debug output of GetFolderPath() for correct
// error code.
return ERROR_INVALID_FUNCTION;
}
//
// append the terminating pathsep so we can
// add more paths to the newly retrieved subdir
//
StringCchCat(szLocalHiveDir, cchLocalHiveDir, CLASSES_SUBDIRECTORY);
//
// We will now create our own subdir, CLASSES_SUBDIRECTORY,
// inside the local appdata subdir we just received above.
//
fCreatedSubdirectory = CreateNestedDirectory(szLocalHiveDir, NULL);
if (fCreatedSubdirectory) {
return ERROR_SUCCESS;
}
return GetLastError();
}
//*************************************************************
//
// UnloadClassHive()
//
// Purpose: unmounts a classes hive
//
// Parameters: lpSidString - string representing user's
// sid
// lpSuffix - hive name suffix
//
// Return: ERROR_SUCCESS if successful,
// other error if not
//
// Comments:
//
// History: Date Author Comment
// 3/6/98 adamed Created
//
//*************************************************************
LONG UnloadClassHive(
LPTSTR lpSidString,
LPTSTR lpSuffix)
{
LPTSTR lpHiveName = NULL;
LONG error;
OBJECT_ATTRIBUTES Attributes;
NTSTATUS Status;
HKEY hKey;
UNICODE_STRING ClassesFullPath;
DWORD cchHiveName;
//
// Get memory for the combined hive key name
//
cchHiveName = lstrlen(lpSidString) +
lstrlen(USER_KEY_PREFIX) +
lstrlen(lpSuffix) +
1;
lpHiveName = (LPTSTR) LocalAlloc(LPTR, cchHiveName * sizeof(TCHAR));
if (!lpHiveName)
{
error = ERROR_NOT_ENOUGH_MEMORY;
goto Exit;
}
//
// build the key name of the combined hive
//
StringCchCopy( lpHiveName, cchHiveName, USER_KEY_PREFIX );
StringCchCat ( lpHiveName, cchHiveName, lpSidString );
StringCchCat ( lpHiveName, cchHiveName, lpSuffix);
//
// Prepare to open the root of the classes hive
//
RtlInitUnicodeString(&ClassesFullPath, lpHiveName);
InitializeObjectAttributes(&Attributes,
&ClassesFullPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenKey( &hKey,
KEY_READ,
&Attributes );
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.
//
if (MyRegUnLoadKey(HKEY_USERS,
lpHiveName + ((sizeof(USER_KEY_PREFIX) / sizeof(TCHAR))-1))) {
error = ERROR_SUCCESS;
} else {
error = GetLastError();
}
} else {
error = RtlNtStatusToDosError(Status);
}
Exit:
if (lpHiveName)
LocalFree(lpHiveName);
if (error != ERROR_SUCCESS) {
DebugMsg((DM_WARNING, TEXT("UnLoadClassHive: failed to unload classes key with %x"), error));
} else {
DebugMsg((DM_VERBOSE, TEXT("UnLoadClassHive: Successfully unmounted %s%s"), lpSidString, lpSuffix));
}
return error;
}
//*************************************************************
//
// UnloadClasses()
//
// Purpose: Free the special combined hive
//
// Parameters: lpProfile - Profile information
// SidString - User's Sid as a string
//
// Return: TRUE if successful
// FALSE if not
//
//*************************************************************
BOOL UnloadClasses(
LPTSTR lpSidString)
{
LONG Error;
// unload user classes hive
Error = UnloadClassHive(
lpSidString,
USER_CLASSES_HIVE_SUFFIX);
return ERROR_SUCCESS == Error;
}
HRESULT MyRegLoadKeyEx(HKEY hKeyRoot, LPTSTR lpSubKey, LPTSTR lpFile, HKEY hKeyTrustClass)
{
HRESULT hr = E_FAIL;
NTSTATUS Status;
TCHAR lpKeyName[MAX_PATH];
UNICODE_STRING UnicodeKeyName;
UNICODE_STRING UnicodeFileName;
OBJECT_ATTRIBUTES keyAttributes;
OBJECT_ATTRIBUTES fileAttributes;
BOOLEAN WasEnabled;
BOOL bAdjustPriv = FALSE;
BOOL bAllocatedFileName = FALSE;
HANDLE hToken = NULL;
DebugMsg((DM_VERBOSE, TEXT("MyRegLoadKeyEx: Loading key <%s>"), lpSubKey));
//
// Only support loading hive to HKU or HKLM
//
if (hKeyRoot != HKEY_USERS && hKeyRoot != HKEY_LOCAL_MACHINE)
{
DebugMsg((DM_WARNING, TEXT("MyRegLoadKeyEx: only HKU or HKLM is supported!")));
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto Exit;
}
//
// Construct the key name for kernel object
//
hr = StringCchCopy(lpKeyName, ARRAYSIZE(lpKeyName), (hKeyRoot == HKEY_USERS) ? TEXT("\\Registry\\User\\") : TEXT("\\Registry\\Machine\\"));
if (FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("MyRegLoadKeyEx: StringCchCopy failed, hr = %08X"), hr));
goto Exit;
}
hr = StringCchCat (lpKeyName, ARRAYSIZE(lpKeyName), lpSubKey);
if (FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("MyRegLoadKeyEx: StringCchCat failed, hr = %08X"), hr));
goto Exit;
}
//
// Initialize the key object attribute
//
RtlInitUnicodeString(&UnicodeKeyName, lpKeyName);
InitializeObjectAttributes(&keyAttributes,
&UnicodeKeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
//
// Convert the file name to kernel file name
//
if (!RtlDosPathNameToNtPathName_U(lpFile,
&UnicodeFileName,
NULL,
NULL))
{
DebugMsg((DM_WARNING, TEXT("MyRegLoadKeyEx: RtlDosPathNameToNtPathName_U failed for <%s>!"), lpFile));
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto Exit;
}
bAllocatedFileName = TRUE;
//
// Initialize the file object attribute
//
InitializeObjectAttributes(&fileAttributes,
&UnicodeFileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
//
// Check to see if we are impersonating, if not, we need to enable the Restore privilege
//
if(!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &hToken) || hToken == NULL)
{
Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasEnabled);
if (!NT_SUCCESS(Status))
{
DebugMsg((DM_WARNING, TEXT("MyRegLoadKeyEx: Failed to enable restore privilege to load registry key, Status = %08x"), Status));
hr = HRESULT_FROM_WIN32(RtlNtStatusToDosError(Status));
goto Exit;
}
bAdjustPriv = TRUE;
}
else
{
CloseHandle(hToken);
}
//
// Now loading the key
//
/*
Status = NtLoadKey(&keyAttributes,
&fileAttributes);
*/
Status = NtLoadKeyEx(&keyAttributes,
&fileAttributes,
0,
hKeyTrustClass);
if (!NT_SUCCESS(Status))
{
TCHAR szErr[MAX_PATH];
DWORD dwErr;
DebugMsg((DM_WARNING, TEXT("MyRegLoadKeyEx: NtLoadKey failed for <%s>, Status = %08x"), lpSubKey, Status));
dwErr = RtlNtStatusToDosError(Status);
ReportError(NULL, PI_NOUI, 2, EVENT_REGLOADKEYFAILED, GetErrString(dwErr, szErr), lpFile);
hr = HRESULT_FROM_WIN32(dwErr);
goto Exit;
}
#if defined(_WIN64)
else
{
//
// Notify Wow64 service that it need to watch this hive if it care to do so
//
if ( hKeyRoot == HKEY_USERS )
Wow64RegNotifyLoadHiveUserSid ( lpSubKey );
}
#endif
DebugMsg((DM_VERBOSE, TEXT("MyRegLoadKeyEx: Successfully loaded <%s>"), lpSubKey));
hr = S_OK;
Exit:
//
// Restore the privilege to its previous state
//
if(bAdjustPriv)
{
Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, WasEnabled, FALSE, &WasEnabled);
if (!NT_SUCCESS(Status))
{
DebugMsg((DM_WARNING, TEXT("MyRegLoadKeyEx: Failed to restore RESTORE privilege to previous enabled state. Status = %08X"), Status));
}
}
if (bAllocatedFileName)
{
RtlFreeUnicodeString(&UnicodeFileName);
}
return hr;
}
//*************************************************************
//
// CreateUserClasses()
//
// Purpose: Creates necessary hives for user classes
//
// Parameters: lpProfile - Profile information
// lpSidString - User's Sid as a string
// lpSuffix - Suffix to follow the user's sid
// when naming the hive
// lpHiveFileName - full path for backing hive file
// of user classes
// phkResult - root of created hive on
// success
//
// Return: ERROR_SUCCESS if successful
// other NTSTATUS if an error occurs
//
//*************************************************************
LONG CreateClassHive(
LPPROFILE lpProfile,
LPTSTR lpSidString,
LPTSTR lpSuffix,
LPTSTR lpHiveFilename,
BOOL bNewlyIssued)
{
LONG res;
LPTSTR lpHiveKeyName = NULL;
WIN32_FILE_ATTRIBUTE_DATA fd;
BOOL fHiveExists;
HKEY hkRoot = NULL;
DWORD cchHiveKeyName;
HKEY hKeyUser = NULL;
HRESULT hr;
//
// allocate a space big enough for the hive name
//
cchHiveKeyName = lstrlen(lpSidString) +
lstrlen(lpSuffix) +
1;
lpHiveKeyName = (LPTSTR) LocalAlloc(LPTR, cchHiveKeyName * sizeof(TCHAR) );
if ( !lpHiveKeyName )
{
res = ERROR_NOT_ENOUGH_MEMORY;
goto Exit;
}
//
// Open the HKU\{Sid} first, we use this handle as the trust class
//
StringCchCopy(lpHiveKeyName, cchHiveKeyName, lpSidString);
res = RegOpenKeyEx(HKEY_USERS,
lpHiveKeyName,
0,
KEY_READ,
&hKeyUser);
if (res != ERROR_SUCCESS)
{
DebugMsg((DM_WARNING, TEXT("CreateClassHive: fail to open user hive. Error %d"),res));
goto Exit;
}
//
// Append the suffix to construct the class hive key name
//
StringCchCat (lpHiveKeyName, cchHiveKeyName, lpSuffix);
//
// First, see if this hive already exists. We need to do this rather than just letting
// RegLoadKey create or load the existing hive because if the hive is new,
// we need to apply security.
//
fHiveExists = GetFileAttributesEx(
lpHiveFilename,
GetFileExInfoStandard,
&fd );
//
// mount the hive
//
hr = MyRegLoadKeyEx(HKEY_USERS, lpHiveKeyName, lpHiveFilename, hKeyUser);
if (FAILED(hr))
{
DebugMsg((DM_WARNING, TEXT("CreateClassHive: MyRegLoadKeyEx failed, hr = %08X"), hr));
res = HRESULT_CODE(hr);
goto Exit;
}
//
// If we succeeded, open the root
//
res = RegOpenKeyEx( HKEY_USERS,
lpHiveKeyName,
0,
WRITE_DAC | KEY_ENUMERATE_SUB_KEYS | READ_CONTROL,
&hkRoot);
if (ERROR_SUCCESS != res) {
MyRegUnLoadKey(HKEY_USERS, lpHiveKeyName);
DebugMsg((DM_WARNING, TEXT("CreateClassHive: fail to open classes hive. Error %d"),res));
goto Exit;
}
if (!fHiveExists || bNewlyIssued) {
if (!fHiveExists) {
DebugMsg((DM_VERBOSE, TEXT("CreateClassHive: existing user classes hive not found")));
}
else {
DebugMsg((DM_VERBOSE, TEXT("CreateClassHive: user classes hive copied from Default User")));
}
//
// This hive is newly issued i.e. either created fresh or copied from
// "Default User" profile, so we need to set security on the new hive
//
//
// set security on this hive
//
if (!SetDefaultUserHiveSecurity(lpProfile, NULL, hkRoot)) {
res = GetLastError();
DebugMsg((DM_WARNING, TEXT("CreateClassHive: Fail to assign proper security on new classes hive")));
}
//
// If we succeed, set the hidden attribute on the backing hive file
//
if (ERROR_SUCCESS == res) {
if (!SetFileAttributes (lpHiveFilename, FILE_ATTRIBUTE_HIDDEN)) {
DebugMsg((DM_WARNING, TEXT("CreateClassHive: unable to set file attributes")
TEXT(" on classes hive %s with error %x"), lpHiveFilename, GetLastError()));
}
}
} else {
DebugMsg((DM_VERBOSE, TEXT("CreateClassHive: existing user classes hive found")));
}
Exit:
if (hKeyUser)
{
RegCloseKey(hKeyUser);
}
if (hkRoot) {
RegCloseKey(hkRoot);
}
if (lpHiveKeyName)
LocalFree(lpHiveKeyName);
return res;
}
//*************************************************************
//
// CreateUserClassesHive()
//
// Purpose: create the user-specific classes hive
//
// Parameters: lpProfile - Profile information
// SidString - User's Sid as a string
// szLocalHiveDir - directory in userprofile
// where hive should be located
//
// Return: ERROR_SUCCESS if successful,
// other error if not
//
// Comments:
//
// History: Date Author Comment
// 3/6/98 adamed Created
//
//*************************************************************
LONG CreateUserClassesHive(
LPPROFILE lpProfile,
LPTSTR SidString,
LPTSTR szLocalHiveDir,
BOOL bNewlyIssued)
{
LPTSTR lpHiveFilename = NULL;
LONG res;
DWORD cchHiveFilename;
// allocate a space big enough for the hive filename (including trailing null)
cchHiveFilename = lstrlen(szLocalHiveDir) +
lstrlen(USER_CLASSES_HIVE_NAME) +
1;
lpHiveFilename = (LPTSTR) LocalAlloc(LPTR, cchHiveFilename * sizeof(TCHAR));
if ( !lpHiveFilename ) {
res = ERROR_NOT_ENOUGH_MEMORY;
goto Exit;
}
StringCchCopy( lpHiveFilename, cchHiveFilename, szLocalHiveDir);
StringCchCat ( lpHiveFilename, cchHiveFilename, USER_CLASSES_HIVE_NAME);
res = CreateClassHive(
lpProfile,
SidString,
USER_CLASSES_HIVE_SUFFIX,
lpHiveFilename,
bNewlyIssued);
if (ERROR_SUCCESS != res) {
goto Exit;
}
res = MapUserClassesIntoUserHive(lpProfile, SidString);
Exit:
if (lpHiveFilename)
LocalFree(lpHiveFilename);
return res;
}
//*************************************************************
//
// 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;
DWORD cchLocalTempHive;
// Allocate enough space for the local hive directory and the temp hive filename.
cchLocalTempHive = lstrlen(lpcszLocalHiveDir) + lstrlen(TEMPHIVE_FILENAME) + 1;
pszLocalTempHive = (LPTSTR) LocalAlloc(LPTR, cchLocalTempHive * sizeof(TCHAR));
// Get a path to a file to save HKCU\Software\Classes into.
if (pszLocalTempHive != NULL)
{
HANDLE hToken = NULL;
BOOL bAdjustPriv = FALSE;
StringCchCopy(pszLocalTempHive, cchLocalTempHive, lpcszLocalHiveDir);
StringCchCat (pszLocalTempHive, cchLocalTempHive, 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);
LocalFree(pszLocalTempHive);
}
else
{
res = ERROR_NOT_ENOUGH_MEMORY;
DebugMsg((DM_WARNING, TEXT("LocalAlloc failed to allocate temp hive path buffer")));
}
}
RegCloseKey(hKeySource);
}
else if (ERROR_FILE_NOT_FOUND == res)
{
res = ERROR_SUCCESS;
}
return res;
}
//*************************************************************
//
// LoadUserClasses()
//
// Purpose: Combines the HKLM\Software\Classes subtree with the
// HKCU\Software\Classes subtree
//
// Parameters: lpProfile - Profile information
// SidString - User's Sid as a string
//
// Return: ERROR_SUCCESS if successful
// other NTSTATUS if an error occurs
//
//*************************************************************
LONG LoadUserClasses( LPPROFILE lpProfile, LPTSTR SidString, BOOL bNewlyIssued)
{
LONG error;
LPTSTR szLocalHiveDir = NULL;
error = ERROR_SUCCESS;
//
// first, we will create a directory for the user-specific
// classes hive -- we need memory for it:
//
szLocalHiveDir = (LPTSTR) LocalAlloc(LPTR, MAX_HIVE_DIR_CCH * sizeof(TCHAR));
if (!szLocalHiveDir) {
error =ERROR_NOT_ENOUGH_MEMORY;
goto Exit;
}
//
// create the directory for the user-specific classes hive
//
error = CreateClassesFolder(lpProfile, szLocalHiveDir, MAX_HIVE_DIR_CCH);
if (ERROR_SUCCESS != error) {
DebugMsg((DM_WARNING, TEXT("LoadUserClasses: Failed to create folder for combined hive (%d)."),
error));
goto Exit;
}
// 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.
error = MoveUserClassesBeforeMerge(lpProfile, szLocalHiveDir);
if (ERROR_SUCCESS != error) {
DebugMsg((DM_WARNING, TEXT("MoveUserClassesBeforeMerge: Failed unexpectedly (%d)."),
error));
}
//
// Now create the user classes hive
//
error = CreateUserClassesHive( lpProfile, SidString, szLocalHiveDir, bNewlyIssued);
if (ERROR_SUCCESS != error) {
DebugMsg((DM_WARNING, TEXT("LoadUserClasses: Failed to create user classes hive (%d)."),
error));
}
Exit:
if (szLocalHiveDir)
LocalFree(szLocalHiveDir);
return error;
}