/************************************************************************* * * connect.c * * This module contains routines for managing TerminalServer connections. * * Copyright 1998, Microsoft. * *************************************************************************/ /* * Includes */ #include #pragma hdrstop NTSTATUS _IcaCallStack( IN PICA_STACK pStack, IN ULONG ProcIndex, IN OUT PVOID pParms ); NTSTATUS IcaDeviceControlConnection ( IN PICA_CONNECTION pConnect, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS IcaCleanupConnection ( IN PICA_CONNECTION pConnect, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS IcaCloseConnection ( IN PICA_CONNECTION pConnect, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ); NTSTATUS IcaStartStopTrace( IN PICA_TRACE_INFO pTraceInfo, IN PICA_TRACE pTrace ); NTSTATUS IcaUnbindVirtualChannel( IN PICA_CONNECTION pConnect, IN PVIRTUALCHANNELNAME pVirtualName ); /* * Local procedure prototypes */ PICA_CONNECTION _IcaAllocateConnection( VOID ); VOID _IcaFreeConnection( PICA_CONNECTION ); /* * Dispatch table for ICA connection objects */ PICA_DISPATCH IcaConnectionDispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1] = { NULL, // IRP_MJ_CREATE NULL, // IRP_MJ_CREATE_NAMED_PIPE IcaCloseConnection, // IRP_MJ_CLOSE NULL, // IRP_MJ_READ NULL, // IRP_MJ_WRITE NULL, // IRP_MJ_QUERY_INFORMATION NULL, // IRP_MJ_SET_INFORMATION NULL, // IRP_MJ_QUERY_EA NULL, // IRP_MJ_SET_EA NULL, // IRP_MJ_FLUSH_BUFFERS NULL, // IRP_MJ_QUERY_VOLUME_INFORMATION NULL, // IRP_MJ_SET_VOLUME_INFORMATION NULL, // IRP_MJ_DIRECTORY_CONTROL NULL, // IRP_MJ_FILE_SYSTEM_CONTROL IcaDeviceControlConnection, // IRP_MJ_DEVICE_CONTROL NULL, // IRP_MJ_INTERNAL_DEVICE_CONTROL NULL, // IRP_MJ_SHUTDOWN NULL, // IRP_MJ_LOCK_CONTROL IcaCleanupConnection, // IRP_MJ_CLEANUP NULL, // IRP_MJ_CREATE_MAILSLOT NULL, // IRP_MJ_QUERY_SECURITY NULL, // IRP_MJ_SET_SECURITY NULL, // IRP_MJ_SET_POWER NULL, // IRP_MJ_QUERY_POWER }; extern PERESOURCE IcaTraceResource; // resource used to protect access to the code that start/stops the keep alive thread PERESOURCE g_pKeepAliveResource; extern NTSTATUS _IcaKeepAlive( IN BOOLEAN startKeepAliveThread, IN ULONG interval ); NTSTATUS IcaCreateConnection ( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This routine is called to create a new ICA_CONNECTION object. Arguments: Irp - Pointer to I/O request packet IrpSp - pointer to the stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PICA_CONNECTION pConnect; /* * Allocate a new ICA connect object */ pConnect = _IcaAllocateConnection(); if ( pConnect == NULL ) return( STATUS_INSUFFICIENT_RESOURCES ); /* * Save a pointer to the connection in the file object * so that we can find it in future calls. */ IrpSp->FileObject->FsContext = pConnect; IcaDereferenceConnection( pConnect ); return( STATUS_SUCCESS ); } NTSTATUS IcaDeviceControlConnection( IN PICA_CONNECTION pConnect, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) { ICA_TRACE LocalTrace; PICA_TRACE_BUFFER pTraceBuffer; ULONG code; SD_IOCTL SdIoctl; NTSTATUS Status; BOOLEAN bConnectionLocked = FALSE; BYTE *Buffer = NULL; PICA_KEEP_ALIVE pKeepAlive; /* * Extract the IOCTL control code and process the request. */ code = IrpSp->Parameters.DeviceIoControl.IoControlCode; #if DBG if ( code != IOCTL_ICA_SYSTEM_TRACE && code != IOCTL_ICA_TRACE ) { TRACE(( pConnect, TC_ICADD, TT_API1, "ICADD: IcaDeviceControlConnection, fc %d (enter)\n", (code & 0x3fff) >> 2 )); } #endif try { switch ( code ) { case IOCTL_ICA_SET_SYSTEM_TRACE : // This IOCTL should only be invoked if we are called from system process // If not, we deny the request if (!((BOOLEAN)IrpSp->FileObject->FsContext2)) { return (STATUS_ACCESS_DENIED); } if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ICA_TRACE) ) return( STATUS_BUFFER_TOO_SMALL ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead(IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(ICA_TRACE), sizeof(BYTE) ); } LocalTrace = *(PICA_TRACE)(IrpSp->Parameters.DeviceIoControl.Type3InputBuffer); KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite( IcaTraceResource, TRUE ); try { Status = IcaStartStopTrace( &G_TraceInfo, &LocalTrace ); } finally { ExReleaseResourceLite( IcaTraceResource ); KeLeaveCriticalRegion(); } break; case IOCTL_ICA_SET_TRACE : // This IOCTL should only be invoked if we are called from system process // If not, we deny the request if (!((BOOLEAN)IrpSp->FileObject->FsContext2)) { return (STATUS_ACCESS_DENIED); } if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ICA_TRACE) ) return( STATUS_BUFFER_TOO_SMALL ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead(IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof(BYTE) ); } LocalTrace = *(PICA_TRACE)(IrpSp->Parameters.DeviceIoControl.Type3InputBuffer); IcaLockConnection( pConnect ); bConnectionLocked = TRUE; Status = IcaStartStopTrace( &pConnect->TraceInfo, &LocalTrace ); if ( !IsListEmpty(&pConnect->StackHead)) { PICA_STACK pStack; pStack = CONTAINING_RECORD( pConnect->StackHead.Flink, ICA_STACK, StackEntry ); SdIoctl.IoControlCode = code; SdIoctl.InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; SdIoctl.InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; SdIoctl.OutputBuffer = NULL; SdIoctl.OutputBufferLength = 0; _IcaCallStack(pStack, SD$IOCTL, &SdIoctl); } IcaUnlockConnection( pConnect ); break; case IOCTL_ICA_SYSTEM_TRACE : if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < (ULONG)(FIELD_OFFSET(ICA_TRACE_BUFFER,Data[0])) ) return( STATUS_BUFFER_TOO_SMALL ); if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength > sizeof(ICA_TRACE_BUFFER) ) return( STATUS_INVALID_BUFFER_SIZE ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead(IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof(BYTE) ); } pTraceBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite( IcaTraceResource, TRUE ); try { IcaTraceFormat( &G_TraceInfo, pTraceBuffer->TraceClass, pTraceBuffer->TraceEnable, pTraceBuffer->Data ); } finally { ExReleaseResourceLite( IcaTraceResource ); KeLeaveCriticalRegion(); } Status = STATUS_SUCCESS; break; case IOCTL_ICA_TRACE : if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < (ULONG)(FIELD_OFFSET(ICA_TRACE_BUFFER,Data[0])) ) return( STATUS_BUFFER_TOO_SMALL ); if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength > sizeof(ICA_TRACE_BUFFER) ) return( STATUS_INVALID_BUFFER_SIZE ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead(IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof(BYTE) ); } pTraceBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; IcaLockConnection( pConnect ); bConnectionLocked=TRUE; IcaTraceFormat( &pConnect->TraceInfo, pTraceBuffer->TraceClass, pTraceBuffer->TraceEnable, pTraceBuffer->Data ); IcaUnlockConnection( pConnect ); Status = STATUS_SUCCESS; break; case IOCTL_ICA_UNBIND_VIRTUAL_CHANNEL : if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(VIRTUALCHANNELNAME) ) return( STATUS_BUFFER_TOO_SMALL ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead(IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(VIRTUALCHANNELNAME), sizeof(BYTE) ); } if (IrpSp->Parameters.DeviceIoControl.InputBufferLength) { Buffer = ICA_ALLOCATE_POOL( NonPagedPool, IrpSp->Parameters.DeviceIoControl.InputBufferLength); if (Buffer) { memcpy(Buffer, IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, IrpSp->Parameters.DeviceIoControl.InputBufferLength); } else { Status = STATUS_NO_MEMORY; break; } } IcaLockConnection( pConnect ); bConnectionLocked = TRUE; Status = IcaUnbindVirtualChannel( pConnect, (PVIRTUALCHANNELNAME)Buffer ); IcaUnlockConnection( pConnect ); break; case IOCTL_ICA_SET_SYSTEM_PARAMETERS: // Settings coming from TermSrv, copy to global variable. if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(TERMSRV_SYSTEM_PARAMS)) return(STATUS_BUFFER_TOO_SMALL); if (Irp->RequestorMode != KernelMode) ProbeForRead(IrpSp->Parameters.DeviceIoControl. Type3InputBuffer, sizeof(TERMSRV_SYSTEM_PARAMS), sizeof(BYTE)); SysParams = *(PTERMSRV_SYSTEM_PARAMS)(IrpSp->Parameters. DeviceIoControl.Type3InputBuffer); Status = STATUS_SUCCESS; break; case IOCTL_ICA_SYSTEM_KEEP_ALIVE: // This should only be invoked if we are called from system process // If not, we deny the request if (!((BOOLEAN)IrpSp->FileObject->FsContext2)) { return (STATUS_ACCESS_DENIED); } if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ICA_KEEP_ALIVE ) ) return( STATUS_BUFFER_TOO_SMALL ); if ( Irp->RequestorMode != KernelMode ) { ProbeForRead(IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(ICA_KEEP_ALIVE ), sizeof(BYTE) ); } pKeepAlive = (PICA_KEEP_ALIVE)(IrpSp->Parameters.DeviceIoControl.Type3InputBuffer); KeEnterCriticalRegion(); ExAcquireResourceExclusive( g_pKeepAliveResource, TRUE ); try { Status = _IcaKeepAlive( pKeepAlive->start, pKeepAlive->interval ); } finally { ExReleaseResource( g_pKeepAliveResource ); KeLeaveCriticalRegion(); } break; default: Status = STATUS_INVALID_DEVICE_REQUEST; break; } } except(EXCEPTION_EXECUTE_HANDLER){ Status = GetExceptionCode(); if (bConnectionLocked) { IcaUnlockConnection( pConnect ); } } if (Buffer) { ICA_FREE_POOL(Buffer); Buffer = NULL; } #if DBG if ( code != IOCTL_ICA_SYSTEM_TRACE && code != IOCTL_ICA_TRACE ) { TRACE(( pConnect, TC_ICADD, TT_API1, "ICADD: IcaDeviceControlConnection, fc %d, 0x%x\n", (code & 0x3fff) >> 2, Status )); } #endif return( Status ); } NTSTATUS IcaCleanupConnection( IN PICA_CONNECTION pConnect, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) { return( STATUS_SUCCESS ); } NTSTATUS IcaCloseConnection( IN PICA_CONNECTION pConnect, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) { /* * Remove the file object reference for this connection. * This will cause the connection to be deleted when all other * references (including stack/channel references) are gone. */ IcaDereferenceConnection( pConnect ); return( STATUS_SUCCESS ); } VOID IcaReferenceConnection( IN PICA_CONNECTION pConnect ) { ASSERT( pConnect->RefCount >= 0 ); /* * Increment the reference count */ if ( InterlockedIncrement(&pConnect->RefCount) <= 0 ) { ASSERT( FALSE ); } } VOID IcaDereferenceConnection( IN PICA_CONNECTION pConnect ) { ASSERT( pConnect->RefCount > 0 ); /* * Decrement the reference count; if it is 0, free the connection. */ if ( InterlockedDecrement( &pConnect->RefCount) == 0 ) { _IcaFreeConnection( pConnect ); } } PICA_CONNECTION _IcaAllocateConnection( VOID ) { PICA_CONNECTION pConnect; NTSTATUS Status; pConnect = ICA_ALLOCATE_POOL( NonPagedPool, sizeof(*pConnect) ); if ( pConnect == NULL ) return NULL; RtlZeroMemory( pConnect, sizeof(*pConnect) ); /* * Initialize the reference count to 2, * one for the caller's reference, one for the file object reference. */ pConnect->RefCount = 2; /* * Initialize the rest of the connect object */ pConnect->Header.Type = IcaType_Connection; pConnect->Header.pDispatchTable = IcaConnectionDispatchTable; ExInitializeResourceLite( &pConnect->Resource ); ExInitializeResourceLite( &pConnect->ChannelTableLock ); InitializeListHead( &pConnect->StackHead ); InitializeListHead( &pConnect->ChannelHead ); InitializeListHead( &pConnect->VcBindHead ); return( pConnect ); } VOID _IcaFreeConnection( PICA_CONNECTION pConnect ) { ICA_TRACE TraceControl; PICA_CHANNEL pChannel; PLIST_ENTRY Head; ASSERT( pConnect->RefCount == 0 ); ASSERT( IsListEmpty( &pConnect->StackHead ) ); ASSERT( IsListEmpty( &pConnect->ChannelHead ) ); ASSERT( IsListEmpty( &pConnect->VcBindHead ) ); ASSERT( !ExIsResourceAcquiredExclusiveLite( &pConnect->Resource ) ); TRACE(( pConnect, TC_ICADD, TT_API2, "ICADD: _IcaFreeConnection: %x\n", pConnect )); /* * Close trace file, if any */ RtlZeroMemory( &TraceControl, sizeof(TraceControl) ); (void) IcaStartStopTrace( &pConnect->TraceInfo, &TraceControl ); ExDeleteResourceLite( &pConnect->Resource ); ExDeleteResourceLite( &pConnect->ChannelTableLock ); ICA_FREE_POOL( pConnect ); }