NT4/private/ntos/srv/smbmisc.c
2020-09-30 17:12:29 +02:00

1717 lines
47 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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"
#pragma hdrstop
#define BugCheckFileId SRV_FILE_SMBMISC
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 )
#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;
PAGED_CODE( );
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 ) {
return SmbStatusNoResponse;
}
//
// 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 =
(USHORT)(SmbGetUshort( &request->EchoCount ) - 1);
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.
//
return SmbStatusInProgress;
} // 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_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 %lx, endpoint %lx\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" );
return;
}
//
// 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" );
return;
}
--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" );
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;
IO_STATUS_BLOCK ioStatusBlock;
PTRANSACTION transaction;
USHORT informationLevel;
USHORT trans2code;
HANDLE fileHandle;
BOOLEAN isRootDirectoryHandle;
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( );
isUnicode = SMB_IS_UNICODE( WorkContext );
transaction = WorkContext->Parameters.Transaction;
IF_SMB_DEBUG(MISC1) {
SrvPrint1( "Query FS Information entered; transaction 0x%lx\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 );
return SmbTransStatusErrorWithoutData;
}
//
// 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 );
return SmbTransStatusErrorWithoutData;
}
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 );
return SmbTransStatusErrorWithoutData;
}
fileHandle = WorkContext->TreeConnect->Share->RootDirectoryHandle;
isRootDirectoryHandle = TRUE;
IF_SMB_DEBUG(MISC1) {
SrvPrint0("SrvSmbQueryFSInformation: Using share root handle\n");
}
switch ( informationLevel ) {
case SMB_INFO_ALLOCATION:
//
// Return information about the disk.
//
fsAllocate = (PFSALLOCATE)transaction->OutData;
if ( transaction->MaxDataCount < sizeof(FSALLOCATE) ) {
SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW );
return SmbTransStatusErrorWithoutData;
}
//
// *** 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 = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
&fsSizeInfo,
sizeof(FILE_FS_SIZE_INFORMATION),
FileFsSizeInformation
);
//
// Release the share root handle if device is removable
//
if ( isRootDirectoryHandle ) {
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 );
return SmbTransStatusErrorWithoutData;
}
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( fsVolumeInfoLength, BlockTypeDataBuffer );
if ( fsVolumeInfo == NULL ) {
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
return SmbTransStatusErrorWithoutData;
}
//
// Get the label information.
//
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
fsVolumeInfo,
fsVolumeInfoLength,
FileFsVolumeInformation
);
//
// Release the share root handle if device is removable
//
if ( isRootDirectoryHandle ) {
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 );
return SmbTransStatusErrorWithoutData;
}
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) +
lengthVolumeLabel / (isUnicode ? 1 : sizeof(WCHAR)) ) ) {
FREE_HEAP( fsVolumeInfo );
SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW );
return SmbTransStatusErrorWithoutData;
}
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_SIZE_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 = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
transaction->OutData,
transaction->MaxDataCount,
MAP_SMB_INFO_TYPE_TO_NT(
QueryVolumeInformation,
informationLevel
)
);
//
// Release the share root handle if device is removable
//
if ( isRootDirectoryHandle ) {
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
}
if ( NT_SUCCESS( status ) ) {
transaction->DataCount = ioStatusBlock.Information;
} else {
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
break;
default:
//
// An invalid information level was passed.
//
SrvSetSmbError( WorkContext, STATUS_OS2_INVALID_LEVEL );
return SmbTransStatusErrorWithoutData;
}
transaction->SetupCount = 0;
transaction->ParameterCount = 0;
return SmbTransStatusSuccess;
} // 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. Set FS Information corresponds to the
OS/2 DosSetFSInfo 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.
--*/
{
PAGED_CODE( );
//
// It seems that the 2.0 redirector never sends this SMB, and it is
// difficult to do this without special support from the file
// system--what volume name should be opened? It is not like Query,
// where we can use the share handle to do the
// NtQueryVolumeInformationFile--Set must have an actual volume
// handle.
//
// The #if'ed out code would work if a good volume handle were
// used.
//
SrvSetSmbError( WorkContext, STATUS_SMB_NO_SUPPORT );
return SmbTransStatusErrorWithoutData;
#if 0
PREQ_SET_FS_INFORMATION request;
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
PTRANSACTION transaction;
USHORT informationLevel;
OEM_STRING oemLabelString;
UNICODE_STRING labelString;
PVOLUMELABEL volumeLabel;
PFILE_FS_LABEL_INFORMATION fsLabelInfo;
ULONG fsLabelInfoLength;
transaction = WorkContext->Parameters.Transaction;
IF_SMB_DEBUG(MISC1) {
SrvPrint1( "Set FS Information entered; transaction 0x%lx\n",
transaction );
}
//
// 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(SMB_ERRORS) {
SrvPrint2( "SrvSmbSetFSInformation: bad parameter byte "
"counts: %ld %ld\n",
transaction->ParameterCount,
transaction->MaxParameterCount );
}
SrvLogInvalidSmb( WorkContext );
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
return SmbTransStatusErrorWithoutData;
}
//
// Confirm that the information level is legitimate.
//
informationLevel = SmbGetUshort( &request->InformationLevel );
if ( informationLevel != SMB_INFO_VOLUME ) {
SrvSetSmbError( WorkContext, STATUS_OS2_INVALID_LEVEL );
return SmbTransStatusErrorWithoutData;
}
//
// 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 ) ) {
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
//
// Make sure that the client passed enough data bytes to account for
// the entire length of the volume label. The +2 accounts for the
// zero terminator and the cch field of the VOLUMELABEL structure.
//
volumeLabel = (PVOLUMELABEL)transaction->InData;
if ( transaction->DataCount < (CLONG)(volumeLabel->cch+2) ) {
IF_DEBUG(SMB_ERRORS) {
SrvPrint1( "SrvSmbSetFSInformation: cch count too large: %ld\n",
volumeLabel->cch );
}
SrvLogInvalidSmb( WorkContext );
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
return SmbTransStatusErrorWithoutData;
}
oemString.Length = volumeLabel->cch;
oemString.Buffer = volumeLabel->szVolumeLabel;
unicodeString.MaximumLength =
(USHORT)RtlOemStringToUnicodeSize( &oemString );
fsLabelInfoLength = FIELD_OFFSET( FILE_FS_LABEL_INFORMATION, VolumeLabel ) +
unicodeString.MaximumLength;
fsLabelInfo = ALLOCATE_HEAP( fsLabelInfoLength, BlockTypeDataBuffer );
if ( fsLabelInfo == NULL ) {
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
return SmbTransStatusErrorWithoutData;
}
unicodeString.Buffer = fsLabelInfo.VolumeLabel;
RtlOemStringToUnicodeString( &unicodeString, &oemString, FALSE );
fsLabelInfo->VolumeLabelLength = unicodeString.Length;
//
// Get the Share root handle.
//
status = SrvGetShareRootHandle( WorkContext->TreeConnect->Share );
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(ERRORS) {
SrvPrint1( "SrvSmbSetFsInformation: SrvGetShareRootHandle failed %x.\n",
status );
}
FREE_HEAP( fsLabelInfo );
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
//
// Set the label on the volume.
//
// !!! Need to convert the input string to Unicode
status = NtSetVolumeInformationFile(
WorkContext->TreeConnect->Share->RootDirectoryHandle,
&ioStatusBlock,
fsLabelInfo,
fsLabelInfoLength,
FileFsLabelInformation
);
FREE_HEAP( fsLabelInfo );
//
// Release the share root handle if device is removable
//
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( !NT_SUCCESS(status) ) {
INTERNAL_ERROR(
ERROR_LEVEL_UNEXPECTED,
"SrvSmbSetFSInformation: NtSetVolumeInformationFile returned %X",
status,
NULL
);
SrvLogServiceFailure( SRV_SVC_NT_SET_VOL_INFO_FILE, status );
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
transaction->SetupCount = 0;
transaction->ParameterCount = 0;
transaction->DataCount = 0;
return SmbTransStatusSuccess;
#endif
} // 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;
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_SMB_DEBUG(MISC1) {
SrvPrint2( "Query Information Disk request header at 0x%lx, "
"response header at 0x%lx\n",
WorkContext->RequestHeader,
WorkContext->ResponseHeader );
SrvPrint2( "Query Information Disk request params at 0x%lx, "
"response params%lx\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 );
return SmbStatusSendResponse;
}
//
// 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 );
return SmbStatusSendResponse;
}
//
// 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 );
return SmbStatusSendResponse;
}
//
// *** 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 = NtQueryVolumeInformationFile(
treeConnect->Share->RootDirectoryHandle,
&ioStatusBlock,
&fsSizeInfo,
sizeof(FILE_FS_SIZE_INFORMATION),
FileFsSizeInformation
);
//
// Release the share root handle if device is removable
//
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 );
return SmbStatusSendResponse;
}
//
// *** 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
);
IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbQueryInformationDisk complete.\n" );
return SmbStatusSendResponse;
} // 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;
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;
PRESP_NT_CANCEL response;
request = (PREQ_NT_CANCEL)WorkContext->RequestParameters;
response = (PRESP_NT_CANCEL)WorkContext->ResponseParameters;
//
// 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 );
return SmbStatusSendResponse;
}
//
// 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 );
return SmbStatusSendResponse;
}
//
// 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
//
return SmbStatusNoResponse;
} // 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;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction;
IF_SMB_DEBUG(QUERY_SET1) {
SrvPrint1( "Set Security Descriptor entered; transaction 0x%lx\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;
}
//
// 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.
//
IMPERSONATE( WorkContext );
status = NtSetSecurityObject(
rfcb->Lfcb->FileHandle,
SmbGetUlong( &request->SecurityInformation ),
transaction->InData
);
REVERT();
//
// If an error occurred, return an appropriate response.
//
if ( !NT_SUCCESS(status) ) {
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
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;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction;
IF_SMB_DEBUG(QUERY_SET1) {
SrvPrint1( "Set Security Descriptor entered; transaction 0x%lx\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;
}
//
// Attempt to query the security descriptor
//
status = NtQuerySecurityObject(
rfcb->Lfcb->FileHandle,
SmbGetUlong( &request->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