/*++ 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 #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 };