527 lines
19 KiB
C
527 lines
19 KiB
C
|
|
||
|
/*************************************************************************
|
||
|
*
|
||
|
* registry.c
|
||
|
*
|
||
|
* WinStation Registry Routines
|
||
|
*
|
||
|
* Copyright Microsoft Corporation, 1998
|
||
|
*
|
||
|
*
|
||
|
*************************************************************************/
|
||
|
|
||
|
/*
|
||
|
* Includes
|
||
|
*/
|
||
|
#include "precomp.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
|
||
|
/*=============================================================================
|
||
|
== Public functions
|
||
|
=============================================================================*/
|
||
|
|
||
|
NTSTATUS WinStationReadRegistryWorker( VOID );
|
||
|
|
||
|
|
||
|
/*=============================================================================
|
||
|
== Functions Used
|
||
|
=============================================================================*/
|
||
|
|
||
|
NTSTATUS IcaRegWinStationEnumerate( PULONG, PWINSTATIONNAME, PULONG );
|
||
|
NTSTATUS QueueWinStationCreate( PWINSTATIONNAME );
|
||
|
PWINSTATION FindWinStationByName( LPWSTR WinStationName, BOOLEAN LockList );
|
||
|
NTSTATUS QueueWinStationReset( ULONG LogonId );
|
||
|
NTSTATUS ReadWinStationSecurityDescriptor( PWINSTATION pWinStation );
|
||
|
NTSTATUS WinStationRenameWorker(PWINSTATIONNAME, ULONG, PWINSTATIONNAME, ULONG);
|
||
|
|
||
|
/*=============================================================================
|
||
|
== Global data
|
||
|
=============================================================================*/
|
||
|
|
||
|
extern LIST_ENTRY WinStationListHead; // protected by WinStationListLock
|
||
|
|
||
|
extern RTL_RESOURCE WinStationSecurityLock;
|
||
|
extern POLICY_TS_MACHINE g_MachinePolicy; //defined in winsta.c
|
||
|
extern RTL_RESOURCE WinStationSecurityLock;
|
||
|
|
||
|
|
||
|
/*******************************************************************************
|
||
|
*
|
||
|
* WinStationReadRegistryWorker
|
||
|
*
|
||
|
* Update the listening winstations to match the registry
|
||
|
*
|
||
|
* This function assumes that g_MachinePolicy is up to date. This object is a global object
|
||
|
* which is updated on TS startup, and any time there is a TS related policy change.
|
||
|
*
|
||
|
* ENTRY:
|
||
|
* nothing
|
||
|
*
|
||
|
* EXIT:
|
||
|
* STATUS_SUCCESS - no error
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
typedef struct _RENAMEINFO {
|
||
|
WINSTATIONNAME OldName;
|
||
|
BOOLEAN Renamed;
|
||
|
} RENAMEINFO, *PRENAMEINFO;
|
||
|
|
||
|
#define KEEP_ALIVE_INTERVAL_DFLT 4 // in minutes
|
||
|
|
||
|
NTSTATUS
|
||
|
WinStationKeepAlive()
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
ICA_KEEP_ALIVE k;
|
||
|
HANDLE hKeepAlive;
|
||
|
static ICA_KEEP_ALIVE kPrev;
|
||
|
static BOOLEAN firstTime = TRUE;
|
||
|
|
||
|
k.start = FALSE;
|
||
|
k.interval = 0;
|
||
|
|
||
|
if ( g_MachinePolicy.fPolicyKeepAlive )
|
||
|
{
|
||
|
k.start = (BOOLEAN) g_MachinePolicy.fKeepAliveEnable;
|
||
|
k.interval = g_MachinePolicy.KeepAliveInterval;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// read to see what the registry policy is set to...
|
||
|
// Code below was cut/paste from termdd ( where Zw was replaced with Nt )
|
||
|
UNICODE_STRING RegistryPath;
|
||
|
UNICODE_STRING KeyName;
|
||
|
HANDLE hKey;
|
||
|
OBJECT_ATTRIBUTES ObjAttribs;
|
||
|
ULONG KeyInfoBuffer[16];
|
||
|
ULONG KeyInfoLength;
|
||
|
PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
|
||
|
ULONG KeepAliveEnable;
|
||
|
ULONG KeepAliveInterval;
|
||
|
|
||
|
// Open the Terminal Server subkey under \\HKEY_LOCAL_MACHINE\SYSTEM\CurrentConttrolSet\
|
||
|
// Control\Terminal Server
|
||
|
RtlInitUnicodeString(&RegistryPath, REG_NTAPI_CONTROL_TSERVER);
|
||
|
InitializeObjectAttributes(&ObjAttribs, &RegistryPath, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
|
Status = NtOpenKey(&hKey, KEY_READ, &ObjAttribs);
|
||
|
|
||
|
if (Status == STATUS_SUCCESS) {
|
||
|
pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfoBuffer;
|
||
|
|
||
|
// Get the value for KeepAliveEnable Key
|
||
|
RtlInitUnicodeString(&KeyName, KEEP_ALIVE_ENABLE_KEY);
|
||
|
Status = NtQueryValueKey(hKey, &KeyName, KeyValuePartialInformation,
|
||
|
pKeyInfo, sizeof(KeyInfoBuffer), &KeyInfoLength);
|
||
|
|
||
|
if ((Status == STATUS_SUCCESS) && (pKeyInfo->Type == REG_DWORD) &&
|
||
|
(pKeyInfo->DataLength == sizeof(ULONG))) {
|
||
|
KeepAliveEnable = *((PULONG) pKeyInfo->Data);
|
||
|
}
|
||
|
else {
|
||
|
// By default, we don't enable keepalive
|
||
|
KeepAliveEnable = 0;
|
||
|
}
|
||
|
|
||
|
if (KeepAliveEnable) {
|
||
|
// Get the value for KeepAliveInterval
|
||
|
RtlInitUnicodeString(&KeyName, KEEP_ALIVE_INTERVAL_KEY);
|
||
|
Status = NtQueryValueKey(hKey, &KeyName, KeyValuePartialInformation,
|
||
|
pKeyInfo, sizeof(KeyInfoBuffer), &KeyInfoLength);
|
||
|
|
||
|
if (Status == STATUS_SUCCESS && (pKeyInfo->Type == REG_DWORD) &&
|
||
|
(pKeyInfo->DataLength == sizeof(ULONG))) {
|
||
|
KeepAliveInterval = *((PULONG) pKeyInfo->Data);
|
||
|
}
|
||
|
else {
|
||
|
// The default KeepAliveInterval is 2 min
|
||
|
KeepAliveInterval = KEEP_ALIVE_INTERVAL_DFLT;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// The default KeepAliveInterval
|
||
|
KeepAliveInterval = KEEP_ALIVE_INTERVAL_DFLT;
|
||
|
}
|
||
|
|
||
|
// Close the Key
|
||
|
NtClose(hKey);
|
||
|
}
|
||
|
else {
|
||
|
// Set the default values for KeepAlive parameters
|
||
|
KeepAliveEnable = 0;
|
||
|
KeepAliveInterval = KEEP_ALIVE_INTERVAL_DFLT;
|
||
|
}
|
||
|
|
||
|
k.start = (BOOLEAN )KeepAliveEnable;
|
||
|
k.interval = KeepAliveInterval;
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( firstTime )
|
||
|
{
|
||
|
kPrev = k;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
#ifdef DBG
|
||
|
#ifdef ARABERN_TEST
|
||
|
#include <time.h>
|
||
|
ULONG x;
|
||
|
srand( (unsigned)time( NULL ) );
|
||
|
x = rand();
|
||
|
k.start = (BOOLEAN ) (0x00000001 & x) ;
|
||
|
k.interval = 0x00000008 & x ;
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
if ( ( kPrev.start == k.start ) && ( kPrev.interval == k.interval ) )
|
||
|
{
|
||
|
// no change, nothing to do, so return;
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Open TermDD.
|
||
|
*/
|
||
|
Status = IcaOpen(&hKeepAlive);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = IcaIoControl(hKeepAlive, IOCTL_ICA_SYSTEM_KEEP_ALIVE , &k,
|
||
|
sizeof(k), NULL, 0, NULL);
|
||
|
|
||
|
IcaClose(hKeepAlive);
|
||
|
hKeepAlive = NULL;
|
||
|
}
|
||
|
|
||
|
firstTime = FALSE;
|
||
|
|
||
|
return Status;
|
||
|
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
WinStationReadRegistryWorker()
|
||
|
{
|
||
|
ULONG WinStationCount;
|
||
|
ULONG ByteCount;
|
||
|
WINSTATIONNAME * pWinStationName;
|
||
|
PWINSTATIONCONFIG2 pWinConfig;
|
||
|
PWINSTATION pWinStation;
|
||
|
PRENAMEINFO pRenameInfo;
|
||
|
PLIST_ENTRY Head, Next;
|
||
|
NTSTATUS Status;
|
||
|
ULONG i;
|
||
|
|
||
|
if ( !gbServer )
|
||
|
ENTERCRIT( &WinStationListenersLock );
|
||
|
|
||
|
// see if keep alive is required, then IOCTL it to TermDD
|
||
|
WinStationKeepAlive();
|
||
|
|
||
|
/*
|
||
|
* Get the number of WinStations in the registry
|
||
|
*/
|
||
|
WinStationCount = 0;
|
||
|
Status = IcaRegWinStationEnumerate( &WinStationCount, NULL, &ByteCount );
|
||
|
if ( !NT_SUCCESS(Status) )
|
||
|
goto badenum1;
|
||
|
|
||
|
/*
|
||
|
* Allocate a buffer for the WinStation names
|
||
|
*/
|
||
|
pWinStationName = MemAlloc( ByteCount );
|
||
|
if ( pWinStationName == NULL ) {
|
||
|
Status = STATUS_NO_MEMORY;
|
||
|
goto badalloc1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get list of WinStation names from registry
|
||
|
*/
|
||
|
WinStationCount = (ULONG) -1;
|
||
|
Status = IcaRegWinStationEnumerate( &WinStationCount,
|
||
|
(PWINSTATIONNAME)pWinStationName,
|
||
|
&ByteCount );
|
||
|
if ( !NT_SUCCESS(Status) )
|
||
|
goto badenum2;
|
||
|
|
||
|
/*
|
||
|
* Allocate a buffer for WinStation configuration data
|
||
|
*/
|
||
|
pWinConfig = MemAlloc( sizeof(WINSTATIONCONFIG2) * WinStationCount );
|
||
|
if ( pWinConfig == NULL ) {
|
||
|
Status = STATUS_NO_MEMORY;
|
||
|
goto badalloc2;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Allocate a buffer for tracking listener WinStation renames
|
||
|
*/
|
||
|
pRenameInfo = MemAlloc( sizeof(RENAMEINFO) * WinStationCount );
|
||
|
if ( pRenameInfo == NULL ) {
|
||
|
Status = STATUS_NO_MEMORY;
|
||
|
goto badalloc3;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Now query the configuration data for each of the WinStation names
|
||
|
*/
|
||
|
for ( i = 0; i < WinStationCount; i++ ) {
|
||
|
pRenameInfo[i].Renamed = FALSE;
|
||
|
{
|
||
|
TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: WinStationReadRegistryWorker: %S\n",pWinStationName[i]));
|
||
|
Status = RegWinStationQueryEx(
|
||
|
SERVERNAME_CURRENT,
|
||
|
&g_MachinePolicy,
|
||
|
pWinStationName[i],
|
||
|
&pWinConfig[i],
|
||
|
sizeof(WINSTATIONCONFIG2),
|
||
|
&ByteCount, TRUE );
|
||
|
if ( !NT_SUCCESS(Status) ) {
|
||
|
goto badregdata;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check if any existing WinStations need to be deleted
|
||
|
*/
|
||
|
Head = &WinStationListHead;
|
||
|
ENTERCRIT( &WinStationListLock );
|
||
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
||
|
|
||
|
pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
|
||
|
|
||
|
/*
|
||
|
* only check listening and single-instance winstations
|
||
|
*/
|
||
|
if ( !(pWinStation->Flags & WSF_LISTEN) &&
|
||
|
!(pWinStation->Config.Pd[0].Create.PdFlag & PD_SINGLE_INST) )
|
||
|
continue;
|
||
|
|
||
|
/* check if name still exists in the registry */
|
||
|
for ( i = 0; i < WinStationCount; i++ ) {
|
||
|
if ( !_wcsicmp( pWinStationName[i], pWinStation->WinStationName ) ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( i == WinStationCount ) {
|
||
|
/* The WinStation is not in the registry. If the listener was
|
||
|
renamed, we don't want to reset it. We look for a registry
|
||
|
entry which has the same configuration info.
|
||
|
*/
|
||
|
|
||
|
for ( i = 0; i < WinStationCount; i++ ) {
|
||
|
if ( !memcmp( &pWinStation->Config, &pWinConfig[i], sizeof(WINSTATIONCONFIG2) ) ) {
|
||
|
pRenameInfo[i].Renamed = TRUE;
|
||
|
wcscpy(pRenameInfo[i].OldName, pWinStation->WinStationName);
|
||
|
DBGPRINT(("TERMSRV: Renaming %ws to %ws\n",
|
||
|
pWinStation->WinStationName, pWinStationName[i]));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/* If no match was found in the registry, or if the matching
|
||
|
listener is diabled, reset the listener.
|
||
|
*/
|
||
|
if ((i == WinStationCount) ||
|
||
|
(CheckWinStationEnable(!pRenameInfo[i].Renamed ?
|
||
|
pWinStation->WinStationName :
|
||
|
pWinStationName[i]) != STATUS_SUCCESS)) {
|
||
|
TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: WinStationReadRegistryWorker: DELETE %u\n",
|
||
|
pWinStation->LogonId ));
|
||
|
QueueWinStationReset( pWinStation->LogonId );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
LEAVECRIT( &WinStationListLock );
|
||
|
|
||
|
/*
|
||
|
* Check if any WinStations need to be created or reset
|
||
|
*/
|
||
|
for ( i = 0; i < WinStationCount; i++ ) {
|
||
|
|
||
|
if ( _wcsicmp( pWinStationName[i], L"Console" ) ){
|
||
|
/*
|
||
|
* Ignore console WinStation
|
||
|
*/
|
||
|
/*
|
||
|
* If this WinStation exists, then see if the Registry data
|
||
|
* has changed. If so, then reset the WinStation.
|
||
|
*/
|
||
|
if ( pWinStation = FindWinStationByName( pWinStationName[i], FALSE ) ) {
|
||
|
|
||
|
if ( memcmp( &pWinStation->Config, &pWinConfig[i], sizeof(WINSTATIONCONFIG2) ) ) {
|
||
|
|
||
|
/*
|
||
|
* NOTE: For network WinStations, we test to see if the Lan
|
||
|
* Adapter setting has changed. If not, we simply
|
||
|
* refresh the configuration data since resetting the
|
||
|
* WinStation would reset ALL connections on the same
|
||
|
* Transport/Lan adapter combination.
|
||
|
*/
|
||
|
if ( pWinConfig[i].Pd[0].Create.SdClass == SdNetwork &&
|
||
|
pWinConfig[i].Pd[0].Params.Network.LanAdapter ==
|
||
|
pWinStation->Config.Pd[0].Params.Network.LanAdapter ) {
|
||
|
memcpy( &pWinStation->Config, &pWinConfig[i], sizeof(WINSTATIONCONFIG2) );
|
||
|
|
||
|
/*
|
||
|
* Listening network winstations should update their security
|
||
|
* descriptors.
|
||
|
*/
|
||
|
RtlAcquireResourceExclusive(&WinStationSecurityLock, TRUE);
|
||
|
ReadWinStationSecurityDescriptor( pWinStation );
|
||
|
RtlReleaseResource(&WinStationSecurityLock);
|
||
|
|
||
|
/*
|
||
|
* NOTE: For async WinStations, if the WinStation is NOT in
|
||
|
* in the listen state and the Device name and Modem
|
||
|
* name have not changed, then we do nothing. The
|
||
|
* new config data will be read when the WinStation
|
||
|
* is next re-created.
|
||
|
*/
|
||
|
} else if ( pWinConfig[i].Pd[0].Create.SdClass == SdAsync &&
|
||
|
pWinStation->State != State_Listen &&
|
||
|
!memcmp ( pWinConfig[i].Pd[0].Params.Async.DeviceName,
|
||
|
pWinStation->Config.Pd[0].Params.Async.DeviceName,
|
||
|
sizeof( pWinConfig[i].Pd[0].Params.Async.DeviceName ) ) &&
|
||
|
!memcmp ( pWinConfig[i].Pd[0].Params.Async.ModemName,
|
||
|
pWinStation->Config.Pd[0].Params.Async.ModemName,
|
||
|
sizeof( pWinConfig[i].Pd[0].Params.Async.ModemName ) ) ) {
|
||
|
|
||
|
// Nothing to do
|
||
|
|
||
|
/*
|
||
|
* NOTE: For OEM WinStations, if the WinStation is NOT in
|
||
|
* in the listen state and the Pd[0] params have not
|
||
|
* changed, then we do nothing. The new config data
|
||
|
* will be read when the WinStation is next re-created.
|
||
|
*/
|
||
|
} else if ( pWinConfig[i].Pd[0].Create.SdClass == SdOemTransport &&
|
||
|
pWinStation->State != State_Listen &&
|
||
|
!memcmp ( &pWinConfig[i].Pd[0].Params,
|
||
|
&pWinStation->Config.Pd[0].Params,
|
||
|
sizeof( pWinConfig[i].Pd[0].Params ) ) ) {
|
||
|
|
||
|
// Nothing to do
|
||
|
|
||
|
} else {
|
||
|
|
||
|
BOOLEAN bRecreate = TRUE;
|
||
|
|
||
|
if ( !gbServer ) {
|
||
|
if ( g_fDenyTSConnectionsPolicy &&
|
||
|
// Performance, we only want to check if policy enable help when connection is denied
|
||
|
(!TSIsMachineInHelpMode() || !TSIsMachinePolicyAllowHelp()) ) {
|
||
|
|
||
|
bRecreate = FALSE;
|
||
|
}
|
||
|
|
||
|
WinStationResetWorker( pWinStation->LogonId, TRUE, FALSE, bRecreate );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
QueueWinStationReset( pWinStation->LogonId );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if ( !(pWinStation->Config.Pd[0].Create.PdFlag & PD_SINGLE_INST) ||
|
||
|
( pWinStation->State == State_Listen ) ) {
|
||
|
|
||
|
RtlAcquireResourceExclusive(&WinStationSecurityLock, TRUE);
|
||
|
ReadWinStationSecurityDescriptor( pWinStation );
|
||
|
RtlReleaseResource(&WinStationSecurityLock);
|
||
|
}
|
||
|
ReleaseWinStation( pWinStation );
|
||
|
|
||
|
} else
|
||
|
if (pRenameInfo[i].Renamed &&
|
||
|
NT_SUCCESS(WinStationRenameWorker(pRenameInfo[i].OldName,
|
||
|
sizeof(WINSTATIONNAMEW)/sizeof(WCHAR),
|
||
|
pWinStationName[i],
|
||
|
sizeof(WINSTATIONNAMEW)/sizeof(WCHAR)))) {
|
||
|
// Rename succeeded - don't recreate listener
|
||
|
/*
|
||
|
* An active WinStation was not found so we will create one.
|
||
|
*/
|
||
|
} else {
|
||
|
|
||
|
if ( !gbServer &&
|
||
|
g_fDenyTSConnectionsPolicy &&
|
||
|
// Performance, we only want to check if policy enable help when connection is denied
|
||
|
(!TSIsMachineInHelpMode() || !TSIsMachinePolicyAllowHelp()) ) {
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* NOTE: NEVER create TAPI modem winstations in this routine.
|
||
|
* We only allow creation of these winstations at
|
||
|
* system startup time due to issues with the TAPI
|
||
|
* database potentially being locked by this and other
|
||
|
* processes, resulting in incorrect TAPI device
|
||
|
* enumeration.
|
||
|
*/
|
||
|
if ( pWinConfig[i].Cd.CdClass != CdModem ) {
|
||
|
if (!gbServer ) {
|
||
|
WinStationCreateWorker( pWinStationName[i], NULL );
|
||
|
} else {
|
||
|
QueueWinStationCreate( pWinStationName[i] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we are dealing with the console session, update userconfig's shadow bit, that is
|
||
|
// the only item I know of that needs updating.
|
||
|
if ( pWinStation = FindWinStationByName( pWinStationName[i], FALSE ) ) {
|
||
|
pWinStation->Config.Config.User.Shadow = pWinConfig[i].Config.User.Shadow;
|
||
|
pWinStation->Config.Config.User.fInheritShadow = pWinConfig[i].Config.User.fInheritShadow;
|
||
|
TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: WinStationReadRegistryWorker: %S, Shadow value of %d copied to console session's USERCONFIG\n",pWinStationName[i],
|
||
|
pWinConfig[i].Config.User.Shadow));
|
||
|
ReleaseWinStation( pWinStation );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Free buffers
|
||
|
*/
|
||
|
MemFree( pRenameInfo );
|
||
|
MemFree( pWinConfig );
|
||
|
MemFree( pWinStationName );
|
||
|
|
||
|
if ( !gbServer )
|
||
|
LEAVECRIT( &WinStationListenersLock );
|
||
|
|
||
|
return( STATUS_SUCCESS );
|
||
|
|
||
|
/*=============================================================================
|
||
|
== Error returns
|
||
|
=============================================================================*/
|
||
|
|
||
|
badregdata:
|
||
|
MemFree( pRenameInfo );
|
||
|
badalloc3:
|
||
|
MemFree( pWinConfig );
|
||
|
badalloc2:
|
||
|
badenum2:
|
||
|
MemFree( pWinStationName );
|
||
|
badalloc1:
|
||
|
badenum1:
|
||
|
|
||
|
if ( !gbServer )
|
||
|
LEAVECRIT( &WinStationListenersLock );
|
||
|
|
||
|
return( Status );
|
||
|
}
|