529 lines
17 KiB
C
529 lines
17 KiB
C
/*++
|
||
|
||
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
|
||
};
|
||
|
||
|