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

2122 lines
66 KiB

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