|
|
/*************************************************************************
* * 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 ); }
|