windows-nt/Source/XPSP1/NT/base/fs/srv/dfs.c
2020-09-26 16:20:57 +08:00

678 lines
17 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
dfs.c
Abstract:
This module contains various support routines for processing Dfs related operations.
--*/
#include "precomp.h"
#include "dfs.tmh"
#pragma hdrstop
#include <dfsfsctl.h>
#define BugCheckFileId SRV_FILE_DFS
NTSTATUS
DfsGetReferrals(
ULONG ClientIPAddress,
PUNICODE_STRING DfsName,
USHORT MaxReferralLevel,
PVOID ReferralListBuffer,
PULONG SizeReferralListBuffer
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvInitializeDfs )
#pragma alloc_text( PAGE, SrvTerminateDfs )
#pragma alloc_text( PAGE, SrvSmbGetDfsReferral )
#pragma alloc_text( PAGE, SrvSmbReportDfsInconsistency )
#pragma alloc_text( PAGE, DfsGetReferrals )
#pragma alloc_text( PAGE, DfsNormalizeName )
#pragma alloc_text( PAGE, DfsFindShareName )
#pragma alloc_text( PAGE, SrvIsShareInDfs )
#endif
//
// Initialize with the Dfs driver. Called at startup
//
VOID
SrvInitializeDfs()
{
NTSTATUS status;
HANDLE dfsHandle;
UNICODE_STRING dfsDriverName;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatusBlock;
PAGED_CODE();
//
// Get the DFS dispatch entry for file control operations
//
RtlInitUnicodeString( &dfsDriverName, DFS_SERVER_NAME );
SrvInitializeObjectAttributes_U(
&objectAttributes,
&dfsDriverName,
0,
NULL,
NULL
);
status = IoCreateFile(
&dfsHandle,
GENERIC_READ | GENERIC_WRITE,
&objectAttributes,
&ioStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
0, // Create Options
NULL, // EA Buffer
0, // EA Length
CreateFileTypeNone, // File type
NULL, // ExtraCreateParameters
IO_FORCE_ACCESS_CHECK // Options
);
if( NT_SUCCESS( status ) ) {
//
// Get a pointer to the fast Device Control entry point of the Dfs driver so
// we can quickly perform Dfs operations
//
status = ObReferenceObjectByHandle(
dfsHandle,
0,
NULL,
KernelMode,
(PVOID *)&SrvDfsFileObject,
NULL
);
if( NT_SUCCESS( status ) ) {
PFAST_IO_DISPATCH fastIoDispatch;
SrvDfsDeviceObject = IoGetRelatedDeviceObject( SrvDfsFileObject );
fastIoDispatch = SrvDfsDeviceObject->DriverObject->FastIoDispatch;
if( fastIoDispatch != NULL &&
fastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET( FAST_IO_DISPATCH, FastIoDeviceControl ) ) {
SrvDfsFastIoDeviceControl = fastIoDispatch->FastIoDeviceControl;
}
if( SrvDfsFastIoDeviceControl == NULL ) {
ObDereferenceObject( SrvDfsFileObject );
SrvDfsFileObject = NULL;
SrvDfsDeviceObject = NULL;
}
}
SrvNtClose( dfsHandle, FALSE );
}
IF_DEBUG( DFS ) {
if( SrvDfsFastIoDeviceControl == NULL ) {
KdPrint(( "SRV: Dfs operations unavailable, status %X\n", status ));
}
}
}
//
// De-initialize with the Dfs driver. Called at server shutdown
//
VOID
SrvTerminateDfs()
{
PAGED_CODE();
//
// Disconnect from the Dfs driver
//
if( SrvDfsFileObject != NULL ) {
SrvDfsFastIoDeviceControl = NULL;
SrvDfsDeviceObject = NULL;
ObDereferenceObject( SrvDfsFileObject );
SrvDfsFileObject = NULL;
}
}
SMB_TRANS_STATUS
SrvSmbGetDfsReferral (
IN OUT PWORK_CONTEXT WorkContext
)
{
PTRANSACTION transaction;
UNICODE_STRING dfsName;
PREQ_GET_DFS_REFERRAL request;
NTSTATUS status = STATUS_SUCCESS;
PTREE_CONNECT treeConnect;
PSHARE share;
PVOID referrals;
ULONG size;
ULONG dataCount;
SMB_TRANS_STATUS SmbStatus = SmbTransStatusInProgress;
PAGED_CODE();
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_GET_DFS_REFERRALS;
SrvWmiStartContext(WorkContext);
transaction = WorkContext->Parameters.Transaction;
request = (PREQ_GET_DFS_REFERRAL)transaction->InParameters;
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes.
// The +1 is to ensure that there is at least room for a single unicode
// character in the supplied buffer, since this assumption is implicitly
// made below.
//
if( (transaction->ParameterCount <
sizeof( REQ_GET_DFS_REFERRAL ) + 1) ||
!SMB_IS_UNICODE( WorkContext ) ) {
//
// Not enough parameter bytes were sent.
//
IF_DEBUG( DFS ) {
KdPrint(( "SrvSmbGetDfsReferral: bad parameter byte counts: "
"%ld\n",
transaction->ParameterCount ));
if( !SMB_IS_UNICODE( WorkContext ) ) {
KdPrint(( "SrvSmbGetDfsReferral: NOT UNICODE!\n" ));
}
}
SrvLogInvalidSmb( WorkContext );
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
SmbStatus = SmbTransStatusErrorWithoutData;
status = STATUS_INVALID_SMB;
goto Cleanup;
}
//
// This SMB can only be sent over IPC$, by a logged-in user
//
treeConnect = transaction->TreeConnect;
share = treeConnect->Share;
if( share->ShareType != ShareTypePipe ) {
IF_DEBUG( DFS ) {
if( share->ShareType != ShareTypePipe ) {
KdPrint(( "SrvSmbGetDfsReferral: Wrong share type %d\n", share->ShareType ));
}
}
SrvSetSmbError( WorkContext, STATUS_ACCESS_DENIED );
SmbStatus = SmbTransStatusErrorWithoutData;
status = STATUS_ACCESS_DENIED;
goto Cleanup;
}
dfsName.Buffer = (PWCHAR)( request->RequestFileName );
dfsName.Length = (USHORT)(transaction->TotalParameterCount -
FIELD_OFFSET(REQ_GET_DFS_REFERRAL, RequestFileName));
dfsName.MaximumLength = dfsName.Length;
dfsName.Length -= sizeof(UNICODE_NULL);
dataCount = transaction->MaxDataCount;
status = DfsGetReferrals( WorkContext->Connection->ClientIPAddress,
&dfsName, request->MaxReferralLevel,
transaction->OutData,
&dataCount );
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
transaction->SetupCount = 0;
transaction->ParameterCount = 0;
transaction->DataCount = dataCount;
SmbStatus = SmbTransStatusSuccess;
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
}
NTSTATUS
DfsGetReferrals(
ULONG IPAddress,
PUNICODE_STRING DfsName,
USHORT MaxReferralLevel,
PVOID ReferralListBuffer,
PULONG SizeReferralListBuffer
)
{
DFS_GET_REFERRALS_INPUT_ARG dfsArgs;
IO_STATUS_BLOCK ioStatus;
PRESP_GET_DFS_REFERRAL pResp;
PUCHAR eBuffer;
ULONG i;
PAGED_CODE();
if( SrvDfsFastIoDeviceControl == NULL ) {
return STATUS_FS_DRIVER_REQUIRED;
}
IF_DEBUG( DFS ) {
KdPrint(( "SRV: Referral sought for: <%wZ>\n", DfsName ));
}
//
// Call DFS, getting back the vector of referrals
//
RtlZeroMemory( &dfsArgs, sizeof(dfsArgs) );
dfsArgs.DfsPathName = *DfsName;
dfsArgs.MaxReferralLevel = MaxReferralLevel;
if( IPAddress != 0 ) {
dfsArgs.IpAddress.IpFamily = TDI_ADDRESS_TYPE_IP;
dfsArgs.IpAddress.IpLen = sizeof( IPAddress );
RtlCopyMemory( dfsArgs.IpAddress.IpData, &IPAddress, sizeof( IPAddress ) );
}
SrvDfsFastIoDeviceControl(
SrvDfsFileObject,
TRUE,
&dfsArgs,
sizeof( dfsArgs ),
ReferralListBuffer,
*SizeReferralListBuffer,
FSCTL_DFS_GET_REFERRALS,
&ioStatus,
SrvDfsDeviceObject
);
if( NT_SUCCESS( ioStatus.Status ) ||
ioStatus.Status == STATUS_BUFFER_OVERFLOW ) {
*SizeReferralListBuffer = (ULONG)ioStatus.Information;
} else {
IF_DEBUG( DFS ) {
KdPrint(("\tSrvDfsFastIoDeviceControl returned %X, 0x%p\n",
ioStatus.Status, (PVOID)ioStatus.Information ));
}
}
return ioStatus.Status;
}
SMB_TRANS_STATUS
SrvSmbReportDfsInconsistency (
IN OUT PWORK_CONTEXT WorkContext
)
{
//
// We no longer support middle triangles in DFS
//
SrvSetSmbError( WorkContext, STATUS_NOT_SUPPORTED );
return SmbTransStatusErrorWithoutData;
#if XXX
PTRANSACTION transaction;
UNICODE_STRING dfsName;
PREQ_REPORT_DFS_INCONSISTENCY request;
PDFS_REFERRAL_V1 ref;
PTREE_CONNECT treeConnect;
PSHARE share;
DFS_REPORT_INCONSISTENCY_ARG dfsArgs;
IO_STATUS_BLOCK ioStatus;
PAGED_CODE();
transaction = WorkContext->Parameters.Transaction;
request = (PREQ_REPORT_DFS_INCONSISTENCY)transaction->InParameters;
ref = (PDFS_REFERRAL_V1)transaction->InData;
//
// Verify that enough parameter bytes were sent and the SMB is unicode
//
if( transaction->ParameterCount < sizeof( *request ) ||
!SMB_IS_UNICODE( WorkContext ) ) {
//
// Not enough parameter bytes were sent.
//
IF_DEBUG( DFS ) {
KdPrint(( "SrvSmbReportDfsInconsistency: bad parameter byte counts: "
"%ld %ld\n",
transaction->ParameterCount, sizeof( *request ) ));
if( !SMB_IS_UNICODE( WorkContext ) ) {
KdPrint(( "SrvSmbReportDfsInconsistency: NOT UNICODE!\n" ));
}
}
SrvLogInvalidSmb( WorkContext );
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
return SmbTransStatusErrorWithoutData;
}
//
// This SMB can only be sent over IPC$, by a logged-in user
//
treeConnect = transaction->TreeConnect;
share = treeConnect->Share;
if( share->ShareType != ShareTypePipe ||
transaction->Session->IsNullSession ) {
IF_DEBUG( DFS ) {
if( share->ShareType != ShareTypePipe ) {
KdPrint(( "SrvSmbReportDfsInconsistency: Wrong share type %d\n", share->ShareType ));
}
if( transaction->Session->IsNullSession ) {
KdPrint(( "SrvSmbReportDfsInconsistency: NULL session!\n" ));
}
}
SrvSetSmbError( WorkContext, STATUS_ACCESS_DENIED );
return SmbTransStatusErrorWithoutData;
}
dfsName.Buffer = ALIGN_SMB_WSTR( request->RequestFileName );
dfsName.Length = (USHORT)transaction->TotalParameterCount -
sizeof(UNICODE_NULL);
dfsName.MaximumLength = dfsName.Length;
IF_DEBUG( DFS ) {
KdPrint(( "SrvSmbReportDfsInconsistency: %wZ\n", &dfsName ));
}
dfsArgs.DfsPathName = dfsName;
dfsArgs.Ref = (PBYTE) ref;
if (SrvDfsFastIoDeviceControl != NULL) {
SrvDfsFastIoDeviceControl(
SrvDfsFileObject,
TRUE,
&dfsArgs,
sizeof( dfsArgs ),
NULL,
0,
FSCTL_DFS_REPORT_INCONSISTENCY,
&ioStatus,
SrvDfsDeviceObject
);
}
transaction->ParameterCount = 0;
transaction->DataCount = 0;
return SmbTransStatusSuccess;
#endif
}
NTSTATUS SRVFASTCALL
DfsNormalizeName(
IN PSHARE Share,
IN PUNICODE_STRING RelatedPath OPTIONAL,
IN BOOLEAN StripLastComponent,
IN PUNICODE_STRING String
)
{
DFS_TRANSLATE_PATH_ARG dfsArgs;
IO_STATUS_BLOCK ioStatus;
#if DBG
UNICODE_STRING save = *String;
#endif
PAGED_CODE();
//
// If Share->NtPathName covers String, then the remaining pathname in String->Buffer should
// be moved to String->Buffer and String->Length should be adjusted. In other words, on return
// the value of String->Buffer must not be changed, but the contents of String->Buffer needs to
// be adjusted.
//
ASSERT( String->Buffer != NULL );
IF_DEBUG( DFS ) {
KdPrint(( "DfsNormalizeName: %p, Share: %wZ\n", String, &Share->NtPathName ));
}
if( Share->ShareType == ShareTypeDisk &&
SrvDfsFastIoDeviceControl != NULL ) {
//
// Make an FSCTL to the DFS driver to normalize the name
//
dfsArgs.Flags = 0;
if (StripLastComponent)
dfsArgs.Flags |= DFS_TRANSLATE_STRIP_LAST_COMPONENT;
dfsArgs.SubDirectory = Share->NtPathName;
if (ARGUMENT_PRESENT(RelatedPath)) {
UNICODE_STRING Parent;
//ASSERT(RelatedPath->Length >= Share->DosPathName.Length);
if (RelatedPath->Length <= Share->DosPathName.Length) {
Parent.MaximumLength = Parent.Length = sizeof(WCHAR);
Parent.Buffer = L"\\";
} else {
Parent.MaximumLength = Parent.Length =
RelatedPath->Length - Share->DosPathName.Length;
Parent.Buffer =
&RelatedPath->Buffer[ Share->DosPathName.Length/sizeof(WCHAR) ];
}
dfsArgs.ParentPathName = Parent;
} else {
dfsArgs.ParentPathName.Length = 0;
dfsArgs.ParentPathName.MaximumLength = 0;
dfsArgs.ParentPathName.Buffer = NULL;
}
dfsArgs.DfsPathName = *String;
SrvDfsFastIoDeviceControl(
SrvDfsFileObject,
TRUE,
&dfsArgs,
sizeof( dfsArgs ),
NULL,
0,
FSCTL_DFS_TRANSLATE_PATH,
&ioStatus,
SrvDfsDeviceObject
);
if (NT_SUCCESS(ioStatus.Status)) {
ASSERT( dfsArgs.DfsPathName.Buffer == String->Buffer );
ASSERT( dfsArgs.DfsPathName.Length <= String->Length );
ASSERT( dfsArgs.DfsPathName.MaximumLength >= dfsArgs.DfsPathName.Length );
*String = dfsArgs.DfsPathName;
IF_DEBUG( DFS ) {
KdPrint(( "\t%wZ\n", String ));
}
}
} else {
ioStatus.Status = STATUS_FS_DRIVER_REQUIRED;
}
ASSERT( save.Buffer == String->Buffer );
ASSERT( save.Length >= String->Length );
if( !NT_SUCCESS( ioStatus.Status ) ) {
IF_DEBUG( DFS ) {
KdPrint(( "\tStatus %X\n", ioStatus.Status ));
}
}
return ioStatus.Status;
}
NTSTATUS SRVFASTCALL
DfsFindShareName(
IN PUNICODE_STRING ShareName
)
{
NTSTATUS status = STATUS_BAD_NETWORK_NAME;
DFS_FIND_SHARE_ARG dfsArgs;
IO_STATUS_BLOCK ioStatus;
KAPC_STATE ApcState;
PEPROCESS process;
//
// Ensure we are in the system process
//
process = IoGetCurrentProcess();
if ( process != SrvServerProcess ) {
KeStackAttachProcess( SrvServerProcess, &ApcState );
}
//
// If 'shareName' is known to the DFS driver, then we must return
// STATUS_PATH_NOT_COVERED. Otherwise we must return STATUS_BAD_NETWORK_NAME.
// This will cause the DFS client to come back and ask for a referral through
// the normal mechanism.
//
IF_DEBUG( DFS ) {
KdPrint(( "SRV: DfsFindShareName: %wZ\n", ShareName ));
}
if( SrvDfsFastIoDeviceControl != NULL ) {
dfsArgs.ShareName = *ShareName;
SrvDfsFastIoDeviceControl(
SrvDfsFileObject,
TRUE,
&dfsArgs,
sizeof( dfsArgs ),
NULL,
0,
FSCTL_DFS_FIND_SHARE,
&ioStatus,
SrvDfsDeviceObject
);
if( ioStatus.Status == STATUS_PATH_NOT_COVERED ) {
status = ioStatus.Status;
}
}
IF_DEBUG( DFS ) {
KdPrint(( "SRV: DfsFindShareName: status %X\n", status ));
}
//
// Get back to where we were
//
if( process != SrvServerProcess ) {
KeUnstackDetachProcess( &ApcState );
}
return status;
}
VOID SRVFASTCALL
SrvIsShareInDfs(
IN PSHARE Share,
OUT BOOLEAN *IsDfs,
OUT BOOLEAN *IsDfsRoot
)
{
DFS_IS_SHARE_IN_DFS_ARG dfsArgs;
IO_STATUS_BLOCK ioStatus;
KAPC_STATE ApcState;
PEPROCESS process;
PAGED_CODE();
*IsDfs = FALSE;
*IsDfsRoot = FALSE;
if( Share->ShareType != ShareTypeDisk ||
SrvDfsFastIoDeviceControl == NULL ) {
return;
}
dfsArgs.ServerType = 1; // SMB server
dfsArgs.ShareName = Share->ShareName;
dfsArgs.SharePath = Share->NtPathName;
//
// Ensure we are in the system process
//
process = IoGetCurrentProcess();
if ( process != SrvServerProcess ) {
KeStackAttachProcess( SrvServerProcess, &ApcState );
}
SrvDfsFastIoDeviceControl(
SrvDfsFileObject,
TRUE,
&dfsArgs,
sizeof( dfsArgs ),
NULL,
0,
FSCTL_DFS_IS_SHARE_IN_DFS,
&ioStatus,
SrvDfsDeviceObject
);
//
// Get back to where we were
//
if( process != SrvServerProcess ) {
KeUnstackDetachProcess( &ApcState );
}
if (NT_SUCCESS(ioStatus.Status)) {
if (dfsArgs.ShareType & DFS_SHARE_TYPE_DFS_VOLUME)
*IsDfs = TRUE;
if (dfsArgs.ShareType & DFS_SHARE_TYPE_ROOT)
*IsDfsRoot = TRUE;
}
}