1041 lines
28 KiB
C
1041 lines
28 KiB
C
/*++ BUILD Version: 0009 // Increment this if a change has global effects
|
|
Copyright (c) 1987-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
srvcall.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the routines for handling the creation/manipulation of
|
|
server entries in the connection engine database. It also contains the routines
|
|
for parsing the negotiate response from the server.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
RXDT_DefineCategory(SRVCALL);
|
|
#define Dbg (DEBUG_TRACE_SRVCALL)
|
|
|
|
NTSTATUS
|
|
SmbCeCreateSrvCall(
|
|
PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine patches the RDBSS created srv call instance with the information required
|
|
by the mini redirector.
|
|
|
|
Arguments:
|
|
|
|
CallBackContext - the call back context in RDBSS for continuation.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PMRX_SRVCALL_CALLBACK_CONTEXT SCCBC = pCallbackContext;
|
|
PMRX_SRV_CALL pSrvCall;
|
|
PMRX_SRVCALLDOWN_STRUCTURE SrvCalldownStructure = (PMRX_SRVCALLDOWN_STRUCTURE)(SCCBC->SrvCalldownStructure);
|
|
|
|
pSrvCall = SrvCalldownStructure->SrvCall;
|
|
ASSERT( pSrvCall );
|
|
ASSERT( NodeType(pSrvCall) == RDBSS_NTC_SRVCALL );
|
|
|
|
Status = SmbCeInitializeServerEntry(pSrvCall,pCallbackContext);
|
|
|
|
SCCBC->Status = Status;
|
|
SrvCalldownStructure->CallBack(SCCBC);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
MRxIfsCreateSrvCall(
|
|
PMRX_SRV_CALL pSrvCall,
|
|
PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine patches the RDBSS created srv call instance with the information required
|
|
by the mini redirector.
|
|
|
|
Arguments:
|
|
|
|
RxContext - Supplies the context of the original create/ioctl
|
|
|
|
CallBackContext - the call back context in RDBSS for continuation.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING ServerName;
|
|
|
|
PMRX_SRVCALLDOWN_STRUCTURE SrvCalldownStructure = (PMRX_SRVCALLDOWN_STRUCTURE)(pCallbackContext->SrvCalldownStructure);
|
|
|
|
ASSERT( pSrvCall );
|
|
ASSERT( NodeType(pSrvCall) == RDBSS_NTC_SRVCALL );
|
|
|
|
//
|
|
// If this request was made on behalf of the RDBSS, perform SmbCeCreatSrvCall
|
|
// immediately. If the request was made from somewhere else, create a work item
|
|
// and place it on a queue for a worker thread to process later. This distinction
|
|
// is made to simplify transport handle management.
|
|
//
|
|
|
|
if (IoGetCurrentProcess() == RxGetRDBSSProcess())
|
|
{
|
|
//
|
|
// Peform the processing immediately because RDBSS is the initiator of this
|
|
// request
|
|
//
|
|
|
|
Status = SmbCeCreateSrvCall(pCallbackContext);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Dispatch the request to a worker thread because the redirected drive
|
|
// buffering sub-system (RDBSS) was not the initiator
|
|
//
|
|
|
|
Status = RxDispatchToWorkerThread(
|
|
MRxIfsDeviceObject,
|
|
DelayedWorkQueue,
|
|
SmbCeCreateSrvCall,
|
|
pCallbackContext);
|
|
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// Map the return value since the wrapper expects PENDING.
|
|
//
|
|
|
|
Status = STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
MRxIfsFinalizeSrvCall(
|
|
PMRX_SRV_CALL pSrvCall,
|
|
BOOLEAN Force)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys a given server call instance
|
|
|
|
Arguments:
|
|
|
|
pSrvCall - the server call instance to be disconnected.
|
|
|
|
Force - TRUE if a disconnection is to be enforced immediately.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
//
|
|
// Get the address of the server entry associated with
|
|
// this srv_call
|
|
//
|
|
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(pSrvCall);
|
|
|
|
//
|
|
// decrement the reference count. Ref counts are used
|
|
// because this is a shared data structure
|
|
//
|
|
|
|
if (pServerEntry != NULL) {
|
|
SmbCeDereferenceServerEntry(pServerEntry);
|
|
}
|
|
|
|
|
|
pSrvCall->Context = NULL;
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxIfsSrvCallWinnerNotify(
|
|
IN PMRX_SRV_CALL pSrvCall,
|
|
IN BOOLEAN ThisMinirdrIsTheWinner,
|
|
IN OUT PVOID pSrvCallContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finalizes the mini rdr context associated with an RDBSS Server call instance
|
|
|
|
Arguments:
|
|
|
|
pSrvCall - the Server Call
|
|
|
|
ThisMinirdrIsTheWinner - TRUE if this mini rdr is the choosen one.
|
|
|
|
pSrvCallContext - the server call context created by the mini redirector.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
The two phase construction protocol for Server calls is required because of parallel
|
|
initiation of a number of mini redirectors. The RDBSS finalizes the particular mini
|
|
redirector to be used in communicating with a given server based on quality of
|
|
service criterion.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
pServerEntry = (PSMBCEDB_SERVER_ENTRY)pSrvCallContext;
|
|
|
|
if (!ThisMinirdrIsTheWinner) {
|
|
|
|
//
|
|
// Some other mini rdr has been choosen to connect to the server. Destroy
|
|
// the data structures created for this mini redirector.
|
|
//
|
|
|
|
SmbCeUpdateServerEntryState(pServerEntry,SMBCEDB_MARKED_FOR_DELETION);
|
|
SmbCeDereferenceServerEntry(pServerEntry);
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have been chosed to connect to the server (we won). Set up the SRV_CALL
|
|
// flags and check for a loopback
|
|
//
|
|
|
|
pSrvCall->Context = pServerEntry;
|
|
pSrvCall->Flags |= SRVCALL_FLAG_CASE_INSENSITIVE_NETROOTS | SRVCALL_FLAG_CASE_INSENSITIVE_FILENAMES;
|
|
|
|
{
|
|
UNICODE_STRING ServerName;
|
|
BOOLEAN CaseInsensitive = TRUE;
|
|
ASSERT (pServerEntry->pRdbssSrvCall == pSrvCall);
|
|
|
|
ServerName = *pSrvCall->pSrvCallName;
|
|
ServerName.Buffer++; ServerName.Length -= sizeof(WCHAR);
|
|
if (RtlEqualUnicodeString(&ServerName,&SmbCeContext.ComputerName,CaseInsensitive)) {
|
|
DbgPrint("LOOPBACK!!!!!\n");
|
|
pServerEntry->Server.IsLoopBack = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// The following type defines and data structures are used for parsing negotiate SMB
|
|
// responses.
|
|
//
|
|
|
|
|
|
|
|
#include "protocol.h"
|
|
|
|
//superceded in smbxchng.h
|
|
//#define MRXSMB_PROCESS_ID 0xCAFE
|
|
|
|
typedef enum _SMB_NEGOTIATE_TYPE_ {
|
|
SMB_CORE_NEGOTIATE,
|
|
SMB_EXTENDED_NEGOTIATE
|
|
} SMB_NEGOTIATE_TYPE, *PSMB_NEGOTIATE_TYPE;
|
|
|
|
typedef struct _SMB_DIALECTS_ {
|
|
SMB_NEGOTIATE_TYPE NegotiateType;
|
|
USHORT DispatchVectorIndex;
|
|
} SMB_DIALECTS, *PSMB_DIALECTS;
|
|
|
|
SMBCE_SERVER_DISPATCH_VECTOR
|
|
s_SmbServerDispatchVectors[] = {
|
|
{BuildSessionSetupSmb,CoreBuildTreeConnectSmb},
|
|
{BuildSessionSetupSmb,LmBuildTreeConnectSmb}
|
|
};
|
|
|
|
|
|
SMB_DIALECTS
|
|
s_SmbDialects[] = {
|
|
{ SMB_CORE_NEGOTIATE, 0},
|
|
{ SMB_CORE_NEGOTIATE, 0 },
|
|
{ SMB_EXTENDED_NEGOTIATE, 1 },
|
|
{ SMB_EXTENDED_NEGOTIATE, 1 },
|
|
{ SMB_EXTENDED_NEGOTIATE, 1 },
|
|
{ SMB_EXTENDED_NEGOTIATE, 1 },
|
|
{ SMB_EXTENDED_NEGOTIATE, 1 }
|
|
};
|
|
|
|
|
|
|
|
//
|
|
// LANMAN21 dialect was chosen to allow user login (as opposed to just
|
|
// share level to be demonstrated in this example
|
|
//
|
|
|
|
CHAR s_DialectNames[] = {
|
|
"\2" PCNET1 "\0"
|
|
"\2" "noway" XENIXCORE "\0"
|
|
"\2" "noway" MSNET103 "\0"
|
|
"\2" "noway" LANMAN10 "\0"
|
|
"\2" WFW10 "\0"
|
|
"\2" "noway"LANMAN12 "\0"
|
|
"\2" LANMAN21
|
|
};
|
|
|
|
|
|
|
|
|
|
#define __second(a,b) (b)
|
|
ULONG
|
|
MRxSmbDialectFlags[] = {
|
|
__second( PCNET1, DF_CORE ),
|
|
|
|
__second( XENIXCORE, DF_CORE | DF_MIXEDCASEPW | DF_MIXEDCASE ),
|
|
|
|
__second( MSNET103, DF_CORE | DF_OLDRAWIO | DF_LOCKREAD | DF_EXTENDNEGOT ),
|
|
|
|
__second( LANMAN10, DF_CORE | DF_NEWRAWIO | DF_LOCKREAD | DF_EXTENDNEGOT |
|
|
DF_LANMAN10 ),
|
|
|
|
__second( WFW10, DF_CORE | DF_NEWRAWIO | DF_LOCKREAD | DF_EXTENDNEGOT |
|
|
DF_LANMAN10 | DF_WFW),
|
|
|
|
__second( LANMAN12, DF_CORE | DF_NEWRAWIO | DF_LOCKREAD | DF_EXTENDNEGOT |
|
|
DF_LANMAN10 | DF_LANMAN20 |
|
|
DF_MIXEDCASE | DF_LONGNAME | DF_SUPPORTEA ),
|
|
|
|
__second( LANMAN21, DF_CORE | DF_NEWRAWIO | DF_LOCKREAD | DF_EXTENDNEGOT |
|
|
DF_LANMAN10 | DF_LANMAN20 |
|
|
DF_MIXEDCASE | DF_LONGNAME | DF_SUPPORTEA |
|
|
DF_LANMAN21),
|
|
|
|
__second( NTLANMAN, DF_CORE | DF_NEWRAWIO | DF_NTNEGOTIATE |
|
|
DF_MIXEDCASEPW | DF_LANMAN10 | DF_LANMAN20 |
|
|
DF_LANMAN21 | DF_MIXEDCASE | DF_LONGNAME |
|
|
DF_SUPPORTEA | DF_TIME_IS_UTC )
|
|
};
|
|
|
|
ULONG s_NumberOfDialects = sizeof(s_SmbDialects) / sizeof(s_SmbDialects[0]);
|
|
|
|
PBYTE s_pNegotiateSmb = NULL;
|
|
ULONG s_NegotiateSmbLength = 0;
|
|
|
|
PBYTE s_pEchoSmb = NULL;
|
|
BYTE s_EchoData[] = "JlJmIhClBsr";
|
|
|
|
MRXSMB_ECHO_PROCESSING_CONTEXT EchoProbeContext;
|
|
|
|
#define SMB_ECHO_COUNT (1)
|
|
|
|
// Number of ticks 100ns ticks in a day.
|
|
LARGE_INTEGER s_MaxTimeZoneBias;
|
|
|
|
extern NTSTATUS
|
|
GetLanmanSecurityParameters(
|
|
PSMBCE_SERVER pServer,
|
|
PRESP_NEGOTIATE pNegotiateResponse);
|
|
|
|
extern VOID
|
|
GetLanmanTimeBias(
|
|
PSMBCE_SERVER pServer,
|
|
PRESP_NEGOTIATE pNegotiateResponse);
|
|
|
|
// Number of 100 ns ticks in one minute
|
|
#define ONE_MINUTE_IN_TIME (60 * 1000 * 10000)
|
|
|
|
|
|
|
|
NTSTATUS
|
|
MRxIfsInitializeEchoProcessingContext()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds the echo SMB
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if construction of an ECHO smb was successful
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG DialectIndex;
|
|
|
|
PSMB_HEADER pSmbHeader = NULL;
|
|
PREQ_ECHO pReqEcho = NULL;
|
|
|
|
EchoProbeContext.EchoSmbLength = sizeof(SMB_HEADER) +
|
|
FIELD_OFFSET(REQ_ECHO,Buffer) +
|
|
sizeof(s_EchoData);
|
|
|
|
EchoProbeContext.pEchoSmb = (PBYTE)RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
EchoProbeContext.EchoSmbLength,
|
|
MRXSMB_ECHO_POOLTAG);
|
|
if (EchoProbeContext.pEchoSmb != NULL) {
|
|
pSmbHeader = (PSMB_HEADER)EchoProbeContext.pEchoSmb;
|
|
pReqEcho = (PREQ_ECHO)((PBYTE)EchoProbeContext.pEchoSmb + sizeof(SMB_HEADER));
|
|
|
|
// Fill in the header
|
|
RtlZeroMemory( pSmbHeader, sizeof( SMB_HEADER ) );
|
|
|
|
*(PULONG)(&pSmbHeader->Protocol) = (ULONG)SMB_HEADER_PROTOCOL;
|
|
|
|
// By default, paths in SMBs are marked as case insensitive and
|
|
// canonicalized.
|
|
pSmbHeader->Flags =
|
|
SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS;
|
|
|
|
// Get the flags2 field out of the SmbContext
|
|
SmbPutAlignedUshort( &pSmbHeader->Flags2,
|
|
(SMB_FLAGS2_KNOWS_LONG_NAMES |
|
|
SMB_FLAGS2_KNOWS_EAS |
|
|
SMB_FLAGS2_IS_LONG_NAME |
|
|
SMB_FLAGS2_NT_STATUS |
|
|
SMB_FLAGS2_UNICODE));
|
|
|
|
// Fill in the process id.
|
|
SmbPutUshort(&pSmbHeader->Pid, MRXSMB_PROCESS_ID );
|
|
SmbPutUshort(&pSmbHeader->Tid,0xffff); // Invalid TID
|
|
|
|
// Lastly, fill in the smb command code.
|
|
pSmbHeader->Command = (UCHAR) SMB_COM_ECHO;
|
|
|
|
pReqEcho->WordCount = 1;
|
|
|
|
RtlMoveMemory( pReqEcho->Buffer, s_EchoData, sizeof( s_EchoData ) );
|
|
|
|
SmbPutUshort(&pReqEcho->EchoCount, SMB_ECHO_COUNT);
|
|
SmbPutUshort(&pReqEcho->ByteCount, (USHORT) sizeof( s_EchoData ) );
|
|
|
|
EchoProbeContext.pEchoSmbMdl = RxAllocateMdl(
|
|
EchoProbeContext.pEchoSmb,
|
|
EchoProbeContext.EchoSmbLength);
|
|
if (EchoProbeContext.pEchoSmbMdl == NULL) {
|
|
RxFreePool(EchoProbeContext.pEchoSmb);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
MmBuildMdlForNonPagedPool(EchoProbeContext.pEchoSmbMdl);
|
|
EchoProbeContext.Interval.QuadPart = 5 * 1000 * 10000; // 5 seconds in 100 ns intervals
|
|
EchoProbeContext.Status = RxPostOneShotTimerRequest(
|
|
MRxIfsDeviceObject,
|
|
&EchoProbeContext.WorkItem,
|
|
SmbCeProbeServers,
|
|
&EchoProbeContext,
|
|
EchoProbeContext.Interval);
|
|
|
|
}
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxIfsTearDownEchoProcessingContext()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tears down the echo processing context
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if tear down was successful
|
|
|
|
--*/
|
|
{
|
|
if (EchoProbeContext.pEchoSmb != NULL) {
|
|
RxFreePool(EchoProbeContext.pEchoSmb);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
BuildNegotiateSmb(
|
|
PVOID *pSmbBufferPointer,
|
|
PULONG pSmbBufferLength)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds the negotiate SMB
|
|
|
|
Arguments:
|
|
|
|
pSmbBufferPointer - a placeholder for the smb buffer
|
|
|
|
pNegotiateSmbLength - the smb buffer size
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - implies that pServer is a valid instnace .
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG DialectIndex;
|
|
|
|
if (s_pNegotiateSmb == NULL)
|
|
{
|
|
PSMB_HEADER pSmbHeader = NULL;
|
|
PREQ_NEGOTIATE pReqNegotiate = NULL;
|
|
|
|
//
|
|
// Allocate memory for the SMB from Paged Pool
|
|
//
|
|
|
|
s_NegotiateSmbLength = sizeof(SMB_HEADER) +
|
|
FIELD_OFFSET(REQ_NEGOTIATE,Buffer) +
|
|
sizeof(s_DialectNames);
|
|
|
|
s_pNegotiateSmb = (PBYTE)RxAllocatePoolWithTag(
|
|
PagedPool,
|
|
s_NegotiateSmbLength,
|
|
MRXSMB_ADMIN_POOLTAG);
|
|
|
|
|
|
if (s_pNegotiateSmb != NULL)
|
|
{
|
|
|
|
pSmbHeader = (PSMB_HEADER)s_pNegotiateSmb;
|
|
|
|
pReqNegotiate = (PREQ_NEGOTIATE)(s_pNegotiateSmb + sizeof(SMB_HEADER));
|
|
|
|
//
|
|
// Fill in the SMB header. This contains 4 bytes 0xFF, 'SMB'
|
|
//
|
|
|
|
RtlZeroMemory( pSmbHeader, sizeof( SMB_HEADER ) );
|
|
|
|
*(PULONG)(&pSmbHeader->Protocol) = (ULONG)SMB_HEADER_PROTOCOL;
|
|
|
|
//
|
|
// Set the SMB Flags.
|
|
//
|
|
|
|
pSmbHeader->Flags =
|
|
SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS;
|
|
|
|
//
|
|
// Set the SMB Flags2
|
|
//
|
|
SmbPutAlignedUshort( &pSmbHeader->Flags2, 0);
|
|
|
|
|
|
//
|
|
// Fill in the process id and command code
|
|
//
|
|
//
|
|
|
|
SmbPutUshort( &pSmbHeader->Pid, MRXSMB_PROCESS_ID );
|
|
|
|
pSmbHeader->Command = (UCHAR) SMB_COM_NEGOTIATE;
|
|
|
|
pReqNegotiate->WordCount = 0;
|
|
|
|
//
|
|
// Copy the dialect strings into the SMB. In this example mini rdr
|
|
// the dialect support has been restricted to PC NETWORK PROGRAM 1.0,
|
|
// which is the simplest and most limited capability set.
|
|
//
|
|
|
|
RtlMoveMemory( pReqNegotiate->Buffer, s_DialectNames, sizeof( s_DialectNames ) );
|
|
|
|
SmbPutUshort( &pReqNegotiate->ByteCount, (USHORT) sizeof( s_DialectNames ) );
|
|
|
|
//
|
|
// Initialize the maximum time zone bias used in negotiate response parsing.
|
|
//
|
|
|
|
s_MaxTimeZoneBias.QuadPart = Int32x32To64(24*60*60,1000*10000);
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Paged pool could not be allocated for the SMB.
|
|
//
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*pSmbBufferLength = s_NegotiateSmbLength;
|
|
*pSmbBufferPointer = s_pNegotiateSmb;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ParseNegotiateResponse(
|
|
PSMBCE_SERVER pServer,
|
|
PUNICODE_STRING pDomainName,
|
|
PSMB_HEADER pSmbHeader,
|
|
ULONG BytesAvailable,
|
|
PULONG pBytesConsumed)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses the response from the server
|
|
|
|
Arguments:
|
|
|
|
pServer - the server instance
|
|
|
|
pDomainName - the domain name string to be extracted from the response
|
|
|
|
pSmbHeader - the response SMB
|
|
|
|
BytesAvailable - length of the response
|
|
|
|
pBytesTaken - response consumed
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the server call construction has been finalized.
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
Notes:
|
|
|
|
The SMB servers can speak a variety of dialects of the SMB protocol. The initial
|
|
negotiate response can come in one of three possible flavours. Either we get the
|
|
NT negotiate response SMB from a NT server or the extended response from DOS and
|
|
OS/2 servers or the CORE response from other servers.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
USHORT DialectIndex;
|
|
PRESP_NEGOTIATE pNegotiateResponse;
|
|
ULONG NegotiateSmbLength;
|
|
|
|
ASSERT( pSmbHeader != NULL );
|
|
|
|
pNegotiateResponse = (PRESP_NEGOTIATE) (pSmbHeader + 1);
|
|
NegotiateSmbLength = sizeof(SMB_HEADER);
|
|
|
|
DialectIndex = SmbGetUshort( &pNegotiateResponse->DialectIndex );
|
|
|
|
if (DialectIndex == (USHORT) -1) {
|
|
|
|
//
|
|
// means server cannot accept the dialog strings we sent
|
|
//
|
|
|
|
*pBytesConsumed = BytesAvailable;
|
|
return STATUS_REQUEST_NOT_ACCEPTED;
|
|
}
|
|
|
|
if (pNegotiateResponse->WordCount < 1 || DialectIndex > s_NumberOfDialects) {
|
|
*pBytesConsumed = BytesAvailable;
|
|
return( STATUS_INVALID_NETWORK_RESPONSE );
|
|
}
|
|
|
|
//
|
|
// set the domain name length to zero ( default initialization )
|
|
//
|
|
|
|
pDomainName->Length = 0;
|
|
|
|
//
|
|
// Fix up the dialect type and the corresponding dispatch vector.
|
|
//
|
|
|
|
pServer->Dialect = (SMB_DIALECT)DialectIndex;
|
|
pServer->DialectFlags = MRxSmbDialectFlags[DialectIndex];
|
|
pServer->pDispatch = &s_SmbServerDispatchVectors[s_SmbDialects[DialectIndex].DispatchVectorIndex];
|
|
|
|
//
|
|
// Parse the response based upon the type of negotiate response expected.
|
|
//
|
|
|
|
switch (s_SmbDialects[DialectIndex].NegotiateType) {
|
|
case SMB_EXTENDED_NEGOTIATE :
|
|
{
|
|
USHORT RawMode;
|
|
|
|
// DOS or OS2 server
|
|
if (pNegotiateResponse->WordCount != 13 &&
|
|
pNegotiateResponse->WordCount != 10 &&
|
|
pNegotiateResponse->WordCount != 8) {
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
} else {
|
|
NegotiateSmbLength += FIELD_OFFSET(RESP_NEGOTIATE,Buffer) +
|
|
SmbGetUshort(&pNegotiateResponse->ByteCount);
|
|
|
|
RawMode = SmbGetUshort( &pNegotiateResponse->RawMode );
|
|
pServer->Capabilities |= ((RawMode & 0x1) != 0 ? RAW_READ_CAPABILITY : 0);
|
|
pServer->Capabilities |= ((RawMode & 0x2) != 0 ? RAW_WRITE_CAPABILITY : 0);
|
|
|
|
if (pSmbHeader->Flags & SMB_FLAGS_LOCK_AND_READ_OK) {
|
|
pServer->DialectFlags |= DF_LOCKREAD;
|
|
}
|
|
|
|
pServer->EncryptPasswords = FALSE;
|
|
pServer->MaximumVCs = 1;
|
|
|
|
pServer->MaximumBufferSize = SmbGetUshort( &pNegotiateResponse->MaxBufferSize );
|
|
pServer->MaximumDiskFileReadBufferSize = pServer->MaximumBufferSize -
|
|
QuadAlign(
|
|
sizeof(SMB_HEADER) +
|
|
FIELD_OFFSET(
|
|
REQ_READ_ANDX,
|
|
Buffer[0]));
|
|
pServer->MaximumNonDiskFileReadBufferSize = pServer->MaximumDiskFileReadBufferSize;
|
|
|
|
pServer->MaximumRequests = SmbGetUshort( &pNegotiateResponse->MaxMpxCount );
|
|
pServer->MaximumVCs = SmbGetUshort( &pNegotiateResponse->MaxNumberVcs );
|
|
|
|
if (pNegotiateResponse->WordCount == 13) {
|
|
switch (pServer->Dialect) {
|
|
case LANMAN10_DIALECT:
|
|
case WFW10_DIALECT:
|
|
case LANMAN12_DIALECT:
|
|
case LANMAN21_DIALECT:
|
|
GetLanmanTimeBias( pServer,pNegotiateResponse );
|
|
break;
|
|
}
|
|
|
|
Status = GetLanmanSecurityParameters( pServer,pNegotiateResponse );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SMB_CORE_NEGOTIATE :
|
|
default :
|
|
{
|
|
pServer->SecurityMode = SECURITY_MODE_SHARE_LEVEL;
|
|
pServer->EncryptPasswords = FALSE;
|
|
pServer->MaximumBufferSize = 0;
|
|
pServer->MaximumRequests = 1;
|
|
pServer->MaximumVCs = 1;
|
|
pServer->SessionKey = 0;
|
|
|
|
NegotiateSmbLength = BytesAvailable;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// Check to make sure that the time zone bias isn't more than +-24
|
|
// hours.
|
|
//
|
|
if ((pServer->TimeZoneBias.QuadPart > s_MaxTimeZoneBias.QuadPart) ||
|
|
(-pServer->TimeZoneBias.QuadPart > s_MaxTimeZoneBias.QuadPart)) {
|
|
|
|
// Set the bias to 0 - assume local time zone.
|
|
pServer->TimeZoneBias.LowPart = pServer->TimeZoneBias.HighPart = 0;
|
|
}
|
|
|
|
// Do not allow negotiated buffersize to exceed the size of a USHORT.
|
|
// Remove 4096 bytes to avoid overrun and make it easier to handle
|
|
// than 0xffff
|
|
|
|
pServer->MaximumBufferSize =
|
|
(pServer->MaximumBufferSize < 0x00010000) ? pServer->MaximumBufferSize :
|
|
0x00010000 - 4096;
|
|
|
|
*pBytesConsumed = NegotiateSmbLength;
|
|
} else {
|
|
*pBytesConsumed = BytesAvailable;
|
|
}
|
|
|
|
if ((pServer->Capabilities & DF_NTNEGOTIATE)!=0) {
|
|
|
|
InterlockedIncrement(&MRxIfsStatistics.LanmanNtConnects);
|
|
|
|
} else if ((pServer->Capabilities & DF_LANMAN21)!=0) {
|
|
|
|
InterlockedIncrement(&MRxIfsStatistics.Lanman21Connects);
|
|
|
|
} else if ((pServer->Capabilities & DF_LANMAN20)!=0) {
|
|
|
|
InterlockedIncrement(&MRxIfsStatistics.Lanman20Connects);
|
|
|
|
} else {
|
|
|
|
InterlockedIncrement(&MRxIfsStatistics.CoreConnects);
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
GetLanmanSecurityParameters(
|
|
PSMBCE_SERVER pServer,
|
|
PRESP_NEGOTIATE pNegotiateResponse)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine extracts the security parameters from a LANMAN server
|
|
|
|
Arguments:
|
|
|
|
pServer - the server
|
|
|
|
pNtNegotiateResponse - the response
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - implies that pServer is a valid instnace .
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
--*/
|
|
{
|
|
|
|
USHORT i;
|
|
USHORT SecurityMode;
|
|
|
|
pServer->SessionKey = SmbGetUlong( &pNegotiateResponse->SessionKey );
|
|
|
|
SecurityMode = SmbGetUshort( &pNegotiateResponse->SecurityMode );
|
|
pServer->SecurityMode = (((SecurityMode & 1) != 0)
|
|
? SECURITY_MODE_USER_LEVEL
|
|
: SECURITY_MODE_SHARE_LEVEL);
|
|
pServer->EncryptPasswords = ((SecurityMode & 2) != 0);
|
|
|
|
if (pServer->EncryptPasswords) {
|
|
if (pServer->Dialect == LANMAN21_DIALECT) {
|
|
pServer->EncryptionKeyLength = SmbGetUshort(&pNegotiateResponse->EncryptionKeyLength);
|
|
} else {
|
|
pServer->EncryptionKeyLength = SmbGetUshort(&pNegotiateResponse->ByteCount);
|
|
}
|
|
|
|
if (pServer->EncryptionKeyLength != 0) {
|
|
if (pServer->EncryptionKeyLength > CRYPT_TXT_LEN) {
|
|
return( STATUS_INVALID_NETWORK_RESPONSE );
|
|
}
|
|
|
|
for (i = 0; i < pServer->EncryptionKeyLength; i++) {
|
|
pServer->EncryptionKey[i] = pNegotiateResponse->Buffer[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
LARGE_INTEGER
|
|
ConvertSmbTimeToTime (
|
|
IN SMB_TIME Time,
|
|
IN SMB_DATE Date
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts an SMB time to an NT time structure.
|
|
|
|
Arguments:
|
|
|
|
IN SMB_TIME Time - Supplies the time of day to convert
|
|
IN SMB_DATE Date - Supplies the day of the year to convert
|
|
IN PSERVERLISTENTRY Server - if supplied, supplies the server for tz bias.
|
|
|
|
Return Value:
|
|
|
|
LARGE_INTEGER - Time structure describing input time.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
TIME_FIELDS TimeFields;
|
|
LARGE_INTEGER OutputTime;
|
|
|
|
//
|
|
// This routine cannot be paged because it is called from both the
|
|
// RdrFileDiscardableSection and the RdrVCDiscardableSection.
|
|
//
|
|
|
|
if (SmbIsTimeZero(&Date) && SmbIsTimeZero(&Time)) {
|
|
OutputTime.LowPart = OutputTime.HighPart = 0;
|
|
} else {
|
|
TimeFields.Year = Date.Struct.Year + (USHORT )1980;
|
|
TimeFields.Month = Date.Struct.Month;
|
|
TimeFields.Day = Date.Struct.Day;
|
|
|
|
TimeFields.Hour = Time.Struct.Hours;
|
|
TimeFields.Minute = Time.Struct.Minutes;
|
|
TimeFields.Second = Time.Struct.TwoSeconds*(USHORT )2;
|
|
TimeFields.Milliseconds = 0;
|
|
|
|
//
|
|
// Make sure that the times specified in the SMB are reasonable
|
|
// before converting them.
|
|
//
|
|
|
|
if (TimeFields.Year < 1601) {
|
|
TimeFields.Year = 1601;
|
|
}
|
|
|
|
if (TimeFields.Month > 12) {
|
|
TimeFields.Month = 12;
|
|
}
|
|
|
|
if (TimeFields.Hour >= 24) {
|
|
TimeFields.Hour = 23;
|
|
}
|
|
if (TimeFields.Minute >= 60) {
|
|
TimeFields.Minute = 59;
|
|
}
|
|
if (TimeFields.Second >= 60) {
|
|
TimeFields.Second = 59;
|
|
|
|
}
|
|
|
|
if (!RtlTimeFieldsToTime(&TimeFields, &OutputTime)) {
|
|
OutputTime.HighPart = 0;
|
|
OutputTime.LowPart = 0;
|
|
|
|
return OutputTime;
|
|
}
|
|
|
|
ExLocalTimeToSystemTime(&OutputTime, &OutputTime);
|
|
|
|
}
|
|
|
|
return OutputTime;
|
|
|
|
}
|
|
|
|
VOID
|
|
GetLanmanTimeBias(
|
|
PSMBCE_SERVER pServer,
|
|
PRESP_NEGOTIATE pNegotiateResponse)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine extracts the time bias from a Lanman server
|
|
|
|
Arguments:
|
|
|
|
pServer - the server
|
|
|
|
pNtNegotiateResponse - the response
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - implies that pServer is a valid instnace .
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
--*/
|
|
{
|
|
// If this is a LM 1.0 or 2.0 server (ie a non NT server), we
|
|
// remember the timezone and bias our time based on this value.
|
|
//
|
|
// The redirector assumes that all times from these servers are
|
|
// local time for the server, and converts them to local time
|
|
// using this bias. It then tells the user the local time for
|
|
// the file on the server.
|
|
|
|
LARGE_INTEGER Workspace, ServerTime, CurrentTime;
|
|
BOOLEAN Negated = FALSE;
|
|
SMB_TIME SmbServerTime;
|
|
SMB_DATE SmbServerDate;
|
|
|
|
SmbMoveTime(&SmbServerTime, &pNegotiateResponse->ServerTime);
|
|
|
|
SmbMoveDate(&SmbServerDate, &pNegotiateResponse->ServerDate);
|
|
|
|
ServerTime = ConvertSmbTimeToTime(SmbServerTime, SmbServerDate);
|
|
|
|
KeQuerySystemTime(&CurrentTime);
|
|
|
|
Workspace.QuadPart = CurrentTime.QuadPart - ServerTime.QuadPart;
|
|
|
|
if ( Workspace.HighPart < 0) {
|
|
// avoid using -ve large integers to routines that accept only unsigned
|
|
Workspace.QuadPart = -Workspace.QuadPart;
|
|
Negated = TRUE;
|
|
}
|
|
|
|
//
|
|
// Workspace has the exact difference in 100ns intervals
|
|
// between the server and redirector times. To remove the minor
|
|
// difference between the time settings on the two machines we
|
|
// round the Bias to the nearest 30 minutes.
|
|
//
|
|
// Calculate ((exact bias+15minutes)/30minutes)* 30minutes
|
|
// then convert back to the bias time.
|
|
//
|
|
|
|
Workspace.QuadPart += ((LONGLONG) ONE_MINUTE_IN_TIME) * 15;
|
|
|
|
// Workspace is now exact bias + 15 minutes in 100ns units
|
|
|
|
Workspace.QuadPart /= ((LONGLONG) ONE_MINUTE_IN_TIME) * 30;
|
|
|
|
pServer->TimeZoneBias.QuadPart = Workspace.QuadPart * ((LONGLONG) ONE_MINUTE_IN_TIME) * 30;
|
|
|
|
if ( Negated == TRUE ) {
|
|
pServer->TimeZoneBias.QuadPart = -pServer->TimeZoneBias.QuadPart;
|
|
}
|
|
}
|
|
|
|
|
|
|