summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/ndsfsctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/ndsfsctl.c')
-rw-r--r--private/nw/rdr/ndsfsctl.c2128
1 files changed, 2128 insertions, 0 deletions
diff --git a/private/nw/rdr/ndsfsctl.c b/private/nw/rdr/ndsfsctl.c
new file mode 100644
index 000000000..b60c98d8f
--- /dev/null
+++ b/private/nw/rdr/ndsfsctl.c
@@ -0,0 +1,2128 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsFsctl.c
+
+Abstract:
+
+ This implements the NDS user mode hooks to the redirector.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+--*/
+
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_NDS)
+
+#pragma alloc_text( PAGE, DispatchNds )
+#pragma alloc_text( PAGE, PrepareLockedBufferFromFsd )
+#pragma alloc_text( PAGE, DoBrowseFsctl )
+#pragma alloc_text( PAGE, NdsRawFragex )
+#pragma alloc_text( PAGE, NdsResolveName )
+#pragma alloc_text( PAGE, NdsGetObjectInfo )
+#pragma alloc_text( PAGE, NdsListSubordinates )
+#pragma alloc_text( PAGE, NdsReadAttributes )
+#pragma alloc_text( PAGE, NdsGetVolumeInformation )
+#pragma alloc_text( PAGE, NdsOpenStream )
+#pragma alloc_text( PAGE, NdsSetContext )
+#pragma alloc_text( PAGE, NdsGetContext )
+#pragma alloc_text( PAGE, NdsVerifyTreeHandle )
+#pragma alloc_text( PAGE, NdsGetPrintQueueInfo )
+#pragma alloc_text( PAGE, NdsChangePass )
+#pragma alloc_text( PAGE, NdsListTrees )
+
+//
+// The main handler for all NDS FSCTL calls.
+//
+
+NTSTATUS
+DispatchNds(
+ ULONG IoctlCode,
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine instigates an NDS transaction requested from
+ the fsctl interface.
+
+Arguments:
+
+ IoctlCode - Supplies the code to be used for the NDS transaction.
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of transaction.
+
+--*/
+{
+ NTSTATUS Status = STATUS_NOT_SUPPORTED;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+
+ PAGED_CODE();
+
+ //
+ // Always set the user uid in the irp context so that
+ // referral creates NEVER go astray.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ IrpContext->Specific.Create.UserUid.QuadPart = Uid.QuadPart;
+
+ switch ( IoctlCode ) {
+
+ //
+ // These calls do not require us to lock down
+ // the user's buffer, but they do generate wire
+ // traffic.
+ //
+
+ case FSCTL_NWR_NDS_SETCONTEXT:
+ DebugTrace( 0, Dbg, "DispatchNds: Set Context\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_GETCONTEXT:
+ DebugTrace( 0, Dbg, "DispatchNds: Get Context\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_OPEN_STREAM:
+ DebugTrace( 0, Dbg, "DispatchNds: Open Stream\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_VERIFY_TREE:
+ DebugTrace( 0, Dbg, "DispatchNds: Verify Tree\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_GET_QUEUE_INFO:
+ DebugTrace( 0, Dbg, "DispatchNds: Get Queue Info\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_GET_VOLUME_INFO:
+ DebugTrace( 0, Dbg, "DispatchNds: Get Volume Info\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ //
+ // These four fsctl calls are the basis of browsing. They
+ // all require a request packet and a user buffer that we
+ // lock down.
+ //
+
+ case FSCTL_NWR_NDS_RESOLVE_NAME:
+ DebugTrace( 0, Dbg, "DispatchNds: Resolve Name\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ case FSCTL_NWR_NDS_LIST_SUBS:
+ DebugTrace( 0, Dbg, "DispatchNds: List Subordinates\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ case FSCTL_NWR_NDS_READ_INFO:
+ DebugTrace( 0, Dbg, "DispatchNds: Read Object Info\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ case FSCTL_NWR_NDS_READ_ATTR:
+ DebugTrace( 0, Dbg, "DispatchNds: Read Attribute\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ //
+ // Support for user mode fragment exchange.
+ //
+
+ case FSCTL_NWR_NDS_RAW_FRAGEX:
+ DebugTrace( 0, Dbg, "DispatchNds: Raw Fragex\n", 0 );
+ return NdsRawFragex( IrpContext );
+
+ //
+ // Change an NDS password.
+ //
+
+ case FSCTL_NWR_NDS_CHANGE_PASS:
+ DebugTrace( 0, Dbg, "DispatchNds: Change Password\n", 0 );
+ return NdsChangePass( IrpContext );
+
+ //
+ // Special fsctl to list the trees that a particular nt user
+ // has credentials to since the change pass ui runs under the
+ // system luid. Sigh.
+ //
+
+ case FSCTL_NWR_NDS_LIST_TREES:
+ DebugTrace( 0, Dbg, "DispatchNds: List trees\n", 0 );
+ return NdsListTrees( IrpContext );
+
+ default:
+
+ DebugTrace( 0, Dbg, "DispatchNds: No Such IOCTL\n", 0 );
+ break;
+
+ }
+
+ DebugTrace( 0, Dbg, " -> %08lx\n", Status );
+ return Status;
+
+}
+
+NTSTATUS
+PrepareLockedBufferFromFsd(
+ PIRP_CONTEXT pIrpContext,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*
+
+Description:
+
+ This routine takes the irp context for an FSD request with
+ a user mode buffer, and locks down the buffer so that it may
+ be sent to the transport. The locked down buffer, in addition
+ to being described in the irp and irp context, is described
+ in the LOCKED_BUFFER structure.
+
+Arguments:
+
+ pIrpContext - irp context for this request
+ pLockedBuffer - the locked response buffer
+
+*/
+{
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+
+ PVOID OutputBuffer;
+ ULONG OutputBufferLength;
+
+ PAGED_CODE();
+
+ //
+ // Get the irp and input buffer information and lock the
+ // buffer to the irp.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ OutputBufferLength = irpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ if ( !OutputBufferLength ) {
+
+ DebugTrace( 0, Dbg, "No fsd buffer length in PrepareLockedBufferFromFsd...\n", 0 );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ NwLockUserBuffer( irp, IoWriteAccess, OutputBufferLength );
+ NwMapUserBuffer( irp, irp->RequestorMode, (PVOID *)&OutputBuffer );
+
+ if ( !OutputBuffer ) {
+
+ DebugTrace( 0, Dbg, "No fsd buffer in PrepareLockedBufferFromFsd...\n", 0 );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Update the original MDL record in the Irp context, since
+ // NwLockUserBuffer may have created a new MDL.
+ //
+
+ pIrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ //
+ // Fill in our locked buffer description.
+ //
+
+ pLockedBuffer->pRecvBufferVa = MmGetMdlVirtualAddress( irp->MdlAddress );
+ pLockedBuffer->dwRecvLen = MdlLength( irp->MdlAddress );
+ pLockedBuffer->pRecvMdl = irp->MdlAddress;
+
+ // DebugTrace( 0, Dbg, "Locked fsd buffer at %08lx\n", pLockedBuffer->pRecvBufferVa );
+ // DebugTrace( 0, Dbg, " len -> %d\n", pLockedBuffer->dwRecvLen );
+ // DebugTrace( 0, Dbg, " recv mdl at %08lx\n", pLockedBuffer->pRecvMdl );
+
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+DoBrowseFsctl( PIRP_CONTEXT pIrpContext,
+ ULONG IoctlCode,
+ BOOL LockdownBuffer
+)
+/*+++
+
+Description:
+
+ This actually sets up for an NDS operation that requires wire
+ traffic, including locking down the user buffer if necessary.
+
+Arguments:
+
+ pIrpContext - the irp context for this request
+ IoctlCode - the ioctl requested
+ LockdownBuffer - do we need to lock down the user buffer
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+
+ PNWR_NDS_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+
+ PVOID fsContext, fsObject;
+ NODE_TYPE_CODE nodeTypeCode;
+ PSCB pScb = NULL;
+ PICB pIcb = NULL;
+
+ PVOID OutputBuffer;
+ ULONG OutputBufferLength;
+
+ LOCKED_BUFFER LockedBuffer;
+
+ PAGED_CODE();
+
+ //
+ // Get the request packet in the input buffer.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ InputBuffer = (PNWR_NDS_REQUEST_PACKET) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = irpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( !InputBuffer ||
+ !InputBufferLength ) {
+
+ DebugTrace( 0, Dbg, "BrowseFsctl has no input buffer...\n", 0 );
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Decode the file object and point the irp context the
+ // the appropriate connection... Should this be in an
+ // exception frame?
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsObject );
+
+ if ( nodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ pIcb = (PICB) fsObject;
+ pScb = (pIcb->SuperType).Scb;
+
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pIrpContext->pScb->pNpScb;
+ pIrpContext->Icb = pIcb;
+
+ }
+
+ //
+ // Lock the users buffer if this destined for the transport.
+ //
+
+ if ( LockdownBuffer &&
+ nodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ Status = PrepareLockedBufferFromFsd( pIrpContext, &LockedBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // Call the appropriate browser.
+ //
+
+ switch ( IoctlCode ) {
+
+ case FSCTL_NWR_NDS_RESOLVE_NAME:
+
+ return NdsResolveName( pIrpContext, InputBuffer, &LockedBuffer );
+
+ case FSCTL_NWR_NDS_LIST_SUBS:
+
+ return NdsListSubordinates( pIrpContext, InputBuffer, &LockedBuffer );
+
+ case FSCTL_NWR_NDS_READ_INFO:
+
+ return NdsGetObjectInfo( pIrpContext, InputBuffer, &LockedBuffer );
+
+ case FSCTL_NWR_NDS_READ_ATTR:
+
+ return NdsReadAttributes( pIrpContext, InputBuffer, &LockedBuffer );
+
+ default:
+
+ DebugTrace( 0, Dbg, "Invalid ioctl for locked BrowseFsctl...\n", 0 );
+ return STATUS_NOT_SUPPORTED;
+
+ }
+
+ }
+
+ //
+ // There's no user reply buffer for these calls, hence there's no lockdown.
+ //
+
+ switch ( IoctlCode ) {
+
+ case FSCTL_NWR_NDS_OPEN_STREAM:
+
+ //
+ // There has to be an ICB for this!
+ //
+
+ if ( nodeTypeCode != NW_NTC_ICB_SCB ) {
+ return STATUS_INVALID_HANDLE;
+ }
+
+ return NdsOpenStream( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_SETCONTEXT:
+
+ return NdsSetContext( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_GETCONTEXT:
+
+ return NdsGetContext( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_VERIFY_TREE:
+
+ //
+ // Verify that this handle is valid for the specified tree.
+ //
+
+ return NdsVerifyTreeHandle( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_GET_QUEUE_INFO:
+
+ //
+ // Get the queue info for this print queue.
+ //
+
+ return NdsGetPrintQueueInfo( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_GET_VOLUME_INFO:
+
+ //
+ // Get the volume info for this volume object.
+ // For the new shell property sheets.
+ //
+
+ return NdsGetVolumeInformation( pIrpContext, InputBuffer );
+
+ }
+
+ //
+ // All others are not supported.
+ //
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS
+NdsRawFragex(
+ PIRP_CONTEXT pIrpContext
+)
+/*+++
+
+ Send a raw user requested fragment.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ NODE_TYPE_CODE nodeTypeCode;
+ PVOID fsContext, fsObject;
+ PSCB pScb = NULL;
+ PICB pIcb = NULL;
+
+ DWORD NdsVerb;
+ LOCKED_BUFFER NdsRequest;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ PBYTE RawRequest;
+ DWORD RawRequestLen;
+
+ PAGED_CODE();
+
+ //
+ // Get the request.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ RawRequestLen = irpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( !Rrp || ( RawRequestLen < sizeof( NWR_NDS_REQUEST_PACKET ) ) ) {
+
+ DebugTrace( 0, Dbg, "No raw request buffer.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Decode the file object and point the irp context
+ // to the appropriate connection.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsObject );
+
+ if ( nodeTypeCode != NW_NTC_ICB_SCB ) {
+
+ DebugTrace( 0, Dbg, "A raw fragment request requires a server handle.\n", 0 );
+ return STATUS_INVALID_HANDLE;
+ }
+
+ pIcb = (PICB) fsObject;
+ pScb = (pIcb->SuperType).Scb;
+
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pIrpContext->pScb->pNpScb;
+ pIrpContext->Icb = pIcb;
+
+ //
+ // Dig out the parameters.
+ //
+
+ NdsVerb = Rrp->Parameters.RawRequest.NdsVerb;
+ RawRequestLen = Rrp->Parameters.RawRequest.RequestLength;
+ RawRequest = &Rrp->Parameters.RawRequest.Request[0];
+
+ //
+ // Get the reply buffer all locked in for the fragex.
+ //
+
+ Status = PrepareLockedBufferFromFsd( pIrpContext, &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ try {
+
+ if ( RawRequestLen ) {
+
+ Status = FragExWithWait( pIrpContext,
+ NdsVerb,
+ &NdsRequest,
+ "r",
+ RawRequest,
+ RawRequestLen );
+ } else {
+
+ Status = FragExWithWait( pIrpContext,
+ NdsVerb,
+ &NdsRequest,
+ NULL );
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+ Rrp->Parameters.RawRequest.ReplyLength = NdsRequest.dwBytesWritten;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = GetExceptionCode();
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsResolveName(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*+++
+
+Description:
+
+ This function decodes the resolve name request and makes the
+ actual wire request.
+
+Parameters:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The status of the exchange.
+
+---*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING uObjectName;
+ DWORD dwResolverFlags;
+ WCHAR ObjectName[MAX_NDS_NAME_CHARS];
+
+ PNDS_WIRE_RESPONSE_RESOLVE_NAME pWireResponse;
+ PNDS_WIRE_RESPONSE_RESOLVE_NAME_REFERRAL pReferral;
+ PNDS_RESPONSE_RESOLVE_NAME pUserResponse;
+ IPXaddress *ReferredAddress;
+ PSCB Scb, OldScb;
+
+ PAGED_CODE();
+
+ //
+ // Fill in the resolver flags and the unicode string for the
+ // object name from the request packet.
+ //
+
+ try {
+
+ uObjectName.Length = (USHORT)(pNdsRequest->Parameters).ResolveName.ObjectNameLength;
+ uObjectName.MaximumLength = sizeof( ObjectName );
+
+ if ( uObjectName.Length > sizeof( ObjectName ) ) {
+ ExRaiseStatus( STATUS_INVALID_BUFFER_SIZE );
+ }
+
+ RtlCopyMemory( ObjectName,
+ (pNdsRequest->Parameters).ResolveName.ObjectName,
+ uObjectName.Length );
+
+ uObjectName.Buffer = ObjectName;
+
+ dwResolverFlags = (pNdsRequest->Parameters).ResolveName.ResolverFlags;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user mode buffer in resolving name.\n", 0 );
+ return GetExceptionCode();
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_RESOLVE_NAME,
+ pLockedBuffer,
+ "DDDSDDDD",
+ 0, // version
+ dwResolverFlags, // flags
+ 0, // scope
+ &uObjectName, // distinguished name
+ 1,0, // transport type
+ 1,0 ); // treeWalker type
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // We need to convert the NDS_WIRE_RESPONSE_RESOLVE_NAME that
+ // we got from the server into an NDS_RESPONSE_RESOLVE_NAME
+ // for more general consumption. Notice that a referral packet
+ // has an additional DWORD in it - what a pain.
+ //
+
+ pWireResponse = (PNDS_WIRE_RESPONSE_RESOLVE_NAME) pLockedBuffer->pRecvBufferVa;
+ pReferral = (PNDS_WIRE_RESPONSE_RESOLVE_NAME_REFERRAL) pLockedBuffer->pRecvBufferVa;
+ pUserResponse = (PNDS_RESPONSE_RESOLVE_NAME) pLockedBuffer->pRecvBufferVa;
+
+ try {
+
+ if ( pWireResponse->RemoteEntry == RESOLVE_NAME_ACCEPT_REMOTE ) {
+
+ //
+ // This server can handle this request.
+ //
+
+ pUserResponse->ServerNameLength = 0;
+ (pNdsRequest->Parameters).ResolveName.BytesWritten = 4 * sizeof( DWORD );
+
+ } else {
+
+ ASSERT( pWireResponse->RemoteEntry == RESOLVE_NAME_REFER_REMOTE );
+
+ ASSERT( pReferral->ServerAddresses == 1 );
+ ASSERT( pReferral->AddressType == 0 );
+ ASSERT( pReferral->AddressLength == sizeof( IPXaddress ) );
+
+ //
+ // We've been referred to another server. We have to connect
+ // to the referred server to get the name for the caller.
+ //
+
+ ReferredAddress = (IPXaddress *) pReferral->Address;
+
+ OldScb = pIrpContext->pScb;
+
+ //
+ // Dequeue us from our original server. Do not defer the
+ // logon at this point since a referral means we're in the
+ // middle of a browse operation.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ Status = CreateScb( &Scb,
+ pIrpContext,
+ NULL,
+ ReferredAddress,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ RtlCopyMemory( pUserResponse->ReferredServer,
+ Scb->pNpScb->ServerName.Buffer,
+ Scb->pNpScb->ServerName.Length );
+
+ pUserResponse->ServerNameLength = Scb->pNpScb->ServerName.Length;
+ (pNdsRequest->Parameters).ResolveName.BytesWritten =
+ ( 4 * sizeof( DWORD ) ) + Scb->pNpScb->ServerName.Length;
+
+ DebugTrace( 0, Dbg, "Resolve name referral to: %wZ\n",
+ &Scb->pNpScb->ServerName );
+
+ //
+ // Restore the server pointers, we're not ready to jump
+ // servers yet since this might be a request from the fsd.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( Scb->pNpScb );
+ pIrpContext->pScb = OldScb;
+ pIrpContext->pNpScb = OldScb->pNpScb;
+
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user mode buffer in resolving name.\n", 0 );
+ return GetExceptionCode();
+
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NdsGetObjectInfo(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*++
+
+Routine Description:
+
+ Get the basic object information for the listed object.
+
+Routine Arguments:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The Status of the exchange.
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD dwObjId;
+
+ PAGED_CODE();
+
+ //
+ // Get the object id from the users request packet.
+ //
+
+ try {
+ dwObjId = (pNdsRequest->Parameters).GetObjectInfo.ObjectId;
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer in NdsGetObjectId...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+ }
+
+ //
+ // Hit the wire.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ_ENTRY_INFO,
+ pLockedBuffer,
+ "DD",
+ 0,
+ dwObjId );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ try {
+
+ (pNdsRequest->Parameters).GetObjectInfo.BytesWritten = pLockedBuffer->dwBytesWritten;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer after getting object info...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+
+ }
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsListSubordinates(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*++
+
+Routine Description:
+
+ List the immediate subordinates of an object.
+
+Routine Arguments:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The Status of the exchange.
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD dwParent, dwIterHandle;
+
+ PAGED_CODE();
+
+ //
+ // Dig out the request parameters.
+ //
+
+ try {
+
+ dwParent = (pNdsRequest->Parameters).ListSubordinates.ObjectId;
+ dwIterHandle = (pNdsRequest->Parameters).ListSubordinates.IterHandle;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! No user mode buffer in ListSubordinates...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+
+ }
+
+ //
+ // Make the request.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_LIST,
+ pLockedBuffer,
+ "DDDD",
+ 0,
+ 0x40,
+ dwIterHandle,
+ dwParent );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ try {
+
+ (pNdsRequest->Parameters).ListSubordinates.BytesWritten = pLockedBuffer->dwBytesWritten;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer after getting subordinate list...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+
+ }
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsReadAttributes(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*++
+
+Routine Description:
+
+ Retrieve the named attribute of an object.
+
+ BUGBUG: We don't really know the max attribute name size.
+
+Routine Arguments:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The Status of the exchange.
+
+--*/
+{
+ NTSTATUS Status;
+
+ DWORD dwIterHandle, dwOid;
+ UNICODE_STRING uAttributeName;
+ WCHAR AttributeName[MAX_NDS_NAME_CHARS];
+
+ PAGED_CODE();
+
+ RtlZeroMemory( AttributeName, sizeof( AttributeName ) );
+
+ try {
+
+ uAttributeName.Length = (USHORT)(pNdsRequest->Parameters).ReadAttribute.AttributeNameLength;
+ uAttributeName.MaximumLength = sizeof( AttributeName );
+
+ if ( uAttributeName.Length > uAttributeName.MaximumLength ) {
+ ExRaiseStatus( STATUS_INVALID_BUFFER_SIZE );
+ }
+
+ RtlCopyMemory( AttributeName,
+ (pNdsRequest->Parameters).ReadAttribute.AttributeName,
+ uAttributeName.Length );
+
+ uAttributeName.Buffer = AttributeName;
+
+ dwIterHandle = (pNdsRequest->Parameters).ReadAttribute.IterHandle;
+ dwOid = (pNdsRequest->Parameters).ReadAttribute.ObjectId;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0 , Dbg, "Bonk! Exception accessing user mode buffer in read attributes...\n", 0 );
+ return GetExceptionCode();
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ,
+ pLockedBuffer,
+ "DDDDDDS",
+ 0, // version
+ dwIterHandle, // iteration handle
+ dwOid, // object id
+ 1, // info type
+ //
+ // The attribute specifier has been seen at zero and
+ // at 0x4e0000. I don't know why... but zero doesn't
+ // work sometimes...
+ //
+ 0x4e0000, // attrib type
+ 1, // number of attribs
+ &uAttributeName ); // attrib name
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ try {
+
+ (pNdsRequest->Parameters).ReadAttribute.BytesWritten = pLockedBuffer->dwBytesWritten;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer after reading attribute...\n", 0 );
+ return GetExceptionCode();
+
+ }
+
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsGetVolumeInformation(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+)
+/*+++
+
+Description:
+
+ This function gets the name of the server that hosts
+ the listed nds volume.
+
+Parameters:
+
+ pIrpContext - describes the irp for this request
+ pNdsRequest - the request parameters
+
+---*/
+{
+
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PSCB pOriginalScb;
+ PBYTE OutputBuffer;
+ ULONG OutputBufferLength;
+
+ UNICODE_STRING VolumeObject;
+ DWORD VolumeOid;
+ UNICODE_STRING HostServerAttr;
+ UNICODE_STRING HostVolumeAttr;
+ UNICODE_STRING Attribute;
+
+ PWCHAR ServerString;
+ ULONG ServerLength;
+
+ PAGED_CODE();
+
+ //
+ // Get the irp and output buffer information.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ OutputBufferLength = irpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ if ( OutputBufferLength ) {
+ NwMapUserBuffer( irp, irp->RequestorMode, (PVOID *)&OutputBuffer );
+ }
+
+ //
+ // Prepare the input information.
+ //
+
+ VolumeObject.Length = (USHORT)pNdsRequest->Parameters.GetVolumeInfo.VolumeNameLen;
+ VolumeObject.MaximumLength = VolumeObject.Length;
+ VolumeObject.Buffer = &(pNdsRequest->Parameters.GetVolumeInfo.VolumeName[0]);
+
+ DebugTrace( 0, Dbg, "Retrieving volume info for %wZ\n", &VolumeObject );
+
+ HostServerAttr.Buffer = HOST_SERVER_ATTRIBUTE; // L"Host Server"
+ HostServerAttr.Length = sizeof( HOST_SERVER_ATTRIBUTE ) - sizeof( WCHAR );
+ HostServerAttr.MaximumLength = HostServerAttr.Length;
+
+ HostVolumeAttr.Buffer = HOST_VOLUME_ATTRIBUTE; // L"Host Resource Name"
+ HostVolumeAttr.Length = sizeof( HOST_VOLUME_ATTRIBUTE ) - sizeof( WCHAR );
+ HostVolumeAttr.MaximumLength = HostVolumeAttr.Length;
+
+ try {
+
+ //
+ // NdsResolveNameKm may have to jump servers to service this
+ // request, however it's dangerous for us to derefence the original
+ // scb because that would expose a scavenger race condition. So,
+ // we add an additional ref-count to the original scb and then clean
+ // up appropriately afterwards, depending on whether or not we
+ // jumped servers.
+ //
+
+ pOriginalScb = pIrpContext->pScb;
+
+ NwReferenceScb( pOriginalScb->pNpScb );
+
+ Status = NdsResolveNameKm ( pIrpContext,
+ &VolumeObject,
+ &VolumeOid,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( !NT_SUCCESS( Status )) {
+ NwDereferenceScb( pOriginalScb->pNpScb );
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ if ( pIrpContext->pScb == pOriginalScb ) {
+
+ //
+ // We didn't jump servers.
+ //
+
+ NwDereferenceScb( pOriginalScb->pNpScb );
+ }
+
+ //
+ // We have to read the server into a temporary buffer so
+ // we can strip off the x500 prefix and the context
+ // from the server name. This isn't really what I would
+ // call nice, but it's the way Netware works.
+ //
+
+ Attribute.Length = 0;
+ Attribute.MaximumLength = MAX_NDS_NAME_SIZE;
+ Attribute.Buffer = ALLOCATE_POOL( PagedPool, MAX_NDS_NAME_SIZE );
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ VolumeOid,
+ &HostServerAttr,
+ &Attribute );
+
+ if ( !NT_SUCCESS( Status )) {
+ FREE_POOL( Attribute.Buffer );
+ goto CleanupScbReferences;
+ }
+
+ ServerString = Attribute.Buffer;
+
+ while( Attribute.Length ) {
+
+ if ( *ServerString == L'=' ) {
+ ServerString += 1;
+ Attribute.Length -= sizeof( WCHAR );
+ break;
+ }
+
+ ServerString += 1;
+ Attribute.Length -= sizeof( WCHAR );
+ }
+
+ if ( Attribute.Length == 0 ) {
+ DebugTrace( 0, Dbg, "Malformed server for volume.\n", 0 );
+ FREE_POOL( Attribute.Buffer );
+ Status = STATUS_UNSUCCESSFUL;
+ goto CleanupScbReferences;
+ }
+
+ ServerLength = 0;
+
+ while ( ServerLength < (Attribute.Length / sizeof( WCHAR )) ) {
+
+ if ( ServerString[ServerLength] == L'.' ) {
+ break;
+ }
+
+ ServerLength++;
+ }
+
+ if ( ServerLength == ( Attribute.Length / sizeof( WCHAR ) ) ) {
+ DebugTrace( 0, Dbg, "Malformed server for volume.\n", 0 );
+ FREE_POOL( Attribute.Buffer );
+ Status = STATUS_UNSUCCESSFUL;
+ goto CleanupScbReferences;
+ }
+
+ ServerLength *= sizeof( WCHAR );
+ RtlCopyMemory( OutputBuffer, ServerString, ServerLength );
+
+ pNdsRequest->Parameters.GetVolumeInfo.ServerNameLen = ServerLength;
+
+ FREE_POOL( Attribute.Buffer );
+
+ Attribute.Length = Attribute.MaximumLength = (USHORT)ServerLength;
+ Attribute.Buffer = (PWCHAR)OutputBuffer;
+ DebugTrace( 0, Dbg, "Host server is: %wZ\n", &Attribute );
+
+ //
+ // Now do the volume in place. This is the easy one.
+ //
+
+ Attribute.MaximumLength = (USHORT)( OutputBufferLength - ServerLength );
+ Attribute.Buffer = (PWSTR) ( OutputBuffer + ServerLength );
+ Attribute.Length = 0;
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ VolumeOid,
+ &HostVolumeAttr,
+ &Attribute );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto CleanupScbReferences;
+ }
+
+ pNdsRequest->Parameters.GetVolumeInfo.TargetVolNameLen = Attribute.Length;
+ DebugTrace( 0, Dbg, "Host volume is: %wZ\n", &Attribute );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Exception handling user mode buffer in GetVolumeInfo.\n", 0 );
+ goto CleanupScbReferences;
+
+ }
+
+ Status = STATUS_SUCCESS;
+
+CleanupScbReferences:
+
+ if ( pIrpContext->pScb != pOriginalScb ) {
+
+ //
+ // We jumped servers and have to cleanup.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pIrpContext->pScb->pNpScb );
+ pIrpContext->pScb = pOriginalScb;
+ pIrpContext->pNpScb = pOriginalScb->pNpScb;
+
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NdsOpenStream(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ UNICODE_STRING uStream;
+ WCHAR StreamName[MAX_NDS_NAME_CHARS];
+
+ LOCKED_BUFFER NdsRequest;
+
+ DWORD dwOid, StreamAccess;
+ DWORD hNwHandle, dwFileLen;
+
+ PICB pIcb;
+ PSCB pScb = pIrpContext->pNpScb->pScb;
+
+ BOOLEAN LicensedConnection = FALSE;
+
+ PAGED_CODE();
+
+ pIcb = pIrpContext->Icb;
+
+ uStream.Length = 0;
+ uStream.MaximumLength = sizeof( StreamName );
+ uStream.Buffer = StreamName;
+
+ DebugTrace( 0 , Dbg, "NDS open stream...\n", 0 );
+
+ try {
+
+ dwOid = (pNdsRequest->Parameters).OpenStream.ObjectOid;
+ StreamAccess = (pNdsRequest->Parameters).OpenStream.StreamAccess;
+ RtlCopyUnicodeString( &uStream, &(pNdsRequest->Parameters).OpenStream.StreamName );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0 , Dbg, "Bonk! Bad user mode buffer in open stream.\n", 0 );
+ return GetExceptionCode();
+ }
+
+ //
+ // We have the oid and stream name; let's get the handle.
+ //
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // If we haven't licensed this connection yet, it's time. Get to the
+ // head of the queue to protect the SCB fields and authenticate the
+ // connection (do not defer the login).
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ ASSERT( pScb->MajorVersion > 3 );
+
+ if ( ( pScb->UserName.Length == 0 ) &&
+ ( pScb->VcbCount == 0 ) &&
+ ( pScb->OpenNdsStreams == 0 ) ) {
+
+ if ( pScb->pNpScb->State != SCB_STATE_IN_USE ) {
+
+ Status = ConnectScb( &pScb,
+ pIrpContext,
+ &(pScb->pNpScb->ServerName),
+ NULL, // address
+ NULL, // name
+ NULL, // password
+ FALSE, // defer login
+ FALSE, // delete connection
+ TRUE ); // existing scb
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Couldn't connect server %08lx to open NDS stream.\n", pScb );
+ goto ExitWithCleanup;
+ }
+ }
+
+ ASSERT( pScb->pNpScb->State == SCB_STATE_IN_USE );
+
+ Status = NdsLicenseConnection( pIrpContext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+ goto ExitWithCleanup;
+ }
+
+ LicensedConnection = TRUE;
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_OPEN_STREAM,
+ &NdsRequest,
+ "DDDs",
+ 0, // version
+ StreamAccess, // file access
+ dwOid, // object id
+ &uStream ); // attribute name
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_DD",
+ sizeof( DWORD ), // completion code
+ &hNwHandle, // remote handle
+ &dwFileLen ); // file length
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ *(WORD *)(&pIcb->Handle[0]) = (WORD)hNwHandle + 1;
+ *( (UNALIGNED DWORD *) (&pIcb->Handle[2]) ) = hNwHandle;
+
+ pIrpContext->pScb->OpenNdsStreams++;
+
+ DebugTrace( 0, Dbg, "File stream opened. Length = %d\n", dwFileLen );
+
+ (pNdsRequest->Parameters).OpenStream.FileLength = dwFileLen;
+ pIcb->HasRemoteHandle = TRUE;
+
+ pIcb->FileObject->CurrentByteOffset.QuadPart = 0;
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+
+ if ( ( !NT_SUCCESS( Status ) ) &&
+ ( LicensedConnection ) ) {
+ NdsUnlicenseConnection( pIrpContext );
+ }
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return Status;
+
+}
+
+NTSTATUS
+NdsSetContext(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ PLOGON pLogon;
+
+ UNICODE_STRING Tree, Context;
+ PNDS_SECURITY_CONTEXT pCredentials;
+
+ PAGED_CODE();
+
+ DebugTrace( 0 , Dbg, "NDS set context.\n", 0 );
+
+ //
+ // Find out who this is.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &(pIrpContext->Specific.Create.UserUid), FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !Logon ) {
+
+ DebugTrace( 0, Dbg, "Couldn't find logon data for this user.\n", 0 );
+ return STATUS_ACCESS_DENIED;
+
+ }
+
+ //
+ // Verify that this context really is a context.
+ //
+
+ Tree.Length = (USHORT)(pNdsRequest->Parameters).SetContext.TreeNameLen;
+ Tree.MaximumLength = Tree.Length;
+ Tree.Buffer = (pNdsRequest->Parameters).SetContext.TreeAndContextString;
+
+ Context.Length = (USHORT)(pNdsRequest->Parameters).SetContext.ContextLen;
+ Context.MaximumLength = Context.Length;
+ Context.Buffer = (WCHAR *) (((BYTE *)Tree.Buffer) + Tree.Length);
+
+ Status = NdsVerifyContext( pIrpContext, &Tree, &Context );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ Status = NdsLookupCredentials( &Tree,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "No credentials in set context.\n", 0 );
+ return STATUS_NO_SUCH_LOGON_SESSION;
+ }
+
+ //
+ // ALERT! We are holding the credential list!
+ //
+
+ if ( Context.Length > MAX_NDS_NAME_SIZE ) {
+
+ DebugTrace( 0, Dbg, "Context too long.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ReleaseAndExit;
+ }
+
+ try {
+
+ RtlCopyUnicodeString( &pCredentials->CurrentContext, &Context );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user buffer in SetContext.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ReleaseAndExit;
+ }
+
+ NwReleaseCredList( pLogon );
+
+ //
+ // RELAX! The credential list is free.
+ //
+
+ DebugTrace( 0, Dbg, "New context: %wZ\n", &Context );
+ return STATUS_SUCCESS;
+
+ReleaseAndExit:
+
+ NwReleaseCredList( pLogon );
+ return Status;
+}
+
+NTSTATUS
+NdsGetContext(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ PLOGON pLogon;
+
+ UNICODE_STRING Tree;
+ PNDS_SECURITY_CONTEXT pCredentials;
+
+ PAGED_CODE();
+
+ DebugTrace( 0 , Dbg, "NDS get context.\n", 0 );
+
+ //
+ // Find out who this is.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &(pIrpContext->Specific.Create.UserUid), FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !Logon ) {
+
+ DebugTrace( 0, Dbg, "Couldn't find logon data for this user.\n", 0 );
+ return STATUS_ACCESS_DENIED;
+
+ }
+
+ //
+ // We know who it is, so get the context.
+ //
+
+ Tree.Length = (USHORT)(pNdsRequest->Parameters).GetContext.TreeNameLen;
+ Tree.MaximumLength = Tree.Length;
+ Tree.Buffer = (pNdsRequest->Parameters).GetContext.TreeNameString;
+
+ Status = NdsLookupCredentials( &Tree,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // No context has been set, so report none.
+ //
+
+ try {
+
+ (pNdsRequest->Parameters).GetContext.Context.Length = 0;
+ return STATUS_SUCCESS;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user buffer in GetContext.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ }
+
+ //
+ // Make sure we can report the whole thing.
+ // ALERT! We are holding the credential list!
+ //
+
+ if ( (pNdsRequest->Parameters).GetContext.Context.MaximumLength <
+ pCredentials->CurrentContext.Length ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ReleaseAndExit;
+ }
+
+ try {
+
+ RtlCopyUnicodeString( &(pNdsRequest->Parameters).GetContext.Context,
+ &pCredentials->CurrentContext );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user buffer in GetContext.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ReleaseAndExit;
+ }
+
+ NwReleaseCredList( pLogon );
+
+ //
+ // RELAX! The credential list is free.
+ //
+
+ DebugTrace( 0, Dbg, "Reported context: %wZ\n", &pCredentials->CurrentContext );
+ return STATUS_SUCCESS;
+
+ReleaseAndExit:
+
+ NwReleaseCredList( pLogon );
+ return Status;
+
+}
+
+NTSTATUS
+NdsVerifyTreeHandle(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+ UNICODE_STRING NdsTree;
+ WCHAR TreeBuffer[NDS_TREE_NAME_LEN];
+
+ PAGED_CODE();
+
+ try {
+
+ //
+ // Check to see if the handle points to a dir server in the
+ // specified tree. Make sure to unmunge the tree name in
+ // the SCB first, just in case.
+ //
+
+ NdsTree.Length = 0;
+ NdsTree.MaximumLength = sizeof( TreeBuffer );
+ NdsTree.Buffer = TreeBuffer;
+
+ UnmungeCredentialName( &pIrpContext->pScb->NdsTreeName,
+ &NdsTree );
+
+ if ( !RtlCompareUnicodeString( &NdsTree,
+ &(pNdsRequest->Parameters).VerifyTree.TreeName,
+ TRUE ) ) {
+
+ DebugTrace( 0 , Dbg, "NdsVerifyTreeHandle: Success\n", 0 );
+ Status = STATUS_SUCCESS;
+ } else {
+
+ DebugTrace( 0 , Dbg, "NdsVerifyTreeHandle: Failure\n", 0 );
+ Status = STATUS_ACCESS_DENIED;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0 , Dbg, "NdsVerifyTreeHandle: Invalid parameters.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsGetPrintQueueInfo(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ UNICODE_STRING ServerAttribute;
+ WCHAR Server[] = L"Host Server";
+
+ PSCB pPrintHost = NULL;
+ PNONPAGED_SCB pOriginalNpScb = NULL;
+
+ DWORD dwObjectId, dwObjectType;
+
+ UNICODE_STRING uPrintServer;
+
+ BYTE *pbQueue, *pbRQueue;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( &ServerAttribute, Server );
+
+ //
+ // Make sure we have a print queue object. We may
+ // have to jump servers if we get referred to another
+ // replica. If this is the case, we can't lose the
+ // ref count on the original server since that's where
+ // the ICB handle is.
+ //
+
+ pOriginalNpScb = pIrpContext->pNpScb;
+ NwReferenceScb( pOriginalNpScb );
+
+ Status = NdsVerifyObject( pIrpContext,
+ &(pNdsRequest->Parameters).GetQueueInfo.QueueName,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS,
+ &dwObjectId,
+ &dwObjectType );
+
+ if ( pIrpContext->pNpScb == pOriginalNpScb ) {
+
+ //
+ // If we were not referred, remove the extra ref
+ // count and clear the original pointer.
+ //
+
+ NwDereferenceScb( pOriginalNpScb );
+ pOriginalNpScb = NULL;
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ if ( dwObjectType != NDS_OBJECTTYPE_QUEUE ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Retrieve the host server name.
+ //
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ dwObjectId,
+ &ServerAttribute,
+ &(pNdsRequest->Parameters).GetQueueInfo.HostServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the actual server name from the X.500 name.
+ //
+
+ Status = NdsGetServerBasicName( &(pNdsRequest->Parameters).GetQueueInfo.HostServer,
+ &uPrintServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Connect to the actual host server. If there was a referral, we
+ // can simply dump the referred server since we are holding the ref
+ // count on the original owner of the ICB.
+ //
+
+ if ( pOriginalNpScb ) {
+ NwDereferenceScb( pIrpContext->pNpScb );
+ } else {
+ pOriginalNpScb = pIrpContext->pNpScb;
+ }
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ Status = CreateScb( &pPrintHost,
+ pIrpContext,
+ &uPrintServer,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pIrpContext->pNpScb = NULL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Re-query the OID of the print queue object on this server.
+ // Don't allow any server jumping this time; we only need the
+ // oid of the queue.
+ //
+
+ Status = NdsVerifyObject( pIrpContext,
+ &(pNdsRequest->Parameters).GetQueueInfo.QueueName,
+ FALSE,
+ RSLV_CREATE_ID,
+ &dwObjectId,
+ NULL );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Byte swap the queue id.
+ //
+
+ pbRQueue = (BYTE *) &dwObjectId;
+ pbQueue = (BYTE *) &(pNdsRequest->Parameters).GetQueueInfo.QueueId;
+
+ pbQueue[0] = pbRQueue[3];
+ pbQueue[1] = pbRQueue[2];
+ pbQueue[2] = pbRQueue[1];
+ pbQueue[3] = pbRQueue[0];
+ }
+
+ExitWithCleanup:
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // Restore the pointers and ref counts as appropriate.
+ //
+
+ if ( pOriginalNpScb ) {
+
+ if ( pIrpContext->pNpScb ) {
+ NwDereferenceScb( pIrpContext->pNpScb );
+ }
+
+ pIrpContext->pNpScb = pOriginalNpScb;
+ pIrpContext->pScb = pOriginalNpScb->pScb;
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsChangePass(
+ PIRP_CONTEXT pIrpContext
+) {
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PNWR_NDS_REQUEST_PACKET Rrp;
+
+ UNICODE_STRING NdsTree;
+ UNICODE_STRING UserName;
+ UNICODE_STRING CurrentPassword;
+ UNICODE_STRING NewPassword;
+ PBYTE CurrentString;
+ BOOLEAN ServerReferenced = FALSE;
+
+ OEM_STRING OemCurrentPassword;
+ BYTE CurrentBuffer[MAX_PW_CHARS];
+
+ OEM_STRING OemNewPassword;
+ BYTE NewBuffer[MAX_PW_CHARS];
+
+ PSCB Scb;
+
+ PAGED_CODE();
+
+ //
+ // Get the request.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ if ( !Rrp ) {
+
+ DebugTrace( 0, Dbg, "No raw request buffer.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Dig out the parameters.
+ //
+
+ CurrentString = ( PBYTE ) &(Rrp->Parameters.ChangePass.StringBuffer[0]);
+
+ NdsTree.Length = NdsTree.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.NdsTreeNameLength;
+ NdsTree.Buffer = ( PWCHAR ) CurrentString;
+
+ CurrentString += NdsTree.Length;
+
+ UserName.Length = UserName.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.UserNameLength;
+ UserName.Buffer = ( PWCHAR ) CurrentString;
+
+ CurrentString += UserName.Length;
+
+ CurrentPassword.Length = CurrentPassword.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.CurrentPasswordLength;
+ CurrentPassword.Buffer = ( PWCHAR ) CurrentString;
+
+ CurrentString += CurrentPassword.Length;
+
+ NewPassword.Length = NewPassword.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.NewPasswordLength;
+ NewPassword.Buffer = ( PWCHAR ) CurrentString;
+
+ //
+ // Get a server to handle this request.
+ //
+
+ try {
+
+ //
+ // Convert the passwords to the appropriate type.
+ //
+
+ OemCurrentPassword.Length = 0;
+ OemCurrentPassword.MaximumLength = sizeof( CurrentBuffer );
+ OemCurrentPassword.Buffer = CurrentBuffer;
+
+ OemNewPassword.Length = 0;
+ OemNewPassword.MaximumLength = sizeof( NewBuffer );
+ OemNewPassword.Buffer = NewBuffer;
+
+ RtlUpcaseUnicodeStringToOemString( &OemCurrentPassword,
+ &CurrentPassword,
+ FALSE );
+
+ RtlUpcaseUnicodeStringToOemString( &OemNewPassword,
+ &NewPassword,
+ FALSE );
+
+ //
+ // Get a dir server to handle the request.
+ //
+
+ Status = NdsCreateTreeScb( pIrpContext,
+ &Scb,
+ &NdsTree,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "No dir servers for nds change password.\n", 0 );
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ ServerReferenced = TRUE;
+
+ //
+ // Perform the change password.
+ //
+
+ Status = NdsTreeLogin( pIrpContext,
+ &UserName,
+ &OemCurrentPassword,
+ &OemNewPassword,
+ NULL );
+
+ NwDereferenceScb( Scb->pNpScb );
+ ServerReferenced = FALSE;
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "NdsChangePass: Exception dealing with user request.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "NdsChangePassword succeeded for %wZ.\n", &UserName );
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( ServerReferenced ) {
+ NwDereferenceScb( Scb->pNpScb );
+ }
+
+ //
+ // We get STATUS_PASSWORD_EXPIRED when the user is not allowed
+ // to change their password on the Netware server, so we return
+ // PASSWORD_RESTRICTION instead.
+ //
+
+ if ( Status == STATUS_PASSWORD_EXPIRED ) {
+ Status = STATUS_PASSWORD_RESTRICTION;
+ }
+
+ return Status;
+
+
+}
+
+
+NTSTATUS
+NdsListTrees(
+ PIRP_CONTEXT pIrpContext
+)
+/*+++
+
+Description:
+
+ This odd little routine takes the NTUSER name of the logged in
+ user (on the system) and returns a list of NDS trees that the
+ NTUSER is connected to and the user names for those connections.
+ This is necessary because the change password ui runs in the
+ systems luid and can't access the GET_CONN_STATUS api and because
+ the change password code might happen when no user is logged in.
+
+ The return data in the users buffer is an array of
+ CONN_INFORMATION structures with the strings packed after the
+ structures. There is no continuation of this routine, so pass
+ a decent sized buffer.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ DWORD OutputBufferLength;
+ PBYTE OutputBuffer;
+
+ UNICODE_STRING NtUserName;
+ PLOGON pLogon;
+ DWORD dwTreesReturned = 0;
+ DWORD dwBytesNeeded;
+
+ PCONN_INFORMATION pConnInfo;
+ PLIST_ENTRY pNdsList;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+ //
+ // Get the request.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ OutputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ NwMapUserBuffer( irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ if ( !Rrp || !OutputBufferLength || !OutputBuffer ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Dig out the parameters.
+ //
+
+ NtUserName.Length = NtUserName.MaximumLength = (USHORT) Rrp->Parameters.ListTrees.NtUserNameLength;
+ NtUserName.Buffer = &(Rrp->Parameters.ListTrees.NtUserName[0]);
+
+ DebugTrace( 0, Dbg, "ListTrees: Looking up %wZ\n", &NtUserName );
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUserByName( &NtUserName );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !pLogon ) {
+ DebugTrace( 0, Dbg, "ListTrees: No such NT user.\n", 0 );
+ return STATUS_NO_SUCH_USER;
+ }
+
+ //
+ // Otherwise build the list of trees.
+ //
+
+ Rrp->Parameters.ListTrees.UserLuid = pLogon->UserUid;
+
+ NwAcquireExclusiveCredList( pLogon );
+ pConnInfo = ( PCONN_INFORMATION ) OutputBuffer;
+
+ pNdsList = pLogon->NdsCredentialList.Flink;
+
+ try {
+
+ while ( pNdsList != &(pLogon->NdsCredentialList) ) {
+
+ pNdsContext = CONTAINING_RECORD( pNdsList, NDS_SECURITY_CONTEXT, Next );
+
+ //
+ // Check to make sure there's a credential.
+ //
+
+ if ( pNdsContext->Credential == NULL ) {
+ goto ProcessNextListEntry;
+ }
+
+ //
+ // Check to make sure there's space to report.
+ //
+
+ dwBytesNeeded = ( sizeof( CONN_INFORMATION ) +
+ pNdsContext->Credential->userNameLength +
+ pNdsContext->NdsTreeName.Length -
+ sizeof( WCHAR ) );
+
+ if ( OutputBufferLength < dwBytesNeeded ) {
+ break;
+ }
+
+ //
+ // Report it! Note that the user name in the credential is NULL terminated.
+ //
+
+ pConnInfo->HostServerLength = pNdsContext->NdsTreeName.Length;
+ pConnInfo->UserNameLength = pNdsContext->Credential->userNameLength - sizeof( WCHAR );
+ pConnInfo->HostServer = (LPWSTR) ( ((BYTE *)pConnInfo) + sizeof( CONN_INFORMATION ) );
+ pConnInfo->UserName = (LPWSTR) ( ( (BYTE *)pConnInfo) +
+ sizeof( CONN_INFORMATION ) +
+ pConnInfo->HostServerLength );
+
+ RtlCopyMemory( pConnInfo->HostServer,
+ pNdsContext->NdsTreeName.Buffer,
+ pConnInfo->HostServerLength );
+
+ RtlCopyMemory( pConnInfo->UserName,
+ ( ((BYTE *) pNdsContext->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize ),
+ pConnInfo->UserNameLength );
+
+ OutputBufferLength -= dwBytesNeeded;
+ dwTreesReturned++;
+ pConnInfo = ( PCONN_INFORMATION ) ( ((BYTE *)pConnInfo) + dwBytesNeeded );
+
+ProcessNextListEntry:
+
+ //
+ // Do the next one.
+ //
+
+ pNdsList = pNdsList->Flink;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If we access violate, stop and return what we have.
+ //
+
+ DebugTrace( 0, Dbg, "User mode buffer access problem.\n", 0 );
+ }
+
+ NwReleaseCredList( pLogon );
+
+ DebugTrace( 0, Dbg, "Returning %d tree entries.\n", dwTreesReturned );
+ Rrp->Parameters.ListTrees.TreesReturned = dwTreesReturned;
+ return STATUS_SUCCESS;
+}