/*++ 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); VOID InsertNativeOSAndType( IN BOOLEAN IsUnicode, OUT PCHAR Buffer, 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 ( !strcmp( s, StrDialects[serverDialect] ) ) { 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; 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. // 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; } #ifdef INCLUDE_SMB_PERSISTENT if ( bestDialect == SmbDialectNtLanMan2 ) { capabilities |= CAP_PERSISTENT_HANDLES; SrvPrint1( "SrvSmbNegotiate: persistent handles for conn 0x%x.\n", WorkContext->Connection ); } #endif 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; // // 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 ); 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( connection->ClientIPAddress ) { capabilities |= CAP_LARGE_WRITEX; if( SrvSupportsCompression ) { capabilities |= CAP_COMPRESSED_DATA; } } } 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 ); 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; RtlCopyMemory( buffer, ptr, domainLength ); byteCount += domainLength; // // Append the server name to the response. // if( SrvComputerName.Buffer ) { buffer = (PWCHAR)((LPSTR)buffer + domainLength); 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; USHORT 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; status = SrvGetExtensibleSecurityNegotiateBuffer( &negotiateHandle, buffer, &bufferLength ); if (!NT_SUCCESS(status)) { 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 += 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; #ifdef INCLUDE_SMB_PERSISTENT ULONG persistentSessionId = 0; #endif 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 ( 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 ) { 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 | #ifdef INCLUDE_SMB_PERSISTENT CAP_PERSISTENT_HANDLES | #endif 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 | #ifdef INCLUDE_SMB_PERSISTENT CAP_PERSISTENT_HANDLES | #endif CAP_LEVEL_II_OPLOCKS ); } if ( connection->ClientCapabilities & CAP_NT_SMBS ) { connection->ClientCapabilities |= CAP_NT_FIND; } #ifdef INCLUDE_SMB_PERSISTENT if ( connection->SmbDialect <= SmbDialectNtLanMan2 ) { // // do we need to restore a previous lost connection? // if (connection->ClientCapabilities & CAP_PERSISTENT_HANDLES) { while ( persistentSessionId == 0 ) { persistentSessionId = InterlockedIncrement( &SrvGlobalPersistentSessionId ); } } } else { connection->ClientCapabilities &= ~CAP_PERSISTENT_HANDLES; } #endif } // // 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 ) { // // 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 ); // // 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, &session->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++; } RELEASE_LOCK( &connection->Lock ); if( NT_SUCCESS(status) ) { // // This client is now fully authenticated! // session->IsAdmin = SrvIsAdmin( session->UserHandle ); session->IsNullSession = SrvIsNullSession( session->UserHandle ); session->KickOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF; session->EncryptedLogon = TRUE; session->LogonSequenceInProgress = FALSE; session->IsSessionExpired = FALSE; if( session->IsNullSession ) { session->LogOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF; } #ifdef INCLUDE_SMB_PERSISTENT session->PersistentId = persistentSessionId; session->PersistentFileOffset = 0; session->PersistentState = PersistentStateFreed; #endif #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)) { SrvAllocateSession( &session, &nameString, &domainString ); if( session != NULL ) { status = SrvValidateUser( &session->UserHandle, session, WorkContext->Connection, &nameString, caseInsensitivePassword, caseInsensitivePasswordLength, caseSensitivePassword, caseSensitivePasswordLength, smbSecuritySignatureRequired, &action ); } 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; 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 ); 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 request; request = (PREQ_SESSION_SETUP_ANDX)(WorkContext->RequestParameters); *RestOfDataLength = (USHORT) ( (PUCHAR)&request->ByteCount + sizeof(USHORT) + SmbGetUshort(&request->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; 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; if (IsUnicode) buffer = ALIGN_SMB_WSTR( buffer ); InsertNativeOSAndType( IsUnicode, buffer, &byteCount ); 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; 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) { buffer = response->Buffer; if (IsUnicode) buffer = ALIGN_SMB_WSTR( buffer ); InsertNativeOSAndType( IsUnicode, buffer, &byteCount ); buffer = buffer + byteCount; if (connection->SmbDialect <= SmbDialectNtLanMan) { USHORT stringLength; if ( IsUnicode ) { buffer = ALIGN_SMB_WSTR( buffer ); stringLength = endpoint->DomainName.Length + sizeof(UNICODE_NULL); RtlCopyMemory( buffer, endpoint->DomainName.Buffer, stringLength ); byteCount += (USHORT)stringLength; } else { stringLength = endpoint->OemDomainName.Length + sizeof(CHAR); RtlCopyMemory( (PVOID) buffer, endpoint->OemDomainName.Buffer, stringLength ); byteCount += (USHORT)stringLength; } } } else { 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 ) ); } VOID InsertNativeOSAndType( IN BOOLEAN IsUnicode, OUT PCHAR Buffer, OUT PUSHORT ByteCount) { USHORT stringLength; if ( IsUnicode ) { stringLength = (USHORT)(SrvNativeOS.Length + sizeof(UNICODE_NULL)); RtlCopyMemory( Buffer, SrvNativeOS.Buffer, stringLength ); *ByteCount = stringLength; stringLength = SrvNativeLanMan.Length + sizeof(UNICODE_NULL); RtlCopyMemory( (PCHAR)Buffer + *ByteCount, SrvNativeLanMan.Buffer, stringLength ); *ByteCount += (USHORT)stringLength; } else { stringLength = SrvOemNativeOS.Length + sizeof(CHAR); RtlCopyMemory( Buffer, SrvOemNativeOS.Buffer, stringLength ); *ByteCount = stringLength; stringLength = SrvOemNativeLanMan.Length + sizeof(CHAR); RtlCopyMemory( (PCHAR)Buffer + *ByteCount, SrvOemNativeLanMan.Buffer, stringLength ); *ByteCount += (USHORT)stringLength; } } 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( &SrvBlockingWorkQueue, (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; // // 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