2658 lines
74 KiB
C
2658 lines
74 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
smbadmin.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains routines for processing the administrative SMBs:
|
|||
|
negotiate, session setup, tree connect, and logoff.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David Treadwell (davidtr) 30-Oct-1989
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
#define ENCRYPT_TEXT_LENGTH 20
|
|||
|
|
|||
|
VOID
|
|||
|
GetEncryptionKey (
|
|||
|
OUT CHAR EncryptionKey[MSV1_0_CHALLENGE_LENGTH]
|
|||
|
);
|
|||
|
|
|||
|
VOID SRVFASTCALL
|
|||
|
BlockingSessionSetupAndX (
|
|||
|
IN OUT PWORK_CONTEXT WorkContext
|
|||
|
);
|
|||
|
|
|||
|
VOID SRVFASTCALL
|
|||
|
BlockingSessionSetup (
|
|||
|
IN OUT PWORK_CONTEXT WorkContext
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// EncryptionKeyCount is a monotonically increasing count of the number
|
|||
|
// of times GetEncryptionKey has been called. This number is added to
|
|||
|
// the system time to ensure that we do not use the same seed twice in
|
|||
|
// generating a random challenge.
|
|||
|
//
|
|||
|
|
|||
|
STATIC
|
|||
|
ULONG EncryptionKeyCount = 0;
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text( PAGE, SrvSmbNegotiate )
|
|||
|
#pragma alloc_text( PAGE, SrvSmbProcessExit )
|
|||
|
#pragma alloc_text( PAGE, SrvSmbSessionSetupAndX )
|
|||
|
#pragma alloc_text( PAGE, BlockingSessionSetupAndX )
|
|||
|
#pragma alloc_text( PAGE, SrvSmbSessionSetup )
|
|||
|
#pragma alloc_text( PAGE, BlockingSessionSetup )
|
|||
|
#pragma alloc_text( PAGE, SrvSmbLogoffAndX )
|
|||
|
#pragma alloc_text( PAGE, GetEncryptionKey )
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
SMB_PROCESSOR_RETURN_TYPE
|
|||
|
SrvSmbNegotiate (
|
|||
|
SMB_PROCESSOR_PARAMETERS
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes a negotiate 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_NEGOTIATE request;
|
|||
|
PRESP_NT_NEGOTIATE ntResponse;
|
|||
|
PRESP_NEGOTIATE response;
|
|||
|
PRESP_OLD_NEGOTIATE respOldNegotiate;
|
|||
|
PCONNECTION connection;
|
|||
|
PENDPOINT endpoint;
|
|||
|
PPAGED_CONNECTION pagedConnection;
|
|||
|
USHORT byteCount;
|
|||
|
|
|||
|
PSZ s;
|
|||
|
SMB_DIALECT bestDialect, serverDialect, firstDialect;
|
|||
|
USHORT consumerDialectChosen, consumerDialect;
|
|||
|
LARGE_INTEGER serverTime;
|
|||
|
SMB_DATE date;
|
|||
|
SMB_TIME time;
|
|||
|
ULONG capabilities;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN1) {
|
|||
|
SrvPrint2( "Negotiate request header at 0x%lx, "
|
|||
|
"response header at 0x%lx\n",
|
|||
|
WorkContext->RequestHeader, WorkContext->ResponseHeader );
|
|||
|
SrvPrint2( "Negotiate request parameters at 0x%lx, "
|
|||
|
"response parameters at 0x%lx\n",
|
|||
|
WorkContext->RequestParameters,
|
|||
|
WorkContext->ResponseParameters );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set up input and output buffers for parameters.
|
|||
|
//
|
|||
|
|
|||
|
request = (PREQ_NEGOTIATE)WorkContext->RequestParameters;
|
|||
|
response = (PRESP_NEGOTIATE)WorkContext->ResponseParameters;
|
|||
|
ntResponse = (PRESP_NT_NEGOTIATE)WorkContext->ResponseParameters;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that this is the first negotiate command sent.
|
|||
|
// SrvStartListen() sets the dialect to illegal, so if it has changed
|
|||
|
// then a negotiate SMB has already been sent.
|
|||
|
//
|
|||
|
|
|||
|
connection = WorkContext->Connection;
|
|||
|
pagedConnection = connection->PagedConnection;
|
|||
|
endpoint = connection->Endpoint;
|
|||
|
if ( connection->SmbDialect != SmbDialectIllegal ) {
|
|||
|
|
|||
|
IF_DEBUG(SMB_ERRORS) {
|
|||
|
SrvPrint0( "SrvSmbNegotiate: Command already sent.\n" );
|
|||
|
}
|
|||
|
|
|||
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|||
|
return SmbStatusSendResponse;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find out which (if any) of the sent dialect strings matches the
|
|||
|
// dialect strings known by this server. The ByteCount is verified
|
|||
|
// to be legitimate in SrvProcessSmb, so it is not possible to walk
|
|||
|
// off the end of the SMB here.
|
|||
|
//
|
|||
|
|
|||
|
bestDialect = SmbDialectIllegal;
|
|||
|
consumerDialectChosen = (USHORT)0xFFFF;
|
|||
|
|
|||
|
if( endpoint->IsPrimaryName ) {
|
|||
|
firstDialect = FIRST_DIALECT;
|
|||
|
} else {
|
|||
|
firstDialect = FIRST_DIALECT_EMULATED;
|
|||
|
}
|
|||
|
|
|||
|
for ( s = (PSZ)request->Buffer, consumerDialect = 0;
|
|||
|
s < SmbGetUshort( &request->ByteCount ) + (PSZ)request->Buffer;
|
|||
|
s += strlen(s) + 1, consumerDialect++ ) {
|
|||
|
|
|||
|
if ( *s++ != SMB_FORMAT_DIALECT ) {
|
|||
|
|
|||
|
IF_DEBUG(SMB_ERRORS) {
|
|||
|
SrvPrint0( "SrvSmbNegotiate: Invalid dialect format code.\n" );
|
|||
|
}
|
|||
|
|
|||
|
SrvLogInvalidSmb( WorkContext );
|
|||
|
|
|||
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|||
|
return SmbStatusSendResponse;
|
|||
|
}
|
|||
|
|
|||
|
for ( serverDialect = firstDialect;
|
|||
|
serverDialect < bestDialect;
|
|||
|
serverDialect++ ) {
|
|||
|
|
|||
|
if ( !strcmp( s, StrDialects[serverDialect] ) ) {
|
|||
|
IF_SMB_DEBUG(ADMIN2) {
|
|||
|
SrvPrint2( "Matched: %s and %s\n",
|
|||
|
StrDialects[serverDialect], s );
|
|||
|
}
|
|||
|
|
|||
|
bestDialect = serverDialect;
|
|||
|
consumerDialectChosen = consumerDialect;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
connection->SmbDialect = bestDialect;
|
|||
|
|
|||
|
if( bestDialect <= SmbDialectNtLanMan ) {
|
|||
|
connection->IpxDropDuplicateCount = MIN_IPXDROPDUP;
|
|||
|
} else {
|
|||
|
connection->IpxDropDuplicateCount = MAX_IPXDROPDUP;
|
|||
|
}
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN1) {
|
|||
|
SrvPrint2( "Choosing dialect #%ld, string = %s\n",
|
|||
|
consumerDialectChosen, StrDialects[bestDialect] );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Determine the current system time on the server. We use this
|
|||
|
// to determine the time zone of the server and to tell the client
|
|||
|
// the current time of day on the server.
|
|||
|
//
|
|||
|
|
|||
|
KeQuerySystemTime( &serverTime );
|
|||
|
|
|||
|
//
|
|||
|
// If the consumer only knows the core protocol, return short (old)
|
|||
|
// form of the negotiate response. Also, if no dialect is acceptable,
|
|||
|
// return 0xFFFF as the selected dialect.
|
|||
|
//
|
|||
|
|
|||
|
if ( bestDialect == SmbDialectPcNet10 ||
|
|||
|
consumerDialectChosen == (USHORT)0xFFFF ) {
|
|||
|
|
|||
|
respOldNegotiate = (PRESP_OLD_NEGOTIATE)response;
|
|||
|
respOldNegotiate->WordCount = 1;
|
|||
|
SmbPutUshort( &respOldNegotiate->DialectIndex, consumerDialectChosen );
|
|||
|
SmbPutUshort( &respOldNegotiate->ByteCount, 0 );
|
|||
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|||
|
respOldNegotiate,
|
|||
|
RESP_OLD_NEGOTIATE,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
else if ( bestDialect > SmbDialectNtLanMan ) {
|
|||
|
|
|||
|
//
|
|||
|
// Send the OS/2 LAN Man SMB response.
|
|||
|
//
|
|||
|
|
|||
|
WorkContext->ResponseHeader->Flags =
|
|||
|
(UCHAR)(WorkContext->RequestHeader->Flags | SMB_FLAGS_LOCK_AND_READ_OK);
|
|||
|
|
|||
|
response->WordCount = 13;
|
|||
|
SmbPutUshort( &response->DialectIndex, consumerDialectChosen );
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that we're user-level security and that we
|
|||
|
// want encrypted passwords.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUshort(
|
|||
|
&response->SecurityMode,
|
|||
|
NEGOTIATE_USER_SECURITY | NEGOTIATE_ENCRYPT_PASSWORDS
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Get an encryption key for this connection.
|
|||
|
//
|
|||
|
|
|||
|
GetEncryptionKey( pagedConnection->EncryptionKey );
|
|||
|
|
|||
|
SmbPutUshort( &response->EncryptionKeyLength, MSV1_0_CHALLENGE_LENGTH );
|
|||
|
SmbPutUshort( &response->Reserved, 0 );
|
|||
|
byteCount = MSV1_0_CHALLENGE_LENGTH;
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
response->Buffer,
|
|||
|
pagedConnection->EncryptionKey,
|
|||
|
MSV1_0_CHALLENGE_LENGTH
|
|||
|
);
|
|||
|
|
|||
|
if ( endpoint->IsConnectionless ) {
|
|||
|
|
|||
|
ULONG adapterNumber;
|
|||
|
ULONG maxBufferSize;
|
|||
|
|
|||
|
//
|
|||
|
// Our server 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;
|
|||
|
|
|||
|
maxBufferSize = GetIpxMaxBufferSize(
|
|||
|
endpoint,
|
|||
|
adapterNumber,
|
|||
|
SrvReceiveBufferLength
|
|||
|
);
|
|||
|
|
|||
|
SmbPutUshort(
|
|||
|
&response->MaxBufferSize,
|
|||
|
(USHORT)maxBufferSize
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
SmbPutUshort(
|
|||
|
&response->MaxBufferSize,
|
|||
|
(USHORT)SrvReceiveBufferLength
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
SmbPutUshort( &response->MaxMpxCount, SrvMaxMpxCount );
|
|||
|
SmbPutUshort( &response->MaxNumberVcs, (USHORT)SrvMaxNumberVcs );
|
|||
|
SmbPutUlong( &response->SessionKey, 0 );
|
|||
|
|
|||
|
//
|
|||
|
// If this is an MS-NET 1.03 client or before, then tell him that we
|
|||
|
// don't support raw writes. MS-NET 1.03 does different things with
|
|||
|
// raw writes that are more trouble than they're worth, and since
|
|||
|
// raw is simply a performance issue, we don't support it.
|
|||
|
//
|
|||
|
|
|||
|
if ( bestDialect >= SmbDialectMsNet103 ) {
|
|||
|
|
|||
|
SmbPutUshort(
|
|||
|
&response->RawMode,
|
|||
|
(USHORT)(SrvEnableRawMode ?
|
|||
|
NEGOTIATE_READ_RAW_SUPPORTED :
|
|||
|
0)
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
SmbPutUshort(
|
|||
|
&response->RawMode,
|
|||
|
(USHORT)(SrvEnableRawMode ?
|
|||
|
NEGOTIATE_READ_RAW_SUPPORTED |
|
|||
|
NEGOTIATE_WRITE_RAW_SUPPORTED :
|
|||
|
0)
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
SmbPutUlong( &response->SessionKey, 0 );
|
|||
|
|
|||
|
SrvTimeToDosTime( &serverTime, &date, &time );
|
|||
|
|
|||
|
SmbPutDate( &response->ServerDate, date );
|
|||
|
SmbPutTime( &response->ServerTime, time );
|
|||
|
|
|||
|
//
|
|||
|
// Get time zone bias. We compute this during session
|
|||
|
// setup rather than once during server startup because
|
|||
|
// we might switch from daylight time to standard time
|
|||
|
// or vice versa during normal server operation.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUshort( &response->ServerTimeZone,
|
|||
|
SrvGetOs2TimeZone(&serverTime) );
|
|||
|
|
|||
|
if ( bestDialect == SmbDialectLanMan21 ||
|
|||
|
bestDialect == SmbDialectDosLanMan21 ) {
|
|||
|
|
|||
|
//
|
|||
|
// Append the domain to the SMB.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
response->Buffer + byteCount,
|
|||
|
endpoint->OemDomainName.Buffer,
|
|||
|
endpoint->OemDomainName.Length + sizeof(CHAR)
|
|||
|
);
|
|||
|
|
|||
|
byteCount += endpoint->OemDomainName.Length + sizeof(CHAR);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
SmbPutUshort( &response->ByteCount, byteCount );
|
|||
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|||
|
response,
|
|||
|
RESP_NEGOTIATE,
|
|||
|
byteCount
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// NT or better protocol has been negotiated.
|
|||
|
//
|
|||
|
|
|||
|
ntResponse->WordCount = 17;
|
|||
|
SmbPutUshort( &ntResponse->DialectIndex, consumerDialectChosen );
|
|||
|
|
|||
|
// !!! This says that we don't want encrypted passwords.
|
|||
|
|
|||
|
SmbPutUshort( &ntResponse->MaxMpxCount, SrvMaxMpxCount );
|
|||
|
SmbPutUshort( &ntResponse->MaxNumberVcs, (USHORT)SrvMaxNumberVcs );
|
|||
|
SmbPutUlong( &ntResponse->MaxRawSize, 64 * 1024 ); // !!!
|
|||
|
SmbPutUlong( &ntResponse->SessionKey, 0 );
|
|||
|
|
|||
|
capabilities = CAP_RAW_MODE |
|
|||
|
CAP_UNICODE |
|
|||
|
CAP_LARGE_FILES |
|
|||
|
CAP_NT_SMBS |
|
|||
|
CAP_NT_FIND |
|
|||
|
CAP_RPC_REMOTE_APIS |
|
|||
|
CAP_NT_STATUS |
|
|||
|
CAP_LEVEL_II_OPLOCKS |
|
|||
|
CAP_LOCK_AND_READ;
|
|||
|
|
|||
|
//
|
|||
|
// If we're supporting Dfs operations, let the client know about it.
|
|||
|
//
|
|||
|
if( SrvDfsFastIoDeviceControl ) {
|
|||
|
capabilities |= CAP_DFS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( endpoint->IsConnectionless ) {
|
|||
|
|
|||
|
ULONG adapterNumber;
|
|||
|
ULONG maxBufferSize;
|
|||
|
|
|||
|
capabilities |= CAP_MPX_MODE;
|
|||
|
|
|||
|
//
|
|||
|
// Our server 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;
|
|||
|
|
|||
|
maxBufferSize = GetIpxMaxBufferSize(
|
|||
|
endpoint,
|
|||
|
adapterNumber,
|
|||
|
SrvReceiveBufferLength
|
|||
|
);
|
|||
|
|
|||
|
SmbPutUlong(
|
|||
|
&ntResponse->MaxBufferSize,
|
|||
|
maxBufferSize
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
SmbPutUlong(
|
|||
|
&ntResponse->MaxBufferSize,
|
|||
|
SrvReceiveBufferLength
|
|||
|
);
|
|||
|
|
|||
|
if( SrvSupportsBulkTransfer ) {
|
|||
|
|
|||
|
capabilities |= CAP_BULK_TRANSFER;
|
|||
|
|
|||
|
if( SrvSupportsCompression ) {
|
|||
|
capabilities |= CAP_COMPRESSED_DATA;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
capabilities |= CAP_LARGE_READX;
|
|||
|
}
|
|||
|
|
|||
|
SmbPutUlong( &ntResponse->Capabilities, capabilities );
|
|||
|
|
|||
|
//
|
|||
|
// Stick the servers system time and timezone in the negotiate
|
|||
|
// response.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUlong( &ntResponse->SystemTimeLow, serverTime.LowPart );
|
|||
|
SmbPutUlong( &ntResponse->SystemTimeHigh, serverTime.HighPart );
|
|||
|
|
|||
|
SmbPutUshort( &ntResponse->ServerTimeZone,
|
|||
|
SrvGetOs2TimeZone(&serverTime) );
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that we're user-level security and that we
|
|||
|
// want encrypted passwords.
|
|||
|
//
|
|||
|
|
|||
|
ntResponse->SecurityMode =
|
|||
|
NEGOTIATE_USER_SECURITY | NEGOTIATE_ENCRYPT_PASSWORDS;
|
|||
|
|
|||
|
//
|
|||
|
// Get an encryption key for this connection.
|
|||
|
//
|
|||
|
|
|||
|
GetEncryptionKey( pagedConnection->EncryptionKey );
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
ntResponse->Buffer,
|
|||
|
pagedConnection->EncryptionKey,
|
|||
|
MSV1_0_CHALLENGE_LENGTH
|
|||
|
);
|
|||
|
|
|||
|
ASSERT ( MSV1_0_CHALLENGE_LENGTH <= 0xff ) ;
|
|||
|
|
|||
|
ntResponse->EncryptionKeyLength = MSV1_0_CHALLENGE_LENGTH;
|
|||
|
|
|||
|
byteCount = MSV1_0_CHALLENGE_LENGTH;
|
|||
|
|
|||
|
{
|
|||
|
USHORT domainLength;
|
|||
|
PWCH buffer = (PWCHAR)( ntResponse->Buffer+byteCount );
|
|||
|
PWCH ptr;
|
|||
|
|
|||
|
//
|
|||
|
// append either the Lanman domain or the Kerberos
|
|||
|
// domain.
|
|||
|
//
|
|||
|
|
|||
|
if((ptr = KerberosRealm.Buffer)
|
|||
|
&&
|
|||
|
(bestDialect <= SmbDialectCairo)
|
|||
|
)
|
|||
|
{
|
|||
|
domainLength = KerberosRealm.MaximumLength;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
domainLength = endpoint->DomainName.Length +
|
|||
|
sizeof(UNICODE_NULL);
|
|||
|
ptr = endpoint->DomainName.Buffer;
|
|||
|
}
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
buffer,
|
|||
|
ptr,
|
|||
|
domainLength
|
|||
|
);
|
|||
|
|
|||
|
byteCount += domainLength;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
SmbPutUshort( &ntResponse->ByteCount, byteCount );
|
|||
|
|
|||
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|||
|
ntResponse,
|
|||
|
RESP_NT_NEGOTIATE,
|
|||
|
byteCount
|
|||
|
);
|
|||
|
|
|||
|
} // else (NT protocol has been negotiated).
|
|||
|
|
|||
|
IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbNegotiate complete.\n" );
|
|||
|
return SmbStatusSendResponse;
|
|||
|
|
|||
|
} // SrvSmbNegotiate
|
|||
|
|
|||
|
|
|||
|
SMB_PROCESSOR_RETURN_TYPE
|
|||
|
SrvSmbProcessExit (
|
|||
|
SMB_PROCESSOR_PARAMETERS
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes a Process Exit 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_PROCESS_EXIT request;
|
|||
|
PRESP_PROCESS_EXIT response;
|
|||
|
|
|||
|
PSESSION session;
|
|||
|
USHORT pid;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN1) {
|
|||
|
SrvPrint2( "Process exit request header at 0x%lx, "
|
|||
|
"response header at 0x%lx\n",
|
|||
|
WorkContext->RequestHeader,
|
|||
|
WorkContext->ResponseHeader );
|
|||
|
SrvPrint2( "Process exit request parameters at 0x%lx, "
|
|||
|
"response parameters at 0x%lx\n",
|
|||
|
WorkContext->RequestParameters,
|
|||
|
WorkContext->ResponseParameters );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set up parameters.
|
|||
|
//
|
|||
|
|
|||
|
request = (PREQ_PROCESS_EXIT)(WorkContext->RequestParameters);
|
|||
|
response = (PRESP_PROCESS_EXIT)(WorkContext->ResponseParameters);
|
|||
|
|
|||
|
//
|
|||
|
// If a session block has not already been assigned to the current
|
|||
|
// work context, verify the UID. If verified, the address of the
|
|||
|
// session block corresponding to this user is stored in the
|
|||
|
// WorkContext block and the session block is referenced.
|
|||
|
//
|
|||
|
|
|||
|
session = SrvVerifyUid(
|
|||
|
WorkContext,
|
|||
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )
|
|||
|
);
|
|||
|
|
|||
|
if ( session == NULL ) {
|
|||
|
|
|||
|
IF_DEBUG(SMB_ERRORS) {
|
|||
|
SrvPrint1( "SrvSmbProcessExit: Invalid UID: 0x%lx\n",
|
|||
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ) );
|
|||
|
}
|
|||
|
|
|||
|
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID );
|
|||
|
return SmbStatusSendResponse;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Close all files with the same PID as in the header for this request.
|
|||
|
//
|
|||
|
|
|||
|
pid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid );
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN1) SrvPrint1( "Closing files with PID = %lx\n", pid );
|
|||
|
|
|||
|
SrvCloseRfcbsOnSessionOrPid( session, &pid );
|
|||
|
|
|||
|
//
|
|||
|
// Close all searches with the same PID as in the header for this request.
|
|||
|
//
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN1) SrvPrint1( "Closing searches with PID = %lx\n", pid );
|
|||
|
|
|||
|
SrvCloseSearches(
|
|||
|
session->Connection,
|
|||
|
(PSEARCH_FILTER_ROUTINE)SrvSearchOnPid,
|
|||
|
(PVOID) pid,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Close any cached directories for this client
|
|||
|
//
|
|||
|
SrvCloseCachedDirectoryEntries( session->Connection );
|
|||
|
|
|||
|
//
|
|||
|
// Build the response SMB.
|
|||
|
//
|
|||
|
|
|||
|
response->WordCount = 0;
|
|||
|
SmbPutUshort( &response->ByteCount, 0 );
|
|||
|
|
|||
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|||
|
response,
|
|||
|
RESP_PROCESS_EXIT,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
return SmbStatusSendResponse;
|
|||
|
|
|||
|
} // SrvSmbProcessExit
|
|||
|
|
|||
|
|
|||
|
SMB_PROCESSOR_RETURN_TYPE
|
|||
|
SrvSmbSessionSetupAndX(
|
|||
|
SMB_PROCESSOR_PARAMETERS
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes a session setup 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
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// This SMB must be processed in a blocking thread.
|
|||
|
//
|
|||
|
|
|||
|
WorkContext->FspRestartRoutine = BlockingSessionSetupAndX;
|
|||
|
SrvQueueWorkToBlockingThread( WorkContext );
|
|||
|
return SmbStatusInProgress;
|
|||
|
|
|||
|
} // SrvSmbSessionSetupAndX
|
|||
|
|
|||
|
|
|||
|
VOID SRVFASTCALL
|
|||
|
BlockingSessionSetupAndX(
|
|||
|
IN OUT PWORK_CONTEXT WorkContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes a session setup 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
|
|||
|
|
|||
|
NOTE:
|
|||
|
|
|||
|
We have disabled CAP_KERBEROS_BLOB until we decide exactly
|
|||
|
how we want to do security negotiation. Since NT 4.0 does
|
|||
|
not ship kerberos, it is premature to turn it on now
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PREQ_SESSION_SETUP_ANDX request;
|
|||
|
PREQ_NT_SESSION_SETUP_ANDX ntRequest;
|
|||
|
PRESP_SESSION_SETUP_ANDX response;
|
|||
|
|
|||
|
NTSTATUS status;
|
|||
|
PSESSION session;
|
|||
|
PCONNECTION connection;
|
|||
|
PENDPOINT endpoint;
|
|||
|
PPAGED_CONNECTION pagedConnection;
|
|||
|
PTABLE_ENTRY entry;
|
|||
|
SHORT uidIndex;
|
|||
|
USHORT reqAndXOffset;
|
|||
|
UCHAR nextCommand;
|
|||
|
PSZ userName;
|
|||
|
UNICODE_STRING nameString;
|
|||
|
UNICODE_STRING domainString;
|
|||
|
PCHAR caseInsensitivePassword;
|
|||
|
CLONG caseInsensitivePasswordLength;
|
|||
|
PCHAR caseSensitivePassword;
|
|||
|
CLONG caseSensitivePasswordLength;
|
|||
|
USHORT action = 0;
|
|||
|
USHORT byteCount;
|
|||
|
USHORT nameLength;
|
|||
|
BOOLEAN locksHeld;
|
|||
|
BOOLEAN isUnicode, IsKerb;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// If the connection has closed (timed out), abort.
|
|||
|
//
|
|||
|
|
|||
|
connection = WorkContext->Connection;
|
|||
|
|
|||
|
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
|
|||
|
|
|||
|
IF_DEBUG(ERRORS) {
|
|||
|
SrvPrint0( "SrvSmbSessionSetupAndX: Connection closing\n" );
|
|||
|
}
|
|||
|
|
|||
|
SrvEndSmbProcessing( WorkContext, SmbStatusNoResponse );
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN1) {
|
|||
|
SrvPrint2( "Session setup request header at 0x%lx, "
|
|||
|
"response header at 0x%lx\n",
|
|||
|
WorkContext->RequestHeader, WorkContext->ResponseHeader );
|
|||
|
SrvPrint2( "Session setup request parameters at 0x%lx, "
|
|||
|
"response parameters at 0x%lx\n",
|
|||
|
WorkContext->RequestParameters,
|
|||
|
WorkContext->ResponseParameters );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize local variables for error cleanup.
|
|||
|
//
|
|||
|
|
|||
|
nameString.Buffer = NULL;
|
|||
|
domainString.Buffer = NULL;
|
|||
|
session = NULL;
|
|||
|
locksHeld = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Set up parameters.
|
|||
|
//
|
|||
|
|
|||
|
request = (PREQ_SESSION_SETUP_ANDX)(WorkContext->RequestParameters);
|
|||
|
ntRequest = (PREQ_NT_SESSION_SETUP_ANDX)(WorkContext->RequestParameters);
|
|||
|
response = (PRESP_SESSION_SETUP_ANDX)(WorkContext->ResponseParameters);
|
|||
|
|
|||
|
connection = WorkContext->Connection;
|
|||
|
pagedConnection = connection->PagedConnection;
|
|||
|
|
|||
|
//
|
|||
|
// First verify that the SMB format is correct.
|
|||
|
//
|
|||
|
|
|||
|
if ( (connection->SmbDialect <= SmbDialectNtLanMan &&
|
|||
|
request->WordCount != 13) ||
|
|||
|
(connection->SmbDialect > SmbDialectNtLanMan &&
|
|||
|
request->WordCount != 10 ) ||
|
|||
|
(connection->SmbDialect == SmbDialectIllegal ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// The SMB word count is invalid.
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(SMB_ERRORS) {
|
|||
|
|
|||
|
if ( connection->SmbDialect == SmbDialectIllegal ) {
|
|||
|
|
|||
|
SrvPrint1("BlockingSessionSetupAndX: Client %Z is using an "
|
|||
|
"illegal dialect.\n", &connection->OemClientMachineNameString );
|
|||
|
}
|
|||
|
}
|
|||
|
status = STATUS_INVALID_SMB;
|
|||
|
goto error_exit1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Convert the client name to unicode
|
|||
|
//
|
|||
|
|
|||
|
if ( pagedConnection->ClientMachineNameString.Length == 0 ) {
|
|||
|
|
|||
|
UNICODE_STRING clientMachineName;
|
|||
|
clientMachineName.Buffer = pagedConnection->ClientMachineName;
|
|||
|
clientMachineName.MaximumLength =
|
|||
|
(USHORT)(COMPUTER_NAME_LENGTH+1)*sizeof(WCHAR);
|
|||
|
|
|||
|
(VOID)RtlOemStringToUnicodeString(
|
|||
|
&clientMachineName,
|
|||
|
&connection->OemClientMachineNameString,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Add the double backslashes to the length
|
|||
|
//
|
|||
|
|
|||
|
pagedConnection->ClientMachineNameString.Length =
|
|||
|
(USHORT)(clientMachineName.Length + 2*sizeof(WCHAR));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is LanMan 2.1 or better, the session setup response may
|
|||
|
// be longer than the request. Allocate an extra SMB buffer. The
|
|||
|
// buffer is freed after we have finished sending the SMB response.
|
|||
|
//
|
|||
|
// !!! Try to be smarter before grabbing the extra buffer.
|
|||
|
//
|
|||
|
|
|||
|
if ( connection->SmbDialect <= SmbDialectDosLanMan21 &&
|
|||
|
!WorkContext->UsingExtraSmbBuffer) {
|
|||
|
|
|||
|
status = SrvAllocateExtraSmbBuffer( WorkContext );
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
response = (PRESP_SESSION_SETUP_ANDX)(WorkContext->ResponseParameters);
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
WorkContext->ResponseHeader,
|
|||
|
WorkContext->RequestHeader,
|
|||
|
sizeof( SMB_HEADER )
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the client capabilities
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if ( connection->SmbDialect <= SmbDialectNtLanMan ) {
|
|||
|
|
|||
|
connection->ClientCapabilities =
|
|||
|
SmbGetUlong( &ntRequest->Capabilities ) &
|
|||
|
( CAP_UNICODE |
|
|||
|
CAP_LARGE_FILES |
|
|||
|
CAP_NT_SMBS |
|
|||
|
CAP_NT_FIND |
|
|||
|
CAP_NT_STATUS |
|
|||
|
#ifdef CAP_KERBEROS_BLOB
|
|||
|
CAP_KERBEROS_BLOB |
|
|||
|
#endif
|
|||
|
CAP_LEVEL_II_OPLOCKS );
|
|||
|
if ( connection->ClientCapabilities & CAP_NT_SMBS ) {
|
|||
|
connection->ClientCapabilities |= CAP_NT_FIND;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the account name, and additional information from the SMB buffer.
|
|||
|
//
|
|||
|
|
|||
|
if ( connection->SmbDialect <= SmbDialectNtLanMan) {
|
|||
|
|
|||
|
//
|
|||
|
// The NT-NT SMB protocol passes both case sensitive (Unicode,
|
|||
|
// mixed case) and case insensitive (ANSI, uppercased) passwords.
|
|||
|
// Get pointers to them to pass to SrvValidateUser.
|
|||
|
//
|
|||
|
|
|||
|
caseInsensitivePasswordLength =
|
|||
|
(CLONG)SmbGetUshort( &ntRequest->CaseInsensitivePasswordLength );
|
|||
|
caseInsensitivePassword = (PCHAR)(ntRequest->Buffer);
|
|||
|
caseSensitivePasswordLength =
|
|||
|
(CLONG)SmbGetUshort( &ntRequest->CaseSensitivePasswordLength );
|
|||
|
caseSensitivePassword =
|
|||
|
caseInsensitivePassword + caseInsensitivePasswordLength;
|
|||
|
userName = (PSZ)(caseSensitivePassword + caseSensitivePasswordLength);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Downlevel clients do not pass the case sensitive password;
|
|||
|
// just get the case insensitive password and use NULL as the
|
|||
|
// case sensitive password. LSA will do the right thing with
|
|||
|
// it.
|
|||
|
//
|
|||
|
|
|||
|
caseInsensitivePasswordLength =
|
|||
|
(CLONG)SmbGetUshort( &request->PasswordLength );
|
|||
|
caseInsensitivePassword = (PCHAR)request->Buffer;
|
|||
|
caseSensitivePasswordLength = 0;
|
|||
|
caseSensitivePassword = NULL;
|
|||
|
userName = (PSZ)(request->Buffer + caseInsensitivePasswordLength);
|
|||
|
}
|
|||
|
|
|||
|
isUnicode = SMB_IS_UNICODE( WorkContext );
|
|||
|
if ( isUnicode ) {
|
|||
|
userName = ALIGN_SMB_WSTR( userName );
|
|||
|
}
|
|||
|
|
|||
|
nameLength = SrvGetStringLength(
|
|||
|
userName,
|
|||
|
END_OF_REQUEST_SMB( WorkContext ),
|
|||
|
isUnicode,
|
|||
|
FALSE // don't include null terminator
|
|||
|
);
|
|||
|
|
|||
|
if ( nameLength == (USHORT)-1 ) {
|
|||
|
status = STATUS_INVALID_SMB;
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
status = SrvMakeUnicodeString(
|
|||
|
isUnicode,
|
|||
|
&nameString,
|
|||
|
userName,
|
|||
|
&nameLength );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( status ) ) {
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If client information strings exists, extract the information
|
|||
|
// from the SMB buffer.
|
|||
|
//
|
|||
|
|
|||
|
if ( connection->SmbDialect <= SmbDialectDosLanMan21) {
|
|||
|
|
|||
|
PCHAR smbInformation;
|
|||
|
USHORT length;
|
|||
|
PWCH infoBuffer;
|
|||
|
|
|||
|
smbInformation = userName + nameLength +
|
|||
|
( isUnicode ? sizeof( WCHAR ) : 1 );
|
|||
|
|
|||
|
//
|
|||
|
// Now copy the strings to the allocated buffer.
|
|||
|
//
|
|||
|
|
|||
|
if ( isUnicode ) {
|
|||
|
smbInformation = ALIGN_SMB_WSTR( smbInformation );
|
|||
|
}
|
|||
|
|
|||
|
length = SrvGetStringLength(
|
|||
|
smbInformation,
|
|||
|
END_OF_REQUEST_SMB( WorkContext ),
|
|||
|
isUnicode,
|
|||
|
FALSE // don't include null terminator
|
|||
|
);
|
|||
|
|
|||
|
if ( length == (USHORT)-1) {
|
|||
|
status = STATUS_INVALID_SMB;
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// DOS clients send an empty domain name if they don't know
|
|||
|
// their domain name (e.g., during logon). OS/2 clients send
|
|||
|
// a name of "?". This confuses the LSA. Convert such a name
|
|||
|
// to an empty name.
|
|||
|
//
|
|||
|
|
|||
|
if ( isUnicode ) {
|
|||
|
if ( (length == sizeof(WCHAR)) &&
|
|||
|
(*(PWCH)smbInformation == '?') ) {
|
|||
|
length = 0;
|
|||
|
}
|
|||
|
} else {
|
|||
|
if ( (length == 1) && (*smbInformation == '?') ) {
|
|||
|
length = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
status = SrvMakeUnicodeString(
|
|||
|
isUnicode,
|
|||
|
&domainString,
|
|||
|
smbInformation,
|
|||
|
&length
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS( status ) ) {
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
smbInformation += length + ( isUnicode ? sizeof(WCHAR) : 1 );
|
|||
|
|
|||
|
//
|
|||
|
// Get the client type strings if we do not already have this
|
|||
|
// information.
|
|||
|
//
|
|||
|
|
|||
|
if ( connection->ClientOSType.Buffer == NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the size of the client LAN Man type and OS type
|
|||
|
// strings. and allocate a buffer large enough to store
|
|||
|
// them all.
|
|||
|
//
|
|||
|
|
|||
|
if ( connection->SmbDialect <= SmbDialectNtLanMan ) {
|
|||
|
length = (USHORT)( (PUCHAR)&ntRequest->ByteCount +
|
|||
|
sizeof( USHORT ) +
|
|||
|
SmbGetUshort( &ntRequest->ByteCount ) -
|
|||
|
smbInformation);
|
|||
|
} else {
|
|||
|
length = (USHORT)( (PUCHAR)&request->ByteCount +
|
|||
|
sizeof( USHORT ) +
|
|||
|
SmbGetUshort( &request->ByteCount ) -
|
|||
|
smbInformation);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the SMB buffer is ANSI, adjust the size of the buffer we
|
|||
|
// are allocating to Unicode size.
|
|||
|
//
|
|||
|
|
|||
|
if ( !isUnicode ) {
|
|||
|
length *= sizeof( WCHAR );
|
|||
|
}
|
|||
|
|
|||
|
infoBuffer = ALLOCATE_NONPAGED_POOL( length, BlockTypeDataBuffer );
|
|||
|
if ( infoBuffer == NULL ) {
|
|||
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
connection->ClientOSType.Buffer = (PWCH)infoBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Copy the client OS type to the new buffer.
|
|||
|
//
|
|||
|
|
|||
|
length = SrvGetString(
|
|||
|
&connection->ClientOSType,
|
|||
|
smbInformation,
|
|||
|
END_OF_REQUEST_SMB( WorkContext ),
|
|||
|
isUnicode
|
|||
|
);
|
|||
|
|
|||
|
if ( length == (USHORT)-1) {
|
|||
|
DEALLOCATE_NONPAGED_POOL( infoBuffer );
|
|||
|
status = STATUS_INVALID_SMB;
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
smbInformation += length;
|
|||
|
connection->ClientLanManType.Buffer = (PWCH)(
|
|||
|
(PCHAR)connection->ClientOSType.Buffer +
|
|||
|
connection->ClientOSType.MaximumLength);
|
|||
|
|
|||
|
//
|
|||
|
// Copy the client LAN Manager type to the new buffer.
|
|||
|
//
|
|||
|
|
|||
|
length = SrvGetString(
|
|||
|
&connection->ClientLanManType,
|
|||
|
smbInformation,
|
|||
|
END_OF_REQUEST_SMB( WorkContext ),
|
|||
|
isUnicode
|
|||
|
);
|
|||
|
|
|||
|
if ( length == (USHORT)-1) {
|
|||
|
DEALLOCATE_NONPAGED_POOL( infoBuffer );
|
|||
|
status = STATUS_INVALID_SMB;
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
domainString.Length = 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a Session block.
|
|||
|
//
|
|||
|
|
|||
|
SrvAllocateSession( &session, &nameString, &domainString );
|
|||
|
|
|||
|
if ( !isUnicode && domainString.Buffer != NULL ) {
|
|||
|
RtlFreeUnicodeString( &domainString );
|
|||
|
domainString.Buffer = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if ( session == NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// Unable to allocate a Session block. Return an error status.
|
|||
|
//
|
|||
|
|
|||
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|||
|
goto error_exit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If using uppercase pathnames, indicate in the session block. DOS
|
|||
|
// always uses uppercase paths.
|
|||
|
//
|
|||
|
|
|||
|
if ( (WorkContext->RequestHeader->Flags &
|
|||
|
SMB_FLAGS_CANONICALIZED_PATHS) != 0 ||
|
|||
|
IS_DOS_DIALECT( connection->SmbDialect ) ) {
|
|||
|
session->UsingUppercasePaths = TRUE;
|
|||
|
} else {
|
|||
|
session->UsingUppercasePaths = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Enter data from request SMB into the session block. If MaxMpx is 1
|
|||
|
// disable oplocks on this connection.
|
|||
|
//
|
|||
|
|
|||
|
endpoint = connection->Endpoint;
|
|||
|
if ( endpoint->IsConnectionless ) {
|
|||
|
|
|||
|
ULONG adapterNumber;
|
|||
|
|
|||
|
//
|
|||
|
// Our session max buffer size is the smaller of the
|
|||
|
// client buffer size and the ipx transport
|
|||
|
// indicated max packet size.
|
|||
|
//
|
|||
|
|
|||
|
adapterNumber =
|
|||
|
WorkContext->ClientAddress->DatagramOptions.LocalTarget.NicId;
|
|||
|
|
|||
|
session->MaxBufferSize =
|
|||
|
(USHORT)GetIpxMaxBufferSize(
|
|||
|
endpoint,
|
|||
|
adapterNumber,
|
|||
|
(ULONG)SmbGetUshort(&request->MaxBufferSize)
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
session->MaxBufferSize = SmbGetUshort( &request->MaxBufferSize );
|
|||
|
}
|
|||
|
|
|||
|
session->MaxMpxCount = SmbGetUshort( &request->MaxMpxCount );
|
|||
|
|
|||
|
if ( session->MaxMpxCount < 2 ) {
|
|||
|
connection->OplocksAlwaysDisabled = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Ready to validate the credentials. We have either a Kerberos
|
|||
|
// ticket, or something alleging to be a Kerberos ticket, or we
|
|||
|
// have Lanman-style credentials. Check which and call the proper
|
|||
|
// routine.
|
|||
|
//
|
|||
|
|
|||
|
#ifdef CAP_KERBEROS_BLOB
|
|||
|
if (SrvHaveKerberos) {
|
|||
|
IsKerb =
|
|||
|
(connection->ClientCapabilities & (CAP_NT_SMBS | CAP_KERBEROS_BLOB)) ==
|
|||
|
(CAP_NT_SMBS | CAP_KERBEROS_BLOB);
|
|||
|
} else
|
|||
|
#endif
|
|||
|
{
|
|||
|
IsKerb = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if(IsKerb)
|
|||
|
{
|
|||
|
//
|
|||
|
// Kerberos it is
|
|||
|
//
|
|||
|
|
|||
|
status = SrvValidateBlob( // We have a Kerberos Blob
|
|||
|
session,
|
|||
|
connection,
|
|||
|
&nameString,
|
|||
|
caseSensitivePassword, // Where the blob is
|
|||
|
&caseSensitivePasswordLength);
|
|||
|
|
|||
|
if(byteCount = (USHORT)caseSensitivePasswordLength)
|
|||
|
{
|
|||
|
//
|
|||
|
// Have something to return. Stick it in
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory(response->Buffer,
|
|||
|
caseSensitivePassword,
|
|||
|
caseSensitivePasswordLength);
|
|||
|
SmbPutUshort( &response->ByteCount, byteCount );
|
|||
|
|
|||
|
if(!NT_SUCCESS(status))
|
|||
|
{
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
byteCount = 0;
|
|||
|
|
|||
|
status = SrvValidateUser(
|
|||
|
&session->UserHandle,
|
|||
|
session,
|
|||
|
connection,
|
|||
|
&nameString,
|
|||
|
caseInsensitivePassword,
|
|||
|
caseInsensitivePasswordLength,
|
|||
|
caseSensitivePassword,
|
|||
|
caseSensitivePasswordLength,
|
|||
|
&action
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if ( !isUnicode ) {
|
|||
|
RtlFreeUnicodeString( &nameString );
|
|||
|
nameString.Buffer = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a bad name/password combination was sent, return an error.
|
|||
|
//
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
|
|||
|
|
|||
|
IF_DEBUG(ERRORS) {
|
|||
|
SrvPrint0( "BlockingSessionSetupAndX: Bad user/password "
|
|||
|
"combination.\n" );
|
|||
|
}
|
|||
|
|
|||
|
SrvStatistics.LogonErrors++;
|
|||
|
goto error_exit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN1) {
|
|||
|
SrvPrint1( "Validated user: %s\n", userName );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the client thinks that it is the first user on this
|
|||
|
// connection, get rid of other connections (may be due to rebooting
|
|||
|
// of client). Also get rid of other sessions on this connection
|
|||
|
// with the same user name--this handles a DOS "weirdness" where
|
|||
|
// it sends multiple session setups if a tree connect fails.
|
|||
|
//
|
|||
|
// *** If VcNumber is non-zero, we do nothing special. This is the
|
|||
|
// case even though the SrvMaxVcNumber configurable variable
|
|||
|
// should always be equal to one. If a second VC is established
|
|||
|
// between machines, a new session must also be established.
|
|||
|
// This duplicates the LM 2.0 server's behavior.
|
|||
|
//
|
|||
|
|
|||
|
if ( SmbGetUshort( &request->VcNumber ) == 0 ) {
|
|||
|
SrvCloseConnectionsFromClient( connection );
|
|||
|
SrvCloseSessionsOnConnection( connection, &session->UserName );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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).
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( SrvSessionList.Lock == &SrvOrderedListLock );
|
|||
|
ACQUIRE_LOCK( SrvSessionList.Lock );
|
|||
|
|
|||
|
ACQUIRE_LOCK( &connection->Lock );
|
|||
|
|
|||
|
locksHeld = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
|
|||
|
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
|
|||
|
|
|||
|
IF_DEBUG(ERRORS) {
|
|||
|
SrvPrint0( "BlockingSessionSetupAndX: Connection closing\n" );
|
|||
|
}
|
|||
|
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
goto error_exit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this client speaks a dialect above LM 1.0, find a UID that can
|
|||
|
// be used for this session. Otherwise, just use location 0 of the
|
|||
|
// table because those clients will not send a UID in SMBs and they
|
|||
|
// can have only one session.
|
|||
|
//
|
|||
|
|
|||
|
if ( connection->SmbDialect < SmbDialectLanMan10 ) {
|
|||
|
|
|||
|
if ( pagedConnection->SessionTable.FirstFreeEntry == -1
|
|||
|
&&
|
|||
|
SrvGrowTable(
|
|||
|
&pagedConnection->SessionTable,
|
|||
|
SrvInitialSessionTableSize,
|
|||
|
SrvMaxSessionTableSize ) == FALSE
|
|||
|
) {
|
|||
|
|
|||
|
//
|
|||
|
// No free entries in the user table. Reject the request.
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(ERRORS) {
|
|||
|
SrvPrint0( "BlockingSessionSetupAndX: No more UIDs available.\n" );
|
|||
|
}
|
|||
|
|
|||
|
SrvLogTableFullError( SRV_TABLE_SESSION );
|
|||
|
|
|||
|
status = STATUS_SMB_TOO_MANY_UIDS;
|
|||
|
goto error_exit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
uidIndex = pagedConnection->SessionTable.FirstFreeEntry;
|
|||
|
|
|||
|
} else { // if ( dialect < SmbDialectLanMan10 )
|
|||
|
|
|||
|
//
|
|||
|
// If this client already has a session at this server, abort.
|
|||
|
// The session should have been closed by the call to
|
|||
|
// SrvCloseSessionsOnConnection above. (We could try to work
|
|||
|
// around the existence of the session by closing it, but that
|
|||
|
// would involve releasing the locks, closing the session, and
|
|||
|
// retrying. This case shouldn't happen.)
|
|||
|
//
|
|||
|
|
|||
|
if ( pagedConnection->SessionTable.Table[0].Owner != NULL ) {
|
|||
|
|
|||
|
IF_DEBUG(ERRORS) {
|
|||
|
SrvPrint0( "BlockingSessionSetupAndX: Core client already "
|
|||
|
"has session.\n" );
|
|||
|
}
|
|||
|
|
|||
|
status = STATUS_SMB_TOO_MANY_UIDS;
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Use location 0 of the session table.
|
|||
|
//
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN2) {
|
|||
|
SrvPrint0( "Client LM 1.0 or before--using location 0 of session "
|
|||
|
"table.\n" );
|
|||
|
}
|
|||
|
|
|||
|
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 = &pagedConnection->SessionTable.Table[uidIndex];
|
|||
|
|
|||
|
pagedConnection->SessionTable.FirstFreeEntry = entry->NextFreeEntry;
|
|||
|
DEBUG entry->NextFreeEntry = -2;
|
|||
|
if ( pagedConnection->SessionTable.LastFreeEntry == uidIndex ) {
|
|||
|
pagedConnection->SessionTable.LastFreeEntry = -1;
|
|||
|
}
|
|||
|
|
|||
|
INCREMENT_UID_SEQUENCE( entry->SequenceNumber );
|
|||
|
if ( uidIndex == 0 && entry->SequenceNumber == 0 ) {
|
|||
|
INCREMENT_UID_SEQUENCE( entry->SequenceNumber );
|
|||
|
}
|
|||
|
session->Uid = MAKE_UID( uidIndex, entry->SequenceNumber );
|
|||
|
|
|||
|
entry->Owner = session;
|
|||
|
|
|||
|
pagedConnection->CurrentNumberOfSessions++;
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN1) {
|
|||
|
SrvPrint2( "Found UID. Index = 0x%lx, sequence = 0x%lx\n",
|
|||
|
UID_INDEX( session->Uid ),
|
|||
|
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. Insert the session in the global
|
|||
|
// list of active sessions. 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;
|
|||
|
|
|||
|
//
|
|||
|
// Build response SMB, making sure to save request fields first in
|
|||
|
// case the response overwrites the request. Save the
|
|||
|
// newly-assigned UID in both the request SMB and the response SMB
|
|||
|
// so that subsequent command processors and the client,
|
|||
|
// respectively, can see it.
|
|||
|
//
|
|||
|
|
|||
|
nextCommand = request->AndXCommand;
|
|||
|
|
|||
|
reqAndXOffset = SmbGetUshort( &request->AndXOffset );
|
|||
|
|
|||
|
SmbPutAlignedUshort( &WorkContext->RequestHeader->Uid, session->Uid );
|
|||
|
SmbPutAlignedUshort( &WorkContext->ResponseHeader->Uid, session->Uid );
|
|||
|
|
|||
|
response->WordCount = 3;
|
|||
|
response->AndXCommand = nextCommand;
|
|||
|
response->AndXReserved = 0;
|
|||
|
|
|||
|
//
|
|||
|
// If appropriate, append the Native OS and Native LAN Man strings
|
|||
|
// to the response.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if (!IsKerb && (connection->SmbDialect <= SmbDialectDosLanMan21) )
|
|||
|
{
|
|||
|
|
|||
|
ULONG stringLength;
|
|||
|
|
|||
|
if ( isUnicode ) {
|
|||
|
|
|||
|
PWCH buffer = ALIGN_SMB_WSTR( response->Buffer );
|
|||
|
|
|||
|
byteCount = (USHORT)(SrvNativeOS.Length + sizeof(UNICODE_NULL));
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
buffer,
|
|||
|
SrvNativeOS.Buffer,
|
|||
|
byteCount
|
|||
|
);
|
|||
|
|
|||
|
stringLength = SrvNativeLanMan.Length + sizeof(UNICODE_NULL);
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
(PCHAR)buffer + byteCount,
|
|||
|
SrvNativeLanMan.Buffer,
|
|||
|
stringLength
|
|||
|
);
|
|||
|
|
|||
|
byteCount += (USHORT)stringLength;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
byteCount = SrvOemNativeOS.Length + sizeof(CHAR);
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
response->Buffer,
|
|||
|
SrvOemNativeOS.Buffer,
|
|||
|
byteCount
|
|||
|
);
|
|||
|
|
|||
|
stringLength = SrvOemNativeLanMan.Length + sizeof(CHAR);
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
(PVOID) (response->Buffer + byteCount),
|
|||
|
SrvOemNativeLanMan.Buffer,
|
|||
|
stringLength
|
|||
|
);
|
|||
|
|
|||
|
byteCount += (USHORT)stringLength;
|
|||
|
}
|
|||
|
|
|||
|
if ( connection->SmbDialect <= SmbDialectNtLanMan ) {
|
|||
|
|
|||
|
if ( isUnicode ) {
|
|||
|
|
|||
|
PWCH buffer = ALIGN_SMB_WSTR( response->Buffer + byteCount );
|
|||
|
|
|||
|
stringLength = endpoint->DomainName.Length + sizeof(UNICODE_NULL);
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
buffer,
|
|||
|
endpoint->DomainName.Buffer,
|
|||
|
stringLength
|
|||
|
);
|
|||
|
|
|||
|
byteCount = (USHORT)(((PCHAR)buffer - response->Buffer) +
|
|||
|
stringLength);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
stringLength = endpoint->OemDomainName.Length + sizeof(CHAR);
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
(PVOID) (response->Buffer + byteCount),
|
|||
|
endpoint->OemDomainName.Buffer,
|
|||
|
stringLength
|
|||
|
);
|
|||
|
|
|||
|
byteCount += (USHORT)stringLength;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
SmbPutUshort( &response->AndXOffset, GET_ANDX_OFFSET(
|
|||
|
WorkContext->ResponseHeader,
|
|||
|
WorkContext->ResponseParameters,
|
|||
|
RESP_SESSION_SETUP_ANDX,
|
|||
|
byteCount
|
|||
|
) );
|
|||
|
|
|||
|
//
|
|||
|
// Normally, turning on bit 0 of Action indicates that the user was
|
|||
|
// logged on as GUEST. However, NT does not have automatic guest
|
|||
|
// logon--a user ID and password are required for every single logon
|
|||
|
// (though the password may have null length). Therefore, the
|
|||
|
// server need not concern itself with what kind of account the
|
|||
|
// client gets.
|
|||
|
//
|
|||
|
// Bit 1 tells the client that the user was logged on
|
|||
|
// using the lm session key instead of the user session key.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUshort( &response->Action, action );
|
|||
|
SmbPutUshort( &response->ByteCount, byteCount );
|
|||
|
|
|||
|
WorkContext->ResponseParameters = (PCHAR)WorkContext->ResponseHeader +
|
|||
|
SmbGetUshort( &response->AndXOffset );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Test for legal followon command.
|
|||
|
//
|
|||
|
|
|||
|
switch ( nextCommand ) {
|
|||
|
|
|||
|
case SMB_COM_TREE_CONNECT_ANDX:
|
|||
|
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_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:
|
|||
|
case SMB_COM_NO_ANDX_COMMAND:
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default: // Illegal followon command
|
|||
|
|
|||
|
IF_DEBUG(SMB_ERRORS) {
|
|||
|
SrvPrint1( "BlockingSessionSetupAndX: Illegal followon command: "
|
|||
|
"0x%lx\n", nextCommand );
|
|||
|
}
|
|||
|
|
|||
|
status = STATUS_INVALID_SMB;
|
|||
|
goto error_exit1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If there is an AndX command, set up to process it. Otherwise,
|
|||
|
// indicate completion to the caller.
|
|||
|
//
|
|||
|
|
|||
|
if ( nextCommand != SMB_COM_NO_ANDX_COMMAND ) {
|
|||
|
|
|||
|
WorkContext->NextCommand = nextCommand;
|
|||
|
|
|||
|
WorkContext->RequestParameters = (PCHAR)WorkContext->RequestHeader +
|
|||
|
reqAndXOffset;
|
|||
|
|
|||
|
SrvProcessSmb( WorkContext );
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG(TRACE2) SrvPrint0( "BlockingSessionSetupAndX complete.\n" );
|
|||
|
goto normal_exit;
|
|||
|
|
|||
|
error_exit:
|
|||
|
|
|||
|
if ( locksHeld ) {
|
|||
|
RELEASE_LOCK( &connection->Lock );
|
|||
|
RELEASE_LOCK( SrvSessionList.Lock );
|
|||
|
}
|
|||
|
|
|||
|
if ( session != NULL ) {
|
|||
|
SrvFreeSession( session );
|
|||
|
}
|
|||
|
|
|||
|
if ( !isUnicode ) {
|
|||
|
if ( domainString.Buffer != NULL ) {
|
|||
|
RtlFreeUnicodeString( &domainString );
|
|||
|
}
|
|||
|
if ( nameString.Buffer != NULL ) {
|
|||
|
RtlFreeUnicodeString( &nameString );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
error_exit1:
|
|||
|
|
|||
|
SrvSetSmbError( WorkContext, status );
|
|||
|
|
|||
|
normal_exit:
|
|||
|
|
|||
|
SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
|
|||
|
return;
|
|||
|
|
|||
|
} // BlockingSessionSetupAndX
|
|||
|
|
|||
|
|
|||
|
SMB_TRANS_STATUS
|
|||
|
SrvSmbSessionSetup (
|
|||
|
IN OUT PWORK_CONTEXT WorkContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes a session setup in a Trans2 SMB.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
WorkContext - Supplies the address of a Work Context Block
|
|||
|
describing the current request. See smbtypes.h for a more
|
|||
|
complete description of the valid fields.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
SMB_TRANS_STATUS - Indicates whether an error occurred. See
|
|||
|
smbtypes.h for a more complete description.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// This SMB must be processed in a blocking thread.
|
|||
|
//
|
|||
|
|
|||
|
WorkContext->FspRestartRoutine = BlockingSessionSetup;
|
|||
|
SrvQueueWorkToBlockingThread( WorkContext );
|
|||
|
return SmbTransStatusInProgress;
|
|||
|
|
|||
|
} // SrvSmbSessionSetup
|
|||
|
|
|||
|
VOID SRVFASTCALL
|
|||
|
BlockingSessionSetup (
|
|||
|
IN OUT PWORK_CONTEXT WorkContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes a session setup in a Trans2 SMB.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
WorkContext - Supplies the address of a Work Context Block
|
|||
|
describing the current request. See smbtypes.h for a more
|
|||
|
complete description of the valid fields.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
SMB_TRANS_STATUS - Indicates whether an error occurred. See
|
|||
|
smbtypes.h for a more complete description.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// The OS/2 redirector never sends a remote FS control request.
|
|||
|
// If we get one, simply reply that we cannot handle it.
|
|||
|
//
|
|||
|
NTSTATUS status;
|
|||
|
PSESSION session = NULL;
|
|||
|
PTABLE_ENTRY entry;
|
|||
|
SHORT uidIndex;
|
|||
|
PREQ_CAIRO_TRANS2_SESSION_SETUP request;
|
|||
|
PRESP_CAIRO_TRANS2_SESSION_SETUP response;
|
|||
|
PCONNECTION connection;
|
|||
|
PPAGED_CONNECTION pagedConnection;
|
|||
|
PTRANSACTION transaction;
|
|||
|
PCHAR Blob;
|
|||
|
CLONG BlobLength;
|
|||
|
PSZ userName;
|
|||
|
UNICODE_STRING nameString;
|
|||
|
UNICODE_STRING domainString;
|
|||
|
USHORT nameLength;
|
|||
|
ULONG capabilities;
|
|||
|
BOOLEAN locksHeld = FALSE;
|
|||
|
USHORT SessId;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
nameString.Buffer = NULL;
|
|||
|
nameString.Length = 0;
|
|||
|
nameString.MaximumLength = 0;
|
|||
|
|
|||
|
domainString.Buffer = NULL;
|
|||
|
domainString.Length = 0;
|
|||
|
domainString.MaximumLength = 0;
|
|||
|
|
|||
|
IF_SMB_DEBUG(TRANSACTION1) {
|
|||
|
SrvPrint0( "SrvSmbSessionSetup\n");
|
|||
|
}
|
|||
|
|
|||
|
transaction = WorkContext->Parameters.Transaction;
|
|||
|
|
|||
|
request = (PREQ_CAIRO_TRANS2_SESSION_SETUP)transaction->InData;
|
|||
|
response = (PRESP_CAIRO_TRANS2_SESSION_SETUP)transaction->OutData;
|
|||
|
connection = WorkContext->Connection;
|
|||
|
pagedConnection = connection->PagedConnection;
|
|||
|
|
|||
|
//
|
|||
|
// Verify that enough parameter bytes were sent and that we're allowed
|
|||
|
// to return enough parameter bytes.
|
|||
|
//
|
|||
|
|
|||
|
if ( (transaction->DataCount < sizeof(REQ_CAIRO_TRANS2_SESSION_SETUP)) ||
|
|||
|
(transaction->MaxDataCount < sizeof(RESP_CAIRO_TRANS2_SESSION_SETUP)) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Not enough parameter bytes were sent.
|
|||
|
//
|
|||
|
|
|||
|
IF_SMB_DEBUG(TRANSACTION1) {
|
|||
|
SrvPrint2( "SrvSmbSessionSetup: bad InData byte counts: "
|
|||
|
"%ld %ld\n",
|
|||
|
transaction->DataCount,
|
|||
|
transaction->MaxDataCount );
|
|||
|
}
|
|||
|
|
|||
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|||
|
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusErrorWithoutData );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Convert the client name to unicode
|
|||
|
//
|
|||
|
|
|||
|
if ( pagedConnection->ClientMachineNameString.Length == 0 ) {
|
|||
|
|
|||
|
UNICODE_STRING clientMachineName;
|
|||
|
clientMachineName.Buffer = pagedConnection->ClientMachineName;
|
|||
|
clientMachineName.MaximumLength =
|
|||
|
(USHORT)(COMPUTER_NAME_LENGTH+1)*sizeof(WCHAR);
|
|||
|
|
|||
|
(VOID)RtlOemStringToUnicodeString(
|
|||
|
&clientMachineName,
|
|||
|
&connection->OemClientMachineNameString,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Add the double backslashes to the length
|
|||
|
//
|
|||
|
|
|||
|
pagedConnection->ClientMachineNameString.Length =
|
|||
|
(USHORT)(clientMachineName.Length + 2*sizeof(WCHAR));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// get the capabilities of the client
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(connection->SmbDialect <= SmbDialectNtLanMan);
|
|||
|
connection->ClientCapabilities =
|
|||
|
SmbGetUlong( &request->Capabilities ) &
|
|||
|
( CAP_UNICODE |
|
|||
|
CAP_LARGE_FILES |
|
|||
|
CAP_NT_SMBS |
|
|||
|
CAP_NT_FIND |
|
|||
|
CAP_NT_STATUS |
|
|||
|
CAP_LEVEL_II_OPLOCKS );
|
|||
|
if ( connection->ClientCapabilities & CAP_NT_SMBS ) {
|
|||
|
connection->ClientCapabilities |= CAP_NT_FIND;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// now get the blob, and its length ( I guess I never really thought
|
|||
|
// a blob would have a length )
|
|||
|
//
|
|||
|
|
|||
|
BlobLength = request->BufferLength;
|
|||
|
Blob = request->Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// get the username, which right now comes right after the Kerberos blob
|
|||
|
//
|
|||
|
|
|||
|
userName = request->Buffer+request->BufferLength;
|
|||
|
|
|||
|
if ( SMB_IS_UNICODE( WorkContext ) ) {
|
|||
|
userName = ALIGN_SMB_WSTR( userName );
|
|||
|
}
|
|||
|
|
|||
|
nameLength = SrvGetStringLength(
|
|||
|
userName,
|
|||
|
transaction->InData + transaction->DataCount,
|
|||
|
SMB_IS_UNICODE( WorkContext ),
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
if ( nameLength == (USHORT)-1 ) {
|
|||
|
status = STATUS_INVALID_SMB;
|
|||
|
IF_SMB_DEBUG(TRANSACTION1) {
|
|||
|
SrvPrint0( "SESSSETUP -- namelength == -1\n");
|
|||
|
}
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
status = SrvMakeUnicodeString(
|
|||
|
SMB_IS_UNICODE( WorkContext ),
|
|||
|
&nameString,
|
|||
|
userName,
|
|||
|
&nameLength );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( status ) ) {
|
|||
|
IF_SMB_DEBUG(TRANSACTION1) {
|
|||
|
SrvPrint1( "SESSSETUP -- failed making unicode string for name, %lC\n",status);
|
|||
|
}
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a Session block.
|
|||
|
//
|
|||
|
|
|||
|
SrvAllocateSession( &session, &nameString, &domainString );
|
|||
|
|
|||
|
if ( !SMB_IS_UNICODE( WorkContext ) && domainString.Buffer != NULL ) {
|
|||
|
RtlFreeUnicodeString( &domainString );
|
|||
|
domainString.Buffer = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if ( session == NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// Unable to allocate a Session block. Return an error status.
|
|||
|
//
|
|||
|
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto error_exit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If using uppercase pathnames, indicate in the session block. DOS
|
|||
|
// always uses uppercase paths.
|
|||
|
//
|
|||
|
|
|||
|
if ( (WorkContext->RequestHeader->Flags &
|
|||
|
SMB_FLAGS_CANONICALIZED_PATHS) != 0 ||
|
|||
|
IS_DOS_DIALECT( connection->SmbDialect ) ) {
|
|||
|
session->UsingUppercasePaths = TRUE;
|
|||
|
} else {
|
|||
|
session->UsingUppercasePaths = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// set the pointer in the transaction to the session
|
|||
|
//
|
|||
|
|
|||
|
transaction->Session = session;
|
|||
|
|
|||
|
//
|
|||
|
// Enter data from request SMB into the session block.
|
|||
|
//
|
|||
|
|
|||
|
session->MaxBufferSize = SmbGetUshort( &request->MaxBufferSize );
|
|||
|
|
|||
|
//
|
|||
|
// Try to find legitimate name/password combination.
|
|||
|
//
|
|||
|
// ASM
|
|||
|
// There are a couple of special cases we test for, both
|
|||
|
// related to the CAIRO dialect. Under this dialect, the
|
|||
|
// SessionSetup can be using either Kerberos authentication
|
|||
|
// or LM authentication. Eventually, Kerberos authentication will
|
|||
|
// use a TRANSACT message do disambiguate the cases, but for
|
|||
|
// now, we need to look at this message to tell the difference.
|
|||
|
|
|||
|
IF_SMB_DEBUG(TRANSACTION1) {
|
|||
|
SrvPrint0( "SESSSETUP -- going to validate the blob\n");
|
|||
|
}
|
|||
|
|
|||
|
status = SrvValidateBlob( // We have a Kerberos Blob
|
|||
|
session,
|
|||
|
connection,
|
|||
|
&nameString,
|
|||
|
Blob, // Where the blob is
|
|||
|
&BlobLength
|
|||
|
);
|
|||
|
|
|||
|
IF_SMB_DEBUG(TRANSACTION1) {
|
|||
|
SrvPrint1( "SESSSETUP -- blob validated, %lC\n",status);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// free if unused the unicode namestring buffer
|
|||
|
//
|
|||
|
|
|||
|
if ( !SMB_IS_UNICODE( WorkContext ) ) {
|
|||
|
RtlFreeUnicodeString( &nameString );
|
|||
|
nameString.Buffer = NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if(NT_SUCCESS(status)
|
|||
|
||
|
|||
|
BlobLength)
|
|||
|
{
|
|||
|
|
|||
|
if(!NT_SUCCESS(status))
|
|||
|
{
|
|||
|
transaction->cMaxBufferSize = (CLONG)session->MaxBufferSize;
|
|||
|
session->Connection = 0;
|
|||
|
SessId = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// If the client thinks that it is the first user on this
|
|||
|
// connection, get rid of other connections (may be due to rebooting
|
|||
|
// of client). Also get rid of other sessions on this connection
|
|||
|
// with the same user name--this handles a DOS "weirdness" where
|
|||
|
// it sends multiple session setups if a tree connect fails.
|
|||
|
//
|
|||
|
// *** If VcNumber is non-zero, we do nothing special. This is the
|
|||
|
// case even though the SrvMaxVcNumber configurable variable
|
|||
|
// should always be equal to one. If a second VC is established
|
|||
|
// between machines, a new session must also be established.
|
|||
|
// This duplicates the LM 2.0 server's behavior.
|
|||
|
//
|
|||
|
|
|||
|
if ( SmbGetUshort( &request->VcNumber ) == 0 ) {
|
|||
|
SrvCloseConnectionsFromClient( connection );
|
|||
|
SrvCloseSessionsOnConnection( connection, &session->UserName );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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).
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( SrvSessionList.Lock == &SrvOrderedListLock );
|
|||
|
ACQUIRE_LOCK( SrvSessionList.Lock );
|
|||
|
|
|||
|
ACQUIRE_LOCK( &connection->Lock );
|
|||
|
|
|||
|
locksHeld = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
|
|||
|
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
|
|||
|
|
|||
|
IF_DEBUG(ERRORS) {
|
|||
|
SrvPrint0( "SrvSmbSessionSetupAndX: Connection closing\n" );
|
|||
|
}
|
|||
|
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
goto error_exit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this client speaks a dialect above LM 1.0, find a UID that can
|
|||
|
// be used for this session. Otherwise, just use location 0 of the
|
|||
|
// table because those clients will not send a UID in SMBs and they
|
|||
|
// can have only one session.
|
|||
|
//
|
|||
|
|
|||
|
if ( pagedConnection->SessionTable.FirstFreeEntry == -1
|
|||
|
&&
|
|||
|
SrvGrowTable(
|
|||
|
&pagedConnection->SessionTable,
|
|||
|
SrvInitialSessionTableSize,
|
|||
|
SrvMaxSessionTableSize ) == FALSE
|
|||
|
) {
|
|||
|
|
|||
|
//
|
|||
|
// No free entries in the user table. Reject the request.
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(ERRORS) {
|
|||
|
SrvPrint0( "SrvSmbSessionSetup: No more UIDs available.\n" );
|
|||
|
}
|
|||
|
|
|||
|
status = STATUS_SMB_TOO_MANY_UIDS;
|
|||
|
goto error_exit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
uidIndex = pagedConnection->SessionTable.FirstFreeEntry;
|
|||
|
|
|||
|
//
|
|||
|
// 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 = &pagedConnection->SessionTable.Table[uidIndex];
|
|||
|
|
|||
|
pagedConnection->SessionTable.FirstFreeEntry = entry->NextFreeEntry;
|
|||
|
DEBUG entry->NextFreeEntry = -2;
|
|||
|
if ( pagedConnection->SessionTable.LastFreeEntry == uidIndex ) {
|
|||
|
pagedConnection->SessionTable.LastFreeEntry = -1;
|
|||
|
}
|
|||
|
|
|||
|
INCREMENT_UID_SEQUENCE( entry->SequenceNumber );
|
|||
|
session->Uid = MAKE_UID( uidIndex, entry->SequenceNumber );
|
|||
|
|
|||
|
IF_SMB_DEBUG(TRANSACTION1) {
|
|||
|
SrvPrint1( "SESSSETUP -- made uid %x\n",session->Uid);
|
|||
|
}
|
|||
|
|
|||
|
entry->Owner = session;
|
|||
|
|
|||
|
pagedConnection->CurrentNumberOfSessions++;
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN1) {
|
|||
|
SrvPrint2( "Found UID. Index = 0x%lx, sequence = 0x%lx\n",
|
|||
|
UID_INDEX( session->Uid ),
|
|||
|
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. Insert the session in the global
|
|||
|
// list of active sessions. 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.
|
|||
|
//
|
|||
|
// It seems that perhaps we need another reference since we are
|
|||
|
// doing this using a trans2 instead of a sessionsetup
|
|||
|
//
|
|||
|
|
|||
|
SrvReferenceSession( session );
|
|||
|
|
|||
|
WorkContext->Session = session;
|
|||
|
SessId = session->Uid;
|
|||
|
|
|||
|
}
|
|||
|
status = S_OK;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// it failed
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(ERRORS) {
|
|||
|
SrvPrint0( "SrvSmbSessionSetupAndX: Bad user/password "
|
|||
|
"combination.\n" );
|
|||
|
}
|
|||
|
|
|||
|
SrvStatistics.LogonErrors++;
|
|||
|
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Build response SMB, making sure to save request fields first in
|
|||
|
// case the response overwrites the request. Save the
|
|||
|
// newly-assigned UID in both the request SMB and the response SMB
|
|||
|
// so that subsequent command processors and the client,
|
|||
|
// respectively, can see it.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutAlignedUshort( &WorkContext->RequestHeader->Uid, SessId );
|
|||
|
SmbPutAlignedUshort( &WorkContext->ResponseHeader->Uid, SessId );
|
|||
|
|
|||
|
SmbPutUshort( &response->Uid, SessId);
|
|||
|
|
|||
|
IF_SMB_DEBUG(TRANSACTION1) {
|
|||
|
SrvPrint1( "SESSSETUP -- put uid for rdr = %x\n",response->Uid);
|
|||
|
}
|
|||
|
|
|||
|
response->WordCount = 0;
|
|||
|
|
|||
|
//
|
|||
|
// If appropriate, append the Native OS and Native LAN Man strings
|
|||
|
// to the response.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( response->Buffer,
|
|||
|
Blob,
|
|||
|
BlobLength );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Normally, turning on bit 0 of Action indicates that the user was
|
|||
|
// logged on as GUEST. However, NT does not have automatic guest
|
|||
|
// logon--a user ID and password are required for every single logon
|
|||
|
// (though the password may have null length). Therefore, the
|
|||
|
// server need not concern itself with what kind of account the
|
|||
|
// client gets.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUlong( &response->BufferLength, BlobLength );
|
|||
|
|
|||
|
IF_SMB_DEBUG(TRANSACTION1) {
|
|||
|
SrvPrint0( "SESSSETUP -- all done\n");
|
|||
|
}
|
|||
|
|
|||
|
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusSuccess );
|
|||
|
return;
|
|||
|
|
|||
|
error_exit:
|
|||
|
|
|||
|
if ( locksHeld ) {
|
|||
|
RELEASE_LOCK( &connection->Lock );
|
|||
|
RELEASE_LOCK( SrvSessionList.Lock );
|
|||
|
}
|
|||
|
|
|||
|
if ( session != NULL ) {
|
|||
|
// !!! Need to close token handle!
|
|||
|
transaction->Session = NULL;
|
|||
|
SrvFreeSession( session );
|
|||
|
}
|
|||
|
|
|||
|
if ( domainString.Buffer != NULL && !SMB_IS_UNICODE(WorkContext) ) {
|
|||
|
RtlFreeUnicodeString( &domainString );
|
|||
|
}
|
|||
|
|
|||
|
if ( nameString.Buffer != NULL && !SMB_IS_UNICODE(WorkContext) ) {
|
|||
|
RtlFreeUnicodeString( &nameString );
|
|||
|
}
|
|||
|
|
|||
|
IF_SMB_DEBUG(TRANSACTION1) {
|
|||
|
SrvPrint1( "SESSSETUP -- all done, status = %lC\n",status);
|
|||
|
}
|
|||
|
SrvSetSmbError( WorkContext, status );
|
|||
|
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusErrorWithoutData );
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // BlockingSessionSetup
|
|||
|
|
|||
|
|
|||
|
SMB_PROCESSOR_RETURN_TYPE
|
|||
|
SrvSmbLogoffAndX (
|
|||
|
SMB_PROCESSOR_PARAMETERS
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes a Logoff 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_LOGOFF_ANDX request;
|
|||
|
PRESP_LOGOFF_ANDX response;
|
|||
|
|
|||
|
PSESSION session;
|
|||
|
USHORT reqAndXOffset;
|
|||
|
UCHAR nextCommand;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
IF_SMB_DEBUG(ADMIN1) {
|
|||
|
SrvPrint2( "Logoff request header at 0x%lx, "
|
|||
|
"response header at 0x%lx\n",
|
|||
|
WorkContext->RequestHeader, WorkContext->ResponseHeader );
|
|||
|
SrvPrint2( "Logoff request parameters at 0x%lx, "
|
|||
|
"response parameters at 0x%lx\n",
|
|||
|
WorkContext->RequestParameters,
|
|||
|
WorkContext->ResponseParameters );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set up parameters.
|
|||
|
//
|
|||
|
|
|||
|
request = (PREQ_LOGOFF_ANDX)(WorkContext->RequestParameters);
|
|||
|
response = (PRESP_LOGOFF_ANDX)(WorkContext->ResponseParameters);
|
|||
|
|
|||
|
//
|
|||
|
// If a session block has not already been assigned to the current
|
|||
|
// work context, verify the UID. If verified, the address of the
|
|||
|
// session block corresponding to this user is stored in the
|
|||
|
// WorkContext block and the session block is referenced.
|
|||
|
//
|
|||
|
|
|||
|
session = SrvVerifyUid(
|
|||
|
WorkContext,
|
|||
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )
|
|||
|
);
|
|||
|
|
|||
|
if ( session == NULL ) {
|
|||
|
|
|||
|
IF_DEBUG(SMB_ERRORS) {
|
|||
|
SrvPrint1( "SrvSmbLogoffAndX: Invalid UID: 0x%lx\n",
|
|||
|
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ) );
|
|||
|
}
|
|||
|
|
|||
|
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID );
|
|||
|
return SmbStatusSendResponse;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we need to visit the license server, get over to a blocking
|
|||
|
// thread to ensure that we don't consume the nonblocking threads
|
|||
|
//
|
|||
|
if( WorkContext->UsingBlockingThread == 0 &&
|
|||
|
session->IsLSNotified == TRUE ) {
|
|||
|
//
|
|||
|
// Insert the work item at the tail of the blocking work queue
|
|||
|
//
|
|||
|
SrvInsertWorkQueueTail(
|
|||
|
&SrvBlockingWorkQueue,
|
|||
|
(PQUEUEABLE_BLOCK_HEADER)WorkContext
|
|||
|
);
|
|||
|
|
|||
|
return SmbStatusInProgress;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do the actual logoff.
|
|||
|
//
|
|||
|
|
|||
|
SrvCloseSession( session );
|
|||
|
|
|||
|
SrvStatistics.SessionsLoggedOff++;
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the session, since it's no longer valid, but we may
|
|||
|
// end up processing a chained command. Clear the session pointer
|
|||
|
// in the work context block to indicate that we've done this.
|
|||
|
//
|
|||
|
|
|||
|
SrvDereferenceSession( session );
|
|||
|
|
|||
|
WorkContext->Session = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Build the response SMB, making sure to save request fields first
|
|||
|
// in case the response overwrites the request.
|
|||
|
//
|
|||
|
|
|||
|
reqAndXOffset = SmbGetUshort( &request->AndXOffset );
|
|||
|
nextCommand = request->AndXCommand;
|
|||
|
|
|||
|
response->WordCount = 2;
|
|||
|
response->AndXCommand = request->AndXCommand;
|
|||
|
response->AndXReserved = 0;
|
|||
|
SmbPutUshort( &response->AndXOffset, GET_ANDX_OFFSET(
|
|||
|
WorkContext->ResponseHeader,
|
|||
|
WorkContext->ResponseParameters,
|
|||
|
RESP_LOGOFF_ANDX,
|
|||
|
0
|
|||
|
) );
|
|||
|
SmbPutUshort( &response->ByteCount, 0 );
|
|||
|
|
|||
|
WorkContext->ResponseParameters = (PCHAR)WorkContext->ResponseHeader +
|
|||
|
SmbGetUshort( &response->AndXOffset );
|
|||
|
|
|||
|
//
|
|||
|
// Test for legal followon command.
|
|||
|
//
|
|||
|
|
|||
|
switch ( nextCommand ) {
|
|||
|
|
|||
|
case SMB_COM_SESSION_SETUP_ANDX:
|
|||
|
case SMB_COM_NO_ANDX_COMMAND:
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
IF_DEBUG(SMB_ERRORS) {
|
|||
|
SrvPrint1( "SrvSmbLogoffAndX: Illegal followon command: 0x%lx\n",
|
|||
|
nextCommand );
|
|||
|
}
|
|||
|
|
|||
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|||
|
return SmbStatusSendResponse;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If there is an AndX command, set up to process it. Otherwise,
|
|||
|
// indicate completion to the caller.
|
|||
|
//
|
|||
|
|
|||
|
if ( nextCommand != SMB_COM_NO_ANDX_COMMAND ) {
|
|||
|
|
|||
|
WorkContext->NextCommand = nextCommand;
|
|||
|
|
|||
|
WorkContext->RequestParameters = (PCHAR)WorkContext->RequestHeader +
|
|||
|
reqAndXOffset;
|
|||
|
|
|||
|
return SmbStatusMoreCommands;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbLogoffAndX complete.\n" );
|
|||
|
return SmbStatusSendResponse;
|
|||
|
|
|||
|
} // SrvSmbLogoffAndX
|
|||
|
|
|||
|
|
|||
|
STATIC
|
|||
|
VOID
|
|||
|
GetEncryptionKey (
|
|||
|
OUT CHAR EncryptionKey[MSV1_0_CHALLENGE_LENGTH]
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Creates an encryption key to use as a challenge for a logon.
|
|||
|
|
|||
|
*** Although the MSV1_0 authentication package has a function that
|
|||
|
returns an encryption key, we do not use that function in order
|
|||
|
to avoid a trip through LPC and into LSA.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
EncryptionKey - a pointer to a buffer which receives the encryption
|
|||
|
key.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - result of operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
union {
|
|||
|
LARGE_INTEGER time;
|
|||
|
UCHAR bytes[8];
|
|||
|
} u;
|
|||
|
ULONG seed;
|
|||
|
ULONG challenge[2];
|
|||
|
ULONG result3;
|
|||
|
|
|||
|
//
|
|||
|
// Create a pseudo-random 8-byte number by munging the system time
|
|||
|
// for use as a random number seed.
|
|||
|
//
|
|||
|
// Start by getting the system time.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( MSV1_0_CHALLENGE_LENGTH == 2 * sizeof(ULONG) );
|
|||
|
|
|||
|
KeQuerySystemTime( &u.time );
|
|||
|
|
|||
|
//
|
|||
|
// To ensure that we don't use the same system time twice, add in the
|
|||
|
// count of the number of times this routine has been called. Then
|
|||
|
// increment the counter.
|
|||
|
//
|
|||
|
// *** Since we don't use the low byte of the system time (it doesn't
|
|||
|
// take on enough different values, because of the timer
|
|||
|
// resolution), we increment the counter by 0x100.
|
|||
|
//
|
|||
|
// *** We don't interlock the counter because we don't really care
|
|||
|
// if it's not 100% accurate.
|
|||
|
//
|
|||
|
|
|||
|
u.time.LowPart += EncryptionKeyCount;
|
|||
|
|
|||
|
EncryptionKeyCount += 0x100;
|
|||
|
|
|||
|
//
|
|||
|
// Now use parts of the system time as a seed for the random
|
|||
|
// number generator.
|
|||
|
//
|
|||
|
// *** Because the middle two bytes of the low part of the system
|
|||
|
// time change most rapidly, we use those in forming the seed.
|
|||
|
//
|
|||
|
|
|||
|
seed = ((u.bytes[1] + 1) << 0) |
|
|||
|
((u.bytes[2] + 0) << 8) |
|
|||
|
((u.bytes[2] - 1) << 16) |
|
|||
|
((u.bytes[1] + 0) << 24);
|
|||
|
|
|||
|
//
|
|||
|
// Now get two random numbers. RtlRandom does not return negative
|
|||
|
// numbers, so we pseudo-randomly negate them.
|
|||
|
//
|
|||
|
|
|||
|
challenge[0] = RtlRandom( &seed );
|
|||
|
challenge[1] = RtlRandom( &seed );
|
|||
|
result3 = RtlRandom( &seed );
|
|||
|
|
|||
|
if ( (result3 & 0x1) != 0 ) {
|
|||
|
challenge[0] |= 0x80000000;
|
|||
|
}
|
|||
|
if ( (result3 & 0x2) != 0 ) {
|
|||
|
challenge[1] |= 0x80000000;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Return the challenge.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( EncryptionKey, challenge, MSV1_0_CHALLENGE_LENGTH );
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
#if 0
|
|||
|
//
|
|||
|
// This is the old code, which uses LSA to get the challenge.
|
|||
|
//
|
|||
|
|
|||
|
PMSV1_0_LM20_CHALLENGE_REQUEST challengeRequest;
|
|||
|
ULONG challengeRequestLength;
|
|||
|
PMSV1_0_LM20_CHALLENGE_RESPONSE challengeResponse;
|
|||
|
ULONG challengeResponseLength;
|
|||
|
NTSTATUS protocolStatus;
|
|||
|
NTSTATUS freeStatus;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
challengeRequest = NULL;
|
|||
|
challengeRequestLength = sizeof(MSV1_0_LM20_CHALLENGE_REQUEST);
|
|||
|
|
|||
|
status = NtAllocateVirtualMemory(
|
|||
|
NtCurrentProcess( ),
|
|||
|
(PVOID *)&challengeRequest,
|
|||
|
0,
|
|||
|
&challengeRequestLength,
|
|||
|
MEM_COMMIT,
|
|||
|
PAGE_READWRITE
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
INTERNAL_ERROR(
|
|||
|
ERROR_LEVEL_EXPECTED,
|
|||
|
"GetEncryptionKey: NtAllocateVirtualMemory failed: %X\n.",
|
|||
|
status,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
SrvLogError(
|
|||
|
SrvDeviceObject,
|
|||
|
EVENT_SRV_NO_VIRTUAL_MEMORY,
|
|||
|
status,
|
|||
|
&challengeRequestLength,
|
|||
|
sizeof(ULONG),
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
challengeRequest->MessageType = MsV1_0Lm20ChallengeRequest;
|
|||
|
|
|||
|
//
|
|||
|
// Get the "challenge" that clients will use to encrypt
|
|||
|
// passwords. This challenge is used for all logons on this
|
|||
|
// VC.
|
|||
|
//
|
|||
|
|
|||
|
status = LsaCallAuthenticationPackage(
|
|||
|
SrvLsaHandle,
|
|||
|
SrvAuthenticationPackage,
|
|||
|
challengeRequest,
|
|||
|
challengeRequestLength,
|
|||
|
(PVOID *)&challengeResponse,
|
|||
|
&challengeResponseLength,
|
|||
|
&protocolStatus
|
|||
|
);
|
|||
|
|
|||
|
freeStatus = NtFreeVirtualMemory(
|
|||
|
NtCurrentProcess( ),
|
|||
|
(PVOID *)&challengeRequest,
|
|||
|
&challengeRequestLength,
|
|||
|
MEM_RELEASE
|
|||
|
);
|
|||
|
ASSERT( NT_SUCCESS(freeStatus) );
|
|||
|
|
|||
|
if ( NT_SUCCESS(status) ) {
|
|||
|
status = protocolStatus;
|
|||
|
}
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
INTERNAL_ERROR(
|
|||
|
ERROR_LEVEL_UNEXPECTED,
|
|||
|
"GetEncryptionKey: LsaCallAuthenticationPackage failed: %X\n.",
|
|||
|
status,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
SrvLogServiceFailure( SRV_SVC_LSA_CALL_AUTH_PACKAGE, status );
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the challenge into the output buffer.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
EncryptionKey,
|
|||
|
challengeResponse->ChallengeToClient,
|
|||
|
MSV1_0_CHALLENGE_LENGTH
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Free the LSA response buffer.
|
|||
|
//
|
|||
|
|
|||
|
status = LsaFreeReturnBuffer( challengeResponse );
|
|||
|
ASSERT( NT_SUCCESS(status) );
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
#endif
|
|||
|
|
|||
|
} // GetEncryptionKey
|
|||
|
|