/*++ 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 #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)ALIGN_SMB_WSTR( request->RequestFileName ); dfsName.Length = MIN( (USHORT)( END_OF_TRANSACTION_PARAMETERS( transaction ) - (PCHAR)dfsName.Buffer + 1) & ~0x1, 0xFFEE ); 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; } }