|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
smbmisc.c
Abstract:
This module contains routines for processing MISC class SMBs: Echo Query FS Information Set FS Information Query Disk Information
Author:
Chuck Lenzmeier (chuckl) 9-Nov-1989 David Treadwell (davidtr)
Revision History:
--*/
#include "precomp.h"
#include "smbmisc.tmh"
#pragma hdrstop
#define BugCheckFileId SRV_FILE_SMBMISC
#define MAX_SMB_ECHO_COUNT 8
STATIC ULONG QueryVolumeInformation[] = { SMB_QUERY_FS_LABEL_INFO, // Base level
FileFsLabelInformation, // Mapping for base level
FileFsVolumeInformation, FileFsSizeInformation, FileFsDeviceInformation, FileFsAttributeInformation };
STATIC VOID SRVFASTCALL RestartEcho ( IN OUT PWORK_CONTEXT WorkContext );
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvSmbEcho )
#pragma alloc_text( PAGE, RestartEcho )
#pragma alloc_text( PAGE, SrvSmbQueryFsInformation )
#pragma alloc_text( PAGE, SrvSmbSetFsInformation )
#pragma alloc_text( PAGE, SrvSmbQueryInformationDisk )
#pragma alloc_text( PAGE, SrvSmbSetSecurityDescriptor )
#pragma alloc_text( PAGE, SrvSmbQuerySecurityDescriptor )
#pragma alloc_text( PAGE, SrvSmbQueryQuota )
#pragma alloc_text( PAGE, SrvSmbSetQuota )
#endif
#if 0
NOT PAGEABLE -- SrvSmbNtCancel #endif
SMB_PROCESSOR_RETURN_TYPE SrvSmbEcho ( SMB_PROCESSOR_PARAMETERS )
/*++
Routine Description:
Processes an Echo SMB. It sends the first echo, if any, specifying RestartEcho as the restart routine. That routine sends the remaining echoes.
Arguments:
SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description of the parameters to SMB processor routines.
Return Value:
SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
--*/
{ PREQ_ECHO request; PRESP_ECHO response; SMB_STATUS SmbStatus = SmbStatusInProgress;
PAGED_CODE( ); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_ECHO; SrvWmiStartContext(WorkContext);
request = (PREQ_ECHO)WorkContext->RequestParameters; response = (PRESP_ECHO)WorkContext->ResponseParameters;
//
// If the echo count is 0, there are no echoes to send.
//
if ( SmbGetUshort( &request->EchoCount ) == 0 ) { SmbStatus = SmbStatusNoResponse; goto Cleanup; }
//
// The echo count is not zero. Save it in the work context, then
// send the first echo.
//
// *** This code depends on the response buffer being the same as
// the request buffer. It does not copy the echo data from the
// request to the response. It does not update the DataLength
// of the response buffer.
//
// !!! Need to put in code to verify the requested TID, if any.
//
SrvReleaseContext( WorkContext );
WorkContext->Parameters.RemainingEchoCount = MIN( (USHORT)(SmbGetUshort( &request->EchoCount ) - 1), MAX_SMB_ECHO_COUNT );
ASSERT( WorkContext->ResponseHeader == WorkContext->RequestHeader );
SmbPutUshort( &response->SequenceNumber, 1 );
//
// Set the bit in the SMB that indicates this is a response from the
// server.
//
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
//
// Send the echo. Notice that the smb statistics will be updated
// here. Instead of measuring the time to finish all the echos,
// we just measure the time to respond to the first. This will
// save us the trouble of storing the timestamp somewhere.
//
SRV_START_SEND_2( WorkContext, SrvQueueWorkToFspAtSendCompletion, NULL, RestartEcho );
//
// The echo has been started. Tell the main SMB processor not to
// do anything more with the current SMB.
//
SmbStatus = SmbStatusInProgress;
Cleanup: SrvWmiEndContext(WorkContext); return SmbStatus;
} // SrvSmbEcho
VOID SRVFASTCALL RestartEcho ( IN PWORK_CONTEXT WorkContext )
/*++
Routine Description:
Processes send completion for an Echo. If more echoes are required, it sends the next one.
Arguments:
WorkContext - Supplies a pointer to the work context block describing server-specific context for the request.
Return Value:
None.
--*/
{ PCONNECTION connection;
PAGED_CODE( ); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_ECHO; SrvWmiStartContext(WorkContext);
IF_DEBUG(WORKER1) SrvPrint0( " - RestartEcho\n" );
//
// Get the connection pointer. The connection pointer is a
// referenced pointer. (The endpoint is valid because the
// connection references the endpoint.)
//
connection = WorkContext->Connection; IF_DEBUG(TRACE2) SrvPrint2( " connection %p, endpoint %p\n", connection, WorkContext->Endpoint );
//
// If the I/O request failed or was canceled, or if the connection
// is no longer active, clean up. (The connection is marked as
// closing when it is disconnected or when the endpoint is closed.)
//
// !!! If I/O failure, should we drop the connection?
//
if ( WorkContext->Irp->Cancel || !NT_SUCCESS(WorkContext->Irp->IoStatus.Status) || (GET_BLOCK_STATE(connection) != BlockStateActive) ) {
IF_DEBUG(TRACE2) { if ( WorkContext->Irp->Cancel ) { SrvPrint0( " I/O canceled\n" ); } else if ( !NT_SUCCESS(WorkContext->Irp->IoStatus.Status) ) { SrvPrint1( " I/O failed: %X\n", WorkContext->Irp->IoStatus.Status ); } else { SrvPrint0( " Connection no longer active\n" ); } }
//
// Indicate that SMB processing is complete.
//
SrvEndSmbProcessing( WorkContext, SmbStatusNoResponse ); IF_DEBUG(TRACE2) SrvPrint0( "RestartEcho complete\n" ); goto Cleanup;
}
//
// The request was successful, and the connection is still active.
// If there are no more echoes to be sent, indicate that SMB
// processing is complete.
//
if ( WorkContext->Parameters.RemainingEchoCount == 0 ) {
SrvEndSmbProcessing( WorkContext, SmbStatusNoResponse ); IF_DEBUG(TRACE2) SrvPrint0( "RestartEcho complete\n" ); goto Cleanup;
}
--WorkContext->Parameters.RemainingEchoCount;
//
// There are more echoes to be sent. Increment the sequence number
// in the response SMB, and send another echo.
//
SmbPutUshort( &((PRESP_ECHO)WorkContext->ResponseParameters)->SequenceNumber, (USHORT)(SmbGetUshort( &((PRESP_ECHO)WorkContext->ResponseParameters)->SequenceNumber ) + 1) );
//
// Don't do smb statistics a second time.
//
WorkContext->StartTime = 0;
//
// Send the echo. (Note that the response bit has already been
// set.)
//
SRV_START_SEND_2( WorkContext, SrvQueueWorkToFspAtSendCompletion, NULL, RestartEcho ); IF_DEBUG(TRACE2) SrvPrint0( "RestartEcho complete\n" );
Cleanup: SrvWmiEndContext(WorkContext); return;
} // RestartEcho
SMB_TRANS_STATUS SrvSmbQueryFsInformation ( IN OUT PWORK_CONTEXT WorkContext )
/*++
Routine Description:
Processes the Query FS Information request. This request arrives in a Transaction2 SMB. Query FS Information corresponds to the OS/2 DosQFSInfo service.
Arguments:
WorkContext - Supplies the address of a Work Context Block describing the current request. See smbtypes.h for a more complete description of the valid fields.
Return Value:
SMB_TRANS_STATUS - Indicates whether an error occurred, and, if so, whether data should be returned to the client. See smbtypes.h for a more complete description.
--*/
{ NTSTATUS status = STATUS_SUCCESS; SMB_TRANS_STATUS SmbStatus = SmbTransStatusInProgress; IO_STATUS_BLOCK ioStatusBlock; PTRANSACTION transaction; USHORT informationLevel;
USHORT trans2code; HANDLE fileHandle;
FILE_FS_SIZE_INFORMATION fsSizeInfo; PFSALLOCATE fsAllocate;
PFILE_FS_VOLUME_INFORMATION fsVolumeInfo; ULONG fsVolumeInfoLength; PFSINFO fsInfo; ULONG lengthVolumeLabel; BOOLEAN isUnicode; PREQ_QUERY_FS_INFORMATION request;
PAGED_CODE( ); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_QUERY_FS_INFORMATION; SrvWmiStartContext(WorkContext);
isUnicode = SMB_IS_UNICODE( WorkContext ); transaction = WorkContext->Parameters.Transaction; IF_SMB_DEBUG(MISC1) { SrvPrint1( "Query FS Information entered; transaction 0x%p\n", transaction ); }
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes. Query FS information has no
// response parameters.
//
if ( (transaction->ParameterCount < sizeof(REQ_QUERY_FS_INFORMATION)) ) {
//
// Not enough parameter bytes were sent.
//
IF_DEBUG(SMB_ERRORS) { SrvPrint2( "SrvSmbQueryFSInformation: bad parameter byte " "counts: %ld %ld\n", transaction->ParameterCount, transaction->MaxParameterCount ); }
SrvLogInvalidSmb( WorkContext );
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB ); status = STATUS_INVALID_SMB; SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
//
// See if a non-admin user is trying to access information on an Administrative share
//
status = SrvIsAllowedOnAdminShare( WorkContext, WorkContext->TreeConnect->Share );
if( !NT_SUCCESS( status ) ) { SrvSetSmbError( WorkContext, status ); SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
trans2code = SmbGetAlignedUshort(transaction->InSetup); IF_SMB_DEBUG(MISC1) { SrvPrint1("SrvSmbQueryFSInformation: Trans2 function = %x\n", trans2code); }
request = (PREQ_QUERY_FS_INFORMATION) transaction->InParameters;
ASSERT( trans2code == TRANS2_QUERY_FS_INFORMATION );
informationLevel = SmbGetUshort( &request->InformationLevel );
//
// *** The share handle is used to get the allocation
// information. This is a "storage channel," and as a
// result could allow people to get information to which
// they are not entitled. For a B2 security rating this may
// need to be changed.
//
status = SrvGetShareRootHandle( WorkContext->TreeConnect->Share );
if (!NT_SUCCESS(status)) {
IF_DEBUG(ERRORS) { SrvPrint1( "SrvSmbQueryFsInformation: SrvGetShareRootHandle failed %x.\n", status ); }
SrvSetSmbError( WorkContext, status ); SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
status = SrvSnapGetRootHandle( WorkContext, &fileHandle ); if( !NT_SUCCESS(status)) {
SrvSetSmbError( WorkContext, status ); SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
IF_SMB_DEBUG(MISC1) { SrvPrint0("SrvSmbQueryFSInformation: Using share root handle\n"); }
if( informationLevel < SMB_INFO_PASSTHROUGH ) { switch ( informationLevel ) {
case SMB_INFO_ALLOCATION:
//
// Return information about the disk.
//
fsAllocate = (PFSALLOCATE)transaction->OutData;
if ( transaction->MaxDataCount < sizeof(FSALLOCATE) ) { SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share ); SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW ); status = STATUS_BUFFER_OVERFLOW; SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
//
// *** The share handle is used to get the allocation
// information. This is a "storage channel," and as a
// result could allow people to get information to which
// they are not entitled. For a B2 security rating this may
// need to be changed.
//
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) { status = NtQueryVolumeInformationFile( fileHandle, &ioStatusBlock, &fsSizeInfo, sizeof(FILE_FS_SIZE_INFORMATION), FileFsSizeInformation );
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( NT_SUCCESS(status) ) { status = NtQueryVolumeInformationFile( fileHandle, &ioStatusBlock, &fsSizeInfo, sizeof(FILE_FS_SIZE_INFORMATION), FileFsSizeInformation ); } }
REVERT(); }
//
// Release the share root handle
//
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( !NT_SUCCESS(status) ) { INTERNAL_ERROR( ERROR_LEVEL_UNEXPECTED, "SrvSmbQueryFsInformation: NtQueryVolumeInformationFile " "returned %X", status, NULL );
SrvLogServiceFailure( SRV_SVC_NT_QUERY_VOL_INFO_FILE, status );
SrvSetSmbError( WorkContext, status ); SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
SmbPutAlignedUlong( &fsAllocate->idFileSystem, 0 ); SmbPutAlignedUlong( &fsAllocate->cSectorUnit, fsSizeInfo.SectorsPerAllocationUnit );
//
// *** If .HighPart is non-zero, there is a problem, as we can
// only return 32 bits for the volume size. In this case,
// we return the largest value that will fit.
//
SmbPutAlignedUlong( &fsAllocate->cUnit, fsSizeInfo.TotalAllocationUnits.HighPart == 0 ? fsSizeInfo.TotalAllocationUnits.LowPart : 0xffffffff ); SmbPutAlignedUlong( &fsAllocate->cUnitAvail, fsSizeInfo.AvailableAllocationUnits.HighPart == 0 ? fsSizeInfo.AvailableAllocationUnits.LowPart : 0xffffffff );
SmbPutAlignedUshort( &fsAllocate->cbSector, (USHORT)fsSizeInfo.BytesPerSector );
transaction->DataCount = sizeof(FSALLOCATE);
break;
case SMB_INFO_VOLUME:
//
// Query the volume label.
//
fsInfo = (PFSINFO)transaction->OutData;
//
// The maximum volume label length we are able to return, given
// the VOLUMELABEL structure (1 byte describes length of label),
// is 255 characters. Therefore, allocate a buffer large enough
// to hold a label that size, and if the label is longer then we
// will get STATUS_BUFFER_OVERFLOW from NtQueryVolumeInformationFile.
//
fsVolumeInfoLength = FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + 255 * sizeof(WCHAR); fsVolumeInfo = ALLOCATE_HEAP_COLD( fsVolumeInfoLength, BlockTypeDataBuffer );
if ( fsVolumeInfo == NULL ) { SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES ); status = STATUS_INSUFF_SERVER_RESOURCES; SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
//
// Get the label information.
//
status = NtQueryVolumeInformationFile( fileHandle, &ioStatusBlock, fsVolumeInfo, fsVolumeInfoLength, FileFsVolumeInformation );
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( NT_SUCCESS(status) ) { status = NtQueryVolumeInformationFile( fileHandle, &ioStatusBlock, fsVolumeInfo, fsVolumeInfoLength, FileFsVolumeInformation ); } }
//
// Release the share root handle
//
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( !NT_SUCCESS(status) ) { INTERNAL_ERROR( ERROR_LEVEL_UNEXPECTED, "SrvSmbQueryFSInformation: NtQueryVolumeInformationFile " "returned %X", status, NULL );
FREE_HEAP( fsVolumeInfo );
SrvLogServiceFailure( SRV_SVC_NT_QUERY_VOL_INFO_FILE, status );
SrvSetSmbError( WorkContext, status ); SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
lengthVolumeLabel = fsVolumeInfo->VolumeLabelLength;
//
// Make sure that the client can accept enough data. The volume
// label length is limited to 13 characters (8 + '.' + 3 + zero
// terminator) in OS/2, so return STATUS_BUFFER_OVERFLOW if the
// label is too long.
//
if ( !isUnicode && !IS_NT_DIALECT( WorkContext->Connection->SmbDialect ) ) {
//
// For a non-NT client, we truncate the volume label in case
// it is longer than 11+1 characters.
//
if ( lengthVolumeLabel > 11 * sizeof(WCHAR) ) { lengthVolumeLabel = 11 * sizeof(WCHAR); }
//
// Wedge a '.' into the name if it's longer than 8 characters long
//
if( lengthVolumeLabel > 8 * sizeof( WCHAR ) ) {
LPWSTR p = &fsVolumeInfo->VolumeLabel[11];
*p = *(p-1); // VolumeLabel[11] = VolumeLabel[10]
--p; *p = *(p-1); // VolumeLabel[10] = VolumeLabel[9]
--p; *p = *(p-1); // VolumeLabel[9] = VolumeLabel[8]
--p; *p = L'.'; // VolumeLabel[8] = '.'
}
}
if ( (ULONG)transaction->MaxDataCount < ( sizeof(FSINFO) - sizeof(VOLUMELABEL) + sizeof( UCHAR ) + lengthVolumeLabel / (isUnicode ? 1 : sizeof(WCHAR)) ) ) {
FREE_HEAP( fsVolumeInfo ); SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW ); status = STATUS_BUFFER_OVERFLOW; SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
SmbPutUlong( &fsInfo->ulVsn, fsVolumeInfo->VolumeSerialNumber );
//
// Put the label in the SMB in Unicode or OEM, depending on what
// was negotiated.
//
if ( isUnicode ) {
RtlCopyMemory( fsInfo->vol.szVolLabel, fsVolumeInfo->VolumeLabel, lengthVolumeLabel );
transaction->DataCount = sizeof(FSINFO) - sizeof(VOLUMELABEL) + lengthVolumeLabel;
fsInfo->vol.cch = (UCHAR)lengthVolumeLabel;
} else {
ULONG i; OEM_STRING oemString; UNICODE_STRING unicodeString;
if ( lengthVolumeLabel != 0 ) {
oemString.Buffer = fsInfo->vol.szVolLabel; oemString.MaximumLength = 12;
unicodeString.Buffer = (PWCH)fsVolumeInfo->VolumeLabel; unicodeString.Length = (USHORT) lengthVolumeLabel; unicodeString.MaximumLength = (USHORT) lengthVolumeLabel;
status = RtlUnicodeStringToOemString( &oemString, &unicodeString, FALSE ); ASSERT( NT_SUCCESS(status) ); }
fsInfo->vol.cch = (UCHAR) (lengthVolumeLabel / sizeof(WCHAR));
//
// Pad the end of the volume name with zeros to fill 12
// characters.
//
for ( i = fsInfo->vol.cch + 1 ; i < 12; i++ ) { fsInfo->vol.szVolLabel[i] = '\0'; }
transaction->DataCount = sizeof(FSINFO); }
IF_SMB_DEBUG(MISC1) { SrvPrint2( "volume label length is %d and label is %s\n", fsInfo->vol.cch, fsInfo->vol.szVolLabel ); }
FREE_HEAP( fsVolumeInfo );
break;
case SMB_QUERY_FS_VOLUME_INFO: case SMB_QUERY_FS_DEVICE_INFO: case SMB_QUERY_FS_ATTRIBUTE_INFO:
//
// These are NT infolevels. We always return unicode.
// Except for the fact that NEXUS on WFW calls through here and is
// not unicode (isaache)
//
// ASSERT( isUnicode );
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) { status = NtQueryVolumeInformationFile( fileHandle, &ioStatusBlock, transaction->OutData, transaction->MaxDataCount, MAP_SMB_INFO_TYPE_TO_NT( QueryVolumeInformation, informationLevel ) );
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( NT_SUCCESS(status) ) { status = NtQueryVolumeInformationFile( fileHandle, &ioStatusBlock, transaction->OutData, transaction->MaxDataCount, MAP_SMB_INFO_TYPE_TO_NT( QueryVolumeInformation, informationLevel ) ); } }
REVERT(); }
//
// Release the share root handle
//
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( NT_SUCCESS( status ) ) { //
// We need to return FAT to the client if the host volume is really
// FAT32
//
if( informationLevel == SMB_QUERY_FS_ATTRIBUTE_INFO && ioStatusBlock.Information > sizeof( FILE_FS_ATTRIBUTE_INFORMATION ) ) {
PFILE_FS_ATTRIBUTE_INFORMATION attrInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)(transaction->OutData);
if( attrInfo->FileSystemNameLength > 3*sizeof(WCHAR) && attrInfo->FileSystemName[0] == L'F' && attrInfo->FileSystemName[1] == L'A' && attrInfo->FileSystemName[2] == L'T' ) {
ioStatusBlock.Information = ioStatusBlock.Information - (attrInfo->FileSystemNameLength - 3*sizeof(WCHAR) );
attrInfo->FileSystemNameLength = 3 * sizeof(WCHAR); attrInfo->FileSystemName[3] = UNICODE_NULL; } }
transaction->DataCount = (ULONG)ioStatusBlock.Information;
} else { SrvSetSmbError( WorkContext, status ); SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
break;
case SMB_QUERY_FS_SIZE_INFO:
//
// These are NT infolevels. We always return unicode.
// Except for the fact that NEXUS on WFW calls through here and is
// not unicode (isaache)
//
// ASSERT( isUnicode );
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
status = NtQueryVolumeInformationFile( fileHandle, &ioStatusBlock, transaction->OutData, transaction->MaxDataCount, MAP_SMB_INFO_TYPE_TO_NT( QueryVolumeInformation, informationLevel ) ); //
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( NT_SUCCESS(status) ) { status = NtQueryVolumeInformationFile( fileHandle, &ioStatusBlock, transaction->OutData, transaction->MaxDataCount, MAP_SMB_INFO_TYPE_TO_NT( QueryVolumeInformation, informationLevel ) ); } }
REVERT(); }
//
// Release the share root handle
//
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( NT_SUCCESS( status ) ) { transaction->DataCount = (ULONG)ioStatusBlock.Information; } else { SrvSetSmbError( WorkContext, status ); SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
break;
default:
//
// An invalid information level was passed.
//
SrvSetSmbError( WorkContext, STATUS_OS2_INVALID_LEVEL ); status = STATUS_OS2_INVALID_LEVEL; SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; }
} else {
informationLevel -= SMB_INFO_PASSTHROUGH;
status = IoCheckQuerySetVolumeInformation( informationLevel, transaction->MaxDataCount, FALSE );
if( NT_SUCCESS( status ) ) {
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
status = NtQueryVolumeInformationFile( fileHandle, &ioStatusBlock, transaction->OutData, transaction->MaxDataCount, informationLevel );
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( NT_SUCCESS(status) ) { status = NtQueryVolumeInformationFile( fileHandle, &ioStatusBlock, transaction->OutData, transaction->MaxDataCount, informationLevel ); } }
REVERT(); } }
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( NT_SUCCESS( status ) ) { transaction->DataCount = (ULONG)ioStatusBlock.Information; } else { SrvSetSmbError( WorkContext, status ); SmbStatus = SmbTransStatusErrorWithoutData; goto Cleanup; } }
transaction->SetupCount = 0; transaction->ParameterCount = 0; SmbStatus = SmbTransStatusSuccess;
Cleanup: SrvWmiEndContext(WorkContext); return SmbStatus;
} // SrvSmbQueryFsInformation
SMB_TRANS_STATUS SrvSmbSetFsInformation ( IN OUT PWORK_CONTEXT WorkContext )
/*++
Routine Description:
Processes the Set FS Information request. This request arrives in a Transaction2 SMB.
Arguments:
WorkContext - Supplies the address of a Work Context Block describing the current request. See smbtypes.h for a more complete description of the valid fields.
Return Value:
SMB_TRANS_STATUS - Indicates whether an error occurred, and, if so, whether data should be returned to the client. See smbtypes.h for a more complete description.
--*/
{ SMB_TRANS_STATUS transactionStatus = SmbTransStatusInProgress; PREQ_SET_FS_INFORMATION request; NTSTATUS status = STATUS_SUCCESS; IO_STATUS_BLOCK ioStatusBlock; PTRANSACTION transaction; USHORT informationLevel; PSESSION session; PTREE_CONNECT treeConnect; PRFCB rfcb;
PAGED_CODE( ); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_SET_FS_INFORMATION; SrvWmiStartContext(WorkContext);
transaction = WorkContext->Parameters.Transaction; IF_SMB_DEBUG(MISC1) { SrvPrint1( "Set FS Information entered; transaction 0x%p\n", transaction ); }
status = SrvVerifyUidAndTid( WorkContext, &session, &treeConnect, ShareTypeDisk );
if( !NT_SUCCESS( status ) ) { goto out; }
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes. Set FS information has no
// response parameters.
//
request = (PREQ_SET_FS_INFORMATION)transaction->InParameters;
if ( (transaction->ParameterCount < sizeof(REQ_SET_FS_INFORMATION)) ) {
//
// Not enough parameter bytes were sent.
//
IF_SMB_DEBUG(ERRORS) { SrvPrint2( "SrvSmbSetFSInformation: bad parameter byte " "counts: %ld %ld\n", transaction->ParameterCount, transaction->MaxParameterCount ); }
status = STATUS_INVALID_SMB; SrvLogInvalidSmb( WorkContext ); goto out; }
//
// Confirm that the information level is legitimate.
//
informationLevel = SmbGetUshort( &request->InformationLevel );
if( informationLevel < SMB_INFO_PASSTHROUGH ) { status = STATUS_NOT_SUPPORTED; goto out; }
informationLevel -= SMB_INFO_PASSTHROUGH;
//
// Make sure the client is allowed to do this, if we have an Admin share
//
status = SrvIsAllowedOnAdminShare( WorkContext, WorkContext->TreeConnect->Share );
if( !NT_SUCCESS( status ) ) { goto out; }
//
// Verify the FID. If verified, the RFCB block is referenced
// and its addresses is stored in the WorkContext block, and the
// RFCB address is returned.
//
rfcb = SrvVerifyFid( WorkContext, SmbGetUshort( &request->Fid ), TRUE, NULL, // don't serialize with raw write
&status );
if ( rfcb == SRV_INVALID_RFCB_POINTER ) { IF_DEBUG(ERRORS) { SrvPrint2( "SrvSmbSetFsInformation: Status %X on FID: 0x%lx\n", status, SmbGetUshort( &request->Fid ) ); }
goto out; }
status = IoCheckQuerySetVolumeInformation( informationLevel, transaction->DataCount, TRUE );
if( NT_SUCCESS( status ) ) {
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
status = NtSetVolumeInformationFile( rfcb->Lfcb->FileHandle, &ioStatusBlock, transaction->InData, transaction->DataCount, informationLevel );
REVERT(); } }
out: if ( !NT_SUCCESS( status ) ) { SrvSetSmbError( WorkContext, status ); transactionStatus = SmbTransStatusErrorWithoutData; } else { transactionStatus = SmbTransStatusSuccess; }
transaction->SetupCount = 0; transaction->ParameterCount = 0; transaction->DataCount = 0;
SrvWmiEndContext(WorkContext); return transactionStatus;
} // SrvSmbSetFsInformation
SMB_PROCESSOR_RETURN_TYPE SrvSmbQueryInformationDisk ( SMB_PROCESSOR_PARAMETERS )
/*++
Routine Description:
This routine processes the Query Information Disk SMB.
Arguments:
SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description of the parameters to SMB processor routines.
Return Value:
SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h
--*/
{ PREQ_QUERY_INFORMATION_DISK request; PRESP_QUERY_INFORMATION_DISK response;
NTSTATUS status = STATUS_SUCCESS; SMB_STATUS SmbStatus = SmbStatusInProgress; IO_STATUS_BLOCK ioStatusBlock; FILE_FS_SIZE_INFORMATION fsSizeInfo;
PSESSION session; PTREE_CONNECT treeConnect;
USHORT totalUnits, freeUnits; ULONG sectorsPerUnit, bytesPerSector; LARGE_INTEGER result; BOOLEAN highpart; ULONG searchword; CCHAR highbit, extrabits;
BOOLEAN isDos;
PAGED_CODE( ); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_QUERY_INFORMATION_DISK; SrvWmiStartContext(WorkContext);
IF_SMB_DEBUG(MISC1) { SrvPrint2( "Query Information Disk request header at 0x%p, response header at 0x%p\n", WorkContext->RequestHeader, WorkContext->ResponseHeader ); SrvPrint2( "Query Information Disk request params at 0x%p, response params%p\n", WorkContext->RequestParameters, WorkContext->ResponseParameters ); }
request = (PREQ_QUERY_INFORMATION_DISK)WorkContext->RequestParameters; response = (PRESP_QUERY_INFORMATION_DISK)WorkContext->ResponseParameters;
//
// If a session block has not already been assigned to the current
// work context , verify the UID. If verified, the address of the
// session block corresponding to this user is stored in the WorkContext
// block and the session block is referenced.
//
// Find tree connect corresponding to given TID if a tree connect
// pointer has not already been put in the WorkContext block by an
// AndX command.
//
status = SrvVerifyUidAndTid( WorkContext, &session, &treeConnect, ShareTypeDisk );
if ( !NT_SUCCESS(status) ) { IF_DEBUG(SMB_ERRORS) { SrvPrint0( "SrvSmbQueryInformationDisk: Invalid UID or TID\n" ); } SrvSetSmbError( WorkContext, status ); SmbStatus = SmbStatusSendResponse; goto Cleanup; }
if( session->IsSessionExpired ) { status = SESSION_EXPIRED_STATUS_CODE; SrvSetSmbError( WorkContext, status ); SmbStatus = SmbStatusSendResponse; goto Cleanup; }
//
// Make sure the client is allowed to do this, if we have an Admin share
//
status = SrvIsAllowedOnAdminShare( WorkContext, treeConnect->Share ); if( !NT_SUCCESS( status ) ) { SrvSetSmbError( WorkContext, status ); SmbStatus = SmbStatusSendResponse; goto Cleanup; } //
// Get the Share root handle.
//
status = SrvGetShareRootHandle( treeConnect->Share );
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(ERRORS) { SrvPrint1( "SrvSmbQueryInformationDisk: SrvGetShareRootHandle failed %x.\n", status ); }
SrvSetSmbError( WorkContext, status ); SmbStatus = SmbStatusSendResponse; goto Cleanup; }
//
// *** The share handle is used to get the allocation information.
// This is a "storage channel," and as a result could allow
// people to get information to which they are not entitled.
// For a B2 security rating this may need to be changed.
//
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
HANDLE RootHandle;
status = SrvSnapGetRootHandle( WorkContext, &RootHandle ); if( NT_SUCCESS(status) ) { status = NtQueryVolumeInformationFile( RootHandle, &ioStatusBlock, &fsSizeInfo, sizeof(FILE_FS_SIZE_INFORMATION), FileFsSizeInformation );
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &RootHandle ); if( NT_SUCCESS(status) ) { status = NtQueryVolumeInformationFile( RootHandle, &ioStatusBlock, &fsSizeInfo, sizeof(FILE_FS_SIZE_INFORMATION), FileFsSizeInformation ); } } }
REVERT(); }
//
// Release the share root handle
//
SrvReleaseShareRootHandle( treeConnect->Share );
if ( !NT_SUCCESS(status) ) { INTERNAL_ERROR( ERROR_LEVEL_UNEXPECTED, "SrvSmbQueryInformationDisk: NtQueryVolumeInformationFile" "returned %X", status, NULL );
SrvLogServiceFailure( SRV_SVC_NT_SET_VOL_INFO_FILE, status );
SrvSetSmbError( WorkContext, status ); SmbStatus = SmbStatusSendResponse; goto Cleanup; }
//
// *** Problem.
//
// This SMB only return 16 bits of information for each field, but we
// may need to return large numbers. In particular TotalAllocationUnits
// is commonly > 64K.
//
// Fortunately, it turns out the all the client cares about is the total
// disk size, in bytes, and the free space, in bytes. So - if one number
// is too big adjust it and adjust the other numbers so that the totals
// come out the same.
//
// If after all adjustment, the number are still too high, return the
// largest possible value for TotalUnit or FreeUnits (i.e. 0xFFFF).
//
// A caveat here is that some DOS apps (like the command interpreter!)
// assume that the cluster size (bytes per sector times sectors per
// cluster) will fit in 16 bits, and will calculate bogus geometry if
// it doesn't. So the first thing we do is ensure that the real
// cluster size is less than 0x10000, if the client is a DOS client.
// This may make the TotalUnits or FreeUnits counts too big, so we'll
// have to round them down, but that's life.
//
// Since we use shifts to adjust the numbers it is possible to lose
// 1 bits when we shift a number to the right. We don't care, we're
// doing our best to fix a broken protocol. NT clients will use
// QueryFSAttribute and will get the correct answer.
//
//
// If this is a DOS client, make the cluster size < 0x10000.
//
isDos = IS_DOS_DIALECT( WorkContext->Connection->SmbDialect );
sectorsPerUnit = fsSizeInfo.SectorsPerAllocationUnit; bytesPerSector = fsSizeInfo.BytesPerSector;
if ( isDos ) { while ( (sectorsPerUnit * bytesPerSector) > 0xFFFF ) { if ( sectorsPerUnit >= 2 ) { sectorsPerUnit /= 2; } else { bytesPerSector /= 2; } fsSizeInfo.TotalAllocationUnits.QuadPart *= 2; fsSizeInfo.AvailableAllocationUnits.QuadPart *= 2; } }
//
// Calculate how much the total cluster count needs to be shifted in
// order to fit in a word.
//
if ( fsSizeInfo.TotalAllocationUnits.HighPart != 0 ) { highpart = TRUE; searchword = fsSizeInfo.TotalAllocationUnits.HighPart; } else { highpart = FALSE; searchword = fsSizeInfo.TotalAllocationUnits.LowPart; }
highbit = 0; while ( searchword != 0 ) { highbit++; searchword /= 2; }
if ( highpart ) { highbit += 32; } else { if ( highbit < 16) { highbit = 0; } else { highbit -= 16; } }
if ( highbit > 0 ) {
//
// Attempt to adjust the other values to absorb the excess bits.
// If this is a DOS client, don't let the cluster size get
// bigger than 0xFFFF.
//
extrabits = highbit;
if ( isDos ) {
while ( (highbit > 0) && ((sectorsPerUnit*bytesPerSector) < 0x8000) ) { sectorsPerUnit *= 2; highbit--; }
} else {
while ( (highbit > 0) && (sectorsPerUnit < 0x8000) ) { sectorsPerUnit *= 2; highbit--; }
while ( (highbit > 0) && (bytesPerSector < 0x8000) ) { bytesPerSector *= 2; highbit--; }
}
//
// Adjust the total and free unit counts.
//
if ( highbit > 0 ) {
//
// There is no way to get the information to fit. Use the
// maximum possible value.
//
totalUnits = 0xFFFF;
} else {
result.QuadPart = fsSizeInfo.TotalAllocationUnits.QuadPart >> extrabits;
ASSERT( result.HighPart == 0 ); ASSERT( result.LowPart < 0x10000 );
totalUnits = (USHORT)result.LowPart;
}
result.QuadPart = fsSizeInfo.AvailableAllocationUnits.QuadPart >> (CCHAR)(extrabits - highbit);
if ( result.HighPart != 0 || result.LowPart > 0xFFFF ) { freeUnits = 0xFFFF; } else { freeUnits = (USHORT)result.LowPart; }
} else {
totalUnits = (USHORT)fsSizeInfo.TotalAllocationUnits.LowPart; freeUnits = (USHORT)fsSizeInfo.AvailableAllocationUnits.LowPart;
}
//
// Build the response SMB.
//
response->WordCount = 5;
SmbPutUshort( &response->TotalUnits, totalUnits ); SmbPutUshort( &response->BlocksPerUnit, (USHORT)sectorsPerUnit ); SmbPutUshort( &response->BlockSize, (USHORT)bytesPerSector ); SmbPutUshort( &response->FreeUnits, freeUnits );
SmbPutUshort( &response->Reserved, 0 ); SmbPutUshort( &response->ByteCount, 0 );
WorkContext->ResponseParameters = NEXT_LOCATION( response, RESP_QUERY_INFORMATION_DISK, 0 ); SmbStatus = SmbStatusSendResponse; IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbQueryInformationDisk complete.\n" );
Cleanup: SrvWmiEndContext(WorkContext); return SmbStatus;
} // SrvSmbQueryInformationDisk
SMB_PROCESSOR_RETURN_TYPE SrvSmbNtCancel ( SMB_PROCESSOR_PARAMETERS )
/*++
Routine Description:
Processes an Nt Cancel SMB.
Arguments:
SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description of the parameters to SMB processor routines.
Return Value:
SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
--*/
{ NTSTATUS status = STATUS_SUCCESS; SMB_STATUS SmbStatus = SmbStatusInProgress; PSESSION session; PTREE_CONNECT treeConnect; PCONNECTION connection; USHORT targetUid, targetPid, targetTid, targetMid; PLIST_ENTRY listHead; PLIST_ENTRY listEntry; PWORK_CONTEXT workContext; PSMB_HEADER header; BOOLEAN match; KIRQL oldIrql;
PREQ_NT_CANCEL request; if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_NT_CANCEL; SrvWmiStartContext(WorkContext);
request = (PREQ_NT_CANCEL)WorkContext->RequestParameters;
//
// The word count has already been checked. Now make sure that
// the byte count is zero.
//
if ( SmbGetUshort( &request->ByteCount) != 0 ) { SrvSetSmbError( WorkContext, STATUS_INVALID_SMB ); status = STATUS_INVALID_SMB; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
//
// If a session block has not already been assigned to the current
// work context , verify the UID. If verified, the address of the
// session block corresponding to this user is stored in the WorkContext
// block and the session block is referenced.
//
// Find tree connect corresponding to given TID if a tree connect
// pointer has not already been put in the WorkContext block by an
// AndX command.
//
status = SrvVerifyUidAndTid( WorkContext, &session, &treeConnect, ShareTypeWild );
if ( !NT_SUCCESS(status) ) { IF_DEBUG(SMB_ERRORS) { SrvPrint0( "SrvSmbNtCancel: Invalid UID or TID\n" ); } SrvSetSmbError( WorkContext, status ); SmbStatus = SmbStatusSendResponse; goto Cleanup; }
//
// Check the work in-progress list to see if this work item is
// cancellable.
//
targetUid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ); targetPid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid ); targetTid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid ); targetMid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Mid );
match = FALSE;
connection = WorkContext->Connection;
ACQUIRE_SPIN_LOCK( connection->EndpointSpinLock, &oldIrql );
listHead = &connection->InProgressWorkItemList; listEntry = listHead; while ( listEntry->Flink != listHead ) {
listEntry = listEntry->Flink;
workContext = CONTAINING_RECORD( listEntry, WORK_CONTEXT, InProgressListEntry );
header = workContext->RequestHeader;
//
// Some workitems in the inprogressworkitemlist are added
// during a receive indication and the requestheader field
// has not been set yet. We can probably set it at that time
// but this seems to be the safest fix.
//
// We have to check whether the workitem ref count is zero or
// not since we dereference it before removing it from the
// InProgressWorkItemList queue. This prevents the workitem
// from being cleaned up twice.
//
// We also need to check the processing count of the workitem.
// Work items being used for actual smb requests will have
// a processing count of at least 1. This will prevent us
// from touching oplock breaks and pending tdi receives.
//
ACQUIRE_DPC_SPIN_LOCK( &workContext->SpinLock ); if ( (workContext->BlockHeader.ReferenceCount != 0) && (workContext->ProcessingCount != 0) && header != NULL && header->Command != SMB_COM_NT_CANCEL && SmbGetAlignedUshort( &header->Mid ) == targetMid && SmbGetAlignedUshort( &header->Pid ) == targetPid && SmbGetAlignedUshort( &header->Tid ) == targetTid && SmbGetAlignedUshort( &header->Uid ) == targetUid ) {
match = TRUE; break; } RELEASE_DPC_SPIN_LOCK( &workContext->SpinLock );
}
if ( match ) {
//
// Reference the work item, so that it cannot get used to process
// a new SMB while we are trying to cancel the old one.
//
SrvReferenceWorkItem( workContext ); RELEASE_DPC_SPIN_LOCK( &workContext->SpinLock ); RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
(VOID)IoCancelIrp( workContext->Irp ); SrvDereferenceWorkItem( workContext );
} else {
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
}
//
// Done. Do not send a response
//
SmbStatus = SmbStatusNoResponse;
Cleanup: SrvWmiEndContext(WorkContext); return SmbStatus;
} // SrvSmbNtCancel
SMB_TRANS_STATUS SrvSmbSetSecurityDescriptor ( IN OUT PWORK_CONTEXT WorkContext )
/*++
Routine Description:
Processes the Set Security Descriptor request. This request arrives in a Transaction2 SMB.
Arguments:
WorkContext - Supplies the address of a Work Context Block describing the current request. See smbtypes.h for a more complete description of the valid fields.
Return Value:
SMB_TRANS_STATUS - Indicates whether an error occurred, and, if so, whether data should be returned to the client. See smbtypes.h for a more complete description.
--*/
{ PREQ_SET_SECURITY_DESCRIPTOR request;
NTSTATUS status; PTRANSACTION transaction; PRFCB rfcb; SECURITY_INFORMATION securityInformation;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction; IF_SMB_DEBUG(QUERY_SET1) { SrvPrint1( "Set Security Descriptor entered; transaction 0x%p\n", transaction ); }
request = (PREQ_SET_SECURITY_DESCRIPTOR)transaction->InParameters;
//
// Verify that enough setup bytes were sent.
//
if ( transaction->ParameterCount < sizeof(REQ_SET_SECURITY_DESCRIPTOR ) ) {
//
// Not enough parameter bytes were sent.
//
IF_DEBUG(SMB_ERRORS) { SrvPrint1( "SrvSmbSetSecurityInformation: bad setup byte count: " "%ld\n", transaction->ParameterCount ); }
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB ); return SmbTransStatusErrorWithoutData; }
//
// Verify the FID. If verified, the RFCB block is referenced
// and its addresses is stored in the WorkContext block, and the
// RFCB address is returned.
//
rfcb = SrvVerifyFid( WorkContext, SmbGetUshort( &request->Fid ), TRUE, NULL, // don't serialize with raw write
&status );
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
//
// Invalid file ID or write behind error. Reject the request.
//
IF_DEBUG(ERRORS) { SrvPrint2( "SrvSmbSetFileInformation: Status %X on FID: 0x%lx\n", status, SmbGetUshort( &request->Fid ) ); }
SrvSetSmbError( WorkContext, status ); return SmbTransStatusErrorWithoutData;
}
//
// First we'll validate that the security descriptor isn't bogus.
// This needs to be done here because NtSetSecurityObject has no
// idea what the buffer size is.
//
if( !RtlValidRelativeSecurityDescriptor( transaction->InData, transaction->DataCount, 0 )) { //
// We were passed a bogus security descriptor to set. Bounce the
// request as an invalid SMB.
//
SrvSetSmbError( WorkContext, STATUS_INVALID_SECURITY_DESCR ); return SmbTransStatusErrorWithoutData; }
securityInformation = SmbGetUlong( &request->SecurityInformation );
//
// Make sure the caller is allowed to set security information on this object
//
status = IoCheckFunctionAccess( rfcb->GrantedAccess, IRP_MJ_SET_SECURITY, 0, 0, &securityInformation, NULL );
if( NT_SUCCESS( status ) ) { //
// Attempt to set the security descriptor. We need to be in the
// the user context to do this, in case the security information
// specifies change ownership.
//
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) { status = NtSetSecurityObject( rfcb->Lfcb->FileHandle, securityInformation, transaction->InData );
REVERT(); } }
//
// If an error occurred, return an appropriate response.
//
if ( !NT_SUCCESS(status) ) {
SrvSetSmbError( WorkContext, status ); return SmbTransStatusErrorWithoutData; }
//
// We probably shouldn't cache this file descriptor on close, since
// the security setting changed.
//
rfcb->IsCacheable = FALSE;
transaction->ParameterCount = 0; transaction->DataCount = 0;
return SmbTransStatusSuccess;
} // SrvSmbSetSecurityDescriptor
SMB_TRANS_STATUS SrvSmbQuerySecurityDescriptor ( IN OUT PWORK_CONTEXT WorkContext )
/*++
Routine Description:
Processes the Query Security Descriptor request. This request arrives in a Transaction2 SMB.
Arguments:
WorkContext - Supplies the address of a Work Context Block describing the current request. See smbtypes.h for a more complete description of the valid fields.
Return Value:
SMB_TRANS_STATUS - Indicates whether an error occurred, and, if so, whether data should be returned to the client. See smbtypes.h for a more complete description.
--*/
{ PREQ_QUERY_SECURITY_DESCRIPTOR request; PRESP_QUERY_SECURITY_DESCRIPTOR response;
NTSTATUS status; PTRANSACTION transaction; PRFCB rfcb; ULONG lengthNeeded; SECURITY_INFORMATION securityInformation;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction; IF_SMB_DEBUG(QUERY_SET1) { SrvPrint1( "Query Security Descriptor entered; transaction 0x%p\n", transaction ); }
request = (PREQ_QUERY_SECURITY_DESCRIPTOR)transaction->InParameters; response = (PRESP_QUERY_SECURITY_DESCRIPTOR)transaction->OutParameters;
//
// Verify that enough setup bytes were sent.
//
if ( transaction->ParameterCount < sizeof(REQ_QUERY_SECURITY_DESCRIPTOR ) || transaction->MaxParameterCount < sizeof( RESP_QUERY_SECURITY_DESCRIPTOR ) ) {
//
// Not enough parameter bytes were sent.
//
IF_DEBUG(SMB_ERRORS) { SrvPrint2( "SrvSmbQuerySecurityInformation: bad parameter byte or " "return parameter count: %ld %ld\n", transaction->ParameterCount, transaction->MaxParameterCount ); }
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB ); return SmbTransStatusErrorWithoutData; }
//
// Verify the FID. If verified, the RFCB block is referenced
// and its addresses is stored in the WorkContext block, and the
// RFCB address is returned.
//
rfcb = SrvVerifyFid( WorkContext, SmbGetUshort( &request->Fid ), TRUE, NULL, // don't serialize with raw write
&status );
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
//
// Invalid file ID or write behind error. Reject the request.
//
IF_DEBUG(ERRORS) { SrvPrint2( "SrvSmbSetFileInformation: Status %X on FID: 0x%lx\n", status, SmbGetUshort( &request->Fid ) ); }
SrvSetSmbError( WorkContext, status ); return SmbTransStatusErrorWithoutData;
}
securityInformation = SmbGetUlong( &request->SecurityInformation ),
//
// Make sure the caller is allowed to query security information on this object
//
status = IoCheckFunctionAccess( rfcb->GrantedAccess, IRP_MJ_QUERY_SECURITY, 0, 0, &securityInformation, NULL );
if( !NT_SUCCESS( status ) ) { SrvSetSmbError( WorkContext, status ); return SmbTransStatusErrorWithoutData; }
//
// Attempt to query the security descriptor
//
status = NtQuerySecurityObject( rfcb->Lfcb->FileHandle, securityInformation, transaction->OutData, transaction->MaxDataCount, &lengthNeeded );
SmbPutUlong( &response->LengthNeeded, lengthNeeded ); transaction->ParameterCount = sizeof( RESP_QUERY_SECURITY_DESCRIPTOR );
//
// If an error occurred, return an appropriate response.
//
if ( !NT_SUCCESS(status) ) {
transaction->DataCount = 0; SrvSetSmbError2( WorkContext, status, TRUE ); return SmbTransStatusErrorWithData; } else { transaction->DataCount = RtlLengthSecurityDescriptor( transaction->OutData ); }
return SmbTransStatusSuccess;
} // SrvSmbQuerySecurityDescriptor
SMB_TRANS_STATUS SrvSmbQueryQuota ( IN OUT PWORK_CONTEXT WorkContext ) /*++
Routine Description:
Processes an NtQueryQuotaInformationFile request. This request arrives in an Nt Transaction SMB.
--*/ { PREQ_NT_QUERY_FS_QUOTA_INFO request; PRESP_NT_QUERY_FS_QUOTA_INFO response;
NTSTATUS status; PTRANSACTION transaction;
PRFCB rfcb; PVOID sidList; ULONG sidListLength,startSidLength,startSidOffset; PVOID sidListBuffer = NULL; PULONG startSid = NULL; ULONG errorOffset;
IO_STATUS_BLOCK iosb;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction;
request = (PREQ_NT_QUERY_FS_QUOTA_INFO)transaction->InParameters; response = (PRESP_NT_QUERY_FS_QUOTA_INFO)transaction->OutParameters;
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes.
//
if ( transaction->ParameterCount < sizeof( *request ) || transaction->MaxParameterCount < sizeof( *response ) ) {
//
// Not enough parameter bytes were sent.
//
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB ); return SmbTransStatusErrorWithoutData; }
//
// Verify the FID. If verified, the RFCB block is referenced
// and its addresses is stored in the WorkContext block, and the
// RFCB address is returned.
//
rfcb = SrvVerifyFid( WorkContext, SmbGetUshort( &request->Fid ), TRUE, NULL, // don't serialize with raw write
&status );
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
//
// Invalid file ID or write behind error. Reject the request.
//
SrvSetSmbError( WorkContext, status ); return SmbTransStatusErrorWithoutData;
}
sidListLength = SmbGetUlong( &request->SidListLength ); startSidLength = SmbGetUlong( &request->StartSidLength ); startSidOffset = SmbGetUlong( &request->StartSidOffset );
//
// If a Sid List is supplied, make sure it is OK
//
if( sidListLength != 0 ) { //
// Length OK?
//
if( sidListLength > transaction->DataCount ) { SrvSetSmbError( WorkContext, STATUS_INVALID_SID ); return SmbTransStatusErrorWithoutData; }
sidListBuffer = transaction->InData;
//
// Alignment OK?
//
if( (ULONG_PTR)sidListBuffer & (sizeof(ULONG)-1) ) { SrvSetSmbError( WorkContext, STATUS_INVALID_SID ); return SmbTransStatusErrorWithoutData; } //
// Content OK?
//
#if XXX
status = IopCheckGetQuotaBufferValidity( sidListBuffer, sidListLength, errorOffset ); if( !NT_SUCCESS( status ) ) { SrvSetSmbError( WorkContext, status ); return SmbTransStatusErrorWithoutData; } #endif
}
// The way the transaction buffers are setup the same buffer pointer is used
// for the incoming data and the outgoing data. This will not work for
// NtQueryQuotaInformationFile since the underlying driver zeroes the
// output buffer before processing the input buffer. This presents us with
// two options ... (1) we can adjust the copying to be staggerred assuming
// that we can contain both the buffers into the transaction buffer or (2)
// allocate anew buffer before calling the QueryQuotaInformationFile.
// The second approach has been implemented since it is well contained.
// If this turns out to be a performance problem we will revert back to the
// first option.
if (sidListLength + startSidLength > 0 && startSidOffset <= transaction->DataCount && startSidLength <= transaction->DataCount && startSidOffset >= sidListLength && startSidOffset + startSidLength <= transaction->DataCount ) {
sidListBuffer = ALLOCATE_HEAP( MAX(startSidOffset + startSidLength, sidListLength), BlockTypeMisc );
if (sidListBuffer != NULL) {
RtlCopyMemory( sidListBuffer, transaction->InData, sidListLength);
if (startSidLength != 0) { startSid = (PULONG)((PBYTE)sidListBuffer + startSidOffset);
RtlCopyMemory( startSid, ((PBYTE)transaction->InData + startSidOffset), startSidLength);
} } } else { sidListBuffer = NULL; }
iosb.Information = 0;
//
// Go ahead and query the quota information!
//
status = NtQueryQuotaInformationFile( rfcb->Lfcb->FileHandle, &iosb, transaction->OutData, transaction->MaxDataCount, request->ReturnSingleEntry, sidListBuffer, sidListLength, startSid, request->RestartScan );
if (sidListBuffer != NULL) { FREE_HEAP(sidListBuffer); }
//
// Paranoia
//
if( iosb.Information > transaction->MaxDataCount ) { iosb.Information = transaction->MaxDataCount; }
transaction->SetupCount = 0;
SmbPutUlong( &response->Length, (ULONG)iosb.Information ); transaction->ParameterCount = sizeof( *response ); transaction->DataCount = (ULONG)iosb.Information;
if( !NT_SUCCESS( status ) ) { SrvSetSmbError2( WorkContext, status, TRUE ); return SmbTransStatusErrorWithData; }
return SmbTransStatusSuccess;
} // SrvSmbQueryQuota
SMB_TRANS_STATUS SrvSmbSetQuota ( IN OUT PWORK_CONTEXT WorkContext ) /*++
Routine Description:
Processes an NtSetQuotaInformationFile request. This request arrives in an Nt Transaction SMB.
--*/ { PREQ_NT_SET_FS_QUOTA_INFO request;
NTSTATUS status; PTRANSACTION transaction;
PRFCB rfcb; PVOID buffer,pQuotaInfo=NULL; ULONG errorOffset;
IO_STATUS_BLOCK iosb;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction;
request = (PREQ_NT_SET_FS_QUOTA_INFO)transaction->InParameters;
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes.
//
if ( transaction->ParameterCount < sizeof( *request ) ) { //
// Not enough parameter bytes were sent.
//
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB ); return SmbTransStatusErrorWithoutData; }
//
// Verify the FID. If verified, the RFCB block is referenced
// and its addresses is stored in the WorkContext block, and the
// RFCB address is returned.
//
rfcb = SrvVerifyFid( WorkContext, SmbGetUshort( &request->Fid ), TRUE, NULL, // don't serialize with raw write
&status );
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
//
// Invalid file ID or write behind error. Reject the request.
//
SrvSetSmbError( WorkContext, status ); return SmbTransStatusErrorWithoutData;
}
//
// We do not need to check the buffer for validity, because
// IopSetEaOrQuotaInformationFile does this even for kernel mode callers!
//
iosb.Information = 0;
// we have to do allocation here in order to get a QUAD_WORD
// aligned pointer. This is so because this is a requirement on
// alpha for the quota buffer
pQuotaInfo = ALLOCATE_HEAP_COLD( transaction->DataCount, BlockTypeDataBuffer );
if (pQuotaInfo) { RtlCopyMemory( pQuotaInfo, transaction->InData, transaction->DataCount );
//
// Go ahead and set the quota information!
//
status = NtSetQuotaInformationFile( rfcb->Lfcb->FileHandle, &iosb, pQuotaInfo, transaction->DataCount );
if( !NT_SUCCESS( status ) ) { SrvSetSmbError( WorkContext, status ); }
//
// Nothing to return to the client except the status
//
transaction->SetupCount = 0; transaction->ParameterCount = 0; transaction->DataCount = 0;
FREE_HEAP(pQuotaInfo); } else { SrvSetSmbError( WorkContext, STATUS_INSUFFICIENT_RESOURCES ); } return SmbTransStatusSuccess; }
|