|
|
/*++
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"
#include "smbadmin.tmh"
#pragma hdrstop
#define ENCRYPT_TEXT_LENGTH 20
VOID GetEncryptionKey ( OUT CHAR EncryptionKey[MSV1_0_CHALLENGE_LENGTH] );
VOID SRVFASTCALL BlockingSessionSetupAndX ( IN OUT PWORK_CONTEXT WorkContext );
NTSTATUS GetNtSecurityParameters( IN PWORK_CONTEXT WorkContext, OUT PCHAR *CasesensitivePassword, OUT PULONG CasesensitivePasswordLength, OUT PCHAR *CaseInsensitivePassword, OUT PULONG CaseInsensitivePasswordLength, OUT PUNICODE_STRING UserName, OUT PUNICODE_STRING DomainName, OUT PCHAR *RestOfDataBuffer, OUT PULONG RestOfDataLength );
VOID BuildSessionSetupAndXResponse( IN PWORK_CONTEXT WorkContext, IN UCHAR NextCommand, IN USHORT Action, IN BOOLEAN IsUnicode);
NTSTATUS GetExtendedSecurityParameters( IN PWORK_CONTEXT WorkContext, OUT PUCHAR *SecurityBuffer, OUT PULONG SecurityBufferLength, OUT PCHAR *RestOfDataBuffer, OUT PULONG RestOfDataLength );
VOID BuildExtendedSessionSetupAndXResponse( IN PWORK_CONTEXT WorkContext, IN ULONG SecurityBlobLength, IN NTSTATUS Status, IN UCHAR NextCommand, IN BOOLEAN IsUnicode);
NTSTATUS InsertNativeOSAndType( IN BOOLEAN IsUnicode, OUT PCHAR Buffer, IN OUT PUSHORT ByteCount);
//
// 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;
ULONG SrvKsecValidErrors = 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, SrvSmbLogoffAndX )
#pragma alloc_text( PAGE, GetEncryptionKey )
#pragma alloc_text( PAGE, GetNtSecurityParameters )
#pragma alloc_text( PAGE, BuildSessionSetupAndXResponse )
#pragma alloc_text( PAGE, GetExtendedSecurityParameters )
#pragma alloc_text( PAGE, BuildExtendedSessionSetupAndXResponse )
#pragma alloc_text( PAGE, InsertNativeOSAndType )
#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; USHORT flags2; PSMB_HEADER smbHeader;
PSZ s, es; SMB_DIALECT bestDialect, serverDialect, firstDialect; USHORT consumerDialectChosen, consumerDialect; LARGE_INTEGER serverTime; SMB_DATE date; SMB_TIME time; ULONG capabilities; NTSTATUS status = STATUS_SUCCESS; SMB_STATUS SmbStatus = SmbStatusInProgress;
PAGED_CODE( ); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_NEGOTIATE; SrvWmiStartContext(WorkContext);
IF_SMB_DEBUG(ADMIN1) { SrvPrint2( "Negotiate request header at 0x%p, response header at 0x%p\n", WorkContext->RequestHeader, WorkContext->ResponseHeader ); SrvPrint2( "Negotiate request parameters at 0x%p, response parameters at 0x%p\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; smbHeader = WorkContext->RequestHeader;
//
// 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 ); status = STATUS_INVALID_SMB; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
//
// We don't know anything about the version number of this client yet.
//
pagedConnection->ClientBuildNumber = 0;
#if SRVNTVERCHK
pagedConnection->ClientTooOld = FALSE; #endif
//
// 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; es = END_OF_REQUEST_SMB( WorkContext );
if( endpoint->IsPrimaryName ) { firstDialect = FIRST_DIALECT; } else { firstDialect = FIRST_DIALECT_EMULATED; }
for ( s = (PSZ)request->Buffer, consumerDialect = 0; s <= es && s < SmbGetUshort( &request->ByteCount ) + (PSZ)request->Buffer; consumerDialect++ ) {
if ( *s++ != SMB_FORMAT_DIALECT ) {
IF_DEBUG(SMB_ERRORS) { SrvPrint0( "SrvSmbNegotiate: Invalid dialect format code.\n" ); }
SrvLogInvalidSmb( WorkContext );
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB ); status = STATUS_INVALID_SMB; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
for ( serverDialect = firstDialect; serverDialect < bestDialect; serverDialect++ ) {
if ( !strncmp( s, StrDialects[serverDialect], es-s+1 ) ) { IF_SMB_DEBUG(ADMIN2) { SrvPrint2( "Matched: %s and %s\n", StrDialects[serverDialect], s ); }
bestDialect = serverDialect; consumerDialectChosen = consumerDialect; } }
//
// Go to the next dialect string
//
for( ; *s && s < es; s++ ) ;
//
// We are now at the end of the buffer, or are pointing to the NULL.
// Advance the pointer. If we are at the end of the buffer, the test in
// the loop will terminate.
//
s++; }
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 ) {
USHORT securityMode;
//
// 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.
//
securityMode = NEGOTIATE_USER_SECURITY | NEGOTIATE_ENCRYPT_PASSWORDS;
SmbPutUshort( &response->SecurityMode, securityMode );
//
// 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;
if( response->Buffer + MSV1_0_CHALLENGE_LENGTH > END_OF_RESPONSE_BUFFER(WorkContext) ) { SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW ); status = STATUS_BUFFER_OVERFLOW; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
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, MIN(125, SrvMaxMpxCount) ); // Only send max of 125 to Win9x machines since they'll not connect if higher
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.
//
if( response->Buffer + byteCount + endpoint->OemDomainName.Length + sizeof(CHAR) > END_OF_RESPONSE_BUFFER(WorkContext) ) { SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW ); status = STATUS_BUFFER_OVERFLOW; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
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.
//
flags2 = SmbGetAlignedUshort( &smbHeader->Flags2 );
//
// We are going to attempt to validate this user with one of the listed
// security packages at the end of the smb. Currently the smb will
// simply contain the output of EnumerateSecurityPackages.
//
if ( flags2 & SMB_FLAGS2_EXTENDED_SECURITY ) {
if (!WorkContext->UsingExtraSmbBuffer) { status = SrvAllocateExtraSmbBuffer(WorkContext); if (!NT_SUCCESS(status)) { SrvSetSmbError(WorkContext, status); SmbStatus = SmbStatusSendResponse; goto Cleanup; }
RtlCopyMemory( WorkContext->ResponseHeader, WorkContext->RequestHeader, sizeof( SMB_HEADER ) ); } ntResponse = (PRESP_NT_NEGOTIATE)WorkContext->ResponseParameters; capabilities = CAP_EXTENDED_SECURITY; } else { capabilities = 0; }
ntResponse->WordCount = 17; SmbPutUshort( &ntResponse->DialectIndex, consumerDialectChosen );
// !!! This says that we don't want encrypted passwords.
// If this is negotiating NtLanMan, but not UNICODE, we know its not a Win9x client
// so it can handle MaxMpx larger than 125
if( flags2 & SMB_FLAGS2_UNICODE ) { SmbPutUshort( &ntResponse->MaxMpxCount, SrvMaxMpxCount ); } else { // Again, for the Win9x problems we need to minimize the Mpx count.
SmbPutUshort( &ntResponse->MaxMpxCount, MIN(125,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_INFOLEVEL_PASSTHRU | CAP_LOCK_AND_READ;
//
// Enable LWIO by default.
//
capabilities |= CAP_LWIO;
//
// 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; capabilities &= ~CAP_RAW_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( !SrvDisableLargeRead ) { capabilities |= CAP_LARGE_READX; }
//
// Unfortunately, NetBT is the only protocol that reliably supports
// transfers exceeding the negotiated buffer size. So disable the
// other protocols for now (hopefully)
//
if( !SrvDisableLargeWrite && !connection->Endpoint->IsConnectionless ) { capabilities |= CAP_LARGE_WRITEX; } }
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;
//
// There is a bug in some W9x clients that preclude the use of security
// signatures. We have produced a fix for vredir.vxd for this, but we
// can not tell whether or not we are working with one of these fixed
// clients. The only way i can think of to tell the difference between
// a W9x client and a properly functioning NT client is to look to see
// if the client understands NT status codes.
//
if( SrvSmbSecuritySignaturesEnabled &&
( SrvEnableW9xSecuritySignatures == TRUE || (flags2 & SMB_FLAGS2_NT_STATUS) ) ) {
ntResponse->SecurityMode |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;
if( SrvSmbSecuritySignaturesRequired ) { ntResponse->SecurityMode |= NEGOTIATE_SECURITY_SIGNATURES_REQUIRED; } }
//
// Get an encryption key for this connection.
//
if ((capabilities & CAP_EXTENDED_SECURITY) == 0) { GetEncryptionKey( pagedConnection->EncryptionKey );
if( response->Buffer + MSV1_0_CHALLENGE_LENGTH > END_OF_RESPONSE_BUFFER(WorkContext) ) { SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW ); status = STATUS_BUFFER_OVERFLOW; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
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;
domainLength = endpoint->DomainName.Length + sizeof(UNICODE_NULL); ptr = endpoint->DomainName.Buffer;
if( (PUCHAR)buffer + domainLength > END_OF_RESPONSE_BUFFER(WorkContext) ) { SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW ); status = STATUS_BUFFER_OVERFLOW; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
RtlCopyMemory( buffer, ptr, domainLength );
byteCount += domainLength;
//
// Append the server name to the response.
//
if( SrvComputerName.Buffer ) {
buffer = (PWCHAR)((LPSTR)buffer + domainLength);
if( (PUCHAR)buffer + SrvComputerName.Length > END_OF_RESPONSE_BUFFER(WorkContext) ) { SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW ); status = STATUS_BUFFER_OVERFLOW; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
RtlCopyMemory( buffer, SrvComputerName.Buffer, SrvComputerName.Length );
SmbPutUshort( &buffer[ SrvComputerName.Length / 2 ], UNICODE_NULL );
byteCount += SrvComputerName.Length + sizeof( UNICODE_NULL ); }
}
SmbPutUshort( &ntResponse->ByteCount, byteCount );
WorkContext->ResponseParameters = NEXT_LOCATION( ntResponse, RESP_NT_NEGOTIATE, byteCount );
} // if !(capabilities & CAP_EXTENDED_SECURITY)
else { CtxtHandle negotiateHandle; ULONG bufferLength; PCHAR buffer;
//
// Reserved if extended security negotiated (MBZ!)
//
ntResponse->EncryptionKeyLength = 0;
//
// SrvGetExtensibleSecurityNegotiateBuffer will fill in the
// securityblob field and return the length of that information.
//
RtlCopyMemory(&ntResponse->Buffer, &ServerGuid, sizeof(ServerGuid) ); byteCount = sizeof(ServerGuid);
buffer = ntResponse->Buffer + byteCount; bufferLength = WorkContext->ResponseBuffer->BufferLength - (ULONG)(buffer - (PCHAR)WorkContext->ResponseBuffer->Buffer);
status = SrvGetExtensibleSecurityNegotiateBuffer( &negotiateHandle, buffer, &bufferLength );
if (!NT_SUCCESS(status)) { SrvSetSmbError(WorkContext, STATUS_ACCESS_DENIED); status = STATUS_ACCESS_DENIED; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
if( bufferLength > 0xFF00 ) { // byteCount is still a USHORT, so don't accept really really big responses
// Note we should never get this since our buffer size is never over 64k, but just in case
SrvSetSmbError(WorkContext, STATUS_ACCESS_DENIED); status = STATUS_ACCESS_DENIED; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
//
// Grab the session locks here...
//
ACQUIRE_LOCK( &connection->Lock );
connection->NegotiateHandle = negotiateHandle;
RELEASE_LOCK( &connection->Lock );
byteCount += (USHORT)bufferLength;
SmbPutUshort( &ntResponse->ByteCount, byteCount );
WorkContext->ResponseParameters = NEXT_LOCATION( ntResponse, RESP_NT_NEGOTIATE, byteCount ); } } // else (NT protocol has been negotiated).
SmbStatus = SmbStatusSendResponse;
IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbNegotiate complete.\n" );
Cleanup: SrvWmiEndContext(WorkContext); return SmbStatus;
} // 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; NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE( ); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_PROCESS_EXIT; SrvWmiStartContext(WorkContext);
IF_SMB_DEBUG(ADMIN1) { SrvPrint2( "Process exit request header at 0x%p, response header at 0x%p\n", WorkContext->RequestHeader, WorkContext->ResponseHeader ); SrvPrint2( "Process exit request parameters at 0x%p, response parameters at 0x%p\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 ); status = STATUS_SMB_BAD_UID; goto Cleanup; }
//
// 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 );
Cleanup: SrvWmiEndContext(WorkContext); 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(); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_SESSION_SETUP_AND_X; SrvWmiStartContext(WorkContext);
//
// This SMB must be processed in a blocking thread.
//
WorkContext->FspRestartRoutine = BlockingSessionSetupAndX; SrvQueueWorkToBlockingThread( WorkContext ); SrvWmiEndContext(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
--*/
{ PREQ_SESSION_SETUP_ANDX request; PREQ_NT_SESSION_SETUP_ANDX ntRequest; PREQ_NT_EXTENDED_SESSION_SETUP_ANDX ntExtendedRequest; PRESP_SESSION_SETUP_ANDX response;
NTSTATUS SecStatus ; NTSTATUS status = STATUS_SUCCESS; SMB_STATUS SmbStatus = SmbStatusInProgress; PSESSION session; PCONNECTION connection; PENDPOINT endpoint; PPAGED_CONNECTION pagedConnection; PTABLE_ENTRY entry; LUID logonId; SHORT uidIndex; USHORT reqAndXOffset; UCHAR nextCommand; PCHAR smbInformation; ULONG smbInformationLength; ULONG returnBufferLength = 0; UNICODE_STRING nameString; UNICODE_STRING domainString; USHORT action = 0; USHORT byteCount; BOOLEAN locksHeld; BOOLEAN isUnicode, isExtendedSecurity; BOOLEAN smbSecuritySignatureRequired = FALSE; BOOLEAN previousSecuritySignatureState;
PAGED_CODE(); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_SESSION_SETUP_AND_X; SrvWmiStartContext(WorkContext);
//
// 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 ); SmbStatus = SmbStatusNoResponse; goto Cleanup;
}
IF_SMB_DEBUG(ADMIN1) { SrvPrint2( "Session setup request header at 0x%p, response header at 0x%p\n", WorkContext->RequestHeader, WorkContext->ResponseHeader ); SrvPrint2( "Session setup request parameters at 0x%p, response parameters at 0x%p\n", WorkContext->RequestParameters, WorkContext->ResponseParameters ); }
//
// Initialize local variables for error cleanup.
//
nameString.Buffer = NULL; domainString.Buffer = NULL; session = NULL; locksHeld = FALSE; isExtendedSecurity = FALSE;
//
// Set up parameters.
//
request = (PREQ_SESSION_SETUP_ANDX)(WorkContext->RequestParameters); ntRequest = (PREQ_NT_SESSION_SETUP_ANDX)(WorkContext->RequestParameters); ntExtendedRequest = (PREQ_NT_EXTENDED_SESSION_SETUP_ANDX)(WorkContext->RequestParameters); response = (PRESP_SESSION_SETUP_ANDX)(WorkContext->ResponseParameters);
connection = WorkContext->Connection; pagedConnection = connection->PagedConnection;
previousSecuritySignatureState = connection->SmbSecuritySignatureActive;
//
// First verify that the SMB format is correct.
//
if ( (connection->SmbDialect <= SmbDialectNtLanMan && (!((request->WordCount == 13) || ((request->WordCount == 12) && ((ntExtendedRequest->Capabilities & CAP_EXTENDED_SECURITY) != 0))))) || (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", (PCSTRING)&connection->OemClientMachineNameString ); } } status = STATUS_INVALID_SMB; goto error_exit1; }
//
// Convert the client name to unicode
//
if ( connection->ClientMachineNameString.Length == 0 ) {
UNICODE_STRING clientMachineName; clientMachineName.Buffer = connection->ClientMachineName; clientMachineName.MaximumLength = (USHORT)(COMPUTER_NAME_LENGTH+1)*sizeof(WCHAR);
(VOID)RtlOemStringToUnicodeString( &clientMachineName, &connection->OemClientMachineNameString, FALSE );
//
// Add the double backslashes to the length
//
connection->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 ) {
if (ntRequest->WordCount == 13) {
connection->ClientCapabilities = SmbGetUlong( &ntRequest->Capabilities ) & ( CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS | CAP_NT_FIND | CAP_NT_STATUS | CAP_DYNAMIC_REAUTH | CAP_EXTENDED_SECURITY | CAP_LEVEL_II_OPLOCKS );
} else {
connection->ClientCapabilities = SmbGetUlong( &ntExtendedRequest->Capabilities ) & ( CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS | CAP_NT_FIND | CAP_NT_STATUS | CAP_DYNAMIC_REAUTH | CAP_EXTENDED_SECURITY | CAP_LEVEL_II_OPLOCKS );
}
if ( connection->ClientCapabilities & CAP_NT_SMBS ) { connection->ClientCapabilities |= CAP_NT_FIND; } }
//
// See if the client is requesting the use of SMB security signatures
//
if( SrvSmbSecuritySignaturesEnabled == TRUE && connection->Endpoint->IsConnectionless == FALSE && connection->SmbSecuritySignatureActive == FALSE && ( SrvSmbSecuritySignaturesRequired == TRUE || (WorkContext->RequestHeader->Flags2 & SMB_FLAGS2_SMB_SECURITY_SIGNATURE)) ) {
smbSecuritySignatureRequired = TRUE;
} else {
smbSecuritySignatureRequired = FALSE;
}
//
// Figure out what kind of security to use, use it to validate the
// session setup request, and construct the session if request checks out.
//
isExtendedSecurity = CLIENT_CAPABLE_OF( EXTENDED_SECURITY, connection );
if( isExtendedSecurity ) { USHORT flags2;
flags2 = SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 ); isExtendedSecurity = ((flags2 & SMB_FLAGS2_EXTENDED_SECURITY) != 0); }
isUnicode = SMB_IS_UNICODE( WorkContext );
if ((connection->SmbDialect <= SmbDialectNtLanMan) && isExtendedSecurity) { //
// We are validating a client using extended security. This meansthat
// there may be multiple round-trips necessary for the SessionSetup&X
// SMB. Each request and response carries a "security blob", which is
// fed into the security system. The security system may generate
// a new blob which is transmitted to the other end. This exchange
// may require an arbitrary number of round trips.
//
PUCHAR securityBuffer; ULONG securityBufferLength;
PRESP_NT_EXTENDED_SESSION_SETUP_ANDX ntExtendedResponse = (PRESP_NT_EXTENDED_SESSION_SETUP_ANDX)( WorkContext->ResponseParameters );
//
// No AndX is permitted with extended security logons
//
if( request->AndXCommand != SMB_COM_NO_ANDX_COMMAND ) {
IF_DEBUG(SMB_ERRORS) { KdPrint(( "No follow-on command allowed for extended SS&X\n" )); }
status = STATUS_INVALID_SMB;
} else {
//
// Clean up old dead connections from this client
//
if( SmbGetUshort( &ntRequest->VcNumber ) == 0 ) { SrvCloseConnectionsFromClient( connection, FALSE ); }
status = GetExtendedSecurityParameters( WorkContext, &securityBuffer, &securityBufferLength, &smbInformation, &smbInformationLength ); }
if (NT_SUCCESS(status)) {
USHORT Uid = SmbGetAlignedUshort(&WorkContext->RequestHeader->Uid);
//
// Let's see if we have a session with this UID already around.
//
if( Uid ) {
session = SrvVerifyUid ( WorkContext, Uid );
if( session != NULL ) { //
// This is an attempt to either refresh the UID, or we are
// in the middle of an extended security negotiation.
//
ACQUIRE_LOCK( &connection->Lock );
if( session->LogonSequenceInProgress == FALSE ) { //
// We are just beginning to start the refresh
// of the UID.
//
session->LogonSequenceInProgress = TRUE; session->IsAdmin = FALSE; session->IsSessionExpired = TRUE; status = SrvFreeSecurityContexts( session );
//
// Reduce the session count, as it will be incremented if authentication succeeds
//
ExInterlockedAddUlong( &SrvStatistics.CurrentNumberOfSessions, -1, &GLOBAL_SPIN_LOCK(Statistics) ); }
RELEASE_LOCK( &connection->Lock );
} else { //
// We don't know anything about the UID which
// the client gave to us.
//
status = STATUS_SMB_BAD_UID; }
} else { //
// This is the first SS&X for this user id
//
SrvAllocateSession( &session, NULL, NULL ); if( session == NULL ) { status = STATUS_INSUFF_SERVER_RESOURCES; } }
if( session != NULL ) {
PSECURITY_CONTEXT SecurityContext = NULL; BOOL bNewContext = FALSE;
//
// Validate the security buffer sent from the client. Note that
// this may change the UserHandle value, so we need to own
// the connection lock.
//
ACQUIRE_LOCK( &connection->Lock );
if( session->LogonSequenceInProgress && session->SecurityContext != NULL ) { SecurityContext = session->SecurityContext; } else { SecurityContext = SrvAllocateSecurityContext(); bNewContext = TRUE; }
if( SecurityContext == NULL ) { status = STATUS_INSUFF_SERVER_RESOURCES; RELEASE_LOCK( &connection->Lock ); } else { //
// Try to authenticate this user. If we get NT_SUCCESS(), then
// the user is fully authenticated. If we get
// STATUS_NOT_MORE_PROCESSING_REQUIRED, then things are going well,
// but we need to do some more exchanges with the client before
// authentication is complete. Anything else is an error
//
returnBufferLength = WorkContext->ResponseBuffer->BufferLength - PTR_DIFF(ntExtendedResponse->Buffer, WorkContext->ResponseBuffer->Buffer);
status = SrvValidateSecurityBuffer( WorkContext->Connection, &SecurityContext->UserHandle, session, securityBuffer, securityBufferLength, smbSecuritySignatureRequired, ntExtendedResponse->Buffer, &returnBufferLength, &session->LogOffTime, session->NtUserSessionKey, &session->LogonId, &session->GuestLogon );
SecStatus = KSecValidateBuffer( ntExtendedResponse->Buffer, returnBufferLength );
if ( !NT_SUCCESS( SecStatus ) ) { #if DBG
KdPrint(( "SRV: invalid buffer from KsecDD: %p,%lx\n", ntExtendedResponse->Buffer, returnBufferLength )); #endif
SrvKsecValidErrors++; }
if( !NT_SUCCESS(status) && (status != STATUS_MORE_PROCESSING_REQUIRED) ) { if( bNewContext ) SrvDereferenceSecurityContext( SecurityContext ); } else { if( bNewContext ) { SrvReplaceSessionSecurityContext( session, SecurityContext, WorkContext ); } session->IsAdmin = SrvIsAdmin( session->SecurityContext->UserHandle ); session->IsNullSession = SrvIsNullSession( session->SecurityContext->UserHandle ); }
RELEASE_LOCK( &connection->Lock );
if( NT_SUCCESS(status) ) { //
// This client is now fully authenticated!
//
session->KickOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF; session->EncryptedLogon = TRUE; session->LogonSequenceInProgress = FALSE; session->IsSessionExpired = FALSE;
if( session->IsNullSession ) { session->LogOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF; }
#if SRVNTVERCHK
//
// If we are restricting the domains of our clients, grab the
// domain string of this client and compare against the list.
// If the client is in the list, set the flag that disallows
// access to disk shares.
//
if( SrvInvalidDomainNames != NULL ) { if( domainString.Buffer == NULL ) { SrvGetUserAndDomainName( session, NULL, &domainString ); }
ACQUIRE_LOCK_SHARED( &SrvConfigurationLock ); if( SrvInvalidDomainNames != NULL && domainString.Buffer != NULL ) { int i; for( i = 0; SrvInvalidDomainNames[i]; i++ ) { if( _wcsicmp( SrvInvalidDomainNames[i], domainString.Buffer ) == 0 ) {
session->ClientBadDomain = TRUE; break; } } } RELEASE_LOCK( &SrvConfigurationLock ); } #endif
} else { if( status == STATUS_MORE_PROCESSING_REQUIRED ) { session->LogonSequenceInProgress = TRUE; } } } } }
} else {
PCHAR caseInsensitivePassword; CLONG caseInsensitivePasswordLength; PCHAR caseSensitivePassword; CLONG caseSensitivePasswordLength;
status = GetNtSecurityParameters( WorkContext, &caseSensitivePassword, &caseSensitivePasswordLength, &caseInsensitivePassword, &caseInsensitivePasswordLength, &nameString, &domainString, &smbInformation, &smbInformationLength );
if (NT_SUCCESS(status)) {
PSECURITY_CONTEXT SecurityContext = SrvAllocateSecurityContext(); if( SecurityContext != NULL ) { SrvAllocateSession( &session, &nameString, &domainString );
if( session != NULL ) {
status = SrvValidateUser( &SecurityContext->UserHandle, session, WorkContext->Connection, &nameString, caseInsensitivePassword, caseInsensitivePasswordLength, caseSensitivePassword, caseSensitivePasswordLength, smbSecuritySignatureRequired, &action );
if( NT_SUCCESS(status) ) { ACQUIRE_LOCK( &connection->Lock ); SrvReplaceSessionSecurityContext( session, SecurityContext, WorkContext ); RELEASE_LOCK( &connection->Lock ); } else { SrvDereferenceSecurityContext( SecurityContext ); }
} else { status = STATUS_INSUFF_SERVER_RESOURCES; SrvDereferenceSecurityContext( SecurityContext ); } } else { status = STATUS_INSUFF_SERVER_RESOURCES; }
} }
//
// Done with the name strings - they were captured into the session
// structure if needed.
//
if (!isUnicode || isExtendedSecurity) {
if (nameString.Buffer != NULL) { RtlFreeUnicodeString( &nameString ); nameString.Buffer = NULL; }
if (domainString.Buffer != NULL) { RtlFreeUnicodeString( &domainString ); domainString.Buffer = NULL; } }
//
// If a bad name/password combination was sent, return an error.
//
if ( !NT_SUCCESS(status) && status != STATUS_MORE_PROCESSING_REQUIRED ) {
IF_DEBUG(ERRORS) { SrvPrint0( "BlockingSessionSetupAndX: Bad user/password combination.\n" ); }
SrvStatistics.LogonErrors++;
goto error_exit;
}
if( previousSecuritySignatureState == FALSE && connection->SmbSecuritySignatureActive == TRUE ) {
//
// We have 'turned on' SMB security signatures. Make sure that the
// signature for the Session Setup & X is correct
//
//
// The client's index was 0
//
WorkContext->SmbSecuritySignatureIndex = 0;
//
// Our response index is 1
//
WorkContext->ResponseSmbSecuritySignatureIndex = 1;
//
// And the next request should be index 2
//
connection->SmbSecuritySignatureIndex = 2; }
//
// If we have a new session, fill in the remaining required information. We
// may be operating on an already existing session if we are in the middle
// of a multi-round-trip extended security blob exchange, or if we are
// renewing a session.
//
if ( WorkContext->Session == NULL ) {
if( connection->SmbDialect <= SmbDialectDosLanMan21 ) {
ACQUIRE_LOCK( &connection->Lock );
if ( connection->ClientOSType.Buffer == NULL ) {
ULONG length; PWCH infoBuffer;
//
// If the SMB buffer is ANSI, adjust the size of the buffer we
// are allocating to Unicode size.
//
if ( isUnicode ) { smbInformation = ALIGN_SMB_WSTR(smbInformation); }
length = isUnicode ? smbInformationLength : smbInformationLength * sizeof( WCHAR ); infoBuffer = ALLOCATE_NONPAGED_POOL( length, BlockTypeDataBuffer );
if ( infoBuffer == NULL ) { RELEASE_LOCK( &connection->Lock ); status = STATUS_INSUFF_SERVER_RESOURCES; goto error_exit; }
connection->ClientOSType.Buffer = (PWCH)infoBuffer; connection->ClientOSType.MaximumLength = (USHORT)length;
//
// Copy the client OS type to the new buffer.
//
length = SrvGetString( &connection->ClientOSType, smbInformation, END_OF_REQUEST_SMB( WorkContext ), isUnicode );
if ( length == (USHORT)-1) { connection->ClientOSType.Buffer = NULL; RELEASE_LOCK( &connection->Lock ); DEALLOCATE_NONPAGED_POOL( infoBuffer ); status = STATUS_INVALID_SMB; goto error_exit; }
smbInformation += length + sizeof( WCHAR );
connection->ClientLanManType.Buffer = (PWCH)( (PCHAR)connection->ClientOSType.Buffer + connection->ClientOSType.Length + sizeof( WCHAR ) );
connection->ClientLanManType.MaximumLength = connection->ClientOSType.MaximumLength - connection->ClientOSType.Length - sizeof( WCHAR );
//
// 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) { connection->ClientOSType.Buffer = NULL; RELEASE_LOCK( &connection->Lock ); DEALLOCATE_NONPAGED_POOL( infoBuffer ); status = STATUS_INVALID_SMB; goto error_exit; }
//
// If we have an NT5 or later client, grab the build number from the
// OS version string.
//
if( isExtendedSecurity && connection->ClientOSType.Length && connection->PagedConnection->ClientBuildNumber == 0 ) {
PWCHAR pdigit = connection->ClientOSType.Buffer; PWCHAR epdigit = pdigit + connection->ClientOSType.Length/sizeof(WCHAR); ULONG clientBuildNumber = 0;
//
// Scan the ClientOSType string to find the last number, and
// convert to a ULONG. It should be the build number
//
while( 1 ) { //
// Scan the string until we find a number.
//
for( ; pdigit < epdigit; pdigit++ ) { if( *pdigit >= L'0' && *pdigit <= L'9' ) { break; } }
//
// If we've hit the end of the string, we are done
//
if( pdigit == epdigit ) { break; }
clientBuildNumber = 0;
//
// Convert the number to a ULONG, assuming it is the build number
//
while( pdigit < epdigit && *pdigit >= L'0' && *pdigit <= '9' ) { clientBuildNumber *= 10; clientBuildNumber += (*pdigit++ - L'0'); } }
connection->PagedConnection->ClientBuildNumber = clientBuildNumber;
#if SRVNTVERCHK
if( SrvMinNT5Client > 0 ) {
BOOLEAN allowThisClient = FALSE; DWORD i;
//
// See if we should allow this client, because it is a well-known
// IP address. This is to allow the build lab to more slowly upgrade
// than the rest of us.
//
if( connection->ClientIPAddress != 0 && connection->Endpoint->IsConnectionless == FALSE ) {
for( i = 0; SrvAllowIPAddress[i]; i++ ) { if( SrvAllowIPAddress[i] == connection->ClientIPAddress ) { allowThisClient = TRUE; break; } } }
if( allowThisClient == FALSE && connection->PagedConnection->ClientBuildNumber < SrvMinNT5Client ) { connection->PagedConnection->ClientTooOld = TRUE; } } #endif
} } RELEASE_LOCK( &connection->Lock ); }
//
// 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 ); }
//
// Make sure the MaxBufferSize is correctly sized
//
session->MaxBufferSize &= ~03;
if( session->MaxBufferSize < SrvMinClientBufferSize ) { //
// Client asked for a buffer size that is too small!
//
IF_DEBUG(ERRORS) { KdPrint(( "BlockingSessionSetupAndX: Bad Client Buffer Size: %u\n", session->MaxBufferSize )); } status = STATUS_INVALID_SMB; goto error_exit; }
session->MaxMpxCount = SmbGetUshort( &request->MaxMpxCount );
if ( session->MaxMpxCount < 2 ) { connection->OplocksAlwaysDisabled = TRUE; } }
//
// If we have completely authenticated the client, and 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( isExtendedSecurity == FALSE && NT_SUCCESS( status ) && SmbGetUshort( &request->VcNumber ) == 0 ) {
UNICODE_STRING userName;
SrvCloseConnectionsFromClient( connection, FALSE );
//
// If a client is smart enough to use extended security, then it
// is presumably smart enough to know what it wants to do with
// its sessions. So don't just blow off sessions from this client.
//
SrvGetUserAndDomainName( session, &userName, NULL );
if( userName.Buffer ) { SrvCloseSessionsOnConnection( connection, &userName ); SrvReleaseUserAndDomainName( session, &userName, NULL ); } }
if( WorkContext->Session == NULL ) {
//
// 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 ) { NTSTATUS TableStatus;
if ( pagedConnection->SessionTable.FirstFreeEntry == -1 && SrvGrowTable( &pagedConnection->SessionTable, SrvInitialSessionTableSize, SrvMaxSessionTableSize, &TableStatus ) == FALSE ) {
//
// No free entries in the user table. Reject the request.
//
IF_DEBUG(ERRORS) { SrvPrint0( "BlockingSessionSetupAndX: No more UIDs available.\n" ); }
if( TableStatus == STATUS_INSUFF_SERVER_RESOURCES ) { // The table size is being exceeded, log an error
SrvLogTableFullError( SRV_TABLE_SESSION ); status = STATUS_SMB_TOO_MANY_UIDS; } else { // Memory allocation error, report it
status = TableStatus; }
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;
connection->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 );
if (isExtendedSecurity) {
BuildExtendedSessionSetupAndXResponse( WorkContext, returnBufferLength, status, nextCommand, isUnicode);
} else {
BuildSessionSetupAndXResponse( WorkContext, nextCommand, action, isUnicode);
}
WorkContext->ResponseParameters = (PCHAR)WorkContext->ResponseHeader + SmbGetUshort( &response->AndXOffset );
//
// Test for legal followon command.
//
switch ( nextCommand ) { case SMB_COM_NO_ANDX_COMMAND: break;
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: //
// Make sure the AndX command is still within the received SMB
//
if( (PCHAR)WorkContext->RequestHeader + reqAndXOffset <= END_OF_REQUEST_SMB( WorkContext ) ) { break; }
/* Falls Through */
default: // Illegal followon command
IF_DEBUG(SMB_ERRORS) { 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 ); SmbStatus = SmbStatusNoResponse; goto Cleanup;
}
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 ) { if( WorkContext->Session ) { //
// A re-validation of the session failed, or the extended exchange
// of security blobs failed. Get rid of this user.
//
SrvCloseSession( session );
SrvStatistics.SessionsLoggedOff++;
//
// Dereference the session, since it's no longer valid
//
SrvDereferenceSession( session );
WorkContext->Session = NULL;
} else {
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 ); SmbStatus = SmbStatusSendResponse;
Cleanup: SrvWmiEndContext(WorkContext); return;
} // BlockingSessionSetupAndX
NTSTATUS GetExtendedSecurityParameters( IN PWORK_CONTEXT WorkContext, OUT PUCHAR *SecurityBuffer, OUT PULONG SecurityBufferLength, OUT PCHAR *RestOfDataBuffer, OUT PULONG RestOfDataLength)
/*++
Routine Description:
Extracts the extensible security parameters from an extended session setup and X SMB.
Arguments:
WorkContext - Context of the SMB
SecurityBuffer - On return, points to the security buffer inside the extended session setup and X SMB
SecurityBufferLength - On return, size in bytes of SecurityBuffer.
RestOfDataBuffer - On return, points just past the security buffer
ResetOfDataLength - On return, size in bytes of *RestOfDataBuffer
Return Value:
STATUS_SUCCESS - This routine merely returns pointers within an SMB
--*/
{ NTSTATUS status; PCONNECTION connection; PREQ_NT_EXTENDED_SESSION_SETUP_ANDX ntExtendedRequest; ULONG maxlength;
connection = WorkContext->Connection; ASSERT( connection->SmbDialect <= SmbDialectNtLanMan );
ntExtendedRequest = (PREQ_NT_EXTENDED_SESSION_SETUP_ANDX) (WorkContext->RequestParameters);
maxlength = (ULONG)(WorkContext->RequestBuffer->DataLength + sizeof( USHORT ) - ((ULONG_PTR)ntExtendedRequest->Buffer - (ULONG_PTR)WorkContext->RequestBuffer->Buffer));
//
// Get the extended security buffer
//
*SecurityBuffer = (PUCHAR) ntExtendedRequest->Buffer; *SecurityBufferLength = ntExtendedRequest->SecurityBlobLength;
*RestOfDataBuffer = ntExtendedRequest->Buffer + ntExtendedRequest->SecurityBlobLength;
*RestOfDataLength = (USHORT)( (PUCHAR)ntExtendedRequest->Buffer + sizeof(USHORT) + SmbGetUshort( &ntExtendedRequest->ByteCount) - (*RestOfDataBuffer) );
if( *SecurityBufferLength > maxlength || *RestOfDataLength > maxlength - *SecurityBufferLength ) {
IF_DEBUG(SMB_ERRORS) { KdPrint(( "GetExtendedSecurityParameters: Invalid security buffer\n" )); }
return STATUS_INVALID_SMB; }
return( STATUS_SUCCESS ); }
NTSTATUS GetNtSecurityParameters( IN PWORK_CONTEXT WorkContext, OUT PCHAR *CaseSensitivePassword, OUT PULONG CaseSensitivePasswordLength, OUT PCHAR *CaseInsensitivePassword, OUT PULONG CaseInsensitivePasswordLength, OUT PUNICODE_STRING UserName, OUT PUNICODE_STRING DomainName, OUT PCHAR *RestOfDataBuffer, OUT PULONG RestOfDataLength) {
NTSTATUS status = STATUS_SUCCESS; PCONNECTION connection; PREQ_NT_SESSION_SETUP_ANDX ntRequest; PREQ_SESSION_SETUP_ANDX request; PSZ userName; USHORT nameLength; BOOLEAN isUnicode;
connection = WorkContext->Connection;
ntRequest = (PREQ_NT_SESSION_SETUP_ANDX)(WorkContext->RequestParameters); request = (PREQ_SESSION_SETUP_ANDX)(WorkContext->RequestParameters);
//
// 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); }
if( (*CaseInsensitivePassword) != NULL && (*CaseInsensitivePassword) + (*CaseInsensitivePasswordLength) > END_OF_REQUEST_SMB( WorkContext ) ) {
status = STATUS_INVALID_SMB; goto error_exit; }
if( (*CaseSensitivePassword) != NULL && (*CaseSensitivePassword) + (*CaseSensitivePasswordLength) > END_OF_REQUEST_SMB( WorkContext ) ) {
status = STATUS_INVALID_SMB; goto error_exit; }
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, UserName, 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, DomainName, smbInformation, &length );
if ( !NT_SUCCESS( status ) ) { goto error_exit; }
smbInformation += length + ( isUnicode ? sizeof(WCHAR) : 1 );
*RestOfDataBuffer = smbInformation;
if (connection->SmbDialect <= SmbDialectNtLanMan) {
*RestOfDataLength = (USHORT) ( (PUCHAR)&ntRequest->ByteCount + sizeof(USHORT) + SmbGetUshort(&ntRequest->ByteCount) - smbInformation ); } else {
PREQ_SESSION_SETUP_ANDX sessionSetupRequest;
sessionSetupRequest = (PREQ_SESSION_SETUP_ANDX)(WorkContext->RequestParameters);
*RestOfDataLength = (USHORT) ( (PUCHAR)&sessionSetupRequest->ByteCount + sizeof(USHORT) + SmbGetUshort(&sessionSetupRequest->ByteCount) - smbInformation );
}
} else {
DomainName->Length = 0;
*RestOfDataBuffer = NULL;
*RestOfDataLength = 0;
}
error_exit:
return( status );
}
VOID BuildExtendedSessionSetupAndXResponse( IN PWORK_CONTEXT WorkContext, IN ULONG ReturnBufferLength, IN NTSTATUS Status, IN UCHAR NextCommand, IN BOOLEAN IsUnicode) { PRESP_NT_EXTENDED_SESSION_SETUP_ANDX ntExtendedResponse; PCHAR buffer; USHORT byteCount; USHORT maxByteCount; NTSTATUS status;
ntExtendedResponse = (PRESP_NT_EXTENDED_SESSION_SETUP_ANDX) (WorkContext->ResponseParameters);
ntExtendedResponse->WordCount = 4; ntExtendedResponse->AndXCommand = NextCommand; ntExtendedResponse->AndXReserved = 0;
if( WorkContext->Session && WorkContext->Session->GuestLogon ) { SmbPutUshort( &ntExtendedResponse->Action, SMB_SETUP_GUEST ); } else { SmbPutUshort( &ntExtendedResponse->Action, 0 ); }
SmbPutUshort( &ntExtendedResponse->SecurityBlobLength,(USHORT)ReturnBufferLength );
buffer = ntExtendedResponse->Buffer + ReturnBufferLength; maxByteCount = (USHORT)(END_OF_RESPONSE_BUFFER(WorkContext) - buffer + 1);
if (IsUnicode) buffer = ALIGN_SMB_WSTR( buffer );
status = InsertNativeOSAndType( IsUnicode, buffer, &maxByteCount ); if( NT_SUCCESS(status) ) { byteCount = maxByteCount; } else { Status = status; }
byteCount += (USHORT)ReturnBufferLength;
SmbPutUshort( &ntExtendedResponse->ByteCount, byteCount );
SmbPutUshort( &ntExtendedResponse->AndXOffset, GET_ANDX_OFFSET( WorkContext->ResponseHeader, WorkContext->ResponseParameters, RESP_NT_EXTENDED_SESSION_SETUP_ANDX, byteCount ) );
//
// Make sure we return the error status here, as the client uses it to
// determine if extra round trips are necessary
//
SrvSetSmbError2 ( WorkContext, Status, TRUE ); }
VOID BuildSessionSetupAndXResponse( IN PWORK_CONTEXT WorkContext, IN UCHAR NextCommand, IN USHORT Action, IN BOOLEAN IsUnicode) {
PRESP_SESSION_SETUP_ANDX response; PCONNECTION connection; PENDPOINT endpoint; PCHAR buffer; USHORT byteCount; USHORT maxByteCount; NTSTATUS status;
response = (PRESP_SESSION_SETUP_ANDX) (WorkContext->ResponseParameters);
connection = WorkContext->Connection;
endpoint = connection->Endpoint;
response->WordCount = 3; response->AndXCommand = NextCommand; response->AndXReserved = 0;
if (connection->SmbDialect <= SmbDialectDosLanMan21) { USHORT OsTypeByteCount;
buffer = response->Buffer;
if (IsUnicode) buffer = ALIGN_SMB_WSTR( buffer );
maxByteCount = OsTypeByteCount = (USHORT)(END_OF_RESPONSE_BUFFER(WorkContext)-buffer+1); byteCount = 0;
status = InsertNativeOSAndType( IsUnicode, buffer, &OsTypeByteCount ); if( NT_SUCCESS(status) ) { byteCount += OsTypeByteCount; } else { SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW ); goto insuff_buffer; }
buffer = buffer + byteCount;
if (connection->SmbDialect <= SmbDialectNtLanMan) {
USHORT stringLength;
if ( IsUnicode ) {
buffer = ALIGN_SMB_WSTR( buffer );
stringLength = endpoint->DomainName.Length + sizeof(UNICODE_NULL); if( byteCount + stringLength > maxByteCount ) { SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW ); goto insuff_buffer; }
RtlCopyMemory( buffer, endpoint->DomainName.Buffer, stringLength );
byteCount += (USHORT)stringLength;
} else {
stringLength = endpoint->OemDomainName.Length + sizeof(CHAR); if( byteCount + stringLength > maxByteCount ) { SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW ); goto insuff_buffer; }
RtlCopyMemory( (PVOID) buffer, endpoint->OemDomainName.Buffer, stringLength );
byteCount += (USHORT)stringLength;
}
}
} else {
insuff_buffer: byteCount = 0; }
SmbPutUshort( &response->ByteCount, 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->AndXOffset, GET_ANDX_OFFSET( WorkContext->ResponseHeader, WorkContext->ResponseParameters, RESP_SESSION_SETUP_ANDX, byteCount ) );
}
NTSTATUS InsertNativeOSAndType( IN BOOLEAN IsUnicode, OUT PCHAR Buffer, IN OUT PUSHORT ByteCount) { USHORT availible = *ByteCount; USHORT stringLength;
*ByteCount = 0;
if ( IsUnicode ) {
stringLength = SrvNativeOS.Length;
if( availible < stringLength ) { return STATUS_BUFFER_OVERFLOW; }
RtlCopyMemory( Buffer, SrvNativeOS.Buffer, stringLength );
*ByteCount = stringLength; availible -= stringLength;
stringLength = SrvNativeLanMan.Length;
if( availible < stringLength ) { return STATUS_BUFFER_OVERFLOW; }
RtlCopyMemory( (PCHAR)Buffer + *ByteCount, SrvNativeLanMan.Buffer, stringLength );
*ByteCount += (USHORT)stringLength; availible -= stringLength;
} else {
stringLength = SrvOemNativeOS.Length;
if( availible < stringLength ) { return STATUS_BUFFER_OVERFLOW; }
RtlCopyMemory( Buffer, SrvOemNativeOS.Buffer, stringLength );
*ByteCount = stringLength; availible -= stringLength;
stringLength = SrvOemNativeLanMan.Length;
if( availible < stringLength ) { return STATUS_BUFFER_OVERFLOW; }
RtlCopyMemory( (PCHAR)Buffer + *ByteCount, SrvOemNativeLanMan.Buffer, stringLength );
*ByteCount += stringLength; availible -= stringLength; }
return STATUS_SUCCESS; }
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;
NTSTATUS status = STATUS_SUCCESS; SMB_STATUS SmbStatus = SmbStatusInProgress;
PAGED_CODE( ); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_LOGOFF_AND_X; SrvWmiStartContext(WorkContext);
IF_SMB_DEBUG(ADMIN1) { SrvPrint2( "Logoff request header at 0x%p, response header at 0x%p\n", WorkContext->RequestHeader, WorkContext->ResponseHeader ); SrvPrint2( "Logoff request parameters at 0x%p, response parameters at 0x%p\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 ); status = STATUS_SMB_BAD_UID; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
//
// 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( GET_BLOCKING_WORK_QUEUE(), (PQUEUEABLE_BLOCK_HEADER)WorkContext );
SmbStatus = SmbStatusInProgress; goto Cleanup; }
//
// 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;
if( WorkContext->SecurityContext ) { SrvDereferenceSecurityContext( WorkContext->SecurityContext ); WorkContext->SecurityContext = 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_NO_ANDX_COMMAND: break;
case SMB_COM_SESSION_SETUP_ANDX: //
// Make sure the AndX command is still within the received SMB
//
if( (PCHAR)WorkContext->RequestHeader + reqAndXOffset <= END_OF_REQUEST_SMB( WorkContext ) ) { break; }
/* Falls Through */
default:
IF_DEBUG(SMB_ERRORS) { SrvPrint1( "SrvSmbLogoffAndX: Illegal followon command: 0x%lx\n", nextCommand ); }
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB ); status = STATUS_INVALID_SMB; SmbStatus = SmbStatusSendResponse; goto Cleanup; }
//
// If there is an AndX command, set up to process it. Otherwise,
// indicate completion to the caller.
//
if ( nextCommand != SMB_COM_NO_ANDX_COMMAND ) {
WorkContext->NextCommand = nextCommand;
WorkContext->RequestParameters = (PCHAR)WorkContext->RequestHeader + reqAndXOffset;
SmbStatus = SmbStatusMoreCommands; goto Cleanup; } SmbStatus = SmbStatusSendResponse; IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbLogoffAndX complete.\n" );
Cleanup: SrvWmiEndContext(WorkContext); return SmbStatus;
} // 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 );
} // GetEncryptionKey
|