windows-nt/Source/XPSP1/NT/ds/security/protocols/digest/ctxt.cxx

488 lines
13 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 2000
//
// File: ctxt.cxx
//
// Contents: Context manipulation functions
//
//
// History: KDamour 15Mar00 Stolen from NTLM context.cxx
//
//------------------------------------------------------------------------
#include "global.h"
// Globals for manipulating Context Lists
RTL_CRITICAL_SECTION l_ContextCritSect;
LIST_ENTRY l_ContextList;
// Indicate if completed Initialization of Credential Handler
BOOL g_bContextInitialized = FALSE;
//+--------------------------------------------------------------------
//
// Function: SspContextInitialize
//
// Synopsis: Initializes the context manager package
//
// Arguments: none
//
// Returns: NTSTATUS
//
// Notes: Called by SpInitialize
//
//---------------------------------------------------------------------
NTSTATUS
CtxtHandlerInit(VOID)
{
NTSTATUS Status = STATUS_SUCCESS;
//
// Initialize the Context list to be empty.
//
Status = RtlInitializeCriticalSection(&l_ContextCritSect);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "CtxtHandlerInit: Failed to initialize critsec 0x%x\n", Status));
goto CleanUp;
}
InitializeListHead( &l_ContextList );
// Simple variable test to make sure all initialized;
g_bContextInitialized = TRUE;
CleanUp:
return Status;
}
// Add a Context into the Context List
NTSTATUS
CtxtHandlerInsertCred(
IN PDIGEST_CONTEXT pDigestCtxt
)
{
DebugLog((DEB_TRACE_FUNC, "CtxtHandlerInsertCred: Entering with Context 0x%x RefCount %ld\n", pDigestCtxt, pDigestCtxt->lReferences));
RtlEnterCriticalSection( &l_ContextCritSect );
DebugLog((DEB_TRACE, "CtxtHandlerInsertCred: add into list\n"));
InsertHeadList( &l_ContextList, &pDigestCtxt->Next );
RtlLeaveCriticalSection( &l_ContextCritSect );
DebugLog((DEB_TRACE_FUNC, "CtxtHandlerInsertCred: Leaving with Context 0x%x\n", pDigestCtxt));
return STATUS_SUCCESS;
}
// Initialize a Context into the IdleState with the data from the Credential provided
NTSTATUS NTAPI
ContextInit(
IN OUT PDIGEST_CONTEXT pContext,
IN PDIGEST_CREDENTIAL pCredential
)
{
NTSTATUS Status = STATUS_SUCCESS;
DebugLog((DEB_TRACE_FUNC, "ContextInit: Entering\n"));
if (!pContext || !pCredential)
{
return STATUS_INVALID_PARAMETER;
}
ZeroMemory(pContext, sizeof(DIGEST_CONTEXT));
pContext->typeQOP = QOP_UNDEFINED;
pContext->typeDigest = DIGEST_UNDEFINED;
pContext->typeAlgorithm = ALGORITHM_UNDEFINED;
pContext->typeCipher = CIPHER_UNDEFINED;
pContext->typeCharset = CHARSET_UNDEFINED;
pContext->lReferences = 0;
pContext->ulSendMaxBuf = SASL_MAX_DATA_BUFFER;
pContext->ulRecvMaxBuf = SASL_MAX_DATA_BUFFER;
pContext->TimeCreated = time(NULL);
pContext->ContextHandle = (ULONG_PTR)pContext;
pContext->PasswordExpires = g_TimeForever; // never expire
// Now copy over all the info we need from the supplied credential
pContext->CredentialUseFlags = pCredential->CredentialUseFlags; // Keep the info on inbound/outbound
Status = UnicodeStringDuplicate(&(pContext->ustrAccountName), &(pCredential->ustrAccountName));
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "ContextInit: Failed to copy Domain into Context\n"));
goto CleanUp;
}
Status = UnicodeStringDuplicate(&(pContext->ustrDomain), &(pCredential->ustrDnsDomainName));
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "ContextInit: Failed to copy Domain into Context\n"));
goto CleanUp;
}
// Copy over the Credential Password if known - thread safe - this is encrypted text
Status = CredHandlerPasswdGet(pCredential, &pContext->ustrPassword);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "ContextInit: CredHandlerPasswdGet error status 0x%x\n", Status));
goto CleanUp;
}
CleanUp:
DebugLog((DEB_TRACE_FUNC, "ContextInit: Leaving\n"));
return Status;
}
// Once done with a context - release the resouces
NTSTATUS NTAPI
ContextFree(
IN PDIGEST_CONTEXT pContext
)
{
NTSTATUS Status = STATUS_SUCCESS;
USHORT iCnt = 0;
DebugLog((DEB_TRACE_FUNC, "ContextFree: Entering with Context 0x%x\n", pContext));
ASSERT(pContext);
ASSERT(0 == pContext->lReferences);
if (!pContext)
{
return STATUS_INVALID_PARAMETER;
}
DebugLog((DEB_TRACE, "ContextFree: Context RefCount %ld\n", pContext->lReferences));
DebugLog((DEB_TRACE, "ContextFree: Checking TokenHandle for LogonID (%x:%lx)\n",
pContext->LoginID.HighPart, pContext->LoginID.LowPart));
if (pContext->TokenHandle)
{
DebugLog((DEB_TRACE, "ContextFree: Closing TokenHandle for LogonID (%x:%lx)\n",
pContext->LoginID.HighPart, pContext->LoginID.LowPart));
NtClose(pContext->TokenHandle);
pContext->TokenHandle = NULL;
}
StringFree(&(pContext->strNonce));
StringFree(&(pContext->strCNonce));
StringFree(&(pContext->strOpaque));
StringFree(&(pContext->strSessionKey));
UnicodeStringFree(&(pContext->ustrDomain));
UnicodeStringFree(&(pContext->ustrPassword));
UnicodeStringFree(&(pContext->ustrAccountName));
StringFree(&(pContext->strResponseAuth));
for (iCnt = 0; iCnt < MD5_AUTH_LAST; iCnt++)
{
StringFree(&(pContext->strDirective[iCnt]));
}
DigestFreeMemory(pContext);
DebugLog((DEB_TRACE_FUNC, "ContextFree: Leaving with Context 0x%x\n", pContext));
return Status;
}
/*++
Routine Description:
This routine checks to see if the Context is for the specified
Client Connection, and references the Context if it is valid.
The caller may optionally request that the Context be
removed from the list of valid Contexts - preventing future
requests from finding this Context.
Arguments:
ContextHandle - Points to the ContextHandle of the Context
to be referenced.
RemoveContext - This boolean value indicates whether the caller
wants the Context to be removed from the list
of Contexts. TRUE indicates the Context is to be removed.
FALSE indicates the Context is not to be removed.
Return Value:
NULL - the Context was not found.
Otherwise - returns a pointer to the referenced Context.
--*/
NTSTATUS NTAPI
CtxtHandlerHandleToContext(
IN ULONG_PTR ContextHandle,
IN BOOLEAN RemoveContext,
OUT PDIGEST_CONTEXT *ppContext
)
{
NTSTATUS Status = STATUS_SUCCESS;
PLIST_ENTRY ListEntry = NULL;
PDIGEST_CONTEXT Context = NULL;
LONG lReferences = 0;
DebugLog((DEB_TRACE_FUNC, "CtxtHandlerHandleToContext: Entering ContextHandle 0x%lx\n", ContextHandle));
//
// Acquire exclusive access to the Context list
//
RtlEnterCriticalSection( &l_ContextCritSect );
//
// Now walk the list of Contexts looking for a match.
//
for ( ListEntry = l_ContextList.Flink;
ListEntry != &l_ContextList;
ListEntry = ListEntry->Flink ) {
Context = CONTAINING_RECORD( ListEntry, DIGEST_CONTEXT, Next );
//
// Found a match ... reference this Context
// (if the Context is being removed, we would increment
// and then decrement the reference, so don't bother doing
// either - since they cancel each other out).
//
if ( Context == (PDIGEST_CONTEXT) ContextHandle)
{
if (!RemoveContext)
{
//
// Timeout this context if caller is not trying to remove it.
// We only timeout contexts that are being setup, not
// fully authenticated contexts.
//
if (CtxtHandlerTimeHasElapsed(Context))
{
DebugLog((DEB_ERROR, "CtxtHandlerHandleToContext: Context 0x%lx has timed out.\n",
ContextHandle ));
Status = SEC_E_CONTEXT_EXPIRED;
goto CleanUp;
}
lReferences = InterlockedIncrement(&Context->lReferences);
}
else
{
RemoveEntryList( &Context->Next );
DebugLog((DEB_TRACE, "CtxtHandlerHandleToContext:Delinked Context 0x%lx\n",Context ));
}
DebugLog((DEB_TRACE, "CtxtHandlerHandleToContext: FOUND Context = 0x%x, RemoveContext = %d, ReferenceCount = %ld\n",
Context, RemoveContext, Context->lReferences));
*ppContext = Context;
goto CleanUp;
}
}
//
// No match found
//
DebugLog((DEB_WARN, "CtxtHandlerHandleToContext: Tried to reference unknown Context 0x%lx\n", ContextHandle ));
Status = STATUS_OBJECT_NAME_NOT_FOUND;
CleanUp:
RtlLeaveCriticalSection( &l_ContextCritSect );
DebugLog((DEB_TRACE_FUNC, "CtxtHandlerHandleToContext: Leaving\n" ));
return(Status);
}
/*++
Routine Description:
This routine checks to see if the LogonId is for the specified
Server Connection, and references the Context if it is valid.
The caller may optionally request that the Context be
removed from the list of valid Contexts - preventing future
requests from finding this Context.
Arguments:
pstrOpaque - Opaque string that uniquely references the SecurityContext
Return Value:
NULL - the Context was not found.
Otherwise - returns a pointer to the referenced Context.
--*/
NTSTATUS NTAPI
CtxtHandlerOpaqueToPtr(
IN PSTRING pstrOpaque,
OUT PDIGEST_CONTEXT *ppContext
)
{
NTSTATUS Status = STATUS_SUCCESS;
PLIST_ENTRY ListEntry = NULL;
PDIGEST_CONTEXT Context = NULL;
LONG rc = 0;
LONG lReferences = 0;
DebugLog((DEB_TRACE_FUNC, "CtxtHandlerOpaqueToPtr: Entering Opaque (%Z)\n", pstrOpaque));
//
// Acquire exclusive access to the Context list
//
RtlEnterCriticalSection( &l_ContextCritSect );
//
// Now walk the list of Contexts looking for a match.
//
for ( ListEntry = l_ContextList.Flink;
ListEntry != &l_ContextList;
ListEntry = ListEntry->Flink ) {
Context = CONTAINING_RECORD( ListEntry, DIGEST_CONTEXT, Next );
//
// Found a match ... reference this Context
// (if the Context is being removed, we would increment
// and then decrement the reference, so don't bother doing
// either - since they cancel each other out).
//
rc = RtlCompareString(pstrOpaque, &(Context->strOpaque), FALSE);
if (!rc)
{
//
// Timeout this context if caller is not trying to remove it.
// We only timeout contexts that are being setup, not
// fully authenticated contexts.
//
if (CtxtHandlerTimeHasElapsed(Context))
{
DebugLog((DEB_ERROR, "CtxtHandlerOpaqueToPtr: Context 0x%x has timed out.\n",
Context ));
Status = SEC_E_CONTEXT_EXPIRED;
goto CleanUp;
}
lReferences = InterlockedIncrement(&Context->lReferences);
DebugLog((DEB_TRACE, "CtxtHandlerOpaqueToPtr: FOUND Context = 0x%x, ReferenceCount = %ld\n",
Context, Context->lReferences));
*ppContext = Context;
goto CleanUp;
}
}
//
// No match found
//
DebugLog((DEB_WARN, "CtxtHandlerOpaqueToPtr: Tried to reference unknown Opaque (%Z)\n", pstrOpaque));
Status = STATUS_OBJECT_NAME_NOT_FOUND;
CleanUp:
RtlLeaveCriticalSection( &l_ContextCritSect );
DebugLog((DEB_TRACE_FUNC, "CtxtHandlerOpaqueToPtr: Leaving\n" ));
return(Status);
}
// Check the Creation time with the Current time.
// If the difference is greater than the MAX allowed, Context is no longer valid
BOOL
CtxtHandlerTimeHasElapsed(
PDIGEST_CONTEXT pContext)
{
BOOL bStatus = FALSE;
DWORD dwTimeElapsed = 0;
time_t timeCurrent = time(NULL);
dwTimeElapsed = (DWORD)(timeCurrent - pContext->TimeCreated);
if (dwTimeElapsed > g_dwParameter_Lifetime)
{
bStatus = TRUE;
}
return(bStatus);
}
//+--------------------------------------------------------------------
//
// Function: CtxtHandlerRelease
//
// Synopsis: Releases the Context by decreasing reference counter
//
// Arguments: pContext - pointer to credential to de-reference
//
// Returns: NTSTATUS
//
// Notes:
//
//---------------------------------------------------------------------
NTSTATUS
CtxtHandlerRelease(
PDIGEST_CONTEXT pContext)
{
NTSTATUS Status = STATUS_SUCCESS;
LONG lReferences = 0;
lReferences = InterlockedDecrement(&pContext->lReferences);
DebugLog((DEB_TRACE, "CtxtHandlerRelease: (RefCount) UserContextInit deref 0x%x references %ld\n",
pContext, lReferences));
ASSERT( lReferences >= 0 );
//
// If the count has dropped to zero, then free all alloced stuff
//
if (lReferences == 0)
{
DebugLog((DEB_TRACE, "CtxtHandlerRelease: (RefCount) UserContextInit freed 0x%x\n", pContext));
Status = ContextFree(pContext);
}
return(Status);
}