You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2070 lines
62 KiB
2070 lines
62 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
smbtree.c
|
|
|
|
Abstract:
|
|
|
|
This module contains routines for dealing with tree connects and
|
|
disconnects:
|
|
|
|
Tree Connect
|
|
Tree Connect And X
|
|
Tree Disconnect
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 15-Nov-1989
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "smbtree.tmh"
|
|
#pragma hdrstop
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, SrvSmbTreeConnect )
|
|
#pragma alloc_text( PAGE, SrvSmbTreeConnectAndX )
|
|
#pragma alloc_text( PAGE, SrvSmbTreeDisconnect )
|
|
#endif
|
|
|
|
|
|
SMB_PROCESSOR_RETURN_TYPE
|
|
SrvSmbTreeConnect (
|
|
SMB_PROCESSOR_PARAMETERS
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes a tree connect 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
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PREQ_TREE_CONNECT request;
|
|
PRESP_TREE_CONNECT response;
|
|
|
|
PSESSION session;
|
|
PSECURITY_CONTEXT SecurityContext;
|
|
PCONNECTION connection;
|
|
PPAGED_CONNECTION pagedConnection;
|
|
PTABLE_HEADER tableHeader;
|
|
PTABLE_ENTRY entry;
|
|
SHORT tidIndex;
|
|
PSHARE share;
|
|
PTREE_CONNECT treeConnect;
|
|
PSZ password, service;
|
|
USHORT len;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
NTSTATUS TableStatus;
|
|
SMB_STATUS SmbStatus = SmbStatusInProgress;
|
|
BOOLEAN didLogon = FALSE;
|
|
SHORT uidIndex;
|
|
SMB_DIALECT smbDialect;
|
|
PUNICODE_STRING clientMachineNameString;
|
|
ACCESS_MASK desiredAccess;
|
|
ACCESS_MASK grantedAccess;
|
|
SECURITY_SUBJECT_CONTEXT subjectContext;
|
|
UNICODE_STRING domain = { 0, 0, StrNull };
|
|
|
|
PAGED_CODE( );
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_TREE_CONNECT;
|
|
SrvWmiStartContext(WorkContext);
|
|
|
|
IF_SMB_DEBUG(TREE1) {
|
|
KdPrint(( "Tree connect request header at 0x%p, response header at 0x%p\n",
|
|
WorkContext->RequestHeader, WorkContext->ResponseHeader ));
|
|
KdPrint(( "Tree connect request parameters at 0x%p, response parameters at 0x%p\n",
|
|
WorkContext->RequestParameters,
|
|
WorkContext->ResponseParameters ));
|
|
}
|
|
|
|
//
|
|
// Set up parameters.
|
|
//
|
|
|
|
request = (PREQ_TREE_CONNECT)(WorkContext->RequestParameters);
|
|
response = (PRESP_TREE_CONNECT)(WorkContext->ResponseParameters);
|
|
|
|
connection = WorkContext->Connection;
|
|
pagedConnection = connection->PagedConnection;
|
|
smbDialect = connection->SmbDialect;
|
|
|
|
// If we are requiring extended security signatures, than we can't let this through
|
|
if( SrvRequireExtendedSignatures )
|
|
{
|
|
SrvSetSmbError( WorkContext, STATUS_LOGIN_WKSTA_RESTRICTION );
|
|
status = STATUS_LOGIN_WKSTA_RESTRICTION;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If this client has not yet done a session setup and this his first
|
|
// tree connection then we must first do a logon. (i.e. SessionSetup)
|
|
//
|
|
|
|
len = SrvGetStringLength(
|
|
(PSZ)request->Buffer,
|
|
END_OF_REQUEST_SMB( WorkContext ),
|
|
FALSE, // not unicode
|
|
FALSE // do not include null terminator
|
|
);
|
|
if( len == (USHORT)-1 ) {
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
status = STATUS_INVALID_SMB;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
password = (PSZ)request->Buffer + 2 + len;
|
|
|
|
len = SrvGetStringLength(
|
|
password,
|
|
END_OF_REQUEST_SMB( WorkContext ),
|
|
FALSE, // not unicode
|
|
FALSE // do not include null terminator
|
|
);
|
|
|
|
if( len == (USHORT)-1 ) {
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
status = STATUS_INVALID_SMB;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
service = password + (len + 1) + 1;
|
|
|
|
//
|
|
// Allocate a tree connect block. We do this early on the
|
|
// assumption that the request will usually succeed. This also
|
|
// reduces the amount of time that we hold the lock.
|
|
//
|
|
|
|
SrvAllocateTreeConnect( &treeConnect, NULL );
|
|
|
|
if ( treeConnect == NULL ) {
|
|
|
|
//
|
|
// Unable to allocate tree connect. Return an error to the
|
|
// client.
|
|
//
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT( SrvSessionList.Lock == &SrvOrderedListLock );
|
|
|
|
ACQUIRE_LOCK( &connection->Lock );
|
|
|
|
if ( connection->CurrentNumberOfSessions != 0 ) {
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
session = SrvVerifyUid (
|
|
WorkContext,
|
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )
|
|
);
|
|
|
|
if ( session == NULL ) {
|
|
|
|
//
|
|
// This should only happen if the client has already
|
|
// established a session, as in tree connecting with a bad
|
|
// UID.
|
|
//
|
|
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID );
|
|
status = STATUS_SMB_BAD_UID;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
else if( session->IsSessionExpired )
|
|
{
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
status = SESSION_EXPIRED_STATUS_CODE;
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
} else if ( (smbDialect <= SmbDialectLanMan10) ||
|
|
(smbDialect == SmbDialectIllegal) ) {
|
|
|
|
//
|
|
// An LM 1.0 or newer client has tried to do a tree connect
|
|
// without first doing session setup. We call this a protocol
|
|
// violation.
|
|
//
|
|
// Also catch clients that are trying to connect without
|
|
// negotiating a valid protocol.
|
|
//
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
|
|
if ( smbDialect == SmbDialectIllegal ) {
|
|
|
|
KdPrint(("SrvSmbTreeConnect: Client %z is using an illegal "
|
|
"dialect.\n", (PCSTRING)&connection->OemClientMachineNameString ));;
|
|
|
|
} else {
|
|
|
|
KdPrint(( "Client speaking dialect %ld sent tree connect without session setup.\n", connection->SmbDialect ));
|
|
}
|
|
}
|
|
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
status = STATUS_INVALID_SMB;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
} else {
|
|
|
|
UNICODE_STRING machineName;
|
|
PENDPOINT endpoint;
|
|
BOOLEAN seqNumbers;
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
//
|
|
// Convert the client name to unicode
|
|
//
|
|
|
|
clientMachineNameString = &connection->ClientMachineNameString;
|
|
if ( clientMachineNameString->Length == 0 ) {
|
|
|
|
UNICODE_STRING clientMachineName;
|
|
clientMachineName.Buffer = connection->ClientMachineName;
|
|
clientMachineName.MaximumLength =
|
|
(USHORT)(COMPUTER_NAME_LENGTH+1)*sizeof(WCHAR);
|
|
|
|
(VOID)RtlOemStringToUnicodeString(
|
|
&clientMachineName,
|
|
&connection->OemClientMachineNameString,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Add the double backslashes to the length
|
|
//
|
|
|
|
clientMachineNameString->Length =
|
|
(USHORT)(clientMachineName.Length + 2*sizeof(WCHAR));
|
|
|
|
}
|
|
|
|
//
|
|
// Form a string describing the computer name without the
|
|
// leading backslashes.
|
|
//
|
|
|
|
machineName.Buffer = clientMachineNameString->Buffer + 2;
|
|
machineName.Length = clientMachineNameString->Length - 2 * sizeof(WCHAR);
|
|
machineName.MaximumLength =
|
|
clientMachineNameString->MaximumLength - 2 * sizeof(WCHAR);
|
|
|
|
SecurityContext = SrvAllocateSecurityContext();
|
|
if( SecurityContext == NULL )
|
|
{
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate a session block.
|
|
//
|
|
|
|
SrvAllocateSession(
|
|
&session,
|
|
&machineName,
|
|
&domain );
|
|
|
|
if ( session == NULL ) {
|
|
|
|
//
|
|
// Unable to allocate a Session block. Return an error
|
|
// status.
|
|
//
|
|
|
|
SrvDereferenceSecurityContext( SecurityContext );
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Assume that down-level clients that are getting logged on
|
|
// here will always use canonicalized (uppercase) paths. This
|
|
// will result in case insensitivity for all operations.
|
|
//
|
|
|
|
session->UsingUppercasePaths = TRUE;
|
|
|
|
//
|
|
// The only way for a client to tell us the buffer size or the
|
|
// max count of pending requests he wants to use is the Session
|
|
// Setup SMB. If he didn't send one, then we get to
|
|
// unilaterally determine the buffer size and multiplex count
|
|
// used by both of us.
|
|
//
|
|
|
|
endpoint = connection->Endpoint;
|
|
if ( endpoint->IsConnectionless ) {
|
|
|
|
ULONG adapterNumber;
|
|
|
|
//
|
|
// Our session max buffer size is the smaller of the
|
|
// server receive buffer size and the ipx transport
|
|
// indicated max packet size.
|
|
//
|
|
|
|
adapterNumber =
|
|
WorkContext->ClientAddress->DatagramOptions.LocalTarget.NicId;
|
|
|
|
session->MaxBufferSize =
|
|
(USHORT) GetIpxMaxBufferSize(
|
|
endpoint,
|
|
adapterNumber,
|
|
SrvReceiveBufferLength
|
|
);
|
|
|
|
} else {
|
|
|
|
session->MaxBufferSize = (USHORT)SrvReceiveBufferLength;
|
|
}
|
|
|
|
session->MaxMpxCount = SrvMaxMpxCount;
|
|
|
|
if ( session->MaxMpxCount < 2 ) {
|
|
connection->OplocksAlwaysDisabled = TRUE;
|
|
}
|
|
|
|
|
|
if( SrvSmbSecuritySignaturesRequired == TRUE &&
|
|
WorkContext->Connection->Endpoint->IsConnectionless == FALSE ) {
|
|
|
|
seqNumbers = TRUE;
|
|
|
|
} else {
|
|
seqNumbers = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Try to find legitimate name/password combination.
|
|
//
|
|
|
|
status = SrvValidateUser(
|
|
&SecurityContext->UserHandle,
|
|
session,
|
|
connection,
|
|
&machineName,
|
|
password,
|
|
strlen( password ) + 1,
|
|
NULL, // CaseSensitivePassword
|
|
0, // CaseSensitivePasswordLength
|
|
seqNumbers,
|
|
NULL // action
|
|
);
|
|
|
|
//
|
|
// If a bad name/password combination was sent, return an error.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
SrvFreeSession( session );
|
|
SrvFreeTreeConnect ( treeConnect );
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnect: Bad user/password combination.\n" ));
|
|
}
|
|
|
|
SrvStatistics.LogonErrors++;
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
IF_SMB_DEBUG(ADMIN1) {
|
|
KdPrint(( "Validated user: %ws\n",
|
|
connection->ClientMachineName ));
|
|
}
|
|
|
|
//
|
|
// Making a new session visible is a multiple-step operation. It
|
|
// must be inserted in the global ordered tree connect list and the
|
|
// containing connection's session table, and the connection must be
|
|
// referenced. We need to make these operations appear atomic, so
|
|
// that the session cannot be accessed elsewhere before we're done
|
|
// setting it up. In order to do this, we hold all necessary locks
|
|
// the entire time we're doing the operations. The first operation
|
|
// is protected by the global ordered list lock
|
|
// (SrvOrderedListLock), while the other operations are protected by
|
|
// the per-connection lock. We take out the ordered list lock
|
|
// first, then the connection lock. This ordering is required by
|
|
// lock levels (see lock.h).
|
|
//
|
|
//
|
|
// Ready to try to find a UID for the session. Check to see if
|
|
// the connection is being closed, and if so, terminate this
|
|
// operation.
|
|
//
|
|
|
|
ASSERT( SrvSessionList.Lock == &SrvOrderedListLock );
|
|
|
|
ACQUIRE_LOCK( SrvSessionList.Lock );
|
|
ACQUIRE_LOCK( &connection->Lock );
|
|
|
|
// Set the Security Context now that we have the lock acquired
|
|
SrvReplaceSessionSecurityContext( session, SecurityContext, WorkContext );
|
|
|
|
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
RELEASE_LOCK( SrvSessionList.Lock );
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnect: Connection closing\n" ));
|
|
}
|
|
|
|
SrvFreeSession( session );
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
|
|
status = STATUS_INVALID_PARAMETER;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Because the client is speaking the "core" dialect, it will
|
|
// not send a valid UID in future SMBs, so it can only have one
|
|
// session. We define that session to live in UID slot 0. We
|
|
// know that the client has no sessions yet, so slot 0 must be
|
|
// free.
|
|
//
|
|
|
|
tableHeader = &pagedConnection->SessionTable;
|
|
ASSERT( tableHeader->Table[0].Owner == NULL );
|
|
|
|
uidIndex = 0;
|
|
|
|
//
|
|
// Remove the UID slot from the free list and set its owner and
|
|
// sequence number. Create a UID for the session. Increment
|
|
// count of sessions.
|
|
//
|
|
|
|
entry = &tableHeader->Table[uidIndex];
|
|
|
|
tableHeader->FirstFreeEntry = entry->NextFreeEntry;
|
|
DEBUG entry->NextFreeEntry = -2;
|
|
if ( tableHeader->LastFreeEntry == uidIndex ) {
|
|
tableHeader->LastFreeEntry = -1;
|
|
}
|
|
|
|
entry->Owner = session;
|
|
|
|
INCREMENT_UID_SEQUENCE( entry->SequenceNumber );
|
|
if ( uidIndex == 0 && entry->SequenceNumber == 0 ) {
|
|
INCREMENT_UID_SEQUENCE( entry->SequenceNumber );
|
|
}
|
|
session->Uid = MAKE_UID( uidIndex, entry->SequenceNumber );
|
|
|
|
connection->CurrentNumberOfSessions++;
|
|
|
|
IF_SMB_DEBUG(ADMIN1) {
|
|
KdPrint(( "Found UID. Index = 0x%lx, sequence = 0x%lx\n",
|
|
(ULONG)UID_INDEX( session->Uid ),
|
|
(ULONG)UID_SEQUENCE( session->Uid ) ));
|
|
}
|
|
|
|
//
|
|
// Insert the session on the global session list.
|
|
//
|
|
|
|
SrvInsertEntryOrderedList( &SrvSessionList, session );
|
|
|
|
//
|
|
// Reference the connection block to account for the new
|
|
// session.
|
|
//
|
|
|
|
SrvReferenceConnection( connection );
|
|
session->Connection = connection;
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
RELEASE_LOCK( SrvSessionList.Lock );
|
|
|
|
//
|
|
// Session successfully created. Remember its address in the
|
|
// work context block.
|
|
//
|
|
// *** Note that the reference count on the session block is
|
|
// initially set to 2, to allow for the active status on the
|
|
// block and the pointer that we're maintaining. In other
|
|
// words, this is a referenced pointer, and the pointer must
|
|
// be dereferenced when processing of this SMB is complete.
|
|
//
|
|
|
|
WorkContext->Session = session;
|
|
|
|
didLogon = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// Try to match pathname against available shared resources. Note
|
|
// that if SrvVerifyShare finds a matching share, it references it
|
|
// and stores its address in WorkContext->Share.
|
|
//
|
|
|
|
share = SrvVerifyShare(
|
|
WorkContext,
|
|
(PSZ)request->Buffer + 1,
|
|
service,
|
|
SMB_IS_UNICODE( WorkContext ),
|
|
session->IsNullSession,
|
|
&status,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// If no match was found, return an error.
|
|
//
|
|
|
|
if ( share == NULL ) {
|
|
|
|
if ( didLogon ) {
|
|
SrvCloseSession( session );
|
|
}
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnect: SrvVerifyShare failed for %s. Status = %x\n", request->Buffer+1, status ));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Impersonate the user so that we can capture his security context.
|
|
// This is necessary in order to determine whether the user can
|
|
// connect to the share.
|
|
//
|
|
|
|
status = IMPERSONATE( WorkContext );
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
SrvSetSmbError( WorkContext, status );
|
|
if ( didLogon ) {
|
|
SrvCloseSession( session );
|
|
}
|
|
SrvFreeTreeConnect( treeConnect );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
SeCaptureSubjectContext( &subjectContext );
|
|
|
|
//
|
|
// Set up the desired access on the share, based on whether the
|
|
// server is paused. If the server is paused, admin privilege is
|
|
// required to connect to any share; if the server is not paused,
|
|
// admin privilege is required only for admin shares (C$, etc.).
|
|
//
|
|
|
|
if ( SrvPaused ) {
|
|
desiredAccess = SRVSVC_PAUSED_SHARE_CONNECT;
|
|
} else {
|
|
desiredAccess = SRVSVC_SHARE_CONNECT;
|
|
}
|
|
|
|
//
|
|
// Check whether the user has access to this share.
|
|
//
|
|
|
|
if ( !SeAccessCheck(
|
|
share->SecurityDescriptor,
|
|
&subjectContext,
|
|
FALSE,
|
|
desiredAccess,
|
|
0L,
|
|
NULL,
|
|
&SrvShareConnectMapping,
|
|
UserMode,
|
|
&grantedAccess,
|
|
&status
|
|
) ) {
|
|
|
|
IF_SMB_DEBUG(TREE2) {
|
|
KdPrint(( "SrvSmbTreeConnect: SeAccessCheck failed: %X\n",
|
|
status ));
|
|
}
|
|
|
|
//
|
|
// Release the subject context and revert to the server's security
|
|
// context.
|
|
//
|
|
|
|
SeReleaseSubjectContext( &subjectContext );
|
|
|
|
REVERT( );
|
|
|
|
if ( SrvPaused ) {
|
|
SrvSetSmbError( WorkContext, STATUS_SHARING_PAUSED );
|
|
status = STATUS_SHARING_PAUSED;
|
|
} else {
|
|
SrvSetSmbError( WorkContext, status );
|
|
}
|
|
|
|
if ( didLogon ) {
|
|
SrvCloseSession( session );
|
|
}
|
|
SrvFreeTreeConnect( treeConnect );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT( grantedAccess == desiredAccess );
|
|
|
|
//
|
|
// Release the subject context and revert to the server's security
|
|
// context.
|
|
//
|
|
|
|
SeReleaseSubjectContext( &subjectContext );
|
|
|
|
REVERT( );
|
|
|
|
|
|
//
|
|
// Let the license server know
|
|
//
|
|
if( share->ShareType != ShareTypePipe ) {
|
|
|
|
status = SrvXsLSOperation( session, XACTSRV_MESSAGE_LSREQUEST );
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
if ( didLogon ) {
|
|
SrvCloseSession( session );
|
|
}
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnect: License server returned %X\n",
|
|
status ));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Making a new tree connect visible is a three-step operation. It
|
|
// must be inserted in the containing share's tree connect list, the
|
|
// global ordered tree connect list, and the containing connection's
|
|
// tree connect table. We need to make these operations appear
|
|
// atomic, so that the tree connect cannot be accessed elsewhere
|
|
// before we're done setting it up. In order to do this, we hold
|
|
// all necessary locks the entire time we're doing the three
|
|
// operations. The first and second operations are protected by the
|
|
// global share lock (SrvShareLock), while the third operation is
|
|
// protected by the per-connection lock. We take out the share lock
|
|
// first, then the connection lock. This ordering is required by
|
|
// lock levels (see lock.h).
|
|
//
|
|
// Another problem here is that the checking of the share state, the
|
|
// inserting of the tree connect on the share's list, and the
|
|
// referencing of the share all need to be atomic. (The same holds
|
|
// for the connection actions.) Normally this would not be a
|
|
// problem, because we could just hold the share lock while doing
|
|
// all three actions. However, in this case we also need to hold
|
|
// the connection lock, and we can't call SrvReferenceShare while
|
|
// doing that. To get around this problem, we reference the share
|
|
// _before_ taking out the locks, and dereference after releasing
|
|
// the locks if we decide not to insert the tree connect.
|
|
//
|
|
|
|
status = SrvReferenceShareForTreeConnect( share );
|
|
|
|
//
|
|
// SrvReferenceShareForTreeConnect will fail if it cannot open the
|
|
// share root directory for some reason. If this happens,
|
|
// fail the tree connect attempt.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
if ( didLogon ) {
|
|
SrvCloseSession( session );
|
|
}
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnect: open of share root failed:%X\n",
|
|
status ));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ACQUIRE_LOCK( &SrvShareLock );
|
|
ASSERT( SrvTreeConnectList.Lock == &SrvShareLock );
|
|
ACQUIRE_LOCK( &connection->Lock );
|
|
|
|
//
|
|
// We first check all conditions to make sure that we can actually
|
|
// insert this tree connect block.
|
|
//
|
|
// Make sure that the share isn't closing, and that there aren't
|
|
// already too many uses on this share.
|
|
//
|
|
|
|
if ( GET_BLOCK_STATE(share) != BlockStateActive ) {
|
|
|
|
//
|
|
// The share is closing. Reject the request.
|
|
//
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnect: Share %wZ (0x%p) is closing\n",
|
|
&share->ShareName, share ));
|
|
}
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto cant_insert;
|
|
|
|
}
|
|
|
|
if ( share->CurrentUses > share->MaxUses ) {
|
|
|
|
//
|
|
// The share is full. Reject the request.
|
|
//
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnect: No more uses available for share %wZ (0x%p), max = %ld\n",
|
|
&share->ShareName, share, share->MaxUses ));
|
|
}
|
|
|
|
status = STATUS_REQUEST_NOT_ACCEPTED;
|
|
goto cant_insert;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that the connection isn't closing.
|
|
//
|
|
|
|
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnect: Connection closing\n" ));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto cant_insert;
|
|
|
|
}
|
|
|
|
//
|
|
// Find a TID that can be used for this tree connect.
|
|
//
|
|
|
|
tableHeader = &pagedConnection->TreeConnectTable;
|
|
if ( tableHeader->FirstFreeEntry == -1
|
|
&&
|
|
SrvGrowTable(
|
|
tableHeader,
|
|
SrvInitialTreeTableSize,
|
|
SrvMaxTreeTableSize,
|
|
&TableStatus ) == FALSE
|
|
) {
|
|
|
|
//
|
|
// No free entries in the tree table. Reject the request.
|
|
//
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnect: No more TIDs available.\n" ));
|
|
}
|
|
|
|
status = TableStatus;
|
|
|
|
if( TableStatus == STATUS_INSUFF_SERVER_RESOURCES )
|
|
{
|
|
SrvLogTableFullError( SRV_TABLE_TREE_CONNECT );
|
|
}
|
|
goto cant_insert;
|
|
|
|
}
|
|
|
|
tidIndex = tableHeader->FirstFreeEntry;
|
|
|
|
//
|
|
// All conditions have been satisfied. We can now do the things
|
|
// necessary to make the tree connect visible.
|
|
//
|
|
// Increment the count of uses for the share. Link the tree connect
|
|
// into the list of active tree connects for the share. Save the
|
|
// share address in the tree connect. Note that we referenced the
|
|
// share earlier, before taking out the connection lock.
|
|
//
|
|
|
|
SrvInsertTailList(
|
|
&share->TreeConnectList,
|
|
&treeConnect->ShareListEntry
|
|
);
|
|
|
|
treeConnect->Share = share;
|
|
|
|
//
|
|
// Remove the TID slot from the free list and set its owner and
|
|
// sequence number. Create a TID for the tree connect.
|
|
//
|
|
|
|
entry = &tableHeader->Table[tidIndex];
|
|
|
|
tableHeader->FirstFreeEntry = entry->NextFreeEntry;
|
|
DEBUG entry->NextFreeEntry = -2;
|
|
if ( tableHeader->LastFreeEntry == tidIndex ) {
|
|
tableHeader->LastFreeEntry = -1;
|
|
}
|
|
|
|
entry->Owner = treeConnect;
|
|
|
|
INCREMENT_TID_SEQUENCE( entry->SequenceNumber );
|
|
if ( tidIndex == 0 && entry->SequenceNumber == 0 ) {
|
|
INCREMENT_TID_SEQUENCE( entry->SequenceNumber );
|
|
}
|
|
treeConnect->Tid = MAKE_TID( tidIndex, entry->SequenceNumber );
|
|
|
|
IF_SMB_DEBUG(TREE1) {
|
|
KdPrint(( "Found TID. Index = 0x%lx, sequence = 0x%lx\n",
|
|
TID_INDEX( treeConnect->Tid ),
|
|
TID_SEQUENCE( treeConnect->Tid ) ));
|
|
}
|
|
|
|
//
|
|
// Reference the connection to account for the active tree connect.
|
|
//
|
|
|
|
SrvReferenceConnection( connection );
|
|
treeConnect->Connection = connection;
|
|
|
|
if( session )
|
|
{
|
|
SrvReferenceSession( session );
|
|
treeConnect->Session = session;
|
|
}
|
|
|
|
//
|
|
// Link the tree connect into the global list of tree connects.
|
|
//
|
|
|
|
SrvInsertEntryOrderedList( &SrvTreeConnectList, treeConnect );
|
|
|
|
//
|
|
// If this session is the one controlling the extended security signatures,
|
|
// see if we need to hash the session key
|
|
//
|
|
if( session->SessionKeyState == SrvSessionKeyAuthenticating )
|
|
{
|
|
// Downlevel machines that use a simple TREE_CONNECT (instead of TREE_CONNECT_ANDX)
|
|
// don't understand extended signatures, so we can make the session key availible.
|
|
// Note that if the REQUIRE_EXTENDED_SIGNATURES policy is active, that check occurred
|
|
// above.
|
|
session->SessionKeyState = SrvSessionKeyAvailible;
|
|
}
|
|
|
|
//
|
|
// Release the locks used to make this operation appear atomic.
|
|
//
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
//
|
|
// Get the qos information for this connection
|
|
//
|
|
|
|
SrvUpdateVcQualityOfService ( connection, NULL );
|
|
|
|
//
|
|
// Tree connect successfully created. Because the tree connect was
|
|
// created with an initial reference count of 2, dereference it now.
|
|
//
|
|
// *** Don't bother to save the tree connect address in the work
|
|
// context block, because we're going to forget our pointers
|
|
// soon anyway (we're done with the request). TreeConnectAndX
|
|
// has to remember these things, though.
|
|
//
|
|
|
|
SrvDereferenceTreeConnect( treeConnect );
|
|
|
|
//
|
|
// Set up response SMB.
|
|
//
|
|
|
|
SmbPutAlignedUshort( &WorkContext->ResponseHeader->Tid, treeConnect->Tid );
|
|
|
|
response->WordCount = 2;
|
|
SmbPutUshort( &response->MaxBufferSize, (USHORT)session->MaxBufferSize );
|
|
SmbPutUshort( &response->Tid, treeConnect->Tid );
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_TREE_CONNECT,
|
|
0
|
|
);
|
|
|
|
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbTreeConnect complete.\n" ));
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
|
|
cant_insert:
|
|
|
|
//
|
|
// We get here if for some reason we decide that we can't insert
|
|
// the tree connect. On entry, status contains the reason code.
|
|
// The connection lock and the share lock are held.
|
|
//
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
if ( didLogon ) {
|
|
SrvCloseSession( session );
|
|
}
|
|
|
|
SrvDereferenceShareForTreeConnect( share );
|
|
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
|
|
Cleanup:
|
|
SrvWmiEndContext(WorkContext);
|
|
return SmbStatus;
|
|
} // SrvSmbTreeConnect
|
|
|
|
|
|
SMB_PROCESSOR_RETURN_TYPE
|
|
SrvSmbTreeConnectAndX (
|
|
SMB_PROCESSOR_PARAMETERS
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes a tree connect and X 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
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PREQ_TREE_CONNECT_ANDX request;
|
|
PRESP_TREE_CONNECT_ANDX response;
|
|
PRESP_EXTENDED_TREE_CONNECT_ANDX responseExtended;
|
|
PRESP_21_TREE_CONNECT_ANDX response21;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
NTSTATUS TableStatus;
|
|
SMB_STATUS SmbStatus = SmbStatusInProgress;
|
|
PCONNECTION connection;
|
|
PPAGED_CONNECTION pagedConnection;
|
|
PTABLE_HEADER tableHeader;
|
|
PTABLE_ENTRY entry;
|
|
SHORT tidIndex;
|
|
PSHARE share;
|
|
PTREE_CONNECT treeConnect;
|
|
PVOID shareName;
|
|
PUCHAR shareType;
|
|
USHORT shareNameLength;
|
|
USHORT reqAndXOffset;
|
|
UCHAR nextCommand;
|
|
PSZ shareString;
|
|
USHORT shareStringLength;
|
|
USHORT RequestFlags;
|
|
USHORT byteCount;
|
|
USHORT maxByteCount;
|
|
PUCHAR smbBuffer;
|
|
PSESSION session;
|
|
SECURITY_SUBJECT_CONTEXT subjectContext;
|
|
ACCESS_MASK desiredAccess;
|
|
ACCESS_MASK grantedAccess;
|
|
BOOLEAN isUnicode;
|
|
UNICODE_STRING serverName;
|
|
BOOLEAN remapPipeNames = FALSE;
|
|
BOOLEAN KeyHashed = FALSE;
|
|
|
|
PAGED_CODE( );
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_TREE_CONNECT_AND_X;
|
|
SrvWmiStartContext(WorkContext);
|
|
|
|
IF_SMB_DEBUG(TREE1) {
|
|
KdPrint(( "Tree connect and X request header at 0x%p, response header at 0x%p\n",
|
|
WorkContext->RequestHeader, WorkContext->ResponseHeader ));
|
|
KdPrint(( "Tree connect and X request parameters at 0x%p, response parameters at 0x%p\n",
|
|
WorkContext->RequestParameters,
|
|
WorkContext->ResponseParameters ));
|
|
}
|
|
|
|
//
|
|
// Set up parameters.
|
|
//
|
|
|
|
request = (PREQ_TREE_CONNECT_ANDX)(WorkContext->RequestParameters);
|
|
response = (PRESP_TREE_CONNECT_ANDX)(WorkContext->ResponseParameters);
|
|
responseExtended = (PRESP_EXTENDED_TREE_CONNECT_ANDX)(WorkContext->ResponseParameters);
|
|
response21 = (PRESP_21_TREE_CONNECT_ANDX)(WorkContext->ResponseParameters);
|
|
|
|
//
|
|
// If bit 0 of Flags is set, disconnect tree in header TID. We must
|
|
// get the appropriate tree connect pointer. SrvVerifyTid does this
|
|
// for us, referencing the tree connect and storing the pointer in
|
|
// the work context block. We have to dereference the block and
|
|
// erase the pointer after calling SrvCloseTreeConnect.
|
|
//
|
|
|
|
if ( (SmbGetUshort( &request->Flags ) & 1) != 0 ) {
|
|
|
|
if ( SrvVerifyTid(
|
|
WorkContext,
|
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid )
|
|
) == NULL ) {
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: Invalid TID to disconnect: 0x%lx\n",
|
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid ) ));
|
|
}
|
|
|
|
//
|
|
// Just ignore an invalid TID--this is what the LM 2.0
|
|
// server does.
|
|
//
|
|
|
|
} else {
|
|
|
|
SrvCloseTreeConnect( WorkContext->TreeConnect );
|
|
|
|
SrvDereferenceTreeConnect( WorkContext->TreeConnect );
|
|
WorkContext->TreeConnect = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Validate the UID in the header and get a session pointer. We need
|
|
// the user's token to check whether they can access this share.
|
|
//
|
|
|
|
session = SrvVerifyUid(
|
|
WorkContext,
|
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )
|
|
);
|
|
|
|
//
|
|
// If we couldn't find a valid session fail the tree connect.
|
|
//
|
|
|
|
if ( session == NULL ) {
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: rejecting tree connect for "
|
|
"session %p due to server paused.\n", session ));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID );
|
|
status = STATUS_SMB_BAD_UID;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
else if( session->IsSessionExpired )
|
|
{
|
|
status = SESSION_EXPIRED_STATUS_CODE;
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Try to match pathname against available shared resources. Note
|
|
// that if SrvVerifyShare finds a matching share, it references it
|
|
// and stores its address in WorkContext->Share.
|
|
//
|
|
|
|
shareName = (PSZ)request->Buffer +
|
|
SmbGetUshort( &request->PasswordLength );
|
|
|
|
connection = WorkContext->Connection;
|
|
pagedConnection = connection->PagedConnection;
|
|
|
|
isUnicode = SMB_IS_UNICODE( WorkContext );
|
|
|
|
if ( isUnicode ) {
|
|
shareName = ALIGN_SMB_WSTR( shareName );
|
|
}
|
|
|
|
shareNameLength = SrvGetStringLength(
|
|
shareName,
|
|
END_OF_REQUEST_SMB( WorkContext ),
|
|
SMB_IS_UNICODE( WorkContext ),
|
|
TRUE // include null terminator
|
|
);
|
|
|
|
//
|
|
// if share name is bogus, return an error.
|
|
//
|
|
|
|
if ( shareNameLength == (USHORT)-1 ) {
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: pathname is bogus.\n"));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_BAD_NETWORK_NAME );
|
|
status = STATUS_BAD_NETWORK_NAME;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
shareType = (PCHAR)shareName + shareNameLength;
|
|
|
|
share = SrvVerifyShare(
|
|
WorkContext,
|
|
shareName,
|
|
shareType,
|
|
isUnicode,
|
|
session->IsNullSession,
|
|
&status,
|
|
&serverName
|
|
);
|
|
|
|
//
|
|
// If no match was found, return an error.
|
|
//
|
|
|
|
if ( share == NULL ) {
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: pathname does not match "
|
|
"any shares: %s\n", shareName ));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If the the client is connecting with a netbiosless transport and the name of the
|
|
// server which the client was requesting doesn't match any of our servernames, then
|
|
// the client has accidentally connected to the wrong server. Let the client know.
|
|
//
|
|
if( !SrvDisableStrictNameChecking &&
|
|
serverName.Buffer != NULL &&
|
|
connection->Endpoint->IsNoNetBios &&
|
|
SrvIsDottedQuadAddress( &serverName ) == FALSE &&
|
|
SrvFindNamedEndpoint( &serverName, NULL ) == FALSE &&
|
|
SrvIsLocalHost( &serverName ) == FALSE ) {
|
|
|
|
BOOL bBadName = TRUE;
|
|
|
|
// Last check, make sure its not the domain DNS name (which may differ from the NETBIOS DNS name)
|
|
ACQUIRE_LOCK_SHARED( &SrvEndpointLock );
|
|
|
|
// We only check up to the first ., so ntdev.microsoft.com would match SrvDnsDomainName "NTDEV"
|
|
// Strip off the excess info for the check, then put it back
|
|
if( SrvDnsDomainName ) {
|
|
if( SrvDnsDomainName->Length <= serverName.Length )
|
|
{
|
|
USHORT oldLength = serverName.Length;
|
|
serverName.Length = SrvDnsDomainName->Length;
|
|
|
|
if( RtlEqualUnicodeString( &serverName, SrvDnsDomainName, TRUE ) )
|
|
{
|
|
bBadName = FALSE;
|
|
}
|
|
|
|
serverName.Length = oldLength;
|
|
}
|
|
}
|
|
|
|
RELEASE_LOCK( &SrvEndpointLock );
|
|
|
|
//
|
|
// The client has connected to this server in error--turn the client back!
|
|
//
|
|
if( bBadName )
|
|
{
|
|
SrvSetSmbError( WorkContext, STATUS_DUPLICATE_NAME );
|
|
status = STATUS_DUPLICATE_NAME;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Impersonate the user so that we can capture his security context.
|
|
// This is necessary in order to determine whether the user can
|
|
// connect to the share.
|
|
//
|
|
|
|
status = IMPERSONATE( WorkContext );
|
|
if( !NT_SUCCESS( status ) ) {
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
SeCaptureSubjectContext( &subjectContext );
|
|
|
|
//
|
|
// Set up the desired access on the share, based on whether the
|
|
// server is paused. If the server is paused, admin privilege is
|
|
// required to connect to any share; if the server is not paused,
|
|
// admin privilege is required only for admin shares (C$, etc.).
|
|
//
|
|
|
|
if ( SrvPaused ) {
|
|
desiredAccess = SRVSVC_PAUSED_SHARE_CONNECT;
|
|
} else {
|
|
desiredAccess = SRVSVC_SHARE_CONNECT;
|
|
}
|
|
|
|
//
|
|
// Check whether the user has access to this share.
|
|
//
|
|
|
|
if ( !SeAccessCheck(
|
|
share->SecurityDescriptor,
|
|
&subjectContext,
|
|
FALSE,
|
|
desiredAccess,
|
|
0L,
|
|
NULL,
|
|
&SrvShareConnectMapping,
|
|
UserMode,
|
|
&grantedAccess,
|
|
&status
|
|
) ) {
|
|
|
|
IF_SMB_DEBUG(TREE2) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: SeAccessCheck failed: %X\n",
|
|
status ));
|
|
}
|
|
|
|
//
|
|
// Release the subject context and revert to the server's security
|
|
// context.
|
|
//
|
|
|
|
SeReleaseSubjectContext( &subjectContext );
|
|
|
|
REVERT( );
|
|
|
|
if ( SrvPaused ) {
|
|
SrvSetSmbError( WorkContext, STATUS_SHARING_PAUSED );
|
|
status = STATUS_SHARING_PAUSED;
|
|
} else {
|
|
SrvSetSmbError( WorkContext, status );
|
|
}
|
|
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT( grantedAccess == desiredAccess );
|
|
|
|
//
|
|
// Release the subject context and revert to the server's security
|
|
// context.
|
|
//
|
|
|
|
SeReleaseSubjectContext( &subjectContext );
|
|
|
|
REVERT( );
|
|
|
|
//
|
|
// See if the license server wants to let this person in on the NTAS
|
|
//
|
|
if( share->ShareType != ShareTypePipe ) {
|
|
|
|
status = SrvXsLSOperation( session, XACTSRV_MESSAGE_LSREQUEST );
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: License server returned %X\n",
|
|
status ));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
} else if( serverName.Buffer != NULL ) {
|
|
|
|
//
|
|
// This is the IPC$ share. See if we're supposed to remap pipe names
|
|
//
|
|
SrvFindNamedEndpoint( &serverName, &remapPipeNames );
|
|
|
|
}
|
|
|
|
//
|
|
// Allocate a tree connect block.
|
|
//
|
|
|
|
SrvAllocateTreeConnect( &treeConnect, serverName.Buffer ? &serverName : NULL );
|
|
|
|
if ( treeConnect == NULL ) {
|
|
|
|
//
|
|
// Unable to allocate tree connect. Return an error to the
|
|
// client.
|
|
//
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
treeConnect->RemapPipeNames = remapPipeNames;
|
|
|
|
//
|
|
// Making a new tree connect visible is a three-step operation. It
|
|
// must be inserted in the containing share's tree connect list, the
|
|
// global ordered tree connect list, and the containing connection's
|
|
// tree connect table. We need to make these operations appear
|
|
// atomic, so that the tree connect cannot be accessed elsewhere
|
|
// before we're done setting it up. In order to do this, we hold
|
|
// all necessary locks the entire time we're doing the three
|
|
// operations. The first and second operations are protected by the
|
|
// global share lock (SrvShareLock), while the third operation is
|
|
// protected by the per-connection lock. We take out the share lock
|
|
// first, then the connection lock. This ordering is required by
|
|
// lock levels (see lock.h).
|
|
//
|
|
// Another problem here is that the checking of the share state, the
|
|
// inserting of the tree connect on the share's list, and the
|
|
// referencing of the share all need to be atomic. (The same holds
|
|
// for the connection actions.) Normally this would not be a
|
|
// problem, because we could just hold the share lock while doing
|
|
// all three actions. However, in this case we also need to hold
|
|
// the connection lock, and we can't call SrvReferenceShare while
|
|
// doing that. To get around this problem, we reference the share
|
|
// _before_ taking out the locks, and dereference after releasing
|
|
// the locks if we decide not to insert the tree connect.
|
|
//
|
|
|
|
status = SrvReferenceShareForTreeConnect( share );
|
|
|
|
//
|
|
// SrvReferenceShareForTreeConnect will fail if it cannot open the
|
|
// share root directory for some reason. If this happens,
|
|
// fail the tree connect attempt.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: open of share root failed:%X\n",
|
|
status ));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ACQUIRE_LOCK( &SrvShareLock );
|
|
ASSERT( SrvTreeConnectList.Lock == &SrvShareLock );
|
|
ACQUIRE_LOCK( &connection->Lock );
|
|
|
|
if( SrvRequireExtendedSignatures )
|
|
{
|
|
// If we are requiring extended signatures, and the client has not
|
|
// asked for them on this request or any previous request, deny
|
|
// this request.
|
|
if( session->SessionKeyState == SrvSessionKeyAuthenticating )
|
|
{
|
|
if ( !(request->Flags & TREE_CONNECT_ANDX_EXTENDED_SIGNATURES) ) {
|
|
status = STATUS_LOGIN_WKSTA_RESTRICTION;
|
|
goto cant_insert;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If they sent this uplevel flag on a downlevel request, than someone must
|
|
// of been tricking us during negotiate. Fail.
|
|
if( (request->Flags & TREE_CONNECT_ANDX_EXTENDED_SIGNATURES) &&
|
|
(connection->SmbDialect > SmbDialectDosLanMan21) )
|
|
{
|
|
status = STATUS_DOWNGRADE_DETECTED;
|
|
goto cant_insert;
|
|
}
|
|
|
|
|
|
//
|
|
// We first check all conditions to make sure that we can actually
|
|
// insert this tree connect block.
|
|
//
|
|
// Make sure that the share isn't closing, and that there aren't
|
|
// already too many uses on this share.
|
|
//
|
|
|
|
if ( GET_BLOCK_STATE(share) != BlockStateActive ) {
|
|
|
|
//
|
|
// The share is closing. Reject the request.
|
|
//
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: Share %wZ (0x%p) is closing\n",
|
|
&share->ShareName, share ));
|
|
}
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto cant_insert;
|
|
|
|
}
|
|
|
|
if ( share->CurrentUses > share->MaxUses ) {
|
|
|
|
//
|
|
// The share is full. Reject the request.
|
|
//
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: No more uses available for share %wZ (0x%p), max = %ld\n",
|
|
&share->ShareName, share, share->MaxUses ));
|
|
}
|
|
|
|
status = STATUS_REQUEST_NOT_ACCEPTED;
|
|
goto cant_insert;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that the connection isn't closing, and that there's
|
|
// room in its tree connect table.
|
|
//
|
|
|
|
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: Connection closing\n" ));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto cant_insert;
|
|
|
|
}
|
|
|
|
//
|
|
// Find a TID that can be used for this tree connect.
|
|
//
|
|
|
|
tableHeader = &pagedConnection->TreeConnectTable;
|
|
if ( tableHeader->FirstFreeEntry == -1
|
|
&&
|
|
SrvGrowTable(
|
|
tableHeader,
|
|
SrvInitialTreeTableSize,
|
|
SrvMaxTreeTableSize,
|
|
&TableStatus ) == FALSE
|
|
) {
|
|
|
|
//
|
|
// No free entries in the tree table. Reject the request.
|
|
//
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnect: No more TIDs available.\n" ));
|
|
}
|
|
|
|
if( TableStatus == STATUS_INSUFF_SERVER_RESOURCES )
|
|
{
|
|
SrvLogTableFullError( SRV_TABLE_TREE_CONNECT );
|
|
}
|
|
|
|
status = TableStatus;
|
|
goto cant_insert;
|
|
|
|
}
|
|
|
|
tidIndex = tableHeader->FirstFreeEntry;
|
|
|
|
//
|
|
// All conditions have been satisfied. We can now do the things
|
|
// necessary to make the tree connect visible.
|
|
//
|
|
// Link the tree connect into the list of active tree connects for
|
|
// the share. Save the share address in the tree connect. Note
|
|
// that we referenced the share earlier, before taking out the
|
|
// connection lock.
|
|
//
|
|
|
|
SrvInsertTailList(
|
|
&share->TreeConnectList,
|
|
&treeConnect->ShareListEntry
|
|
);
|
|
|
|
treeConnect->Share = share;
|
|
|
|
//
|
|
// Remove the TID slot from the free list and set its owner and
|
|
// sequence number. Create a TID for the tree connect.
|
|
//
|
|
|
|
entry = &tableHeader->Table[tidIndex];
|
|
|
|
tableHeader->FirstFreeEntry = entry->NextFreeEntry;
|
|
DEBUG entry->NextFreeEntry = -2;
|
|
if ( tableHeader->LastFreeEntry == tidIndex ) {
|
|
tableHeader->LastFreeEntry = -1;
|
|
}
|
|
|
|
entry->Owner = treeConnect;
|
|
|
|
INCREMENT_TID_SEQUENCE( entry->SequenceNumber );
|
|
if ( tidIndex == 0 && entry->SequenceNumber == 0 ) {
|
|
INCREMENT_TID_SEQUENCE( entry->SequenceNumber );
|
|
}
|
|
treeConnect->Tid = MAKE_TID( tidIndex, entry->SequenceNumber );
|
|
|
|
IF_SMB_DEBUG(TREE1) {
|
|
KdPrint(( "Found TID. Index = 0x%lx, sequence = 0x%lx\n",
|
|
TID_INDEX( treeConnect->Tid ),
|
|
TID_SEQUENCE( treeConnect->Tid ) ));
|
|
}
|
|
|
|
//
|
|
// Reference the connection to account for the active tree connect.
|
|
//
|
|
|
|
SrvReferenceConnection( connection );
|
|
treeConnect->Connection = connection;
|
|
if( session )
|
|
{
|
|
SrvReferenceSession( session );
|
|
treeConnect->Session = session;
|
|
}
|
|
|
|
//
|
|
// Link the tree connect into the global list of tree connects.
|
|
//
|
|
|
|
SrvInsertEntryOrderedList( &SrvTreeConnectList, treeConnect );
|
|
|
|
//
|
|
// If this session is the one controlling the extended security signatures,
|
|
// see if we need to hash the session key
|
|
//
|
|
if( session->SessionKeyState == SrvSessionKeyAuthenticating )
|
|
{
|
|
if (request->Flags & TREE_CONNECT_ANDX_EXTENDED_SIGNATURES) {
|
|
// Hash the session key
|
|
SrvHashUserSessionKey( session->NtUserSessionKey );
|
|
KeyHashed = TRUE;
|
|
}
|
|
|
|
// This machine has either upgraded to hashed session key, or does not want to
|
|
// Move to availible
|
|
session->SessionKeyState = SrvSessionKeyAvailible;
|
|
}
|
|
|
|
//
|
|
// Release the locks used to make this operation appear atomic.
|
|
//
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
//
|
|
// Get the qos information for this connection
|
|
//
|
|
|
|
SrvUpdateVcQualityOfService ( connection, NULL );
|
|
|
|
//
|
|
// Tree connect successfully created. Save the tree connect block
|
|
// address in the work context block. Note that the reference count
|
|
// on the new block was incremented on creation to account for our
|
|
// reference to the block.
|
|
//
|
|
|
|
WorkContext->TreeConnect = treeConnect;
|
|
|
|
//
|
|
// Set up response SMB, making sure to save request fields first in
|
|
// case the response overwrites the request.
|
|
//
|
|
|
|
reqAndXOffset = SmbGetUshort( &request->AndXOffset );
|
|
nextCommand = request->AndXCommand;
|
|
|
|
RequestFlags = SmbGetUshort(&request->Flags);
|
|
|
|
SmbPutAlignedUshort( &WorkContext->RequestHeader->Tid, treeConnect->Tid );
|
|
SmbPutAlignedUshort( &WorkContext->ResponseHeader->Tid, treeConnect->Tid );
|
|
|
|
response->AndXCommand = nextCommand;
|
|
response->AndXReserved = 0;
|
|
|
|
if ( connection->SmbDialect > SmbDialectDosLanMan21) {
|
|
response->WordCount = 2;
|
|
smbBuffer = (PUCHAR)response->Buffer;
|
|
} else {
|
|
if (RequestFlags & TREE_CONNECT_ANDX_EXTENDED_RESPONSE) {
|
|
responseExtended->WordCount = 7;
|
|
smbBuffer = (PUCHAR)responseExtended->Buffer;
|
|
} else {
|
|
response21->WordCount = 3;
|
|
smbBuffer = (PUCHAR)response21->Buffer;
|
|
}
|
|
|
|
// Fields common to 21 and extended response.
|
|
response21->OptionalSupport = SMB_SUPPORT_SEARCH_BITS;
|
|
|
|
if (share->IsDfs) {
|
|
response21->OptionalSupport |= SMB_SHARE_IS_IN_DFS;
|
|
}
|
|
|
|
if (KeyHashed)
|
|
{
|
|
response21->OptionalSupport |= SMB_EXTENDED_SIGNATURES;
|
|
}
|
|
|
|
switch( share->CSCState ) {
|
|
case CSC_CACHE_MANUAL_REINT:
|
|
response21->OptionalSupport |= SMB_CSC_CACHE_MANUAL_REINT;
|
|
break;
|
|
case CSC_CACHE_AUTO_REINT:
|
|
response21->OptionalSupport |= SMB_CSC_CACHE_AUTO_REINT;
|
|
break;
|
|
case CSC_CACHE_VDO:
|
|
response21->OptionalSupport |= SMB_CSC_CACHE_VDO;
|
|
break;
|
|
case CSC_CACHE_NONE:
|
|
response21->OptionalSupport |= SMB_CSC_NO_CACHING;
|
|
break;
|
|
}
|
|
|
|
if( SrvNoAliasingOnFilesystem || (share->ShareProperties & SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING) )
|
|
{
|
|
response21->OptionalSupport |= SMB_UNIQUE_FILE_NAME;
|
|
}
|
|
}
|
|
|
|
// Calculate the size of the response buffer
|
|
maxByteCount = (USHORT)(END_OF_RESPONSE_BUFFER(WorkContext) - smbBuffer + 1);
|
|
|
|
//
|
|
// Append the service name string to the SMB. The service name
|
|
// is always sent in ANSI.
|
|
//
|
|
|
|
shareString = StrShareTypeNames[share->ShareType];
|
|
shareStringLength = (USHORT)( strlen( shareString ) + 1 );
|
|
|
|
if( shareStringLength > maxByteCount )
|
|
{
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
status = STATUS_INVALID_SMB;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory ( smbBuffer, shareString, shareStringLength );
|
|
|
|
byteCount = shareStringLength;
|
|
smbBuffer += shareStringLength;
|
|
|
|
if ( connection->SmbDialect <= SmbDialectDosLanMan21 ) {
|
|
|
|
//
|
|
// Append the file system name to the response.
|
|
// If the file system name is unavailable, supply the nul string
|
|
// as the name.
|
|
//
|
|
|
|
if ( isUnicode ) {
|
|
|
|
if ( ((ULONG_PTR)smbBuffer & 1) != 0 ) {
|
|
smbBuffer++;
|
|
byteCount++;
|
|
}
|
|
|
|
if ( share->Type.FileSystem.Name.Buffer != NULL ) {
|
|
|
|
if( byteCount + share->Type.FileSystem.Name.Length > maxByteCount )
|
|
{
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
status = STATUS_INVALID_SMB;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
smbBuffer,
|
|
share->Type.FileSystem.Name.Buffer,
|
|
share->Type.FileSystem.Name.Length
|
|
);
|
|
|
|
byteCount += share->Type.FileSystem.Name.Length;
|
|
|
|
} else {
|
|
|
|
*(PWCH)smbBuffer = UNICODE_NULL;
|
|
byteCount += sizeof( UNICODE_NULL );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( share->Type.FileSystem.Name.Buffer != NULL ) {
|
|
|
|
if( byteCount + share->Type.FileSystem.OemName.Length > maxByteCount )
|
|
{
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
status = STATUS_INVALID_SMB;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
smbBuffer,
|
|
share->Type.FileSystem.OemName.Buffer,
|
|
share->Type.FileSystem.OemName.Length
|
|
);
|
|
|
|
byteCount += share->Type.FileSystem.OemName.Length;
|
|
|
|
} else {
|
|
|
|
*(PUCHAR)smbBuffer = '\0';
|
|
byteCount += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (RequestFlags & TREE_CONNECT_ANDX_EXTENDED_RESPONSE) {
|
|
PRESP_EXTENDED_TREE_CONNECT_ANDX ExtendedResponse;
|
|
|
|
ExtendedResponse = (PRESP_EXTENDED_TREE_CONNECT_ANDX)response;
|
|
|
|
SmbPutUshort( &ExtendedResponse->ByteCount, byteCount );
|
|
|
|
SrvUpdateMaximalShareAccessRightsInResponse(
|
|
WorkContext,
|
|
&ExtendedResponse->MaximalShareAccessRights,
|
|
&ExtendedResponse->GuestMaximalShareAccessRights);
|
|
|
|
SmbPutUshort(
|
|
&ExtendedResponse->AndXOffset,
|
|
GET_ANDX_OFFSET(
|
|
WorkContext->ResponseHeader,
|
|
WorkContext->ResponseParameters,
|
|
RESP_EXTENDED_TREE_CONNECT_ANDX,
|
|
byteCount
|
|
)
|
|
);
|
|
} else {
|
|
SmbPutUshort( &response21->ByteCount, byteCount );
|
|
|
|
SmbPutUshort(
|
|
&response->AndXOffset,
|
|
GET_ANDX_OFFSET(
|
|
WorkContext->ResponseHeader,
|
|
WorkContext->ResponseParameters,
|
|
RESP_21_TREE_CONNECT_ANDX,
|
|
byteCount
|
|
)
|
|
);
|
|
}
|
|
} else { // if Smb dialect == LAN Man 2.1
|
|
SmbPutUshort( &response->ByteCount, byteCount );
|
|
|
|
SmbPutUshort(
|
|
&response->AndXOffset,
|
|
GET_ANDX_OFFSET(
|
|
WorkContext->ResponseHeader,
|
|
WorkContext->ResponseParameters,
|
|
RESP_TREE_CONNECT_ANDX,
|
|
byteCount
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
WorkContext->ResponseParameters = (PUCHAR)WorkContext->ResponseHeader +
|
|
SmbGetUshort( &response->AndXOffset );
|
|
|
|
//
|
|
// Test for legal followon command.
|
|
//
|
|
|
|
switch ( nextCommand ) {
|
|
case SMB_COM_NO_ANDX_COMMAND:
|
|
break;
|
|
|
|
case SMB_COM_OPEN:
|
|
case SMB_COM_OPEN_ANDX:
|
|
case SMB_COM_CREATE:
|
|
case SMB_COM_CREATE_NEW:
|
|
case SMB_COM_CREATE_DIRECTORY:
|
|
case SMB_COM_DELETE:
|
|
case SMB_COM_DELETE_DIRECTORY:
|
|
case SMB_COM_SEARCH:
|
|
case SMB_COM_FIND:
|
|
case SMB_COM_FIND_UNIQUE:
|
|
case SMB_COM_COPY:
|
|
case SMB_COM_RENAME:
|
|
case SMB_COM_NT_RENAME:
|
|
case SMB_COM_CHECK_DIRECTORY:
|
|
case SMB_COM_QUERY_INFORMATION:
|
|
case SMB_COM_SET_INFORMATION:
|
|
case SMB_COM_QUERY_INFORMATION_SRV:
|
|
case SMB_COM_OPEN_PRINT_FILE:
|
|
case SMB_COM_GET_PRINT_QUEUE:
|
|
case SMB_COM_TRANSACTION:
|
|
//
|
|
// Make sure the AndX command is still within the received SMB
|
|
//
|
|
if( (PCHAR)WorkContext->RequestHeader + reqAndXOffset <=
|
|
END_OF_REQUEST_SMB( WorkContext ) ) {
|
|
break;
|
|
}
|
|
|
|
/* Falls Through */
|
|
|
|
default: // Illegal followon command
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
KdPrint(( "SrvSmbTreeConnectAndX: Illegal followon command: 0x%c\n", nextCommand ));
|
|
}
|
|
|
|
SrvLogInvalidSmb( WorkContext );
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
status = STATUS_INVALID_SMB;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If there is an AndX command, set up to process it. Otherwise,
|
|
// indicate completion to the caller.
|
|
//
|
|
|
|
if ( nextCommand != SMB_COM_NO_ANDX_COMMAND ) {
|
|
|
|
// *** Watch out for overwriting request with response.
|
|
|
|
WorkContext->NextCommand = nextCommand;
|
|
|
|
WorkContext->RequestParameters = (PUCHAR)WorkContext->RequestHeader +
|
|
reqAndXOffset;
|
|
|
|
SmbStatus = SmbStatusMoreCommands;
|
|
goto Cleanup;
|
|
}
|
|
|
|
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbTreeConnectAndX complete.\n" ));
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
|
|
cant_insert:
|
|
|
|
//
|
|
// We get here if for some reason we decide that we can't insert
|
|
// the tree connect. On entry, status contains the reason code.
|
|
// The connection lock and the share lock are held.
|
|
//
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
RELEASE_LOCK( &SrvShareLock );
|
|
|
|
SrvDereferenceShareForTreeConnect( share );
|
|
|
|
SrvFreeTreeConnect( treeConnect );
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
|
|
Cleanup:
|
|
SrvWmiEndContext(WorkContext);
|
|
return SmbStatus;
|
|
} // SrvSmbTreeConnectAndX
|
|
|
|
|
|
SMB_PROCESSOR_RETURN_TYPE
|
|
SrvSmbTreeDisconnect (
|
|
SMB_PROCESSOR_PARAMETERS
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes a tree disconnect 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
|
|
|
|
--*/
|
|
|
|
{
|
|
PREQ_TREE_DISCONNECT request;
|
|
PRESP_TREE_DISCONNECT response;
|
|
PTREE_CONNECT treeConnect;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
SMB_STATUS SmbStatus = SmbStatusInProgress;
|
|
|
|
PAGED_CODE( );
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_TREE_DISCONNECT;
|
|
SrvWmiStartContext(WorkContext);
|
|
|
|
IF_SMB_DEBUG(TREE1) {
|
|
KdPrint(( "Tree disconnect request header at 0x%p, response header at 0x%p\n",
|
|
WorkContext->RequestHeader, WorkContext->ResponseHeader ));
|
|
KdPrint(( "Tree disconnect request parameters at 0x%p, response parameters at 0x%p\n",
|
|
WorkContext->RequestParameters,
|
|
WorkContext->ResponseParameters ));
|
|
}
|
|
|
|
//
|
|
// Set up parameters.
|
|
//
|
|
|
|
request = (PREQ_TREE_DISCONNECT)(WorkContext->RequestParameters);
|
|
response = (PRESP_TREE_DISCONNECT)(WorkContext->ResponseParameters);
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
treeConnect = SrvVerifyTid(
|
|
WorkContext,
|
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid )
|
|
);
|
|
|
|
if ( treeConnect == NULL ) {
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
KdPrint(( "SrvSmbTreeDisconnect: Invalid TID: 0x%lx\n",
|
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid ) ));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_TID );
|
|
status = STATUS_SMB_BAD_UID;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Do the actual tree disconnect.
|
|
//
|
|
|
|
SrvCloseTreeConnect( WorkContext->TreeConnect );
|
|
|
|
//
|
|
// Build the response SMB.
|
|
//
|
|
|
|
response->WordCount = 0;
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_TREE_DISCONNECT,
|
|
0
|
|
);
|
|
SmbStatus = SmbStatusSendResponse;
|
|
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbTreeDisconnect complete.\n" ));
|
|
|
|
Cleanup:
|
|
SrvWmiEndContext(WorkContext);
|
|
return SmbStatus;
|
|
|
|
} // SrvSmbTreeDisconnect
|
|
|