/*++ 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" // // MUP lock macros // #define ACQUIRE_MUP_LOCK() NwAcquireOpenLock() #define RELEASE_MUP_LOCK() NwReleaseOpenLock() // // The local debug trace level // #define Dbg (DEBUG_TRACE_FSCTRL) // // Local procedure prototypes // NTSTATUS NwCommonDeviceIoControl ( IN PIRP_CONTEXT IrpContext ); #ifndef _PNP_POWER_ NTSTATUS StartRedirector( PIRP_CONTEXT IrpContext ); NTSTATUS StopRedirector( IN PIRP_CONTEXT IrpContext ); NTSTATUS BindToTransport ( IN PIRP_CONTEXT IrpContext ); #endif 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 GetConnectionDetails2( IN PIRP_CONTEXT IrpContext ); NTSTATUS GetConnectionPerformance( IN PIRP_CONTEXT IrpContext ); #ifndef _PNP_POWER_ NTSTATUS RegisterWithMup( VOID ); VOID DeregisterWithMup( VOID ); #endif 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( PIRP_CONTEXT pIrpContext, 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 = 0; #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, ChangePassword ) #pragma alloc_text( PAGE, SetInfo ) #pragma alloc_text( PAGE, GetStats ) #pragma alloc_text( PAGE, GetPrintJobId ) #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, GetConnectionInfo ) #pragma alloc_text( PAGE, GetPreferredServer ) #ifndef _PNP_POWER_ #pragma alloc_text( PAGE, BindToTransport ) #pragma alloc_text( PAGE, RegisterWithMup ) #pragma alloc_text( PAGE, DeregisterWithMup ) #endif #ifndef QFE_BUILD #pragma alloc_text( PAGE1, UserNcpCallback ) #pragma alloc_text( PAGE1, GetConnectionDetails ) #pragma alloc_text( PAGE1, GetConnectionDetails2 ) #pragma alloc_text( PAGE1, GetMessage ) #pragma alloc_text( PAGE1, EnumConnections ) #endif #endif #if 0 // Not pageable // see ifndef QFE_BUILD above GetConnStatus #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; } // // tommye // // If the output buffer came from user space, then probe it for write. // if (((Function & 3) == METHOD_NEITHER) && (Irp->RequestorMode != KernelMode)) { ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; try { ProbeForWrite( Irp->UserBuffer, OutputBufferLength, sizeof(CHAR) ); } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } 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: #ifndef _PNP_POWER_ Status = BindToTransport( IrpContext ); #else Status = RegisterTdiPnPEventHandlers( IrpContext ); #endif 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_CONN_DETAILS2: Status = GetConnectionDetails2( 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; //Terminal Server merge case FSCTL_NWR_CLOSEALL: NwCloseAllVcbs( IrpContext ); Status = STATUS_SUCCESS; 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; } #ifndef _PNP_POWER_ 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; SECURITY_SUBJECT_CONTEXT SubjectContext; PLOGON Logon; LARGE_INTEGER Uid; PAGED_CODE(); // // Don't re-register if we have already registered. // if ( TdiBindingHandle != NULL ) { return STATUS_SUCCESS; } // ========= Multi-user support ============== // Get the LOGON structure // SeCaptureSubjectContext(&SubjectContext); NwAcquireExclusiveRcb( &NwRcb, TRUE ); Uid = GetUid( &SubjectContext ); /* * Either we are setting the global stuff, or everything. * Yes this is a hack. It would be better to have two calls for * this stuff. */ if ( Uid.QuadPart == DefaultLuid.QuadPart ) Logon = NULL; else Logon = FindUser( &Uid, TRUE ); NwReleaseRcb( &NwRcb ); SeReleaseSubjectContext(&SubjectContext); // // Now we have the the Logon structure for the user //===================================== // // Register the PnP bind handlers. // DebugTrace( 0 , Dbg, "Register TDI bind handlers.\n", 0 ); TdiInitialize(); return TdiRegisterNotificationHandler( HandleTdiBindMessage, HandleTdiUnbindMessage, &TdiBindingHandle ); /************************ // // The old non-pnp code for legacy support. // 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; ******************/ } 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 ) { IrpContext->pOriginalIrp = NULL; // Avoid FreeIrpContext modifying freed Irp. 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; SECURITY_SUBJECT_CONTEXT SubjectContext; PLOGON Logon; LARGE_INTEGER Uid; PAGED_CODE(); SeCaptureSubjectContext(&SubjectContext); NwAcquireExclusiveRcb( &NwRcb, TRUE ); Uid = GetUid( &SubjectContext ); /* * Either we are setting the global stuff, or everything. * Yes this is a hack. It would be better to have two calls for * this stuff. */ if ( Uid.QuadPart == DefaultLuid.QuadPart ) Logon = NULL; else Logon = FindUser( &Uid, TRUE ); NwReleaseRcb( &NwRcb ); SeReleaseSubjectContext(&SubjectContext); 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 // //--- Multi-User modification: ------ // The NwPrintOption is per "Logon" based // if ( Logon == NULL ) { NwPrintOptions = InputBuffer->Parameters.SetInfo.PrintOption; DebugTrace(0, Dbg, "Set Global print options\n", 0); } else { Logon->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; KIRQL OldIrql; DebugTrace(+1, Dbg, "GetMessage\n", 0); NwLockUserBuffer( Irp, IoWriteAccess, OutputBufferLength ); NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); // // tommye MS bug 26590 / MCS 258 // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; } else { // // 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; KeAcquireSpinLock( &NwMessageSpinLock, &OldIrql ); // // tommye MS 17200 / MCS 366 // // Go ahead and get the cancel lock, this will keep // someone from cancelling the Irp while we're in here. // IoAcquireCancelSpinLock( &Irp->CancelIrql ); // // tommye // // If this Irp is cancelled, we're done // if (Irp->Cancel) { Status = STATUS_CANCELLED; } else { InsertTailList( &NwGetMessageList, &IrpContext->NextRequest ); IoMarkIrpPending( Irp ); // // tommye // // Set the cancel routine and release the cancel lock // IoSetCancelRoutine( Irp, NwCancelIrp ); } IoReleaseCancelSpinLock( Irp->CancelIrql ); KeReleaseSpinLock( &NwMessageSpinLock, OldIrql ); } 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 ); // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; } else { 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 ); // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; } else { OutputBuffer->JobId = Icb->JobId; 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 ); // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; } else { 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; // // tommye - MS 71688 // // Changed this from hard-coded '4' and '11' to use the // values in pScb. // OutputBuffer->MajorVersion = pScb->MajorVersion; OutputBuffer->MinorVersion = pScb->MinorVersion; 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 ); // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; } else { RtlCopyMemory( OutputBuffer->Address, &OurAddress, sizeof(OurAddress ); Status = STATUS_SUCCESS; } } DebugTrace(-1, Dbg, "GetOurAddress -> %08lx\n", Status ); return Status; } #endif #ifndef _PNP_POWER_ 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 ); } // -- MultiUser --- // Logoff and disconnect from all servers. // This makes very sure we do this. The workstation is having a // hard time deleting other user's connections. Also (at least on // slow debugging systems) RCB_STATE_SHUTDOWN cannot be on. // NwLogoffAllServers( IrpContext, NULL ); 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 ); } // // Unregister the bind handler with tdi. // if ( TdiBindingHandle != NULL ) { TdiDeregisterNotificationHandler( TdiBindingHandle ); TdiBindingHandle = NULL; } 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 ); } } #endif 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 = STATUS_SUCCESS; UNICODE_STRING RdrName; HANDLE LocalMupHandle = 0; PAGED_CODE(); RtlInitUnicodeString( &RdrName, DD_NWFS_DEVICE_NAME_U ); // // tommye MS 29173 / MCS 362 // // We had a problem with us getting in here twice; since // there was no lock, the MupHandle got registered twice // and we would leak when we shut down. Because we didn't // know if a lock would affect stability around the register // call, we'll go ahead and register using a local handle. // If we don't have our global handle, then we'll set it to // the local. Otherwise, we'll just clean up the local and // pretend everything is fine. MUP_LOCK macros are defined // at the top of this file. // if (MupHandle == 0) { Status = FsRtlRegisterUncProvider( &LocalMupHandle, &RdrName, FALSE // Do not support mailslots ); /** Lock **/ ACQUIRE_MUP_LOCK(); if (MupHandle) { RELEASE_MUP_LOCK(); FsRtlDeregisterUncProvider( LocalMupHandle ); return STATUS_SUCCESS; } else { MupHandle = LocalMupHandle; } /** Unlock **/ RELEASE_MUP_LOCK(); } 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(); DebugTrace(+1, Dbg, "QueryPath...\n", 0); 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; if (qpRequest == NULL) { return STATUS_INVALID_PARAMETER; } // // Probe before trying to read the request. This will make sure // we don't bugcheck when given a bogus address like 0xffff0000. // if ( Irp->RequestorMode != KernelMode ) { try { DebugTrace(+1, Dbg, "QueryPath...Probing for Read 1\n", 0); ProbeForRead( qpRequest, sizeof( QUERY_PATH_REQUEST ), sizeof(ULONG) ); DebugTrace(+1, Dbg, "QueryPath...Probing for Read 2\n", 0); ProbeForRead( qpRequest, sizeof( QUERY_PATH_REQUEST ) + qpRequest->PathNameLength, sizeof(ULONG) ); } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } try { 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; IrpContext->Specific.Create.fExCredentialCreate = FALSE; 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); // // 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( EXCEPTION_EXECUTE_HANDLER ) { 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. TRACKING - 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_PTR)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(pScb->pNpScb, (ULONG_PTR)PsGetCurrentThread(), &icb->Pid ); if ( !NT_SUCCESS( status ) ) { return( status ); } DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp Pid = %02lx\n", icb->Pid ); NwSetEndOfJobRequired(pScb->pNpScb, 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; // // tommye - make sure the input buffer is valid // try { // // Probe for safety. // if ( irp->RequestorMode != KernelMode ) { ProbeForRead( InputBuffer, InputBufferLength, sizeof( CHAR )); } } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } if ( OutputBufferLength ) { NwLockUserBuffer( irp, IoWriteAccess, OutputBufferLength ); NwMapUserBuffer( irp, KernelMode, (PVOID *)&OutputBuffer ); // // tommye MS bug 26590 / MCS 258 // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { DebugTrace(-1, DEBUG_TRACE_USERNCP, "NwMapUserBuffer returned NULL OutputBuffer", 0); return STATUS_INSUFFICIENT_RESOURCES; } } 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; PVCB * DriveMapTable; SECURITY_SUBJECT_CONTEXT SubjectContext; 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; //---Multi user---- // Need to get the Uid in order to find the proper DriveMapTable // SeCaptureSubjectContext(&SubjectContext); DriveMapTable = GetDriveMapTable( GetUid( &SubjectContext ) ); SeReleaseSubjectContext(&SubjectContext); //----- // // Find the VCB // try { if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( InputBuffer, InputBufferLength, sizeof(CHAR) ); } DriveName = InputBuffer->Parameters.GetConn.DeviceName; DriveNameLength = InputBuffer->Parameters.GetConn.DeviceNameLength; Vcb = NULL; // // check the device name length to see if its sound. This subtraction can't underflow because of the tests above // if ( DriveNameLength > InputBufferLength - FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConn.DeviceName) ) { return STATUS_INVALID_PARAMETER; } 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) { return STATUS_NO_SUCH_FILE; } OutputBuffer = (PNWR_SERVER_RESOURCE)Irp->UserBuffer; OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; if (OutputBufferLength < Vcb->Path.Length + 2 * sizeof(WCHAR)) { InputBuffer->Parameters.GetConn.BytesNeeded = Vcb->Path.Length + 2 * sizeof(WCHAR); return STATUS_BUFFER_TOO_SMALL; } // // Probe to ensure that the buffer is kosher. // if ( Irp->RequestorMode != KernelMode ) { ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(CHAR) ); } // // Return the Connection name in the form \\server\share // OutputBuffer->UncName[0] = L'\\'; RtlMoveMemory( &OutputBuffer->UncName[1], Vcb->Path.Buffer, Vcb->Path.Length ); OutputBuffer->UncName[ (Vcb->Path.Length + sizeof(WCHAR)) / sizeof(WCHAR) ] = L'\0'; Irp->IoStatus.Information = Vcb->Path.Length + 2 * sizeof(WCHAR); } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } 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 if ( NodeTypeCode == NW_NTC_ICB ) { IrpContext->pNpScb = Icb->SuperType.Fcb->Scb->pNpScb; Dcb = NonPagedDcb->Fcb; } else { return( STATUS_INVALID_PARAMETER ); } 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; WCHAR PrintPlaceHolder[] = L"LPT1"; WCHAR DiskPlaceHolder[] = L"A:"; UNICODE_STRING ContainerName; PCHAR FixedPortion; PWCHAR EndOfVariableData; ULONG EntrySize; ULONG_PTR ResumeKey; ULONG ShareType; ULONG EntriesRead = 0; ULONG EntriesRequested; DWORD ConnectionType; PLIST_ENTRY ListEntry; UNICODE_STRING Path; SECURITY_SUBJECT_CONTEXT SubjectContext; LARGE_INTEGER Uid; 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 ); } // // tommye - MS bug 32155 // Added ProbeForRead to check input buffers. // OutputBuffer has been probed by caller. // try { // // Probe for safety. // if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( InputBuffer, InputBufferLength, sizeof( CHAR )); } OutputBuffer = (PNWR_SERVER_RESOURCE)Irp->UserBuffer; OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; // // Probe to ensure that the buffer is kosher. // if ( Irp->RequestorMode != KernelMode ) { ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(CHAR)); } } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } Status = STATUS_SUCCESS; // ---- Multiuser --- // Get the Uid if ( InputBuffer->Parameters.EnumConn.ConnectionType & CONNTYPE_UID ) { Uid = *(PLARGE_INTEGER)(&InputBuffer->Parameters.EnumConn.Uid); } else { SeCaptureSubjectContext(&SubjectContext); Uid = GetUid( &SubjectContext ); SeReleaseSubjectContext(&SubjectContext); } 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_PTR) 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; } /* ---- Multi-user ---- * Skip connections that are not ours */ if ( Vcb->Scb->UserUid.QuadPart != Uid.QuadPart ) 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, (PCWSTR) &PrintPlaceHolder ); LocalName.Buffer[3] = Vcb->DriveLetter; ShareType = RESOURCETYPE_PRINT; } else { RtlInitUnicodeString( &LocalName, (PCWSTR) &DiskPlaceHolder ); 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 ) || // ---Mutl-user --- // Skip over not ours ( ( Scb != NULL ) && ( Scb->UserUid.QuadPart != Uid.QuadPart ) ) || ( 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. --*/ { BOOL 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_PTR) *FixedPortion + *EntrySize) > (ULONG_PTR) *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_PTR) (*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 ); return 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 ); // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status ); return Status; } // // Probe the output buffer before touching it. // try { if ( Irp->RequestorMode != KernelMode ) { ProbeForWrite( OutputBuffer, 6 * sizeof(CHAR), sizeof(CHAR) ); } RtlCopyMemory( OutputBuffer, Icb->Handle, 6 * sizeof(CHAR)); } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } 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 ); // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { DebugTrace(-1, DEBUG_TRACE_USERNCP, "NwMapUserBuffer returned NULL OutputBuffer", 0); Status = STATUS_INSUFFICIENT_RESOURCES; } else { *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 ); // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { DebugTrace(-1, DEBUG_TRACE_USERNCP, "NwMapUserBuffer returned NULL OutputBuffer", 0); Status = STATUS_INSUFFICIENT_RESOURCES; } else { 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); // // Probe the input arguments to make sure they are kosher before // touching them. // try { if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( InputBuffer, InputBufferLength, sizeof( CHAR ) ); } ServerName.Buffer = InputBuffer; ServerName.MaximumLength = (USHORT)InputBufferLength; ServerName.Length = (USHORT)InputBufferLength; Status = MakeUidServer( &UidServer, &Uid, &ServerName ); } except (EXCEPTION_EXECUTE_HANDLER) { return (GetExceptionCode()); } 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( IrpContext, &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( IrpContext, &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 ); // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { DebugTrace(-1, DEBUG_TRACE_USERNCP, "NwMapUserBuffer returned NULL OutputBuffer", 0); Status = STATUS_INSUFFICIENT_RESOURCES; goto ReleaseAndExit; } // // Probe to ensure that the buffer is kosher. // if ( Irp->RequestorMode != KernelMode ) { ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(CHAR) ); } 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, IrpContext ); } 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; LUID _system_luid = SYSTEM_LUID; PAGED_CODE(); DebugTrace(+1, Dbg, "GetChallenge\n", 0); // // Buffer big enough to contain fixed header? // if (InputBufferLength < (ULONG) FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0])) { return(STATUS_INVALID_PARAMETER); } // // Check output buffer length // if (OutputBufferLength < sizeof(NWR_GET_CHALLENGE_REPLY)) { return(STATUS_INVALID_PARAMETER); } // // tommye - make sure the InputBuffer is kosher // try { // // Probe for safety. // if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( InputBuffer, InputBufferLength, sizeof( CHAR )); } } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } // // Buffer big enough to contain the variable portion. Subtraction here can't underflow because // of the previous test if ((InputBufferLength - FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0]) < InputBuffer->ServerNameorPasswordLength)) { return(STATUS_INVALID_PARAMETER); } // // String length must be an even number of characters // if (InputBuffer->ServerNameorPasswordLength&(sizeof (WCHAR) - 1)) { 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); if (! RtlEqualLuid(&ProcessUid, &_system_luid)) { 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( PIRP_CONTEXT pIrpContext, 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( pIrpContext, &(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, pIrpContext ); } 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 ); // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { DebugTrace(-1, DEBUG_TRACE_USERNCP, "NwMapUserBuffer returned NULL OutputBuffer", 0); return STATUS_INSUFFICIENT_RESOURCES; } if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConnStatus.ConnectionName[1] ) ) { return( STATUS_INVALID_PARAMETER ); } // // 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 { // // Probe for safety. // if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( InputBuffer, InputBufferLength, sizeof( CHAR ) ); } if ( InputBuffer->Parameters.GetConnStatus.ConnectionNameLength != 0 ) { // // Check the connection name length to see if its sound. This // subtraction can't underflow because of the test above. // if ( InputBuffer->Parameters.GetConnStatus.ConnectionNameLength > InputBufferLength - FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConnStatus.ConnectionName) ) { return STATUS_INVALID_PARAMETER; } 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 ) { Status = GetExceptionCode(); DebugTrace( 0, Dbg, "Bad input buffer in GetConnStatus.\n" , 0 ); goto ExitWithCleanup; } // // 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( IrpContext, 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 ) && ( !IsCredentialName( &(pNpScb->pScb->NdsTreeName) ) ) ) { // // 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( IrpContext, 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; PVCB * DriveMapTable; // // 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; } // // tommye - MS bug 31996 // Added ProbeForRead to check input buffer. // Also added check for pConnInfo being NULL. // if (pConnInfo == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } try { // // Probe for safety. // if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( InputBuffer, InputBufferLength, sizeof( CHAR )); } } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } 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]); // // tommye - MS bug 129818 // // Probe ConnectionName for the advertised length // try { // // Probe for safety. // if ( Irp->RequestorMode != KernelMode ) { ProbeForWrite( ConnectionName.Buffer, ConnectionName.Length, sizeof( CHAR )); } } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } // // 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 ) { DriveMapTable = GetDriveMapTable( Uid ); 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; } } // // tommye - MS bug 16129 / MCS 360 // // If the client called WNetGetUser and pass only the server name // (e.g. "\\novell41") we would fail because we only looked in the // volume table. So, we go ahead and look through the server table // to see if there are any matches // else { Prefix = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidVolumeName, 0 ); if (Prefix != NULL) { Scb = CONTAINING_RECORD( Prefix, SCB, PrefixEntry ); goto GotScb; } } } 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; GotScb: ASSERT( Scb != NULL ); if ( ( Scb->MajorVersion > 3 ) && ( Scb->UserName.Length == 0 ) ) { NwAcquireExclusiveRcb( &NwRcb, TRUE ); pLogon = FindUser( &Uid, FALSE ); NwReleaseRcb( &NwRcb ); if ( pLogon ) { Status = NdsLookupCredentials( IrpContext, &(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, IrpContext ); } 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; } // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { DebugTrace(-1, DEBUG_TRACE_USERNCP, "NwMapUserBuffer returned NULL OutputBuffer", 0); return STATUS_INSUFFICIENT_RESOURCES; } // // 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; PVCB * DriveMapTable; // // Get the input buffer. // InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer; InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConnPerformance.RemoteName[1] ) ) { return( STATUS_INVALID_PARAMETER ); } // // Get the UID for the caller. // SeCaptureSubjectContext(&SubjectContext); Uid = GetUid( &SubjectContext ); SeReleaseSubjectContext(&SubjectContext); try { // // Probe for safety. // if ( Irp->RequestorMode != KernelMode ) { ProbeForRead( InputBuffer, InputBufferLength, sizeof( CHAR ) ); } // // Check the remote name length to see if it is sound. This subtraction // can't underflow because of the test above. // if ( InputBuffer->Parameters.GetConnPerformance.RemoteNameLength > InputBufferLength - FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConnPerformance.RemoteName) ) { return STATUS_INVALID_PARAMETER; } // // 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 ) { DriveMapTable = GetDriveMapTable( Uid ); 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; // // TRACKING: 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. // } except ( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); goto ExitWithCleanup; } 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; } NTSTATUS GetConnectionDetails2( IN PIRP_CONTEXT IrpContext ) /*++ Routine Description: This routine retrieves the details of a connection. This will return details as to whether a connection is NDS enabled and if yes, it will return the treename. The return structure looks like this: typedef struct _CONN_DETAILS2 { BOOL fNds; // TRUE if NDS, false for Bindery servers WCHAR NdsTreeName[48]; // The tree name or '\0' for a 2.x or 3.x server } CONN_DETAILS2, *PCONN_DETAILS2; 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; PCONN_DETAILS2 OutputBuffer; PSCB pScb; PNONPAGED_SCB pNpScb; PICB Icb; PVOID FsContext; NODE_TYPE_CODE nodeTypeCode; PAGED_CODE(); DebugTrace(+1, Dbg, "GetConnectionDetails2\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, "GetConnectionDetails2 -> %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( CONN_DETAILS2 ) ) { return STATUS_BUFFER_TOO_SMALL; } NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer ); // // tommye // // NwMapUserBuffer may return a NULL OutputBuffer in low resource // situations; this was not being checked. // if (OutputBuffer == NULL) { DebugTrace(-1, DEBUG_TRACE_USERNCP, "NwMapUserBuffer returned NULL OutputBuffer", 0); return STATUS_INSUFFICIENT_RESOURCES; } try { // // Set the NDS flag // if ( ( pScb->MajorVersion > 3 ) && ( pScb->UserName.Length == 0 ) ) { OutputBuffer->fNds = TRUE; } else { OutputBuffer->fNds = FALSE; } // // Copy over the tree name. // if ( pScb->NdsTreeName.Buffer != NULL && pScb->NdsTreeName.Length > 0 ) { RtlCopyMemory( (PBYTE)( OutputBuffer->NdsTreeName ), (PBYTE)(pScb->NdsTreeName.Buffer), pScb->NdsTreeName.Length ); *( OutputBuffer->NdsTreeName +( pScb->NdsTreeName.Length / sizeof( WCHAR ) ) ) = L'\0'; } else { *OutputBuffer->NdsTreeName = L'\0'; } Status = STATUS_SUCCESS; } except ( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); DebugTrace( 0, Dbg, "Exception %08lx accessing user mode buffer.\n", Status ); } DebugTrace(-1, Dbg, "GetConnectionDetails2 -> %08lx\n", Status ); return Status; }