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.
2555 lines
73 KiB
2555 lines
73 KiB
/*++
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
try {
|
|
NwLockUserBuffer( irp, IoWriteAccess, OutputBufferLength );
|
|
NwMapUserBuffer( irp, irp->RequestorMode, (PVOID *)&OutputBuffer );
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
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;
|
|
|
|
LOCKED_BUFFER LockedBuffer;
|
|
PNDS_SECURITY_CONTEXT pCredential;
|
|
UNICODE_STRING CredentialName;
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// tommye - MS bug 32134 (MCS 265)
|
|
//
|
|
// Probe the input arguments to make sure they are kosher before
|
|
// touching them.
|
|
//
|
|
|
|
try {
|
|
|
|
if ( irp->RequestorMode != KernelMode ) {
|
|
|
|
ProbeForRead( InputBuffer,
|
|
InputBufferLength,
|
|
sizeof(CHAR));
|
|
|
|
|
|
//
|
|
// tommye
|
|
//
|
|
// If the output buffer came from user space, then probe it for write.
|
|
//
|
|
|
|
if ((irpSp->Parameters.FileSystemControl.FsControlCode & 3) == METHOD_NEITHER) {
|
|
ULONG OutputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
ProbeForWrite( irp->UserBuffer,
|
|
OutputBufferLength,
|
|
sizeof(CHAR)
|
|
);
|
|
}
|
|
}
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// If this is a handle made on an ex-create, then
|
|
// we have to be aware of our credentials while
|
|
// jumping servers.
|
|
//
|
|
// This is not too intuitive since this doesn't
|
|
// seem to be a create path irp, but referrals
|
|
// on any path cause the create paths to be
|
|
// traversed.
|
|
//
|
|
|
|
if ( pIcb->IsExCredentialHandle ) {
|
|
|
|
pIrpContext->Specific.Create.fExCredentialCreate = TRUE;
|
|
|
|
pCredential = (PNDS_SECURITY_CONTEXT) pIcb->pContext;
|
|
|
|
Status = GetCredentialFromServerName( &pCredential->NdsTreeName,
|
|
&CredentialName );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
pIrpContext->Specific.Create.puCredentialName = &CredentialName;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// 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, InputBufferLength, &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, InputBufferLength, &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, InputBufferLength );
|
|
|
|
case FSCTL_NWR_NDS_SETCONTEXT:
|
|
|
|
return NdsSetContext( pIrpContext, InputBuffer, InputBufferLength );
|
|
|
|
case FSCTL_NWR_NDS_GETCONTEXT:
|
|
|
|
return NdsGetContext( pIrpContext, InputBuffer, InputBufferLength );
|
|
|
|
case FSCTL_NWR_NDS_VERIFY_TREE:
|
|
|
|
//
|
|
// Verify that this handle is valid for the specified tree.
|
|
//
|
|
|
|
return NdsVerifyTreeHandle( pIrpContext, InputBuffer, InputBufferLength );
|
|
|
|
case FSCTL_NWR_NDS_GET_QUEUE_INFO:
|
|
|
|
//
|
|
// Get the queue info for this print queue.
|
|
//
|
|
|
|
return NdsGetPrintQueueInfo( pIrpContext, InputBuffer, InputBufferLength );
|
|
|
|
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, InputBufferLength );
|
|
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
PNDS_SECURITY_CONTEXT pCredential;
|
|
UNICODE_STRING CredentialName;
|
|
|
|
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;
|
|
|
|
//
|
|
// If this is a handle made on an ex-create, then
|
|
// we have to be aware of our credentials while
|
|
// jumping servers.
|
|
//
|
|
// This is not too intuitive since this doesn't
|
|
// seem to be a create path irp, but referrals
|
|
// on any path cause the create paths to be
|
|
// traversed.
|
|
//
|
|
|
|
if ( pIcb->IsExCredentialHandle ) {
|
|
|
|
pIrpContext->Specific.Create.fExCredentialCreate = TRUE;
|
|
|
|
pCredential = (PNDS_SECURITY_CONTEXT) pIcb->pContext;
|
|
|
|
Status = GetCredentialFromServerName( &pCredential->NdsTreeName,
|
|
&CredentialName );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
pIrpContext->Specific.Create.puCredentialName = &CredentialName;
|
|
}
|
|
|
|
//
|
|
// 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,
|
|
ULONG RequestLength,
|
|
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;
|
|
DWORD NeededLength;
|
|
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 {
|
|
|
|
if (RequestLength < (FIELD_OFFSET(NWR_NDS_REQUEST_PACKET, Parameters.ResolveName.ObjectName) + pNdsRequest->Parameters.ResolveName.ObjectNameLength)) {
|
|
DebugTrace( 0, Dbg, "ResolveName Request Length is too short.\n", 0 );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
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 );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// tommye - MS 71699
|
|
// These were BUGB-G's but we made it a valid check instead.
|
|
// Original comment: I have seen this assertion fail because we only get
|
|
// a valid competion code (four bytes) and no more data. I wonder
|
|
// if the server is sending us this incomplete packet? If we
|
|
// don't get a complete referal, we probably shouldn't chase it.
|
|
//
|
|
|
|
if ((pWireResponse->RemoteEntry != RESOLVE_NAME_REFER_REMOTE) ||
|
|
(pReferral->ServerAddresses != 1) ||
|
|
(pReferral->AddressType != 0) ||
|
|
(pReferral->AddressLength != sizeof(IPXaddress))) {
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// Make sure the output buffer is long enough
|
|
//
|
|
|
|
NeededLength =
|
|
( 4 * sizeof( DWORD ) ) + Scb->pNpScb->ServerName.Length;
|
|
|
|
if (pLockedBuffer->dwRecvLen >= NeededLength) {
|
|
|
|
//
|
|
// If will fit - put the data in
|
|
//
|
|
|
|
RtlCopyMemory( pUserResponse->ReferredServer,
|
|
Scb->pNpScb->ServerName.Buffer,
|
|
Scb->pNpScb->ServerName.Length );
|
|
|
|
pUserResponse->ServerNameLength = Scb->pNpScb->ServerName.Length;
|
|
(pNdsRequest->Parameters).ResolveName.BytesWritten = NeededLength;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Set the return status - we still need to cleanup
|
|
// since the CreateScb call did succeed.
|
|
//
|
|
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 = (DWORD) (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,
|
|
ULONG RequestLength,
|
|
PLOCKED_BUFFER pLockedBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve the named attribute 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 dwIterHandle, dwOid;
|
|
UNICODE_STRING uAttributeName;
|
|
WCHAR AttributeName[MAX_NDS_SCHEMA_NAME_CHARS]; // was MAX_NDS_NAME_CHARS
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlZeroMemory( AttributeName, sizeof( AttributeName ) );
|
|
|
|
try {
|
|
|
|
if (RequestLength < (FIELD_OFFSET(NWR_NDS_REQUEST_PACKET, Parameters.ReadAttribute.AttributeName) + pNdsRequest->Parameters.ReadAttribute.AttributeNameLength)) {
|
|
DebugTrace( 0, Dbg, "ReadAttributes Request Length is too short.\n", 0 );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
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 = (DWORD) (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,
|
|
ULONG RequestLength
|
|
)
|
|
/*+++
|
|
|
|
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 = NULL;
|
|
ULONG OutputBufferLength;
|
|
|
|
UNICODE_STRING VolumeObject;
|
|
DWORD VolumeOid;
|
|
UNICODE_STRING HostServerAttr;
|
|
UNICODE_STRING HostVolumeAttr;
|
|
UNICODE_STRING Attribute;
|
|
|
|
PWCHAR ServerString;
|
|
ULONG ServerLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
try {
|
|
if (RequestLength < (FIELD_OFFSET(NWR_NDS_REQUEST_PACKET, Parameters.GetVolumeInfo.VolumeName) + pNdsRequest->Parameters.GetVolumeInfo.VolumeNameLen)) {
|
|
DebugTrace( 0, Dbg, "GetVolumeInfo Request Length is too short.\n", 0 );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
} except ( EXCEPTION_EXECUTE_HANDLER ) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
//
|
|
// 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 );
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
else {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
try {
|
|
|
|
//
|
|
// Prepare the input information.
|
|
//
|
|
|
|
VolumeObject.Length = (USHORT)pNdsRequest->Parameters.GetVolumeInfo.VolumeNameLen;
|
|
VolumeObject.MaximumLength = VolumeObject.Length;
|
|
VolumeObject.Buffer = &(pNdsRequest->Parameters.GetVolumeInfo.VolumeName[0]);
|
|
|
|
// tommye - make sure that the name length isn't bigger than we expect
|
|
|
|
if (VolumeObject.Length > MAX_NDS_NAME_SIZE) {
|
|
DebugTrace( 0 , Dbg, "NdsGetVolumeInformation: Volume name too long!.\n", 0 );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
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;
|
|
|
|
|
|
//
|
|
// 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 );
|
|
|
|
if (!Attribute.Buffer) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto CleanupScbReferences;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Make sure the ServerString will fit in the
|
|
// OutputBuffer. If not then return an error
|
|
//
|
|
|
|
ServerLength *= sizeof( WCHAR );
|
|
|
|
if (ServerLength > OutputBufferLength) {
|
|
FREE_POOL( Attribute.Buffer );
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto CleanupScbReferences;
|
|
}
|
|
|
|
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,
|
|
ULONG RequestLength
|
|
) {
|
|
|
|
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 {
|
|
|
|
if (RequestLength < (ULONG)(FIELD_OFFSET(NWR_NDS_REQUEST_PACKET, Parameters.OpenStream.StreamNameString) + pNdsRequest->Parameters.OpenStream.StreamName.Length)) {
|
|
DebugTrace( 0, Dbg, "OpenStream Request Length is too short.\n", 0 );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
dwOid = (pNdsRequest->Parameters).OpenStream.ObjectOid;
|
|
StreamAccess = (pNdsRequest->Parameters).OpenStream.StreamAccess;
|
|
RtlCopyUnicodeString( &uStream, &(pNdsRequest->Parameters).OpenStream.StreamName );
|
|
(pNdsRequest->Parameters).OpenStream.FileLength = 0;
|
|
|
|
} 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 );
|
|
|
|
try {
|
|
(pNdsRequest->Parameters).OpenStream.FileLength = dwFileLen;
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//We have a handle open but hit exception writing stream length
|
|
//back out to use space. Previously set length to 0 in case
|
|
//this happened. Caller must deal with invalid length if
|
|
//we return 0 with a valid stream (file) handle.
|
|
}
|
|
|
|
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,
|
|
ULONG RequestLength
|
|
) {
|
|
|
|
NTSTATUS Status;
|
|
|
|
PLOGON pLogon;
|
|
|
|
UNICODE_STRING Tree, Context;
|
|
PNDS_SECURITY_CONTEXT pCredentials;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( 0 , Dbg, "NDS set context.\n", 0 );
|
|
|
|
try {
|
|
if (RequestLength < (FIELD_OFFSET(NWR_NDS_REQUEST_PACKET, Parameters.SetContext.TreeAndContextString) + pNdsRequest->Parameters.SetContext.TreeNameLen)) {
|
|
DebugTrace( 0, Dbg, "SetContext Request Length is too short.\n", 0 );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
} except ( EXCEPTION_EXECUTE_HANDLER ) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
//
|
|
// Find out who this is.
|
|
//
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
pLogon = FindUser( &(pIrpContext->Specific.Create.UserUid), FALSE );
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
if ( !pLogon ) {
|
|
|
|
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.
|
|
//
|
|
|
|
try {
|
|
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( pIrpContext,
|
|
&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;
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
//
|
|
// 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, pIrpContext );
|
|
|
|
//
|
|
// RELAX! The credential list is free.
|
|
//
|
|
|
|
DebugTrace( 0, Dbg, "New context: %wZ\n", &Context );
|
|
return STATUS_SUCCESS;
|
|
|
|
ReleaseAndExit:
|
|
|
|
NwReleaseCredList( pLogon, pIrpContext );
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NdsGetContext(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNWR_NDS_REQUEST_PACKET pNdsRequest,
|
|
ULONG RequestLength
|
|
) {
|
|
|
|
NTSTATUS Status;
|
|
|
|
PLOGON pLogon;
|
|
|
|
UNICODE_STRING Tree;
|
|
PNDS_SECURITY_CONTEXT pCredentials;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( 0 , Dbg, "NDS get context.\n", 0 );
|
|
|
|
try {
|
|
if (RequestLength < (FIELD_OFFSET(NWR_NDS_REQUEST_PACKET, Parameters.GetContext.TreeNameString) + pNdsRequest->Parameters.GetContext.TreeNameLen)) {
|
|
DebugTrace( 0, Dbg, "GetContext Request Length is too short.\n", 0 );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
} except ( EXCEPTION_EXECUTE_HANDLER ) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
//
|
|
// Find out who this is.
|
|
//
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
pLogon = FindUser( &(pIrpContext->Specific.Create.UserUid), FALSE );
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
if ( !pLogon ) {
|
|
|
|
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.
|
|
//
|
|
|
|
try {
|
|
Tree.Length = (USHORT)(pNdsRequest->Parameters).GetContext.TreeNameLen;
|
|
Tree.MaximumLength = Tree.Length;
|
|
Tree.Buffer = (pNdsRequest->Parameters).GetContext.TreeNameString;
|
|
|
|
Status = NdsLookupCredentials( pIrpContext,
|
|
&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;
|
|
|
|
}
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
//
|
|
// Make sure we can report the whole thing.
|
|
// ALERT! We are holding the credential list!
|
|
//
|
|
|
|
try {
|
|
if ( (pNdsRequest->Parameters).GetContext.Context.MaximumLength <
|
|
pCredentials->CurrentContext.Length ) {
|
|
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto ReleaseAndExit;
|
|
}
|
|
|
|
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, pIrpContext );
|
|
|
|
//
|
|
// RELAX! The credential list is free.
|
|
//
|
|
|
|
DebugTrace( 0, Dbg, "Reported context: %wZ\n", &pCredentials->CurrentContext );
|
|
return STATUS_SUCCESS;
|
|
|
|
ReleaseAndExit:
|
|
|
|
NwReleaseCredList( pLogon, pIrpContext );
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
NdsVerifyTreeHandle(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNWR_NDS_REQUEST_PACKET pNdsRequest,
|
|
ULONG RequestLength
|
|
) {
|
|
|
|
NTSTATUS Status;
|
|
UNICODE_STRING NdsTree;
|
|
WCHAR TreeBuffer[NDS_TREE_NAME_LEN];
|
|
|
|
PAGED_CODE();
|
|
|
|
try {
|
|
|
|
if (RequestLength < (ULONG)(FIELD_OFFSET(NWR_NDS_REQUEST_PACKET, Parameters.VerifyTree.NameString) + pNdsRequest->Parameters.VerifyTree.TreeName.Length)) {
|
|
DebugTrace( 0, Dbg, "VerifyTreeHandle Request Length is too short.\n", 0 );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// 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,
|
|
ULONG RequestLength
|
|
) {
|
|
|
|
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();
|
|
|
|
try {
|
|
if (RequestLength < (ULONG)FIELD_OFFSET(NWR_NDS_REQUEST_PACKET, Parameters.GetQueueInfo.QueueId)) {
|
|
DebugTrace( 0, Dbg, "GetQueueInfo Request Length is too short.\n", 0 );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( pIrpContext->pOriginalIrp->RequestorMode != KernelMode ) {
|
|
|
|
ProbeForRead( pNdsRequest->Parameters.GetQueueInfo.QueueName.Buffer,
|
|
pNdsRequest->Parameters.GetQueueInfo.QueueName.Length,
|
|
sizeof(CHAR));
|
|
}
|
|
|
|
} except ( EXCEPTION_EXECUTE_HANDLER ) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
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;
|
|
|
|
//
|
|
// tommye - fix for case where pOriginalNpScb == NULL (devctl test case)
|
|
//
|
|
|
|
if (pOriginalNpScb == NULL) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
NwReferenceScb( pOriginalNpScb );
|
|
|
|
try {
|
|
Status = NdsVerifyObject( pIrpContext,
|
|
&(pNdsRequest->Parameters).GetQueueInfo.QueueName,
|
|
TRUE,
|
|
DEFAULT_RESOLVE_FLAGS,
|
|
&dwObjectId,
|
|
&dwObjectType );
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
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.
|
|
//
|
|
|
|
try {
|
|
Status = NdsReadStringAttribute( pIrpContext,
|
|
dwObjectId,
|
|
&ServerAttribute,
|
|
&(pNdsRequest->Parameters).GetQueueInfo.HostServer );
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto ExitWithCleanup;
|
|
}
|
|
|
|
//
|
|
// Dig out the actual server name from the X.500 name.
|
|
//
|
|
|
|
try {
|
|
Status = NdsGetServerBasicName( &(pNdsRequest->Parameters).GetQueueInfo.HostServer,
|
|
&uPrintServer );
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
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.
|
|
//
|
|
|
|
try {
|
|
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];
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
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;
|
|
ULONGLONG InputBufferLength;
|
|
|
|
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+1]; //+1 for ending NULL
|
|
|
|
OEM_STRING OemNewPassword;
|
|
BYTE NewBuffer[MAX_PW_CHARS+1]; //+1 for ending NULL
|
|
|
|
NODE_TYPE_CODE nodeTypeCode;
|
|
PSCB Scb;
|
|
PICB pIcb;
|
|
PVOID fsContext, fsObject;
|
|
UNICODE_STRING CredentialName;
|
|
PNDS_SECURITY_CONTEXT pCredential;
|
|
ULONG LocalNdsTreeNameLength;
|
|
ULONG LocalUserNameLength;
|
|
ULONG LocalCurrentPasswordLength;
|
|
ULONG LocalNewPasswordLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get the request.
|
|
//
|
|
|
|
irp = pIrpContext->pOriginalIrp;
|
|
irpSp = IoGetCurrentIrpStackLocation( irp );
|
|
|
|
Rrp = ( PNWR_NDS_REQUEST_PACKET ) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
|
|
InputBufferLength = irpSp->Parameters.FileSystemControl.InputBufferLength;
|
|
|
|
if ( !Rrp ) {
|
|
|
|
DebugTrace( 0, Dbg, "No raw request buffer.\n", 0 );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( InputBufferLength <
|
|
((ULONG) FIELD_OFFSET( NWR_NDS_REQUEST_PACKET, Parameters.ChangePass.StringBuffer[0]))) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Decode the file object to see if this is an ex-create handle.
|
|
//
|
|
|
|
nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
|
|
&fsContext,
|
|
&fsObject );
|
|
|
|
if ( nodeTypeCode == NW_NTC_ICB_SCB ) {
|
|
|
|
pIcb = (PICB) fsObject;
|
|
|
|
//
|
|
// If this is a handle made on an ex-create, then
|
|
// we have to be aware of our credentials while
|
|
// jumping servers.
|
|
//
|
|
// This is not too intuitive since this doesn't
|
|
// seem to be a create path irp, but referrals
|
|
// on any path cause the create paths to be
|
|
// traversed.
|
|
//
|
|
|
|
if ( pIcb->IsExCredentialHandle ) {
|
|
|
|
pIrpContext->Specific.Create.fExCredentialCreate = TRUE;
|
|
|
|
pCredential = (PNDS_SECURITY_CONTEXT) pIcb->pContext;
|
|
|
|
Status = GetCredentialFromServerName( &pCredential->NdsTreeName,
|
|
&CredentialName );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
pIrpContext->Specific.Create.puCredentialName = &CredentialName;
|
|
}
|
|
|
|
}
|
|
try {
|
|
|
|
if ( irp->RequestorMode != KernelMode ) {
|
|
|
|
ProbeForRead( Rrp,
|
|
(ULONG) InputBufferLength,
|
|
sizeof(CHAR)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Capture all the interesting parameters locally so that they don't change
|
|
// after validating them.
|
|
//
|
|
|
|
LocalNdsTreeNameLength = Rrp->Parameters.ChangePass.NdsTreeNameLength;
|
|
LocalUserNameLength = Rrp->Parameters.ChangePass.UserNameLength;
|
|
LocalCurrentPasswordLength = Rrp->Parameters.ChangePass.CurrentPasswordLength;
|
|
LocalNewPasswordLength = Rrp->Parameters.ChangePass.NewPasswordLength;
|
|
|
|
if ( InputBufferLength <
|
|
((ULONGLONG) FIELD_OFFSET( NWR_NDS_REQUEST_PACKET, Parameters.ChangePass.StringBuffer[0]) +
|
|
(ULONGLONG) LocalNdsTreeNameLength +
|
|
(ULONGLONG) LocalUserNameLength +
|
|
(ULONGLONG) LocalCurrentPasswordLength +
|
|
(ULONGLONG) LocalNewPasswordLength )) {
|
|
|
|
return( STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Dig out the parameters.
|
|
//
|
|
|
|
CurrentString = ( PBYTE ) &(Rrp->Parameters.ChangePass.StringBuffer[0]);
|
|
|
|
NdsTree.Length = NdsTree.MaximumLength =
|
|
( USHORT ) LocalNdsTreeNameLength;
|
|
NdsTree.Buffer = ( PWCHAR ) CurrentString;
|
|
|
|
CurrentString += NdsTree.Length;
|
|
|
|
UserName.Length = UserName.MaximumLength =
|
|
( USHORT ) LocalUserNameLength;
|
|
UserName.Buffer = ( PWCHAR ) CurrentString;
|
|
|
|
CurrentString += UserName.Length;
|
|
|
|
CurrentPassword.Length = CurrentPassword.MaximumLength =
|
|
( USHORT ) LocalCurrentPasswordLength;
|
|
CurrentPassword.Buffer = ( PWCHAR ) CurrentString;
|
|
|
|
CurrentString += CurrentPassword.Length;
|
|
|
|
NewPassword.Length = NewPassword.MaximumLength =
|
|
( USHORT ) LocalNewPasswordLength;
|
|
NewPassword.Buffer = ( PWCHAR ) CurrentString;
|
|
|
|
//
|
|
// Get a server to handle this request.
|
|
//
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// Check the lengths. We allow for the extra NULL at the
|
|
// end of the string in the calculation since the
|
|
// RtlUnicodeStringToOemSize routine calculates in
|
|
// the size of the null.
|
|
//
|
|
|
|
{
|
|
ULONG OemCurrentLength = RtlUnicodeStringToOemSize(&CurrentPassword);
|
|
ULONG OemNewLength = RtlUnicodeStringToOemSize(&NewPassword);
|
|
|
|
if (OemCurrentLength > (MAX_PW_CHARS+1)) {
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
if (OemNewLength > (MAX_PW_CHARS+1)) {
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
|
|
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 InputBufferLength;
|
|
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;
|
|
InputBufferLength = irpSp->Parameters.FileSystemControl.InputBufferLength;
|
|
|
|
OutputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
NwMapUserBuffer( irp, KernelMode, (PVOID *)&OutputBuffer );
|
|
|
|
if ( !Rrp || !OutputBufferLength || !OutputBuffer ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// tommye - MS bug 138643
|
|
//
|
|
// Probe the input arguments to make sure they are kosher before
|
|
// touching them.
|
|
//
|
|
|
|
try {
|
|
if ( irp->RequestorMode != KernelMode ) {
|
|
|
|
ProbeForRead( Rrp,
|
|
(ULONG) InputBufferLength,
|
|
sizeof(CHAR)
|
|
);
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
NwAcquireExclusiveCredList( pLogon, pIrpContext );
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Don't report ex create credentials.
|
|
//
|
|
|
|
if ( IsCredentialName( &(pNdsContext->NdsTreeName) ) ) {
|
|
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, pIrpContext );
|
|
|
|
DebugTrace( 0, Dbg, "Returning %d tree entries.\n", dwTreesReturned );
|
|
try {
|
|
Rrp->Parameters.ListTrees.TreesReturned = dwTreesReturned;
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|