From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/nw/rdr/fsctl.c | 5930 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5930 insertions(+) create mode 100644 private/nw/rdr/fsctl.c (limited to 'private/nw/rdr/fsctl.c') diff --git a/private/nw/rdr/fsctl.c b/private/nw/rdr/fsctl.c new file mode 100644 index 000000000..159cd1385 --- /dev/null +++ b/private/nw/rdr/fsctl.c @@ -0,0 +1,5930 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + FsCtrl.c + +Abstract: + + This module implements the File System Control routines for the + NetWare redirector called by the dispatch driver. + +Author: + + Colin Watson [ColinW] 29-Dec-1992 + +Revision History: + +--*/ + +#include "Procs.h" +#include "ntddrdr.h" +#include "ntddmup.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_FSCTRL) + +// +// Local procedure prototypes +// + +NTSTATUS +NwCommonDeviceIoControl ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +StartRedirector( + PIRP_CONTEXT IrpContext + ); + +NTSTATUS +StopRedirector( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +BindToTransport ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +ChangePassword ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +SetInfo ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +SetDebug ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +GetMessage ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +GetStats ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +GetPrintJobId ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +GetConnectionDetails( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +GetConnectionPerformance( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +RegisterWithMup( + VOID + ); + +VOID +DeregisterWithMup( + VOID + ); + +NTSTATUS +QueryPath ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +UserNcp( + ULONG Function, + PIRP_CONTEXT IrpContext + ); + +NTSTATUS +UserNcpCallback ( + IN PIRP_CONTEXT IrpContext, + IN ULONG BytesAvailable, + IN PUCHAR Response + ); + +NTSTATUS +FspCompleteLogin( + PIRP_CONTEXT IrpContext + ); + +NTSTATUS +GetConnection( + PIRP_CONTEXT IrpContext + ); + +NTSTATUS +EnumConnections( + PIRP_CONTEXT IrpContext + ); + +NTSTATUS +DeleteConnection( + PIRP_CONTEXT IrpContext + ); + +NTSTATUS +WriteNetResourceEntry( + IN OUT PCHAR *FixedPortion, + IN OUT PWCHAR *EndOfVariableData, + IN PUNICODE_STRING ContainerName OPTIONAL, + IN PUNICODE_STRING LocalName OPTIONAL, + IN PUNICODE_STRING RemoteName, + IN ULONG ScopeFlag, + IN ULONG DisplayFlag, + IN ULONG UsageFlag, + IN ULONG ShareType, + OUT PULONG EntrySize + ); + +BOOL +CopyStringToBuffer( + IN LPCWSTR SourceString OPTIONAL, + IN DWORD CharacterCount, + IN LPCWSTR FixedDataEnd, + IN OUT LPWSTR *EndOfVariableData, + OUT LPWSTR *VariableDataPointer + ); + +NTSTATUS +GetRemoteHandle( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +GetUserName( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +GetChallenge( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +WriteConnStatusEntry( + PSCB pConnectionScb, + PBYTE pbUserBuffer, + DWORD dwBufferLen, + DWORD *pdwBytesWritten, + DWORD *pdwBytesNeeded, + BOOLEAN fCallerScb + ); + +NTSTATUS +GetConnStatus( + IN PIRP_CONTEXT IrpContext, + PFILE_OBJECT FileObject + ); + +NTSTATUS +GetConnectionInfo( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +GetPreferredServer( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +SetShareBit( + IN PIRP_CONTEXT IrpContext, + PFILE_OBJECT FileObject + ); + +// +// Statics +// + +HANDLE MupHandle; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, NwFsdFileSystemControl ) +#pragma alloc_text( PAGE, NwCommonFileSystemControl ) +#pragma alloc_text( PAGE, NwFsdDeviceIoControl ) +#pragma alloc_text( PAGE, NwCommonDeviceIoControl ) +#pragma alloc_text( PAGE, BindToTransport ) +#pragma alloc_text( PAGE, ChangePassword ) +#pragma alloc_text( PAGE, SetInfo ) +#pragma alloc_text( PAGE, GetStats ) +#pragma alloc_text( PAGE, GetPrintJobId ) +#pragma alloc_text( PAGE, StartRedirector ) +#pragma alloc_text( PAGE, StopRedirector ) +#pragma alloc_text( PAGE, RegisterWithMup ) +#pragma alloc_text( PAGE, DeregisterWithMup ) +#pragma alloc_text( PAGE, QueryPath ) +#pragma alloc_text( PAGE, UserNcp ) +#pragma alloc_text( PAGE, GetConnection ) +#pragma alloc_text( PAGE, DeleteConnection ) +#pragma alloc_text( PAGE, WriteNetResourceEntry ) +#pragma alloc_text( PAGE, CopyStringToBuffer ) +#pragma alloc_text( PAGE, GetRemoteHandle ) +#pragma alloc_text( PAGE, GetUserName ) +#pragma alloc_text( PAGE, GetChallenge ) +#pragma alloc_text( PAGE, WriteConnStatusEntry ) +#pragma alloc_text( PAGE, GetConnStatus ) +#pragma alloc_text( PAGE, GetConnectionInfo ) +#pragma alloc_text( PAGE, GetPreferredServer ) + +#ifndef QFE_BUILD +#pragma alloc_text( PAGE1, UserNcpCallback ) +#pragma alloc_text( PAGE1, GetConnectionDetails ) +#pragma alloc_text( PAGE1, GetMessage ) +#pragma alloc_text( PAGE1, EnumConnections ) +#endif + +#endif + +#if 0 // Not pageable + +// see ifndef QFE_BUILD above + +#endif + + + +NTSTATUS +NwFsdFileSystemControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of FileSystem control operations + +Arguments: + + DeviceObject - Supplies the redirector device object. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwFsdFileSystemControl\n", 0); + + FsRtlEnterFileSystem(); + TopLevel = NwIsIrpTopLevel( Irp ); + + try { + + IrpContext = AllocateIrpContext( Irp ); + SetFlag( IrpContext->Flags, IRP_FLAG_IN_FSD ); + Status = NwCommonFileSystemControl( IrpContext ); + + } except(NwExceptionFilter( Irp, GetExceptionInformation() )) { + + if ( IrpContext == NULL ) { + + // + // If we couldn't allocate an irp context, just complete + // irp without any fanfare. + // + + Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = 0; + IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT ); + + } else { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error Status that we get back from the + // execption code + // + + Status = NwProcessException( IrpContext, GetExceptionCode() ); + } + + } + + if ( IrpContext ) { + + if ( Status != STATUS_PENDING ) { + NwDequeueIrpContext( IrpContext, FALSE ); + } + + NwCompleteRequest( IrpContext, Status ); + } + + if ( TopLevel ) { + NwSetTopLevelIrp( NULL ); + } + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NwFsdFileSystemControl -> %08lx\n", Status); + + return Status; +} + + +NTSTATUS +NwCommonFileSystemControl ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This is the common routine for doing FileSystem control operations called + by both the fsd and fsp threads + +Arguments: + + IrpContext - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PIRP Irp; + ULONG Function; + + PAGED_CODE(); + + NwReferenceUnlockableCodeSection(); + + try { + + // + // Get a pointer to the current Irp stack location + // + + Irp = IrpContext->pOriginalIrp; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + Function = IrpSp->Parameters.FileSystemControl.FsControlCode; + + DebugTrace(+1, Dbg, "NwCommonFileSystemControl\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "Function = %08lx\n", Function); + DebugTrace( 0, Dbg, "Function = %d\n", (Function >> 2) & 0x0fff); + + // + // We know this is a file system control so we'll case on the + // minor function, and call a internal worker routine to complete + // the irp. + // + + if (IrpSp->MinorFunction != IRP_MN_USER_FS_REQUEST ) { + DebugTrace( 0, Dbg, "Invalid FS Control Minor Function %08lx\n", IrpSp->MinorFunction); + return STATUS_INVALID_DEVICE_REQUEST; + } + + switch (Function) { + + case FSCTL_NWR_START: + Status = StartRedirector( IrpContext ); + break; + + case FSCTL_NWR_STOP: + Status = StopRedirector( IrpContext ); + break; + + case FSCTL_NWR_LOGON: + Status = Logon( IrpContext ); + break; + + case FSCTL_NWR_LOGOFF: + Status = Logoff( IrpContext ); + break; + + case FSCTL_NWR_GET_CONNECTION: + Status = GetConnection( IrpContext ); + break; + + case FSCTL_NWR_ENUMERATE_CONNECTIONS: + Status = EnumConnections( IrpContext ); + break; + + case FSCTL_NWR_DELETE_CONNECTION: + Status = DeleteConnection( IrpContext ); + break; + + case FSCTL_NWR_BIND_TO_TRANSPORT: + Status = BindToTransport( IrpContext ); + break; + + case FSCTL_NWR_CHANGE_PASS: + Status = ChangePassword( IrpContext ); + break; + + case FSCTL_NWR_SET_INFO: + Status = SetInfo( IrpContext ); + break; + + case FSCTL_NWR_GET_CONN_DETAILS: + Status = GetConnectionDetails( IrpContext ); + break; + + case FSCTL_NWR_GET_MESSAGE: + Status = GetMessage( IrpContext ); + break; + + case FSCTL_NWR_GET_STATISTICS: + Status = GetStats( IrpContext ); + break; + + case FSCTL_NWR_GET_USERNAME: + Status = GetUserName( IrpContext ); + break; + + case FSCTL_NWR_CHALLENGE: + Status = GetChallenge( IrpContext ); + break; + + case FSCTL_GET_PRINT_ID: + Status = GetPrintJobId( IrpContext ); + break; + + case FSCTL_NWR_GET_CONN_STATUS: + Status = GetConnStatus( IrpContext, IrpSp->FileObject ); + break; + + case FSCTL_NWR_GET_CONN_INFO: + Status = GetConnectionInfo( IrpContext ); + break; + + case FSCTL_NWR_GET_PREFERRED_SERVER: + Status = GetPreferredServer( IrpContext ); + break; + + case FSCTL_NWR_GET_CONN_PERFORMANCE: + Status = GetConnectionPerformance( IrpContext ); + break; + + case FSCTL_NWR_SET_SHAREBIT: + Status = SetShareBit( IrpContext, IrpSp->FileObject ); + break; + + default: + + if (( Function >= NWR_ANY_NCP(0)) && + ( Function <= NWR_ANY_HANDLE_NCP(0x00ff))) { + + Status = UserNcp( Function, IrpContext ); + break; + + } + + if (( Function >= NWR_ANY_NDS(0)) && + ( Function <= NWR_ANY_NDS(0x00ff))) { + + Status = DispatchNds( Function, IrpContext ); + break; + } + + DebugTrace( 0, Dbg, "Invalid FS Control Code %08lx\n", + IrpSp->Parameters.FileSystemControl.FsControlCode); + + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + + } + + } finally { + + NwDereferenceUnlockableCodeSection (); + + DebugTrace(-1, Dbg, "NwCommonFileSystemControl -> %08lx\n", Status); + + } + + return Status; +} + + +NTSTATUS +NwFsdDeviceIoControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of DeviceIoControl file operations + +Arguments: + + DeviceObject - Supplies the redirector device object. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwFsdDeviceIoControl\n", 0); + + FsRtlEnterFileSystem(); + TopLevel = NwIsIrpTopLevel( Irp ); + + try { + + IrpContext = AllocateIrpContext( Irp ); + SetFlag( IrpContext->Flags, IRP_FLAG_IN_FSD ); + Status = NwCommonDeviceIoControl( IrpContext ); + + } except(NwExceptionFilter( Irp, GetExceptionInformation() )) { + + if ( IrpContext == NULL ) { + + // + // If we couldn't allocate an irp context, just complete + // irp without any fanfare. + // + + Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = 0; + IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT ); + + } else { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error Status that we get back from the + // execption code + // + + Status = NwProcessException( IrpContext, GetExceptionCode() ); + } + + } + + if ( IrpContext ) { + + if ( Status != STATUS_PENDING ) { + NwDequeueIrpContext( IrpContext, FALSE ); + } + + NwCompleteRequest(IrpContext, Status); + } + + if ( TopLevel ) { + NwSetTopLevelIrp( NULL ); + } + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NwFsdDeviceIoControl -> %08lx\n", Status); + + return Status; +} + + +NTSTATUS +NwCommonDeviceIoControl ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This is the common routine for doing FileSystem control operations called + by both the fsd and fsp threads + +Arguments: + + IrpContext - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PIRP Irp; + + PAGED_CODE(); + + NwReferenceUnlockableCodeSection(); + + try { + + // + // Get a pointer to the current Irp stack location + // + + Irp = IrpContext->pOriginalIrp; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NwCommonDeviceIoControl\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "Function = %08lx\n", + IrpSp->Parameters.DeviceIoControl.IoControlCode); + + // + // We know this is a DeviceIoControl so we'll case on the + // minor function, and call a internal worker routine to complete + // the irp. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_REDIR_QUERY_PATH: + Status = QueryPath( IrpContext ); + break; + + case IOCTL_NWR_RAW_HANDLE: + Status = GetRemoteHandle( IrpContext ); + break; + + default: + + DebugTrace( 0, Dbg, "Invalid IO Control Code %08lx\n", + IrpSp->Parameters.DeviceIoControl.IoControlCode); + + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + } finally { + + NwDereferenceUnlockableCodeSection (); + DebugTrace(-1, Dbg, "NwCommonDeviceIoControl -> %08lx\n", Status); + + } + + return Status; +} + + +NTSTATUS +BindToTransport ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine records the name of the transport to be used and + initialises the PermanentScb. + +Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + NTSTATUS Status; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer; + ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + + PAGED_CODE(); + +#ifdef _PNP_POWER + + // + // For PnP builds, register the bind handlers. + // + + DebugTrace( 0 , Dbg, "Register TDI bind handlers.\n", 0 ); + + TdiInitialize(); + + return TdiRegisterNotificationHandler( HandleTdiBindMessage, + HandleTdiUnbindMessage, + &TdiBindingHandle ); + +#endif + + DebugTrace(+1, Dbg, "Bind to transport\n", 0); + + try { + + if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) { + Status = NwPostToFsp( IrpContext, TRUE ); + try_return( Status ); + } + + if (IpxHandle != NULL) { + + // + // Can only bind to one transport at a time in this implementation + // + + try_return(Status= STATUS_SHARING_VIOLATION); + } + + // + // Check some fields in the input buffer. + // + + if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) { + try_return(Status = STATUS_BUFFER_TOO_SMALL); + } + + if (InputBuffer->Version != REQUEST_PACKET_VERSION) { + try_return(Status = STATUS_INVALID_PARAMETER); + } + + if (InputBufferLength < + (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.Bind.TransportName)) + + InputBuffer->Parameters.Bind.TransportNameLength) { + try_return(Status = STATUS_INVALID_PARAMETER); + } + + if ( IpxTransportName.Buffer != NULL ) { + FREE_POOL( IpxTransportName.Buffer ); + } + + Status = SetUnicodeString ( &IpxTransportName, + InputBuffer->Parameters.Bind.TransportNameLength, + InputBuffer->Parameters.Bind.TransportName); + + DebugTrace(-1, Dbg, "\"%wZ\"\n", &IpxTransportName); + + if ( !NT_SUCCESS(Status) ) { + try_return(Status); + } + + Status = IpxOpen(); + if ( !NT_SUCCESS(Status) ) { + try_return(Status); + } + + // + // Verify that have a large enough stack size. + // + + if ( pIpxDeviceObject->StackSize >= FileSystemDeviceObject->StackSize) { + IpxClose(); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + +#ifndef QFE_BUILD + + // + // Submit a line change request. + // + + SubmitLineChangeRequest(); +#endif + + // + // Open a handle to IPX. + // + + NwPermanentNpScb.Server.Socket = 0; + Status = IPX_Open_Socket( IrpContext, &NwPermanentNpScb.Server ); + ASSERT( NT_SUCCESS( Status ) ); + + Status = SetEventHandler ( + IrpContext, + &NwPermanentNpScb.Server, + TDI_EVENT_RECEIVE_DATAGRAM, + &ServerDatagramHandler, + &NwPermanentNpScb ); + + ASSERT( NT_SUCCESS( Status ) ); + + IrpContext->pNpScb = &NwPermanentNpScb; + + NwRcb.State = RCB_STATE_RUNNING; + +try_exit:NOTHING; + } except (EXCEPTION_EXECUTE_HANDLER) { + Status = GetExceptionCode(); + } + + DebugTrace(-1, Dbg, "Bind to transport\n", 0); + return Status; + +} + +#ifdef _PNP_POWER + +VOID +HandleTdiBindMessage( + IN PUNICODE_STRING DeviceName +) +/*+++ + +Description: This function is the bind handler for NetPnP + support. This function is registered with TDI and is called + whenever a transport starts up or stops. We watch for IPX + coming and going and do the appropriate thing. + + See also: HandleTdiUnbindMessage() + +---*/ +{ + + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + PIRP pIrp = NULL; + + PAGED_CODE(); + + // + // See if this is IPX requesting a bind. We only bind to NwLnkIpx. + // + + if ( !RtlEqualUnicodeString( &TdiIpxDeviceName, DeviceName, TRUE ) ) { + + DebugTrace( 0, Dbg, "Ignoring PnP Bind request for %wZ\n", DeviceName ); + return; + } + + // + // Make sure we aren't already bound. + // + + if ( ( NwRcb.State != RCB_STATE_NEED_BIND ) || + ( IpxHandle != NULL ) ) { + + DebugTrace( 0, Dbg, "Discarding duplicate PnP bind request.\n", 0 ); + return; + } + + ASSERT( IpxTransportName.Buffer == NULL ); + ASSERT( pIpxDeviceObject == NULL ); + + Status = DuplicateUnicodeStringWithString ( &IpxTransportName, + DeviceName, + PagedPool ); + + if ( !NT_SUCCESS( Status ) ) { + + DebugTrace( 0, Dbg, "Failing IPX bind: Can't set device name.\n", 0 ); + return; + } + + // + // Open IPX. + // + + Status = IpxOpen(); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // Verify that have a large enough stack size. + // + + if ( pIpxDeviceObject->StackSize >= FileSystemDeviceObject->StackSize) { + + Status = STATUS_INVALID_PARAMETER; + goto ExitWithCleanup; + } + + // + // Submit a line change request. + // + + SubmitLineChangeRequest(); + + // + // Allocate an irp and irp context. AllocateIrpContext may raise status. + // + + pIrp = ALLOCATE_IRP( pIpxDeviceObject->StackSize, FALSE ); + + if ( pIrp == NULL ) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitWithCleanup; + } + + try { + + IrpContext = AllocateIrpContext( pIrp ); + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitWithCleanup; + } + + ASSERT( IrpContext != NULL ); + + // + // Open a handle to IPX for the permanent scb. + // + + NwPermanentNpScb.Server.Socket = 0; + Status = IPX_Open_Socket( IrpContext, &NwPermanentNpScb.Server ); + ASSERT( NT_SUCCESS( Status ) ); + + Status = SetEventHandler ( + IrpContext, + &NwPermanentNpScb.Server, + TDI_EVENT_RECEIVE_DATAGRAM, + &ServerDatagramHandler, + &NwPermanentNpScb ); + + ASSERT( NT_SUCCESS( Status ) ); + + IrpContext->pNpScb = &NwPermanentNpScb; + + NwRcb.State = RCB_STATE_RUNNING; + + DebugTrace( 0, Dbg, "Opened IPX for NwRdr.\n", 0 ); + + Status = STATUS_SUCCESS; + +ExitWithCleanup: + + if ( !NT_SUCCESS( Status ) ) { + + // + // If we failed, clean up our globals. + // + + if ( pIpxDeviceObject != NULL ) { + IpxClose(); + pIpxDeviceObject = NULL; + } + + IpxHandle = NULL; + + if ( IpxTransportName.Buffer != NULL ) { + FREE_POOL( IpxTransportName.Buffer ); + IpxTransportName.Buffer = NULL; + } + + DebugTrace( 0, Dbg, "Failing IPX bind request.\n", 0 ); + + } + + if ( pIrp != NULL ) { + FREE_IRP( pIrp ); + } + + if ( IrpContext != NULL ) { + FreeIrpContext( IrpContext ); + } + + return; + +} + +VOID +HandleTdiUnbindMessage( + IN PUNICODE_STRING DeviceName +) +/*+++ + +Description: This function is the unbind handler for NetPnP + support. This function is registered with TDI and is called + whenever a transport stops. We watch for IPX coming and going + and do the appropriate thing. + + See also: HandleTdiBindMessage() + +---*/ +{ + + DebugTrace( 0, Dbg, "TDI unbind request ignored. Not Supported.\n", 0 ); + return; + +} + +#endif + + +NTSTATUS +ChangePassword ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine records a change in the user's cached password. + +Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + NTSTATUS Status; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer; + ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + + UNICODE_STRING UserName; + UNICODE_STRING Password; + UNICODE_STRING ServerName; + LARGE_INTEGER Uid; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "change password\n", 0); + + try { + + // + // Check some fields in the input buffer. + // + + if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) { + try_return(Status = STATUS_BUFFER_TOO_SMALL); + } + + if (InputBuffer->Version != REQUEST_PACKET_VERSION) { + try_return(Status = STATUS_INVALID_PARAMETER); + } + + if (InputBufferLength < + (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.ChangePass.UserName)) + + InputBuffer->Parameters.ChangePass.UserNameLength + + InputBuffer->Parameters.ChangePass.PasswordLength + + InputBuffer->Parameters.ChangePass.ServerNameLength ) { + try_return(Status = STATUS_INVALID_PARAMETER); + } + + // + // Get local pointer to the fsctl parameters + // + + UserName.Buffer = InputBuffer->Parameters.ChangePass.UserName; + UserName.Length = (USHORT)InputBuffer->Parameters.ChangePass.UserNameLength; + + Password.Buffer = UserName.Buffer + + (InputBuffer->Parameters.ChangePass.UserNameLength / 2); + Password.Length = (USHORT)InputBuffer->Parameters.ChangePass.PasswordLength; + + ServerName.Buffer = Password.Buffer + + (InputBuffer->Parameters.ChangePass.PasswordLength / 2); + ServerName.Length = (USHORT)InputBuffer->Parameters.ChangePass.ServerNameLength; + + // + // Update the default password for this user + // + + Status = UpdateUsersPassword( &UserName, &Password, &Uid ); + + // + // Update the default password for this user + // + + if ( NT_SUCCESS( Status ) ) { + UpdateServerPassword( IrpContext, &ServerName, &UserName, &Password, &Uid ); + } + + Status = STATUS_SUCCESS; + +try_exit:NOTHING; + } except (EXCEPTION_EXECUTE_HANDLER) { + Status = GetExceptionCode(); + } + + DebugTrace(-1, Dbg, "Change Password\n", 0); + return Status; +} + + +NTSTATUS +SetInfo ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine set netware redirector parameters. + +Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer; + ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "Set info\n", 0); + + try { + + // + // Check some fields in the input buffer. + // + + if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) { + try_return(Status = STATUS_BUFFER_TOO_SMALL); + } + + if (InputBuffer->Version != REQUEST_PACKET_VERSION) { + try_return(Status = STATUS_INVALID_PARAMETER); + } + + if (InputBufferLength < + (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.SetInfo.PreferredServer)) + + InputBuffer->Parameters.SetInfo.PreferredServerLength + + InputBuffer->Parameters.SetInfo.ProviderNameLength ) { + try_return(Status = STATUS_INVALID_PARAMETER); + } + + // + // We don't do anything with a preferred server change, but if we + // get a request to change the preferred tree and context, we + // validate the context. The rest of the changes happen at the next + // login. + // + + if ( InputBuffer->Parameters.SetInfo.PreferredServerLength > 0 && + InputBuffer->Parameters.SetInfo.PreferredServer[0] == '*' ) { + + UNICODE_STRING Tree, NewContext; + USHORT i = 0; + + // + // Dig out the tree name. Skip over the *. + // + + Tree.Length = 0; + Tree.Buffer = InputBuffer->Parameters.SetInfo.PreferredServer + 1; + + while ( i < InputBuffer->Parameters.SetInfo.PreferredServerLength ) { + + if ( InputBuffer->Parameters.SetInfo.PreferredServer[i] == L'\\' ) { + + i++; + Tree.Length -= sizeof( WCHAR ); + Tree.MaximumLength = Tree.Length; + break; + + } else { + + Tree.Length += sizeof( WCHAR ); + i++; + + } + } + + DebugTrace( 0, Dbg, "Tree: %wZ\n", &Tree ); + + NewContext.Length = (USHORT)InputBuffer->Parameters.SetInfo.PreferredServerLength - + ( Tree.Length + (2 * sizeof( WCHAR ) ) ); + NewContext.Buffer = &InputBuffer->Parameters.SetInfo.PreferredServer[i]; + NewContext.MaximumLength = NewContext.Length; + + // + // Strip off any leading period. + // + + if ( NewContext.Buffer[0] == L'.' ) { + + NewContext.Buffer++; + NewContext.Length -= sizeof( WCHAR ); + NewContext.MaximumLength -= sizeof( WCHAR ); + + } + + DebugTrace( 0, Dbg, "Context: %wZ\n", &NewContext ); + + Status = NdsVerifyContext( IrpContext, &Tree, &NewContext ); + + if ( !NT_SUCCESS( Status )) { + try_return( STATUS_INVALID_PARAMETER ); + } + } + + // + // Next set the provider name string. + // + + if ( InputBuffer->Parameters.SetInfo.ProviderNameLength != 0 ) { + + PWCH TempBuffer; + + TempBuffer = ALLOCATE_POOL_EX( PagedPool, InputBuffer->Parameters.SetInfo.ProviderNameLength ); + + if ( NwProviderName.Buffer != NULL ) { + FREE_POOL( NwProviderName.Buffer ); + } + + NwProviderName.Buffer = TempBuffer; + NwProviderName.Length = (USHORT)InputBuffer->Parameters.SetInfo.ProviderNameLength; + + RtlCopyMemory( + NwProviderName.Buffer, + (PUCHAR)InputBuffer->Parameters.SetInfo.PreferredServer + + InputBuffer->Parameters.SetInfo.PreferredServerLength, + NwProviderName.Length ); + + } + + // + // Set burst mode parameters + // + + if ( InputBuffer->Parameters.SetInfo.MaximumBurstSize == 0 ) { + NwBurstModeEnabled = FALSE; + } else if ( InputBuffer->Parameters.SetInfo.MaximumBurstSize != -1 ) { + NwBurstModeEnabled = TRUE; + NwMaxSendSize = InputBuffer->Parameters.SetInfo.MaximumBurstSize; + NwMaxReceiveSize = InputBuffer->Parameters.SetInfo.MaximumBurstSize; + } + + // + // Set print options + // + + NwPrintOptions = InputBuffer->Parameters.SetInfo.PrintOption; + +try_exit:NOTHING; + } except (EXCEPTION_EXECUTE_HANDLER) { + Status = GetExceptionCode(); + } + + DebugTrace(-1, Dbg, "Set info\n", 0); + return Status; +} + + +NTSTATUS +GetMessage ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine queues an IRP to a list of IRP Contexts available for + reading server administrative messages. + +Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + NTSTATUS Status = STATUS_PENDING; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PVOID OutputBuffer; + + DebugTrace(+1, Dbg, "GetMessage\n", 0); + + NwLockUserBuffer( Irp, IoWriteAccess, OutputBufferLength ); + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + + // + // Update the original MDL record in the Irp context, since + // NwLockUserBuffer may have created a new MDL. + // + + IrpContext->pOriginalMdlAddress = Irp->MdlAddress; + + IrpContext->Specific.FileSystemControl.Buffer = OutputBuffer; + IrpContext->Specific.FileSystemControl.Length = OutputBufferLength; + + ExInterlockedInsertTailList( + &NwGetMessageList, + &IrpContext->NextRequest, + &NwMessageSpinLock ); + + IoMarkIrpPending( Irp ); + + // + // Set the cancel routine. + // + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + + if ( Irp->Cancel ) { + NwCancelIrp( NULL, Irp ); + } else { + IoSetCancelRoutine( Irp, NwCancelIrp ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + } + + DebugTrace(-1, Dbg, "Get Message -> %08lx\n", Status ); + return Status; +} + + +NTSTATUS +GetStats ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine copies Stats into the users buffer. + + Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + NTSTATUS Status = STATUS_PENDING; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PVOID OutputBuffer; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "GetStats\n", 0); + + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + + if (NwRcb.State != RCB_STATE_RUNNING) { + + Status = STATUS_REDIRECTOR_NOT_STARTED; + + } else if (OutputBufferLength < sizeof(NW_REDIR_STATISTICS)) { + + Status = STATUS_BUFFER_TOO_SMALL; + + } else if (OutputBufferLength != sizeof(NW_REDIR_STATISTICS)) { + + Status = STATUS_INVALID_PARAMETER; + + } else { + + Stats.CurrentCommands = ContextCount; + + RtlCopyMemory(OutputBuffer, &Stats, OutputBufferLength); + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = OutputBufferLength; + + } + + DebugTrace(-1, Dbg, "GetStats -> %08lx\n", Status ); + return Status; +} + + +NTSTATUS +GetPrintJobId ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine gets the Job ID for this job. + +Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + NTSTATUS Status = STATUS_PENDING; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PQUERY_PRINT_JOB_INFO OutputBuffer; + PICB Icb; + PVOID FsContext; + NODE_TYPE_CODE NodeTypeCode; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "GetJobId\n", 0); + + NodeTypeCode = NwDecodeFileObject( + IrpSp->FileObject, + &FsContext, + (PVOID *)&Icb ); + + if (NodeTypeCode != NW_NTC_ICB) { + + DebugTrace(0, Dbg, "Not a file\n", 0); + Status = STATUS_INVALID_PARAMETER; + + } else if ( OutputBufferLength < sizeof( QUERY_PRINT_JOB_INFO ) ) { + Status = STATUS_BUFFER_TOO_SMALL; + } else { + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + + OutputBuffer->JobId = Icb->JobId; + //OutputBuffer->ServerName = BUGBUG + //OutputBuffer->QueueName = BUGBUG + + Status = STATUS_SUCCESS; + } + + DebugTrace(-1, Dbg, "GetJobId -> %08lx\n", Status ); + return Status; +} + + +NTSTATUS +GetConnectionDetails( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine gets the details for a connection. This is normally used + for support of NetWare aware Dos applications. + +Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + NTSTATUS Status = STATUS_PENDING; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PNWR_GET_CONNECTION_DETAILS OutputBuffer; + PSCB pScb; + PNONPAGED_SCB pNpScb; + PICB Icb; + PVOID FsContext; + NODE_TYPE_CODE nodeTypeCode; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "GetConnectionDetails\n", 0); + + if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject, + &FsContext, + (PVOID *)&Icb )) != NW_NTC_ICB_SCB) { + + DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode); + + Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "GetConnectionDetails -> %08lx\n", Status ); + + return Status; + } + + // + // Make sure that this ICB is still active. + // + + NwVerifyIcb( Icb ); + + pScb = (PSCB)Icb->SuperType.Scb; + nodeTypeCode = pScb->NodeTypeCode; + + if (nodeTypeCode != NW_NTC_SCB) { + return STATUS_INVALID_DEVICE_REQUEST; + } + + pNpScb = pScb->pNpScb; + + if ( OutputBufferLength < sizeof( NWR_GET_CONNECTION_DETAILS ) ) { + Status = STATUS_BUFFER_TOO_SMALL; + } else { + PLIST_ENTRY ScbQueueEntry; + KIRQL OldIrql; + PNONPAGED_SCB pNextNpScb; + UCHAR OrderNumber; + OEM_STRING ServerName; + + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + + KeAcquireSpinLock(&ScbSpinLock, &OldIrql); + + for ( ScbQueueEntry = ScbQueue.Flink, OrderNumber = 1; + ScbQueueEntry != &ScbQueue ; + ScbQueueEntry = ScbQueueEntry->Flink, OrderNumber++ ) { + + pNextNpScb = CONTAINING_RECORD( + ScbQueueEntry, + NONPAGED_SCB, + ScbLinks ); + + // + // Check to make sure that this SCB is usable. + // + + if ( pNextNpScb == pNpScb ) { + break; + } + } + + KeReleaseSpinLock( &ScbSpinLock, OldIrql); + + OutputBuffer->OrderNumber = OrderNumber; + + RtlZeroMemory( OutputBuffer->ServerName, sizeof(OutputBuffer->ServerName)); + ServerName.Buffer = OutputBuffer->ServerName; + ServerName.Length = sizeof(OutputBuffer->ServerName); + ServerName.MaximumLength = sizeof(OutputBuffer->ServerName); + RtlUpcaseUnicodeStringToCountedOemString( &ServerName, &pNpScb->ServerName, FALSE); + + RtlCopyMemory( OutputBuffer->ServerAddress, + &pNpScb->ServerAddress, + sizeof(OutputBuffer->ServerAddress) ); + + OutputBuffer->ServerAddress[12]; + OutputBuffer->ConnectionNumberLo = pNpScb->ConnectionNo; + OutputBuffer->ConnectionNumberHi = pNpScb->ConnectionNoHigh; + // BUGBUG We need to ask the server during connect! + OutputBuffer->MajorVersion = 1; + OutputBuffer->MinorVersion = 11; + OutputBuffer->Preferred = pScb->PreferredServer; + + Status = STATUS_SUCCESS; + } + + DebugTrace(-1, Dbg, "GetConnectionDetails -> %08lx\n", Status ); + return Status; +} + +#if 0 + +NTSTATUS +GetOurAddress( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine gets the value of OurAddress. This is normally used + for support of NetWare aware Dos applications. + +Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + NTSTATUS Status = STATUS_PENDING; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PNWR_GET_OUR_ADDRESS OutputBuffer; + PSCB pScb; + PNONPAGED_SCB pNpScb; + PICB Icb; + PVOID FsContext; + NODE_TYPE_CODE nodeTypeCode; + + DebugTrace(+1, Dbg, "GetOurAddress\n", 0); + + if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject, + &FsContext, + (PVOID *)&Icb )) != NW_NTC_ICB_SCB) { + + DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode); + + Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "GetOurAddress -> %08lx\n", Status ); + } + + // + // Make sure that this ICB is still active. + // + + NwVerifyIcb( Icb ); + + if ( OutputBufferLength < sizeof( NWR_GET_OUR_ADDRESS ) ) { + Status = STATUS_BUFFER_TOO_SMALL; + } else { + + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + + RtlCopyMemory( OutputBuffer->Address, + &OurAddress, + sizeof(OurAddress ); + + Status = STATUS_SUCCESS; + } + + DebugTrace(-1, Dbg, "GetOurAddress -> %08lx\n", Status ); + return Status; +} +#endif + + +NTSTATUS +StartRedirector( + PIRP_CONTEXT IrpContext + ) +/*++ + +Routine Description: + + This routine starts the redirector. + +Arguments: + + None. + +Return Value: + + NTSTATUS - The status of the operation. + +--*/ +{ + NTSTATUS Status; + + PAGED_CODE(); + + // + // We need to be in the FSP to Register the MUP. + // + + if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) { + Status = NwPostToFsp( IrpContext, TRUE ); + return( Status ); + } + + NwRcb.State = RCB_STATE_STARTING; + + FspProcess = PsGetCurrentProcess(); + +#ifdef QFE_BUILD + StartTimer() ; +#endif + + // + // Now connect to the MUP. + // + + RegisterWithMup(); + + KeQuerySystemTime( &Stats.StatisticsStartTime ); + + NwRcb.State = RCB_STATE_NEED_BIND; + + return( STATUS_SUCCESS ); +} + + +NTSTATUS +StopRedirector( + PIRP_CONTEXT IrpContext + ) +/*++ + +Routine Description: + + This routine shuts down the redirector. + +Arguments: + + None. + +Return Value: + + NTSTATUS - The status of the operation. + +--*/ +{ + NTSTATUS Status; + PLIST_ENTRY LogonListEntry; + ULONG ActiveHandles; + ULONG RcbOpenCount; + + PAGED_CODE(); + + // + // We need to be in the FSP to Deregister the MUP. + // + + if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) { + Status = NwPostToFsp( IrpContext, TRUE ); + return( Status ); + } + +#ifdef _PNP_POWER + + // + // Unregister the bind handler with tdi. + // + + if ( TdiBindingHandle != NULL ) { + TdiDeregisterNotificationHandler( TdiBindingHandle ); + TdiBindingHandle = NULL; + } + +#endif + + NwRcb.State = RCB_STATE_SHUTDOWN; + + // + // Invalid all ICBs + // + + SetFlag( IrpContext->Flags, IRP_FLAG_SEND_ALWAYS ); + ActiveHandles = NwInvalidateAllHandles(NULL, IrpContext); + + // + // To expedite shutdown, set retry count down to 2. + // + + DefaultRetryCount = 2; + + // + // Close all VCBs + // + + NwCloseAllVcbs( IrpContext ); + + // + // Logoff and disconnect from all servers. + // + + NwLogoffAllServers( IrpContext, NULL ); + + while ( !IsListEmpty( &LogonList ) ) { + + LogonListEntry = RemoveHeadList( &LogonList ); + + FreeLogon(CONTAINING_RECORD( LogonListEntry, LOGON, Next )); + } + + InsertTailList( &LogonList, &Guest.Next ); // just in-case we don't unload. + + StopTimer(); + + IpxClose(); + + // + // Remember the open count before calling DeristerWithMup since this + // will asynchronously cause handle count to get decremented. + // + + RcbOpenCount = NwRcb.OpenCount; + + DeregisterWithMup( ); + + DebugTrace(0, Dbg, "StopRedirector: Active handle count = %d\n", ActiveHandles ); + + // + // On shutdown, we need 0 remote handles and 2 open handles to + // the redir (one for the service, and one for the MUP) and the timer stopped. + // + + if ( ActiveHandles == 0 && RcbOpenCount <= 2 ) { + return( STATUS_SUCCESS ); + } else { + return( STATUS_REDIRECTOR_HAS_OPEN_HANDLES ); + } +} + + +NTSTATUS +RegisterWithMup( + VOID + ) +/*++ + +Routine Description: + + This routine register this redirector as a UNC provider. + +Arguments: + + None. + +Return Value: + + NTSTATUS - The status of the operation. + +--*/ +{ + NTSTATUS Status; + UNICODE_STRING RdrName; + + PAGED_CODE(); + + RtlInitUnicodeString( &RdrName, DD_NWFS_DEVICE_NAME_U ); + Status = FsRtlRegisterUncProvider( + &MupHandle, + &RdrName, + FALSE // Do not support mailslots + ); + + return( Status ); +} + + + +VOID +DeregisterWithMup( + VOID + ) +/*++ + +Routine Description: + + This routine deregisters this redirector as a UNC provider. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + FsRtlDeregisterUncProvider( MupHandle ); +} + + +NTSTATUS +QueryPath( + PIRP_CONTEXT IrpContext + ) +/*++ + +Routine Description: + + This routine verifies whether a path is a netware path. + +Arguments: + + IrpContext - A pointer to IRP context information for this request. + +Return Value: + + None. + +--*/ +{ + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PQUERY_PATH_REQUEST qpRequest; + PQUERY_PATH_RESPONSE qpResponse; + UNICODE_STRING FilePathName; + ULONG OutputBufferLength; + ULONG InputBufferLength; + SECURITY_SUBJECT_CONTEXT SubjectContext; + + UNICODE_STRING DriveName; + UNICODE_STRING ServerName; + UNICODE_STRING VolumeName; + UNICODE_STRING PathName; + UNICODE_STRING FileName; + UNICODE_STRING UnicodeUid; + WCHAR DriveLetter; + + NTSTATUS status; + + PAGED_CODE(); + + ASSERT (( IOCTL_REDIR_QUERY_PATH & 3) == METHOD_NEITHER); + + RtlInitUnicodeString( &UnicodeUid, NULL ); + + try { + + Irp = IrpContext->pOriginalIrp; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + // + // The input buffer is either in Irp->AssociatedIrp.SystemBuffer, or + // in the Type3InputBuffer for type 3 IRP's. + // + + qpRequest = (PQUERY_PATH_REQUEST)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + qpResponse = (PQUERY_PATH_RESPONSE)qpRequest; + + ASSERT( qpRequest != NULL ); + + FilePathName.Buffer = qpRequest->FilePathName; + FilePathName.Length = (USHORT)qpRequest->PathNameLength; + + status = CrackPath( &FilePathName, &DriveName, &DriveLetter, &ServerName, &VolumeName, &PathName, &FileName, NULL ); + + if (( !NT_SUCCESS( status ) ) || + ( ServerName.Length == 0 )) { + + try_return( status = STATUS_BAD_NETWORK_PATH ); + } + + qpResponse->LengthAccepted = VolumeName.Length; + + // + // As far as the redirector is concerned, QueryPath is a form + // of create. Set up the IrpContext appropriately. + // + + IrpContext->Specific.Create.VolumeName = VolumeName; + IrpContext->Specific.Create.PathName = PathName; + IrpContext->Specific.Create.DriveLetter = DriveLetter; + IrpContext->Specific.Create.FullPathName = FilePathName; + + RtlInitUnicodeString( &IrpContext->Specific.Create.UidConnectName, NULL ); + + // + // The irp context specific data is now zeroed out by AllocateIrpContext, + // so we don't have to worry about re-setting the specific data here. + // + + SeCaptureSubjectContext(&SubjectContext); + + IrpContext->Specific.Create.UserUid = GetUid( &SubjectContext ); + + SeReleaseSubjectContext(&SubjectContext); + + try { + + // + // The slightly more complicated approach. This function + // handles the resolution of the server/volume duple. It + // may use the bindery, cached nds information, or fresh + // nds information. + // + + status = HandleVolumeAttach( IrpContext, + &ServerName, + &VolumeName ); + + } except( NwExceptionFilter( Irp, GetExceptionInformation() )) { + status = STATUS_BAD_NETWORK_PATH; + } + +try_exit: NOTHING; + + } finally { + + RtlFreeUnicodeString(&UnicodeUid); + } + + return( status ); +} + +NTSTATUS +UserNcp( + ULONG IoctlCode, + PIRP_CONTEXT IrpContext + ) +/*++ + +Routine Description: + + This routine exchanges an NCP with the server. + + BUGBUG - We need to filter or security check what the user is + doing. + +Arguments: + + IoctlCode - Supplies the code to be used for the NCP. + + IrpContext - A pointer to IRP context information for this request. + +Return Value: + + Status of transfer. + +--*/ +{ + PIRP irp; + PIO_STACK_LOCATION irpSp; + PVOID OutputBuffer; + ULONG OutputBufferLength; + PCHAR InputBuffer; + ULONG InputBufferLength; + + PICB icb; + PSCB pScb; + NODE_TYPE_CODE nodeTypeCode; + PVOID fsContext; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + UCHAR Function = ANY_NCP_OPCODE( IoctlCode ); + UCHAR Subfunction = 0; + + irp = IrpContext->pOriginalIrp; + irpSp = IoGetCurrentIrpStackLocation( irp ); + + OutputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; + InputBufferLength = irpSp->Parameters.DeviceIoControl.InputBufferLength; + + DebugTrace(+1, DEBUG_TRACE_USERNCP, "UserNcp...\n", 0); + DebugTrace( 0, DEBUG_TRACE_USERNCP, "irp = %08lx\n", (ULONG)irp); + + // + // This F2 and ANY NCP must be addressed either to \Device\NwRdr or + // \Device\NwRdr\ any additional name is not allowed. + // If the handle used for the Irp specifies \Device\NwRdr then the + // redirector gets to choose among the connected servers. + // + // For HANDLE NCP the file must be an FCB. + // + + nodeTypeCode = NwDecodeFileObject( irpSp->FileObject, + &fsContext, + (PVOID *)&icb ); + + if ((nodeTypeCode == NW_NTC_ICB_SCB) && + (!IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode))) { + + // All ok + + // + // Make sure that this ICB is still active. + // + + NwVerifyIcb( icb ); + + pScb = (PSCB)icb->SuperType.Scb; + nodeTypeCode = pScb->NodeTypeCode; + + IrpContext->pScb = pScb; + IrpContext->pNpScb = IrpContext->pScb->pNpScb; + + } else if (nodeTypeCode == NW_NTC_ICB) { + + if ((IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) && + (InputBufferLength < 7)) { + + // Buffer needs enough space for the handle! + DebugTrace(0, DEBUG_TRACE_USERNCP, "Not enough space for handle %x\n", InputBufferLength); + + status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status ); + return status; + } + + // + // Make sure that this ICB is still active. + // Let through FCB's and DCB's + // + + NwVerifyIcb( icb ); + + pScb = (PSCB)icb->SuperType.Fcb->Scb; + nodeTypeCode = icb->SuperType.Fcb->NodeTypeCode; + + IrpContext->pScb = pScb; + IrpContext->pNpScb = IrpContext->pScb->pNpScb; + + // + // Set the icb pointer in case the cache gets + // flushed because the write routines look at it. + // + + IrpContext->Icb = icb; + AcquireFcbAndFlushCache( IrpContext, icb->NpFcb ); + + } else { + + DebugTrace(0, DEBUG_TRACE_USERNCP, "Incorrect nodeTypeCode %x\n", nodeTypeCode); + DebugTrace(0, DEBUG_TRACE_USERNCP, "Incorrect nodeTypeCode %x\n", irpSp->FileObject); + + status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status ); + return status; + } + + if (icb->Pid == INVALID_PID) { + status = NwMapPid( (ULONG)PsGetCurrentThread(), &icb->Pid ); + + if ( !NT_SUCCESS( status ) ) { + return( status ); + } + + DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp Pid = %02lx\n", icb->Pid ); + NwSetEndOfJobRequired(icb->Pid); + + } + + // + // We now know where to send the NCP. Lock down the users buffers and + // build the Mdls required to transfer the data. + // + + InputBuffer = irpSp->Parameters.FileSystemControl.Type3InputBuffer; + + if ( OutputBufferLength ) { + NwLockUserBuffer( irp, IoWriteAccess, OutputBufferLength ); + NwMapUserBuffer( irp, KernelMode, (PVOID *)&OutputBuffer ); + } else { + OutputBuffer = NULL; + } + + // + // Update the original MDL record in the Irp context, since + // NwLockUserBuffer may have created a new MDL. + // + + IrpContext->pOriginalMdlAddress = irp->MdlAddress; + + if (InputBufferLength != 0) { + if (IS_IT_NWR_ANY_NCP(IoctlCode)) { + Subfunction = InputBuffer[0]; + } else if (InputBufferLength >= 3) { + Subfunction = InputBuffer[2]; + } + } + + + DebugTrace( 0, DEBUG_TRACE_USERNCP, "UserNcp function = %x\n", Function ); + DebugTrace( 0, DEBUG_TRACE_USERNCP, " & Subfunction = %x\n", Subfunction ); + dump( DEBUG_TRACE_USERNCP, InputBuffer, InputBufferLength ); + //dump( DEBUG_TRACE_USERNCP, OutputBuffer, OutputBufferLength ); + + if ((Function == NCP_ADMIN_FUNCTION ) && + (InputBufferLength >= 4 )) { + + if ( ( (Subfunction == NCP_SUBFUNC_79) || + (Subfunction == NCP_CREATE_QUEUE_JOB ) ) && + icb->HasRemoteHandle) { + + // + // Trying to create a job on a queue that already has a job + // on it. Cancel the old job. + // + + status = ExchangeWithWait( + IrpContext, + SynchronousResponseCallback, + "Sdw", + NCP_ADMIN_FUNCTION, NCP_CLOSE_FILE_AND_CANCEL_JOB, // Close File And Cancel Queue Job + icb->SuperType.Fcb->Vcb->Specific.Print.QueueId, + icb->JobId ); + + if (!NT_SUCCESS(status)) { + + DebugTrace( 0, DEBUG_TRACE_USERNCP, "DeleteOldJob got status -> %08lx\n", status ); + // Don't worry if the delete fails, proceed with the create + } + + icb->IsPrintJob = FALSE; // App will have to queue or cancel job, not rdr + + } else if ((Subfunction == NCP_PLAIN_TEXT_LOGIN ) || + (Subfunction == NCP_ENCRYPTED_LOGIN )) { + + UNICODE_STRING UserName; + OEM_STRING OemUserName; + PUCHAR InputBuffer; + + // + // Trying to do a login. + // + + // + // Queue ourselves to the SCB, and wait to get to the front to + // protect access to server State. + // + + NwAppendToQueueAndWait( IrpContext ); + + // + // Assume success, store the user name in the SCB. + // + + try { + + InputBuffer = irpSp->Parameters.FileSystemControl.Type3InputBuffer; + + OemUserName.Length = InputBuffer[ 13 ]; + OemUserName.Buffer = &InputBuffer[14]; + + UserName.MaximumLength = OemUserName.Length * sizeof(WCHAR); + if ( OemUserName.Length == 0 || OemUserName.Length > MAX_USER_NAME_LENGTH ) { + try_return( status = STATUS_NO_SUCH_USER ); + } + + UserName.Buffer = ALLOCATE_POOL_EX( NonPagedPool, UserName.MaximumLength ); + + // + // Note the the Rtl function would set pUString->Buffer = NULL, + // if OemString.Length is 0. + // + + if ( OemUserName.Length != 0 ) { + status = RtlOemStringToCountedUnicodeString( &UserName, &OemUserName, FALSE ); + } else { + UserName.Length = 0; + } +try_exit: NOTHING; + } finally { + NOTHING; + } + + if ( NT_SUCCESS( status )) { + + if ( pScb->OpenFileCount != 0 && + pScb->pNpScb->State == SCB_STATE_IN_USE ) { + + if (!RtlEqualUnicodeString( &pScb->UserName, &UserName, TRUE )) { + + // + // But were already logged in to this server and at + // least one other handle is using the connection and + // the user is trying to change the username. + // + + FREE_POOL( UserName.Buffer ); + return STATUS_NETWORK_CREDENTIAL_CONFLICT; + + } else { + + PUCHAR VerifyBuffer = ALLOCATE_POOL( PagedPool, InputBufferLength ); + + // + // Same username. Validate password is correct. + + if (VerifyBuffer == NULL) { + FREE_POOL( UserName.Buffer ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory( VerifyBuffer, InputBuffer, InputBufferLength ); + + if (IS_IT_NWR_ANY_NCP(IoctlCode)) { + VerifyBuffer[0] = (Subfunction == NCP_PLAIN_TEXT_LOGIN ) ? + NCP_PLAIN_TEXT_VERIFY_PASSWORD: + NCP_ENCRYPTED_VERIFY_PASSWORD; + + } else { + VerifyBuffer[2] = (Subfunction == NCP_PLAIN_TEXT_LOGIN ) ? + NCP_PLAIN_TEXT_VERIFY_PASSWORD: + NCP_ENCRYPTED_VERIFY_PASSWORD; + } + + status = ExchangeWithWait( + IrpContext, + SynchronousResponseCallback, + IS_IT_NWR_ANY_NCP(IoctlCode)? "Sr":"Fbr", + Function, VerifyBuffer[0], + &VerifyBuffer[1], InputBufferLength - 1 ); + + FREE_POOL( UserName.Buffer ); + FREE_POOL( VerifyBuffer ); + return status; + + } + } + + if (pScb->UserName.Buffer) { + FREE_POOL( pScb->UserName.Buffer ); // May include space for password too. + } + + IrpContext->pNpScb->pScb->UserName = UserName; + IrpContext->pNpScb->pScb->Password.Buffer = UserName.Buffer; + IrpContext->pNpScb->pScb->Password.Length = 0; + + } else { + return( status ); + } + } + } else if (Function == NCP_LOGOUT ) { + + // + // Queue ourselves to the SCB, and wait to get to the front to + // protect access to server State. + // + + NwAppendToQueueAndWait( IrpContext ); + + if ( pScb->OpenFileCount == 0 && + pScb->pNpScb->State == SCB_STATE_IN_USE && + !pScb->PreferredServer ) { + + NwLogoffAndDisconnect( IrpContext, pScb->pNpScb); + return STATUS_SUCCESS; + + } else { + + return(STATUS_CONNECTION_IN_USE); + + } + } + + IrpContext->Icb = icb; + + // + // Remember where the response goes. + // + + IrpContext->Specific.FileSystemControl.Buffer = OutputBuffer; + IrpContext->Specific.FileSystemControl.Length = OutputBufferLength; + + IrpContext->Specific.FileSystemControl.Function = Function; + IrpContext->Specific.FileSystemControl.Subfunction = Subfunction; + + // + // Decide how to send the buffer. If it is small enough, send it + // by copying the user buffer to our send buffer. If it is bigger + // we will need to build an MDL for the user's buffer, and used a + // chained send. + // + + if ( InputBufferLength == 0 ) { + + // Simple request such as systime.exe + + IrpContext->Specific.FileSystemControl.InputMdl = NULL; + + status = Exchange( + IrpContext, + UserNcpCallback, + "F", Function); + + } else if ( InputBufferLength < MAX_SEND_DATA - sizeof( NCP_REQUEST ) - 2 ) { + + // + // Send the request by copying it to our send buffer. + // + + IrpContext->Specific.FileSystemControl.InputMdl = NULL; + + if (!IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) { + + // + // E0, E1, E2 and E3 get mapped to 14,15,16 and 17. These need + // a length word before the buffer. + // + + status = Exchange( + IrpContext, + UserNcpCallback, + IS_IT_NWR_ANY_NCP(IoctlCode)? "Sr":"Fbr", + Function, InputBuffer[0], + &InputBuffer[1], InputBufferLength - 1 ); + } else { + + // + // Replace the 6 bytes of InputBuffer starting at offset 1 + // with the 6 byte NetWare address for this icb. This request + // is used in some of the 16 bit NCP's used for file locking. + // These requests are always fairly small. + // + + if (!icb->HasRemoteHandle) { + return STATUS_INVALID_HANDLE; + } + + status = Exchange( + IrpContext, + UserNcpCallback, + "Fbrr", + Function, + InputBuffer[0], + &icb->Handle, sizeof(icb->Handle), + &InputBuffer[7], InputBufferLength - 7 ); + } + + } else { + + PMDL pMdl = NULL; + + if (IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) { + return STATUS_INVALID_PARAMETER; + } + + // + // We need to chain send the request. Allocate an MDL. + // + + try { + pMdl = ALLOCATE_MDL( + &InputBuffer[1], + InputBufferLength - 1, + TRUE, // Secondary MDL + TRUE, // Charge quota + NULL ); + + if ( pMdl == NULL ) { + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + } + + MmProbeAndLockPages( pMdl, irp->RequestorMode, IoReadAccess ); + + // + // Remember the MDL so we can free it. + // + + IrpContext->Specific.FileSystemControl.InputMdl = pMdl; + + // + // Send the request. + // + + status = Exchange( + IrpContext, + UserNcpCallback, + IS_IT_NWR_ANY_NCP(IoctlCode)? "Sf":"Fbf", + Function, InputBuffer[0], + pMdl ); + + + } finally { + + if ((status != STATUS_PENDING ) && + ( pMdl != NULL)) { + + FREE_MDL( pMdl ); + + } + } + } + + DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status ); + return status; +} + + + +NTSTATUS +UserNcpCallback ( + IN PIRP_CONTEXT IrpContext, + IN ULONG BytesAvailable, + IN PUCHAR Response + ) + +/*++ + +Routine Description: + + This routine receives the response from a user NCP. + +Arguments: + + +Return Value: + + VOID + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PVOID Buffer; + ULONG BufferLength; + PIRP Irp; + ULONG Length; + PICB Icb = IrpContext->Icb; + PEPresponse *pResponseParameters; + + DebugTrace(0, DEBUG_TRACE_USERNCP, "UserNcpCallback...\n", 0); + + if ( IrpContext->Specific.FileSystemControl.InputMdl != NULL ) { + MmUnlockPages( IrpContext->Specific.FileSystemControl.InputMdl ); + FREE_MDL( IrpContext->Specific.FileSystemControl.InputMdl ); + } + + if ( BytesAvailable == 0) { + + // + // No response from server. Status is in pIrpContext-> + // ResponseParameters.Error + // + + NwDequeueIrpContext( IrpContext, FALSE ); + NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING ); + + return STATUS_REMOTE_NOT_LISTENING; + } + + dump( DEBUG_TRACE_USERNCP, Response, BytesAvailable ); + + Buffer = IrpContext->Specific.FileSystemControl.Buffer; + BufferLength = IrpContext->Specific.FileSystemControl.Length; + + // + // Get the data from the response. + // + + Length = MIN( BufferLength, BytesAvailable - 8 ); + + if (IrpContext->Specific.FileSystemControl.Function == NCP_ADMIN_FUNCTION ) { + + if (IrpContext->Specific.FileSystemControl.Subfunction == NCP_SUBFUNC_79) { + + // + // Create Queue Job and File Ncp. If the operation was a success + // then we need to save the handle. This will allow Write Irps + // on this Icb to be sent to the server. + // + + Status = ParseResponse( + IrpContext, + Response, + BytesAvailable, + "N_r", + 0x3E, + Icb->Handle+2,4); + + // Pad the handle to its full 6 bytes. + Icb->Handle[0] = 0; + Icb->Handle[1] = 0; + + if (NT_SUCCESS(Status)) { + Icb->HasRemoteHandle = TRUE; + } + + // + // Reset the file offset. + // + + Icb->FileObject->CurrentByteOffset.QuadPart = 0; + + } else if (IrpContext->Specific.FileSystemControl.Subfunction == NCP_CREATE_QUEUE_JOB ) { + + // + // Create Queue Job and File Ncp. If the operation was a success + // then we need to save the handle. This will allow Write Irps + // on this Icb to be sent to the server. + // + + Status = ParseResponse( + IrpContext, + Response, + BytesAvailable, + "N_r", + 0x2A, + Icb->Handle,6); + + if (NT_SUCCESS(Status)) { + Icb->HasRemoteHandle = TRUE; + } + + // + // Reset the file offset. + // + + Icb->FileObject->CurrentByteOffset.QuadPart = 0; + + } else if ((IrpContext->Specific.FileSystemControl.Subfunction == NCP_SUBFUNC_7F) || + (IrpContext->Specific.FileSystemControl.Subfunction == NCP_CLOSE_FILE_AND_START_JOB )) { + + // End Job request + + Icb->HasRemoteHandle = FALSE; + + } else if ((IrpContext->Specific.FileSystemControl.Subfunction == NCP_PLAIN_TEXT_LOGIN ) || + (IrpContext->Specific.FileSystemControl.Subfunction == NCP_ENCRYPTED_LOGIN )) { + + // + // Trying to do a login from a 16 bit application. + // + + Status = ParseResponse( + IrpContext, + Response, + BytesAvailable, + "N" ); + + if ( NT_SUCCESS( Status ) ) { + + + // + // Set the reconnect attempt flag so that we don't try to + // run this irp context through the reconnect logic. Doing + // this could deadlock the worker thread that's handling this + // fsp side request. + // + + SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ); + IrpContext->PostProcessRoutine = FspCompleteLogin; + Status = NwPostToFsp( IrpContext, TRUE ); + return Status; + + } else { + if (IrpContext->pNpScb->pScb->UserName.Buffer) { + FREE_POOL( IrpContext->pNpScb->pScb->UserName.Buffer ); + } + RtlInitUnicodeString( &IrpContext->pNpScb->pScb->UserName, NULL); + RtlInitUnicodeString( &IrpContext->pNpScb->pScb->Password, NULL); + } + } + } + + pResponseParameters = (PEPresponse *)( ((PEPrequest *)Response) + 1); + + ParseResponse( IrpContext, Response, BytesAvailable, "Nr", Buffer, Length ); + + Status = ( ( pResponseParameters->status & + ( NCP_STATUS_BAD_CONNECTION | + NCP_STATUS_NO_CONNECTIONS | + NCP_STATUS_SERVER_DOWN ) ) << 8 ) | + pResponseParameters->error; + + if ( Status ) { + // + // Use the special error code that will cause conversion + // of the status back to a Dos error code to leave status and + // error unchanged. This is necessary because many of the + // NetWare error codes have different meanings depending on the + // operation being performed. + // + + Status |= 0xc0010000; + } + + Irp = IrpContext->pOriginalIrp; + Irp->IoStatus.Information = Length; + + // + // We're done with this request. Dequeue the IRP context from + // SCB and complete the request. + // + + NwDequeueIrpContext( IrpContext, FALSE ); + NwCompleteRequest( IrpContext, Status ); + + return STATUS_SUCCESS; + +} + + +NTSTATUS +FspCompleteLogin( + PIRP_CONTEXT IrpContext + ) +/*++ + +Routine Description: + + This routine reopens any Vcb directory handles. + It also sets the Scb as in use. This could have been done + in the callback routine too. + +Arguments: + + None. + +Return Value: + + NTSTATUS - The status of the operation. + +--*/ +{ + + IrpContext->pNpScb->State = SCB_STATE_IN_USE; + + ReconnectScb( IrpContext, IrpContext->pScb ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +GetConnection( + PIRP_CONTEXT IrpContext + ) +/*++ + +Routine Description: + + This routine returns the path of a connection. + +Arguments: + + None. + +Return Value: + + NTSTATUS - The status of the operation. + +--*/ +{ + NTSTATUS Status; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PNWR_SERVER_RESOURCE OutputBuffer; + PNWR_REQUEST_PACKET InputBuffer; + ULONG InputBufferLength; + ULONG OutputBufferLength; + PVCB Vcb; + PWCH DriveName; + ULONG DriveNameLength; + UNICODE_STRING Path; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "GetConnection...\n", 0); + Irp = IrpContext->pOriginalIrp; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + + if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConn.DeviceName[1] ) ) { + return( STATUS_INVALID_PARAMETER ); + } + + Status = STATUS_SUCCESS; + + try { + + // + // Find the VCB + // + + DriveName = InputBuffer->Parameters.GetConn.DeviceName; + DriveNameLength = InputBuffer->Parameters.GetConn.DeviceNameLength; + Vcb = NULL; + + if ( DriveName[0] >= L'A' && DriveName[0] <= L'Z' && + DriveName[1] == L':' && + DriveNameLength == sizeof( L"X:" ) - sizeof( L'\0' ) ) { + + Vcb = DriveMapTable[DriveName[0] - 'A']; + + } else if ( _wcsnicmp( DriveName, L"LPT", 3 ) == 0 && + DriveName[3] >= '1' && DriveName[3] <= '9' && + DriveNameLength == sizeof( L"LPTX" ) - sizeof( L'\0' ) ) { + + Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveName[3] - '1']; + } + + if ( Vcb == NULL) { + try_return( Status = STATUS_NO_SUCH_FILE ); + } + + OutputBuffer = (PNWR_SERVER_RESOURCE)Irp->UserBuffer; + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + // + // Calculate the VCB path to return. + // + // BUGBUG. We shouldn't have to recalc all the time. Add a + // new string the the VCB to remember this info. Init it + // in NwCreateVcb. + // + + if ( Vcb->DriveLetter >= L'A' && Vcb->DriveLetter <= L'Z' ) { + Path.Buffer = Vcb->Name.Buffer + 3; + Path.Length = Vcb->Name.Length - 6; + } else if ( Vcb->DriveLetter >= L'1' && Vcb->DriveLetter <= L'9' ) { + Path.Buffer = Vcb->Name.Buffer + 5; + Path.Length = Vcb->Name.Length - 10; + } else { + Path = Vcb->Name; + } + + // Strip off the unicode prefix + + Path.Buffer += Vcb->Scb->UnicodeUid.Length/sizeof(WCHAR); + Path.Length -= Vcb->Scb->UnicodeUid.Length; + Path.MaximumLength -= Vcb->Scb->UnicodeUid.Length; + + if (OutputBufferLength < Path.Length + 2 * sizeof(WCHAR)) { + InputBuffer->Parameters.GetConn.BytesNeeded = + Path.Length + 2 * sizeof(WCHAR); + try_return( Status = STATUS_BUFFER_TOO_SMALL ); + } + + // + // Return the Connection name in the form \\server\share + // + + OutputBuffer->UncName[0] = L'\\'; + + RtlMoveMemory( + &OutputBuffer->UncName[1], + Path.Buffer, + Path.Length ); + + OutputBuffer->UncName[ (Path.Length + sizeof(WCHAR)) / sizeof(WCHAR) ] = L'\0'; + + Irp->IoStatus.Information = Path.Length + 2 * sizeof(WCHAR); + +try_exit: NOTHING; + + } finally { + NOTHING; + } + + return( Status ); +} + + +NTSTATUS +DeleteConnection( + PIRP_CONTEXT IrpContext + ) +/*++ + +Routine Description: + + This routine returns removes a connection if force is specified or + if there are no open handles on this Vcb. + +Arguments: + + None. + +Return Value: + + NTSTATUS - The status of the operation. + +--*/ +{ + NTSTATUS Status; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PNWR_REQUEST_PACKET InputBuffer; + ULONG InputBufferLength; + PICB Icb; + PVCB Vcb; + PDCB Dcb; + PNONPAGED_DCB NonPagedDcb; + NODE_TYPE_CODE NodeTypeCode; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "DeleteConnection...\n", 0); + Irp = IrpContext->pOriginalIrp; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + + if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConn.DeviceName[1] ) ) { + return( STATUS_INVALID_PARAMETER ); + } + + Status = STATUS_SUCCESS; + + // + // Wait to get to the head of the SCB queue. We do this in case + // we need to disconnect, so that we can send packets with the RCB + // resource held. + // + + NodeTypeCode = NwDecodeFileObject( IrpSp->FileObject, &NonPagedDcb, &Icb ); + + if ( NodeTypeCode == NW_NTC_ICB_SCB ) { + IrpContext->pNpScb = Icb->SuperType.Scb->pNpScb; + } else { + ASSERT( NodeTypeCode == NW_NTC_ICB ); + IrpContext->pNpScb = Icb->SuperType.Fcb->Scb->pNpScb; + Dcb = NonPagedDcb->Fcb; + } + + NwAppendToQueueAndWait( IrpContext ); + ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE ); + + // + // Acquire exclusive access to the RCB. + // + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + try { + + // + // Get the a referenced pointer to the node and make sure it is + // not being closed, and that it is a directory handle. + // + + if ( NodeTypeCode == NW_NTC_ICB_SCB ) { + + + if ( Icb->IsTreeHandle ) { + + // + // Do an NDS logoff. This will release the RCB. + // + + Status = NdsLogoff( IrpContext ); + DebugTrace( 0, Dbg, "Nds tree logoff -> %08lx\n", Status ); + + } else { + + DebugTrace( 0, Dbg, "Delete connection to SCB %X\n", Icb->SuperType.Scb ); + + Status = TreeDisconnectScb( IrpContext, Icb->SuperType.Scb ); + DebugTrace(-1, Dbg, "DeleteConnection -> %08lx\n", Status ); + + } + + try_return( NOTHING ); + + } else if ( NodeTypeCode != NW_NTC_ICB || + Dcb == NULL || + ( Dcb->NodeTypeCode != NW_NTC_DCB && + Dcb->NodeTypeCode != NW_NTC_FCB) ) { + + DebugTrace(0, Dbg, "Invalid file handle\n", 0); + + Status = STATUS_INVALID_HANDLE; + + DebugTrace(-1, Dbg, "DeleteConnection -> %08lx\n", Status ); + try_return( NOTHING ); + } + + // + // Make sure that this ICB is still active. + // + + NwVerifyIcb( Icb ); + + Vcb = Dcb->Vcb; + DebugTrace(0, Dbg, "Attempt to delete VCB = %08lx\n", Vcb); + + // + // Vcb->OpenFileCount will be 1, (to account for this DCB), if the + // connection can be deleted. + // + + if ( !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ) ) { + DebugTrace(0, Dbg, "Cannot delete unredireced connection\n", 0); + try_return( Status = STATUS_INVALID_DEVICE_REQUEST ); + } else { + + if ( Vcb->OpenFileCount > 1 ) { + DebugTrace(0, Dbg, "Cannot delete in use connection\n", 0); + Status = STATUS_CONNECTION_IN_USE; + } else { + + // + // To delete the VCB, simply dereference it. + // + + DebugTrace(0, Dbg, "Deleting connection\n", 0); + + ClearFlag( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ); + --Vcb->Scb->OpenFileCount; + + NwDereferenceVcb( Vcb, IrpContext, TRUE ); + } + } + + try_exit: NOTHING; + + } finally { + + + // + // An NDS logoff will have already freed the RCB + // and dequeued the irp context. + // + + if ( ! ( Icb->IsTreeHandle ) ) { + NwReleaseRcb( &NwRcb ); + NwDequeueIrpContext( IrpContext, FALSE ); + } + + + } + + Irp->IoStatus.Information = 0; + + return( Status ); +} + + + +NTSTATUS +EnumConnections( + PIRP_CONTEXT IrpContext + ) +/*++ + +Routine Description: + + This routine returns the list of redirector connections. + +Arguments: + + IrpContext - A pointer to the IRP Context block for this request. + +Return Value: + + NTSTATUS - The status of the operation. + +--*/ +{ + NTSTATUS Status; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PNWR_SERVER_RESOURCE OutputBuffer; + PNWR_REQUEST_PACKET InputBuffer; + ULONG InputBufferLength; + ULONG OutputBufferLength; + PVCB Vcb; + PSCB Scb; + BOOLEAN OwnRcb; + + UNICODE_STRING LocalName; + UNICODE_STRING ContainerName; + PCHAR FixedPortion; + PWCHAR EndOfVariableData; + ULONG EntrySize; + ULONG ResumeKey; + + ULONG ShareType; + ULONG EntriesRead = 0; + ULONG EntriesRequested; + DWORD ConnectionType; + + PLIST_ENTRY ListEntry; + UNICODE_STRING Path; + + DebugTrace(0, Dbg, "EnumConnections...\n", 0); + + Irp = IrpContext->pOriginalIrp; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + + if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.EnumConn.BytesNeeded ) ) { + return( STATUS_INVALID_PARAMETER ); + } + + OutputBuffer = (PNWR_SERVER_RESOURCE)Irp->UserBuffer; + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + Status = STATUS_SUCCESS; + + try { + + // + // Acquire shared access to the drive map table. + // + + NwAcquireSharedRcb( &NwRcb, TRUE ); + OwnRcb = TRUE; + + // + // Initialize returned strings + // + + RtlInitUnicodeString( &ContainerName, L"\\" ); + + FixedPortion = (PCHAR) OutputBuffer; + EndOfVariableData = (PWCHAR) ((ULONG) FixedPortion + OutputBufferLength); + ConnectionType = InputBuffer->Parameters.EnumConn.ConnectionType; + + EntriesRequested = InputBuffer->Parameters.EnumConn.EntriesRequested; + + // + // Run through the global VCB list looking for redirections. + // + + ResumeKey = InputBuffer->Parameters.EnumConn.ResumeKey; + + DebugTrace(0, Dbg, "Starting resume key is %d\n", InputBuffer->Parameters.EnumConn.ResumeKey ); + + for ( ListEntry = GlobalVcbList.Flink; + ListEntry != &GlobalVcbList && + EntriesRequested > EntriesRead && + Status == STATUS_SUCCESS ; + ListEntry = ListEntry->Flink ) { + + Vcb = CONTAINING_RECORD( ListEntry, VCB, GlobalVcbListEntry ); + + // + // Skip connections that we've already enumerated. + // + + if ( Vcb->SequenceNumber <= InputBuffer->Parameters.EnumConn.ResumeKey ) { + continue; + } + + // + // Skip implicit connections, if they are not requested. + // + + if ( !(ConnectionType & CONNTYPE_IMPLICIT) && + !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION )) { + + continue; + } + + // + // Skip connections that are not requested. + // + if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE )) { + if ( !( ConnectionType & CONNTYPE_PRINT )) + continue; + } else { + if ( !( ConnectionType & CONNTYPE_DISK )) + continue; + } + + + if ( Vcb->DriveLetter != 0 ) { + if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE )) { + RtlInitUnicodeString( &LocalName, L"LPT1" ); + LocalName.Buffer[3] = Vcb->DriveLetter; + ShareType = RESOURCETYPE_PRINT; + } else { + RtlInitUnicodeString( &LocalName, L"A:" ); + LocalName.Buffer[0] = Vcb->DriveLetter; + ShareType = RESOURCETYPE_DISK; + } + } else { // No drive letter connection, i.e. UNC Connection + if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE )) + ShareType = RESOURCETYPE_PRINT; + else + ShareType = RESOURCETYPE_DISK; + } + + if ( Vcb->DriveLetter >= L'A' && Vcb->DriveLetter <= L'Z' ) { + Path.Buffer = Vcb->Name.Buffer + 3; + Path.Length = Vcb->Name.Length - 6; + } else if ( Vcb->DriveLetter >= L'1' && Vcb->DriveLetter <= L'9' ) { + Path.Buffer = Vcb->Name.Buffer + 5; + Path.Length = Vcb->Name.Length - 10; + } else { + Path = Vcb->Name; + } + + // Strip off the unicode prefix + + Path.Buffer += Vcb->Scb->UnicodeUid.Length/sizeof(WCHAR); + Path.Length -= Vcb->Scb->UnicodeUid.Length; + Path.MaximumLength -= Vcb->Scb->UnicodeUid.Length; + + Status = WriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + &ContainerName, + Vcb->DriveLetter != 0 ? &LocalName : NULL, + &Path, + RESOURCE_CONNECTED, + RESOURCEDISPLAYTYPE_SHARE, + RESOURCEUSAGE_CONNECTABLE, + ShareType, + &EntrySize + ); + + if ( Status == STATUS_MORE_ENTRIES ) { + + // + // Could not write current entry into output buffer. + // + + InputBuffer->Parameters.EnumConn.BytesNeeded = EntrySize; + + } else if ( Status == STATUS_SUCCESS ) { + + // + // Note that we've returned the current entry. + // + + EntriesRead++; + ResumeKey = Vcb->SequenceNumber; + + DebugTrace(0, Dbg, "Returning VCB %08lx\n", Vcb ); + DebugTrace(0, Dbg, "Sequence # is %08lx\n", ResumeKey ); + } + } + + // + // Return the Servers we are connected to. This is most important for + // support of NetWare aware 16 bit apps. + // + + if ((ConnectionType & CONNTYPE_IMPLICIT) && + ( ConnectionType & CONNTYPE_DISK )) { + + KIRQL OldIrql; + PNONPAGED_SCB pNpScb; + PLIST_ENTRY NextScbQueueEntry; + ULONG EnumSequenceNumber = 0x80000000; + + NwReleaseRcb( &NwRcb ); + OwnRcb = FALSE; + + RtlInitUnicodeString( &ContainerName, L"\\\\" ); + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + + for ( ListEntry = ScbQueue.Flink; + ListEntry != &ScbQueue && + EntriesRequested > EntriesRead && + Status == STATUS_SUCCESS ; + ListEntry = NextScbQueueEntry ) { + + pNpScb = CONTAINING_RECORD( ListEntry, NONPAGED_SCB, ScbLinks ); + Scb = pNpScb->pScb; + + NwReferenceScb( pNpScb ); + + KeReleaseSpinLock(&ScbSpinLock, OldIrql); + + // + // Skip connections that we've already enumerated. + // + + if (( EnumSequenceNumber <= InputBuffer->Parameters.EnumConn.ResumeKey ) || + + ( pNpScb == &NwPermanentNpScb ) || + + (( pNpScb->State != SCB_STATE_LOGIN_REQUIRED ) && + ( pNpScb->State != SCB_STATE_IN_USE ))) { + + // + // Move to next entry in the list + // + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + NextScbQueueEntry = pNpScb->ScbLinks.Flink; + NwDereferenceScb( pNpScb ); + EnumSequenceNumber++; + continue; + } + + DebugTrace( 0, Dbg, " EnumConnections returning Servername = %wZ\n", &pNpScb->ServerName ); + + Status = WriteNetResourceEntry( + &FixedPortion, + &EndOfVariableData, + &ContainerName, + NULL, + &pNpScb->ServerName, + RESOURCE_CONNECTED, + RESOURCEDISPLAYTYPE_SHARE, + RESOURCEUSAGE_CONNECTABLE, + RESOURCETYPE_DISK, + &EntrySize + ); + + if ( Status == STATUS_MORE_ENTRIES ) { + + // + // Could not write current entry into output buffer. + // + + InputBuffer->Parameters.EnumConn.BytesNeeded = EntrySize; + + } else if ( Status == STATUS_SUCCESS ) { + + // + // Note that we've returned the current entry. + // + + EntriesRead++; + ResumeKey = EnumSequenceNumber; + + DebugTrace(0, Dbg, "Returning SCB %08lx\n", Scb ); + DebugTrace(0, Dbg, "Sequence # is %08lx\n", ResumeKey ); + } + + // + // Move to next entry in the list + // + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + NextScbQueueEntry = pNpScb->ScbLinks.Flink; + NwDereferenceScb( pNpScb ); + EnumSequenceNumber++; + } + + KeReleaseSpinLock(&ScbSpinLock, OldIrql); + } + + InputBuffer->Parameters.EnumConn.EntriesReturned = EntriesRead; + InputBuffer->Parameters.EnumConn.ResumeKey = ResumeKey; + + if ( EntriesRead == 0 ) { + + if (Status == STATUS_SUCCESS) { + Status = STATUS_NO_MORE_ENTRIES; + } + + Irp->IoStatus.Information = 0; + } + else { + Irp->IoStatus.Information = OutputBufferLength; + } + + } finally { + if (OwnRcb) { + NwReleaseRcb( &NwRcb ); + } + } + + return( Status ); +} + + + +NTSTATUS +WriteNetResourceEntry( + IN OUT PCHAR *FixedPortion, + IN OUT PWCHAR *EndOfVariableData, + IN PUNICODE_STRING ContainerName OPTIONAL, + IN PUNICODE_STRING LocalName OPTIONAL, + IN PUNICODE_STRING RemoteName, + IN ULONG ScopeFlag, + IN ULONG DisplayFlag, + IN ULONG UsageFlag, + IN ULONG ShareType, + OUT PULONG EntrySize + ) +/*++ + +Routine Description: + + This function packages a NETRESOURCE entry into the user output buffer. + +Arguments: + + FixedPortion - Supplies a pointer to the output buffer where the next + entry of the fixed portion of the use information will be written. + This pointer is updated to point to the next fixed portion entry + after a NETRESOURCE entry is written. + + EndOfVariableData - Supplies a pointer just off the last available byte + in the output buffer. This is because the variable portion of the + user information is written into the output buffer starting from + the end. + + This pointer is updated after any variable length information is + written to the output buffer. + + ContainerName - Supplies the full path qualifier to make RemoteName + a full UNC name. + + LocalName - Supplies the local device name, if any. + + RemoteName - Supplies the remote resource name. + + ScopeFlag - Supplies the flag which indicates whether this is a + CONNECTED or GLOBALNET resource. + + DisplayFlag - Supplies the flag which tells the UI how to display + the resource. + + UsageFlag - Supplies the flag which indicates that the RemoteName + is either a container or a connectable resource or both. + + ShareType - Type of the share connected to, RESOURCETYPE_PRINT or + RESOURCETYPE_DISK + + EntrySize - Receives the size of the NETRESOURCE entry in bytes. + +Return Value: + + STATUS_SUCCESS - Successfully wrote entry into user buffer. + + STATUS_NO_MEMORY - Failed to allocate work buffer. + + STATUS_MORE_ENTRIES - Buffer was too small to fit entry. + +--*/ +{ + BOOLEAN FitInBuffer = TRUE; + LPNETRESOURCEW NetR = (LPNETRESOURCEW) *FixedPortion; + UNICODE_STRING TmpRemote; + + PAGED_CODE(); + + *EntrySize = sizeof(NETRESOURCEW) + + RemoteName->Length + NwProviderName.Length + 2 * sizeof(WCHAR); + + if (ARGUMENT_PRESENT(LocalName)) { + *EntrySize += LocalName->Length + sizeof(WCHAR); + } + + if (ARGUMENT_PRESENT(ContainerName)) { + *EntrySize += ContainerName->Length; + } + + // + // See if buffer is large enough to fit the entry. + // + if (((ULONG) *FixedPortion + *EntrySize) > + (ULONG) *EndOfVariableData) { + + return STATUS_MORE_ENTRIES; + } + + NetR->dwScope = ScopeFlag; + NetR->dwType = ShareType; + NetR->dwDisplayType = DisplayFlag; + NetR->dwUsage = UsageFlag; + NetR->lpComment = NULL; + + // + // Update fixed entry pointer to next entry. + // + (ULONG) (*FixedPortion) += sizeof(NETRESOURCEW); + + // + // RemoteName + // + if (ARGUMENT_PRESENT(ContainerName)) { + + // + // Prefix the RemoteName with its container name making the + // it a fully-qualified UNC name. + // + + TmpRemote.MaximumLength = RemoteName->Length + ContainerName->Length + sizeof(WCHAR); + TmpRemote.Buffer = ALLOCATE_POOL( + PagedPool, + RemoteName->Length + ContainerName->Length + sizeof(WCHAR) + ); + + if (TmpRemote.Buffer == NULL) { + return STATUS_NO_MEMORY; + } + + RtlCopyUnicodeString(&TmpRemote, ContainerName); + RtlAppendUnicodeStringToString(&TmpRemote, RemoteName); + } + else { + TmpRemote = *RemoteName; + } + + FitInBuffer = CopyStringToBuffer( + TmpRemote.Buffer, + TmpRemote.Length / sizeof(WCHAR), + (LPCWSTR) *FixedPortion, + EndOfVariableData, + &NetR->lpRemoteName + ); + + if (ARGUMENT_PRESENT(ContainerName)) { + FREE_POOL(TmpRemote.Buffer); + } + + ASSERT(FitInBuffer); + + // + // LocalName + // + if (ARGUMENT_PRESENT(LocalName)) { + FitInBuffer = CopyStringToBuffer( + LocalName->Buffer, + LocalName->Length / sizeof(WCHAR), + (LPCWSTR) *FixedPortion, + EndOfVariableData, + &NetR->lpLocalName + ); + + ASSERT(FitInBuffer); + } + else { + NetR->lpLocalName = NULL; + } + + // + // ProviderName + // + + FitInBuffer = CopyStringToBuffer( + NwProviderName.Buffer, + NwProviderName.Length / sizeof(WCHAR), + (LPCWSTR) *FixedPortion, + EndOfVariableData, + &NetR->lpProvider + ); + + ASSERT(FitInBuffer); + + if (! FitInBuffer) { + return STATUS_MORE_ENTRIES; + } + + return STATUS_SUCCESS; +} + +BOOL +CopyStringToBuffer( + IN LPCWSTR SourceString OPTIONAL, + IN DWORD CharacterCount, + IN LPCWSTR FixedDataEnd, + IN OUT LPWSTR *EndOfVariableData, + OUT LPWSTR *VariableDataPointer + ) + +/*++ + +Routine Description: + + This is based on ..\nwlib\NwlibCopyStringToBuffer + + This routine puts a single variable-length string into an output buffer. + The string is not written if it would overwrite the last fixed structure + in the buffer. + +Arguments: + + SourceString - Supplies a pointer to the source string to copy into the + output buffer. If SourceString is null then a pointer to a zero terminator + is inserted into output buffer. + + CharacterCount - Supplies the length of SourceString, not including zero + terminator. (This in units of characters - not bytes). + + FixedDataEnd - Supplies a pointer to just after the end of the last + fixed structure in the buffer. + + EndOfVariableData - Supplies an address to a pointer to just after the + last position in the output buffer that variable data can occupy. + Returns a pointer to the string written in the output buffer. + + VariableDataPointer - Supplies a pointer to the place in the fixed + portion of the output buffer where a pointer to the variable data + should be written. + +Return Value: + + Returns TRUE if string fits into output buffer, FALSE otherwise. + +--*/ +{ + DWORD CharsNeeded = (CharacterCount + 1); + + PAGED_CODE(); + + // + // Determine if source string will fit, allowing for a zero terminator. + // If not, just set the pointer to NULL. + // + + if ((*EndOfVariableData - CharsNeeded) >= FixedDataEnd) { + + // + // It fits. Move EndOfVariableData pointer up to the location where + // we will write the string. + // + + *EndOfVariableData -= CharsNeeded; + + // + // Copy the string to the buffer if it is not null. + // + + if (CharacterCount > 0 && SourceString != NULL) { + + (VOID) wcsncpy(*EndOfVariableData, SourceString, CharacterCount); + } + + // + // Set the zero terminator. + // + + *(*EndOfVariableData + CharacterCount) = L'\0'; + + // + // Set up the pointer in the fixed data portion to point to where the + // string is written. + // + + *VariableDataPointer = *EndOfVariableData; + + return TRUE; + + } + else { + + // + // It doesn't fit. Set the offset to NULL. + // + + *VariableDataPointer = NULL; + + return FALSE; + } +} + + +NTSTATUS +GetRemoteHandle( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine gets the NetWare handle for a Directory. This is used + for support of NetWare aware Dos applications. + +Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + NTSTATUS Status = STATUS_PENDING; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PCHAR OutputBuffer; + PICB Icb; + PDCB Dcb; + PVOID FsContext; + NODE_TYPE_CODE nodeTypeCode; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "GetRemoteHandle\n", 0); + + if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject, + &FsContext, + (PVOID *)&Icb )) != NW_NTC_ICB) { + + DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode); + + Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status ); + } + + Dcb = (PDCB)Icb->SuperType.Fcb; + nodeTypeCode = Dcb->NodeTypeCode; + + if ( nodeTypeCode != NW_NTC_DCB ) { + + DebugTrace(0, Dbg, "Not a directory\n", 0); + +#if 1 + if ( nodeTypeCode != NW_NTC_FCB ) { + + Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status ); + return Status; + } + + // + // Return the 6 byte NetWare handle for this file. + // + + if (!Icb->HasRemoteHandle) { + + Status = STATUS_INVALID_HANDLE; + + DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status ); + return Status; + } + + if ( OutputBufferLength < sizeof( UCHAR ) ) { + + Status = STATUS_BUFFER_TOO_SMALL; + + DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status ); + return Status; + } + + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + + RtlCopyMemory( OutputBuffer, Icb->Handle, 6 * sizeof(CHAR)); + IrpContext->pOriginalIrp->IoStatus.Information = 6 * sizeof(CHAR); + + Status = STATUS_SUCCESS; + + DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status ); + return Status; +#else + Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status ); + return Status; +#endif + } + + // + // Make sure that this ICB is still active. + // + + NwVerifyIcb( Icb ); + + if ( OutputBufferLength < sizeof( UCHAR ) ) { + + Status = STATUS_BUFFER_TOO_SMALL; + + } else if ( Icb->HasRemoteHandle ) { + + // Already been asked for the handle + + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + + *OutputBuffer = Icb->Handle[0]; + + IrpContext->pOriginalIrp->IoStatus.Information = sizeof(CHAR); + Status = STATUS_SUCCESS; + + } else { + + CHAR Handle; + + IrpContext->pScb = Dcb->Scb; + IrpContext->pNpScb = IrpContext->pScb->pNpScb; + + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "SbbJ", + NCP_DIR_FUNCTION, NCP_ALLOCATE_TEMP_DIR_HANDLE, + Dcb->Vcb->Specific.Disk.Handle, + 0, + &Dcb->RelativeFileName ); + + if ( NT_SUCCESS( Status ) ) { + + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nb", + &Handle ); + + if (NT_SUCCESS(Status)) { + *OutputBuffer = Handle; + Icb->Handle[0] = Handle; + Icb->HasRemoteHandle = TRUE; + IrpContext->pOriginalIrp->IoStatus.Information = sizeof(CHAR); + } + } + + NwDequeueIrpContext( IrpContext, FALSE ); + + DebugTrace( 0, Dbg, " -> %02x\n", Handle ); + + } + + DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status ); + return Status; +} + + +NTSTATUS +GetUserName( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine gets the UserName that would be used to connect to a particular + server. + + If there are credentials specific to this connection use them + otherwise use the logon credentials. + +Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + + NTSTATUS Status = STATUS_PENDING; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PWSTR InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PWSTR OutputBuffer; + SECURITY_SUBJECT_CONTEXT SubjectContext; + LARGE_INTEGER Uid; + UNICODE_STRING UidServer; + UNICODE_STRING ServerName; + UNICODE_STRING ConvertedName; + PUNICODE_STRING pUserName; + PSCB pScb; + PLOGON pLogon; + BOOLEAN CredentialsHeld = FALSE; + BOOLEAN FailedTreeLookup = FALSE; + PNDS_SECURITY_CONTEXT pNdsCredentials; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "GetUserName\n", 0); + + SeCaptureSubjectContext(&SubjectContext); + Uid = GetUid( &SubjectContext ); + SeReleaseSubjectContext(&SubjectContext); + + ServerName.Buffer = InputBuffer; + ServerName.MaximumLength = (USHORT)InputBufferLength; + ServerName.Length = (USHORT)InputBufferLength; + Status = MakeUidServer( &UidServer, &Uid, &ServerName ); + + if (!NT_SUCCESS(Status)) { + DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", Status ); + return(Status); + } + + DebugTrace( 0, Dbg, " ->UidServer = \"%wZ\"\n", &UidServer ); + + // + // Get the login for this user. + // + + NwDequeueIrpContext( IrpContext, FALSE ); + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + pLogon = FindUser( &Uid, FALSE); + NwReleaseRcb( &NwRcb ); + + // + // First try this name as a server. Avoid FindScb creating a + // connection to the server if one doesn't exist already. + // + + SetFlag( IrpContext->Flags, IRP_FLAG_NOCONNECT ); + NwFindScb( &pScb, IrpContext, &UidServer, &ServerName ); + + pUserName = NULL; + + // + // Look for bindery server name, or tree login name. + // + + if ( pScb != NULL ) { + + if ( pScb->UserName.Buffer != NULL ) { + + pUserName = &pScb->UserName; + + } else if ( pScb->NdsTreeName.Buffer != NULL && + pScb->NdsTreeName.Length > 0 ) { + + Status = NdsLookupCredentials( &pScb->NdsTreeName, + pLogon, + &pNdsCredentials, + CREDENTIAL_READ, + FALSE ); + + if ( NT_SUCCESS( Status ) ) { + + CredentialsHeld = TRUE; + + if ( pNdsCredentials->Credential ) { + + // + // If we have login data, get the user name. + // + + ConvertedName.Length = pNdsCredentials->Credential->userNameLength - + sizeof( WCHAR ); + ConvertedName.MaximumLength = ConvertedName.Length; + ConvertedName.Buffer = (USHORT *) + ( ((BYTE *) pNdsCredentials->Credential ) + + sizeof( NDS_CREDENTIAL ) + + pNdsCredentials->Credential->optDataSize ); + + pUserName = &ConvertedName; + + } else { + + // + // If there's no credential data, we're not logged in. + // + + FailedTreeLookup = TRUE; + } + + } else { + + FailedTreeLookup = TRUE; + } + + } + + } + + // + // If it wasn't a server and we haven't already tried a tree, do so now. + // + + if ( pUserName == NULL && + !FailedTreeLookup ) { + + Status = NdsLookupCredentials( &ServerName, + pLogon, + &pNdsCredentials, + CREDENTIAL_READ, + FALSE ); + + if ( NT_SUCCESS( Status ) ) { + + CredentialsHeld = TRUE; + + if ( pNdsCredentials->Credential ) { + + // + // If we've logged in, get the user name. + // + + ConvertedName.Length = pNdsCredentials->Credential->userNameLength - + sizeof( WCHAR ); + ConvertedName.MaximumLength = ConvertedName.Length; + ConvertedName.Buffer = (USHORT *) + ( ((BYTE *) pNdsCredentials->Credential ) + + sizeof( NDS_CREDENTIAL ) + + pNdsCredentials->Credential->optDataSize ); + + pUserName = &ConvertedName; + + } + } + + } + + // + // If we still don't know, return the default name. + // + + if ( pUserName == NULL && + pLogon != NULL ) { + + pUserName = &pLogon->UserName; + } + + FREE_POOL(UidServer.Buffer); + + if ( pUserName ) { + + DebugTrace( 0, Dbg, "Get User Name: %wZ\n", pUserName ); + + try { + + if (pUserName->Length > OutputBufferLength) { + + DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", STATUS_BUFFER_TOO_SMALL ); + Status = STATUS_BUFFER_TOO_SMALL; + goto ReleaseAndExit; + } + + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + + IrpContext->pOriginalIrp->IoStatus.Information = pUserName->Length; + RtlMoveMemory( OutputBuffer, pUserName->Buffer, pUserName->Length); + + Status = STATUS_SUCCESS; + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + + Status = STATUS_INVALID_PARAMETER; + } + } + +ReleaseAndExit: + + if ( pScb ) { + NwDereferenceScb( pScb->pNpScb ); + } + + DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", Status ); + + if ( CredentialsHeld ) { + NwReleaseCredList( pLogon ); + } + + return Status; +} + + +NTSTATUS +GetChallenge( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine builds the challenge and session key for rpc using the + credentials stored in the redirector. The Rpc client can supply a + password. This allows the redirector to keep the algorithm in one + place. + + If a password is supplied then use that, if there is a password on this + specific connection use that, otherwise use the logon credentials. + +Arguments: + + IN PIRP_CONTEXT IrpContext - Io Request Packet for request + +Return Value: + +NTSTATUS + +--*/ + +{ + + NTSTATUS Status = STATUS_PENDING; + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PNWR_GET_CHALLENGE_REQUEST InputBuffer = Irp->AssociatedIrp.SystemBuffer; + ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PNWR_GET_CHALLENGE_REPLY OutputBuffer = Irp->AssociatedIrp.SystemBuffer; + OEM_STRING Password; + PSCB pScb; + PLOGON pLogon; + BOOLEAN RcbHeld = FALSE; + SECURITY_SUBJECT_CONTEXT SubjectContext; + LARGE_INTEGER ProcessUid; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "GetChallenge\n", 0); + + if ((InputBufferLength < + (FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0])) + + InputBuffer->ServerNameorPasswordLength)) { + + return(STATUS_INVALID_PARAMETER); + } + + // + // Only allow processes running in the system context to call this api to prevent + // password attacks. + // + SeCaptureSubjectContext(&SubjectContext); + SeQueryAuthenticationIdToken(&SubjectContext.PrimaryToken, (PLUID)&ProcessUid); + SeReleaseSubjectContext(&SubjectContext); + + // FIXFIX surely there's a define for 3e7 somewhere. + + if (ProcessUid.QuadPart != 0x3e7) { + return(STATUS_ACCESS_DENIED); + } + + Password.Buffer = NULL; + + if ( InputBuffer->Flags == CHALLENGE_FLAGS_SERVERNAME ) { + + PUNICODE_STRING pPassword; + UNICODE_STRING ServerName; + LARGE_INTEGER Uid; + UNICODE_STRING UidServer; + + if (InputBuffer->ServerNameorPasswordLength == 0) { + return(STATUS_INVALID_PARAMETER); + } + + // + // We have to supply the password from the redirector + // + + SeCaptureSubjectContext(&SubjectContext); + Uid = GetUid( &SubjectContext ); + SeReleaseSubjectContext(&SubjectContext); + + ServerName.Buffer = (PWSTR)((PUCHAR)InputBuffer + + FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0])); + ServerName.MaximumLength = (USHORT)InputBuffer->ServerNameorPasswordLength; + ServerName.Length = (USHORT)InputBuffer->ServerNameorPasswordLength; + + Status = MakeUidServer( &UidServer, &Uid, &ServerName ); + + if (!NT_SUCCESS(Status)) { + DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status ); + return(Status); + } + + DebugTrace( 0, Dbg, " ->UidServer = \"%wZ\"\n", &UidServer ); + + // + // Avoid FindScb creating a connection to the server if one + // doesn't exist already. + // + + SetFlag( IrpContext->Flags, IRP_FLAG_NOCONNECT ); + NwFindScb( &pScb, IrpContext, &UidServer, &ServerName ); + + try { + + if ((pScb != NULL) && + (pScb->Password.Buffer != NULL)) { + + pPassword = &pScb->Password; + + } else { + + // + // Use default credentials for this UID + // + + NwDequeueIrpContext( IrpContext, FALSE ); + RcbHeld = TRUE; + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + pLogon = FindUser( &Uid, FALSE); + + if (pLogon != NULL ) { + + pPassword = &pLogon->PassWord; + + } else { + DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", STATUS_ACCESS_DENIED ); + return( STATUS_ACCESS_DENIED ); + } + } + + if (pPassword->Length != 0) { + Status = RtlUpcaseUnicodeStringToOemString( &Password, pPassword, TRUE ); + if (!NT_SUCCESS(Status)) { + DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status ); + return( Status ); + } + } else { + Password.Buffer = ""; + Password.Length = Password.MaximumLength = 0; + } + + } finally { + + if (RcbHeld) { + NwReleaseRcb( &NwRcb ); + } + + if (pScb != NULL) { + NwDereferenceScb( pScb->pNpScb ); + } + + FREE_POOL(UidServer.Buffer); + } + + } else { + + UNICODE_STRING LocalPassword; + + LocalPassword.Buffer = (PWSTR)((PUCHAR)InputBuffer + + FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0])); + LocalPassword.MaximumLength = (USHORT)InputBuffer->ServerNameorPasswordLength; + LocalPassword.Length = (USHORT)InputBuffer->ServerNameorPasswordLength; + + if (LocalPassword.Length != 0) { + Status = RtlUpcaseUnicodeStringToOemString( &Password, &LocalPassword, TRUE ); + if (!NT_SUCCESS(Status)) { + DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status ); + return( Status ); + } + } else { + Password.Buffer = ""; + Password.Length = Password.MaximumLength = 0; + } + } + + DebugTrace( 0, Dbg, " ->Password = \"%Z\"\n", &Password ); + + try { + RespondToChallenge( (PUCHAR)&InputBuffer->ObjectId, &Password, InputBuffer->Challenge, OutputBuffer->Challenge); + + } finally { + + if ( Password.Length > 0 ) { + + RtlFreeAnsiString( &Password ); + } + } + + Irp->IoStatus.Information = sizeof(NWR_GET_CHALLENGE_REPLY); + Status = STATUS_SUCCESS; + + DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status ); + return Status; +} + +NTSTATUS +WriteConnStatusEntry( + PSCB pConnectionScb, + PBYTE pbUserBuffer, + DWORD dwBufferLen, + DWORD *pdwBytesWritten, + DWORD *pdwBytesNeeded, + BOOLEAN fCallerScb + ) +{ + + NTSTATUS Status; + PLOGON pLogon; + PNDS_SECURITY_CONTEXT pNdsContext; + BOOLEAN fHoldingCredentials = FALSE; + PUNICODE_STRING puUserName = NULL; + UNICODE_STRING CredentialName; + UNICODE_STRING ServerName; + PCONN_STATUS pStatus; + DWORD dwBytesNeeded; + PBYTE pbStrPtr; + DWORD dwAllowedHandles; + + // + // If this is an NDS connection, get the credentials. + // + + if ( ( pConnectionScb->MajorVersion > 3 ) && + ( pConnectionScb->UserName.Length == 0 ) ) { + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + pLogon = FindUser( &(pConnectionScb->UserUid), FALSE ); + NwReleaseRcb( &NwRcb ); + + if ( pLogon ) { + + Status = NdsLookupCredentials( &(pConnectionScb->NdsTreeName), + pLogon, + &pNdsContext, + CREDENTIAL_READ, + FALSE ); + + if ( NT_SUCCESS( Status ) ) { + + fHoldingCredentials = TRUE; + + if ( pNdsContext->Credential != NULL ) { + + CredentialName.Length = pNdsContext->Credential->userNameLength - + sizeof( WCHAR ); + CredentialName.MaximumLength = CredentialName.Length; + CredentialName.Buffer = (USHORT *) + ( ((BYTE *) pNdsContext->Credential ) + + sizeof( NDS_CREDENTIAL ) + + pNdsContext->Credential->optDataSize ); + + puUserName = &CredentialName; + } + + } + } + + } else { + + if ( pConnectionScb->UserName.Length != 0 ) { + puUserName = &(pConnectionScb->UserName); + } else { + puUserName = NULL; + } + + } + + DebugTrace( 0, Dbg, "WriteConnStatus: UserName %wZ\n", puUserName ); + + // + // Strip off the uid from the server name. + // + + ServerName.Length = (pConnectionScb->UidServerName).Length; + ServerName.Buffer = (pConnectionScb->UidServerName).Buffer; + + while ( ServerName.Length ) { + + if ( ServerName.Buffer[0] == L'\\' ) { + + ServerName.Length -= sizeof( WCHAR ); + ServerName.Buffer += 1; + break; + } + + ServerName.Length -= sizeof( WCHAR ); + ServerName.Buffer += 1; + + } + + DebugTrace( 0, Dbg, "WriteConnStatus: ServerName %wZ\n", &ServerName ); + + // + // Do we have enough space? Don't forget that we have to + // NULL terminate the WCHAR strings. + // + + dwBytesNeeded = sizeof( CONN_STATUS ); + + dwBytesNeeded += ( ServerName.Length + sizeof( WCHAR ) ); + + if ( pConnectionScb->NdsTreeName.Length ) { + dwBytesNeeded += ( pConnectionScb->NdsTreeName.Length + sizeof( WCHAR ) ); + } + + if ( puUserName ) { + dwBytesNeeded += ( puUserName->Length + sizeof( WCHAR ) ); + } + + // + // Pad the end to make sure all structures are aligned. + // + + dwBytesNeeded = ROUNDUP4( dwBytesNeeded ); + + if ( dwBytesNeeded > dwBufferLen ) { + + *pdwBytesNeeded = dwBytesNeeded; + Status = STATUS_BUFFER_TOO_SMALL; + goto ExitWithCleanup; + } + + // + // Fill in the CONN_STATUS structure. + // + + try { + + pStatus = (PCONN_STATUS)pbUserBuffer; + pbStrPtr = pbUserBuffer + sizeof( CONN_STATUS ); + + // + // We always have a server name. + // + + pStatus->pszServerName = (PWSTR) pbStrPtr; + pbStrPtr += ( ServerName.Length + sizeof( WCHAR ) ); + + // + // Fill in the user name if applicable. + // + + if ( puUserName ) { + + pStatus->pszUserName = (PWSTR) pbStrPtr; + pbStrPtr += ( puUserName->Length + sizeof( WCHAR ) ); + + } else { + + pStatus->pszUserName = NULL; + } + + // + // Fill in the tree name if applicable. + // + + if ( pConnectionScb->NdsTreeName.Length ) { + + pStatus->pszTreeName = (PWSTR) pbStrPtr; + + } else { + + pStatus->pszTreeName = NULL; + } + + // + // Fill in the connection number if applicable. + // + + if ( ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) || + ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ) { + + pStatus->nConnNum = (DWORD)(pConnectionScb->pNpScb->ConnectionNo); + + } else { + + pStatus->nConnNum = 0; + + } + + // + // Copy the user name over. + // + + if ( puUserName ) { + + RtlCopyMemory( (PBYTE)(pStatus->pszUserName), + (PBYTE)(puUserName->Buffer), + puUserName->Length ); + *(pStatus->pszUserName + (puUserName->Length / sizeof( WCHAR ))) = L'\0'; + + } + + // + // Set the NDS flag and authentication fields. + // + + if ( ( pConnectionScb->MajorVersion > 3 ) && + ( pConnectionScb->UserName.Length == 0 ) ) { + + pStatus->fNds = TRUE; + + if ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) { + + if ( ( pConnectionScb->VcbCount ) || ( pConnectionScb->OpenNdsStreams ) ) { + pStatus->dwConnType = NW_CONN_NDS_AUTHENTICATED_LICENSED; + } else { + pStatus->dwConnType = NW_CONN_NDS_AUTHENTICATED_NO_LICENSE; + } + + } else if ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) { + + pStatus->dwConnType = NW_CONN_NOT_AUTHENTICATED; + + } else { + + pStatus->dwConnType = NW_CONN_DISCONNECTED; + + } + + } else { + + pStatus->fNds = FALSE; + + if ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) { + + pStatus->dwConnType = NW_CONN_BINDERY_LOGIN; + + } else if ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) { + + pStatus->dwConnType = NW_CONN_NOT_AUTHENTICATED; + + } else { + + pStatus->dwConnType = NW_CONN_DISCONNECTED; + + } + + } + + // + // Copy over the tree name. + // + + if ( pConnectionScb->NdsTreeName.Length ) { + + RtlCopyMemory( (PBYTE)(pStatus->pszTreeName), + (PBYTE)(pConnectionScb->NdsTreeName.Buffer), + pConnectionScb->NdsTreeName.Length ); + *( pStatus->pszTreeName + + ( pConnectionScb->NdsTreeName.Length / sizeof( WCHAR ) ) ) = L'\0'; + + } else { + + pStatus->pszTreeName = NULL; + } + + // + // Copy the server name over. + // + + RtlCopyMemory( (PBYTE)(pStatus->pszServerName), + (PBYTE)(ServerName.Buffer), + ServerName.Length ); + *(pStatus->pszServerName + (ServerName.Length / sizeof( WCHAR ))) = L'\0'; + + // + // Set the preferred server field if this is a preferred server + // and there are no explicit uses for the connection. If the + // fCallerScb parameter is TRUE, then this SCB has a handle from + // the caller of the API and we have to make an allowance for + // that handle. Yes, this is kind of ugly. + // + + if ( fCallerScb ) { + dwAllowedHandles = 1; + } else { + dwAllowedHandles = 0; + } + + if ( ( pConnectionScb->PreferredServer ) && + ( pConnectionScb->OpenFileCount == 0 ) && + ( pConnectionScb->IcbCount == dwAllowedHandles ) ) { + + pStatus->fPreferred = TRUE; + + } else { + + pStatus->fPreferred = FALSE; + } + + // + // Fill out the length. + // + + pStatus->dwTotalLength = dwBytesNeeded; + *pdwBytesWritten = dwBytesNeeded; + Status = STATUS_SUCCESS; + + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + + Status = GetExceptionCode(); + DebugTrace( 0, Dbg, "Exception %08lx accessing user mode buffer.\n", Status ); + goto ExitWithCleanup; + + } + +ExitWithCleanup: + + if ( fHoldingCredentials ) { + NwReleaseCredList( pLogon ); + } + + return Status; +} + +NTSTATUS +GetConnStatus( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject + ) +/*++ + + Get the connection status for the described connection. + The following connection requests are valid: + + Server (e.g. "MARS312") - returns a single connection + status structure for this server if the user has a + connection to the server. + + Tree (e.g. "*MARSDEV") - returns a connection status + structure for every server in the tree that the user + has a connection to. + + All Connections (e.g. "") - returns a connection status + structure for every server that the user has a + connection to. + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + PNWR_REQUEST_PACKET InputBuffer; + ULONG InputBufferLength; + BYTE *OutputBuffer; + ULONG OutputBufferLength; + + SECURITY_SUBJECT_CONTEXT SubjectContext; + LARGE_INTEGER Uid; + + PLIST_ENTRY ListEntry; + UNICODE_STRING ConnectionName, UidServer; + BOOL fTreeConnections = FALSE; + BOOL fServerConnection = FALSE; + BOOL OwnRcb = FALSE; + PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry; + DWORD dwBytesWritten, dwBytesNeeded; + KIRQL OldIrql; + PSCB pScb; + PNONPAGED_SCB pNpScb; + DWORD dwReturned = 0; + ULONG SequenceNumber = 0; + + NODE_TYPE_CODE nodeTypeCode; + PICB pIcb; + PSCB pCallerScb; + PVOID fsContext, fsContext2; + + // + // Get the appropriate buffers. + // + + InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + + // + // Figure out who this request applies to. + // + + SeCaptureSubjectContext(&SubjectContext); + Uid = GetUid( &SubjectContext ); + SeReleaseSubjectContext(&SubjectContext); + + RtlInitUnicodeString( &ConnectionName, NULL ); + RtlInitUnicodeString( &UidServer, NULL ); + + // + // Figure out who the caller of this routine is so we know to + // ignore their handle when deciding what to return. + // + + nodeTypeCode = NwDecodeFileObject( FileObject, &fsContext, &fsContext2 ); + + if ( nodeTypeCode == NW_NTC_ICB_SCB ) { + + pIcb = (PICB) fsContext2; + pCallerScb = pIcb->SuperType.Scb; + DebugTrace( 0, Dbg, "GetConnStatus called by handle on %08lx\n", pCallerScb ); + + } else { + + pCallerScb = NULL; + DebugTrace( 0, Dbg, "Couldn't figure out who called us.\n", 0 ); + } + + // + // + // Figure out which connections we're looking for. + // + + try { + + if ( InputBuffer->Parameters.GetConnStatus.ConnectionNameLength != 0 ) { + + if ( InputBuffer->Parameters.GetConnStatus.ConnectionName[0] == L'*' ) { + + ConnectionName.Buffer = &(InputBuffer->Parameters.GetConnStatus.ConnectionName[1]); + ConnectionName.Length = (USHORT) + ( InputBuffer->Parameters.GetConnStatus.ConnectionNameLength - + sizeof( WCHAR ) ); + ConnectionName.MaximumLength = ConnectionName.Length; + + fTreeConnections = TRUE; + + DebugTrace( 0, Dbg, "GetConnStatus: Tree is %wZ\n", &ConnectionName ); + + } else { + + ConnectionName.Buffer = InputBuffer->Parameters.GetConnStatus.ConnectionName; + ConnectionName.Length = (USHORT) + (InputBuffer->Parameters.GetConnStatus.ConnectionNameLength); + ConnectionName.MaximumLength = ConnectionName.Length; + + fServerConnection = TRUE; + + Status = MakeUidServer( &UidServer, &Uid, &ConnectionName ); + if ( !NT_SUCCESS( Status )) { + return Status; + } + + DebugTrace( 0, Dbg, "GetConnStatus: Server is %wZ\n", &UidServer ); + } + + } else { + + DebugTrace( 0, Dbg, "GetConnectionStatus: Enumerate all connections.\n", 0 ); + + } + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + + DebugTrace( 0, Dbg, "Bad input buffer in GetConnStatus.\n" , 0 ); + + } + + // + // If this is a server connection, find and return it. + // + + if ( fServerConnection ) { + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + OwnRcb = TRUE; + PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidServer, 0 ); + + if ( !PrefixEntry ) { + Status = STATUS_INVALID_PARAMETER; + goto ExitWithCleanup; + } + + pScb = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry ); + + if ( ( pScb->PreferredServer ) || + ( pScb->OpenFileCount > 0 ) ) { + + // + // If there are open files, we need to return this. + // We always write status entries for the preferred + // server so that we can give default logon info. + // + + goto ProcessServer; + } + + // + // Are there open handles other than the caller? + // + + if ( pScb == pCallerScb ) { + + if ( pScb->IcbCount > 1 ) { + + ASSERT( pScb->pNpScb->Reference > 1 ); + goto ProcessServer; + } + + } else { + + if ( pScb->IcbCount > 0 ) { + + ASSERT( pScb->pNpScb->Reference > 0 ); + goto ProcessServer; + } + } + + // + // Not an explicit use for this server. + // + goto ExitWithCleanup; + +ProcessServer: + + NwReferenceScb( pScb->pNpScb ); + + NwReleaseRcb( &NwRcb ); + OwnRcb = FALSE; + + Status = WriteConnStatusEntry( pScb, + OutputBuffer, + OutputBufferLength, + &dwBytesWritten, + &dwBytesNeeded, + (BOOLEAN)( pScb == pCallerScb ) ); + + NwDereferenceScb( pScb->pNpScb ); + + InputBuffer->Parameters.GetConnStatus.ResumeKey = 0; + + if ( !NT_SUCCESS( Status )) { + + InputBuffer->Parameters.GetConnStatus.EntriesReturned = 0; + InputBuffer->Parameters.GetConnStatus.BytesNeeded = dwBytesNeeded; + Irp->IoStatus.Information = 0; + goto ExitWithCleanup; + + } else { + + InputBuffer->Parameters.GetConnStatus.EntriesReturned = 1; + InputBuffer->Parameters.GetConnStatus.BytesNeeded = 0; + Irp->IoStatus.Information = dwBytesWritten; + goto ExitWithCleanup; + + } + } + + // + // We want all connections or all tree connections, so + // we need to walk the list. + // + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + ListEntry = ScbQueue.Flink; + + while ( ListEntry != &ScbQueue ) { + + pNpScb = CONTAINING_RECORD( ListEntry, NONPAGED_SCB, ScbLinks ); + pScb = pNpScb->pScb; + + NwReferenceScb( pNpScb ); + + KeReleaseSpinLock(&ScbSpinLock, OldIrql); + + // + // Make sure we pass up the one's we've already returned. + // + + if ( ( SequenceNumber >= InputBuffer->Parameters.GetConnStatus.ResumeKey ) && + ( pNpScb != &NwPermanentNpScb ) ) { + + // + // If there are open files, we need to return this. + // We always write status entries for the preferred + // server so that we can give default logon info. + // + + if ( ( pScb->PreferredServer ) || + ( pScb->OpenFileCount > 0 ) ) { + goto SecondProcessServer; + } + + // + // Are there any handles other than the caller? + // + + if ( pScb == pCallerScb ) { + + if ( pScb->IcbCount > 1 ) { + + ASSERT( pScb->pNpScb->Reference > 2 ); + goto SecondProcessServer; + } + + } else { + + if ( pScb->IcbCount > 0 ) { + + ASSERT( pScb->pNpScb->Reference > 1 ); + goto SecondProcessServer; + } + } + + } + + // + // Not an interesting server; move to next entry. + // + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + ListEntry = pNpScb->ScbLinks.Flink; + NwDereferenceScb( pNpScb ); + SequenceNumber++; + continue; + +SecondProcessServer: + + // + // We have a possible candidate; see if the uid and tree are appropriate. + // + + if ( ( (pScb->UserUid).QuadPart != Uid.QuadPart ) || + + ( fTreeConnections && + !RtlEqualUnicodeString( &(pScb->NdsTreeName), + &ConnectionName, + TRUE ) ) ) { + + // + // No dice. Move onto the next one. + // + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + ListEntry = pNpScb->ScbLinks.Flink; + NwDereferenceScb( pNpScb ); + SequenceNumber++; + continue; + + } + + // + // Ok, we definitely want to report this one. + // + + Status = WriteConnStatusEntry( pScb, + OutputBuffer, + OutputBufferLength, + &dwBytesWritten, + &dwBytesNeeded, + (BOOLEAN)( pScb == pCallerScb ) ); + + if ( !NT_SUCCESS( Status )) { + + // + // If we couldn't write this entry, then we have to update + // the ResumeKey and return. We don't really know how many + // more there are going to be so we 'suggest' to the caller + // a 2k buffer size. + // + + InputBuffer->Parameters.GetConnStatus.ResumeKey = SequenceNumber; + InputBuffer->Parameters.GetConnStatus.EntriesReturned = dwReturned; + InputBuffer->Parameters.GetConnStatus.BytesNeeded = 2048; + NwDereferenceScb( pNpScb ); + goto ExitWithCleanup; + + } else { + + OutputBuffer = ( OutputBuffer + dwBytesWritten ); + OutputBufferLength -= dwBytesWritten; + dwReturned++; + } + + // + // Move to next entry in the list. + // + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + ListEntry = pNpScb->ScbLinks.Flink; + NwDereferenceScb( pNpScb ); + SequenceNumber++; + } + + // + // We made it through the list. + // + + KeReleaseSpinLock(&ScbSpinLock, OldIrql); + + InputBuffer->Parameters.GetConnStatus.ResumeKey = 0; + InputBuffer->Parameters.GetConnStatus.EntriesReturned = dwReturned; + InputBuffer->Parameters.GetConnStatus.BytesNeeded = 0; + + Status = STATUS_SUCCESS; + +ExitWithCleanup: + + // + // If we returned any entries, then set the status to success. + // + + if ( dwReturned ) { + + ASSERT( SequenceNumber != 0 ); + Status = STATUS_SUCCESS; + } + + if ( OwnRcb ) { + NwReleaseRcb( &NwRcb ); + } + + if ( UidServer.Buffer != NULL ) { + FREE_POOL( UidServer.Buffer ); + } + + return Status; +} + +NTSTATUS +GetConnectionInfo( + IN PIRP_CONTEXT IrpContext + ) +/*+++ + +GetConnectionInfo: + + Takes a connection name from the new shell and returns + some info commonly requested by property sheets and the + such. + + The following connection names are supported: + + Drive Letter: "X:" + Printer Port: "LPTX:" + UNC Name: "\\SERVER\Share\{Path\} + + +---*/ +{ + + NTSTATUS Status; + + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PNWR_REQUEST_PACKET InputBuffer; + PCONN_INFORMATION pConnInfo; + ULONG InputBufferLength, OutputBufferLength; + ULONG BytesNeeded; + + SECURITY_SUBJECT_CONTEXT SubjectContext; + LARGE_INTEGER Uid; + UNICODE_STRING ConnectionName; + UNICODE_STRING UidVolumeName; + WCHAR DriveLetter = 0; + + BOOLEAN OwnRcb = FALSE; + BOOLEAN ReferenceVcb = FALSE; + PVCB Vcb = NULL; + PSCB Scb = NULL; + PUNICODE_PREFIX_TABLE_ENTRY Prefix; + + PLOGON pLogon; + UNICODE_STRING CredentialName; + UNICODE_STRING ServerName; + PUNICODE_STRING puUserName = NULL; + PNDS_SECURITY_CONTEXT pNdsContext; + BOOLEAN fHoldingCredentials = FALSE; + + // + // Get the input and output buffers. + // + + InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + if ( OutputBufferLength ) { + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&pConnInfo ); + } else { + return STATUS_BUFFER_TOO_SMALL; + } + + SeCaptureSubjectContext(&SubjectContext); + Uid = GetUid( &SubjectContext ); + SeReleaseSubjectContext(&SubjectContext); + + RtlInitUnicodeString( &UidVolumeName, NULL ); + + ConnectionName.Length = (USHORT)(InputBuffer->Parameters).GetConnInfo.ConnectionNameLength; + ConnectionName.MaximumLength = ConnectionName.Length; + ConnectionName.Buffer = &((InputBuffer->Parameters).GetConnInfo.ConnectionName[0]); + + // + // Ok, this gets a little hand-wavey, but we have to try and figure + // what this connection name represents. + // + + if ( ConnectionName.Length == sizeof( L"X:" ) - sizeof( WCHAR ) ) { + DriveLetter = ConnectionName.Buffer[0]; + } else if ( ConnectionName.Length == sizeof( L"LPT1:" ) - sizeof( WCHAR ) ) { + DriveLetter = ConnectionName.Buffer[3]; + } + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + OwnRcb = TRUE; + + if ( DriveLetter != 0 ) { + + DebugTrace( 0, Dbg, "GetConnectionInfo: Drive %wZ\n", &ConnectionName ); + + // + // This is a drive relative path. Look up the drive letter. + // + + ASSERT( ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) || + ( DriveLetter >= L'1' && DriveLetter <= L'9' ) ); + + if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) { + Vcb = DriveMapTable[DriveLetter - L'A']; + } else { + Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1']; + } + + // + // Was the Vcb created for this user? + // + + if ( ( Vcb != NULL ) && ( Uid.QuadPart != Vcb->Scb->UserUid.QuadPart ) ) { + Status = STATUS_ACCESS_DENIED; + goto ExitWithCleanup; + } + + } else { + + // + // This is a UNC path. Skip over the backslashes and + // prepend the unicode uid. + // + + ConnectionName.Length -= (2 * sizeof( WCHAR ) ); + ConnectionName.Buffer += 2; + + Status = MakeUidServer( &UidVolumeName, &Uid, &ConnectionName ); + + if ( !NT_SUCCESS( Status )) { + goto ExitWithCleanup; + } + + DebugTrace( 0, Dbg, "GetConnectionInfo: %wZ\n", &UidVolumeName ); + + Prefix = RtlFindUnicodePrefix( &NwRcb.VolumeNameTable, &UidVolumeName, 0 ); + + if ( Prefix != NULL ) { + Vcb = CONTAINING_RECORD( Prefix, VCB, PrefixEntry ); + + if ( Vcb->Name.Length != UidVolumeName.Length ) { + Vcb = NULL; + } + } + } + + if ( !Vcb ) { + Status = STATUS_BAD_NETWORK_PATH; + goto ExitWithCleanup; + } + + DebugTrace( 0, Dbg, "GetConnectionInfo: Vcb is 0x%08lx\n", Vcb ); + + NwReferenceVcb( Vcb ); + ReferenceVcb = TRUE; + NwReleaseRcb( &NwRcb ); + OwnRcb = FALSE; + + // + // Get the username. This is the same code block as in + // WriteConnStatusEntry; it should be abstracted out. + // + + Scb = Vcb->Scb; + ASSERT( Scb != NULL ); + + if ( ( Scb->MajorVersion > 3 ) && + ( Scb->UserName.Length == 0 ) ) { + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + pLogon = FindUser( &Uid, FALSE ); + NwReleaseRcb( &NwRcb ); + + if ( pLogon ) { + + Status = NdsLookupCredentials( &(Scb->NdsTreeName), + pLogon, + &pNdsContext, + CREDENTIAL_READ, + FALSE ); + + if ( NT_SUCCESS( Status ) ) { + + fHoldingCredentials = TRUE; + + if ( pNdsContext->Credential != NULL ) { + + CredentialName.Length = pNdsContext->Credential->userNameLength - + sizeof( WCHAR ); + CredentialName.MaximumLength = CredentialName.Length; + CredentialName.Buffer = (USHORT *) + ( ((BYTE *) pNdsContext->Credential ) + + sizeof( NDS_CREDENTIAL ) + + pNdsContext->Credential->optDataSize ); + + puUserName = &CredentialName; + } + + } + } + + } else { + + puUserName = &(Scb->UserName); + + } + + DebugTrace( 0, Dbg, "GetConnectionInfo: UserName %wZ\n", puUserName ); + + // + // Strip off the uid from the server name. + // + + ServerName.Length = (Scb->UidServerName).Length; + ServerName.Buffer = (Scb->UidServerName).Buffer; + + while ( ServerName.Length ) { + + if ( ServerName.Buffer[0] == L'\\' ) { + + ServerName.Length -= sizeof( WCHAR ); + ServerName.Buffer += 1; + break; + } + + ServerName.Length -= sizeof( WCHAR ); + ServerName.Buffer += 1; + + } + + DebugTrace( 0, Dbg, "GetConnectionInfo: ServerName %wZ\n", &ServerName ); + + // + // Write a single CONN_INFORMATION structure into the output buffer. + // + + if ( puUserName ) { + + BytesNeeded = sizeof( CONN_INFORMATION ) + + ServerName.Length + + puUserName->Length; + } else { + + BytesNeeded = sizeof( CONN_INFORMATION ) + + ServerName.Length; + + } + + if ( BytesNeeded > OutputBufferLength ) { + Status = STATUS_BUFFER_TOO_SMALL; + goto ExitWithCleanup; + } + + pConnInfo->HostServerLength = ServerName.Length; + pConnInfo->HostServer = (LPWSTR) ( (PBYTE) pConnInfo ) + sizeof( CONN_INFORMATION ); + RtlCopyMemory( pConnInfo->HostServer, ServerName.Buffer, ServerName.Length ); + + pConnInfo->UserName = (LPWSTR) ( ( (PBYTE) pConnInfo->HostServer ) + + ServerName.Length ); + + if ( puUserName ) { + + pConnInfo->UserNameLength = puUserName->Length; + RtlCopyMemory( pConnInfo->UserName, puUserName->Buffer, puUserName->Length ); + + } else { + + pConnInfo->UserNameLength = 0; + } + + Status = STATUS_SUCCESS; + +ExitWithCleanup: + + if ( fHoldingCredentials ) { + NwReleaseCredList( pLogon ); + } + + if ( OwnRcb ) { + NwReleaseRcb( &NwRcb ); + } + + if ( ReferenceVcb ) { + NwDereferenceVcb( Vcb, NULL, FALSE ); + } + + if ( UidVolumeName.Buffer ) { + FREE_POOL( UidVolumeName.Buffer ); + } + + return Status; +} + +NTSTATUS +GetPreferredServer( + IN PIRP_CONTEXT IrpContext + ) +/*+++ + +GetPreferredServer: + + Returns the current preferred server. + +---*/ +{ + + NTSTATUS Status; + + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + BYTE *OutputBuffer; + ULONG OutputBufferLength; + + SECURITY_SUBJECT_CONTEXT SubjectContext; + LARGE_INTEGER Uid; + PLOGON pLogon; + + PUNICODE_STRING PreferredServer; + + // + // Get the output buffer. + // + + OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + if ( OutputBufferLength ) { + NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); + } else { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Get the logon structure for the user and return the preferred server. + // + + SeCaptureSubjectContext(&SubjectContext); + Uid = GetUid( &SubjectContext ); + SeReleaseSubjectContext(&SubjectContext); + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + pLogon = FindUser( &Uid, FALSE ); + + Status = STATUS_NO_SUCH_LOGON_SESSION; + + if ( ( pLogon ) && + ( pLogon->ServerName.Length ) && + ( ( pLogon->ServerName.Length + sizeof( UNICODE_STRING ) ) <= OutputBufferLength ) ) { + + PreferredServer = (PUNICODE_STRING) OutputBuffer; + PreferredServer->Length = pLogon->ServerName.Length; + PreferredServer->MaximumLength = pLogon->ServerName.Length; + PreferredServer->Buffer = ( PWCHAR ) ( OutputBuffer + sizeof( UNICODE_STRING ) ); + + RtlCopyMemory( PreferredServer->Buffer, + pLogon->ServerName.Buffer, + pLogon->ServerName.Length ); + + Status = STATUS_SUCCESS; + } + + NwReleaseRcb( &NwRcb ); + + return Status; +} + +NTSTATUS +GetConnectionPerformance( + IN PIRP_CONTEXT IrpContext + ) +/*+++ + +GetConnectionPerformance: + + Takes a connection name from the new shell and returns + some estimated performance info to the shell so the shell + can decide whether or not it wants to download icons, etc. + + The following connection names are supported: + + Drive Letter: "X:" + Printer Port: "LPTX:" + UNC Name: "\\SERVER\Share\{Path\} + +---*/ +{ + + NTSTATUS Status; + + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PNWR_REQUEST_PACKET InputBuffer; + ULONG InputBufferLength; + + SECURITY_SUBJECT_CONTEXT SubjectContext; + LARGE_INTEGER Uid; + UNICODE_STRING RemoteName; + + WCHAR DriveLetter = 0; + BOOLEAN OwnRcb = FALSE; + BOOLEAN ReferenceScb = FALSE; + PVCB Vcb = NULL; + PSCB Scb = NULL; + + PLIST_ENTRY ListEntry; + UNICODE_STRING OriginalUnc; + + // + // Get the input buffer. + // + + InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + + // + // Get the UID for the caller. + // + + SeCaptureSubjectContext(&SubjectContext); + Uid = GetUid( &SubjectContext ); + SeReleaseSubjectContext(&SubjectContext); + + // + // Dig out the remote name. + // + + RemoteName.Length = (USHORT)(InputBuffer->Parameters).GetConnPerformance.RemoteNameLength; + RemoteName.MaximumLength = RemoteName.Length; + RemoteName.Buffer = &((InputBuffer->Parameters).GetConnPerformance.RemoteName[0]); + + // + // Ok, this gets a little hand-wavey, but we have to try and figure + // what this connection name represents (just like in GetConnectionInfo). + // + + if ( RemoteName.Length == sizeof( L"X:" ) - sizeof( WCHAR ) ) { + DriveLetter = RemoteName.Buffer[0]; + } else if ( RemoteName.Length == sizeof( L"LPT1:" ) - sizeof( WCHAR ) ) { + DriveLetter = RemoteName.Buffer[3]; + } + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + OwnRcb = TRUE; + + DebugTrace( 0, Dbg, "GetConnectionPerformance: Remote Name %wZ\n", &RemoteName ); + + if ( DriveLetter != 0 ) { + + if ( ! ( ( ( DriveLetter >= L'a' ) && ( DriveLetter <= L'z' ) ) || + ( ( DriveLetter >= L'A' ) && ( DriveLetter <= L'Z' ) ) || + ( ( DriveLetter >= L'0' ) && ( DriveLetter <= L'9' ) ) ) ) { + + Status = STATUS_BAD_NETWORK_PATH; + goto ExitWithCleanup; + } + + // + // This is a drive relative path. Look up the drive letter. + // + + if ( DriveLetter >= L'a' && DriveLetter <= L'z' ) { + DriveLetter += (WCHAR) ( L'A' - L'a' ); + } + + if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) { + Vcb = DriveMapTable[DriveLetter - L'A']; + } else { + Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1']; + } + + // + // Did we get a connection? + // + + if ( Vcb == NULL ) { + Status = STATUS_BAD_NETWORK_PATH; + goto ExitWithCleanup; + } + + // + // Was the Vcb created for this user? + // + + if ( Uid.QuadPart != Vcb->Scb->UserUid.QuadPart ) { + Status = STATUS_ACCESS_DENIED; + goto ExitWithCleanup; + } + + Scb = Vcb->Scb; + + } else { + + // + // It's valid for the shell to pass us the remote name of a drive + // with no reference to the drive at all. Since we file these in + // volume prefix table with their drive letter information, we won't + // find them if we do a flat munge and lookup. Therefore, we have + // to walk the global vcb list and find the match. + // + + // + // Skip over the first slash of the provided UNC remote name. + // + + RemoteName.Length -= sizeof( WCHAR ); + RemoteName.Buffer += 1; + + for ( ListEntry = GlobalVcbList.Flink; + ( ListEntry != &GlobalVcbList ) && ( Scb == NULL ); + ListEntry = ListEntry->Flink ) { + + Vcb = CONTAINING_RECORD( ListEntry, VCB, GlobalVcbListEntry ); + + OriginalUnc.Length = Vcb->Name.Length; + OriginalUnc.MaximumLength = Vcb->Name.MaximumLength; + OriginalUnc.Buffer = Vcb->Name.Buffer; + + if ( Vcb->DriveLetter ) { + + // + // Try it as a drive connection. + // + + while ( ( OriginalUnc.Length ) && + ( OriginalUnc.Buffer[0] != L':' ) ) { + + OriginalUnc.Length -= sizeof( WCHAR ); + OriginalUnc.Buffer += 1; + } + + if ( OriginalUnc.Buffer[0] == L':' ) { + + OriginalUnc.Length -= sizeof( WCHAR ); + OriginalUnc.Buffer += 1; + + if ( RtlEqualUnicodeString( &OriginalUnc, + &RemoteName, + TRUE ) ) { + Scb = Vcb->Scb; + } + } + + } else { + + // + // Try it as a UNC connection; start by skipping + // only the leading slash, the walking to the next + // slash. + // + + OriginalUnc.Length -= sizeof( WCHAR ); + OriginalUnc.Buffer += 1; + + while ( ( OriginalUnc.Length ) && + ( OriginalUnc.Buffer[0] != L'\\' ) ) { + + OriginalUnc.Length -= sizeof( WCHAR ); + OriginalUnc.Buffer += 1; + } + + if ( OriginalUnc.Length ) { + + if ( RtlEqualUnicodeString( &OriginalUnc, + &RemoteName, + TRUE ) ) { + Scb = Vcb->Scb; + } + } + + } + } + + } + + if ( !Scb ) { + Status = STATUS_BAD_NETWORK_PATH; + goto ExitWithCleanup; + } + + NwReferenceScb( Scb->pNpScb ); + ReferenceScb = TRUE; + NwReleaseRcb( &NwRcb ); + OwnRcb = FALSE; + + DebugTrace( 0, Dbg, "GetConnectionPerformance: Scb is 0x%08lx\n", Scb ); + + // + // Now dig out the performance info from the LIP negotiation. + // + // dwSpeed - The speed of the media to the network resource in units of 100bps (e.g 1,200 + // baud point to point link returns 12). + // dwDelay - The delay introduced by the network when sending information (i.e. the time + // between starting sending data and the time that it starts being received) in + // units of a millisecond. This is in addition to any latency that was incorporated + // into the calculation of dwSpeed, so the value returned will be 0 for accessing + // most resources. + // dwOptDataSize - A recommendation for the size of data in bytes that is most efficiently + // sent through the network when an application makes a single request to + // the network resource. For example, for a disk network resource, this + // value might be 2048 or 512 when writing a block of data. + + (InputBuffer->Parameters).GetConnPerformance.dwFlags = WNCON_DYNAMIC; + (InputBuffer->Parameters).GetConnPerformance.dwDelay = 0; + (InputBuffer->Parameters).GetConnPerformance.dwOptDataSize = Scb->pNpScb->BufferSize; + (InputBuffer->Parameters).GetConnPerformance.dwSpeed = Scb->pNpScb->LipDataSpeed; + + // + // BUGBUG: We don't return any good speed info for servers that have not yet + // negotiated lip. We may return out of date information for servers that have + // become disconnected unless a RAS line transition occurred. This API is bogus. + // + + Status = STATUS_SUCCESS; + +ExitWithCleanup: + + if ( OwnRcb ) { + NwReleaseRcb( &NwRcb ); + } + + if ( ReferenceScb ) { + NwDereferenceScb( Scb->pNpScb ); + } + + return Status; + +} + +NTSTATUS +SetShareBit( + IN PIRP_CONTEXT IrpContext, + PFILE_OBJECT FileObject + ) +/*+++ + +SetShareBit: + + This function sets the share bit on a file. + The bit won't get set until all handles to the + file are closed. + +---*/ +{ + + NTSTATUS Status; + + PIRP Irp = IrpContext->pOriginalIrp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + NODE_TYPE_CODE nodeTypeCode; + PICB pIcb; + PFCB pFcb; + PVOID fsContext, fsContext2; + + DebugTrace( 0, Dbg, "SetShareBit.\n", 0 ); + + // + // Make sure this is a handle to a file. + // + + nodeTypeCode = NwDecodeFileObject( FileObject, &fsContext, &fsContext2 ); + + if ( nodeTypeCode != NW_NTC_ICB ) { + DebugTrace( 0, Dbg, "You can only set the share bit on a file!\n", 0 ); + return STATUS_INVALID_PARAMETER; + } + + pIcb = (PICB) fsContext2; + pFcb = pIcb->SuperType.Fcb; + + if ( pFcb->NodeTypeCode != NW_NTC_FCB ) { + DebugTrace( 0, Dbg, "You can't set the share bit on a directory!\n", 0 ); + return STATUS_INVALID_PARAMETER; + } + + // + // Acquire this FCB so we can muck with the flags. + // + + NwAcquireExclusiveFcb( pFcb->NonPagedFcb, TRUE ); + + SetFlag( pFcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE ); + + NwReleaseFcb( pFcb->NonPagedFcb ); + + return STATUS_SUCCESS; + +} + +VOID +LazySetShareable( + PIRP_CONTEXT IrpContext, + PICB pIcb, + PFCB pFcb +) +/*** + +Function Description: + + This function gets called everytime an ICB with a remote handle + is closed. If we are closing the last ICB to an FCB and the + caller has requested that we set the shareable bit on the FCB, + then we need to do so now. Otherwise, we simply return. + +Caveats: + + If we fail to set the shareable bit, there is no way to notify + the requestor of the operation that the operation was not carried + out. + +***/ +{ + + NTSTATUS Status; + + PLIST_ENTRY IcbListEntry; + PICB pCurrentIcb; + BOOLEAN OtherHandlesExist = FALSE; + + ULONG Attributes; + BOOLEAN AttributesAreValid = FALSE; + + + // + // Get to the head of the queue, acquire the RCB, + // and acquire this FCB to protect the ICB list + // and FCB flags. + // + + NwAppendToQueueAndWait( IrpContext ); + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + NwAcquireExclusiveFcb( pFcb->NonPagedFcb, TRUE ); + + // + // Scan the other ICBs on this FCB to see if any of + // them have remote handles. + // + + for ( IcbListEntry = pFcb->IcbList.Flink; + IcbListEntry != &(pFcb->IcbList) ; + IcbListEntry = IcbListEntry->Flink ) { + + pCurrentIcb = CONTAINING_RECORD( IcbListEntry, ICB, ListEntry ); + + if ( ( pCurrentIcb != pIcb ) && + ( pCurrentIcb->HasRemoteHandle ) ) { + OtherHandlesExist = TRUE; + } + } + + if ( OtherHandlesExist ) { + + // + // We'll do it when the last handle is closed. + // + + DebugTrace( 0, Dbg, "LazySetShareable: This isn't the last remote handle.\n", 0 ); + goto ReleaseAllAndExit; + } + + // + // We're closing the last handle. Make sure we have valid attributes. + // + + if ( !FlagOn( pFcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ) { + + if ( !BooleanFlagOn( pFcb->Flags, FCB_FLAGS_LONG_NAME ) ) { + + Status = ExchangeWithWait ( IrpContext, + SynchronousResponseCallback, + "FwbbJ", + NCP_SEARCH_FILE, + -1, + pFcb->Vcb->Specific.Disk.Handle, + SEARCH_ALL_FILES, + &pFcb->RelativeFileName ); + + if ( NT_SUCCESS( Status ) ) { + + Status = ParseResponse( IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "N==_b", + 14, + &Attributes ); + + if ( NT_SUCCESS( Status ) ) { + AttributesAreValid = TRUE; + } + } + + } else { + + Status = ExchangeWithWait ( IrpContext, + SynchronousResponseCallback, + "LbbWDbDbC", + NCP_LFN_GET_INFO, + pFcb->Vcb->Specific.Disk.LongNameSpace, + pFcb->Vcb->Specific.Disk.LongNameSpace, + SEARCH_ALL_FILES, + LFN_FLAG_INFO_ATTRIBUTES, + pFcb->Vcb->Specific.Disk.VolumeNumber, + pFcb->Vcb->Specific.Disk.Handle, + 0, + &pFcb->RelativeFileName ); + + if ( NT_SUCCESS( Status ) ) { + + Status = ParseResponse( IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "N_e", + 4, + &Attributes ); + + if ( NT_SUCCESS( Status ) ) { + AttributesAreValid = TRUE; + } + + } + + } + + } else { + + Attributes = pFcb->NonPagedFcb->Attributes; + AttributesAreValid = TRUE; + } + + if ( !AttributesAreValid ) { + DebugTrace( 0, Dbg, "Couldn't get valid attributes for this file.\n", 0 ); + goto ReleaseAllAndExit; + } + + // + // Do the set with the shareable bit on! + // + + if ( BooleanFlagOn( pFcb->Flags, FCB_FLAGS_LONG_NAME ) ) { + + Status = ExchangeWithWait( IrpContext, + SynchronousResponseCallback, + "LbbWDW--WW==WW==_W_bDbC", + NCP_LFN_SET_INFO, + pFcb->Vcb->Specific.Disk.LongNameSpace, + pFcb->Vcb->Specific.Disk.LongNameSpace, + SEARCH_ALL_FILES, + LFN_FLAG_SET_INFO_ATTRIBUTES, + Attributes | 0x80, + 0, + 0, + 0, + 0, + 8, + 0, + 8, + pFcb->Vcb->Specific.Disk.VolumeNumber, + pFcb->Vcb->Specific.Disk.Handle, + 0, + &pFcb->RelativeFileName ); + + } else { + + Status = ExchangeWithWait( IrpContext, + SynchronousResponseCallback, + "FbbbU", + NCP_SET_FILE_ATTRIBUTES, + Attributes | 0x80, + pFcb->Vcb->Specific.Disk.Handle, + SEARCH_ALL_FILES, + &pFcb->RelativeFileName ); + + } + + if ( !NT_SUCCESS( Status ) ) { + DebugTrace( 0, Dbg, "Failed to set the shareable attribute on the file.\n", 0 ); + ASSERT( FALSE && "File NOT marked as shareable!!" ); + } else { + DebugTrace( 0, Dbg, "Shareable bit successfully set.\n", 0 ); + ClearFlag( pFcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE ); + } + +ReleaseAllAndExit: + + NwReleaseFcb( pFcb->NonPagedFcb ); + NwReleaseRcb( &NwRcb ); + NwDequeueIrpContext( IrpContext, FALSE ); + return; +} -- cgit v1.2.3