windows-nt/Source/XPSP1/NT/base/fs/rdr2/rdbss/smb.mrx/kerbxchg.c

529 lines
17 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
kerbxchg.c
Abstract:
This module implements the routines for setting up a kerberos session.
Author:
Balan Sethu Raman [SethuR] 7-March-1995
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include <kerbxchg.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, SmbKerberosSessionSetupExchangeStart)
#pragma alloc_text(PAGE, ParseKerberosSessionSetupResponse)
#pragma alloc_text(PAGE, SmbKerberosSessionSetupExchangeReceive)
#pragma alloc_text(PAGE, SmbKerberosSessionSetupExchangeSendCompletionHandler)
#pragma alloc_text(PAGE, SmbKerberosSessionSetupExchangeCopyDataHandler)
#pragma alloc_text(PAGE, SmbKerberosSessionSetupExchangeFinalize)
#endif
//
// The Bug check file id for this module
//
#define BugCheckFileId (RDBSS_BUG_CHECK_SMB_NETROOT)
//
// The local debug trace level
//
#define Dbg (DEBUG_TRACE_DISPATCH)
//
// Forward declarations ...
//
#define KERBEROS_SESSION_SETUP_BUFFER_SIZE (4096)
NTSTATUS
SmbKerberosSessionSetupExchangeFinalize(
PSMB_EXCHANGE pExchange,
BOOLEAN *pPostFinalize);
NTSTATUS
SmbKerberosSessionSetupExchangeStart(
PSMB_EXCHANGE pExchange)
/*++
Routine Description:
This is the start routine for net root construction exchanges. This initiates the
construction of the appropriate SMB's if required.
Arguments:
pExchange - the exchange instance
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS Status;
PSMB_KERBEROS_SESSION_SETUP_EXCHANGE pKerberosExchange;
PAGED_CODE();
pKerberosExchange = (PSMB_KERBEROS_SESSION_SETUP_EXCHANGE)pExchange;
ASSERT(pKerberosExchange->Type == KERBEROS_SESSION_SETUP_EXCHANGE);
pKerberosExchange->BufferLength = KERBEROS_SESSION_SETUP_BUFFER_SIZE;
pKerberosExchange->pBuffer = RxAllocatePoolWithTag(
PagedPool,
pKerberosExchange->BufferLength,
MRXSMB_KERBEROS_POOLTAG);
pKerberosExchange->pServerResponseBlob = NULL;
pKerberosExchange->ServerResponseBlobLength = 0;
if (pKerberosExchange->pBuffer == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
PSMB_HEADER pSmbHeader = (PSMB_HEADER)(pKerberosExchange->pBuffer);
PREQ_NT_SESSION_SETUP_ANDX pSessionSetupRequest;
PGENERIC_ANDX pGenericAndX;
ULONG SmbBufferUnconsumed;
USHORT Flags2 = 0;
// Fill in the buffer header
pSessionSetupRequest = (PREQ_NT_SESSION_SETUP_ANDX)(pSmbHeader + 1);
pGenericAndX = (PGENERIC_ANDX)pSessionSetupRequest;
SmbBufferUnconsumed = pKerberosExchange->BufferLength - sizeof(SMB_HEADER);
ASSERT(pExchange->SmbCeContext.pServerEntry->Server.Dialect == CAIROX_DIALECT);
Flags2 |= (SMB_FLAGS2_UNICODE |
SMB_FLAGS2_KNOWS_EAS |
SMB_FLAGS2_KNOWS_LONG_NAMES |
SMB_FLAGS2_NT_STATUS);
*((PULONG)&pSmbHeader->Protocol) = SMB_HEADER_PROTOCOL;
pSmbHeader->Flags = (SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS);
pSmbHeader->Flags2 = Flags2;
pSmbHeader->Pid = MRXSMB_PROCESS_ID;
pSmbHeader->Uid = 0;
pSmbHeader->Tid = 0;
pSmbHeader->ErrorClass = 0;
pSmbHeader->Reserved = 0;
pSmbHeader->Command = SMB_COM_SESSION_SETUP_ANDX;
SmbPutUshort(&pSmbHeader->Error,0);
// Build the session setup and x.
Status = SMBCE_SERVER_DIALECT_DISPATCH(
&pExchange->SmbCeContext.pServerEntry->Server,
BuildSessionSetup,
(pExchange,
pGenericAndX,
&SmbBufferUnconsumed));
if (Status == RX_MAP_STATUS(SUCCESS)) {
// Update the buffer for the construction of the following SMB.
SmbPutUshort(&pSessionSetupRequest->AndXOffset,
(USHORT)(pKerberosExchange->BufferLength - SmbBufferUnconsumed));
pSessionSetupRequest->AndXCommand = SMB_COM_NO_ANDX_COMMAND;
pSessionSetupRequest->AndXReserved = 0;
} else {
if (Status == RX_MAP_STATUS(NO_LOGON_SERVERS)) {
// If no kerberos logon servers are available downgrade to a downlevel
// connection and retry.
pKerberosExchange->SmbCeContext.pServerEntry->Server.Dialect = NTLANMAN_DIALECT;
}
SmbCeReferenceSessionEntry(pKerberosExchange->SmbCeContext.pSessionEntry);
SmbCeUpdateSessionEntryState(
pExchange->SmbCeContext.pSessionEntry,
SMBCEDB_INVALID);
SmbCeCompleteSessionEntryInitialization(pExchange->SmbCeContext.pSessionEntry);
pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_SESSION_CONSTRUCTOR;
}
if (Status == RX_MAP_STATUS(SUCCESS)) {
pKerberosExchange->pBufferAsMdl = RxAllocateMdl(
pKerberosExchange->pBuffer,
KERBEROS_SESSION_SETUP_BUFFER_SIZE);
if (pKerberosExchange->pBufferAsMdl != NULL) {
RxProbeAndLockPages(
pKerberosExchange->pBufferAsMdl,
KernelMode,
IoModifyAccess,
Status);
if (NT_SUCCESS(Status)) {
Status = SmbCeTranceive(
pExchange,
(RXCE_SEND_PARTIAL | RXCE_SEND_SYNCHRONOUS),
pKerberosExchange->pBufferAsMdl,
(pKerberosExchange->BufferLength -
SmbBufferUnconsumed));
RxDbgTrace( 0, Dbg, ("Net Root SmbCeTranceive returned %lx\n",Status));
}
}
}
}
return Status;
}
NTSTATUS
ParseKerberosSessionSetupResponse(
IN PSMB_KERBEROS_SESSION_SETUP_EXCHANGE pKerberosExchange,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
IN PSMB_HEADER pSmbHeader)
{
NTSTATUS Status;
ULONG ResponseLength;
PAGED_CODE();
// The SMB exchange completed without an error.
RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseSmbHeader BytesIndicated %ld\n",BytesIndicated));
RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseSmbHeader BytesIndicated %ld\n",BytesIndicated));
RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseSmbHeader BytesAvailable %ld\n",BytesAvailable));
// The bytes indicated should be atleast cover the SMB_HEADER and the
// session setup response ( fixed portion )
ResponseLength = sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_SESSION_SETUP_ANDX,Buffer);
if (BytesIndicated > ResponseLength) {
PRESP_SESSION_SETUP_ANDX pSessionSetupResponse;
pSessionSetupResponse = (PRESP_SESSION_SETUP_ANDX)(pSmbHeader + 1);
pKerberosExchange->ResponseLength = ResponseLength +
SmbGetUshort(&pSessionSetupResponse->ByteCount);
pKerberosExchange->SmbCeContext.pSessionEntry->Session.UserId = pSmbHeader->Uid;
RxDbgTrace(0,Dbg,("Kerberos session setup response length %ld\n",pKerberosExchange->ResponseLength));
if (BytesIndicated < pKerberosExchange->ResponseLength) {
// Set up the response for copying the data.
if (pKerberosExchange->ResponseLength > pKerberosExchange->BufferLength) {
Status = STATUS_BUFFER_OVERFLOW;
} else {
Status = STATUS_MORE_PROCESSING_REQUIRED;
}
} else {
// The regular session setup response consists of three strings corresponding
// to the server's operating system type, lanman type and the domain name.
// Skip past the three strings to locate the kerberos blob that has been
// returned which needs to be autheticated locally.
// ***** NOTE ******
// Currently the server changes made by Arnold do not support the three
// strings that were previously returned by the Server, viz., the operating
// system name, the LANMAN version and the domain name. If the server is
// changed in this regard the corresponding change neeeds to be made here.
// set up the offsets in the response.
pKerberosExchange->ServerResponseBlobOffset = sizeof(SMB_HEADER) +
FIELD_OFFSET(RESP_SESSION_SETUP_ANDX,Buffer);
pKerberosExchange->ServerResponseBlobLength = pSessionSetupResponse->ByteCount;
// Copy the response onto the buffer associated with the exchange.
RtlCopyMemory(pKerberosExchange->pBuffer,
pSmbHeader,
pKerberosExchange->ResponseLength);
Status = STATUS_SUCCESS;
}
} else {
// Abort the exchange. No further processing can be done.
Status = STATUS_INVALID_NETWORK_RESPONSE;
}
return Status;
}
NTSTATUS
SmbKerberosSessionSetupExchangeReceive(
IN struct _SMB_EXCHANGE *pExchange, // The exchange instance
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *pBytesTaken,
IN PSMB_HEADER pSmbHeader,
OUT PMDL *pDataBufferPointer,
OUT PULONG pDataSize)
/*++
Routine Description:
This is the recieve indication handling routine for net root construction exchanges
Arguments:
pExchange - the exchange instance
BytesIndicated - the number of bytes indicated
Bytes Available - the number of bytes available
pBytesTaken - the number of bytes consumed
pSmbHeader - the byte buffer
pDataBufferPointer - the buffer into which the remaining data is to be copied.
pDataSize - the buffer size.
Return Value:
RXSTATUS - The return status for the operation
Notes:
This routine is called at DPC level.
--*/
{
NTSTATUS Status;
PSMB_KERBEROS_SESSION_SETUP_EXCHANGE pKerberosExchange;
ULONG SessionSetupResponseLength = 0;
PAGED_CODE();
pKerberosExchange = (PSMB_KERBEROS_SESSION_SETUP_EXCHANGE)pExchange;
// Parse the response. Finalize the exchange instance if all the data is available
Status = ParseKerberosSessionSetupResponse(
pKerberosExchange,
BytesIndicated,
BytesAvailable,
pSmbHeader);
if (Status != STATUS_MORE_PROCESSING_REQUIRED) {
*pBytesTaken = BytesAvailable;
Status = STATUS_SUCCESS;
} else {
*pBytesTaken = 0;
*pDataBufferPointer = pKerberosExchange->pBufferAsMdl;
*pDataSize = pKerberosExchange->ResponseLength;
}
return Status;
}
NTSTATUS
SmbKerberosSessionSetupExchangeSendCompletionHandler(
IN PSMB_EXCHANGE pExchange, // The exchange instance
IN PMDL pXmitBuffer,
IN NTSTATUS SendCompletionStatus)
/*++
Routine Description:
This is the send call back indication handling routine for net root construction exchanges
Arguments:
pExchange - the exchange instance
Return Value:
RXSTATUS - The return status for the operation
--*/
{
PAGED_CODE();
return STATUS_SUCCESS;
}
NTSTATUS
SmbKerberosSessionSetupExchangeCopyDataHandler(
IN PSMB_EXCHANGE pExchange, // The exchange instance
IN PMDL pCopyDataBuffer,
IN ULONG DataSize)
/*++
Routine Description:
This is the copy data handling routine for net root construction exchanges
Arguments:
pExchange - the exchange instance
Return Value:
RXSTATUS - The return status for the operation
--*/
{
PSMB_KERBEROS_SESSION_SETUP_EXCHANGE pKerberosExchange;
PSMB_HEADER pSmbHeader;
PAGED_CODE();
pKerberosExchange = (PSMB_KERBEROS_SESSION_SETUP_EXCHANGE)pExchange;
pSmbHeader = (PSMB_HEADER)pCopyDataBuffer;
pKerberosExchange->Status = ParseKerberosSessionSetupResponse(
pKerberosExchange,
DataSize,
DataSize,
pSmbHeader);
return STATUS_SUCCESS;
}
NTSTATUS
SmbKerberosSessionSetupExchangeFinalize(
PSMB_EXCHANGE pExchange,
BOOLEAN *pPostFinalize)
/*++
Routine Description:
This routine finalkzes the construct net root exchange. It resumes the RDBSS by invoking
the call back and discards the exchange
Arguments:
pExchange - the exchange instance
CurrentIrql - the current interrupt request level
pPostFinalize - a pointer to a BOOLEAN if the request should be posted
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS Status;
PSMB_KERBEROS_SESSION_SETUP_EXCHANGE pKerberosExchange;
PSMBCE_RESUMPTION_CONTEXT pResumptionContext;
PAGED_CODE();
if (RxShouldPostCompletion()) {
*pPostFinalize = TRUE;
return RX_MAP_STATUS(SUCCESS);
} else {
*pPostFinalize = FALSE;
}
pKerberosExchange = (PSMB_KERBEROS_SESSION_SETUP_EXCHANGE)pExchange;
// A copying operation on the server response BLOB is avoided by temporarily
// setting up the exchange pointer to the original buffer in which the response
// was received and initiating a allocation only if required.
pKerberosExchange->pServerResponseBlob =
((PBYTE)pKerberosExchange->pBuffer +
pKerberosExchange->ServerResponseBlobOffset);
// Determine if further processing is required. If not finalize the
// session entry.
RxDbgTrace(0,Dbg,
("SmbKerberosSessionSetupExchangeFinalize: pKerberosExchange->Status = %lx\n",pKerberosExchange->Status));
if (pKerberosExchange->Status == RX_MAP_STATUS(SUCCESS)) {
Status = KerberosValidateServerResponse(pKerberosExchange);
}
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
pKerberosExchange->pServerResponseBlob = RxAllocatePoolWithTag(
PagedPool,
pKerberosExchange->ServerResponseBlobLength,
MRXSMB_KERBEROS_POOLTAG);
if (pKerberosExchange->pServerResponseBlob != NULL) {
RtlCopyMemory(
pKerberosExchange->pServerResponseBlob,
((PBYTE)pKerberosExchange->pBuffer +
pKerberosExchange->ServerResponseBlobOffset),
pKerberosExchange->ServerResponseBlobLength);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
pKerberosExchange->pServerResponseBlob = NULL;
}
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
Status = SmbCeInitiateExchange((PSMB_EXCHANGE)pKerberosExchange);
} else {
// Reset the constructor flags in the exchange.
pKerberosExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_SESSION_CONSTRUCTOR;
if (pKerberosExchange->pServerResponseBlob != NULL) {
RxFreePool(pKerberosExchange->pServerResponseBlob);
}
RxDbgTrace(0,Dbg,("Kerberos Exchange Session Final Status(%lx)\n",Status));
// Finalize the session based upon the status
if (Status == STATUS_SUCCESS) {
SmbCeUpdateSessionEntryState(
pKerberosExchange->SmbCeContext.pSessionEntry,
SMBCEDB_ACTIVE);
} else {
if (Status == RX_MAP_STATUS(NO_LOGON_SERVERS)) {
// If no kerberos logon servers are available downgrade to a downlevel
// connection and retry.
pKerberosExchange->SmbCeContext.pServerEntry->Server.Dialect = NTLANMAN_DIALECT;
}
SmbCeUpdateSessionEntryState(
pKerberosExchange->SmbCeContext.pSessionEntry,
SMBCEDB_INVALID);
}
// Complete the session construction.
SmbCeReferenceSessionEntry(pKerberosExchange->SmbCeContext.pSessionEntry);
SmbCeCompleteSessionEntryInitialization(pKerberosExchange->SmbCeContext.pSessionEntry);
pKerberosExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_SESSION_CONSTRUCTOR;
pResumptionContext = pKerberosExchange->pResumptionContext;
// Tear down the exchange instance ...
SmbCeDiscardExchange(pKerberosExchange);
if (pResumptionContext != NULL) {
pResumptionContext->Status = Status;
SmbCeResume(pResumptionContext);
}
}
return STATUS_SUCCESS;
}
SMB_EXCHANGE_DISPATCH_VECTOR
KerberosSessionSetupExchangeDispatch =
{
SmbKerberosSessionSetupExchangeStart,
SmbKerberosSessionSetupExchangeReceive,
SmbKerberosSessionSetupExchangeCopyDataHandler,
NULL,
SmbKerberosSessionSetupExchangeFinalize
};