Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2656 lines
68 KiB

/*++
Copyright (c) 1998-2000 Microsoft Corporation
Module Name:
rdpdr.cpp
Abstract:
This module implements the driver initialization for the RDP redirector,
and the dispatch routines for the master device object. The master
device object mostly ignores real I/O operations
Environment:
Kernel mode
--*/
#include "precomp.hxx"
#define TRC_FILE "rdpdr"
#include "trc.h"
#include "ntddmup.h"
#include "TSQPublic.h"
HANDLE DrSystemProcessId;
PSECURITY_DESCRIPTOR DrAdminSecurityDescriptor = NULL;
ULONG DrSecurityDescriptorLength = 0;
extern ULONG DebugBreakOnEntry;
//
// Default USBMON Port Write Size. Need to keep it under 64k for
// 16-bit clients ... otherwise, the go off the end of a segment.
//
ULONG PrintPortWriteSize;
ULONG PrintPortWriteSizeDefault = 63000; // bytes
//
// Maximum numer of TS worker threads
//
#define MAX_WORKER_THREADS_COUNT 5
ULONG MaxWorkerThreadsDefault = MAX_WORKER_THREADS_COUNT;
ULONG MaxWorkerThreads = MAX_WORKER_THREADS_COUNT;
// The TS Worker Queue pointer
PVOID RDPDR_TsQueue = NULL;
//
// Configure Devices to send IO packets to client at low priority.
//
ULONG DeviceLowPrioSendFlags;
ULONG DeviceLowPrioSendFlagsDefault = DEVICE_LOWPRIOSEND_PRINTERS;
extern "C" BOOLEAN RxForceQFIPassThrough;
NTSTATUS DrCreateSCardDevice(SmartPtr<DrSession> &Session, PV_NET_ROOT pVNetRoot,
SmartPtr<DrDevice> &Device);
//
// This is the minirdr dispatch table. It is initialized by DrInitializeTables.
// This table will be used by the wrapper to call into this minirdr
//
struct _MINIRDR_DISPATCH DrDispatch;
#if DBG
UCHAR IrpNames[IRP_MJ_MAXIMUM_FUNCTION + 1][40] = {
"IRP_MJ_CREATE ",
"IRP_MJ_CREATE_NAMED_PIPE ",
"IRP_MJ_CLOSE ",
"IRP_MJ_READ ",
"IRP_MJ_WRITE ",
"IRP_MJ_QUERY_INFORMATION ",
"IRP_MJ_SET_INFORMATION ",
"IRP_MJ_QUERY_EA ",
"IRP_MJ_SET_EA ",
"IRP_MJ_FLUSH_BUFFERS ",
"IRP_MJ_QUERY_VOLUME_INFORMATION",
"IRP_MJ_SET_VOLUME_INFORMATION ",
"IRP_MJ_DIRECTORY_CONTROL ",
"IRP_MJ_FILE_SYSTEM_CONTROL ",
"IRP_MJ_DEVICE_CONTROL ",
"IRP_MJ_INTERNAL_DEVICE_CONTROL ",
"IRP_MJ_SHUTDOWN ",
"IRP_MJ_LOCK_CONTROL ",
"IRP_MJ_CLEANUP ",
"IRP_MJ_CREATE_MAILSLOT ",
"IRP_MJ_QUERY_SECURITY ",
"IRP_MJ_SET_SECURITY ",
"IRP_MJ_POWER ",
"IRP_MJ_SYSTEM_CONTROL ",
"IRP_MJ_DEVICE_CHANGE ",
"IRP_MJ_QUERY_QUOTA ",
"IRP_MJ_SET_QUOTA ",
"IRP_MJ_PNP "
};
#endif // DBG
//
// Pointer to the device Object for this minirdr. Since the device object is created
// by the wrapper when this minirdr registers, this pointer is initialized in the
// DriverEntry routine below (see RxRegisterMinirdr)
//
PRDBSS_DEVICE_OBJECT DrDeviceObject = NULL;
PRDBSS_DEVICE_OBJECT DrPortDeviceObject = NULL;
DrSessionManager *Sessions = NULL;
//
// A global spinlock
//
KSPIN_LOCK DrSpinLock;
KIRQL DrOldIrql;
//
// A global mutex
//
FAST_MUTEX DrMutex;
//
// Global Registry Path for RDPDR.SYS
//
UNICODE_STRING DrRegistryPath;
//
// The following enumerated values signify the current state of the minirdr
// initialization. With the aid of this state information, it is possible
// to determine which resources to deallocate, whether deallocation comes
// as a result of a normal stop/unload, or as the result of an exception
//
typedef enum tagDrInitStates {
DrUninitialized,
DrRegistered,
DrInitialized
} DrInitStates;
//
// function prototypes
//
extern "C" {
NTSTATUS
DrInitializeTables(
void
);
NTSTATUS
CreateAdminSecurityDescriptor(
VOID
);
NTSTATUS
BuildDeviceAcl(
OUT PACL *DeviceAcl
);
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
NTSTATUS
DrPeekDispatch (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
DrUninitialize(
IN PDRIVER_OBJECT DriverObject,
IN DrInitStates DrInitState
);
NTSTATUS
DrLoadRegistrySettings (
IN PCWSTR RegistryPath
);
NTSTATUS
DrStart(
PRX_CONTEXT RxContext,
IN OUT PRDBSS_DEVICE_OBJECT RxDeviceObject
);
NTSTATUS
DrStop(
PRX_CONTEXT RxContext,
IN OUT PRDBSS_DEVICE_OBJECT RxDeviceObject
);
NTSTATUS
DrDeallocateForFcb(
IN OUT PMRX_FCB pFcb
);
NTSTATUS
ObGetObjectSecurity(
IN PVOID Object,
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
OUT PBOOLEAN MemoryAllocated
);
VOID
ObReleaseObjectSecurity(
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN BOOLEAN MemoryAllocated
);
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
};
BOOL GetDeviceFromRxContext(PRX_CONTEXT RxContext, SmartPtr<DrDevice> &Device);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#endif
WCHAR DrDriverName[] = RDPDR_DEVICE_NAME_U;
WCHAR DrPortDriverName[] = RDPDR_PORT_DEVICE_NAME_U;
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This is the initialization routine for the RDP mini redirector
Arguments:
DriverObject - Pointer to driver object created by the system.
Return Value:
RXSTATUS - The function value is the final status from the initialization
operation.
--*/
{
NTSTATUS Status;
UNICODE_STRING RdpDrName;
UNICODE_STRING RdpDrPortName;
PDEVICE_OBJECT RdpDrDevice;
DrInitStates DrInitState = DrUninitialized;
PRX_CONTEXT RxContext;
PWCHAR path;
BEGIN_FN("DriverEntry");
#ifdef MONOLITHIC_MINIRDR
Status = RxDriverEntry(DriverObject, RegistryPath);
TRC_NRM((TB, "BackFromInitWrapper %08lx", Status));
if (!NT_SUCCESS(Status)) {
TRC_ERR((TB, "Wrapper failed to initialize. "
"Status = %08lx", Status));
DbgPrint("rdpdr.sys erroring out (#1)\n");
DbgBreakPoint();
return Status;
}
#endif
//
// Copy the registry path for RDPDR.SYS.
//
path = (PWCHAR)new(NonPagedPool) WCHAR[RegistryPath->Length + 1];
if (!path) {
TRC_ERR((TB, "DR:Failed to allocate registry path %Wz",
RegistryPath));
DbgPrint("rdpdr.sys erroring out (#2)\n");
DbgBreakPoint();
Status = STATUS_INSUFFICIENT_RESOURCES;
return (Status);
}
RtlZeroMemory(path, RegistryPath->Length+sizeof(WCHAR));
RtlMoveMemory(path, RegistryPath->Buffer, RegistryPath->Length);
DrRegistryPath.Length = RegistryPath->Length;
DrRegistryPath.MaximumLength = RegistryPath->Length+sizeof(WCHAR);
DrRegistryPath.Buffer = path;
//
// Load registry settings.
//
DrLoadRegistrySettings(path);
#if DBG
if (DebugBreakOnEntry) {
DbgBreakPoint();
}
#endif
CodePageConversionInitialize();
// Initialize the client list
KeInitializeSpinLock(&DrSpinLock);
// Initialize the mutex object for device I/O transaction exchange
ExInitializeFastMutex(&DrMutex);
if (InitializeKernelUtilities()) {
Status = STATUS_SUCCESS;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
DbgPrint("rdpdr.sys erroring out (#3)\n");
DbgBreakPoint();
}
if (NT_SUCCESS(Status)) {
Sessions = new(NonPagedPool) DrSessionManager;
if (Sessions != NULL) {
Status = STATUS_SUCCESS;
TRC_NRM((TB, "Created DrSessionManager"));
} else {
TRC_ERR((TB, "Unable to create DrSessionManager"));
Status = STATUS_INSUFFICIENT_RESOURCES;
DbgPrint("rdpdr.sys erroring out (#4)\n");
DbgBreakPoint();
}
}
if (NT_SUCCESS(Status)) {
RtlInitUnicodeString(&RdpDrPortName, DrPortDriverName);
// Create the port device object.
Status = IoCreateDevice(DriverObject,
0,
&RdpDrPortName,
FILE_DEVICE_NETWORK_REDIRECTOR,
0,
FALSE,
(PDEVICE_OBJECT *)(&DrPortDeviceObject));
}
else {
TRC_ERR((TB, "IoCreateDevice failed: %08lx", Status ));
DbgPrint("rdpdr.sys erroring out (#5)\n");
DbgBreakPoint();
return Status;
}
if (NT_SUCCESS(Status)) {
//
// Register the RdpDr with the connection engine. Registration
// makes the connection engine aware of the device name, driver
// object, and other characteristics. If registration is successful,
// a new device object is returned
//
// The name of the device is L"\\Device\\RdpDr"
//
RtlInitUnicodeString(&RdpDrName, DrDriverName);
TRC_DBG((TB, "Registering minirdr"));
Status = RxRegisterMinirdr(
&DrDeviceObject, // where the new device object goes
DriverObject, // the Driver Object to register
&DrDispatch, // the dispatch table for this driver
RX_REGISTERMINI_FLAG_DONT_PROVIDE_MAILSLOTS,
&RdpDrName, // the device name for this minirdr
0, // IN ULONG DeviceExtensionSize,
FILE_DEVICE_NETWORK_FILE_SYSTEM, // In DEVICE_TYPE DeviceType
0 // IN ULONG DeviceCharacteristics
);
}
if (NT_SUCCESS(Status)) {
PSECURITY_DESCRIPTOR RdpDrSD = NULL;
BOOLEAN memoryAllocated = FALSE;
TRC_NRM((TB, "RxRegisterMinirdr succeeded."));
//
// Get the SD for the rdpdr device object.
// Apply the same SD to the rdp port device object.
//
if (NT_SUCCESS(ObGetObjectSecurity(DrDeviceObject,
&RdpDrSD,
&memoryAllocated))) {
if (!NT_SUCCESS(ObSetSecurityObjectByPointer((PDEVICE_OBJECT)DrPortDeviceObject,
DACL_SECURITY_INFORMATION,
RdpDrSD
))) {
//
// We will ignore the error.
//
TRC_ERR((TB, "ObSetSecurityObjectByPointer failed: 0x%08lx", Status ));
}
ObReleaseObjectSecurity(RdpDrSD, memoryAllocated);
}
else {
//
// We will ignore the error. Just log the error
//
TRC_ERR((TB, "ObGetObjectSecurity failed: 0x%08lx", Status ));
}
//
// After this we can't just return, some uninitialization is
// needed if we fail or unload
//
DrInitState = DrRegistered;
Status = CreateAdminSecurityDescriptor();
} else {
TRC_ERR((TB, "RxRegisterMinirdr failed: %08lx", Status ));
DbgPrint("rdpdr.sys erroring out (#6)\n");
DbgBreakPoint();
if (DrPortDeviceObject) {
IoDeleteDevice((PDEVICE_OBJECT) DrPortDeviceObject);
DrPortDeviceObject = NULL;
}
return Status;
}
if (NT_SUCCESS(Status)) {
//
// Build the dispatch tables for the minirdr
//
Status = DrInitializeTables();
} else {
TRC_ERR((TB, "CreateAdminSecurityDescriptor failed: 0x%08lx", Status ));
DbgPrint("rdpdr.sys erroring out (#7)\n");
DbgBreakPoint();
}
//
// Initialize our TS worker queue module.
//
TRC_NRM((TB, "RDPDR: Initialize TS Worker Queue"));
RDPDR_TsQueue = TSInitQueue( TSQUEUE_OWN_THREAD,
MaxWorkerThreads,
(PDEVICE_OBJECT)DrDeviceObject );
if ( RDPDR_TsQueue == NULL) {
TRC_ERR((TB, "RDPDR: Failed to initialize the TS Queue"));
DbgPrint("rdpdr.sys erroring out (#8)\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if (NT_SUCCESS(Status)) {
//
// Setup Unload Routine
//
DriverObject->DriverUnload = DrUnload;
//
// Set up the PnP AddDevice entry point.
//
DriverObject->DriverExtension->AddDevice = RDPDRPNP_PnPAddDevice;
//
// setup the DriverDispatch for people who come in here directly
// ....like the browser
//
{
ULONG i;
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = (PDRIVER_DISPATCH)DrPeekDispatch;
}
}
DrSystemProcessId = PsGetCurrentProcessId();
} else {
DbgPrint("rdpdr.sys erroring out (#9)\n");
DbgBreakPoint();
DrUninitialize(DriverObject, DrInitState);
}
return Status;
}
VOID
DrUninitialize(
IN PDRIVER_OBJECT DriverObject,
IN DrInitStates DrInitState
)
/*++
Routine Description:
This routine does the common uninit work
Arguments:
DrInitState - tells how far we got into the intialization
Return Value:
None
--*/
{
PRX_CONTEXT RxContext;
NTSTATUS Status;
BEGIN_FN("DrUninitialize");
PAGED_CODE();
RxContext = RxCreateRxContext(
NULL,
DrDeviceObject,
RX_CONTEXT_FLAG_IN_FSP);
if (RxContext != NULL) {
Status = RxStopMinirdr(
RxContext,
&RxContext->PostRequest);
RxDereferenceAndDeleteRxContext(RxContext);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
CodePageConversionCleanup();
if (DrAdminSecurityDescriptor) {
delete DrAdminSecurityDescriptor;
DrAdminSecurityDescriptor = NULL;
DrSecurityDescriptorLength = 0;
}
if (Sessions != NULL) {
delete Sessions;
Sessions = NULL;
}
if (DrRegistryPath.Buffer != NULL) {
delete DrRegistryPath.Buffer;
DrRegistryPath.Buffer = NULL;
}
//
// Delete the TS Queue
//
if ( RDPDR_TsQueue != NULL) {
if (TSDeleteQueue( RDPDR_TsQueue ) != STATUS_SUCCESS) {
TRC_ERR((TB, "RDPDR: TsDeleteQueue Failed"));
}
}
UninitializeKernelUtilities();
switch (DrInitState) {
case DrInitialized:
#ifdef MONOLITHIC_MINIRDR
RxUnload(DriverObject);
#endif
case DrRegistered:
RxUnregisterMinirdr(DrDeviceObject);
}
if (DrPortDeviceObject) {
IoDeleteDevice((PDEVICE_OBJECT) DrPortDeviceObject);
DrPortDeviceObject = NULL;
}
}
VOID
DrUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This is the dispatch routine for Unload.
Arguments:
DriverObject - Pointer to the driver object controling all of the
devices.
Return Value:
None.
--*/
{
BEGIN_FN("DrUnload");
PAGED_CODE();
TRC_NRM((TB, "DriverObject =%p", DriverObject));
DrUninitialize(DriverObject, DrInitialized);
TRC_NRM((TB, "MRxIfsUnload exit: DriverObject =%p",
DriverObject));
}
NTSTATUS
DrFlush(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for flush operations.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
BEGIN_FN("DrFlush");
return STATUS_SUCCESS;
}
NTSTATUS
DrWrite(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for write operations.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrWrite");
TRC_NRM((TB, "DrWrite"));
GetDeviceFromRxContext(RxContext, Device);
return Device->Write(RxContext);
}
NTSTATUS
DrRead(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for read operations.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrRead");
TRC_NRM((TB, "DrRead"));
GetDeviceFromRxContext(RxContext, Device);
return Device->Read(RxContext);
}
NTSTATUS
DrIoControl(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for IoControl operations.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrIoControl");
TRC_NRM((TB, "DrIoControl"));
if (GetDeviceFromRxContext(RxContext, Device))
return Device->IoControl(RxContext);
else
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
DrShouldTryToCollapseThisOpen(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This routine determines if the mini knows of a good reason not
to try collapsing on this open.
Arguments:
RxContext - Context for the operation
Return Value:
NTSTATUS - The return status for the operation
SUCCESS --> okay to try collapse
other (MORE_PROCESSING_REQUIRED) --> dont collapse
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
RxCaptureFcb;
BEGIN_FN("DrShouldTryToCollapseThisOpen");
PAGED_CODE();
TRC_NRM((TB, "DrShouldTryToCollapseThisOpen not implemented"));
return STATUS_NOT_IMPLEMENTED;
}
ULONG
DrExtendForNonCache(
IN OUT struct _RX_CONTEXT * RxContext,
IN PLARGE_INTEGER pNewFileSize,
OUT PLARGE_INTEGER pNewAllocationSize
)
/*++
Routine Description:
This routine handles network requests to extend the file for noncached IO. since the write
itself will extend the file, we can pretty much just get out quickly.
Arguments:
RxContext - the RDBSS context
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
pNewAllocationSize->QuadPart = pNewFileSize->QuadPart;
return (ULONG)Status;
}
NTSTATUS
DrTruncate(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This routine determines Truncate operation
Arguments:
RxContext - Context for the operation
Return Value:
NTSTATUS - The return status for the operation
--*/
{
BEGIN_FN("DrTruncate");
TRC_ERR((TB, "DrTruncate not implemented"));
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS DrCreate(IN OUT PRX_CONTEXT RxContext)
/*++
Routine Description:
Opens a file (or device) across the network
Arguments:
RxContext - Context for the operation
Return Value:
Could return status success, cancelled, or pending.
--*/
{
NTSTATUS Status;
RxCaptureFcb;
PMRX_SRV_CALL SrvCall = RxContext->Create.pSrvCall;
PMRX_NET_ROOT NetRoot = RxContext->Create.pNetRoot;
PMRX_V_NET_ROOT VNetRoot = RxContext->Create.pVNetRoot;
SmartPtr<DrSession> Session;
DrDevice *pDevice;
SmartPtr<DrDevice> Device, DeviceNew;
BEGIN_FN("DrCreate");
TRC_NRM((TB, "DrCreate"));
//
// Make sure the device is still enabled, Protect the
// VNetRoot Context (the DeviceEntry) with the device list
// SpinLock because we may change it
//
DrAcquireSpinLock();
Device = (DrDevice *)VNetRoot->Context;
ASSERT(Device != NULL);
DrReleaseSpinLock();
//
// Make sure it's okay to access the Client at this time
// This is an optimization, we don't need to acquire the spin lock,
// because it is okay if we're not, we'll just catch it later
//
Session = Device->GetSession();
ASSERT(Session != NULL);
if (!Session->IsConnected() && (Device->GetDeviceType() != RDPDR_DTYP_SMARTCARD)) {
TRC_ALT((TB, "Tried to open client device while not "
"connected. State: %ld", Session->GetState()));
return STATUS_DEVICE_NOT_CONNECTED;
}
//
// We leave the SpinLock after we get our reference to the device. It could
// change while we're gone, but since everything is reference counted it
// is safe to put the correct pointer in later
//
if (!Device->IsAvailable()) {
TRC_ALT((TB, "Tried to open client device which is not "
"available. "));
if (Device->GetDeviceType() == RDPDR_DTYP_SMARTCARD &&
!Session->FindDeviceByDosName((UCHAR *)DR_SMARTCARD_SUBSYSTEM,
DeviceNew, TRUE)) {
Status = DrCreateSCardDevice(Session, NULL, DeviceNew);
if (Status != STATUS_SUCCESS) {
return STATUS_DEVICE_NOT_CONNECTED;
}
}
if (Device->GetDeviceType() == RDPDR_DTYP_SMARTCARD ||
Session->FindDeviceById(Device->GetDeviceId(), DeviceNew, TRUE)) {
//
// There's a new DeviceEntry for this device. Replace the old
// one in the VNetRoot with this one. We also need an extra
// reference to stick in the fobx so we can track whether
// this particular open is using an old DeviceEntry or a new
// one
//
// Put it in the netroot, safely swapping in and manually
// bumping the reference count up going in and down going out
DeviceNew->AddRef();
DrAcquireSpinLock();
pDevice = (DrDevice *)VNetRoot->Context;
VNetRoot->Context = (DrDevice *)DeviceNew;
DrReleaseSpinLock();
#if DBG
pDevice->_VNetRoot = NULL;
#endif
pDevice->Release();
pDevice = NULL;
Device = DeviceNew;
} else {
//
// The device is disabled, but we didn't find a shiny new
// version with which to replace it. Leave the icky old disabled
// one there so we know what to look for later, and return the
// device not connected error.
//
return STATUS_DEVICE_NOT_CONNECTED;
}
}
return Device->Create(RxContext);
}
BOOL GetDeviceFromRxContext(PRX_CONTEXT RxContext, SmartPtr<DrDevice> &Device)
{
BOOL rc = FALSE;
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PMRX_V_NET_ROOT VNetRoot;
BEGIN_FN("GetDeviceFromRxContext");
if (SrvOpen == NULL) {
goto Exit;
}
VNetRoot= SrvOpen->pVNetRoot;
if (VNetRoot == NULL) {
goto Exit;
}
DrAcquireSpinLock();
Device = (DrDevice *)VNetRoot->Context;
DrReleaseSpinLock();
ASSERT(Device != NULL);
rc = TRUE;
Exit:
return rc;
}
NTSTATUS DrCloseSrvOpen(IN OUT PRX_CONTEXT RxContext)
/*++
Routine Description:
This is the dispatch routine for close operations.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrCloseSrvOpen");
TRC_NRM((TB, "DrCloseSrvOpen"));
GetDeviceFromRxContext(RxContext, Device);
return Device->Close(RxContext);
}
NTSTATUS DrCleanupFobx(IN OUT PRX_CONTEXT RxContext)
/*++
Routine Description:
This is the dispatch routine for cleaning up Fobx.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
RxCaptureFobx;
BEGIN_FN("DrCleanupFobx");
TRC_NRM((TB, "DrCleanupFobx"));
return STATUS_SUCCESS;
}
NTSTATUS DrCleanup(IN OUT PRX_CONTEXT RxContext)
/*++
Routine Description:
This is the dispatch routine for cleanup operations.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrCleanup");
TRC_NRM((TB, "DrCleanup"));
GetDeviceFromRxContext(RxContext, Device);
return Device->Cleanup(RxContext);
}
NTSTATUS
DrQueryDirectory(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for Query Direcotry information.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrQueryDirecotry");
TRC_NRM((TB, "DrQueryDirectory"));
GetDeviceFromRxContext(RxContext, Device);
return Device->QueryDirectory(RxContext);
}
NTSTATUS
DrQueryVolumeInfo(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for Query Volume Information.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrQueryVolumeInfo");
TRC_NRM((TB, "DrQueryVolumeInfo"));
GetDeviceFromRxContext(RxContext, Device);
return Device->QueryVolumeInfo(RxContext);
}
NTSTATUS
DrSetVolumeInfo(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for Set Volume Information.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrSetVolumeInfo");
TRC_NRM((TB, "DrSetVolumeInfo"));
GetDeviceFromRxContext(RxContext, Device);
return Device->SetVolumeInfo(RxContext);
}
NTSTATUS
DrQuerySdInfo(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for Query Security Information.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrQuerySdInfo");
TRC_NRM((TB, "DrQuerySdInfo"));
GetDeviceFromRxContext(RxContext, Device);
return Device->QuerySdInfo(RxContext);
}
NTSTATUS
DrSetSdInfo(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for Set Security Information.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrSetSdInfo");
TRC_NRM((TB, "DrSetSdInfo"));
GetDeviceFromRxContext(RxContext, Device);
return Device->SetSdInfo(RxContext);
}
NTSTATUS
DrQueryFileInfo(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for Query File Information.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrQueryFileInfo");
TRC_NRM((TB, "DrQueryFileInfo"));
GetDeviceFromRxContext(RxContext, Device);
return Device->QueryFileInfo(RxContext);
}
NTSTATUS
DrSetFileInfo(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for SetFileInformation.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrSetFileInfo");
TRC_NRM((TB, "DrSetFileInfo"));
GetDeviceFromRxContext(RxContext, Device);
return Device->SetFileInfo(RxContext);
}
NTSTATUS
DrSetFileInfoAtCleanUp(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for SetFileInformationAtCleanUp.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP for the current request
Return Value:
Could return status success, cancelled, or pending.
--*/
{
BEGIN_FN("DrSetFileInfoAtCleanUp");
TRC_NRM((TB, "DrSetFileInfoAtCleanUp"));
return STATUS_SUCCESS;
}
NTSTATUS
DrLocks(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for file locking.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrLocks");
TRC_NRM((TB, "DrLocks"));
GetDeviceFromRxContext(RxContext, Device);
return Device->Locks(RxContext);
}
NTSTATUS
DrIsLockRealizable(
IN OUT PMRX_FCB pFcb,
IN PLARGE_INTEGER ByteOffset,
IN PLARGE_INTEGER Length,
IN ULONG LowIoLockFlags
)
/*++
Routine Description:
This is the dispatch routine for IsLockRealizable.
Arguments:
Return Value:
Could return status success, cancelled, or pending.
--*/
{
BEGIN_FN("DrIsLockRealizable");
TRC_NRM((TB, "DrIsLockRealizable"));
//
// TODO: We do not support share locks for win9x clients
// Can we just return success here and then fail on the
// client share lock function?
//
#if 0
if (!FlagOn(LowIoLockFlags,LOWIO_LOCKSFLAG_EXCLUSIVELOCK)) {
return STATUS_NOT_SUPPORTED;
}
#endif
return STATUS_SUCCESS;
}
NTSTATUS
DrIsValidDirectory(
IN OUT PRX_CONTEXT RxContext,
IN PUNICODE_STRING DirectoryName
)
/*++
Routine Description:
This is the dispatch routine for IsValidDirectory.
Arguments:
RxContext - RDBSS context structure for our mini-redir
DirectoryName - name of directory to verify its validity
Return Value:
Could return status success, cancelled, or pending.
--*/
{
BEGIN_FN("DrIsValidDirectory");
TRC_NRM((TB, "DrIsValidDirectory"));
//
// TODO: Always return success for now. Need to verify later
//
return STATUS_SUCCESS;
}
NTSTATUS
DrNotifyChangeDirectory(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This is the dispatch routine for DrNotifyChangeDirectory.
Arguments:
RxContext - RDBSS context structure for our mini-redir
Return Value:
Could return status success, cancelled, or pending.
--*/
{
SmartPtr<DrDevice> Device;
BEGIN_FN("DrNotifyChangeDirectory");
TRC_NRM((TB, "DrNotifyChangeDirectory"));
GetDeviceFromRxContext(RxContext, Device);
return Device->NotifyChangeDirectory(RxContext);
}
NTSTATUS
DrInitializeTables(
void
)
/*++
Routine Description:
This routine sets up the rdp redirector dispatch vector and also calls
to initialize any other tables needed.
Return Value:
NTSTATUS - The return status for the operation
--*/
{
BEGIN_FN("DrInitializeTables");
//
// Build the local minirdr dispatch table and initialize
//
ZeroAndInitializeNodeType(&DrDispatch, RDBSS_NTC_MINIRDR_DISPATCH,
sizeof(MINIRDR_DISPATCH));
//
// redirector extension sizes and allocation policies.
//
// REVIEW: wtf?
DrDispatch.MRxFlags = (RDBSS_MANAGE_FCB_EXTENSION |
RDBSS_MANAGE_SRV_OPEN_EXTENSION |
RDBSS_MANAGE_FOBX_EXTENSION);
DrDispatch.MRxSrvCallSize = 0;
DrDispatch.MRxNetRootSize = 0;
DrDispatch.MRxVNetRootSize = 0;
DrDispatch.MRxFcbSize = 0; // sizeof(MRX_SMB_FCB);
DrDispatch.MRxSrvOpenSize = 0; // sizeof(MRX_SMB_SRV_OPEN);
DrDispatch.MRxFobxSize = 0; // sizeof(MRX_SMB_FOBX);
// Transport update handler
// REVIEW: How do we indicate we have our own dedicated transport?
//MRxIfsDispatch.MRxTransportUpdateHandler = MRxIfsTransportUpdateHandler;
// Mini redirector cancel routine ..
DrDispatch.MRxCancel = NULL;
//
// Mini redirector Start/Stop. Each mini-rdr can be started or stopped
// while the others continue to operate.
//
DrDispatch.MRxStart = DrStart;
DrDispatch.MRxStop = DrStop;
DrDispatch.MRxDevFcbXXXControlFile = DrDevFcbXXXControlFile;
//
// Mini redirector name resolution.
//
DrDispatch.MRxCreateSrvCall = DrCreateSrvCall;
DrDispatch.MRxSrvCallWinnerNotify = DrSrvCallWinnerNotify;
DrDispatch.MRxCreateVNetRoot = DrCreateVNetRoot;
DrDispatch.MRxUpdateNetRootState = DrUpdateNetRootState;
DrDispatch.MRxExtractNetRootName = DrExtractNetRootName;
DrDispatch.MRxFinalizeSrvCall = DrFinalizeSrvCall;
DrDispatch.MRxFinalizeNetRoot = DrFinalizeNetRoot;
DrDispatch.MRxFinalizeVNetRoot = DrFinalizeVNetRoot;
//
// File System Object Creation/Deletion.
//
DrDispatch.MRxCreate = DrCreate;
//
// TODO: Need to implement this for file system redirect caching
//
DrDispatch.MRxShouldTryToCollapseThisOpen = DrShouldTryToCollapseThisOpen;
//DrDispatch.MRxCollapseOpen = MRxIfsCollapseOpen;
//DrDispatch.MRxExtendForCache = MRxIfsExtendFile;
DrDispatch.MRxExtendForNonCache = DrExtendForNonCache;
DrDispatch.MRxTruncate = DrTruncate; //MRxIfsTruncate;
DrDispatch.MRxCleanupFobx = DrCleanupFobx;
DrDispatch.MRxCloseSrvOpen = DrCloseSrvOpen;
DrDispatch.MRxFlush = DrFlush;
DrDispatch.MRxForceClosed = DrForceClosed;
DrDispatch.MRxDeallocateForFcb = DrDeallocateForFcb;
DrDispatch.MRxDeallocateForFobx = DrDeallocateForFobx;
DrDispatch.MRxIsLockRealizable = DrIsLockRealizable;
//
// File System Objects query/Set
//
DrDispatch.MRxQueryDirectory = DrQueryDirectory; //MRxIfsQueryDirectory;
DrDispatch.MRxQueryVolumeInfo = DrQueryVolumeInfo; //MRxIfsQueryVolumeInformation;
DrDispatch.MRxSetVolumeInfo = DrSetVolumeInfo; //MRxSmbSetVolumeInformation;
//DrDispatch.MRxQueryEaInfo = MRxIfsQueryEaInformation;
//DrDispatch.MRxSetEaInfo = MRxIfsSetEaInformation;
DrDispatch.MRxQuerySdInfo = DrQuerySdInfo; //MRxIfsQuerySecurityInformation;
DrDispatch.MRxSetSdInfo = DrSetSdInfo; //MRxIfsSetSecurityInformation;
//MRxSmbDispatch.MRxQueryQuotaInfo = MRxSmbQueryQuotaInformation;
//MRxSmbDispatch.MRxSetQuotaInfo = MRxSmbSetQuotaInformation;
DrDispatch.MRxQueryFileInfo = DrQueryFileInfo; //MRxIfsQueryFileInformation;
DrDispatch.MRxSetFileInfo = DrSetFileInfo; //MRxIfsSetFileInformation;
DrDispatch.MRxSetFileInfoAtCleanup = DrSetFileInfoAtCleanUp; //MRxIfsSetFileInformationAtCleanup;
DrDispatch.MRxIsValidDirectory = DrIsValidDirectory;
//
// Buffering state change
//
//DrDispatch.MRxComputeNewBufferingState = MRxIfsComputeNewBufferingState;
//
// File System Object I/O
//
DrDispatch.MRxLowIOSubmit[LOWIO_OP_READ] = DrRead;
DrDispatch.MRxLowIOSubmit[LOWIO_OP_WRITE] = DrWrite;
DrDispatch.MRxLowIOSubmit[LOWIO_OP_SHAREDLOCK] = DrLocks;
DrDispatch.MRxLowIOSubmit[LOWIO_OP_EXCLUSIVELOCK] = DrLocks;
DrDispatch.MRxLowIOSubmit[LOWIO_OP_UNLOCK] = DrLocks;
DrDispatch.MRxLowIOSubmit[LOWIO_OP_UNLOCK_MULTIPLE] = DrLocks;
DrDispatch.MRxLowIOSubmit[LOWIO_OP_FSCTL] = DrIoControl; //MRxIfsFsCtl;
DrDispatch.MRxLowIOSubmit[LOWIO_OP_IOCTL] = DrIoControl;
DrDispatch.MRxLowIOSubmit[LOWIO_OP_NOTIFY_CHANGE_DIRECTORY] = DrNotifyChangeDirectory; //MRxIfsNotifyChangeDirectory;
//
// Miscellanous - buffering
//
//DrDispatch.MRxCompleteBufferingStateChangeRequest = MRxIfsCompleteBufferingStateChangeRequest;
return STATUS_SUCCESS;
}
BOOLEAN
DrIsAdminIoRequest(
PIRP Irp,
PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
(Lifted from AFD - AfdPerformSecurityCheck)
Compares security context of the endpoint creator to that
of the administrator and local system.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
TRUE - the socket creator has admin or local system privilige
FALSE - the socket creator is just a plain user
--*/
{
BOOLEAN accessGranted;
PACCESS_STATE accessState;
PIO_SECURITY_CONTEXT securityContext;
PPRIVILEGE_SET privileges = NULL;
ACCESS_MASK grantedAccess;
PGENERIC_MAPPING GenericMapping;
ACCESS_MASK AccessMask = GENERIC_ALL;
NTSTATUS Status;
BEGIN_FN("DrIsAdminIoRequest");
ASSERT(Irp != NULL);
ASSERT(IrpSp != NULL);
ASSERT(IrpSp->MajorFunction == IRP_MJ_CREATE);
//
// Enable access to all the globally defined SIDs
//
GenericMapping = IoGetFileObjectGenericMapping();
RtlMapGenericMask( &AccessMask, GenericMapping );
securityContext = IrpSp->Parameters.Create.SecurityContext;
ASSERT(securityContext != NULL);
accessState = securityContext->AccessState;
SeLockSubjectContext(&accessState->SubjectSecurityContext);
TRC_ASSERT(DrAdminSecurityDescriptor != NULL,
(TB, "DrAdminSecurityDescriptor != NULL"));
accessGranted = SeAccessCheck(
DrAdminSecurityDescriptor,
&accessState->SubjectSecurityContext,
TRUE,
AccessMask,
0,
&privileges,
IoGetFileObjectGenericMapping(),
(KPROCESSOR_MODE)((IrpSp->Flags & SL_FORCE_ACCESS_CHECK)
? UserMode
: Irp->RequestorMode),
&grantedAccess,
&Status
);
if (privileges) {
(VOID) SeAppendPrivileges(
accessState,
privileges
);
SeFreePrivileges(privileges);
}
if (accessGranted) {
accessState->PreviouslyGrantedAccess |= grantedAccess;
accessState->RemainingDesiredAccess &= ~( grantedAccess | MAXIMUM_ALLOWED );
ASSERT (NT_SUCCESS (Status));
}
else {
ASSERT (!NT_SUCCESS (Status));
}
SeUnlockSubjectContext(&accessState->SubjectSecurityContext);
return accessGranted;
}
BOOLEAN
DrIsSystemProcessRequest(
PIRP Irp,
PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Checks to see if the IRP originated from a system process.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
TRUE if the IRP originated from a system process. FALSE, otherwise.
--*/
{
PACCESS_STATE accessState;
PIO_SECURITY_CONTEXT securityContext;
PACCESS_TOKEN accessToken;
PTOKEN_USER userId = NULL;
BOOLEAN result = FALSE;
NTSTATUS status = STATUS_SUCCESS;
PSID systemSid;
BEGIN_FN("DrIsSystemProcessRequest");
TRC_NRM((TB, "DrIsSystemProcessRequest called"));
ASSERT(Irp != NULL);
ASSERT(IrpSp != NULL);
ASSERT(IrpSp->MajorFunction == IRP_MJ_CREATE);
securityContext = IrpSp->Parameters.Create.SecurityContext;
ASSERT(securityContext != NULL);
//
// Get the well-known system SID.
//
systemSid = (PSID)new(PagedPool) BYTE[RtlLengthRequiredSid(1)];
if (systemSid) {
SID_IDENTIFIER_AUTHORITY identifierAuthority = SECURITY_NT_AUTHORITY;
*(RtlSubAuthoritySid(systemSid, 0)) = SECURITY_LOCAL_SYSTEM_RID;
status = RtlInitializeSid(systemSid, &identifierAuthority, (UCHAR)1);
}
else {
TRC_ERR((TB, "Can't allocate %ld bytes for system SID.",
RtlLengthRequiredSid(1)));
status = STATUS_INSUFFICIENT_RESOURCES;
}
//
// Get the non-impersonated, primary token for the IRP request.
//
accessState = securityContext->AccessState;
accessToken = accessState->SubjectSecurityContext.PrimaryToken;
//
// We got the system SID. Now compare the caller's SID.
//
if (NT_SUCCESS(status) && accessToken){
//
// Get the user ID associated with the primary token for the process
// that generated the IRP.
//
status = SeQueryInformationToken(
accessToken,
TokenUser,
(PVOID *)&userId
);
//
// Do the comparison.
//
if (NT_SUCCESS(status)) {
result = RtlEqualSid(systemSid, userId->User.Sid);
ExFreePool(userId);
}
else {
TRC_ERR((TB, "SeQueryInformationToken failed with %08X",
status));
}
}
else {
TRC_ERR((TB, "Failed to get system sid because of error %08X",
status));
}
if (systemSid) {
delete systemSid;
}
return result;
}
BOOL
DrQueryServerName(PUNICODE_STRING PathName)
/*++
Routine Description:
This routine check if the pathname belongs to our minirdr.
Arguments:
PathName: path name to check
Return Value:
TRUE - if the path is to our mini-rdr
FALSE - if the path not our mini-rdr
--*/
{
PWCHAR ServerName;
PWCHAR ServerNameEnd;
unsigned CompareLen; // in characters
unsigned PathNameLen; // in characters
BEGIN_FN("DrQueryServerName");
TRC_NRM((TB, "Got query path for file: %wZ", PathName));
//
// Make sure the server name we are comparing has at least length
// of the server name our rdpdr recongize
//
if (PathName->Length >= DRUNCSERVERNAME_U_LENGTH) {
ServerName = PathName->Buffer;
// bypass the first backslash
ServerName++;
PathNameLen = PathName->Length / sizeof(WCHAR) - 1;
// Find the next backslash
ServerNameEnd = ServerName;
while ((unsigned)(ServerNameEnd - ServerName) < PathNameLen) {
if (*ServerNameEnd == L'\\') {
break;
}
ServerNameEnd++;
}
CompareLen = (unsigned)(ServerNameEnd - ServerName);
//
// Determine if this is a server name belongs to our minirdr
//
if ( (CompareLen == DRUNCSERVERNAME_A_LENGTH - 1) &&
_wcsnicmp(ServerName, DRUNCSERVERNAME_U, CompareLen) == 0) {
TRC_NRM((TB, "Quick return that we know the name"));
return TRUE;
}
}
return FALSE;
}
NTSTATUS
DrPeekDispatch (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the driver dispatch for the rdpdr DRIVER object.
Arguments:
DeviceObject - Supplies the device object for the packet being processed.
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The status for the Irp
--*/
{
PIO_STACK_LOCATION IoStackLocation;
NTSTATUS Status;
ULONG irpSessionId;
BEGIN_FN("DrPeekDispatch ");
IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
#if DBG
TRC_NRM((TB, "Irp: %s", IrpNames[IoStackLocation->MajorFunction]));
switch (IoStackLocation->MajorFunction) {
case IRP_MJ_CREATE:
TRC_NRM((TB, "CreateFile name: %wZ",
&IoStackLocation->FileObject->FileName));
break;
case IRP_MJ_WRITE:
TRC_NRM((TB, "IRP_MJ_WRITE"));
break;
}
#endif // DBG
//
// For Read and Write IRP, we disable caching because the client
// is an usermode app and can't synchronize with the server cache
// manager
//
if (IoStackLocation->MajorFunction == IRP_MJ_READ ||
IoStackLocation->MajorFunction == IRP_MJ_WRITE) {
Irp->Flags |= IRP_NOCACHE;
}
//
// We need to return immediately for redir_query_path
//
if (IoStackLocation->MajorFunction == IRP_MJ_DEVICE_CONTROL &&
IoStackLocation->Parameters.DeviceIoControl.IoControlCode == IOCTL_REDIR_QUERY_PATH &&
Irp->RequestorMode == KernelMode) {
QUERY_PATH_REQUEST *qpRequest = (QUERY_PATH_REQUEST *)
IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer;
if (qpRequest != NULL) {
UNICODE_STRING PathName;
PathName.Length = (USHORT)qpRequest->PathNameLength;
PathName.Buffer= qpRequest->FilePathName;
if (DrQueryServerName(&PathName)) {
//
// We must now complete the IRP
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
}
}
//
// We want to bypass filesize caching
//
RxForceQFIPassThrough = TRUE;
// If it's not the IFS DO, then let RDPDYN have a shot at it. Eventually,
// it would be nice to confirm that it is for RDPDYN. We can work this
// out later ...
if (DeviceObject != (PDEVICE_OBJECT)DrDeviceObject &&
DeviceObject != (PDEVICE_OBJECT) DrPortDeviceObject) {
TRC_NRM((TB, "Pass IRP on to RDPDYN_Dispatch"));
return RDPDYN_Dispatch(DeviceObject, Irp);
} else {
// Only for port device, we deny driver attachment
if (DeviceObject == (PDEVICE_OBJECT) DrPortDeviceObject) {
if (DeviceObject->AttachedDevice != NULL ||
(IoStackLocation->FileObject != NULL &&
IoStackLocation->FileObject->DeviceObject != (PDEVICE_OBJECT)DrPortDeviceObject)) {
//
// We don't accept another device attaches to us or
// is passing irps to us
//
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_ACCESS_DENIED;
}
//
// We swap back to the rdpdr device object for port device so it'll go through
// the rdbss route
//
IoStackLocation->DeviceObject = (PDEVICE_OBJECT)DrDeviceObject;
// For Multi-User TS environment, we need to set the port carete share access
// to be sharable, otherwise two users can't use com1 at the same time because
// rdbss check for share access for NET_ROOT \\tsclient\com1.
if (IoStackLocation->MajorFunction == IRP_MJ_CREATE) {
IoStackLocation->Parameters.Create.ShareAccess = FILE_SHARE_VALID_FLAGS;
}
}
if ((IoStackLocation->MajorFunction == IRP_MJ_CREATE) &&
(IoStackLocation->FileObject->FileName.Length == 0) &&
(IoStackLocation->FileObject->RelatedFileObject == NULL)) {
//
// This is a blank create, like rdpwsx uses to open us for
// session connect disconnect notification. Only allowed
// by the system because we trust rdpwsx to hold a kernel
// pointer for us
//
//
// Security check the irp.
//
Status = IoGetRequestorSessionId(Irp, &irpSessionId);
if (NT_SUCCESS(Status)) {
//
// If the request is from the console session, it needs to be from a system
// process.
//
if (irpSessionId == CONSOLE_SESSIONID) {
TRC_NRM((TB, "Create request from console process."));
if (!DrIsSystemProcessRequest(Irp, IoStackLocation)) {
TRC_ALT((TB, "Root Create request not from system process."));
//
// We may get called from a user process through the UNC
// network provider. e.g. when user does a net use
// In this case, we have to allow root access. We have to
// do the security check on per IRP bases.
//
//Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
//Irp->IoStatus.Information = 0;
//IoCompleteRequest(Irp, IO_NO_INCREMENT);
//return STATUS_ACCESS_DENIED;
return RxFsdDispatch((PRDBSS_DEVICE_OBJECT)DrDeviceObject, Irp);
} else {
TRC_NRM((TB, "Root Create request from system accepted."));
return RxFsdDispatch((PRDBSS_DEVICE_OBJECT)DrDeviceObject, Irp);
}
} else {
//
// If not from the console then deny access.
//
TRC_ALT((TB, "Root request from %ld", irpSessionId));
//
// We may get called from a user process through the UNC
// network provider. e.g. when user does a net use
// In this case, we have to allow root access. We have to
// do the security check on per IRP bases.
//
//Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
//Irp->IoStatus.Information = 0;
//IoCompleteRequest(Irp, IO_NO_INCREMENT);
//return STATUS_ACCESS_DENIED;
return RxFsdDispatch((PRDBSS_DEVICE_OBJECT)DrDeviceObject, Irp);
}
}
else {
TRC_ERR((TB, "IoGetRequestorSessionId failed with %08X.", Status));
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
} else {
//
// This is not a create, or at least not a create to just the root
//
TRC_NRM((TB, "Pass IRP on to RxFsdDispatch = %d", IoStackLocation->MajorFunction));
return RxFsdDispatch((PRDBSS_DEVICE_OBJECT)DrDeviceObject, Irp);
}
}
}
NTSTATUS DrLoadRegistrySettings (
IN PCWSTR RegistryPath
)
/*++
Routine Description:
This routine reads the default configuration data from the
registry for the device redirector driver.
Arguments:
RegistryPath - points to the entry for this driver in the
current control set of the registry.
Return Value:
STATUS_SUCCESS if we got the defaults, otherwise we failed.
The only way to fail this call is if the STATUS_INSUFFICIENT_RESOURCES.
--*/
{
NTSTATUS Status = STATUS_SUCCESS; // return value
BEGIN_FN("DrLoadRegistrySettings ");
#if DBG
extern TRC_CONFIG TRC_Config;
int i;
//
// We use this to query into the registry for defaults
// paramTable needs to be one entry larger than the set we
// need because a NULL entry indicates we're done
//
RTL_QUERY_REGISTRY_TABLE paramTable[9];
TRC_CONFIG trcConfig;
ULONG DebugBreakOnEntryDefault = FALSE;
PAGED_CODE();
RtlZeroMemory(&trcConfig, sizeof(trcConfig));
trcConfig.FunctionLength = TRC_FUNCNAME_LEN;
trcConfig.TraceDebugger = FALSE;
trcConfig.TraceLevel = TRC_LEVEL_ALT;
trcConfig.TraceProfile = TRUE;
RtlZeroMemory (&paramTable[0], sizeof(paramTable));
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[0].Name = L"FunctionLength";
paramTable[0].EntryContext = &TRC_Config.FunctionLength;
paramTable[0].DefaultType = REG_DWORD;
paramTable[0].DefaultData = &trcConfig.FunctionLength;
paramTable[0].DefaultLength = sizeof(trcConfig.FunctionLength);
paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[1].Name = L"TraceLevel";
paramTable[1].EntryContext = &TRC_Config.TraceLevel;
paramTable[1].DefaultType = REG_DWORD;
paramTable[1].DefaultData = &trcConfig.TraceLevel;
paramTable[1].DefaultLength = sizeof(trcConfig.TraceLevel);
paramTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[2].Name = L"TraceProfile";
paramTable[2].EntryContext = &TRC_Config.TraceProfile;
paramTable[2].DefaultType = REG_DWORD;
paramTable[2].DefaultData = &trcConfig.TraceProfile;
paramTable[2].DefaultLength = sizeof(trcConfig.TraceProfile);
paramTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[3].Name = L"TraceDebugger";
paramTable[3].EntryContext = &TRC_Config.TraceDebugger;
paramTable[3].DefaultType = REG_DWORD;
paramTable[3].DefaultData = &trcConfig.TraceDebugger;
paramTable[3].DefaultLength = sizeof(trcConfig.TraceDebugger);
paramTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[4].Name = L"BreakOnEntry";
paramTable[4].EntryContext = &DebugBreakOnEntry;
paramTable[4].DefaultType = REG_DWORD;
paramTable[4].DefaultData = &DebugBreakOnEntryDefault;
paramTable[4].DefaultLength = sizeof(ULONG);
paramTable[5].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[5].Name = L"PrintPortWriteSize";
paramTable[5].EntryContext = &PrintPortWriteSize;
paramTable[5].DefaultType = REG_DWORD;
paramTable[5].DefaultData = &PrintPortWriteSizeDefault;
paramTable[5].DefaultLength = sizeof(ULONG);
paramTable[6].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[6].Name = L"DeviceLowPrioSendFlags";
paramTable[6].EntryContext = &DeviceLowPrioSendFlags;
paramTable[6].DefaultType = REG_DWORD;
paramTable[6].DefaultData = &DeviceLowPrioSendFlagsDefault;
paramTable[6].DefaultLength = sizeof(ULONG);
paramTable[7].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[7].Name = L"MaxWorkerThreads";
paramTable[7].EntryContext = &MaxWorkerThreads;
paramTable[7].DefaultType = REG_DWORD;
paramTable[7].DefaultData = &MaxWorkerThreadsDefault;
paramTable[7].DefaultLength = sizeof(ULONG);
Status = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
RegistryPath,
&paramTable[0],
NULL,
NULL);
if (!NT_SUCCESS(Status)) {
DebugBreakOnEntry = DebugBreakOnEntryDefault;
}
RtlZeroMemory (&paramTable[0], sizeof(paramTable));
WCHAR wcPrefix[10] = L"Prefix";
WCHAR wcStart[10] = L"Start";
WCHAR wcEnd[10] = L"End";
UNICODE_STRING usPrefix;
UNICODE_STRING usStart;
UNICODE_STRING usEnd;
usPrefix.Buffer = &wcPrefix[6]; // Just past "Prefix"
usPrefix.MaximumLength = 3 * sizeof(WCHAR); // Remaining space, room for null term.
usStart.Buffer = &wcStart[5]; // Just past "Start"
usStart.MaximumLength = 4 * sizeof(WCHAR); // Remaining space, room for null term.
usEnd.Buffer = &wcEnd[4]; // Just past "End"
usEnd.MaximumLength = 5 * sizeof(WCHAR); // Remaining space, room for null term.
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[0].Name = wcPrefix;
paramTable[0].DefaultType = REG_BINARY;
paramTable[0].DefaultData = &trcConfig.Prefix[0].name[0];
paramTable[0].DefaultLength = sizeof(trcConfig.Prefix[0].name);
paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[1].Name = wcStart;
paramTable[1].DefaultType = REG_DWORD;
paramTable[1].DefaultData = &trcConfig.Prefix[0].start;
paramTable[1].DefaultLength = sizeof(trcConfig.Prefix[0].start);
paramTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[2].Name = wcEnd;
paramTable[2].DefaultType = REG_DWORD;
paramTable[2].DefaultData = &trcConfig.Prefix[0].end;
paramTable[2].DefaultLength = sizeof(trcConfig.Prefix[0].end);
//
// So the registry can have values like:
// Prefix1 = "rdpdr"
// Start1 = 400
// End1 = 425
//
// Prefix1 = "channel"
// Start1 = 765
// End1 = 765
//
// And that will restrict tracing output to rdpdr, lines 400-425
// and channel, line 765
//
for (i = 0; i < TRC_MAX_PREFIX; i ++) {
RtlZeroMemory(&TRC_Config.Prefix[i].name[0],
sizeof(TRC_Config.Prefix[i].name[0]));
// Clear out the end of the strings
usPrefix.Length = 0; // no length yet
RtlZeroMemory(usPrefix.Buffer, usPrefix.MaximumLength);
usStart.Length = 0; // no length yet
RtlZeroMemory(usStart.Buffer, usStart.MaximumLength);
usEnd.Length = 0; // no length yet
RtlZeroMemory(usEnd.Buffer, usEnd.MaximumLength);
// Append the integer
RtlIntegerToUnicodeString(i + 1, 10, &usPrefix);
RtlIntegerToUnicodeString(i + 1, 10, &usStart);
RtlIntegerToUnicodeString(i + 1, 10, &usEnd);
paramTable[0].EntryContext = &TRC_Config.Prefix[i].name;
paramTable[1].EntryContext = &TRC_Config.Prefix[i].start;
paramTable[2].EntryContext = &TRC_Config.Prefix[i].end;
Status = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
RegistryPath,
&paramTable[0],
NULL,
NULL);
}
#endif // DBG
return (Status);
}
NTSTATUS DrStart(PRX_CONTEXT RxContext, IN OUT PRDBSS_DEVICE_OBJECT RxDeviceObject)
/*++
Routine Description:
This routine completes the initialization of the mini redirector fromn the
RDBSS perspective. Note that this is different from the initialization done
in DriverEntry. Any initialization that depends on RDBSS should be done as
part of this routine while the initialization that is independent of RDBSS
should be done in the DriverEntry routine.
Arguments:
RxContext - Supplies the Irp that was used to startup the rdbss
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
BEGIN_FN("DrStart");
return Status;
}
NTSTATUS DrStop(PRX_CONTEXT RxContext, IN OUT PRDBSS_DEVICE_OBJECT RxDeviceObject)
/*++
Routine Description:
This routine is used to deactivate the mini redirector from the RDBSS perspective
Arguments:
RxContext - the context that was used to start the mini redirector
pContext - the mini rdr context passed in at registration time.
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS Status;
BEGIN_FN("DrStop");
return STATUS_SUCCESS;
}
NTSTATUS DrDeallocateForFcb(IN OUT PMRX_FCB pFcb)
{
BEGIN_FN("DrDeallocateForFcb");
return STATUS_SUCCESS;
}
NTSTATUS DrDeallocateForFobx(IN OUT PMRX_FOBX pFobx)
/*++
Routine Description:
This routine is the last gasp of a Fobx. We remove the DeviceEntry ref
Arguments:
pFobx - the instance to be closed
Return Value:
RXSTATUS - The return status for the operation
Notes:
--*/
{
DrDevice *Device;
DrFile *FileObj;
BEGIN_FN("DrDeallocateForFobx");
//
// Dereference the device object.
//
if (pFobx->Context != NULL) {
Device = (DrDevice *)pFobx->Context;
pFobx->Context = NULL;
Device->Release();
}
//
// Cleanup the file object
//
if (pFobx->Context2 != NULL) {
FileObj = (DrFile *)pFobx->Context2;
FileObj->Release();
pFobx->Context2 = NULL;
}
return STATUS_SUCCESS;
}
NTSTATUS DrForceClosed(IN PMRX_SRV_OPEN pSrvOpen)
/*++
Routine Description:
This routine closes a file system object
Arguments:
pSrvOpen - the instance to be closed
Return Value:
RXSTATUS - The return status for the operation
Notes:
--*/
{
BEGIN_FN("DrForceClosed");
TRC_NRM((TB, "DrForceClosed not implemented"));
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
BuildDeviceAcl(
OUT PACL *DeviceAcl
)
/*++
Routine Description:
(Lifted from AFD - AfdBuildDeviceAcl)
This routine builds an ACL which gives Administrators and LocalSystem
principals full access. All other principals have no access.
Arguments:
DeviceAcl - Output pointer to the new ACL.
Return Value:
STATUS_SUCCESS or an appropriate error code.
--*/
{
PGENERIC_MAPPING GenericMapping;
PSID AdminsSid;
PSID SystemSid;
ULONG AclLength;
NTSTATUS Status;
ACCESS_MASK AccessMask = GENERIC_ALL;
PACL NewAcl;
BEGIN_FN("BuildDeviceAcl");
//
// Enable access to all the globally defined SIDs
//
GenericMapping = IoGetFileObjectGenericMapping();
RtlMapGenericMask( &AccessMask, GenericMapping );
AdminsSid = SeExports->SeAliasAdminsSid;
SystemSid = SeExports->SeLocalSystemSid;
AclLength = sizeof( ACL ) +
2 * sizeof( ACCESS_ALLOWED_ACE ) +
RtlLengthSid( AdminsSid ) +
RtlLengthSid( SystemSid ) -
2 * sizeof( ULONG );
NewAcl = (PACL)new(PagedPool) BYTE[AclLength];
if (NewAcl == NULL) {
return( STATUS_INSUFFICIENT_RESOURCES );
}
Status = RtlCreateAcl (NewAcl, AclLength, ACL_REVISION );
if (!NT_SUCCESS( Status )) {
delete NewAcl;
return( Status );
}
Status = RtlAddAccessAllowedAce (
NewAcl,
ACL_REVISION2,
AccessMask,
AdminsSid
);
ASSERT( NT_SUCCESS( Status ));
Status = RtlAddAccessAllowedAce (
NewAcl,
ACL_REVISION2,
AccessMask,
SystemSid
);
ASSERT( NT_SUCCESS( Status ));
*DeviceAcl = NewAcl;
return( STATUS_SUCCESS );
} // BuildDeviceAcl
NTSTATUS
CreateAdminSecurityDescriptor(
VOID
)
/*++
Routine Description:
(Lifted from AFD - AfdCreateAdminSecurityDescriptor)
This routine creates a security descriptor which gives access
only to Administrtors and LocalSystem. This descriptor is used
to access check raw endpoint opens and exclisive access to transport
addresses.
Arguments:
None.
Return Value:
STATUS_SUCCESS or an appropriate error code.
--*/
{
PACL rawAcl = NULL;
NTSTATUS status;
BOOLEAN memoryAllocated = FALSE;
PSECURITY_DESCRIPTOR drSecurityDescriptor;
ULONG localDrSecurityDescriptorLength = 0;
CHAR buffer[SECURITY_DESCRIPTOR_MIN_LENGTH];
PSECURITY_DESCRIPTOR localSecurityDescriptor =
(PSECURITY_DESCRIPTOR) &buffer;
PSECURITY_DESCRIPTOR localDrAdminSecurityDescriptor;
SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION;
BEGIN_FN("CreateAdminSecurityDescriptor");
//
// Get a pointer to the security descriptor from the Dr device object.
//
status = ObGetObjectSecurity(
DrDeviceObject,
&drSecurityDescriptor,
&memoryAllocated
);
if (!NT_SUCCESS(status)) {
TRC_ERR((TB, "Unable to get security descriptor, error: %x",
status));
ASSERT(memoryAllocated == FALSE);
return(status);
}
else {
if (drSecurityDescriptor == NULL) {
TRC_ERR((TB, "No security descriptor for DrDeviceObject"));
status = STATUS_UNSUCCESSFUL;
return(status);
}
}
//
// Build a local security descriptor with an ACL giving only
// administrators and system access.
//
status = BuildDeviceAcl(&rawAcl);
if (!NT_SUCCESS(status)) {
TRC_ERR((TB, "Unable to create Raw ACL, error: %x", status));
goto error_exit;
}
(VOID) RtlCreateSecurityDescriptor(
localSecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION
);
(VOID) RtlSetDaclSecurityDescriptor(
localSecurityDescriptor,
TRUE,
rawAcl,
FALSE
);
//
// Make a copy of the Dr descriptor. This copy will be the raw descriptor.
//
localDrSecurityDescriptorLength = RtlLengthSecurityDescriptor(
drSecurityDescriptor
);
localDrAdminSecurityDescriptor = (PSECURITY_DESCRIPTOR)new(PagedPool) BYTE[localDrSecurityDescriptorLength];
if (localDrAdminSecurityDescriptor == NULL) {
TRC_ERR((TB, "couldn't allocate security descriptor"));
status = STATUS_NO_MEMORY;
goto error_exit;
}
RtlMoveMemory(
localDrAdminSecurityDescriptor,
drSecurityDescriptor,
localDrSecurityDescriptorLength
);
DrAdminSecurityDescriptor = localDrAdminSecurityDescriptor;
DrSecurityDescriptorLength = localDrSecurityDescriptorLength;
//
// Now apply the local descriptor to the raw descriptor.
//
status = SeSetSecurityDescriptorInfo(
NULL,
&securityInformation,
localSecurityDescriptor,
&DrAdminSecurityDescriptor,
PagedPool,
IoGetFileObjectGenericMapping()
);
if (!NT_SUCCESS(status)) {
TRC_ERR((TB, "SeSetSecurity failed, %lx", status));
ASSERT (DrAdminSecurityDescriptor==localDrAdminSecurityDescriptor);
delete DrAdminSecurityDescriptor;
DrAdminSecurityDescriptor = NULL;
DrSecurityDescriptorLength = 0;
goto error_exit;
}
if (DrAdminSecurityDescriptor != localDrAdminSecurityDescriptor) {
delete localDrAdminSecurityDescriptor;
}
status = STATUS_SUCCESS;
error_exit:
ObReleaseObjectSecurity(
drSecurityDescriptor,
memoryAllocated
);
if (rawAcl!=NULL) {
delete rawAcl;
}
return(status);
}