mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6829 lines
176 KiB
6829 lines
176 KiB
/*++
|
|
|
|
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\<servername> 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<NUL>
|
|
//
|
|
|
|
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;
|
|
}
|
|
|
|
|