Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

646 lines
18 KiB

/*++
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;
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
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 ||
pServerEntry->SecuritySignaturesEnabled &&
!pServerEntry->SecuritySignaturesActive) {
//
// if security signature is enabled and not yet turned on, exchange should wait for
// outstanding extended session setup to finish before resume in order to avoid index mismatch.
//
RxLog(("** Syncing xchg %lx for sess recovery.\n",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;
}
}
//
// If we are performing an operation that does not attempt reconnects, it will
// not recover from the disconnect/lack of session. We should simply abort here.
// However, if we are retrying because of a session expiry (indicated by a RECOVER
// flag on the session entry.), then we retry regardless of the exchange flag.
//
if( ! ( pSessionEntry->Header.State == SMBCEDB_RECOVER ||
pSessionEntry->SessionRecoverInProgress ) ) {
if( ( pExchange->SmbCeFlags & SMBCE_EXCHANGE_ATTEMPT_RECONNECTS ) == 0 ) {
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;
}
RxLog(("** Recovered Sess for xchg %lx\n",pExchange));
}
return Status;
}
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);
}
VOID
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 = &pExchange->MD5Context;
PCHAR SavedSignature = pExchange->ResponseSignature;
PSMB_HEADER Smb = (PSMB_HEADER)pBuffer;
ULONG ServerIndex;
BOOLEAN Correct;
//
// Initialize the Context
//
RtlCopyMemory(Context, &Server->SmbSecuritySignatureIntermediateContext, sizeof(MD5_CTX));
//
// Save the signature that's presently in the SMB
//
RtlCopyMemory( SavedSignature, Smb->SecuritySignature, sizeof(CHAR) * SMB_SECURITY_SIGNATURE_LENGTH);
//
// 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);
//
// Restore the signature that's presently in the SMB Header
//
RtlCopyMemory( Smb->SecuritySignature, SavedSignature, sizeof(CHAR)*SMB_SECURITY_SIGNATURE_LENGTH);
}
BOOLEAN
SmbCheckSecuritySignaturePartial(
IN PSMB_EXCHANGE pExchange,
IN PSMBCE_SERVER Server,
IN ULONG DataLength,
IN PMDL Mdl
)
/*++
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 = &pExchange->MD5Context;
PCHAR SavedSignature = pExchange->ResponseSignature;
ULONG ServerIndex;
BOOLEAN Correct;
PCHAR SysAddress;
ULONG MessageLength = 0;
do {
SysAddress = MmGetSystemAddressForMdlSafe(Mdl,LowPagePriority);
if (SysAddress == NULL) {
return FALSE;
}
if (Mdl->ByteCount >= DataLength) {
MD5Update(Context, SysAddress, DataLength);
MessageLength += DataLength;
ASSERT(Mdl->Next == NULL);
break;
} else {
MD5Update(Context, SysAddress, Mdl->ByteCount);
MessageLength += Mdl->ByteCount;
ASSERT(Mdl->Next != NULL);
}
} while( (Mdl = Mdl->Next) != NULL );
MD5Final(Context);
//
// 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;
} else {
return TRUE;
}
}
BOOLEAN
SmbCheckSecuritySignatureWithMdl(
IN PSMB_EXCHANGE pExchange,
IN PSMBCE_SERVER Server,
IN ULONG DataLength,
IN PMDL Mdl
)
/*++
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 = &pExchange->MD5Context;
PCHAR SavedSignature = pExchange->ResponseSignature;
ULONG ServerIndex;
BOOLEAN Correct;
PCHAR SysAddress;
ULONG MessageLength = 0;
ASSERT(Mdl->Next == NULL);
SysAddress = MmGetSystemAddressForMdlSafe(Mdl,LowPagePriority);
if (SysAddress == NULL) {
return FALSE;
}
SmbCheckSecuritySignature(pExchange,
Server,
DataLength,
SysAddress);
MD5Final(&pExchange->MD5Context);
if (RtlCompareMemory( Context->digest, pExchange->ResponseSignature, SMB_SECURITY_SIGNATURE_LENGTH*sizeof(CHAR)) !=
SMB_SECURITY_SIGNATURE_LENGTH*sizeof(CHAR)) {
PSMB_HEADER Smb = (PSMB_HEADER)SysAddress;
//SmbDumpSignatureError(pExchange,
// Context.digest,
// SavedSignature,
// MessageLength);
#if DBG
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));
#endif
//DbgBreakPoint();
//SmbCeTransportDisconnectIndicated(pExchange->SmbCeContext.pServerEntry);
//RxLogFailure(
// MRxSmbDeviceObject,
// NULL,
// EVENT_RDR_SECURITY_SIGNATURE_MISMATCH,
// STATUS_UNSUCCESSFUL);
return FALSE;
} else {
return TRUE;
}
}