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.
1064 lines
37 KiB
1064 lines
37 KiB
|
|
/*************************************************************************
|
|
*
|
|
* wait.c
|
|
*
|
|
* WinStation wait for connection routines
|
|
*
|
|
* Copyright Microsoft Corporation, 1998
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
|
|
/*
|
|
* Includes
|
|
*/
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "conntfy.h"
|
|
|
|
// These are the maximum lengths of UserName, Password and Domain allowed by Winlogon
|
|
#define MAX_ALLOWED_USERNAME_LEN 255
|
|
#define MAX_ALLOWED_PASSWORD_LEN 126
|
|
#define MAX_ALLOWED_DOMAIN_LEN 255
|
|
|
|
|
|
|
|
/*=============================================================================
|
|
== Data
|
|
=============================================================================*/
|
|
|
|
NTSTATUS
|
|
WinStationInheritSecurityDescriptor(
|
|
PVOID pSecurityDescriptor,
|
|
PWINSTATION pTargetWinStation
|
|
);
|
|
NTSTATUS
|
|
WaitForConsoleConnectWorker( PWINSTATION pWinStation );
|
|
BOOL
|
|
IsKernelDebuggerAttached();
|
|
|
|
//
|
|
//From regapi.dll
|
|
//
|
|
BOOLEAN RegIsTimeZoneRedirectionEnabled();
|
|
|
|
extern PSECURITY_DESCRIPTOR DefaultConsoleSecurityDescriptor;
|
|
|
|
extern WINSTATIONCONFIG2 gConsoleConfig;
|
|
extern HANDLE WinStationIdleControlEvent;
|
|
extern RTL_CRITICAL_SECTION ConsoleLock;
|
|
extern RTL_RESOURCE WinStationSecurityLock;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WaitForConnectWorker
|
|
*
|
|
* Message parameter unmarshalling function for WinStation API.
|
|
*
|
|
* ENTRY:
|
|
* pWinStation (input)
|
|
* Pointer to our WinStation (locked)
|
|
*
|
|
* Note, comes in locked and returned released.
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WaitForConnectWorker( PWINSTATION pWinStation, HANDLE ClientProcessId )
|
|
{
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
ULONG ReturnLength;
|
|
BYTE version;
|
|
ULONG Offset;
|
|
ICA_STACK_LAST_ERROR tdlasterror;
|
|
WINSTATION_APIMSG WMsg;
|
|
BOOLEAN rc;
|
|
NTSTATUS Status;
|
|
BOOLEAN fOwnsConsoleTerminal = FALSE;
|
|
ULONG BytesGot ;
|
|
|
|
#define MODULE_SIZE 1024
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WaitForConnectWorker, LogonId=%d\n",
|
|
pWinStation->LogonId ));
|
|
|
|
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "TERMSRV: WaitForConnectWorker, LogonId=%d\n",pWinStation->LogonId ));
|
|
|
|
/*
|
|
* You only go through this API once
|
|
*/
|
|
if ( !pWinStation->NeverConnected ) {
|
|
ReleaseWinStation( pWinStation );
|
|
#ifdef DBG
|
|
DbgBreakPoint();
|
|
#endif
|
|
return( STATUS_ACCESS_DENIED );
|
|
}
|
|
|
|
// Should really be winlogon only here, however if ntsd is started
|
|
// then the first time we know it's winlogon is here.
|
|
|
|
/*
|
|
* Ensure this is WinLogon calling
|
|
*/
|
|
if ( ClientProcessId != pWinStation->InitialCommandProcessId ) {
|
|
|
|
/*
|
|
* If NTSD is started instead of winlogon, the InitialCommandProcessId is wrong.
|
|
*/
|
|
if ( !pWinStation->InitialProcessSet ) {
|
|
|
|
// need to close handle if already opened
|
|
if ( pWinStation->InitialCommandProcess ) {
|
|
NtClose( pWinStation->InitialCommandProcess );
|
|
pWinStation->InitialCommandProcess = NULL;
|
|
InvalidateTerminateWaitList();
|
|
}
|
|
|
|
pWinStation->InitialCommandProcess = OpenProcess(
|
|
PROCESS_ALL_ACCESS,
|
|
FALSE,
|
|
(DWORD)(UINT_PTR)ClientProcessId );
|
|
|
|
if ( pWinStation->InitialCommandProcess == NULL ) {
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto done;
|
|
}
|
|
pWinStation->InitialCommandProcessId = ClientProcessId;
|
|
pWinStation->InitialProcessSet = TRUE;
|
|
|
|
}
|
|
else {
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* Only do this once
|
|
*/
|
|
pWinStation->InitialProcessSet = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Console's work is done
|
|
*/
|
|
|
|
|
|
/*
|
|
* At this point the create is done.
|
|
*/
|
|
if (pWinStation->CreateEvent != NULL) {
|
|
NtSetEvent( pWinStation->CreateEvent, NULL );
|
|
}
|
|
|
|
|
|
/*
|
|
* At this point The session may be terminating. If this is
|
|
* the case, fail the call now.
|
|
*/
|
|
|
|
if ( pWinStation->Terminating ) {
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
goto done;
|
|
}
|
|
|
|
|
|
/*
|
|
* We are going to wait for a connect (Idle)
|
|
*/
|
|
memset( &WMsg, 0, sizeof(WMsg) );
|
|
pWinStation->State = State_Idle;
|
|
NotifySystemEvent( WEVENT_STATECHANGE );
|
|
|
|
/*
|
|
* Initialize connect event to wait on
|
|
*/
|
|
InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
|
|
Status = NtCreateEvent( &pWinStation->ConnectEvent, EVENT_ALL_ACCESS, &ObjA,
|
|
NotificationEvent, FALSE );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* OK, now wait for a connection
|
|
*/
|
|
UnlockWinStation( pWinStation );
|
|
Status = NtWaitForSingleObject( pWinStation->ConnectEvent, FALSE, NULL );
|
|
rc = RelockWinStation( pWinStation );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
|
|
}
|
|
|
|
fOwnsConsoleTerminal = pWinStation->fOwnsConsoleTerminal;
|
|
|
|
if (pWinStation->ConnectEvent) {
|
|
NtClose( pWinStation->ConnectEvent );
|
|
pWinStation->ConnectEvent = NULL;
|
|
}
|
|
if ( !rc || pWinStation->Terminating ) {
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
goto done;
|
|
}
|
|
|
|
// if this is a connect to the console session, Do all the console specific.
|
|
if (fOwnsConsoleTerminal) {
|
|
Status = WaitForConsoleConnectWorker( pWinStation );
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Reset the broken connection Flag.
|
|
*
|
|
*/
|
|
|
|
pWinStation->StateFlags &= ~WSF_ST_BROKEN_CONNECTION;
|
|
|
|
|
|
|
|
/*
|
|
* Duplicate the beep channel.
|
|
* This is one channel that both CSR and ICASRV have open.
|
|
*/
|
|
Status = IcaChannelOpen( pWinStation->hIca,
|
|
Channel_Beep,
|
|
NULL,
|
|
&pWinStation->hIcaBeepChannel );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationWaitForConnect, LogonId=%d, IcaChannelOpen 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
pWinStation->hIcaBeepChannel,
|
|
pWinStation->WindowsSubSysProcess,
|
|
&WMsg.u.DoConnect.hIcaBeepChannel,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationWaitForConnect, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Duplicate the thinwire channel.
|
|
* This is one channel that both CSR and ICASRV have open.
|
|
*/
|
|
Status = IcaChannelOpen( pWinStation->hIca,
|
|
Channel_Virtual,
|
|
VIRTUAL_THINWIRE,
|
|
&pWinStation->hIcaThinwireChannel );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationWaitForConnect, LogonId=%d, IcaChannelOpen 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
pWinStation->hIcaThinwireChannel,
|
|
pWinStation->WindowsSubSysProcess,
|
|
&WMsg.u.DoConnect.hIcaThinwireChannel,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationWaitForConnect, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
Status = IcaChannelIoControl( pWinStation->hIcaThinwireChannel,
|
|
IOCTL_ICA_CHANNEL_ENABLE_SHADOW,
|
|
NULL, 0, NULL, 0, NULL );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
/*
|
|
* Video channel
|
|
*/
|
|
Status = WinStationOpenChannel( pWinStation->hIca,
|
|
pWinStation->WindowsSubSysProcess,
|
|
Channel_Video,
|
|
NULL,
|
|
&WMsg.u.DoConnect.hIcaVideoChannel );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationOpenChannel, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Keyboard channel
|
|
*/
|
|
Status = WinStationOpenChannel( pWinStation->hIca,
|
|
pWinStation->WindowsSubSysProcess,
|
|
Channel_Keyboard,
|
|
NULL,
|
|
&WMsg.u.DoConnect.hIcaKeyboardChannel );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationOpenChannel, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Mouse channel
|
|
*/
|
|
Status = WinStationOpenChannel( pWinStation->hIca,
|
|
pWinStation->WindowsSubSysProcess,
|
|
Channel_Mouse,
|
|
NULL,
|
|
&WMsg.u.DoConnect.hIcaMouseChannel );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationOpenChannel, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Command channel
|
|
*/
|
|
Status = WinStationOpenChannel( pWinStation->hIca,
|
|
pWinStation->WindowsSubSysProcess,
|
|
Channel_Command,
|
|
NULL,
|
|
&WMsg.u.DoConnect.hIcaCommandChannel );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationOpenChannel, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
|
|
/*
|
|
* Secure any virtual channels
|
|
*/
|
|
VirtualChannelSecurity( pWinStation );
|
|
|
|
// HACK? Yes int the sense that a tmp session is getting some attribs of session0 but no
|
|
// in the sense that the purpose of the tmp session is to finally connect to session0 given our current design.
|
|
//
|
|
// Its possible that we are dealing with a tmp session here, due to the fact that user has
|
|
// selected /CONSOLE and we will login into a tmp session and then reconnect to session0 after
|
|
// we pass creds to session zero's winlogon.
|
|
// So we have a prlblem here where login is talking place inside a temp session but the target is
|
|
// session0.
|
|
// If TSCC was setup to use a specific creds for all remote session, we don't want to use that cred for the
|
|
// tmp session since we really are tryint to get to session0, and TSCC's config for rdp sessions should not hold.
|
|
// AraBern 09/13/2002
|
|
|
|
if (pWinStation->bRequestedSessionIDFieldValid && ( pWinStation->RequestedSessionID == 0 ) )
|
|
{
|
|
// we are dealing with a tmp session and we intend to connect to console session assuming
|
|
// good login
|
|
|
|
PWINSTATION pWinStation_0;
|
|
|
|
if ( pWinStation_0 = FindWinStationById( 0, FALSE ) )
|
|
{
|
|
pWinStation->Config.Config.User.fInheritAutoLogon =
|
|
pWinStation_0->Config.Config.User.fInheritAutoLogon ;
|
|
|
|
pWinStation->Config.Config.User.fPromptForPassword =
|
|
pWinStation_0->Config.Config.User.fPromptForPassword ;
|
|
|
|
|
|
ReleaseWinStation( pWinStation_0 );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Client specific connection extension completion
|
|
*/
|
|
if ( pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxInitializeClientData ) {
|
|
|
|
Status = pWinStation->pWsx->pWsxInitializeClientData( pWinStation->pWsxContext,
|
|
pWinStation->hStack,
|
|
pWinStation->hIca,
|
|
pWinStation->hIcaThinwireChannel,
|
|
pWinStation->VideoModuleName,
|
|
sizeof(pWinStation->VideoModuleName),
|
|
&pWinStation->Config.Config.User,
|
|
&pWinStation->Client.HRes,
|
|
&pWinStation->Client.VRes,
|
|
&pWinStation->Client.ColorDepth,
|
|
&WMsg.u.DoConnect );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
if (pWinStation->LogonId == 0 || g_bPersonalTS) {
|
|
if (pWinStation->hWinmmConsoleAudioEvent) {
|
|
if (pWinStation->Client.fRemoteConsoleAudio) {
|
|
// Set the console audio event - means console audio can be remoted
|
|
SetEvent(pWinStation->hWinmmConsoleAudioEvent);
|
|
}
|
|
else {
|
|
// Set the console audio event - means console audio can't be remoted
|
|
ResetEvent(pWinStation->hWinmmConsoleAudioEvent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Get long UserNames and Password now */
|
|
/* Do this only if Client autologon credentials are needed */
|
|
/* If Server logon settings are going to Override Client logon settings, no need to do this at all */
|
|
|
|
if ( pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxEscape && pWinStation->Config.Config.User.fInheritAutoLogon ) {
|
|
|
|
pWinStation->pNewClientCredentials = MemAlloc( sizeof(ExtendedClientCredentials) );
|
|
if (pWinStation->pNewClientCredentials == NULL) {
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
Status = pWinStation->pWsx->pWsxEscape( pWinStation->pWsxContext,
|
|
GET_LONG_USERNAME,
|
|
NULL,
|
|
0,
|
|
pWinStation->pNewClientCredentials,
|
|
sizeof(ExtendedClientCredentials),
|
|
&BytesGot) ;
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// WsxEscape for GET_LONG_USERNAME succeeded
|
|
|
|
// Check if u need the ExtendedClientCredentials - the common case is short UserName and
|
|
// short password - so optimize the common case
|
|
|
|
if ( (wcslen(pWinStation->pNewClientCredentials->UserName) <= USERNAME_LENGTH) &&
|
|
(wcslen(pWinStation->pNewClientCredentials->Password) <= PASSWORD_LENGTH) &&
|
|
(wcslen(pWinStation->pNewClientCredentials->Domain) <= DOMAIN_LENGTH) ) {
|
|
|
|
// We can use the old credentials itself
|
|
MemFree(pWinStation->pNewClientCredentials);
|
|
pWinStation->pNewClientCredentials = NULL ;
|
|
}
|
|
|
|
// Winlogon does not allow > 126 chars for Password and > 255 chars for UserName and Domain in some code paths
|
|
// So we have to use the old truncated credentials in case the extended credentials exceed these limits
|
|
|
|
if (pWinStation->pNewClientCredentials != NULL) {
|
|
|
|
if (wcslen(pWinStation->pNewClientCredentials->UserName) > MAX_ALLOWED_USERNAME_LEN) {
|
|
wcscpy(pWinStation->pNewClientCredentials->UserName, pWinStation->Config.Config.User.UserName);
|
|
}
|
|
if (wcslen(pWinStation->pNewClientCredentials->Password) > MAX_ALLOWED_PASSWORD_LEN) {
|
|
wcscpy(pWinStation->pNewClientCredentials->Password, pWinStation->Config.Config.User.Password);
|
|
}
|
|
if (wcslen(pWinStation->pNewClientCredentials->Domain) > MAX_ALLOWED_DOMAIN_LEN) {
|
|
wcscpy(pWinStation->pNewClientCredentials->Domain, pWinStation->Config.Config.User.Domain);
|
|
}
|
|
}
|
|
} else {
|
|
// WsxEscape for GET_LONG_USERNAME failed
|
|
MemFree(pWinStation->pNewClientCredentials);
|
|
pWinStation->pNewClientCredentials = NULL ;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Store WinStation name in connect msg
|
|
*/
|
|
RtlCopyMemory( WMsg.u.DoConnect.WinStationName,
|
|
pWinStation->WinStationName,
|
|
sizeof(WINSTATIONNAME) );
|
|
|
|
/*
|
|
* KLUDGE ALERT!!
|
|
* The Wsx initializes AudioDriverName in the DoConnect struct.
|
|
* However, we need to save it for later use during reconnect,
|
|
* so we now copy it into the WinStation->Client struct.
|
|
* (This field was NOT not initialized during the earlier
|
|
* IOCTL_ICA_STACK_QUERY_CLIENT call.)
|
|
*/
|
|
RtlCopyMemory( pWinStation->Client.AudioDriverName,
|
|
WMsg.u.DoConnect.AudioDriverName,
|
|
sizeof( pWinStation->Client.AudioDriverName ) );
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Store protocol and Display driver name in WINSTATION since we may need them later for reconnect.
|
|
*/
|
|
|
|
memset(pWinStation->ProtocolName, 0, sizeof(pWinStation->ProtocolName));
|
|
memcpy(pWinStation->ProtocolName, WMsg.u.DoConnect.ProtocolName, sizeof(pWinStation->ProtocolName) - sizeof(WCHAR));
|
|
|
|
memset(pWinStation->DisplayDriverName, 0, sizeof(pWinStation->DisplayDriverName));
|
|
memcpy(pWinStation->DisplayDriverName, WMsg.u.DoConnect.DisplayDriverName, sizeof(pWinStation->DisplayDriverName) - sizeof(WCHAR));
|
|
|
|
/*
|
|
* Save protocol type, screen resolution, and color depth
|
|
*/
|
|
WMsg.u.DoConnect.HRes = pWinStation->Client.HRes;
|
|
WMsg.u.DoConnect.VRes = pWinStation->Client.VRes;
|
|
WMsg.u.DoConnect.ProtocolType = pWinStation->Client.ProtocolType;
|
|
|
|
/*
|
|
* Translate the color to the format excpected in winsrv
|
|
*/
|
|
|
|
switch(pWinStation->Client.ColorDepth){
|
|
case 1:
|
|
WMsg.u.DoConnect.ColorDepth=4 ; // 16 colors
|
|
break;
|
|
case 2:
|
|
WMsg.u.DoConnect.ColorDepth=8 ; // 256
|
|
break;
|
|
case 4:
|
|
WMsg.u.DoConnect.ColorDepth= 16;// 64K
|
|
break;
|
|
case 8:
|
|
WMsg.u.DoConnect.ColorDepth= 24;// 16M
|
|
break;
|
|
#define DC_HICOLOR
|
|
#ifdef DC_HICOLOR
|
|
case 16:
|
|
WMsg.u.DoConnect.ColorDepth= 15;// 32K
|
|
break;
|
|
#endif
|
|
default:
|
|
WMsg.u.DoConnect.ColorDepth=8 ;
|
|
break;
|
|
}
|
|
|
|
|
|
WMsg.u.DoConnect.KeyboardType = pWinStation->Client.KeyboardType;
|
|
WMsg.u.DoConnect.KeyboardSubType = pWinStation->Client.KeyboardSubType;
|
|
WMsg.u.DoConnect.KeyboardFunctionKey = pWinStation->Client.KeyboardFunctionKey;
|
|
|
|
|
|
/*
|
|
* Tell Win32 about the connection
|
|
*/
|
|
|
|
WMsg.ApiNumber = SMWinStationDoConnect;
|
|
|
|
Status = SendWinStationCommand( pWinStation, &WMsg, 600 );
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: SMWinStationDoConnect %d Status=0x%x\n",
|
|
pWinStation->LogonId, Status));
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
} else {
|
|
pWinStation->StateFlags |= WSF_ST_CONNECTED_TO_CSRSS;
|
|
|
|
}
|
|
|
|
//
|
|
//Set session time zone information.
|
|
//
|
|
if(pWinStation->LogonId != 0 && !pWinStation->fOwnsConsoleTerminal &&
|
|
RegIsTimeZoneRedirectionEnabled())
|
|
{
|
|
WINSTATION_APIMSG TimezoneMsg;
|
|
memset( &TimezoneMsg, 0, sizeof(TimezoneMsg) );
|
|
|
|
TimezoneMsg.ApiNumber = SMWinStationSetTimeZone;
|
|
memcpy(&(TimezoneMsg.u.SetTimeZone.TimeZone),&(pWinStation->Client.ClientTimeZone),
|
|
sizeof(TS_TIME_ZONE_INFORMATION));
|
|
|
|
SendWinStationCommand( pWinStation, &TimezoneMsg, 600 );
|
|
|
|
}
|
|
|
|
/*
|
|
* Indicate we're now connected. Only after succesful connection to Win32/CSR.
|
|
*/
|
|
pWinStation->NeverConnected = FALSE;
|
|
|
|
|
|
/*
|
|
* Check if we received a broken connection indication while connecting to to Win32/CSR.
|
|
*/
|
|
|
|
if (pWinStation->StateFlags & WSF_ST_BROKEN_CONNECTION) {
|
|
QueueWinStationReset(pWinStation->LogonId);
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set connect time and start disconnect timer
|
|
*/
|
|
NtQuerySystemTime( &pWinStation->ConnectTime );
|
|
|
|
/*
|
|
* Attempt to connect to the CdmRedirector
|
|
* for Client Drive Mapping
|
|
*
|
|
* NOTE: We still init the WinStation even if Client Drive
|
|
* mapping does not connect.
|
|
*/
|
|
if ( pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxCdmConnect ) {
|
|
Status = pWinStation->pWsx->pWsxCdmConnect( pWinStation->pWsxContext,
|
|
pWinStation->LogonId,
|
|
pWinStation->hIca );
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: CdmConnect %d Status=0x%x\n",
|
|
pWinStation->LogonId, Status));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
|
|
pWinStation->State = State_Connected;
|
|
NotifySystemEvent( WEVENT_CONNECT | WEVENT_STATECHANGE );
|
|
|
|
|
|
Status = NotifyConnect(pWinStation, fOwnsConsoleTerminal);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
DBGPRINT(( "TERMSRV: NotifyConsoleConnect failed Status= 0x%x\n", Status));
|
|
}
|
|
|
|
/*
|
|
* Release WinStation
|
|
*/
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
|
|
|
|
done:
|
|
|
|
/*
|
|
* Failure here will cause Winlogon to terminate the session. If we are failing
|
|
* the console session, let wake up the IdleControlThread, he may have to create
|
|
* a new console session.
|
|
*/
|
|
if (!NT_SUCCESS( Status ) && fOwnsConsoleTerminal) {
|
|
NtSetEvent(WinStationIdleControlEvent, NULL);
|
|
}
|
|
|
|
|
|
return( Status );
|
|
}
|
|
|
|
// Since the physical console does not have a real client, init data to some defaults
|
|
void InitializeConsoleClientData( PWINSTATIONCLIENTW pWC )
|
|
{
|
|
pWC->fTextOnly = FALSE;
|
|
pWC->fDisableCtrlAltDel = FALSE;
|
|
pWC->fMouse = TRUE;
|
|
pWC->fDoubleClickDetect = FALSE;
|
|
pWC->fINetClient = FALSE;
|
|
pWC->fPromptForPassword = FALSE;
|
|
pWC->fMaximizeShell = TRUE;
|
|
pWC->fEnableWindowsKey = TRUE;
|
|
pWC->fRemoteConsoleAudio= FALSE;
|
|
|
|
wcscpy( pWC->ClientName , L"");
|
|
wcscpy( pWC->Domain , L"");
|
|
wcscpy( pWC->UserName , L"");
|
|
wcscpy( pWC->Password , L"");
|
|
wcscpy( pWC->WorkDirectory , L"");
|
|
wcscpy( pWC->InitialProgram , L"");
|
|
|
|
pWC->SerialNumber = 0; // client computer unique serial number
|
|
pWC->EncryptionLevel = 3; // security level of encryption pd
|
|
pWC->ClientAddressFamily = 0;
|
|
|
|
wcscpy( pWC->ClientAddress , L"");
|
|
|
|
pWC->HRes = 640;
|
|
pWC->VRes = 480;
|
|
pWC->ColorDepth = 0x2;
|
|
pWC->ProtocolType = PROTOCOL_CONSOLE ;
|
|
pWC->KeyboardLayout = 0;
|
|
pWC->KeyboardType = 0;
|
|
pWC->KeyboardSubType = 0;
|
|
pWC->KeyboardFunctionKey = 0;
|
|
|
|
wcscpy( pWC->imeFileName , L"");
|
|
wcscpy( pWC->ClientDirectory, L"");
|
|
wcscpy( pWC->ClientLicense , L"");
|
|
wcscpy( pWC->ClientModem , L"");
|
|
|
|
pWC->ClientBuildNumber = 0;
|
|
pWC->ClientHardwareId = 0; // client software serial number
|
|
pWC->ClientProductId = 0; // client software product id
|
|
pWC->OutBufCountHost = 0; // number of outbufs on host
|
|
pWC->OutBufCountClient = 0; // number of outbufs on client
|
|
pWC->OutBufLength = 0; // length of outbufs in bytes
|
|
|
|
wcscpy( pWC->AudioDriverName, L"" );
|
|
|
|
pWC->ClientSessionId = LOGONID_NONE;
|
|
|
|
{
|
|
//This time zone information is invalid
|
|
//using it we set BaseSrvpStaticServerData->TermsrvClientTimeZoneId to
|
|
//TIME_ZONE_ID_INVALID!
|
|
TS_TIME_ZONE_INFORMATION InvalidTZ={0,L"",
|
|
{0,10,0,6/*this number makes it invalid; day numbers >5 not allowed*/,0,0,0,0},0,L"",
|
|
{0,4,0,6/*this number makes it invalid*/,0,0,0,0},0};
|
|
|
|
memcpy(&(pWC->ClientTimeZone), &InvalidTZ,
|
|
sizeof(TS_TIME_ZONE_INFORMATION));
|
|
}
|
|
|
|
pWC->clientDigProductId[0] = 0;
|
|
}
|
|
|
|
BOOLEAN gConsoleNeverConnected = TRUE;
|
|
|
|
NTSTATUS
|
|
WaitForConsoleConnectWorker( PWINSTATION pWinStation )
|
|
{
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
ULONG ReturnLength;
|
|
BYTE version;
|
|
ULONG Offset;
|
|
ICA_STACK_LAST_ERROR tdlasterror;
|
|
WINSTATION_APIMSG WMsg;
|
|
BOOLEAN rc;
|
|
NTSTATUS Status;
|
|
|
|
#define MODULE_SIZE 1024
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WaitForConnectWorker, LogonId=%d\n",
|
|
pWinStation->LogonId ));
|
|
|
|
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "TERMSRV: WaitForConsoleConnectWorker, LogonId=%d\n", pWinStation->LogonId ));
|
|
|
|
if (pWinStation->LogonId == 0) {
|
|
/*
|
|
* We need to acquire console lock. UnLock winstation first to avoid deadlock.
|
|
*/
|
|
|
|
UnlockWinStation( pWinStation );
|
|
ENTERCRIT( &ConsoleLock );
|
|
if (!RelockWinStation( pWinStation )) {
|
|
LEAVECRIT( &ConsoleLock );
|
|
return STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
}
|
|
|
|
/*
|
|
* You only go through this API once for console session.
|
|
*/
|
|
if (!gConsoleNeverConnected) {
|
|
LEAVECRIT( &ConsoleLock );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
if (!pWinStation->pSecurityDescriptor && pWinStation->LogonId) {
|
|
RtlAcquireResourceShared(&WinStationSecurityLock, TRUE);
|
|
Status = RtlCopySecurityDescriptor(DefaultConsoleSecurityDescriptor,
|
|
&(pWinStation->pSecurityDescriptor));
|
|
RtlReleaseResource(&WinStationSecurityLock);
|
|
}
|
|
|
|
|
|
|
|
// Read console config,
|
|
// For the session 0, this was already initalized in WinStationCreateWorker()
|
|
|
|
if (pWinStation->LogonId != 0) {
|
|
|
|
pWinStation->Config = gConsoleConfig;
|
|
|
|
// initalize client data, since there isn't any real rdp client sending anythhing to us
|
|
InitializeConsoleClientData( & pWinStation->Client );
|
|
}
|
|
|
|
|
|
/*
|
|
* We are going to wait for a connect (Idle)
|
|
*/
|
|
memset( &WMsg, 0, sizeof(WMsg) );
|
|
pWinStation->State = State_ConnectQuery;
|
|
NotifySystemEvent( WEVENT_STATECHANGE );
|
|
|
|
/*
|
|
* Initialize connect event to wait on
|
|
*/
|
|
InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
|
|
if (pWinStation->ConnectEvent == NULL) {
|
|
Status = NtCreateEvent( &pWinStation->ConnectEvent, EVENT_ALL_ACCESS, &ObjA,
|
|
NotificationEvent, FALSE );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WaitForConsoleConnectWorker, LogonId=%d, NtCreateEvent 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Duplicate the beep channel.
|
|
* This is one channel that both CSR and ICASRV have open.
|
|
*/
|
|
if (pWinStation->hIcaBeepChannel == NULL) {
|
|
Status = IcaChannelOpen( pWinStation->hIca,
|
|
Channel_Beep,
|
|
NULL,
|
|
&pWinStation->hIcaBeepChannel );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WaitForConsoleConnectWorker, LogonId=%d, IcaChannelOpen 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
pWinStation->hIcaBeepChannel,
|
|
pWinStation->WindowsSubSysProcess,
|
|
&WMsg.u.DoConnect.hIcaBeepChannel,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WaitForConsoleConnectWorker, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Duplicate the thinwire channel.
|
|
* This is one channel that both CSR and ICASRV have open.
|
|
*/
|
|
if (pWinStation->hIcaThinwireChannel == NULL) {
|
|
Status = IcaChannelOpen( pWinStation->hIca,
|
|
Channel_Virtual,
|
|
VIRTUAL_THINWIRE,
|
|
&pWinStation->hIcaThinwireChannel );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WaitForConsoleConnectWorker, LogonId=%d, IcaChannelOpen 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
pWinStation->hIcaThinwireChannel,
|
|
pWinStation->WindowsSubSysProcess,
|
|
&WMsg.u.DoConnect.hIcaThinwireChannel,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WaitForConsoleConnectWorker, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
Status = IcaChannelIoControl( pWinStation->hIcaThinwireChannel,
|
|
IOCTL_ICA_CHANNEL_ENABLE_SHADOW,
|
|
NULL, 0, NULL, 0, NULL );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
/*
|
|
* Video channel
|
|
*/
|
|
Status = WinStationOpenChannel( pWinStation->hIca,
|
|
pWinStation->WindowsSubSysProcess,
|
|
Channel_Video,
|
|
NULL,
|
|
&WMsg.u.DoConnect.hIcaVideoChannel );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WaitForConsoleConnectWorker, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Keyboard channel
|
|
*/
|
|
Status = WinStationOpenChannel( pWinStation->hIca,
|
|
pWinStation->WindowsSubSysProcess,
|
|
Channel_Keyboard,
|
|
NULL,
|
|
&WMsg.u.DoConnect.hIcaKeyboardChannel );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WaitForConsoleConnectWorker, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Mouse channel
|
|
*/
|
|
Status = WinStationOpenChannel( pWinStation->hIca,
|
|
pWinStation->WindowsSubSysProcess,
|
|
Channel_Mouse,
|
|
NULL,
|
|
&WMsg.u.DoConnect.hIcaMouseChannel );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WaitForConsoleConnectWorker, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Command channel
|
|
*/
|
|
Status = WinStationOpenChannel( pWinStation->hIca,
|
|
pWinStation->WindowsSubSysProcess,
|
|
Channel_Command,
|
|
NULL,
|
|
&WMsg.u.DoConnect.hIcaCommandChannel );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WaitForConsoleConnectWorker, LogonId=%d, NtDuplicateObject 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto done;
|
|
}
|
|
|
|
|
|
if (!pWinStation->LogonId) {
|
|
goto SkipClientData;
|
|
}
|
|
/*
|
|
* Secure any virtual channels
|
|
*/
|
|
VirtualChannelSecurity( pWinStation );
|
|
|
|
/*
|
|
* Tell Win32 about the connection
|
|
*/
|
|
|
|
WMsg.u.DoConnect.fEnableWindowsKey = (BOOLEAN) pWinStation->Client.fEnableWindowsKey;
|
|
SkipClientData:
|
|
WMsg.ApiNumber = SMWinStationDoConnect;
|
|
|
|
Status = SendWinStationCommand( pWinStation, &WMsg, 600 );
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: SMWinStationDoConnect %d Status=0x%x\n",
|
|
pWinStation->LogonId, Status));
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WaitForConsoleConnectWorker SMWinStationDoConnect failed Status= 0x%x\n", Status));
|
|
goto done;
|
|
} else {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "TERMSRV: WaitForConsoleConnectWorker SMWinStationDoConnect OK\n"));
|
|
pWinStation->StateFlags |= WSF_ST_CONNECTED_TO_CSRSS;
|
|
if (pWinStation->LogonId == 0) {
|
|
gConsoleNeverConnected=FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Indicate we're now connected. Only after succesful connection to Win32/CSR.
|
|
*/
|
|
pWinStation->NeverConnected = FALSE;
|
|
|
|
|
|
/*
|
|
* Check if we received a broken connection indication while connecting to to Win32/CSR.
|
|
*/
|
|
|
|
if (pWinStation->StateFlags & WSF_ST_BROKEN_CONNECTION) {
|
|
QueueWinStationReset(pWinStation->LogonId);
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
goto done;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set connect time and start disconnect timer
|
|
*/
|
|
NtQuerySystemTime( &pWinStation->ConnectTime );
|
|
|
|
/*
|
|
* Attempt to connect to the CdmRedirector
|
|
* for Client Drive Mapping
|
|
*
|
|
* NOTE: We still init the WinStation even if Client Drive
|
|
* mapping does not connect.
|
|
*/
|
|
if ( pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxCdmConnect ) {
|
|
Status = pWinStation->pWsx->pWsxCdmConnect( pWinStation->pWsxContext,
|
|
pWinStation->LogonId,
|
|
pWinStation->hIca );
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: CdmConnect %d Status=0x%x\n",
|
|
pWinStation->LogonId, Status));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/*
|
|
* Start logon timers
|
|
*/
|
|
StartLogonTimers( pWinStation );
|
|
|
|
pWinStation->State = State_Connected;
|
|
NotifySystemEvent( WEVENT_CONNECT | WEVENT_STATECHANGE );
|
|
|
|
Status = NotifyConnect(pWinStation, pWinStation->fOwnsConsoleTerminal);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
DBGPRINT(( "TERMSRV: NotifyConsoleConnect failed Status= 0x%x\n", Status));
|
|
}
|
|
|
|
|
|
done:
|
|
//
|
|
// Set the licensing policy for the console session. This must be done
|
|
// to prevent a weird state when a console session goes remote. This must
|
|
// Done weither wi faill or succeed. Licensing code assumes the policy is
|
|
// set.
|
|
//
|
|
if (pWinStation->LogonId == 0) {
|
|
LEAVECRIT( &ConsoleLock );
|
|
}
|
|
LCAssignPolicy(pWinStation);
|
|
|
|
return( Status );
|
|
}
|