488 lines
13 KiB
C++
488 lines
13 KiB
C++
|
//+-----------------------------------------------------------------------
|
|||
|
//
|
|||
|
// 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);
|
|||
|
}
|
|||
|
|