|
|
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 2000
//
// File: krnlapi.cxx
//
// Contents: Kernel-mode APIs to the WDigest security packageSSP
// Main kernel mode entry points into this dll:
// WDigestInitKernelPackage,
// WDigestDeleteKernelContext,
// WDigestInitKernelContext,
// WDigestMapKernelHandle,
// WDigestMakeSignature,
// WDigestVerifySignature,
// WDigestSealMessage,
// WDigestUnsealMessage,
// WDigestGetContextToken,
// WDigestQueryContextAttributes,
// WDigestCompleteToken,
// WDigestExportSecurityContext,
// WDigestImportSecurityContext,
// WDigestSetPagingMode
//
// Helper functions:
//
//
//
// History: KDamour 18Mar00 Based on NTLM's kernel mode functions
// We could merge the digest processing parameters into unified code for longhorn
//
//------------------------------------------------------------------------
#include "..\global.h"
#include "krnldgst.h"
#include <stdio.h> // For sprintf
#if defined (_MSC_VER)
#if _MSC_VER >= 1200
#pragma warning(push)
#endif
#pragma warning(disable:4201) // Disable warning/error for nameless struct/union
#endif
extern "C" { #include <ntosp.h>
// #include <ntlsa.h>
// #include <ntsam.h>
}
#if defined (_MSC_VER) && ( _MSC_VER >= 800 )
#if _MSC_VER >= 1200
#pragma warning(pop)
#else
#pragma warning(default:4201) // Disable warning/error for nameless struct/union
#endif
#endif
SECPKG_KERNEL_FUNCTION_TABLE WDigestFunctionTable = { WDigestInitKernelPackage, WDigestDeleteKernelContext, WDigestInitKernelContext, WDigestMapKernelHandle, WDigestMakeSignature, WDigestVerifySignature, WDigestSealMessage, WDigestUnsealMessage, WDigestGetContextToken, WDigestQueryContextAttributes, WDigestCompleteToken, WDigestExportSecurityContext, WDigestImportSecurityContext, WDigestSetPagingMode };
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, WDigestInitKernelPackage)
#pragma alloc_text(PAGE, WDigestDeleteKernelContext)
#pragma alloc_text(PAGE, WDigestInitKernelContext)
#pragma alloc_text(PAGE, WDigestMapKernelHandle)
#pragma alloc_text(PAGEMSG, WDigestMakeSignature)
#pragma alloc_text(PAGEMSG, WDigestVerifySignature)
#pragma alloc_text(PAGEMSG, WDigestSealMessage)
#pragma alloc_text(PAGEMSG, WDigestUnsealMessage)
#pragma alloc_text(PAGEMSG, WDigestGetContextToken)
#pragma alloc_text(PAGEMSG, WDigestQueryContextAttributes)
#pragma alloc_text(PAGEMSG, WDigestDerefContext )
#pragma alloc_text(PAGE, WDigestCompleteToken)
#pragma alloc_text(PAGE, WDigestExportSecurityContext)
#pragma alloc_text(PAGE, WDigestImportSecurityContext)
#pragma alloc_text(PAGEMSG, WDigestFreeKernelContext )
#endif
PSECPKG_KERNEL_FUNCTIONS g_LsaKernelFunctions = NULL; POOL_TYPE WDigestPoolType = PagedPool;
PVOID WDigestPagedList = NULL; PVOID WDigestNonPagedList = NULL; PVOID WDigestActiveList = NULL;
ULONG WDigestPackageId = 0;
//+-------------------------------------------------------------------------
//
// Function: WDigestInitKernelPackage
//
// Synopsis: Initialize an instance of the WDigest package in
// a client's (kernel) address space
//
// Arguments: None
//
// Returns: STATUS_SUCCESS
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestInitKernelPackage( IN PSECPKG_KERNEL_FUNCTIONS KernelFunctions ) { NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
DebugLog(( DEB_TRACE, "Entering WDigestInitKernelPackage\n" ));
g_LsaKernelFunctions = KernelFunctions;
//
// Set up Context list support:
//
WDigestPoolType = PagedPool ; WDigestPagedList = g_LsaKernelFunctions->CreateContextList( KSecPaged );
if ( !WDigestPagedList ) { return STATUS_NO_MEMORY ; }
WDigestActiveList = WDigestPagedList;
DebugLog(( DEB_TRACE, "Leaving WDigestInitKernelPackage 0x%lx\n", Status )); return(Status); }
//+-------------------------------------------------------------------------
//
// Function: WDigestDeleteKernelContext
//
// Synopsis: Deletes a kernel mode context by unlinking it and then
// dereferencing it.
//
// Effects:
//
// Arguments: KernelContextHandle - Kernel context handle of the context to delete
// LsaContextHandle - The Lsa mode handle
//
// Requires:
//
// Returns: STATUS_SUCCESS on success, STATUS_INVALID_HANDLE if the
// context can't be located
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestDeleteKernelContext( IN ULONG_PTR pKernelContextHandle, OUT PULONG_PTR pLsaContextHandle ) {
PDIGEST_KERNELCONTEXT pContext = NULL; NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
DebugLog(( DEB_TRACE, "WDigestDeleteKernelContext: Entering\n" ));
Status = WDigestReferenceContext( pKernelContextHandle, TRUE );
if ( NT_SUCCESS( Status ) ) { pContext = (PDIGEST_KERNELCONTEXT) pKernelContextHandle ;
} else { *pLsaContextHandle = pKernelContextHandle; DebugLog(( DEB_ERROR, "WDigestDeleteKernelContext: Bad kernel context 0x%lx\n", pKernelContextHandle)); goto CleanUp;
}
*pLsaContextHandle = pContext->LsaContext;
CleanUp:
if (pContext != NULL) { WDigestDerefContext( pContext );
}
DebugLog(( DEB_TRACE, "WDigestDeleteKernelContext: Leaving 0x%lx\n", Status )); return(Status); }
//+---------------------------------------------------------------------------
//
// Function: WDigestDerefContext
//
// Synopsis: Dereference a kernel context
//
// Arguments: [Context] --
//
// History: 7-07-98 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID WDigestDerefContext( PDIGEST_KERNELCONTEXT pContext ) { BOOLEAN Delete ;
MAYBE_PAGED_CODE();
KSecDereferenceListEntry( &pContext->List, &Delete );
if ( Delete ) { WDigestFreeKernelContext( pContext ); }
}
//+-------------------------------------------------------------------------
//
// Function: WDigestFreeKernelContext
//
// Synopsis: frees alloced pointers in this context and
// then frees the context
//
// Arguments: KernelContext - the unlinked kernel context
//
// Returns: STATUS_SUCCESS on success
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS WDigestFreeKernelContext ( PDIGEST_KERNELCONTEXT pKernelContext ) { NTSTATUS Status = STATUS_SUCCESS; int i = 0;
MAYBE_PAGED_CODE();
DebugLog(( DEB_TRACE, "WDigestFreeKernelContext: Entering\n" ));
if (!pKernelContext) { Status = STATUS_INVALID_PARAMETER; goto CleanUp; } if (pKernelContext->ClientTokenHandle) { NTSTATUS IgnoreStatus; IgnoreStatus = NtClose(pKernelContext->ClientTokenHandle); // ASSERT (NT_SUCCESS (IgnoreStatus));
if (!NT_SUCCESS(IgnoreStatus)) { DebugLog((DEB_ERROR, "WDigestFreeKernelContext: Could not Close the TokenHandle!!!!\n")); } pKernelContext->ClientTokenHandle = NULL; } StringFree(&(pKernelContext->strSessionKey)); UnicodeStringFree(&(pKernelContext->ustrAccountName)); //
// Values utilized in the Initial Digest Auth ChallResponse
// Can be utilized for defaults in future MakeSignature/VerifySignature
//
for (i=0; i < MD5_AUTH_LAST; i++) { StringFree(&(pKernelContext->strParam[i])); } DebugLog(( DEB_TRACE, "WDigestFreeKernelContext: Deleting Context 0x%lx\n", pKernelContext));
DigestFreeMemory(pKernelContext);
CleanUp:
DebugLog(( DEB_TRACE, "WDigestFreeKernelContext: Leaving 0x%lx\n", Status ));
return Status; }
//+-------------------------------------------------------------------------
//
// Function: WDigestInitKernelContext
//
// Synopsis: Creates a kernel-mode context from a packed LSA mode context
//
// Arguments: LsaContextHandle - Lsa mode context handle for the context
// PackedContext - A marshalled buffer containing the LSA
// mode context.
//
// Requires:
//
// Returns: STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestInitKernelContext( IN ULONG_PTR LsaContextHandle, IN PSecBuffer PackedContext, OUT PULONG_PTR NewContextHandle ) {
NTSTATUS Status = STATUS_SUCCESS; PDIGEST_KERNELCONTEXT pContext = NULL; PDIGEST_PACKED_USERCONTEXT pPackedUserContext = NULL;
PAGED_CODE();
DebugLog(( DEB_TRACE, "WDigestInitKernelContext: Entering\n" ));
*NewContextHandle = NULL;
if (PackedContext->cbBuffer < sizeof(DIGEST_PACKED_USERCONTEXT)) { Status = STATUS_INVALID_PARAMETER; DebugLog(( DEB_ERROR, "WDigestInitKernelContext: Bad size of Packed context 0x%lx\n", PackedContext->cbBuffer)); goto CleanUp; }
pPackedUserContext = (PDIGEST_PACKED_USERCONTEXT) DigestAllocateMemory(PackedContext->cbBuffer); if (!pPackedUserContext) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugLog((DEB_ERROR, "WDigestInitKernelContext: DigestAllocateMemory for Packed Copy returns NULL\n" )); goto CleanUp; }
// Copy the Packed User Context from LSA to local memory so it wil be long word aligned
memcpy(pPackedUserContext, PackedContext->pvBuffer, PackedContext->cbBuffer);
// Now we will unpack this transfered LSA context into UserMode space Context List
pContext = (PDIGEST_KERNELCONTEXT) DigestAllocateMemory( sizeof(DIGEST_KERNELCONTEXT) ); if (!pContext) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugLog((DEB_ERROR, "WDigestInitKernelContext: DigestAllocateMemory returns NULL\n" )); goto CleanUp; }
KsecInitializeListEntry( &pContext->List, WDIGEST_CONTEXT_SIGNATURE ); pContext->LsaContext = LsaContextHandle;
Status = DigestKernelUnpackContext(pPackedUserContext, pContext); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "WDigestInitKernelContext: DigestUnpackContext error 0x%x\n", Status)); goto CleanUp; }
KernelContextPrint(pContext);
KSecInsertListEntry( WDigestActiveList, &pContext->List );
WDigestDerefContext( pContext );
*NewContextHandle = (ULONG_PTR) pContext;
CleanUp:
if (!NT_SUCCESS(Status)) { if (pContext != NULL) { WDigestFreeKernelContext( pContext ); } }
if (pPackedUserContext) { DigestFreeMemory(pPackedUserContext); pPackedUserContext = NULL; }
if (PackedContext->pvBuffer != NULL) { g_LsaKernelFunctions->FreeHeap(PackedContext->pvBuffer); PackedContext->pvBuffer = NULL; PackedContext->cbBuffer = 0; }
DebugLog(( DEB_TRACE, "WDigestInitKernelContext: Leaving status 0x%lx\n", Status )); return(Status); }
// Unpack the context from LSA mode into the User mode Context
NTSTATUS DigestKernelUnpackContext( IN PDIGEST_PACKED_USERCONTEXT pPackedUserContext, OUT PDIGEST_KERNELCONTEXT pContext) { NTSTATUS Status = STATUS_SUCCESS; PUCHAR pucLoc = NULL; USHORT uNumWChars = 0; int iAuth = 0;
DebugLog((DEB_TRACE_FUNC, "DigestKernelUnpackContext: Entering\n"));
ASSERT(pContext);
//
// If TokenHandle is NULL, we are being called as
// as an effect of InitializeSecurityContext, else we are
// being called because of AcceptSecurityContext
//
if (pPackedUserContext->ClientTokenHandle != NULL) { Status = STATUS_NOT_SUPPORTED; // put support here for ASC calls
DebugLog((DEB_ERROR, "DigestKernelUnpackContext: ASC contexts not supported\n" )); goto CleanUp; } else { DebugLog((DEB_TRACE, "DigestUnpackContext: Called from ISC\n" )); }
//
// Copy over all of the other fields - some data might be binary so
// use RtlCopyMemory(Dest, Src, len)
//
pContext->ExpirationTime = pPackedUserContext->ExpirationTime; pContext->typeAlgorithm = (ALGORITHM_TYPE)pPackedUserContext->typeAlgorithm; pContext->typeCharset = (CHARSET_TYPE)pPackedUserContext->typeCharset; pContext->typeCipher = (CIPHER_TYPE)pPackedUserContext->typeCipher; pContext->typeDigest = (DIGEST_TYPE)pPackedUserContext->typeDigest; pContext->typeQOP = (QOP_TYPE)pPackedUserContext->typeQOP; pContext->ulSendMaxBuf = pPackedUserContext->ulSendMaxBuf; pContext->ulRecvMaxBuf = pPackedUserContext->ulRecvMaxBuf; pContext->ulNC = 1; // Force to one to account for ISC/ASC first message verify
pContext->lReferences = 1; pContext->ContextReq = pPackedUserContext->ContextReq; pContext->CredentialUseFlags = pPackedUserContext->CredentialUseFlags; pContext->ulFlags = pPackedUserContext->ulFlags;
// Now check on the strings attached
pucLoc = &(pPackedUserContext->ucData); for (iAuth = 0; iAuth < MD5_AUTH_LAST; iAuth++) { if (pPackedUserContext->uDigestLen[iAuth]) { Status = StringAllocate(&(pContext->strParam[iAuth]), (USHORT)pPackedUserContext->uDigestLen[iAuth]); if (!NT_SUCCESS(Status)) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugLog((DEB_ERROR, "DigestKernelUnpackContext: DigestAllocateMemory for Params returns NULL\n" )); goto CleanUp; } memcpy(pContext->strParam[iAuth].Buffer, pucLoc, (USHORT)pPackedUserContext->uDigestLen[iAuth]); pContext->strParam[iAuth].Length = (USHORT)pPackedUserContext->uDigestLen[iAuth]; pucLoc += (USHORT)pPackedUserContext->uDigestLen[iAuth]; DebugLog((DEB_TRACE, "DigestKernelUnpackContext: Param[%d] is length %d - %.50s\n", iAuth, pPackedUserContext->uDigestLen[iAuth], pContext->strParam[iAuth].Buffer )); } }
// Now do the SessionKey
if (pPackedUserContext->uSessionKeyLen) { ASSERT(pPackedUserContext->uSessionKeyLen == MD5_HASH_HEX_SIZE); if (pPackedUserContext->uSessionKeyLen != MD5_HASH_HEX_SIZE) { Status = STATUS_NO_USER_SESSION_KEY; DebugLog((DEB_ERROR, "DigestUnpackContext: Session key length incorrect\n" )); goto CleanUp; }
Status = StringAllocate(&(pContext->strSessionKey), (USHORT)pPackedUserContext->uSessionKeyLen); if (!NT_SUCCESS(Status)) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugLog((DEB_ERROR, "DigestUnpackContext: DigestAllocateMemory for SessionKey returns NULL\n" )); goto CleanUp; } memcpy(pContext->strSessionKey.Buffer, pucLoc, pPackedUserContext->uSessionKeyLen); pContext->strSessionKey.Length = (USHORT)pPackedUserContext->uSessionKeyLen; pucLoc += (USHORT)pPackedUserContext->uSessionKeyLen;
// Now determine the binary version of the SessionKey from HEX() version
HexToBin(pContext->strSessionKey.Buffer, MD5_HASH_HEX_SIZE, pContext->bSessionKey); } // Now do the AccountName
if (pPackedUserContext->uAccountNameLen) { uNumWChars = (USHORT)pPackedUserContext->uAccountNameLen / sizeof(WCHAR); Status = UnicodeStringAllocate(&(pContext->ustrAccountName), uNumWChars); if (!NT_SUCCESS(Status)) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugLog((DEB_ERROR, "DigestKernelUnpackContext: DigestAllocateMemory for AccountName returns NULL\n" )); goto CleanUp; } memcpy(pContext->ustrAccountName.Buffer, pucLoc, pPackedUserContext->uAccountNameLen); pContext->ustrAccountName.Length = (USHORT)pPackedUserContext->uAccountNameLen; pucLoc += (USHORT)pPackedUserContext->uAccountNameLen; } CleanUp:
DebugLog((DEB_TRACE_FUNC, "DigestKernelUnpackContext: Leaving Status 0x%x\n", Status)); return(Status); }
//+-------------------------------------------------------------------------
//
// Function: DigestAllocateMemory
//
// Synopsis: Allocate memory in kernel mode
//
// Effects: Allocated chunk is zeroed out
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes: Replacements for functions in SSP but for kernel mode
//
//--------------------------------------------------------------------------
PVOID DigestAllocateMemory( IN ULONG BufferSize ) { PVOID Buffer = NULL; // DebugLog((DEB_TRACE, "Entering DigestAllocateMemory\n"));
Buffer = WDigestKAllocate(BufferSize);
if (Buffer) { ZeroMemory(Buffer, BufferSize); } DebugLog((DEB_TRACE_MEM, "Memory: Local alloc %lu bytes at 0x%x\n", BufferSize, Buffer ));
// DebugLog((DEB_TRACE, "Leaving DigestAllocateMemory\n"));
return Buffer; }
//+-------------------------------------------------------------------------
//
// Function: DigestFreeMemory
//
// Synopsis: Free memory in kernel mode
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID DigestFreeMemory( IN PVOID Buffer ) { // DebugLog((DEB_TRACE, "Entering DigestFreeMemory\n"));
if (ARGUMENT_PRESENT(Buffer)) { DebugLog((DEB_TRACE_MEM, "DigestFreeMemory: Local free at 0x%x\n", Buffer )); WDigestKFree(Buffer); }
// DebugLog((DEB_TRACE, "Leaving DigestFreeMemory\n"));
}
//+-------------------------------------------------------------------------
//
// Function: WDigestMapKernelHandle
//
// Synopsis: Maps a kernel handle into an LSA handle
//
// Arguments: KernelContextHandle - Kernel context handle of the context to map
// LsaContextHandle - Receives LSA context handle of the context
// to map
//
// Returns: STATUS_SUCCESS on success
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestMapKernelHandle( IN ULONG_PTR KernelContextHandle, OUT PULONG_PTR LsaContextHandle ) {
NTSTATUS Status = STATUS_SUCCESS; PDIGEST_KERNELCONTEXT pContext = NULL;
PAGED_CODE();
DebugLog((DEB_TRACE,"WDigestMapKernelHandle: Entering\n"));
Status = WDigestReferenceContext( KernelContextHandle, FALSE );
if ( NT_SUCCESS( Status ) ) { pContext = (PDIGEST_KERNELCONTEXT) KernelContextHandle ;
*LsaContextHandle = pContext->LsaContext ;
WDigestDerefContext( pContext );
} else { DebugLog(( DEB_WARN, "WDigestMapKernelHandle: Invalid context handle - %x\n", KernelContextHandle )); *LsaContextHandle = KernelContextHandle ; }
DebugLog((DEB_TRACE,"WDigestMapKernelHandle: Leaving 0x%lx\n", Status));
return (Status); }
//+-------------------------------------------------------------------------
//
// Function: WDigestMakeSignature
//
// Synopsis: Computes the Digest Auth response and and creates challengeResponse
//
// Effects:
//
// Arguments: KernelContextHandle - Handle of the context to use to sign the
// message.
// QualityOfProtection - Unused flags.
// MessageBuffers - Contains an array of buffers to sign and
// to store the signature.
// MessageSequenceNumber - Sequence number for this message,
// only used in datagram cases.
//
// Returns: STATUS_INVALID_HANDLE - the context could not be found or
// was not configured for message integrity.
// STATUS_INVALID_PARAMETER - the signature buffer could not
// be found.
// STATUS_BUFFER_TOO_SMALL - the signature buffer is too small
// to hold the signature
//
// STATUS_SUCCESS - completed normally
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestMakeSignature( IN ULONG_PTR KernelContextHandle, IN ULONG fQOP, IN PSecBufferDesc pMessage, IN ULONG MessageSeqNo ) { NTSTATUS Status = STATUS_SUCCESS;
PDIGEST_KERNELCONTEXT pContext = NULL;
MAYBE_PAGED_CODE();
DebugLog(( DEB_TRACE, "WDigestMakeSignature: Entering\n" ));
UNREFERENCED_PARAMETER(fQOP);
Status = WDigestReferenceContext( KernelContextHandle, FALSE );
if ( NT_SUCCESS( Status ) ) { pContext = (PDIGEST_KERNELCONTEXT) KernelContextHandle ; } else { DebugLog(( DEB_ERROR, "WDigestMakeSignature: Bad kernel context 0x%lx\n", KernelContextHandle)); goto CleanUp_NoDeref;
}
// Since we are in UserMode we MUST have a sessionkey to use - if not then can not process
if (!pContext->strSessionKey.Length) { Status = STATUS_NO_USER_SESSION_KEY; DebugLog((DEB_ERROR, "WDigestMakeSignature: No Session Key contained in UserContext\n")); goto CleanUp; }
Status = DigestKernelHTTPHelper( pContext, eSign, pMessage, MessageSeqNo );
if( !NT_SUCCESS( Status ) ) { DebugLog(( DEB_ERROR, "WDigestMakeSignature: SspSignSealHelper returns %lx\n", Status )); goto CleanUp; }
CleanUp:
WDigestDerefContext( pContext );
CleanUp_NoDeref:
DebugLog(( DEB_TRACE, "WDigestMakeSignature: Leaving 0x%lx\n", Status )); return(Status); }
//+-------------------------------------------------------------------------
//
// Function: WDigestVerifySignature
//
// Synopsis: Verifies a a challengeResponse on a server (not implemented in kernel mode)
//
// Effects:
//
// Arguments: KernelContextHandle - Handle of the context to use to sign the
// message.
// MessageBuffers - Contains an array of signed buffers and
// a signature buffer.
// MessageSequenceNumber - Sequence number for this message,
// only used in datagram cases.
// QualityOfProtection - Unused flags.
//
// Returns: STATUS_INVALID_HANDLE - the context could not be found or
// was not configured for message integrity.
// STATUS_INVALID_PARAMETER - the signature buffer could not
// be found or was too small.
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestVerifySignature( IN ULONG_PTR KernelContextHandle, IN PSecBufferDesc pMessage, IN ULONG MessageSeqNo, OUT PULONG pfQOP ) { NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
MAYBE_PAGED_CODE();
DebugLog(( DEB_TRACE, "WDigestVerifySignature Entering\n" ));
UNREFERENCED_PARAMETER(KernelContextHandle); UNREFERENCED_PARAMETER(pMessage); UNREFERENCED_PARAMETER(MessageSeqNo); UNREFERENCED_PARAMETER(pfQOP);
DebugLog(( DEB_TRACE, "WDigestVerifySignature: Leaving 0x%lx\n", Status )); return(Status); }
//+--------------------------------------------------------------------
//
// Function: DigestKernelHTTPHelper
//
// Synopsis: Process a SecBuffer with a given Kernel Security Context
// Used with HTTP for auth after initial ASC/ISC exchange
//
// Arguments: pContext - KernelMode Context for the security state
// Op - operation to perform on the Sec buffers
// pMessage - sec buffers to processs and return output
//
// Returns: NTSTATUS
//
// Notes:
//
//---------------------------------------------------------------------
NTSTATUS NTAPI DigestKernelHTTPHelper( IN PDIGEST_KERNELCONTEXT pContext, IN eSignSealOp Op, IN OUT PSecBufferDesc pSecBuff, IN ULONG MessageSeqNo ) { NTSTATUS Status = STATUS_SUCCESS; ULONG ulSeqNo = 0; PSecBuffer pChalRspInputToken = NULL; PSecBuffer pMethodInputToken = NULL; PSecBuffer pURIInputToken = NULL; PSecBuffer pHEntityInputToken = NULL; PSecBuffer pFirstOutputToken = NULL; DIGEST_PARAMETER Digest; USHORT usLen = 0; int iAuth = 0; char *cptr = NULL; char szNCOverride[2*NCNUM]; // Overrides the provided NC if non-zero using only NCNUM digits
STRING strURI = {0};
DebugLog((DEB_TRACE, "DigestKernelHTTPHelper: Entering \n"));
Status = DigestInit(&Digest); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: Digest init error status 0x%x\n", Status)); goto CleanUp; }
if (pSecBuff->cBuffers < 1) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: Not enough input buffers 0x%x\n", Status)); goto CleanUp; } pChalRspInputToken = &(pSecBuff->pBuffers[0]); if (!ContextIsTokenOK(pChalRspInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: ContextIsTokenOK (ChalRspInputToken) failed 0x%x\n", Status)); goto CleanUp; }
// Set any digest processing parameters based on Context
if (pContext->ulFlags & FLAG_CONTEXT_NOBS_DECODE) { Digest.usFlags |= FLAG_NOBS_DECODE; }
// We have input in the SECBUFFER 0th location - parse it
Status = DigestParser2(pChalRspInputToken, MD5_AUTH_NAMES, MD5_AUTH_LAST, &Digest); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: DigestParser error 0x%x\n", Status)); goto CleanUp; }
// Now determine all of the other buffers
DebugLog((DEB_TRACE, "DigestKernelHTTPHelper: pContext->ContextReq 0x%lx \n", pContext->ContextReq));
DebugLog((DEB_TRACE, "DigestKernelHTTPHelper: HTTP SecBuffer Format\n")); // Retrieve the information from the SecBuffers & check proper formattting
if (pSecBuff->cBuffers < 4) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: Not enough input buffers 0x%x\n", Status)); goto CleanUp; } pMethodInputToken = &(pSecBuff->pBuffers[1]); if (!ContextIsTokenOK(pMethodInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE)) { // Check to make sure that string is present
Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: ContextIsTokenOK (MethodInputToken) failed 0x%x\n", Status)); goto CleanUp; }
pURIInputToken = &(pSecBuff->pBuffers[2]); if (!ContextIsTokenOK(pURIInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: ContextIsTokenOK (URIInputToken) failed 0x%x\n", Status)); goto CleanUp; }
pHEntityInputToken = &(pSecBuff->pBuffers[3]); if (!ContextIsTokenOK(pHEntityInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: ContextIsTokenOK (HEntityInputToken) failed 0x%x\n", Status)); goto CleanUp; }
// Take care of the output buffer
if (Op == eSign) { if (pSecBuff->cBuffers < 5) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: No Output Buffers %d\n", Status)); goto CleanUp; } pFirstOutputToken = &(pSecBuff->pBuffers[4]); if (!ContextIsTokenOK(pFirstOutputToken, 0)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper, ContextIsTokenOK (FirstOutputToken) failed 0x%x\n", Status)); goto CleanUp; }
// Reset output buffer
if (pFirstOutputToken && (pFirstOutputToken->pvBuffer) && (pFirstOutputToken->cbBuffer >= 1)) { cptr = (char *)pFirstOutputToken->pvBuffer; *cptr = '\0'; }
} else { pFirstOutputToken = NULL; // There is no output buffer
}
// Verify that there is a valid Method provided
if (!pMethodInputToken->pvBuffer || !pMethodInputToken->cbBuffer || (PBUFFERTYPE(pMethodInputToken) != SECBUFFER_PKG_PARAMS)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: Method SecBuffer must have valid method string status 0x%x\n", Status)); goto CleanUp; }
usLen = strlencounted((char *)pMethodInputToken->pvBuffer, (USHORT)pMethodInputToken->cbBuffer); if (!usLen) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: Method SecBuffer must have valid method string status 0x%x\n", Status)); goto CleanUp; } Digest.refstrParam[MD5_AUTH_METHOD].Length = usLen; Digest.refstrParam[MD5_AUTH_METHOD].MaximumLength = (unsigned short)(pMethodInputToken->cbBuffer); Digest.refstrParam[MD5_AUTH_METHOD].Buffer = (char *)pMethodInputToken->pvBuffer; // refernce memory - no alloc!!!!
// Check to see if we have H(Entity) data to utilize
if (pHEntityInputToken->cbBuffer) { // Verify that there is a valid Method provided
if (!pHEntityInputToken->pvBuffer || (PBUFFERTYPE(pMethodInputToken) != SECBUFFER_PKG_PARAMS)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: HEntity SecBuffer must have valid string status 0x%x\n", Status)); goto CleanUp; }
usLen = strlencounted((char *)pHEntityInputToken->pvBuffer, (USHORT)pHEntityInputToken->cbBuffer);
if ((usLen != 0) && (usLen != (MD5_HASH_BYTESIZE * 2))) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: HEntity SecBuffer must have valid MD5 Hash data 0x%x\n", Status)); goto CleanUp; }
if (usLen) { Digest.refstrParam[MD5_AUTH_HENTITY].Length = usLen; Digest.refstrParam[MD5_AUTH_HENTITY].MaximumLength = (unsigned short)(pHEntityInputToken->cbBuffer); Digest.refstrParam[MD5_AUTH_HENTITY].Buffer = (char *)pHEntityInputToken->pvBuffer; // refernce memory - no alloc!!!!
} }
// Import the URI if it is a sign otherwise verify URI match if verify
if (Op == eSign) { // Pull in the URI provided in SecBuffer
if (!pURIInputToken || !pURIInputToken->cbBuffer || !pURIInputToken->pvBuffer) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: URI SecBuffer must have valid string 0x%x\n", Status)); goto CleanUp; }
if (PBUFFERTYPE(pURIInputToken) == SECBUFFER_PKG_PARAMS) { usLen = strlencounted((char *)pURIInputToken->pvBuffer, (USHORT)pURIInputToken->cbBuffer);
if (usLen > 0) { Status = StringCharDuplicate(&strURI, (char *)pURIInputToken->pvBuffer, usLen); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: StringCharDuplicate error 0x%x\n", Status)); goto CleanUp; } } } else { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: URI buffer type invalid error %d\n", Status)); goto CleanUp; }
StringReference(&(Digest.refstrParam[MD5_AUTH_URI]), &strURI); // refernce memory - no alloc!!!!
}
// If we have a NonceCount in the MessageSequenceNumber then use that
if (MessageSeqNo) { ulSeqNo = MessageSeqNo; } else { ulSeqNo = pContext->ulNC + 1; // Else use the next sequence number
}
sprintf(szNCOverride, "%0.8x", ulSeqNo); // Buffer is twice as big as we need (for safety) so just clip out first 8 characters
szNCOverride[NCNUM] = '\0'; // clip to 8 digits
DebugLog((DEB_TRACE, "DigestKernelHTTPHelper: Message Sequence NC is %s\n", szNCOverride)); Digest.refstrParam[MD5_AUTH_NC].Length = (USHORT)NCNUM; Digest.refstrParam[MD5_AUTH_NC].MaximumLength = (unsigned short)(NCNUM+1); Digest.refstrParam[MD5_AUTH_NC].Buffer = (char *)szNCOverride; // refernce memory - no alloc!!!!
// Now link in the stored context values into the digest if this is a SignMessage
// If there are values there from the input auth line then override them with context's value
if (Op == eSign) { for (iAuth = 0; iAuth < MD5_AUTH_LAST; iAuth++) { if ((iAuth != MD5_AUTH_URI) && (iAuth != MD5_AUTH_HENTITY) && (iAuth != MD5_AUTH_METHOD) && pContext->strParam[iAuth].Length) { // Link in only if passed into the user context from the LSA context
Digest.refstrParam[iAuth].Length = pContext->strParam[iAuth].Length; Digest.refstrParam[iAuth].MaximumLength = pContext->strParam[iAuth].MaximumLength; Digest.refstrParam[iAuth].Buffer = pContext->strParam[iAuth].Buffer; // reference memory - no alloc!!!!
} } } DebugLog((DEB_TRACE, "DigestKernelHTTPHelper: Digest inputs processing completed\n"));
Status = DigestKernelProcessParameters(pContext, &Digest, pFirstOutputToken); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: DigestUserProcessParameters error 0x%x\n", Status)); goto CleanUp; }
pContext->ulNC = ulSeqNo; // Everything verified so increment to next nonce count
// Keep a copy of the new URI in ChallengeResponse
StringFree(&(pContext->strParam[MD5_AUTH_URI])); Status = StringDuplicate(&(pContext->strParam[MD5_AUTH_URI]), &(Digest.refstrParam[MD5_AUTH_URI])); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestKernelHTTPHelper: Failed to copy over new URI\n")); goto CleanUp; }
CleanUp:
DigestFree(&Digest);
StringFree(&strURI);
DebugLog((DEB_TRACE, "DigestKernelHTTPHelper: Leaving Status 0x%x\n", Status));
return(Status); }
//+--------------------------------------------------------------------
//
// Function: DigestKernelProcessParameters
//
// Synopsis: Process the Digest information with the context info
// and generate any output token info
//
// Arguments: pContext - KernelMode Context for the security state
// pDigest - Digest parameters to process into output buffer
// pFirstOutputToken - sec buffers to processs and return output
//
// Returns: NTSTATUS
//
// Notes:
//
//---------------------------------------------------------------------
//
NTSTATUS NTAPI DigestKernelProcessParameters( IN PDIGEST_KERNELCONTEXT pContext, IN PDIGEST_PARAMETER pDigest, OUT PSecBuffer pFirstOutputToken) { NTSTATUS Status = STATUS_SUCCESS; ULONG ulNonceCount = 0;
DebugLog((DEB_TRACE_FUNC, "DigestKernelProcessParameters: Entering\n"));
// Some common input verification tests
// We must have a noncecount specified since we specified a qop in the Challenge
// If we decide to support no noncecount modes then we need to make sure that qop is not specified
if (pDigest->refstrParam[MD5_AUTH_NC].Length) { Status = RtlCharToInteger(pDigest->refstrParam[MD5_AUTH_NC].Buffer, HEXBASE, &ulNonceCount); if (!NT_SUCCESS(Status)) { Status = STATUS_INVALID_PARAMETER; DebugLog((DEB_ERROR, "DigestKernelProcessParameters: Nonce Count badly formatted\n")); goto CleanUp; } }
// Check nonceCount is incremented to preclude replay
if (!(ulNonceCount > pContext->ulNC)) { // We failed to verify next noncecount
Status = SEC_E_OUT_OF_SEQUENCE; DebugLog((DEB_ERROR, "DigestKernelProcessParameters: NonceCount failed to increment!\n")); goto CleanUp; }
// Since we are in UserMode we MUST have a sessionkey to use - if non then can not process
if (!pContext->strSessionKey.Length) { Status = SEC_E_NO_AUTHENTICATING_AUTHORITY; // indicate that we needed a call to ASC or ISC first
DebugLog((DEB_ERROR, "DigestKernelProcessParameters: No Session Key contained in UserContext\n")); goto CleanUp; }
// Copy the SessionKey from the Context into the Digest Structure to verify against
// This will have Digest Auth routines use the SessionKey rather than recompute H(A1)
StringFree(&(pDigest->strSessionKey)); Status = StringDuplicate(&(pDigest->strSessionKey), &(pContext->strSessionKey)); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestKernelProcessParameters: Failed to copy over SessionKey\n")); goto CleanUp; }
// Set the type of Digest Parameters we are to process
pDigest->typeDigest = pContext->typeDigest; pDigest->typeQOP = pContext->typeQOP; pDigest->typeAlgorithm = pContext->typeAlgorithm; pDigest->typeCharset = pContext->typeCharset;
if (pContext->ulFlags & FLAG_CONTEXT_QUOTE_QOP) { pDigest->usFlags |= FLAG_QUOTE_QOP; }
(void)DigestPrint(pDigest);
// No check locally that Digest is authentic
Status = DigestCalculation(pDigest, NULL); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestKernelProcessParameters: Oh no we FAILED Authentication!!!!\n")); goto CleanUp; }
// Send to output buffer only if there is an output buffer
// This allows this routine to be used in UserMode
if (pFirstOutputToken) { Status = DigestCreateChalResp(pDigest, NULL, pFirstOutputToken); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestKernelProcessParameters: Failed to create Output String\n")); goto CleanUp; } }
CleanUp: DebugLog((DEB_TRACE_FUNC, "DigestKernelProcessParameters: Leaving Status 0x%x\n", Status)); return(Status); }
//+-------------------------------------------------------------------------
//
// Function: WDigestSealMessage
//
// Synopsis: Not implemented
//
// Effects:
//
// Arguments: KernelContextHandle - Handle of the context to use to sign the
// message.
// MessageBuffers - Contains an array of signed buffers and
// a signature buffer.
// MessageSequenceNumber - Sequence number for this message,
// only used in datagram cases.
// QualityOfProtection - Unused flags.
//
// Requires: STATUS_INVALID_HANDLE - the context could not be found or
// was not configured for message integrity.
// STATUS_INVALID_PARAMETER - the signature buffer could not
// be found or was too small.
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestSealMessage( IN ULONG_PTR KernelContextHandle, IN ULONG fQOP, IN PSecBufferDesc pMessage, IN ULONG MessageSeqNo ) { NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
MAYBE_PAGED_CODE();
DebugLog(( DEB_TRACE, "WDigestSealMessage: Entering\n" ));
UNREFERENCED_PARAMETER(KernelContextHandle); UNREFERENCED_PARAMETER(fQOP); UNREFERENCED_PARAMETER(pMessage); UNREFERENCED_PARAMETER(MessageSeqNo);
DebugLog(( DEB_TRACE, "WDigestSealMessage: Leaving 0x%lx\n", Status )); return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: WDigestUnsealMessage
//
// Synopsis: Verifies a signed message buffer by calculating a checksum over all
// the non-read only data buffers and encrypting the checksum
// along with a nonce.
//
// Effects:
//
// Arguments: KernelContextHandle - Handle of the context to use to sign the
// message.
// MessageBuffers - Contains an array of signed buffers and
// a signature buffer.
// MessageSequenceNumber - Sequence number for this message,
// only used in datagram cases.
// QualityOfProtection - Unused flags.
//
// Requires: STATUS_INVALID_HANDLE - the context could not be found or
// was not configured for message integrity.
// STATUS_INVALID_PARAMETER - the signature buffer could not
// be found or was too small.
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestUnsealMessage( IN ULONG_PTR KernelContextHandle, IN PSecBufferDesc pMessage, IN ULONG MessageSeqNo, OUT PULONG pfQOP ) { NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
MAYBE_PAGED_CODE();
DebugLog(( DEB_TRACE, "WDigestUnsealMessage: Entering\n" ));
UNREFERENCED_PARAMETER(KernelContextHandle); UNREFERENCED_PARAMETER(pMessage); UNREFERENCED_PARAMETER(MessageSeqNo); UNREFERENCED_PARAMETER(pfQOP);
DebugLog(( DEB_TRACE, "WDigestUnsealMessage: Leaving 0x%lx\n", Status )); return (Status); }
//+-------------------------------------------------------------------------
//
// Function: WDigestGetContextToken
//
// Synopsis: returns a pointer to the token for a server-side context
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestGetContextToken( IN ULONG_PTR KernelContextHandle, OUT PHANDLE ImpersonationToken, OUT OPTIONAL PACCESS_TOKEN *RawToken ) { NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
MAYBE_PAGED_CODE();
DebugLog(( DEB_TRACE, "WDigestGetContextToken: Entering\n" ));
UNREFERENCED_PARAMETER(KernelContextHandle); UNREFERENCED_PARAMETER(ImpersonationToken); UNREFERENCED_PARAMETER(RawToken);
DebugLog(( DEB_TRACE, "WDigestGetContextToken: Leaving 0x%lx\n", Status )); return (Status); }
//+-------------------------------------------------------------------------
//
// Function: WDigestQueryContextAttributes
//
// Synopsis: Querys attributes of the specified context
// This API allows a customer of the security
// services to determine certain attributes of
// the context. These are: sizes, names, and lifespan.
//
// Effects:
//
// Arguments:
//
// ContextHandle - Handle to the context to query.
//
// Attribute - Attribute to query.
//
// #define SECPKG_ATTR_SIZES 0
// #define SECPKG_ATTR_NAMES 1
// #define SECPKG_ATTR_LIFESPAN 2
//
// Buffer - Buffer to copy the data into. The buffer must
// be large enough to fit the queried attribute.
//
//
// Requires:
//
// Returns:
//
// STATUS_SUCCESS - Call completed successfully
//
// STATUS_INVALID_HANDLE -- Credential/Context Handle is invalid
// STATUS_UNSUPPORTED_FUNCTION -- Function code is not supported
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestQueryContextAttributes( IN ULONG_PTR KernelContextHandle, IN ULONG Attribute, IN OUT PVOID Buffer ) {
NTSTATUS Status = STATUS_SUCCESS; PDIGEST_KERNELCONTEXT pContext = NULL;
MAYBE_PAGED_CODE();
DebugLog((DEB_TRACE_FUNC, "WDigestQueryContextAttributes: Entering ContextHandle 0x%lx\n", KernelContextHandle ));
PSecPkgContext_Sizes ContextSizes = NULL; PSecPkgContext_DceInfo ContextDceInfo = NULL; PSecPkgContext_Names ContextNames = NULL; PSecPkgContext_PackageInfo PackageInfo = NULL; PSecPkgContext_NegotiationInfo NegInfo = NULL; PSecPkgContext_PasswordExpiry PasswordExpires = NULL; PSecPkgContext_KeyInfo KeyInfo = NULL; PSecPkgContext_AccessToken AccessToken = NULL; PSecPkgContext_StreamSizes StreamSizes = NULL;
ULONG PackageInfoSize = 0; BOOL bServer = FALSE; LPWSTR pszEncryptAlgorithmName = NULL; LPWSTR pszSignatureAlgorithmName = NULL; DWORD dwBytes = 0; ULONG ulMaxMessage = 0;
DIGESTMODE_TYPE typeDigestMode = DIGESTMODE_UNDEFINED; // Are we in SASL or HTTP mode
Status = WDigestReferenceContext( KernelContextHandle, FALSE );
if ( NT_SUCCESS( Status ) ) { pContext = (PDIGEST_KERNELCONTEXT) KernelContextHandle ; } else { DebugLog(( DEB_ERROR, "WDigestQueryContextAttributes: Bad kernel context 0x%lx\n", KernelContextHandle)); goto CleanUp; }
// Check to see if Integrity is negotiated for SC
bServer = pContext->CredentialUseFlags & DIGEST_CRED_INBOUND;
if ((pContext->typeDigest == SASL_CLIENT) || (pContext->typeDigest == SASL_SERVER)) { typeDigestMode = DIGESTMODE_SASL; } else { typeDigestMode = DIGESTMODE_HTTP; }
//
// Handle each of the various queried attributes
//
DebugLog((DEB_TRACE, "WDigestQueryContextAttributes : Attribute 0x%lx\n", Attribute )); switch ( Attribute) { case SECPKG_ATTR_SIZES:
ContextSizes = (PSecPkgContext_Sizes) Buffer; ZeroMemory(ContextSizes, sizeof(SecPkgContext_Sizes)); ContextSizes->cbMaxToken = NTDIGEST_SP_MAX_TOKEN_SIZE; if (typeDigestMode == DIGESTMODE_HTTP) { // HTTP has signature the same as token in Authentication Header info
ContextSizes->cbMaxSignature = NTDIGEST_SP_MAX_TOKEN_SIZE; } else { // SASL has specialized signature block
ContextSizes->cbMaxSignature = MAC_BLOCK_SIZE + MAX_PADDING; } if ((pContext->typeCipher == CIPHER_3DES) || (pContext->typeCipher == CIPHER_DES)) { ContextSizes->cbBlockSize = DES_BLOCKSIZE; ContextSizes->cbSecurityTrailer = MAC_BLOCK_SIZE + MAX_PADDING; } else if ((pContext->typeCipher == CIPHER_RC4) || (pContext->typeCipher == CIPHER_RC4_40) || (pContext->typeCipher == CIPHER_RC4_56)) { ContextSizes->cbBlockSize = RC4_BLOCKSIZE; ContextSizes->cbSecurityTrailer = MAC_BLOCK_SIZE + MAX_PADDING; } else { ContextSizes->cbBlockSize = 0; if (typeDigestMode == DIGESTMODE_HTTP) { // HTTP has signature the same as token in Authentication Header info
ContextSizes->cbSecurityTrailer = 0; } else { // SASL has specialized signature block
ContextSizes->cbSecurityTrailer = MAC_BLOCK_SIZE + MAX_PADDING; // handle Auth-int case
} } break; case SECPKG_ATTR_DCE_INFO:
ContextDceInfo = (PSecPkgContext_DceInfo) Buffer; ZeroMemory(ContextDceInfo, sizeof(SecPkgContext_DceInfo)); ContextDceInfo->AuthzSvc = 0;
break;
case SECPKG_ATTR_NAMES:
ContextNames = (PSecPkgContext_Names) Buffer; ZeroMemory(ContextNames, sizeof(SecPkgContext_Names));
if (pContext->ustrAccountName.Length && pContext->ustrAccountName.Buffer) { dwBytes = pContext->ustrAccountName.Length + sizeof(WCHAR); ContextNames->sUserName = (LPWSTR)g_LsaKernelFunctions->AllocateHeap(dwBytes); if (ContextNames->sUserName) { ZeroMemory(ContextNames->sUserName, dwBytes); memcpy(ContextNames->sUserName, pContext->ustrAccountName.Buffer, pContext->ustrAccountName.Length); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } else { Status = STATUS_INSUFFICIENT_RESOURCES; }
break; case SECPKG_ATTR_PACKAGE_INFO: case SECPKG_ATTR_NEGOTIATION_INFO: //
// Return the information about this package. This is useful for
// callers who used SPNEGO and don't know what package they got.
//
// if ((Attribute == SECPKG_ATTR_NEGOTIATION_INFO) && (g_fParameter_Negotiate == FALSE))
if (Attribute == SECPKG_ATTR_NEGOTIATION_INFO) { Status = STATUS_NOT_SUPPORTED; goto CleanUp; }
PackageInfo = (PSecPkgContext_PackageInfo) Buffer; ZeroMemory(PackageInfo, sizeof(SecPkgContext_PackageInfo)); PackageInfoSize = sizeof(SecPkgInfoW) + sizeof(WDIGEST_SP_NAME) + sizeof(NTDIGEST_SP_COMMENT); PackageInfo->PackageInfo = (PSecPkgInfoW)g_LsaKernelFunctions->AllocateHeap(PackageInfoSize); if (PackageInfo->PackageInfo == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto CleanUp; } PackageInfo->PackageInfo->Name = (LPWSTR) (PackageInfo->PackageInfo + 1); PackageInfo->PackageInfo->Comment = (LPWSTR) ((((PBYTE) PackageInfo->PackageInfo->Name)) + sizeof(WDIGEST_SP_NAME)); wcscpy( PackageInfo->PackageInfo->Name, WDIGEST_SP_NAME );
wcscpy( PackageInfo->PackageInfo->Comment, NTDIGEST_SP_COMMENT ); PackageInfo->PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION; PackageInfo->PackageInfo->wRPCID = RPC_C_AUTHN_DIGEST; PackageInfo->PackageInfo->fCapabilities = NTDIGEST_SP_CAPS; PackageInfo->PackageInfo->cbMaxToken = NTDIGEST_SP_MAX_TOKEN_SIZE;
if ( Attribute == SECPKG_ATTR_NEGOTIATION_INFO ) { NegInfo = (PSecPkgContext_NegotiationInfo) PackageInfo ; NegInfo->NegotiationState = SECPKG_NEGOTIATION_COMPLETE ; }
break;
case SECPKG_ATTR_PASSWORD_EXPIRY: PasswordExpires = (PSecPkgContext_PasswordExpiry) Buffer; if (pContext->ExpirationTime.QuadPart != 0) { PasswordExpires->tsPasswordExpires = pContext->ExpirationTime; } else Status = STATUS_NOT_SUPPORTED; break;
case SECPKG_ATTR_KEY_INFO: KeyInfo = (PSecPkgContext_KeyInfo) Buffer; ZeroMemory(KeyInfo, sizeof(SecPkgContext_KeyInfo)); if (typeDigestMode == DIGESTMODE_HTTP) { // HTTP mode
KeyInfo->SignatureAlgorithm = CALG_MD5; pszSignatureAlgorithmName = WSTR_CIPHER_MD5; KeyInfo->sSignatureAlgorithmName = (LPWSTR) g_LsaKernelFunctions->AllocateHeap(sizeof(WCHAR) * ((ULONG)wcslen(pszSignatureAlgorithmName) + 1)); if (KeyInfo->sSignatureAlgorithmName != NULL) { wcscpy( KeyInfo->sSignatureAlgorithmName, pszSignatureAlgorithmName ); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } else { // SASL mode
KeyInfo->KeySize = 128; // All modes use a 128 bit key - may have less entropy though (i.e. rc4-XX)
KeyInfo->SignatureAlgorithm = CALG_HMAC; pszSignatureAlgorithmName = WSTR_CIPHER_HMAC_MD5; switch (pContext->typeCipher) { case CIPHER_RC4: case CIPHER_RC4_40: case CIPHER_RC4_56: KeyInfo->KeySize = 16 * 8; // All modes use a 128 bit key - may have less entropy though (i.e. rc4-XX)
KeyInfo->SignatureAlgorithm = CALG_RC4; pszEncryptAlgorithmName = WSTR_CIPHER_RC4; break; case CIPHER_DES: KeyInfo->KeySize = 7 * 8; KeyInfo->SignatureAlgorithm = CALG_DES; pszEncryptAlgorithmName = WSTR_CIPHER_DES; break; case CIPHER_3DES: KeyInfo->KeySize = 14 * 8; KeyInfo->SignatureAlgorithm = CALG_3DES_112; pszEncryptAlgorithmName = WSTR_CIPHER_3DES; break; } if (pszEncryptAlgorithmName) { KeyInfo->sEncryptAlgorithmName = (LPWSTR) g_LsaKernelFunctions->AllocateHeap(sizeof(WCHAR) * ((ULONG)wcslen(pszEncryptAlgorithmName) + 1)); if (KeyInfo->sEncryptAlgorithmName != NULL) { wcscpy( KeyInfo->sEncryptAlgorithmName, pszEncryptAlgorithmName ); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } if (pszSignatureAlgorithmName) { KeyInfo->sSignatureAlgorithmName = (LPWSTR) g_LsaKernelFunctions->AllocateHeap(sizeof(WCHAR) * ((ULONG)wcslen(pszSignatureAlgorithmName) + 1)); if (KeyInfo->sSignatureAlgorithmName != NULL) { wcscpy( KeyInfo->sSignatureAlgorithmName, pszSignatureAlgorithmName ); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } }
// Make sure that EncryptAlgorithmName and SignatureAlgorithmName is a valid NULL terminated string #601928
if (NT_SUCCESS(Status) && !KeyInfo->sEncryptAlgorithmName) { KeyInfo->sEncryptAlgorithmName = (LPWSTR) g_LsaKernelFunctions->AllocateHeap(sizeof(WCHAR));
if (KeyInfo->sEncryptAlgorithmName) { KeyInfo->sEncryptAlgorithmName[0] = L'\0'; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } }
if (NT_SUCCESS(Status) && !KeyInfo->sSignatureAlgorithmName) { KeyInfo->sSignatureAlgorithmName = (LPWSTR) g_LsaKernelFunctions->AllocateHeap(sizeof(WCHAR));
if (KeyInfo->sSignatureAlgorithmName) { KeyInfo->sSignatureAlgorithmName[0] = L'\0'; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } }
break; case SECPKG_ATTR_STREAM_SIZES: StreamSizes = (PSecPkgContext_StreamSizes) Buffer; ZeroMemory(StreamSizes, sizeof(SecPkgContext_StreamSizes));
if (typeDigestMode == DIGESTMODE_HTTP) { } else { // SASL
ulMaxMessage = pContext->ulRecvMaxBuf; if (pContext->ulSendMaxBuf < ulMaxMessage) { ulMaxMessage = pContext->ulSendMaxBuf; } StreamSizes->cbMaximumMessage = ulMaxMessage - (MAC_BLOCK_SIZE + MAX_PADDING); }
if ((pContext->typeCipher == CIPHER_3DES) || (pContext->typeCipher == CIPHER_DES)) { StreamSizes->cbBlockSize = DES_BLOCKSIZE; StreamSizes->cbTrailer = MAC_BLOCK_SIZE + MAX_PADDING; } else if ((pContext->typeCipher == CIPHER_RC4) || (pContext->typeCipher == CIPHER_RC4_40) || (pContext->typeCipher == CIPHER_RC4_56)) { StreamSizes->cbBlockSize = RC4_BLOCKSIZE; StreamSizes->cbTrailer = MAC_BLOCK_SIZE + MAX_PADDING; } break; case SECPKG_ATTR_ACCESS_TOKEN: AccessToken = (PSecPkgContext_AccessToken) Buffer; //
// ClientTokenHandle can be NULL, for instance:
// 1. client side context.
// 2. incomplete server context.
// Token is not duped - caller must not CloseHandle
AccessToken->AccessToken = (void*)pContext->ClientTokenHandle; break;
default: Status = STATUS_NOT_SUPPORTED; break; }
CleanUp:
if (!NT_SUCCESS(Status)) { switch (Attribute) {
case SECPKG_ATTR_NAMES:
if (ContextNames != NULL && ContextNames->sUserName ) { g_LsaKernelFunctions->FreeHeap(ContextNames->sUserName); ContextNames->sUserName = NULL; } break;
case SECPKG_ATTR_DCE_INFO:
if (ContextDceInfo != NULL && ContextDceInfo->pPac) { g_LsaKernelFunctions->FreeHeap(ContextDceInfo->pPac); ContextDceInfo->pPac = NULL; } break;
case SECPKG_ATTR_KEY_INFO: if (KeyInfo != NULL && KeyInfo->sEncryptAlgorithmName) { g_LsaKernelFunctions->FreeHeap(KeyInfo->sEncryptAlgorithmName); KeyInfo->sEncryptAlgorithmName = NULL; } if (KeyInfo != NULL && KeyInfo->sSignatureAlgorithmName) { g_LsaKernelFunctions->FreeHeap(KeyInfo->sSignatureAlgorithmName); KeyInfo->sSignatureAlgorithmName = NULL; } break; } }
if( pContext ) { WDigestDerefContext( pContext ); }
DebugLog((DEB_TRACE_FUNC, "WDigestQueryContextAttributes: Leaving\n")); return(Status); }
//+-------------------------------------------------------------------------
//
// Function: WDigestCompleteToken
//
// Synopsis: Completes a context - used to perform user mode verification of
// challenge response for non-persistent connections re-established via ASC
// call.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes: Not implemented - server function not needed right now
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI WDigestCompleteToken( IN ULONG_PTR ContextHandle, IN PSecBufferDesc InputBuffer ) { UNREFERENCED_PARAMETER (ContextHandle); UNREFERENCED_PARAMETER (InputBuffer); PAGED_CODE(); DebugLog(( DEB_TRACE, "Entering WDigestCompleteToken: Entering\n" )); DebugLog(( DEB_TRACE, "Leaving WDigestCompleteToken: Leaving \n" )); return(STATUS_NOT_SUPPORTED); }
//+-------------------------------------------------------------------------
//
// Function: WDigestExportSecurityContext
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS WDigestExportSecurityContext( IN ULONG_PTR ContextHandle, IN ULONG Flags, OUT PSecBuffer PackedContext, IN OUT PHANDLE TokenHandle ) { PAGED_CODE();
UNREFERENCED_PARAMETER (ContextHandle); UNREFERENCED_PARAMETER (Flags); UNREFERENCED_PARAMETER (PackedContext); UNREFERENCED_PARAMETER (TokenHandle);
DebugLog(( DEB_TRACE, "WDigestExportSecurityContext: Entering\n" )); DebugLog(( DEB_TRACE, "WDigestExportSecurityContext: Leaving\n" ));
return(STATUS_NOT_SUPPORTED); }
//+-------------------------------------------------------------------------
//
// Function: WDigestImportSecurityContext
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS WDigestImportSecurityContext( IN PSecBuffer PackedContext, IN OPTIONAL HANDLE TokenHandle, OUT PULONG_PTR ContextHandle ) { PAGED_CODE(); UNREFERENCED_PARAMETER (PackedContext); UNREFERENCED_PARAMETER (TokenHandle); UNREFERENCED_PARAMETER (ContextHandle); DebugLog((DEB_TRACE,"WDigestImportSecurityContext: Entering\n")); DebugLog((DEB_TRACE,"WDigestImportSecurityContext: Leaving\n"));
return(STATUS_NOT_SUPPORTED); }
//+---------------------------------------------------------------------------
//
// Function: WDigestSetPagingMode
//
// Synopsis: Switch the paging mode for cluster support
//
// Arguments: [Pagable] --
//
// History: 7-07-98 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS WDigestSetPagingMode( BOOLEAN Pagable ) { if ( Pagable ) { WDigestPoolType = PagedPool ; WDigestActiveList = WDigestPagedList ; } else { if ( WDigestNonPagedList == NULL ) { WDigestNonPagedList = g_LsaKernelFunctions->CreateContextList( KSecNonPaged ); if ( WDigestNonPagedList == NULL ) { return STATUS_NO_MEMORY ; } }
WDigestActiveList = WDigestNonPagedList ;
WDigestPoolType = NonPagedPool ; } return STATUS_SUCCESS ;
}
// Printout the fields present in usercontext pContext
NTSTATUS KernelContextPrint(PDIGEST_KERNELCONTEXT pContext) { NTSTATUS Status = STATUS_SUCCESS; int i = 0;
if (!pContext) { return (STATUS_INVALID_PARAMETER); }
DebugLog((DEB_TRACE_FUNC, "KernelContextPrint: Entering for Context Handle at 0x%x\n", pContext));
DebugLog((DEB_TRACE, "KernelContextPrint: NC %ld\n", pContext->ulNC));
DebugLog((DEB_TRACE, "KernelContextPrint: LSA Context 0x%x\n", pContext->LsaContext));
if (pContext->typeDigest == DIGEST_CLIENT) { DebugLog((DEB_TRACE, "KernelContextPrint: DIGEST_CLIENT\n")); } if (pContext->typeDigest == DIGEST_SERVER) { DebugLog((DEB_TRACE, "KernelContextPrint: DIGEST_SERVER\n")); } if (pContext->typeDigest == SASL_SERVER) { DebugLog((DEB_TRACE, "KernelContextPrint: SASL_SERVER\n")); } if (pContext->typeDigest == SASL_CLIENT) { DebugLog((DEB_TRACE, "KernelContextPrint: SASL_CLIENT\n")); }
if (pContext->typeQOP == AUTH) { DebugLog((DEB_TRACE, "KernelContextPrint: QOP: AUTH\n")); } if (pContext->typeQOP == AUTH_INT) { DebugLog((DEB_TRACE, "KernelContextPrint: QOP: AUTH_INT\n")); } if (pContext->typeQOP == AUTH_CONF) { DebugLog((DEB_TRACE, "KernelContextPrint: QOP: AUTH_CONF\n")); } if (pContext->typeAlgorithm == MD5) { DebugLog((DEB_TRACE, "KernelContextPrint: Algorithm: MD5\n")); } if (pContext->typeAlgorithm == MD5_SESS) { DebugLog((DEB_TRACE, "KernelContextPrint: Algorithm: MD5_SESS\n")); }
if (pContext->typeCharset == ISO_8859_1) { DebugLog((DEB_TRACE, "KernelContextPrint: Charset: ISO 8859-1\n")); } if (pContext->typeCharset == UTF_8) { DebugLog((DEB_TRACE, "KernelContextPrint: Charset: UTF-8\n")); }
if (pContext->typeCipher == CIPHER_RC4) { DebugLog((DEB_TRACE, "KernelContextPrint: Cipher: CIPHER_RC4\n")); } else if (pContext->typeCipher == CIPHER_RC4_40) { DebugLog((DEB_TRACE, "KernelContextPrint: Cipher: CIPHER_RC4_40\n")); } else if (pContext->typeCipher == CIPHER_RC4_56) { DebugLog((DEB_TRACE, "KernelContextPrint: Cipher: CIPHER_RC4_56\n")); } else if (pContext->typeCipher == CIPHER_DES) { DebugLog((DEB_TRACE, "KernelContextPrint: Cipher: CIPHER_DES\n")); } else if (pContext->typeCipher == CIPHER_3DES) { DebugLog((DEB_TRACE, "KernelContextPrint: Cipher: CIPHER_3DES\n")); }
DebugLog((DEB_TRACE, "KernelContextPrint: ContextReq 0x%lx CredentialUseFlags 0x%x\n", pContext->ContextReq, pContext->CredentialUseFlags));
for (i=0; i < MD5_AUTH_LAST;i++) { if (pContext->strParam[i].Buffer && pContext->strParam[i].Length) { DebugLog((DEB_TRACE, "KernelContextPrint: Digest[%d] = \"%Z\"\n", i, &pContext->strParam[i])); } }
if (pContext->strSessionKey.Length) { DebugLog((DEB_TRACE, "KernelContextPrint: SessionKey %Z\n", &pContext->strSessionKey)); }
if (pContext->ustrAccountName.Length) { DebugLog((DEB_TRACE, "KernelContextPrint: AccountName %wZ\n", &pContext->ustrAccountName)); }
DebugLog((DEB_TRACE_FUNC, "KernelContextPrint: Leaving\n"));
return(Status); }
|