windows-nt/Source/XPSP1/NT/base/fs/rdr2/rdbss/smb.mrx/smbsecur.c
2020-09-26 16:20:57 +08:00

1109 lines
34 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
smbsecur.c
Abstract:
This module implements all functions related to enforce the SMB security signature on
transmitting and recieving SMB packages.
Revision History:
Yun Lin [YunLin] 23-December-1997
Notes:
--*/
#include "precomp.h"
extern LONG NumOfBuffersForServerResponseInUse;
extern LIST_ENTRY ExchangesWaitingForServerResponseBuffer;
// this varible defines the maximum number of large buffer allowed to be pre-allocated for the
// server responses that contain the security signature, which prevents it drains all system
// resource under heavy network traffic situation and large read request
LONG MaxNumOfLargeBuffersForServerResponse = 3;
// the buffer size for most of the SMB server responses which don't contain much data
ULONG MinimumBufferSizeForServerResponse = 0x100;
BOOLEAN
SmbCheckSecuritySignature(
IN PSMB_EXCHANGE pExchange,
IN PSMBCE_SERVER Server,
IN ULONG MessageLength,
IN PVOID pBuffer
);
LIST_ENTRY SmbSecurityMdlWaitingExchanges;
NTSTATUS
SmbCeCheckMessageLength(
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
IN PVOID pTsdu, // pointer describing this TSDU, typically a lump of bytes
OUT PULONG pMessageLength
)
/*++
Routine Description:
This routine calculates the server message length based on the SMB response command and data.
Arguments:
BytesIndicated - the bytes that are present in the indication.
BytesAvailable - the total data available
pTsdu - the data
pDataBufferSize - the length of the buffer
Return Value:
STATUS_SUCCESS -
Other Status codes correspond to error situations.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
UCHAR SmbCommand;
PGENERIC_ANDX pSmbBuffer;
PSMB_HEADER pSmbHeader = (PSMB_HEADER)pTsdu;
ULONG ByteCount;
LONG WordCount;
LONG ByteLeft = BytesIndicated - sizeof(SMB_HEADER);
if (ByteLeft < 0) {
return STATUS_INVALID_NETWORK_RESPONSE;
}
*pMessageLength = sizeof(SMB_HEADER);
SmbCommand = pSmbHeader->Command;
pSmbBuffer = (PGENERIC_ANDX)(pSmbHeader + 1);
do {
switch (SmbCommand) {
case SMB_COM_LOCKING_ANDX:
case SMB_COM_WRITE_ANDX:
case SMB_COM_SESSION_SETUP_ANDX:
case SMB_COM_LOGOFF_ANDX:
case SMB_COM_TREE_CONNECT_ANDX:
case SMB_COM_NT_CREATE_ANDX:
case SMB_COM_OPEN_ANDX:
SmbCommand = pSmbBuffer->AndXCommand;
*pMessageLength = pSmbBuffer->AndXOffset;
pSmbBuffer = (PGENERIC_ANDX)((PUCHAR)pTsdu + pSmbBuffer->AndXOffset);
break;
case SMB_COM_READ_ANDX:
{
PRESP_READ_ANDX ReadAndX = (PRESP_READ_ANDX)pSmbBuffer;
WordCount = (ULONG)pSmbBuffer->WordCount;
if (ReadAndX->DataLengthHigh > 0) {
ByteCount = ReadAndX->DataLengthHigh << 16;
ByteCount += ReadAndX->DataLength;
} else {
ByteCount = *(PUSHORT)((PCHAR)pSmbBuffer + 1 + WordCount*sizeof(USHORT));
}
*pMessageLength += (WordCount+1)*sizeof(USHORT) + ByteCount + 1;
SmbCommand = SMB_COM_NO_ANDX_COMMAND;
break;
}
default:
WordCount = (ULONG)pSmbBuffer->WordCount;
if (ByteLeft > (signed)sizeof(USHORT)*WordCount) {
ByteCount = *(PUSHORT)((PCHAR)pSmbBuffer + 1 + WordCount*sizeof(USHORT));
} else {
ByteCount = 0;
}
*pMessageLength += (WordCount+1)*sizeof(USHORT) + ByteCount + 1;
SmbCommand = SMB_COM_NO_ANDX_COMMAND;
}
ByteLeft = BytesIndicated - *pMessageLength;
if (ByteLeft < 0) {
Status = STATUS_MORE_PROCESSING_REQUIRED;
break;
}
} while (SmbCommand != SMB_COM_NO_ANDX_COMMAND);
return Status;
}
NTSTATUS
SmbCeReceiveIndWithSecuritySignature(
IN PSMBCEDB_SERVER_ENTRY pServerEntry,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *pBytesTaken,
IN PVOID pTsdu,
OUT PMDL *pDataBufferPointer,
OUT PULONG pDataBufferSize,
IN ULONG ReceiveFlags
)
/*++
Routine Description:
This routine handles the SMB server response that contains the security signature. There are 3
scenaorios handled in this routine.
1. TDI indicates the entire SMB message, and SmbCeReceiveInd returns STATUS_SUCCESS;
2. TDI indicates the entire SMB message, and SmbceReceiveInd returns STATUS_MORE_PROCESSING_REQUIRED;
3. TDI indicates partial SMB message.
For corresponding solution are:
1. Check the security signature and calls SmbCeReceiveInd
2. Check the security signature and calls SmbCeReceiveInd, then call SmbCeDataReadyInd for the
rest of the message
3. Use the pre-allocated buffer for the entir SMB message and return to TDI
In case of bad security signatre, an error status is set on the SMB header and the receive exchange
routine will drop the message.
Arguments:
pServerEntry - the server entry
BytesIndicated - the bytes that are present in the indication.
BytesAvailable - the total data available
pTsdu - pointer describing this TSDU, typically a lump of bytes
pDataBufferPointer - the buffer for copying the data not indicated.
pDataBufferSize - the length of the buffer
Return Value:
STATUS_SUCCESS -
Other Status codes correspond to error situations.
--*/
{
NTSTATUS Status;
PSMB_HEADER pSmbHeader = (PSMB_HEADER)pTsdu;
PSMB_EXCHANGE pExchange;
ULONG MessageLength;
// Perform the quick tests by which ill formed SMB's, mangled SMB's can be rejected.
// e.g., any indication which is of non zero length whihc is less then the length of
// a SMB_HEADER cannot be a valid SMB.
if ((BytesAvailable < sizeof(SMB_HEADER) + 3) ||
(SmbGetUlong(((PULONG )pSmbHeader->Protocol)) != (ULONG)SMB_HEADER_PROTOCOL) ||
(pSmbHeader->Command == SMB_COM_NO_ANDX_COMMAND) ) {
RxLog(("SmbCeReceiveInd: Invalid Response for %lx\n",pServerEntry));
SmbLogError(STATUS_UNSUCCESSFUL,
LOG,
SmbCeReceiveInd,
LOGPTR(pServerEntry)
LOGUSTR(pServerEntry->Name));
*pBytesTaken = BytesIndicated;
return STATUS_SUCCESS;
}
ASSERT(pServerEntry->Header.ObjectType == SMBCEDB_OT_SERVER);
if (pSmbHeader->Command == SMB_COM_ECHO) {
Status = SmbCeReceiveInd(
pServerEntry,
BytesIndicated,
BytesAvailable,
pBytesTaken,
pTsdu,
pDataBufferPointer,
pDataBufferSize,
ReceiveFlags);
return Status;
}
//RxLog(("Smb (Rece) %lx %lx %lx\n",pServerEntry,pSmbHeader->Command,pSmbHeader->Mid));
// Perform the tests for detecting oplock break SMB's. These are SMB's with the
// command SMB_COM_LOCKING_ANDX with the LOCKING_ANDX_OPLOCK_RELEASE bit set.
// These SMB's are transformed into buffering state change requests which are
// processed by the RDBSS.
// CODE.IMPROVEMENT -- raw mode handling needs to be incorporated
//
if (pSmbHeader->Command == SMB_COM_LOCKING_ANDX) {
if (BytesIndicated == LOCK_BROKEN_SIZE) {
PREQ_LOCKING_ANDX pOplockBreakRequest = (PREQ_LOCKING_ANDX)(pSmbHeader + 1);
if (SmbGetUshort(&pOplockBreakRequest->LockType) & LOCKING_ANDX_OPLOCK_RELEASE) {
Status = SmbCeReceiveInd(
pServerEntry,
BytesIndicated,
BytesAvailable,
pBytesTaken,
pTsdu,
pDataBufferPointer,
pDataBufferSize,
ReceiveFlags);
return Status;
}
}
}
// Handle the cases when the server responds to the oplock break response.
if ((pSmbHeader->Mid == SMBCE_MAILSLOT_OPERATION_MID) ||
(pSmbHeader->Mid == SMBCE_OPLOCK_RESPONSE_MID)) {
Status = SmbCeReceiveInd(
pServerEntry,
BytesIndicated,
BytesAvailable,
pBytesTaken,
pTsdu,
pDataBufferPointer,
pDataBufferSize,
ReceiveFlags);
return Status;
}
pExchange = SmbCeMapMidToExchange(pServerEntry,pSmbHeader->Mid);
// some exchange might have been initiated before the security signature is enabled.
// In this case, we should avoid security signature check.
if (pExchange != NULL && pExchange->IsSecuritySignatureEnabled) {
if (BytesAvailable > BytesIndicated ||
!FlagOn(ReceiveFlags,TDI_RECEIVE_ENTIRE_MESSAGE)) {
ASSERT(pExchange->MdlForServerResponse != NULL &&
pExchange->MdlForServerResponse->ByteCount >= BytesAvailable);
*pBytesTaken = 0;
*pDataBufferPointer = pExchange->MdlForServerResponse;
*pDataBufferSize = pExchange->MdlForServerResponse->ByteCount;
Status = SmbCeAssociateBufferWithExchange(pServerEntry,pExchange,*pDataBufferPointer);
if (Status == STATUS_SUCCESS) {
SmbCeIncrementPendingCopyDataOperations(pExchange);
Status = STATUS_MORE_PROCESSING_REQUIRED;
} else {
DbgPrint("MRxSmb:Fail to associate MDL witn exchange. %lx\n",Status);
pExchange->Status = Status;
*pBytesTaken = BytesIndicated;
Status = STATUS_SUCCESS;
}
} else {
if (!SmbCheckSecuritySignature(pExchange,
&pServerEntry->Server,
BytesIndicated,
pTsdu)) {
pSmbHeader->ErrorClass = SMB_ERR_CLASS_SERVER;
SmbPutUshort(&pSmbHeader->Error, ERROR_UNEXP_NET_ERR);
RxLog(("SmbCeReceiveInd: Invalid Security Signature\n"));
SmbLogError(STATUS_UNSUCCESSFUL,
LOG,
SmbCeReceiveIndWithSecuritySignature,
LOGPTR(pServerEntry)
LOGUSTR(pServerEntry->Name));
}
Status = SmbCeReceiveInd(
pServerEntry,
BytesIndicated,
BytesAvailable,
pBytesTaken,
pTsdu,
pDataBufferPointer,
pDataBufferSize,
ReceiveFlags);
if (Status==STATUS_MORE_PROCESSING_REQUIRED) {
ULONG BytesCopied;
Status = TdiCopyBufferToMdl(
pTsdu,
*pBytesTaken,
BytesIndicated,
*pDataBufferPointer,
0,
&BytesCopied);
SmbCeDataReadyInd(pServerEntry,
*pDataBufferPointer,
BytesCopied,
Status);
}
Status = STATUS_SUCCESS;
*pBytesTaken = BytesIndicated;
}
} else {
if (pExchange != NULL) {
Status = SmbCeReceiveInd(
pServerEntry,
BytesIndicated,
BytesAvailable,
pBytesTaken,
pTsdu,
pDataBufferPointer,
pDataBufferSize,
ReceiveFlags);
} else {
RxLog(("SmbCeReceiveInd:No resumption context %lx\n",pServerEntry));
Status = STATUS_SUCCESS;
*pBytesTaken = BytesIndicated;
}
}
return Status;
}
NTSTATUS
SmbCeDataReadyIndWithSecuritySignature(
IN PSMBCEDB_SERVER_ENTRY pServerEntry,
IN PMDL pBuffer,
IN ULONG DataSize,
IN NTSTATUS CopyDataStatus
)
/*++
Routine Description:
This routine handles the indication when the requested data has been copied which contains security
signature. In case of bad security signature, an error is set on SMB message header and the receive
exchange routine will drop the message.
Arguments:
pServerEntry - the server instance
pBuffer - the buffer being returned
DataSize - the amount of data copied in bytes
Return Value:
STATUS_SUCCESS - the server call construction has been finalized.
Other Status codes correspond to error situations.
--*/
{
NTSTATUS Status;
ULONG BytesTaken;
PMDL pDataBufferPointer;
ULONG DataBufferSize;
PSMB_EXCHANGE pExchange = SmbCeGetExchangeAssociatedWithBuffer(pServerEntry,pBuffer);
// some exchange might have been initiated before the security signature is enabled.
// In this case, we should avoid security signature check.
if (pExchange != NULL && pExchange->IsSecuritySignatureEnabled) {
if (CopyDataStatus == STATUS_SUCCESS) {
PSMB_HEADER pSmbHeader = (PSMB_HEADER)pExchange->BufferForServerResponse;
if (!SmbCheckSecuritySignature(pExchange,
&pServerEntry->Server,
DataSize,
pExchange->BufferForServerResponse)) {
pSmbHeader->ErrorClass = SMB_ERR_CLASS_SERVER;
SmbPutUshort(&pSmbHeader->Error, ERROR_UNEXP_NET_ERR);
RxLog(("SmbCeDataReadyInd: Invalid Security Signature\n"));
SmbLogError(STATUS_UNSUCCESSFUL,
LOG,
SmbCeDataReadyIndWithSecuritySignature,
LOGPTR(pServerEntry)
LOGUSTR(pServerEntry->Name));
}
Status = SmbCeReceiveInd(
pServerEntry,
DataSize,
DataSize,
&BytesTaken,
pExchange->BufferForServerResponse,
&pDataBufferPointer,
&DataBufferSize,
TDI_RECEIVE_ENTIRE_MESSAGE);
if (Status==STATUS_MORE_PROCESSING_REQUIRED) {
ULONG BytesCopied;
ASSERT(DataBufferSize >= DataSize - BytesTaken);
Status = TdiCopyBufferToMdl(
pExchange->BufferForServerResponse,
BytesTaken,
DataSize,
pDataBufferPointer,
0,
&BytesCopied);
SmbCeDataReadyInd(pServerEntry,
pDataBufferPointer,
BytesCopied,
Status);
}
} else {
// Resume the exchange that was waiting for the receive.
pExchange->Status = STATUS_CONNECTION_DISCONNECTED;
pExchange->SmbStatus = STATUS_CONNECTION_DISCONNECTED;
SmbCeDecrementPendingReceiveOperationsAndFinalize(pExchange);
}
// Resume the exchange that was waiting for the data.
SmbCeDecrementPendingCopyDataOperationsAndFinalize(pExchange);
} else {
if (pExchange != NULL) {
if (CopyDataStatus == STATUS_SUCCESS) {
// Notify the exchange of the completion
//ExInterlockedAddLargeStatistic(&MRxSmbStatistics.SmbsReceived,1);
ExInterlockedAddLargeStatistic(&MRxSmbStatistics.BytesReceived,DataSize);
SMB_EXCHANGE_DISPATCH(
pExchange,
CopyDataHandler,
(pExchange,pBuffer,DataSize));
} else {
pExchange->Status = CopyDataStatus;
pExchange->SmbStatus = CopyDataStatus;
}
// Resume the exchange that was waiting for the data.
SmbCeDecrementPendingCopyDataOperationsAndFinalize(pExchange);
} else {
// The data MDL is part of the exchange, which should be freed with the exchange.
ASSERT(FALSE);
}
}
return STATUS_SUCCESS;
}
NTSTATUS
SmbCeSyncExchangeForSecuritySignature(
PSMB_EXCHANGE pExchange
)
/*++
Routine Description:
This routines puts the exchange on the list waiting for the previous extended session
setup to finish in order to serialize the requests sent to the server with security
signature enabled.
Arguments:
pExchange - the smb exchange
Return Value:
STATUS_SUCCESS - the exchange can be initiated.
STATUS_PENDING - the exchange can be resumed after the extended session setup finishes
Other Status codes correspond to error situations.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PSMBCEDB_SERVER_ENTRY pServerEntry;
PSMBCEDB_SESSION_ENTRY pSessionEntry;
KEVENT SmbCeSynchronizationEvent;
PSMBCEDB_REQUEST_ENTRY pRequestEntry = NULL;
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
pSessionEntry = SmbCeGetExchangeSessionEntry(pExchange);
if (!pSessionEntry->SessionRecoverInProgress) {
if (!pServerEntry->ExtSessionSetupInProgress) {
if (pExchange->Type == EXTENDED_SESSION_SETUP_EXCHANGE) {
// if this is the first extended session setup, let it proceed
pServerEntry->ExtSessionSetupInProgress = TRUE;
}
return Status;
}
} else {
if (pExchange->Type == EXTENDED_SESSION_SETUP_EXCHANGE) {
// if this is the extended session setup, let it proceed
return Status;
}
}
// We are performing an operation that does not attempt reconnects, so it will
// not recover from the disconnect/lack of session. We should simply abort here.
if( !(pExchange->SmbCeFlags & SMBCE_EXCHANGE_ATTEMPT_RECONNECTS) )
{
return STATUS_CONNECTION_DISCONNECTED;
}
pRequestEntry = (PSMBCEDB_REQUEST_ENTRY)SmbMmAllocateObject(SMBCEDB_OT_REQUEST);
if (pRequestEntry != NULL) {
pRequestEntry->Request.pExchange = pExchange;
SmbCeIncrementPendingLocalOperations(pExchange);
SmbCeAddRequestEntry(&pServerEntry->SecuritySignatureSyncRequests,pRequestEntry);
if (pExchange->pSmbCeSynchronizationEvent != NULL) {
Status = STATUS_PENDING;
} else {
KeInitializeEvent(
&SmbCeSynchronizationEvent,
SynchronizationEvent,
FALSE);
pExchange->pSmbCeSynchronizationEvent = &SmbCeSynchronizationEvent;
SmbCeReleaseResource();
KeWaitForSingleObject(
&SmbCeSynchronizationEvent,
Executive,
KernelMode,
FALSE,
NULL);
SmbCeAcquireResource();
pExchange->pSmbCeSynchronizationEvent = NULL;
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return Status;
}
NTSTATUS
SmbCeAllocateBufferForServerResponse(
PSMB_EXCHANGE pExchange
)
/*++
Routine Description:
This routine allocates the buffer for server response in case that the TDI doesn't indicates the
entire message once. The security siganture cannot be checked until entire message is indicated.
Normally, we allocate 0x100 for the short message from the server. In case of read and transact
exchanges, we allocate the buffer large enough to handle the message that server might return.
Since the large buffer will consum a lot of system memory, we only allow a certain amount of this
kind pre-allocated buffers outstanding, which cannot exceed the MaxNumOfLargeBuffersForServerResponse
defined at beginning of this module. If the request exceeds the limitation, the exchange acquiring
the buffer is put onto sleep until and existing large buffer is freed by another exchange.
Arguments:
pExchange - the smb exchange
Return Value:
STATUS_SUCCESS - the server call construction has been finalized.
Other Status codes correspond to error situations.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG BufferSize = MinimumBufferSizeForServerResponse;
PVOID Buffer = NULL;
PMDL Mdl = NULL;
PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
PSMBCE_SERVER pServer = &pServerEntry->Server;
switch (pExchange->Type) {
case ORDINARY_EXCHANGE:
{
PLOWIO_CONTEXT LowIoContext = &pExchange->RxContext->LowIoContext;
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange = (PSMB_PSE_ORDINARY_EXCHANGE)pExchange;
PSMBCE_NET_ROOT pNetRoot = SmbCeGetExchangeNetRoot(OrdinaryExchange);
switch(OrdinaryExchange->EntryPoint) {
case SMBPSE_OE_FROM_READ:
BufferSize += min(LowIoContext->ParamsFor.ReadWrite.ByteCount,
pNetRoot->MaximumReadBufferSize);
break;
case SMBPSE_OE_FROM_QUERYDIRECTORY:
case SMBPSE_OE_FROM_QUERYFILEINFO:
case SMBPSE_OE_FROM_QUERYVOLUMEINFO:
BufferSize += min(LowIoContext->ParamsFor.FsCtl.OutputBufferLength,
pNetRoot->MaximumReadBufferSize);
break;
}
}
break;
case TRANSACT_EXCHANGE:
{
PSMB_TRANSACT_EXCHANGE TransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;
BufferSize += TransactExchange->ReceiveDataBufferSize +
TransactExchange->ReceiveParamBufferSize +
TransactExchange->ReceiveSetupBufferSize;
}
break;
case CONSTRUCT_NETROOT_EXCHANGE:
case EXTENDED_SESSION_SETUP_EXCHANGE:
// for netroot and session setup request, we cannot predict how many bytes
// the server is going to return. For doscore, the MaximumBufferSize is 0.
BufferSize = max(pServer->MaximumBufferSize,MinimumBufferSizeForServerResponse);
break;
case ADMIN_EXCHANGE:
break;
default:
break;
}
if ((BufferSize > MinimumBufferSizeForServerResponse) &&
(pExchange->Type != EXTENDED_SESSION_SETUP_EXCHANGE) &&
FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION)) {
LONG NumOfBuffers;
SmbCeAcquireResource();
NumOfBuffers = InterlockedIncrement(&NumOfBuffersForServerResponseInUse);
if (NumOfBuffers > MaxNumOfLargeBuffersForServerResponse) {
if (!IsListEmpty(&pExchange->ExchangeList)) {
RemoveEntryList(&pExchange->ExchangeList);
}
InsertTailList(
&ExchangesWaitingForServerResponseBuffer,
&pExchange->ExchangeList);
SmbCeIncrementPendingLocalOperations(pExchange);
InterlockedDecrement(&NumOfBuffersForServerResponseInUse);
Status = STATUS_PENDING;
}
else
{
// We incremented the NumOfBuffers counter, mark it
pExchange->SmbCeFlags |= SMBCE_EXCHANGE_SIGNATURE_BUFFER_ALLOCATED;
}
SmbCeReleaseResource();
}
if (Status == STATUS_SUCCESS) {
Buffer = RxAllocatePoolWithTag(PagedPool,BufferSize,MRXSMB_MISC_POOLTAG);
if (Buffer != NULL) {
Mdl = RxAllocateMdl(Buffer, BufferSize);
if (Mdl != NULL) {
RxProbeAndLockPages(Mdl,KernelMode,IoModifyAccess,Status);
if (Status == STATUS_SUCCESS) {
if (MmGetSystemAddressForMdlSafe(Mdl,LowPagePriority) == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if (Status != STATUS_SUCCESS &&
FlagOn( pExchange->SmbCeFlags, SMBCE_EXCHANGE_SIGNATURE_BUFFER_ALLOCATED) ) {
InterlockedDecrement(&NumOfBuffersForServerResponseInUse);
}
}
ASSERT(pExchange->MdlForServerResponse == NULL);
ASSERT(pExchange->BufferForServerResponse == NULL);
if (Status == STATUS_SUCCESS) {
pExchange->BufferForServerResponse = Buffer;
pExchange->MdlForServerResponse = Mdl;
pExchange->SmbCeState = SMBCE_EXCHANGE_SECURITYBUFFER_INITIALIZED;
} else {
if (Buffer != NULL) {
RxFreePool(Buffer);
}
if (Mdl != NULL) {
IoFreeMdl(Mdl);
}
// We did not succeed, so make sure its not marked as having incremented the counter
pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_SIGNATURE_BUFFER_ALLOCATED;
}
return Status;
}
VOID
SmbCeFreeBufferForServerResponse(
PSMB_EXCHANGE pExchange
)
/*++
Routine Description:
This routine frees the buffer for server response and resume one exchange, if exists,
that is waiting for allocating the large buffer.
Arguments:
pExchange - the smb exchange
Return Value:
none
--*/
{
PSMB_EXCHANGE pWaitingExchange = NULL;
if (pExchange->MdlForServerResponse != NULL) {
if ( FlagOn(pExchange->SmbCeFlags, SMBCE_EXCHANGE_SIGNATURE_BUFFER_ALLOCATED) ) {
PLIST_ENTRY pListHead = &ExchangesWaitingForServerResponseBuffer;
SmbCeAcquireResource();
InterlockedDecrement(&NumOfBuffersForServerResponseInUse);
ClearFlag( pExchange->SmbCeFlags, SMBCE_EXCHANGE_SIGNATURE_BUFFER_ALLOCATED );
if (!IsListEmpty(pListHead)) {
PLIST_ENTRY pListEntry;
pListEntry = pListHead->Flink;
RemoveHeadList(pListHead);
pWaitingExchange = (PSMB_EXCHANGE)CONTAINING_RECORD(pListEntry,SMB_EXCHANGE,ExchangeList);
}
SmbCeReleaseResource();
}
RxUnlockHeaderPages(pExchange->MdlForServerResponse);
IoFreeMdl(pExchange->MdlForServerResponse);
pExchange->MdlForServerResponse = NULL;
}
if (pExchange->BufferForServerResponse != NULL) {
RxFreePool(pExchange->BufferForServerResponse);
pExchange->BufferForServerResponse = NULL;
}
if (pWaitingExchange != NULL) {
NTSTATUS Status = STATUS_SUCCESS;
InitializeListHead(&pWaitingExchange->ExchangeList);
if (pWaitingExchange->pSmbCeSynchronizationEvent == NULL) {
SmbCeResumeExchange(pWaitingExchange);
SmbCeDecrementPendingLocalOperationsAndFinalize(pWaitingExchange);
} else {
SmbCeDecrementPendingLocalOperations(pWaitingExchange);
KeSetEvent(
pWaitingExchange->pSmbCeSynchronizationEvent,
0,
FALSE);
}
}
}
VOID
SmbInitializeSmbSecuritySignature(
IN OUT PSMBCE_SERVER Server,
IN PUCHAR SessionKey,
IN PUCHAR ChallengeResponse,
IN ULONG ChallengeResponseLength
)
/*++
Routine Description:
Initializes the security signature generator for a session by calling MD5Update
on the session key, challenge response
Arguments:
SessionKey - Either the LM or NT session key, depending on which
password was used for authentication, must be at least 16 bytes
ChallengeResponse - The challenge response used for authentication, must
be at least 24 bytes
--*/
{
//DbgPrint( "MRxSmb: Initialize Security Signature Intermediate Contex\n");
RtlZeroMemory(&Server->SmbSecuritySignatureIntermediateContext, sizeof(MD5_CTX));
MD5Init(&Server->SmbSecuritySignatureIntermediateContext);
if (SessionKey != NULL) {
MD5Update(&Server->SmbSecuritySignatureIntermediateContext,
(PCHAR)SessionKey,
MSV1_0_USER_SESSION_KEY_LENGTH);
}
MD5Update(&Server->SmbSecuritySignatureIntermediateContext,
(PCHAR)ChallengeResponse,
ChallengeResponseLength);
Server->SmbSecuritySignatureIndex = 0;
}
BOOLEAN DumpSecuritySignature = FALSE;
NTSTATUS
SmbAddSmbSecuritySignature(
IN PSMBCE_SERVER Server,
IN OUT PMDL Mdl,
IN OUT ULONG *ServerIndex,
IN ULONG SendLength
)
/*++
Routine Description:
Generates the next security signature
Arguments:
WorkContext - the context to sign
Return Value:
none.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
MD5_CTX Context;
PSMB_HEADER Smb;
PCHAR SysAddress;
ULONG MessageLength = 0;
Smb = MmGetSystemAddressForMdlSafe(Mdl,LowPagePriority);
if (Smb == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//SmbPutUshort(&Smb->Gid,(USHORT)Server->SmbSecuritySignatureIndex+1);
SmbPutUlong(Smb->SecuritySignature,Server->SmbSecuritySignatureIndex);
*ServerIndex = Server->SmbSecuritySignatureIndex+1; //Index of server response
RtlZeroMemory(Smb->SecuritySignature + sizeof(ULONG),
SMB_SECURITY_SIGNATURE_LENGTH-sizeof(ULONG));
//
// Start out with our initial context
//
RtlCopyMemory( &Context, &Server->SmbSecuritySignatureIntermediateContext, sizeof( Context ) );
//
// Compute the signature for the SMB we're about to send
//
do {
SysAddress = MmGetSystemAddressForMdlSafe(Mdl,LowPagePriority);
if (SysAddress == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
if (Mdl->ByteCount >= SendLength) {
MD5Update(&Context, SysAddress, SendLength);
MessageLength += SendLength;
SendLength = 0;
ASSERT(Mdl->Next == NULL);
break;
} else {
MD5Update(&Context, SysAddress, Mdl->ByteCount);
SendLength -= Mdl->ByteCount;
MessageLength += Mdl->ByteCount;
ASSERT(Mdl->Next != NULL);
}
} while( (Mdl = Mdl->Next) != NULL );
MD5Final( &Context );
// Put the signature into the SMB
RtlCopyMemory(
Smb->SecuritySignature,
Context.digest,
SMB_SECURITY_SIGNATURE_LENGTH
);
if (DumpSecuritySignature) {
DbgPrint("Add Signature: index %u length %u\n", *ServerIndex-1,MessageLength);
}
return STATUS_SUCCESS;
}
VOID
SmbDumpSignatureError(
IN PSMB_EXCHANGE pExchange,
IN PUCHAR ExpectedSignature,
IN PUCHAR ActualSignature,
IN ULONG Length
)
/*++
Routine Description:
Print the mismatched signature information to the debugger
Arguments:
Return Value:
none.
--*/
{
PWCHAR p;
DWORD i;
PSMBCEDB_SERVER_ENTRY pServerEntry = (PSMBCEDB_SERVER_ENTRY)SmbCeGetExchangeServerEntry(pExchange);
//
// Security Signature Mismatch!
//
//DbgPrint("MRXSMB: Bad security signature from %wZ ", &pServerEntry->Name);
DbgPrint("\n\t Wanted: ");
for( i = 0; i < SMB_SECURITY_SIGNATURE_LENGTH; i++ ) {
DbgPrint( "%X ", ExpectedSignature[i] & 0xff );
}
DbgPrint("\n\tReceived: ");
for( i = 0; i < SMB_SECURITY_SIGNATURE_LENGTH; i++ ) {
DbgPrint( "%X ", ActualSignature[i] & 0xff );
}
DbgPrint("\n\tLength %u, Expected Index Number %X\n", Length, pExchange->SmbSecuritySignatureIndex);
}
BOOLEAN
SmbCheckSecuritySignature(
IN PSMB_EXCHANGE pExchange,
IN PSMBCE_SERVER Server,
IN ULONG MessageLength,
IN PVOID pBuffer
)
/*++
Routine Description:
This routine checks whether the security signature on the server response matches the one that is
calculated on the client machine.
Arguments:
Return Value:
A BOOLEAN value is returned to indicated whether the security signature matches.
--*/
{
MD5_CTX Context;
CHAR SavedSignature[ SMB_SECURITY_SIGNATURE_LENGTH ];
PSMB_HEADER Smb = (PSMB_HEADER)pBuffer;
ULONG ServerIndex;
BOOLEAN Correct;
//
// Initialize the Context
//
RtlCopyMemory(&Context, &Server->SmbSecuritySignatureIntermediateContext, sizeof(Context));
//
// Save the signature that's presently in the SMB
//
RtlCopyMemory( SavedSignature, Smb->SecuritySignature, sizeof( SavedSignature ));
//
// Put the correct (expected) signature index into the buffer
//
SmbPutUlong( Smb->SecuritySignature, pExchange->SmbSecuritySignatureIndex );
RtlZeroMemory( Smb->SecuritySignature + sizeof(ULONG),
SMB_SECURITY_SIGNATURE_LENGTH-sizeof(ULONG));
//
// Compute what the signature should be
//
MD5Update(&Context, (PUCHAR)pBuffer, (UINT)MessageLength);
MD5Final(&Context);
//
// Put the signature back
//
//RtlCopyMemory( Smb->SecuritySignature, SavedSignature, sizeof( Smb->SecuritySignature ));
//
// Now compare them!
//
if( RtlCompareMemory( Context.digest, SavedSignature, sizeof( SavedSignature ) ) !=
sizeof( SavedSignature ) ) {
//SmbDumpSignatureError(pExchange,
// Context.digest,
// SavedSignature,
// MessageLength);
DbgPrint("MRXSMB: SS mismatch command %X, Length %X, Expected Index Number %X\n",
Smb->Command, MessageLength, pExchange->SmbSecuritySignatureIndex);
DbgPrint(" server send length %X, mdl length %X index %X\n",
SmbGetUshort(&Smb->PidHigh), SmbGetUshort(&Smb->Pid), SmbGetUshort(&Smb->Gid));
//DbgBreakPoint();
SmbCeTransportDisconnectIndicated(pExchange->SmbCeContext.pServerEntry);
RxLogFailure(
MRxSmbDeviceObject,
NULL,
EVENT_RDR_SECURITY_SIGNATURE_MISMATCH,
STATUS_UNSUCCESSFUL);
return FALSE;
}
return TRUE;
}