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.
2331 lines
80 KiB
2331 lines
80 KiB
/*************************************************************************
|
|
*
|
|
* regmap.c
|
|
*
|
|
* Handle Copy-On-Reference Registry Entry Mapping
|
|
*
|
|
* copyright notice: Copyright 1996-1997, Citrix Systems Inc.
|
|
* Copyright (C) 1997-1999 Microsoft Corp.
|
|
*
|
|
* Author: Bill Madden
|
|
*
|
|
* NOTE for Hydra (butchd 9/26/97): In comments below, substitute
|
|
*
|
|
* SOFTWARE\Citrix
|
|
*
|
|
* with
|
|
*
|
|
* SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server
|
|
*
|
|
* Here's a brief(?) summary of how the registry mapping works. The goal is
|
|
* that an administrator can install an application, and then all users can
|
|
* use it, without any per-user configuration. The current design is that
|
|
* the administrator puts the system into installation mode (change user
|
|
* /install), installs the application, and then returns the system to
|
|
* execute mode (change user /execute). There are hooks in the API's used to
|
|
* create keys and values in the registry (BaseRegCreateKey, BaseRegSetValue,
|
|
* BaseRegRestoreKey, etc), and the hooks create a copy of the registry keys
|
|
* created under \HKEY_LOCAL_MACHINE\SOFTWARE\Citrix\Install (both for the
|
|
* user specific keys and the local machine keys). The local machine keys
|
|
* are added so that if sometime in the future we need to know all of the
|
|
* registry values created by an application, there is a copy of them
|
|
* available.
|
|
*
|
|
* When a user starts a Win32 app for the first time, the app will open the
|
|
* keys it needs to query. If the key doesn't exist underneath
|
|
* HKEY_CURRENT_USER, there are hooks in the base registry API's to catch the
|
|
* error and then search underneath the Citrix/Install section to see if the
|
|
* key exists there. If the key exists in the install section, we copy the
|
|
* key, its values, and its subkeys to the current user's registry. This
|
|
* way we only have to hook opens, and not every registry API. This helps
|
|
* reduce the overhead associated with this registry mapping.
|
|
*
|
|
* Some apps (such as the office short cut bar) delete entries and need to
|
|
* recreate the entries themselves. When an app deletes a key and the
|
|
* system is in execute mode, we will set a value under the key indicating
|
|
* that we should only copy the key to the user once. What this currently
|
|
* means, is that if this is the only key being created, we won't create it
|
|
* when that flag is set. However, if we are creating this key because we
|
|
* created its parent, we will still create the key.
|
|
*
|
|
* The other part of the registry mapping support is that when a user logs
|
|
* in, userinit calls a routine (CtxCheckNewRegEntries) which checks if any
|
|
* of the system keys are newer than any of the corresponding user keys. If
|
|
* they are, the user keys are deleted (we're assuming that if the system
|
|
* key is newer, than the admin has installed a newer version of an
|
|
* application). This deleting can be disabled by setting a value under
|
|
* HKEY_LOCAL_MACHINE\Software\Citrix\Compatibility\IniFiles\xxx where xxx
|
|
* is the key name, and the value should be 0x48.
|
|
*
|
|
*************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
#include <rpc.h>
|
|
#include <regmap.h>
|
|
#include <aclapi.h>
|
|
#include "omission.h"
|
|
|
|
/* External Functions */
|
|
|
|
ULONG GetCtxAppCompatFlags(LPDWORD, LPDWORD);
|
|
|
|
/* Internal Functions */
|
|
PWCHAR GetUserSWKey(PWCHAR pPath, PBOOL fUserReg, PBOOL bClassesKey);
|
|
PWCHAR Ctxwcsistr(PWCHAR pstring1, PWCHAR pstring2);
|
|
NTSTATUS TermsrvGetRegPath(IN HANDLE hKey,
|
|
IN POBJECT_ATTRIBUTES pObjectAttr,
|
|
IN PWCHAR pInstPath,
|
|
IN ULONG ulbuflen);
|
|
NTSTATUS TermsrvGetInstallPath(IN PWCHAR pUserPath,
|
|
IN PWCHAR *ppInstPath);
|
|
NTSTATUS TermsrvCreateKey(IN PWCHAR pSrcPath,
|
|
IN PWCHAR pDstPath,
|
|
IN BOOL fCloneValues,
|
|
IN BOOL fCloneSubKeys,
|
|
OUT PHANDLE phKey);
|
|
NTSTATUS TermsrvCloneKey(HANDLE hSrcKey,
|
|
HANDLE hDstKey,
|
|
PKEY_FULL_INFORMATION pDefKeyInfo,
|
|
BOOL fCreateSubKeys);
|
|
void TermsrvLogRegInstallTime(void);
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvCreateRegEntry
|
|
*
|
|
* If in installation mode, create the registry entry in the citrix
|
|
* install user section of the registry. If the system is in execution
|
|
* mode, verify that the key values and subkeys have been created.
|
|
*
|
|
* ENTRY:
|
|
* IN HANDLE hKey: Handle of new key just created
|
|
* IN ULONG TitleIndex: Title Index
|
|
* IN PUNICODE_STRING pUniClass: Ptr to unicode string of class
|
|
* IN ULONG ulCreateOpt: Creation options
|
|
*
|
|
*
|
|
* EXIT:
|
|
* TRUE: Entry created in install section or cloned from install section
|
|
* FALSE: Entry not created or cloned
|
|
*
|
|
****************************************************************************/
|
|
|
|
BOOL TermsrvCreateRegEntry(IN HANDLE hKey,
|
|
IN POBJECT_ATTRIBUTES pObjAttr,
|
|
IN ULONG TitleIndex,
|
|
IN PUNICODE_STRING pUniClass OPTIONAL,
|
|
IN ULONG ulCreateOpt)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ultemp;
|
|
OBJECT_ATTRIBUTES InstObjectAttr;
|
|
UNICODE_STRING UniString;
|
|
HKEY hNewKey = NULL;
|
|
PWCHAR wcbuff = NULL;
|
|
PWCHAR pUserPath;
|
|
BOOL fMapping;
|
|
BOOL fUserReg;
|
|
PKEY_FULL_INFORMATION pDefKeyInfo = NULL;
|
|
|
|
// Get the current state of ini file mapping
|
|
fMapping = !TermsrvAppInstallMode();
|
|
|
|
// Get a buffer to hold the path of the key
|
|
ultemp = sizeof(WCHAR)*MAX_PATH*2;
|
|
pUserPath = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ultemp);
|
|
|
|
// Get the full path associated with this key
|
|
if (pUserPath) {
|
|
Status = TermsrvGetRegPath(hKey,
|
|
NULL,
|
|
pUserPath,
|
|
ultemp);
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// Get the corresponding path in the install section
|
|
Status = TermsrvGetInstallPath(pUserPath,
|
|
&wcbuff);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// Set up an object attribute structure to point to the
|
|
// path of the key in the install section
|
|
RtlInitUnicodeString(&UniString, wcbuff);
|
|
InitializeObjectAttributes(&InstObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
pObjAttr->SecurityDescriptor);
|
|
|
|
// If we're in install mode, create the key in the default
|
|
// install section
|
|
if (!fMapping) {
|
|
|
|
// Inherit the default security for the install section
|
|
InstObjectAttr.SecurityDescriptor = NULL;
|
|
|
|
Status = NtCreateKey(&hNewKey,
|
|
KEY_WRITE,
|
|
&InstObjectAttr,
|
|
TitleIndex,
|
|
pUniClass,
|
|
ulCreateOpt,
|
|
&ultemp);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
// Need to build up the path to the registry key
|
|
Status = TermsrvCreateKey(pUserPath,
|
|
wcbuff,
|
|
FALSE,
|
|
FALSE,
|
|
&hNewKey);
|
|
}
|
|
|
|
// Update the registry entry for the last time a registry
|
|
// entry was added
|
|
if (NT_SUCCESS(Status)) {
|
|
TermsrvLogRegInstallTime();
|
|
}
|
|
|
|
// The system is in execute mode, try to copy the key from the
|
|
// installation section
|
|
} else {
|
|
HANDLE hUserKey = NULL;
|
|
ULONG ulAppType = TERMSRV_COMPAT_WIN32;
|
|
|
|
// First verify that this is in the user software section
|
|
if (!GetUserSWKey(pUserPath, &fUserReg, NULL)) {
|
|
Status = STATUS_NO_MORE_FILES;
|
|
}
|
|
|
|
// If mapping is on, but disabled for this app, return
|
|
GetCtxAppCompatFlags(&ultemp, &ulAppType);
|
|
if ((ultemp & (TERMSRV_COMPAT_NOREGMAP | TERMSRV_COMPAT_WIN32)) ==
|
|
(TERMSRV_COMPAT_NOREGMAP | TERMSRV_COMPAT_WIN32)) {
|
|
Status = STATUS_NO_MORE_FILES;
|
|
}
|
|
|
|
// Check if registry mapping is disabled for this key path
|
|
GetTermsrCompatFlags(pUserPath, &ultemp, CompatibilityRegEntry);
|
|
if ((ultemp & (TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) ==
|
|
(TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) {
|
|
Status = STATUS_NO_MORE_FILES;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// Open up a key for the install section
|
|
Status = NtOpenKey(&hNewKey,
|
|
KEY_READ,
|
|
&InstObjectAttr);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// Set an attribute structure to point at the user path
|
|
RtlInitUnicodeString(&UniString, pUserPath);
|
|
InitializeObjectAttributes(&InstObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
pObjAttr->SecurityDescriptor);
|
|
|
|
// Open the user path so we can write to it
|
|
Status = NtOpenKey(&hUserKey,
|
|
KEY_WRITE,
|
|
&InstObjectAttr);
|
|
}
|
|
|
|
// Get the key info
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// Get a buffer for the key info
|
|
ultemp = sizeof(KEY_FULL_INFORMATION) +
|
|
MAX_PATH*sizeof(WCHAR);
|
|
pDefKeyInfo = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ultemp);
|
|
|
|
if (pDefKeyInfo) {
|
|
Status = NtQueryKey(hNewKey,
|
|
KeyFullInformation,
|
|
pDefKeyInfo,
|
|
ultemp,
|
|
&ultemp);
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
// Copy all of the values and subkeys for this key from the
|
|
// install section to the user section
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = TermsrvCloneKey(hNewKey,
|
|
hUserKey,
|
|
pDefKeyInfo,
|
|
TRUE);
|
|
if (pDefKeyInfo) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pDefKeyInfo);
|
|
}
|
|
}
|
|
if (hUserKey) {
|
|
NtClose(hUserKey);
|
|
}
|
|
}
|
|
if (hNewKey) {
|
|
NtClose(hNewKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pUserPath) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pUserPath);
|
|
}
|
|
|
|
if (wcbuff) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, wcbuff);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvOpenRegEntry
|
|
*
|
|
* If the system is in execute mode, copy application registry entries
|
|
* from the default user to the current user.
|
|
*
|
|
* ENTRY:
|
|
* OUT PHANDLE pUserKey:
|
|
* Pointer to return key handle if opened
|
|
* IN ACCESS_MASK DesiredAccess:
|
|
* Desired access to the key
|
|
* IN POBJECT_ATTRIBUTES ObjectAttributes:
|
|
* Object attribute structure for key to open
|
|
*
|
|
* EXIT:
|
|
* TRUE: Entry moved from install to current user
|
|
* FALSE: Entry not moved
|
|
*
|
|
****************************************************************************/
|
|
|
|
BOOL TermsrvOpenRegEntry(OUT PHANDLE pUserhKey,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES pUserObjectAttr)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ultemp=0;
|
|
ULONG ulAppType = TERMSRV_COMPAT_WIN32;
|
|
HKEY hNewKey;
|
|
WCHAR wcbuff[MAX_PATH*2];
|
|
PWCHAR pwch, pUserPath;
|
|
BOOL fUserReg;
|
|
|
|
// If running in install mode, return
|
|
if (TermsrvAppInstallMode() ) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// If mapping is on, but disabled for this app, return
|
|
GetCtxAppCompatFlags(&ultemp, &ulAppType);
|
|
if ((ultemp & (TERMSRV_COMPAT_NOREGMAP | TERMSRV_COMPAT_WIN32)) ==
|
|
(TERMSRV_COMPAT_NOREGMAP | TERMSRV_COMPAT_WIN32)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// Get a buffer to hold the user's path in the registry
|
|
ultemp = sizeof(WCHAR)*MAX_PATH*2;
|
|
pUserPath = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ultemp);
|
|
if (pUserPath) {
|
|
// Get the full path associated with this object attribute structure
|
|
Status = TermsrvGetRegPath(NULL,
|
|
pUserObjectAttr,
|
|
pUserPath,
|
|
ultemp);
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
// Create the key for this user
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = STATUS_NO_SUCH_FILE;
|
|
|
|
//DbgPrint("Attempting to open key %ws\n",pUserPath);
|
|
// Check if they are trying to open the key under HKEY_CURRENT_USER
|
|
pwch = GetUserSWKey(pUserPath, &fUserReg, NULL);
|
|
|
|
if (pwch) {
|
|
// Check if registry mapping is disabled for this key
|
|
GetTermsrCompatFlags(pUserPath, &ultemp, CompatibilityRegEntry);
|
|
if ((ultemp & (TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) !=
|
|
(TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) {
|
|
|
|
wcscpy(wcbuff, TERMSRV_INSTALL);
|
|
wcscat(wcbuff, pwch);
|
|
|
|
Status = TermsrvCreateKey(wcbuff,
|
|
pUserPath,
|
|
TRUE,
|
|
TRUE,
|
|
&hNewKey);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
NtClose(hNewKey);
|
|
}
|
|
} else {
|
|
|
|
Status = STATUS_NO_MORE_FILES;
|
|
}
|
|
|
|
// App is trying to open the key under HKEY_LOCAL_MACHINE, mask off
|
|
// the access bits they don't have by default
|
|
} else if (!_wcsnicmp(pUserPath,
|
|
TERMSRV_MACHINEREGISTRY,
|
|
wcslen(TERMSRV_MACHINEREGISTRY)) &&
|
|
(DesiredAccess &
|
|
(WRITE_DAC | WRITE_OWNER | KEY_CREATE_LINK))) {
|
|
DesiredAccess &= ~(WRITE_DAC | WRITE_OWNER | KEY_CREATE_LINK);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
Status = STATUS_NO_SUCH_FILE;
|
|
}
|
|
|
|
if (pUserPath) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pUserPath);
|
|
}
|
|
|
|
// We successfully copied the key, so actually do the open
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = NtOpenKey(pUserhKey,
|
|
DesiredAccess,
|
|
pUserObjectAttr);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvSetValueKey
|
|
*
|
|
* If the system is in install (ini mapping off) mode, set the entry in
|
|
* the citrix install user section of the registry. If the system is in
|
|
* execute (ini mapping on) mode, do nothing.
|
|
*
|
|
* ENTRY:
|
|
* HANDLE hKey: Open key to query value from
|
|
* PUNICODE_STRING ValueName: Ptr to unicode value name to set
|
|
* ULONG TitleIndex: Title Index
|
|
* ULONG Type: Type of data
|
|
* PVOID Data: Ptr to data
|
|
* ULONG DataSize: Data length
|
|
*
|
|
* EXIT:
|
|
* TRUE: Entry created in install section
|
|
* FALSE: Entry not created
|
|
*
|
|
****************************************************************************/
|
|
BOOL TermsrvSetValueKey(HANDLE hKey,
|
|
PUNICODE_STRING ValueName,
|
|
ULONG TitleIndex,
|
|
ULONG Type,
|
|
PVOID Data,
|
|
ULONG DataSize)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ultemp;
|
|
PWCHAR pwch, pUserPath;
|
|
PWCHAR wcbuff = NULL;
|
|
UNICODE_STRING UniString;
|
|
OBJECT_ATTRIBUTES InstObjectAttr;
|
|
HKEY hNewKey;
|
|
|
|
// If not in install mode, return
|
|
if ( !TermsrvAppInstallMode() ) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// Allocate a buffer to hold the path to the key in the registry
|
|
ultemp = sizeof(WCHAR)*MAX_PATH*2;
|
|
pUserPath = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ultemp);
|
|
|
|
// Get the path of this key
|
|
if (pUserPath) {
|
|
Status = TermsrvGetRegPath(hKey,
|
|
NULL,
|
|
pUserPath,
|
|
ultemp);
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// Get the path to the entry in the install section of the registry
|
|
Status = TermsrvGetInstallPath(pUserPath,
|
|
&wcbuff);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlInitUnicodeString(&UniString, wcbuff);
|
|
InitializeObjectAttributes(&InstObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Open the key in under the install section
|
|
Status = NtOpenKey(&hNewKey,
|
|
KEY_WRITE,
|
|
&InstObjectAttr);
|
|
|
|
// If we couldn't open it, try creating the key
|
|
if (!NT_SUCCESS(Status)) {
|
|
Status = TermsrvCreateKey(pUserPath,
|
|
wcbuff,
|
|
TRUE,
|
|
FALSE,
|
|
&hNewKey);
|
|
}
|
|
|
|
// If the key was opened, set the value in the install section
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = NtSetValueKey(hNewKey,
|
|
ValueName,
|
|
TitleIndex,
|
|
Type,
|
|
Data,
|
|
DataSize);
|
|
NtClose(hNewKey);
|
|
|
|
// Update the registry entry for the last time a registry
|
|
// entry was added
|
|
if (NT_SUCCESS(Status)) {
|
|
TermsrvLogRegInstallTime();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pUserPath) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pUserPath);
|
|
}
|
|
|
|
if (wcbuff) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, wcbuff);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvDeleteKey
|
|
*
|
|
* If the system is in install mode, delete the registry entry in the citrix
|
|
* install section of the registry. If the system is in execution mode,
|
|
* mark the entry in the install section as being deleted.
|
|
*
|
|
* ENTRY:
|
|
* HANDLE hKey: Handle of key in user section to delete
|
|
*
|
|
* EXIT:
|
|
* TRUE: Entry deleted in install section
|
|
* FALSE: Entry not created
|
|
*
|
|
****************************************************************************/
|
|
|
|
BOOL TermsrvDeleteKey(HANDLE hKey)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ultemp=0;
|
|
ULONG ulAppType = TERMSRV_COMPAT_WIN32;
|
|
OBJECT_ATTRIBUTES ObjectAttr;
|
|
PKEY_BASIC_INFORMATION pKeyInfo;
|
|
UNICODE_STRING UniString;
|
|
HKEY hNewKey;
|
|
PWCHAR wcbuff = NULL;
|
|
PWCHAR pwch, pUserPath;
|
|
BOOL fMapping;
|
|
|
|
|
|
// Get the current state of ini file/registry mapping, default to execute
|
|
fMapping = !TermsrvAppInstallMode();
|
|
|
|
// If mapping is on, but disabled for this app, return
|
|
if (fMapping) {
|
|
GetCtxAppCompatFlags(&ultemp, &ulAppType);
|
|
if ((ultemp & (TERMSRV_COMPAT_NOREGMAP | TERMSRV_COMPAT_WIN32)) ==
|
|
(TERMSRV_COMPAT_NOREGMAP | TERMSRV_COMPAT_WIN32)) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
// Allocate a buffer to hold the path of the key
|
|
ultemp = sizeof(WCHAR)*MAX_PATH*2;
|
|
pUserPath = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ultemp);
|
|
|
|
// Get the path to the user's key
|
|
if (pUserPath) {
|
|
Status = TermsrvGetRegPath(hKey,
|
|
NULL,
|
|
pUserPath,
|
|
ultemp);
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// Get the corresponding path in the install section
|
|
Status = TermsrvGetInstallPath(pUserPath,
|
|
&wcbuff);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlInitUnicodeString(&UniString, wcbuff);
|
|
InitializeObjectAttributes(&ObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Open the key in the install section to mark it or delete it
|
|
if (fMapping) {
|
|
Status = NtOpenKey(&hNewKey,
|
|
KEY_READ | KEY_WRITE,
|
|
&ObjectAttr);
|
|
|
|
} else {
|
|
Status = NtOpenKey(&hNewKey,
|
|
KEY_READ | KEY_WRITE | DELETE,
|
|
&ObjectAttr);
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// If in execute mode, set the copy once flag, but preserve the
|
|
// last write time of this key
|
|
if (fMapping) {
|
|
PKEY_VALUE_PARTIAL_INFORMATION pValKeyInfo;
|
|
PKEY_BASIC_INFORMATION pKeyInfo;
|
|
NTSTATUS SubStatus;
|
|
ULONG ulcbuf;
|
|
|
|
// Get a buffer
|
|
ulcbuf = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH*sizeof(WCHAR);
|
|
pKeyInfo = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ultemp);
|
|
|
|
// If we got the buffer, see if the copy once flag exists
|
|
if (pKeyInfo) {
|
|
RtlInitUnicodeString(&UniString, TERMSRV_COPYONCEFLAG);
|
|
pValKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)pKeyInfo;
|
|
SubStatus = NtQueryValueKey(hNewKey,
|
|
&UniString,
|
|
KeyValuePartialInformation,
|
|
pValKeyInfo,
|
|
ulcbuf,
|
|
&ultemp);
|
|
|
|
// If we couldn't get the value of the key, or it's not
|
|
// what it should be, reset it
|
|
if (!NT_SUCCESS(SubStatus) ||
|
|
(pValKeyInfo->Type != REG_DWORD) ||
|
|
(*pValKeyInfo->Data != 1)) {
|
|
|
|
// Get the last update time of the key
|
|
SubStatus = NtQueryKey(hNewKey,
|
|
KeyBasicInformation,
|
|
pKeyInfo,
|
|
ultemp,
|
|
&ultemp);
|
|
|
|
// Set the copy once flag
|
|
ultemp = 1;
|
|
Status = NtSetValueKey(hNewKey,
|
|
&UniString,
|
|
0,
|
|
REG_DWORD,
|
|
&ultemp,
|
|
sizeof(ultemp));
|
|
|
|
// Restore the lastwritetime of the key
|
|
if (NT_SUCCESS(SubStatus)) {
|
|
NtSetInformationKey(hNewKey,
|
|
KeyWriteTimeInformation,
|
|
&pKeyInfo->LastWriteTime,
|
|
sizeof(pKeyInfo->LastWriteTime));
|
|
}
|
|
}
|
|
|
|
// Free up our buffer
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pKeyInfo);
|
|
}
|
|
|
|
// For install mode, delete our copy of the key
|
|
} else {
|
|
Status = NtDeleteKey( hNewKey );
|
|
}
|
|
NtClose( hNewKey );
|
|
}
|
|
}
|
|
|
|
if (pUserPath) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pUserPath);
|
|
}
|
|
|
|
if (wcbuff) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, wcbuff);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvDeleteValue
|
|
*
|
|
* Delete the registry value in the citrix install user section of the
|
|
* registry.
|
|
*
|
|
* ENTRY:
|
|
* HANDLE hKey: Handle of key in install section
|
|
* PUNICODE_STRING pUniValue: Ptr to unicode value name to delete
|
|
*
|
|
* EXIT:
|
|
* TRUE: Entry deleted in install section
|
|
* FALSE: Entry not created
|
|
*
|
|
****************************************************************************/
|
|
|
|
BOOL TermsrvDeleteValue(HANDLE hKey,
|
|
PUNICODE_STRING pUniValue)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttr;
|
|
WCHAR wcUserPath[MAX_PATH*2];
|
|
PWCHAR wcInstPath = NULL;
|
|
UNICODE_STRING UniString;
|
|
HANDLE hInstKey;
|
|
|
|
// If not in install mode, return
|
|
if ( !TermsrvAppInstallMode() ) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// Get the path to the key in the user section
|
|
Status = TermsrvGetRegPath(hKey,
|
|
NULL,
|
|
wcUserPath,
|
|
sizeof(wcUserPath)/sizeof(WCHAR));
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// Get the corresponding path in the install section
|
|
Status = TermsrvGetInstallPath(wcUserPath,
|
|
&wcInstPath);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlInitUnicodeString(&UniString, wcInstPath);
|
|
InitializeObjectAttributes(&ObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Open the install path key to delete the value
|
|
Status = NtOpenKey(&hInstKey,
|
|
MAXIMUM_ALLOWED,
|
|
&ObjectAttr);
|
|
|
|
// Delete the value
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = NtDeleteValueKey(hInstKey,
|
|
pUniValue);
|
|
NtClose( hInstKey );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wcInstPath) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, wcInstPath);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvRestoreKey
|
|
*
|
|
* If the system is in installation mode and the application is trying to
|
|
* restore a key into the user or machine section of the registry, also
|
|
* restore the key into our install section.
|
|
*
|
|
* ENTRY:
|
|
* HANDLE hKey: Handle of key in user section
|
|
* HANDLE hFile: Handle of file to load in
|
|
* ULONG Flags: Restore key flags
|
|
*
|
|
* EXIT:
|
|
* TRUE: Entry created in install section
|
|
* FALSE: Entry not created
|
|
*
|
|
****************************************************************************/
|
|
|
|
BOOL TermsrvRestoreKey(IN HANDLE hKey,
|
|
IN HANDLE hFile,
|
|
IN ULONG Flags)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttr;
|
|
WCHAR wcUserPath[MAX_PATH*2];
|
|
PWCHAR wcInstPath = NULL;
|
|
UNICODE_STRING UniString;
|
|
HANDLE hInstKey;
|
|
|
|
// If not in install mode or it's
|
|
// a memory only restore, return
|
|
if ( !TermsrvAppInstallMode() ||
|
|
(Flags & REG_WHOLE_HIVE_VOLATILE)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// Get the path to the key in the user section
|
|
Status = TermsrvGetRegPath(hKey,
|
|
NULL,
|
|
wcUserPath,
|
|
sizeof(wcUserPath)/sizeof(WCHAR));
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// Get the corresponding path in the install section
|
|
Status = TermsrvGetInstallPath(wcUserPath,
|
|
&wcInstPath);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlInitUnicodeString(&UniString, wcInstPath);
|
|
InitializeObjectAttributes(&ObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Open the install path key to load the key into
|
|
Status = NtOpenKey(&hInstKey,
|
|
KEY_WRITE,
|
|
&ObjectAttr);
|
|
|
|
// If we couldn't open it, try creating the key
|
|
if (!NT_SUCCESS(Status)) {
|
|
Status = TermsrvCreateKey(wcUserPath,
|
|
wcInstPath,
|
|
TRUE,
|
|
FALSE,
|
|
&hInstKey);
|
|
}
|
|
|
|
// Restore the key into the user section
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = NtRestoreKey(hInstKey,
|
|
hFile,
|
|
Flags);
|
|
NtClose( hInstKey );
|
|
|
|
// Update the registry entry for the last time a registry
|
|
// entry was added
|
|
if (NT_SUCCESS(Status)) {
|
|
TermsrvLogRegInstallTime();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wcInstPath) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, wcInstPath);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvSetKeySecurity
|
|
*
|
|
* If the system is in installation mode and the application is trying to
|
|
* set the security of an entry in the the user or machine section of the
|
|
* registry, also set the security of the key in the install section.
|
|
*
|
|
* ENTRY:
|
|
* HANDLE hKey: Handle in user section to set security
|
|
* SECURITY_INFORMATION SecInfo: Security info struct
|
|
* PSECURITY_DESCRIPTOR pSecDesc: Ptr to security descriptor
|
|
*
|
|
* EXIT:
|
|
* TRUE: Security set in install section
|
|
* FALSE: Error
|
|
*
|
|
****************************************************************************/
|
|
|
|
BOOL TermsrvSetKeySecurity(IN HANDLE hKey,
|
|
IN SECURITY_INFORMATION SecInfo,
|
|
IN PSECURITY_DESCRIPTOR pSecDesc)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttr;
|
|
WCHAR wcUserPath[MAX_PATH*2];
|
|
PWCHAR wcInstPath = NULL;
|
|
UNICODE_STRING UniString;
|
|
HANDLE hInstKey;
|
|
|
|
// If not in install mode, return
|
|
if ( !TermsrvAppInstallMode() ) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// Get the path to the key in the user section
|
|
Status = TermsrvGetRegPath(hKey,
|
|
NULL,
|
|
wcUserPath,
|
|
sizeof(wcUserPath)/sizeof(WCHAR));
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// Get the corresponding path in the install section
|
|
Status = TermsrvGetInstallPath(wcUserPath,
|
|
&wcInstPath);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlInitUnicodeString(&UniString, wcInstPath);
|
|
InitializeObjectAttributes(&ObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Open the install path key to load the key into
|
|
Status = NtOpenKey(&hInstKey,
|
|
KEY_WRITE | WRITE_OWNER | WRITE_DAC,
|
|
&ObjectAttr);
|
|
|
|
// Set the security
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = NtSetSecurityObject(hKey,
|
|
SecInfo,
|
|
pSecDesc);
|
|
NtClose( hInstKey );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wcInstPath) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, wcInstPath);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvGetRegPath
|
|
*
|
|
* From the handle or a pointer to the object attributes, return the
|
|
* object's path in the registry.
|
|
*
|
|
* ENTRY:
|
|
* HANDLE hKey: Handle of an open key to get the path of
|
|
* POBJECT_ATTRIBUTES Ptr to open attribute structure to get the path of
|
|
* PWCHAR pInstPath Ptr to return buffer
|
|
* ULONG ulbuflen Length of return buffer
|
|
*
|
|
* NOTES:
|
|
* Either hKey or pObjectAttr must be specified, but not both.
|
|
*
|
|
* EXIT:
|
|
* NTSTATUS return code
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS TermsrvGetRegPath(IN HANDLE hKey,
|
|
IN POBJECT_ATTRIBUTES pObjectAttr,
|
|
IN PWCHAR pUserPath,
|
|
IN ULONG ulbuflen)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG ultemp;
|
|
ULONG ulWcharLength; //Keep track of the WCHAR string length
|
|
PWCHAR pwch;
|
|
PVOID pBuffer = NULL;
|
|
|
|
// Make sure only one of hKey or pObjectAttr was specified
|
|
if ((hKey && pObjectAttr) || (!hKey && !pObjectAttr)) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
// A key handle or root directory was specified, so get its path
|
|
if (hKey || (pObjectAttr && pObjectAttr->RootDirectory)) {
|
|
ultemp = sizeof(UNICODE_STRING) + sizeof(WCHAR)*MAX_PATH*2;
|
|
pBuffer = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ultemp);
|
|
|
|
// Got the buffer OK, query the path
|
|
if (pBuffer) {
|
|
// Get the path for key or root directory
|
|
Status = NtQueryObject(hKey ? hKey : pObjectAttr->RootDirectory,
|
|
ObjectNameInformation,
|
|
(PVOID)pBuffer,
|
|
ultemp,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pBuffer);
|
|
return(Status);
|
|
}
|
|
} else {
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
// Build the full path to the key to be created
|
|
pwch = ((PUNICODE_STRING)pBuffer)->Buffer;
|
|
|
|
// BBUG 417564. Bad apps close HKLM, but we might need it here.
|
|
if (!pwch) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pBuffer);
|
|
return(STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
// Make sure the string is zero terminated
|
|
ulWcharLength = ((PUNICODE_STRING)pBuffer)->Length / sizeof(WCHAR);
|
|
pwch[ulWcharLength] = 0;
|
|
|
|
// If using Object Attributes and there's room, tack on the object name
|
|
if (pObjectAttr) {
|
|
if ((((PUNICODE_STRING)pBuffer)->Length +
|
|
pObjectAttr->ObjectName->Length + sizeof(WCHAR)) < ultemp) {
|
|
wcscat(pwch, L"\\");
|
|
//Increment the length of the string
|
|
ulWcharLength += 1;
|
|
//Append the relative path to the root path (Don't use wcscat. The string may not
|
|
// be zero terminated
|
|
wcsncpy(&pwch[ulWcharLength], pObjectAttr->ObjectName->Buffer, pObjectAttr->ObjectName->Length / sizeof (WCHAR));
|
|
// Make sure the string is zero terminated
|
|
ulWcharLength += (pObjectAttr->ObjectName->Length / sizeof(WCHAR));
|
|
pwch[ulWcharLength] = 0;
|
|
} else {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// No root directory, they specified the entire path
|
|
pwch = pObjectAttr->ObjectName->Buffer;
|
|
//Make sure it is zero terminated
|
|
pwch[pObjectAttr->ObjectName->Length / sizeof(WCHAR)] = 0;
|
|
}
|
|
|
|
// Make sure the path will fit in the buffer
|
|
if ((Status == STATUS_SUCCESS) &&
|
|
(wcslen(pwch)*sizeof(WCHAR) < ulbuflen)) {
|
|
wcscpy(pUserPath, pwch);
|
|
} else {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (pBuffer) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pBuffer);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvGetInstallPath
|
|
*
|
|
* From the path to the user entry in the registry, get the path to the
|
|
* entry in the default install section.
|
|
*
|
|
* ENTRY:
|
|
* IN PWCHAR pUserPath: Ptr to path of key in user section
|
|
* IN PWCHAR *ppInstPath: Ptr to ptr to return path of key in install section
|
|
*
|
|
* NOTES:
|
|
* Caller must use RtlFreeHeap to free memory allocated for ppInstPath!
|
|
*
|
|
* EXIT:
|
|
* NTSTATUS return code
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS TermsrvGetInstallPath(IN PWCHAR pUserPath,
|
|
IN PWCHAR *ppInstPath)
|
|
{
|
|
NTSTATUS Status = STATUS_NO_SUCH_FILE;
|
|
PWCHAR pwch = NULL;
|
|
BOOL fUserReg;
|
|
BOOL bClassesKey = FALSE;
|
|
|
|
*ppInstPath = NULL;
|
|
|
|
// Check if the app is accessing the user or local machine section
|
|
pwch = GetUserSWKey(pUserPath, &fUserReg, &bClassesKey);
|
|
|
|
// Copy the path to the user's buffer
|
|
if (pwch || bClassesKey)
|
|
{
|
|
ULONG ulInstBufLen = ( wcslen(TERMSRV_INSTALL) + wcslen(SOFTWARE_PATH) + wcslen(CLASSES_PATH) + 1 )*sizeof(WCHAR);
|
|
if (pwch)
|
|
ulInstBufLen += wcslen(pwch) * sizeof(WCHAR);
|
|
|
|
*ppInstPath = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ulInstBufLen);
|
|
if(*ppInstPath) {
|
|
|
|
wcscpy(*ppInstPath, TERMSRV_INSTALL);
|
|
if (bClassesKey)
|
|
{
|
|
wcscat(*ppInstPath, SOFTWARE_PATH);
|
|
wcscat(*ppInstPath, CLASSES_PATH);
|
|
}
|
|
if (pwch)
|
|
wcscat(*ppInstPath, pwch);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvCreateKey
|
|
*
|
|
* This routine will create (or open) the specified path in the registry.
|
|
* If the path doesn't exist in the registry, it will be created.
|
|
*
|
|
* ENTRY:
|
|
* PWCHAR pSrcPath: Source path to copy keys from (optional)
|
|
* PWCHAR pDstPath: Destination path to create
|
|
* BOOL fCloneValues: TRUE means to clone all values under this key
|
|
* BOOL fCloneSubKeys: TRUE means to create all subkeys under this key
|
|
* PHANDLE phKey: Pointer for key created
|
|
*
|
|
* EXIT:
|
|
* NTSTATUS return code
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS TermsrvCreateKey(IN PWCHAR pSrcPath,
|
|
IN PWCHAR pDstPath,
|
|
BOOL fCloneValues,
|
|
BOOL fCloneSubKeys,
|
|
OUT PHANDLE phKey)
|
|
{
|
|
NTSTATUS Status;
|
|
PWCHAR pSource = NULL, pDest = NULL;
|
|
HANDLE hDstKey, hDstRoot, hSrcKey, hSrcRoot;
|
|
ULONG ultemp, NumSubKeys, ulcnt, ulbufsize, ulkey;
|
|
UNICODE_STRING UniString, UniClass;
|
|
OBJECT_ATTRIBUTES ObjectAttr;
|
|
PKEY_FULL_INFORMATION pDefKeyInfo = NULL;
|
|
ULONG aulbuf[4];
|
|
PKEY_VALUE_PARTIAL_INFORMATION pValKeyInfo =
|
|
(PKEY_VALUE_PARTIAL_INFORMATION)aulbuf;
|
|
BOOL fClassesKey = FALSE, fUserReg;
|
|
|
|
// Check if we are trying to copy values into the user's registry
|
|
pDest = GetUserSWKey(pDstPath, &fUserReg, NULL);
|
|
if (fUserReg) {
|
|
if (fCloneValues || fCloneSubKeys) {
|
|
|
|
// Skip to software section of the default install user
|
|
pSource = pSrcPath + wcslen(TERMSRV_INSTALL);
|
|
|
|
// If copying CLASSES, set Clone to FALSE so don't clone
|
|
// until we're at the CLASSES key
|
|
|
|
// Actually, this func is not called for copying classes, I belive that feature is/was
|
|
// turned off in W2K, which means that any time this func is called, we for sure are
|
|
// dealing with either HKCU\SW or HKLM, but never HKCU\SW\Clasess.
|
|
// Nov 30, 2000
|
|
//
|
|
if (pDest)
|
|
{
|
|
if (!_wcsnicmp(pDest,
|
|
TERMSRV_SOFTWARECLASSES,
|
|
wcslen(TERMSRV_SOFTWARECLASSES))) {
|
|
fClassesKey = TRUE;
|
|
fCloneValues = fCloneSubKeys = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Trying to copy to citrix install section?
|
|
} else if (!_wcsnicmp(pDstPath,
|
|
TERMSRV_INSTALL,
|
|
wcslen(TERMSRV_INSTALL))) {
|
|
|
|
// Skip to software section of the default install user
|
|
pDest = pDstPath + wcslen(TERMSRV_INSTALL);
|
|
|
|
// If copying from MACHINE\..\CLASSES, set Clone values to FALSE
|
|
// so we don't clone until we're at the CLASSES key.
|
|
if (pSrcPath && !_wcsnicmp(pSrcPath,
|
|
TERMSRV_CLASSES,
|
|
wcslen(TERMSRV_CLASSES))) {
|
|
fClassesKey = TRUE;
|
|
pSource = Ctxwcsistr(pSrcPath, SOFTWARE_PATH);
|
|
fCloneValues = fCloneSubKeys = FALSE;
|
|
}
|
|
// If we're cloning, set up the source path
|
|
else if (fCloneValues || fCloneSubKeys) {
|
|
|
|
// Is this from the user section?
|
|
pSource = GetUserSWKey(pSrcPath, &fUserReg, NULL);
|
|
|
|
// Must be from the local machine section
|
|
if (!fUserReg) {
|
|
pSource = Ctxwcsistr(pSrcPath, L"\\machine");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure the paths are valid
|
|
if (!pDest || ((fCloneValues || fCloneSubKeys) && !pSource)) {
|
|
return(STATUS_NO_SUCH_FILE);
|
|
}
|
|
|
|
// Initialize the number of subkeys to be created
|
|
NumSubKeys = 1;
|
|
|
|
while ((pDest = wcschr(pDest, L'\\')) != NULL) {
|
|
*pDest = L'\0';
|
|
pDest++;
|
|
NumSubKeys++;
|
|
}
|
|
|
|
// If we need to clone values or keys from the source path, get the
|
|
// buffers we'll need, and tokenize the source path
|
|
if (fCloneValues || fCloneSubKeys || fClassesKey) {
|
|
|
|
// Allocate a buffer for the class of the source key
|
|
ulbufsize = sizeof(KEY_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR);
|
|
pDefKeyInfo = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ulbufsize);
|
|
if (pDefKeyInfo) {
|
|
while ((pSource = wcschr(pSource, L'\\')) != NULL) {
|
|
*pSource = L'\0';
|
|
pSource++;
|
|
}
|
|
pSource = pSrcPath;
|
|
} else {
|
|
fCloneValues = fCloneSubKeys = fClassesKey = FALSE;
|
|
}
|
|
}
|
|
|
|
hSrcRoot = hDstRoot = NULL;
|
|
pDest = pDstPath;
|
|
|
|
// Go through each key in the path, creating it if it doesn't exist
|
|
for (ulcnt = 0; ulcnt < NumSubKeys; ulcnt++) {
|
|
|
|
if ((*pDest == L'\0') &&
|
|
(ulcnt != NumSubKeys - 1)) {
|
|
pDest++;
|
|
pSource++;
|
|
continue;
|
|
}
|
|
// If we're at CLASSES key, we need to clone the whole key
|
|
else if (fClassesKey && !_wcsicmp(pDest, L"classes")) {
|
|
fCloneValues = fCloneSubKeys = TRUE;
|
|
}
|
|
|
|
// If we're copying values from the source, open up the source so we
|
|
// can get the values and subkeys
|
|
// Also need to check for ClassesKey cause we'll be cloning later and
|
|
// we need some setup done
|
|
if (fCloneValues || fCloneSubKeys || fClassesKey) {
|
|
|
|
// Set up the attribute structure for the source path
|
|
RtlInitUnicodeString(&UniString, pSource);
|
|
InitializeObjectAttributes(&ObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hSrcRoot,
|
|
NULL);
|
|
|
|
// Open the source key
|
|
Status = NtOpenKey(&hSrcKey,
|
|
KEY_READ,
|
|
&ObjectAttr);
|
|
|
|
// Get the source key info and value
|
|
if (NT_SUCCESS(Status)) {
|
|
// Close the source root, if necessary
|
|
if (hSrcRoot) {
|
|
NtClose(hSrcRoot);
|
|
}
|
|
hSrcRoot = hSrcKey;
|
|
|
|
Status = NtQueryKey(hSrcKey,
|
|
KeyFullInformation,
|
|
pDefKeyInfo,
|
|
ulbufsize,
|
|
&ultemp);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlInitUnicodeString(&UniString, TERMSRV_COPYONCEFLAG);
|
|
Status = NtQueryValueKey(hSrcKey,
|
|
&UniString,
|
|
KeyValuePartialInformation,
|
|
pValKeyInfo,
|
|
sizeof(aulbuf),
|
|
&ultemp);
|
|
if (NT_SUCCESS(Status) && (pValKeyInfo->Data)) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
// Setup the unicode string for the class
|
|
if ( pDefKeyInfo->ClassLength ) {
|
|
pDefKeyInfo->Class[pDefKeyInfo->ClassLength/sizeof(WCHAR)] = UNICODE_NULL;
|
|
RtlInitUnicodeString(&UniClass, pDefKeyInfo->Class );
|
|
} else
|
|
RtlInitUnicodeString(&UniClass, NULL);
|
|
|
|
// Advance to the next key
|
|
pSource += wcslen( pSource ) + 1;
|
|
} else {
|
|
// Set the class to NULL
|
|
RtlInitUnicodeString(&UniClass, NULL);
|
|
}
|
|
|
|
// Set up the attribute structure for the destination
|
|
RtlInitUnicodeString(&UniString, pDest);
|
|
InitializeObjectAttributes(&ObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hDstRoot,
|
|
NULL);
|
|
|
|
// Open/Create the destination key
|
|
Status = NtCreateKey(&hDstKey,
|
|
MAXIMUM_ALLOWED,
|
|
&ObjectAttr,
|
|
0,
|
|
&UniClass,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&ultemp);
|
|
|
|
// If the key was created (NOT opened) copy the values and subkeys
|
|
if (NT_SUCCESS(Status) &&
|
|
((ultemp == REG_CREATED_NEW_KEY) &&
|
|
(fCloneSubKeys || fCloneValues))) {
|
|
Status = TermsrvCloneKey(hSrcKey,
|
|
hDstKey,
|
|
pDefKeyInfo,
|
|
fCloneSubKeys);
|
|
}
|
|
|
|
// Close the intermediate key.
|
|
if( hDstRoot != NULL ) {
|
|
NtClose( hDstRoot );
|
|
}
|
|
|
|
// Initialize the next object directory (i.e. parent key)
|
|
hDstRoot = hDstKey;
|
|
|
|
// If creating the key failed, break out of the loop
|
|
if( !NT_SUCCESS( Status )) {
|
|
break;
|
|
}
|
|
|
|
pDest += wcslen( pDest ) + 1;
|
|
}
|
|
|
|
// Close the source root, if necessary
|
|
if (hSrcRoot) {
|
|
NtClose(hSrcRoot);
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) && hDstRoot) {
|
|
NtClose(hDstRoot);
|
|
hDstKey = NULL;
|
|
}
|
|
|
|
if (pDefKeyInfo) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pDefKeyInfo);
|
|
}
|
|
|
|
*phKey = hDstKey;
|
|
return(Status);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvCloneKey
|
|
*
|
|
* This routine will recursively create (or open) the specified path in the
|
|
* registry. If the path doesn't exist in the registry, it will be created.
|
|
*
|
|
* ENTRY:
|
|
* HANDLE hSrcKey: Handle for source key
|
|
* HANDLE hDstKey: Handle for destination key
|
|
* PKEY_FULL_INFORMATION pDefKeyInfo: Ptr to key info structure of source
|
|
* BOOL fCreateSubKeys: TRUE means to recursively clone subkeys
|
|
*
|
|
* EXIT:
|
|
* NTSTATUS return code
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS TermsrvCloneKey(HANDLE hSrcKey,
|
|
HANDLE hDstKey,
|
|
PKEY_FULL_INFORMATION pDefKeyInfo,
|
|
BOOL fCreateSubKeys)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG ulbufsize, ultemp, ulkey, ulcursize;
|
|
UNICODE_STRING UniString, UniClass;
|
|
OBJECT_ATTRIBUTES ObjectAttr;
|
|
PKEY_NODE_INFORMATION pKeyNodeInfo;
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValInfo;
|
|
PKEY_VALUE_BASIC_INFORMATION pKeyCurInfo;
|
|
PKEY_FULL_INFORMATION pKeyNewInfo;
|
|
HANDLE hNewDst, hNewSrc;
|
|
SECURITY_DESCRIPTOR SecDesc;
|
|
|
|
#ifdef CLONE_SECURITY
|
|
// Get the security access for the source key
|
|
Status = NtQuerySecurityObject(hSrcKey,
|
|
OWNER_SECURITY_INFORMATION |
|
|
GROUP_SECURITY_INFORMATION |
|
|
DACL_SECURITY_INFORMATION |
|
|
SACL_SECURITY_INFORMATION,
|
|
&SecDesc,
|
|
sizeof(SecDesc),
|
|
&ultemp);
|
|
|
|
// Set the security access for the destination key
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = NtSetSecurityObject(hDstKey,
|
|
OWNER_SECURITY_INFORMATION |
|
|
GROUP_SECURITY_INFORMATION |
|
|
DACL_SECURITY_INFORMATION |
|
|
SACL_SECURITY_INFORMATION,
|
|
&SecDesc);
|
|
}
|
|
#endif
|
|
|
|
// Create the values for this key
|
|
if (pDefKeyInfo->Values) {
|
|
|
|
ulbufsize = sizeof(KEY_VALUE_FULL_INFORMATION) +
|
|
(pDefKeyInfo->MaxValueNameLen + 1)*sizeof(WCHAR) +
|
|
pDefKeyInfo->MaxValueDataLen;
|
|
|
|
pKeyValInfo = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ulbufsize);
|
|
|
|
// Get a buffer to hold current value of the key (for existance check)
|
|
ulcursize = sizeof(KEY_VALUE_BASIC_INFORMATION) +
|
|
(pDefKeyInfo->MaxNameLen + 1)*sizeof(WCHAR);
|
|
|
|
pKeyCurInfo = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ulcursize);
|
|
|
|
if (pKeyValInfo && pKeyCurInfo) {
|
|
for (ulkey = 0; ulkey < pDefKeyInfo->Values; ulkey++) {
|
|
Status = NtEnumerateValueKey(hSrcKey,
|
|
ulkey,
|
|
KeyValueFullInformation,
|
|
pKeyValInfo,
|
|
ulbufsize,
|
|
&ultemp);
|
|
|
|
// If successful and this isn't our "copy once" flag, copy
|
|
// the value to the user's keys
|
|
if (NT_SUCCESS(Status) &&
|
|
(wcsncmp(pKeyValInfo->Name, TERMSRV_COPYONCEFLAG,
|
|
sizeof(TERMSRV_COPYONCEFLAG)/sizeof(WCHAR)-1))) {
|
|
UniString.Buffer = pKeyValInfo->Name;
|
|
UniString.Length = (USHORT)pKeyValInfo->NameLength;
|
|
UniString.MaximumLength = UniString.Length + 2;
|
|
|
|
// Check if the value exists
|
|
Status = NtQueryValueKey(hDstKey,
|
|
&UniString,
|
|
KeyValueBasicInformation,
|
|
pKeyCurInfo,
|
|
ulcursize,
|
|
&ultemp);
|
|
|
|
// Value doesn't exist, go ahead and create it
|
|
if (!NT_SUCCESS(Status)) {
|
|
Status = NtSetValueKey(hDstKey,
|
|
&UniString,
|
|
0,
|
|
pKeyValInfo->Type,
|
|
(PCHAR)pKeyValInfo +
|
|
pKeyValInfo->DataOffset,
|
|
pKeyValInfo->DataLength);
|
|
}
|
|
}
|
|
}
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pKeyValInfo);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pKeyCurInfo);
|
|
} else {
|
|
if (pKeyValInfo) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pKeyValInfo);
|
|
}
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
// If requested, create all of the child keys
|
|
if (fCreateSubKeys && pDefKeyInfo->SubKeys) {
|
|
|
|
// Allocate a buffer to get name and class of key to create
|
|
ulbufsize = sizeof(KEY_NODE_INFORMATION) + 2*MAX_PATH*sizeof(WCHAR);
|
|
pKeyNodeInfo = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ulbufsize);
|
|
|
|
// Allocate a buffer for subkey info
|
|
ulbufsize = sizeof(KEY_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR);
|
|
pKeyNewInfo = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ulbufsize);
|
|
|
|
if (pKeyNodeInfo && pKeyNewInfo) {
|
|
for (ulkey = 0; ulkey < pDefKeyInfo->SubKeys; ulkey++) {
|
|
Status = NtEnumerateKey(hSrcKey,
|
|
ulkey,
|
|
KeyNodeInformation,
|
|
pKeyNodeInfo,
|
|
ulbufsize,
|
|
&ultemp);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// Init the unicode string for the class
|
|
UniClass.Buffer = (PWCHAR)((PCHAR)pKeyNodeInfo +
|
|
pKeyNodeInfo->ClassOffset);
|
|
UniClass.Length = (USHORT)pKeyNodeInfo->ClassLength;
|
|
UniClass.MaximumLength = UniString.Length + 2;
|
|
|
|
// Init the unicode string for the name
|
|
UniString.Buffer = pKeyNodeInfo->Name;
|
|
UniString.Length = (USHORT)pKeyNodeInfo->NameLength;
|
|
UniString.MaximumLength = UniString.Length + 2;
|
|
|
|
InitializeObjectAttributes(&ObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hDstKey,
|
|
NULL);
|
|
|
|
Status = NtCreateKey(&hNewDst,
|
|
MAXIMUM_ALLOWED,
|
|
&ObjectAttr,
|
|
0,
|
|
&UniClass,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&ultemp);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
InitializeObjectAttributes(&ObjectAttr,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hSrcKey,
|
|
NULL);
|
|
|
|
Status = NtOpenKey(&hNewSrc,
|
|
KEY_READ,
|
|
&ObjectAttr);
|
|
|
|
// Get the key info
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = NtQueryKey(hNewSrc,
|
|
KeyFullInformation,
|
|
pKeyNewInfo,
|
|
ulbufsize,
|
|
&ultemp);
|
|
|
|
if (NT_SUCCESS(Status) &&
|
|
(pKeyNewInfo->SubKeys ||
|
|
pKeyNewInfo->Values)) {
|
|
Status = TermsrvCloneKey(hNewSrc,
|
|
hNewDst,
|
|
pKeyNewInfo,
|
|
TRUE);
|
|
}
|
|
NtClose(hNewSrc);
|
|
}
|
|
NtClose(hNewDst);
|
|
}
|
|
}
|
|
}
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pKeyNodeInfo);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pKeyNewInfo);
|
|
} else {
|
|
if (pKeyNodeInfo) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pKeyNodeInfo);
|
|
}
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Ctxwcsistr
|
|
*
|
|
* This is a case insensitive version of wcsstr.
|
|
*
|
|
* ENTRY:
|
|
* PWCHAR pstring1 (In) - String to search in
|
|
* PWCHAR pstring2 (In) - String to search for
|
|
*
|
|
* EXIT:
|
|
* Success:
|
|
* pointer to substring
|
|
* Failure:
|
|
* NULL
|
|
*
|
|
****************************************************************************/
|
|
|
|
PWCHAR
|
|
Ctxwcsistr(PWCHAR pstring1,
|
|
PWCHAR pstring2)
|
|
{
|
|
PWCHAR pch, ps1, ps2;
|
|
|
|
pch = pstring1;
|
|
|
|
while (*pch)
|
|
{
|
|
ps1 = pch;
|
|
ps2 = pstring2;
|
|
|
|
while (*ps1 && *ps2 && !(towupper(*ps1) - towupper(*ps2))) {
|
|
ps1++;
|
|
ps2++;
|
|
}
|
|
|
|
if (!*ps2) {
|
|
return(pch);
|
|
}
|
|
|
|
pch++;
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* IsSystemLUID
|
|
*
|
|
* This routines checks if we are running under the system context, and if
|
|
* so returns false. We want all of the registry mapping support disable
|
|
* for system services.
|
|
*
|
|
* Note we don't check the thread's token, so impersonation doesn't work.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* EXIT:
|
|
* TRUE:
|
|
* Called from within system context
|
|
* FALSE:
|
|
* Regular context
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define SIZE_OF_STATISTICS_TOKEN_INFORMATION \
|
|
sizeof( TOKEN_STATISTICS )
|
|
|
|
BOOL IsSystemLUID(VOID)
|
|
{
|
|
HANDLE TokenHandle;
|
|
UCHAR TokenInformation[ SIZE_OF_STATISTICS_TOKEN_INFORMATION ];
|
|
ULONG ReturnLength;
|
|
static LUID CurrentLUID = { 0, 0 };
|
|
LUID SystemLUID = SYSTEM_LUID;
|
|
|
|
if ( CurrentLUID.LowPart == 0 && CurrentLUID.HighPart == 0 ) {
|
|
if ( !OpenProcessToken( GetCurrentProcess(),
|
|
TOKEN_READ,
|
|
&TokenHandle ))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
if ( !GetTokenInformation( TokenHandle,
|
|
TokenStatistics,
|
|
TokenInformation,
|
|
sizeof( TokenInformation ),
|
|
&ReturnLength ))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
CloseHandle( TokenHandle );
|
|
|
|
RtlCopyLuid(&CurrentLUID,
|
|
&(((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId));
|
|
}
|
|
|
|
if (RtlEqualLuid(&CurrentLUID, &SystemLUID)) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvLogRegInstallTime
|
|
*
|
|
* This routines updates the LatestRegistryKey value in the registry to
|
|
* contain the current time.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* EXIT:
|
|
* No return value
|
|
*
|
|
****************************************************************************/
|
|
|
|
void TermsrvLogRegInstallTime()
|
|
{
|
|
UNICODE_STRING UniString;
|
|
HANDLE hKey;
|
|
FILETIME FileTime;
|
|
ULONG ultmp;
|
|
NTSTATUS Status;
|
|
WCHAR wcbuff[MAX_PATH];
|
|
|
|
// Open up the registry key to store the last write time of the file
|
|
wcscpy(wcbuff, TERMSRV_INIFILE_TIMES);
|
|
|
|
// Create or open the path to the IniFile Times key
|
|
Status = TermsrvCreateKey(NULL,
|
|
wcbuff,
|
|
FALSE,
|
|
FALSE,
|
|
&hKey);
|
|
|
|
// Opened up the registry key, now set the value to the current time
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
GetSystemTimeAsFileTime(&FileTime);
|
|
RtlTimeToSecondsSince1970((PLARGE_INTEGER)&FileTime,
|
|
&ultmp);
|
|
|
|
RtlInitUnicodeString(&UniString,
|
|
INIFILE_TIMES_LATESTREGISTRYKEY);
|
|
|
|
// Now store it under the citrix key in the registry
|
|
Status = NtSetValueKey(hKey,
|
|
&UniString,
|
|
0,
|
|
REG_DWORD,
|
|
&ultmp,
|
|
sizeof(ultmp));
|
|
// Close the registry key
|
|
NtClose(hKey);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvOpenUserClasses
|
|
*
|
|
* If the system is in execute mode, open \SOFTWARE\CLASSES key under
|
|
* HKEY_CURRENT_USER. If CLASSES doesen't exist under TERMSRV\INSTALL
|
|
* or HKEY_CURRENT_USER, copy it from \MACHINE\SOFTWARE\CLASSES.
|
|
*
|
|
* ENTRY:
|
|
* IN ACCESS_MASK DesiredAccess:
|
|
* Desired access to the key
|
|
* OUT PHANDLE phKey:
|
|
* Pointer to return key handle if opened
|
|
*
|
|
* EXIT:
|
|
* NT_STATUS return code
|
|
*
|
|
****************************************************************************/
|
|
|
|
BOOL TermsrvOpenUserClasses(IN ACCESS_MASK DesiredAccess,
|
|
OUT PHANDLE pUserhKey)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ultemp;
|
|
ULONG ulAppType = TERMSRV_COMPAT_WIN32;
|
|
HKEY hDstKey;
|
|
WCHAR wcbuff[MAX_PATH],wcClassbuff[TERMSRV_CLASSES_SIZE];
|
|
PWCHAR pUserPath;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING UniString;
|
|
|
|
// set return handle to 0 cause OpenClassesRoot checks it
|
|
*pUserhKey = 0;
|
|
|
|
|
|
//Disable it for now
|
|
return(STATUS_NO_SUCH_FILE);
|
|
|
|
|
|
// If called under a system service or in install mode, return
|
|
if ( IsSystemLUID() || TermsrvAppInstallMode() ) {
|
|
return(STATUS_NO_SUCH_FILE);
|
|
}
|
|
|
|
// If mapping is on, but disabled for CLASSES, return
|
|
GetTermsrCompatFlags(TERMSRV_CLASSES, &ultemp, CompatibilityRegEntry);
|
|
if ((ultemp & (TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) ==
|
|
(TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) {
|
|
return(STATUS_NO_SUCH_FILE);
|
|
}
|
|
|
|
|
|
// Open MACHINE\SOFTWARE\CLASSES key
|
|
RtlInitUnicodeString(&UniString, TERMSRV_CLASSES);
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenKey(&hDstKey,
|
|
KEY_READ,
|
|
&Obja);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(STATUS_NO_SUCH_FILE);
|
|
}
|
|
|
|
NtClose(hDstKey);
|
|
|
|
// Need to put this in buffer cause CtxCreateKey modifies the string.
|
|
wcscpy(wcClassbuff, TERMSRV_CLASSES);
|
|
|
|
// Try to open TERMSRV\INSTALL\SOFTWARE\CLASSES; if it doesn't exist,
|
|
// clone it from MACHINE\SOFTWARE\CLASSES.
|
|
wcscpy(wcbuff, TERMSRV_INSTALLCLASSES);
|
|
Status = TermsrvCreateKey(wcClassbuff,
|
|
wcbuff,
|
|
TRUE,
|
|
TRUE,
|
|
&hDstKey);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
NtClose(hDstKey);
|
|
}
|
|
|
|
// Try to open HKEY_CURRENT_USER\SOFTWARE\CLASSES; if it doesn't exist,
|
|
// clone it from TERMSRV\INSTALL\SOFTWARE\CLASSES.
|
|
Status = RtlOpenCurrentUser( DesiredAccess, pUserhKey );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
ultemp = sizeof(WCHAR)*MAX_PATH;
|
|
Status = TermsrvGetRegPath(*pUserhKey,
|
|
NULL,
|
|
(PWCHAR)&wcbuff,
|
|
ultemp);
|
|
NtClose(*pUserhKey);
|
|
if (NT_SUCCESS(Status)) {
|
|
wcscat(wcbuff, TERMSRV_SOFTWARECLASSES);
|
|
wcscpy(wcClassbuff, TERMSRV_INSTALLCLASSES);
|
|
Status = TermsrvCreateKey(wcClassbuff,
|
|
wcbuff,
|
|
TRUE,
|
|
TRUE,
|
|
pUserhKey);
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvGetPreSetValue
|
|
*
|
|
* Get any preset value during install.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* IN HANDLE hKey: Key user wants to set
|
|
* IN PUNICODE_STRING pValueName: Value name user wants to set
|
|
* IN ULONG Type: Type of value
|
|
* OUT PVOID *Data: Pre-set data
|
|
* OUT PULONG DataSize: Size of preset data
|
|
*
|
|
* NOTES:
|
|
*
|
|
* EXIT:
|
|
* NTSTATUS return code
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS TermsrvGetPreSetValue( IN HANDLE hKey,
|
|
IN PUNICODE_STRING pValueName,
|
|
IN ULONG Type,
|
|
OUT PVOID *Data
|
|
)
|
|
{
|
|
|
|
#define DEFAULT_VALUE_SIZE 128
|
|
|
|
NTSTATUS Status = STATUS_NO_SUCH_FILE;
|
|
PWCHAR pwch = NULL;
|
|
WCHAR pUserPath[MAX_PATH];
|
|
WCHAR ValuePath[2 * MAX_PATH];
|
|
ULONG ultemp;
|
|
UNICODE_STRING UniString;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
ULONG BufferLength;
|
|
PVOID KeyValueInformation;
|
|
ULONG ResultLength;
|
|
HANDLE hValueKey;
|
|
BOOL fUserReg;
|
|
|
|
// If running in execute mode, return
|
|
if ( !TermsrvAppInstallMode() ) {
|
|
return(STATUS_NO_SUCH_FILE);
|
|
}
|
|
|
|
ultemp = sizeof(WCHAR)*MAX_PATH;
|
|
|
|
// Get the path of this key
|
|
Status = TermsrvGetRegPath(hKey,
|
|
NULL,
|
|
pUserPath,
|
|
ultemp);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
// Check if the app is accessing the local machine section
|
|
// or the user section.
|
|
|
|
pwch = GetUserSWKey(pUserPath, &fUserReg, NULL);
|
|
if (!fUserReg) {
|
|
if (!_wcsnicmp(pUserPath,
|
|
TERMSRV_VALUE,
|
|
wcslen(TERMSRV_VALUE))) {
|
|
Status = STATUS_NO_SUCH_FILE;
|
|
return Status;
|
|
} else if (!_wcsnicmp(pUserPath,
|
|
TERMSRV_MACHINEREGISTRY,
|
|
wcslen(TERMSRV_MACHINEREGISTRY))) {
|
|
pwch = Ctxwcsistr(pUserPath, L"\\machine");
|
|
} else {
|
|
Status = STATUS_NO_SUCH_FILE;
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if ( pwch == NULL )
|
|
{
|
|
Status = STATUS_NO_SUCH_FILE;
|
|
return Status;
|
|
}
|
|
|
|
// Get the path to the preset value section
|
|
|
|
wcscpy(ValuePath, TERMSRV_VALUE);
|
|
wcscat(ValuePath, pwch);
|
|
|
|
// Open Value key
|
|
RtlInitUnicodeString(&UniString, ValuePath);
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&UniString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenKey(&hValueKey,
|
|
KEY_READ,
|
|
&Obja);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(STATUS_NO_SUCH_FILE);
|
|
}
|
|
|
|
// Allocate space for the "new" value
|
|
|
|
BufferLength = DEFAULT_VALUE_SIZE + sizeof( KEY_VALUE_PARTIAL_INFORMATION );
|
|
|
|
KeyValueInformation = RtlAllocateHeap( RtlProcessHeap( ), 0, BufferLength );
|
|
if ( !KeyValueInformation ) {
|
|
NtClose(hValueKey);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Status = NtQueryValueKey( hValueKey,
|
|
pValueName,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
BufferLength,
|
|
&ResultLength
|
|
);
|
|
|
|
// If we didn't allocate enough space, try again
|
|
|
|
if ( Status == STATUS_BUFFER_OVERFLOW ) {
|
|
|
|
RtlFreeHeap(RtlProcessHeap(), 0, KeyValueInformation);
|
|
|
|
BufferLength = ResultLength;
|
|
|
|
KeyValueInformation = RtlAllocateHeap( RtlProcessHeap( ), 0,
|
|
BufferLength
|
|
);
|
|
if ( !KeyValueInformation ) {
|
|
NtClose(hValueKey);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// This one should succeed
|
|
//
|
|
|
|
Status = NtQueryValueKey( hValueKey,
|
|
pValueName,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
BufferLength,
|
|
&ResultLength
|
|
);
|
|
}
|
|
|
|
NtClose(hValueKey);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
Status = STATUS_NO_SUCH_FILE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Make sure the types match.
|
|
// If they do, return the new value.
|
|
//
|
|
|
|
if ( Type == (( PKEY_VALUE_PARTIAL_INFORMATION )
|
|
KeyValueInformation )->Type )
|
|
*Data = KeyValueInformation;
|
|
else
|
|
Status = STATUS_NO_SUCH_FILE;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* IsUserSWPath
|
|
*
|
|
* Determine if user is accessing registry key user \registry\user\xxx\software
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* IN PWCHAR pPath: Registry path to check
|
|
* OUT PBOOL pUserReg: If this key is under registry\user
|
|
*
|
|
* NOTES:
|
|
*
|
|
* EXIT:
|
|
* Returns: pointer to \software key of user registry (or NULL if not
|
|
* user software key)
|
|
* pUserReg: TRUE if registry path is HKCU (\registry\user)
|
|
*
|
|
****************************************************************************/
|
|
PWCHAR GetUserSWKey(PWCHAR pPath, PBOOL pUserReg, PBOOL bClassesKey)
|
|
{
|
|
PWCHAR pwch = NULL;
|
|
PWCHAR pwchClassesTest = NULL;
|
|
PWCHAR pwClassesKey = NULL;
|
|
ULONG ultemp = 0;
|
|
|
|
if (pUserReg)
|
|
*pUserReg = FALSE;
|
|
|
|
if (bClassesKey)
|
|
*bClassesKey = FALSE;
|
|
|
|
if (!pPath)
|
|
return NULL;
|
|
|
|
if (!_wcsnicmp(pPath,
|
|
TERMSRV_USERREGISTRY,
|
|
sizeof(TERMSRV_USERREGISTRY)/sizeof(WCHAR) - 1))
|
|
{
|
|
if (pUserReg)
|
|
*pUserReg = TRUE;
|
|
|
|
// Skip over first part of path + backslash
|
|
pwch = pPath + (sizeof(TERMSRV_USERREGISTRY)/sizeof(WCHAR));
|
|
|
|
if (pwch)
|
|
{
|
|
//First test for classes
|
|
if (wcschr(pwch, L'\\'))
|
|
pwchClassesTest = wcschr(pwch, L'\\') - sizeof(CLASSES_SUBSTRING)/sizeof(WCHAR) + 1;
|
|
else
|
|
pwchClassesTest = pwch + wcslen(pwch) - sizeof(CLASSES_SUBSTRING)/sizeof(WCHAR) + 1;
|
|
if (pwchClassesTest)
|
|
{
|
|
if (!_wcsnicmp(pwchClassesTest, CLASSES_SUBSTRING, sizeof(CLASSES_SUBSTRING)/sizeof(WCHAR) - 1))
|
|
{
|
|
ultemp = sizeof(SOFTWARE_PATH) + sizeof(CLASSES_PATH) + (wcslen(pwch) + 1) * sizeof(WCHAR);
|
|
pwClassesKey = RtlAllocateHeap(RtlProcessHeap(), 0, ultemp);
|
|
|
|
// Depending on the result of this function the calling routine either sets the status
|
|
// to STATUS_NO_MORE_FILES or just return FALSE. So, if we return NULL here, we are fine.
|
|
if (!pwClassesKey)
|
|
return NULL;
|
|
|
|
wcscpy(pwClassesKey, SOFTWARE_PATH);
|
|
wcscat(pwClassesKey, CLASSES_PATH);
|
|
|
|
// Skip over user sid
|
|
pwch = wcschr(pwch, L'\\');
|
|
if (pwch)
|
|
wcscat(pwClassesKey, pwch);
|
|
|
|
if (RegPathExistsInOmissionList(pwClassesKey))
|
|
pwch = NULL;
|
|
else
|
|
{
|
|
if (bClassesKey)
|
|
*bClassesKey = TRUE;
|
|
}
|
|
|
|
if (pwClassesKey)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pwClassesKey);
|
|
|
|
return (pwch);
|
|
}
|
|
}
|
|
|
|
// Skip over user sid
|
|
pwch = wcschr(pwch, L'\\');
|
|
if (pwch)
|
|
{
|
|
if (_wcsnicmp(pwch, SOFTWARE_PATH, sizeof(SOFTWARE_PATH)/sizeof(WCHAR) - 1))
|
|
return NULL;
|
|
|
|
if (RegPathExistsInOmissionList(pwch))
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(pwch);
|
|
}
|
|
|
|
|
|
void DeleteKeyAndSubkeys(IN HKEY hkey, IN LPCWSTR pwszSubKey)
|
|
{
|
|
HKEY hkSubKey = NULL;
|
|
|
|
// Open the subkey so we can enumerate any children
|
|
if (RegOpenKeyEx(hkey, pwszSubKey, 0, MAXIMUM_ALLOWED, &hkSubKey) == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwIndex = 0;
|
|
WCHAR szSubKeyName[MAX_PATH + 1];
|
|
|
|
// I can't just call RegEnumKey with an ever-increasing index, because
|
|
// I'm deleting the subkeys as I go, which alters the indices of the
|
|
// remaining subkeys in an implementation-dependent way. In order to
|
|
// be safe, I have to count backwards while deleting the subkeys.
|
|
|
|
// Find out how many subkeys there are
|
|
if (RegQueryInfoKey(hkSubKey, NULL, NULL, NULL,
|
|
&dwIndex, // The # of subkeys -- all we need
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL) == NO_ERROR)
|
|
{
|
|
// dwIndex is now the count of subkeys, but it needs to be zero-based
|
|
//for RegEnumKey, so I'll pre-decrement, rather than post-decrement.
|
|
while (ERROR_SUCCESS == RegEnumKey(hkSubKey, --dwIndex, szSubKeyName, MAX_PATH))
|
|
DeleteKeyAndSubkeys(hkSubKey, szSubKeyName);
|
|
}
|
|
|
|
RegCloseKey(hkSubKey);
|
|
|
|
if (pwszSubKey)
|
|
RegDeleteKey(hkey, pwszSubKey);
|
|
else
|
|
{
|
|
// we want to delete all the values by hand
|
|
DWORD cchSubKeyName = MAX_PATH;
|
|
while (ERROR_SUCCESS == RegEnumValue(hkey, 0, szSubKeyName, &cchSubKeyName, NULL, NULL, NULL, NULL))
|
|
{
|
|
// avoid looping infinitely when we cant delete the value
|
|
if (RegDeleteValue(hkey, szSubKeyName))
|
|
break;
|
|
|
|
//Reinitialize this since cchSubKeyName is an IN/OUT parameter
|
|
cchSubKeyName = MAX_PATH;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* TermsrvRemoveClassesKey
|
|
*
|
|
* Delete the classes key for the current user and then set a
|
|
* registry flag indicating that this has been done (we only
|
|
* want this to be done the first time the user logs in).
|
|
*
|
|
* ENTRY: None
|
|
*
|
|
*
|
|
*
|
|
* EXIT: True if the classes key was deleted (even if it was empty
|
|
* or didn't exist) and false otherwise
|
|
*
|
|
*
|
|
****************************************************************************/
|
|
|
|
BOOL TermsrvRemoveClassesKey(LPTSTR sSid)
|
|
{
|
|
BOOL bDeletionPerformed = FALSE;
|
|
|
|
HKEY hPerformed;
|
|
HKEY hDeletionFlag;
|
|
|
|
ULONG ulFlagPathLen = 0;
|
|
PWCHAR pFlagPath = NULL;
|
|
|
|
ULONG ulClassesPathLen = 0;
|
|
PWCHAR pClassesPath = NULL;
|
|
|
|
if (!sSid)
|
|
return FALSE;
|
|
|
|
ulFlagPathLen = (wcslen(sSid) + wcslen(TERMSRV_APP_PATH) + wcslen(CLASSES_DELETED) + 1) * sizeof(WCHAR);
|
|
pFlagPath = RtlAllocateHeap(RtlProcessHeap(), 0, ulFlagPathLen);
|
|
if (pFlagPath)
|
|
{
|
|
wcscpy(pFlagPath, sSid);
|
|
wcscat(pFlagPath, TERMSRV_APP_PATH);
|
|
wcscat(pFlagPath, CLASSES_DELETED);
|
|
|
|
//Make sure the operation hasn't already been performed for this user
|
|
if (RegOpenKeyEx(HKEY_USERS, pFlagPath, 0, KEY_READ, &hPerformed) == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
//It hasn't, so delete the software\classes key
|
|
ulClassesPathLen = (wcslen(TERMSRV_APP_PATH) + wcslen(SOFTWARE_PATH) + wcslen(CLASSES_PATH) + 1) * sizeof(WCHAR);
|
|
pClassesPath = RtlAllocateHeap(RtlProcessHeap(), 0, ulClassesPathLen);
|
|
if (pClassesPath)
|
|
{
|
|
wcscpy(pClassesPath, sSid);
|
|
wcscat(pClassesPath, SOFTWARE_PATH);
|
|
wcscat(pClassesPath, CLASSES_PATH);
|
|
|
|
DeleteKeyAndSubkeys(HKEY_USERS, pClassesPath);
|
|
|
|
RegCreateKeyEx(HKEY_USERS, pFlagPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hDeletionFlag, NULL);
|
|
RegCloseKey(hDeletionFlag);
|
|
|
|
bDeletionPerformed = TRUE;
|
|
}
|
|
|
|
if (pClassesPath)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pClassesPath);
|
|
}
|
|
else
|
|
RegCloseKey(hPerformed);
|
|
}
|
|
|
|
if (pFlagPath)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pFlagPath);
|
|
|
|
return bDeletionPerformed;
|
|
}
|