1109 lines
34 KiB
C
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;
|
|
}
|
|
|