2025-04-27 07:49:33 -04:00

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;
}
}