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.
3138 lines
80 KiB
3138 lines
80 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1992 - 1996
|
|
//
|
|
// File: ctxtmgr.cxx
|
|
//
|
|
// Contents: Code for managing contexts list for the Kerberos package
|
|
//
|
|
//
|
|
// History: 17-April-1996 Created MikeSw
|
|
// 26-Sep-1998 ChandanS
|
|
// Added more debugging support etc.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
#include <kerb.hxx>
|
|
#define CTXTMGR_ALLOCATE
|
|
#include <kerbp.h>
|
|
#include "userapi.h"
|
|
#ifdef RETAIL_LOG_SUPPORT
|
|
static TCHAR THIS_FILE[]=TEXT(__FILE__);
|
|
#endif
|
|
|
|
ULONG
|
|
HandleToListIndex(
|
|
ULONG_PTR ContextHandle
|
|
);
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbInitContextList
|
|
//
|
|
// Synopsis: Initializes the contexts list
|
|
//
|
|
// Effects: allocates a resources
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS on success, other error codes
|
|
// on failure
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbInitContextList(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Index = 0;
|
|
|
|
__try {
|
|
SafeInitializeResource( &KerbContextResource, CONTEXT_RESOURCE_LOCK_ENUM );
|
|
} __except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
for(Index = 0 ; Index < KERB_USERLIST_COUNT ; Index++)
|
|
{
|
|
Status = KerbInitializeList( &KerbContextList[Index], CONTEXT_LIST_LOCK_ENUM );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
KerberosContextsInitialized = TRUE;
|
|
|
|
Cleanup:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ULONG i;
|
|
|
|
SafeDeleteResource( &KerbContextResource );
|
|
|
|
for( i = 0 ; i < Index ; i++ )
|
|
{
|
|
KerbFreeList( &KerbContextList[i] );
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreeContextList
|
|
//
|
|
// Synopsis: Frees the contexts list
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: none
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbFreeContextList(
|
|
VOID
|
|
)
|
|
{
|
|
#if 0
|
|
PKERB_CONTEXT Context;
|
|
|
|
if (KerberosContextsInitialized)
|
|
{
|
|
KerbLockList(&KerbContextList);
|
|
|
|
//
|
|
// Go through the list of logon sessions and dereferences them all
|
|
//
|
|
|
|
while (!IsListEmpty(&KerbContextList.List))
|
|
{
|
|
Context = CONTAINING_RECORD(
|
|
KerbContextList.List.Flink,
|
|
KERB_CONTEXT,
|
|
ListEntry.Next
|
|
);
|
|
|
|
KerbReferenceListEntry(
|
|
&KerbContextList,
|
|
&Context->ListEntry,
|
|
TRUE
|
|
);
|
|
|
|
KerbDereferenceContext(Context);
|
|
|
|
}
|
|
|
|
KerbFreeList(&KerbContextList);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbAllocateContext
|
|
//
|
|
// Synopsis: Allocates a Context structure
|
|
//
|
|
// Effects: Allocates a Context, but does not add it to the
|
|
// list of Contexts
|
|
//
|
|
// Arguments: NewContext - receives a new Context allocated
|
|
// with KerbAllocate
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS on success
|
|
// STATUS_INSUFFICIENT_RESOURCES if the allocation fails
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbAllocateContext(
|
|
OUT PKERB_CONTEXT * NewContext,
|
|
IN BOOLEAN UserMode
|
|
)
|
|
{
|
|
PKERB_CONTEXT Context;
|
|
SECPKG_CALL_INFO CallInfo = {0};
|
|
|
|
//
|
|
// Get the client process ID if we are running in the LSA
|
|
//
|
|
|
|
if (!UserMode)
|
|
{
|
|
if (!LsaFunctions->GetCallInfo(&CallInfo))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to get call info\n. %ws, line %d\n",
|
|
THIS_FILE, __LINE__));
|
|
DsysAssert(FALSE);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
|
|
Context = (PKERB_CONTEXT) KerbAllocate(
|
|
sizeof(KERB_CONTEXT) );
|
|
|
|
if (Context == NULL)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
Context->ClientProcess = CallInfo.ProcessId;
|
|
Context->ContextState = IdleState;
|
|
|
|
//
|
|
// Set the references to 1 since we are returning a pointer to the
|
|
// logon session
|
|
//
|
|
|
|
KerbInitializeListEntry(
|
|
&Context->ListEntry
|
|
);
|
|
|
|
|
|
*NewContext = Context;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbInsertContext
|
|
//
|
|
// Synopsis: Inserts a logon session into the list of logon sessions
|
|
//
|
|
// Effects: bumps reference count on logon session
|
|
//
|
|
// Arguments: Context - Context to insert
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS always
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbInsertContext(
|
|
IN PKERB_CONTEXT Context
|
|
)
|
|
{
|
|
ULONG ListIndex;
|
|
|
|
ListIndex = HandleToListIndex( Context->LsaContextHandle );
|
|
|
|
Context->ContextTag = KERB_CONTEXT_TAG_ACTIVE;
|
|
|
|
KerbInsertListEntry(
|
|
&Context->ListEntry,
|
|
&KerbContextList[ListIndex]
|
|
);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbReferenceContext
|
|
//
|
|
// Synopsis: Locates a context context handleand references it
|
|
//
|
|
// Effects: Increments reference count and possible unlinks it from list
|
|
//
|
|
// Arguments: ContextHandle - Handle of context to reference.
|
|
// RemoveFromList - If TRUE, context will be delinked.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
SECURITY_STATUS
|
|
KerbReferenceContext(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
IN BOOLEAN RemoveFromList,
|
|
OUT PKERB_CONTEXT * FoundContext
|
|
)
|
|
{
|
|
PKERB_CONTEXT Context = NULL;
|
|
BOOLEAN Found = FALSE;
|
|
SECPKG_CLIENT_INFO ClientInfo;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG ListIndex = 0;
|
|
BOOLEAN ListLocked = FALSE;
|
|
|
|
if (KerberosState == KerberosLsaMode)
|
|
{
|
|
Status = LsaFunctions->GetClientInfo(&ClientInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SECPKG_CALL_INFO CallInfo;
|
|
//
|
|
// Check to see if the call is terminating. If so, give it
|
|
// TCB privilege because it is really the LSA doing this.
|
|
//
|
|
|
|
if (LsaFunctions->GetCallInfo(&CallInfo))
|
|
{
|
|
if ((CallInfo.Attributes & SECPKG_CALL_CLEANUP) != 0)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
RtlZeroMemory(
|
|
&ClientInfo,
|
|
sizeof(SECPKG_CLIENT_INFO)
|
|
);
|
|
ClientInfo.HasTcbPrivilege = TRUE;
|
|
}
|
|
|
|
}
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to get client info: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__));
|
|
*FoundContext = NULL ;
|
|
return(Status);
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
ClientInfo.HasTcbPrivilege = FALSE ;
|
|
ClientInfo.ProcessID = GetCurrentProcessId();
|
|
}
|
|
|
|
//
|
|
// Go through the list of logon sessions looking for the correct
|
|
// context
|
|
//
|
|
|
|
Context = (PKERB_CONTEXT)ContextHandle;
|
|
|
|
while ( Context->ContextTag == KERB_CONTEXT_TAG_ACTIVE )
|
|
{
|
|
ListIndex = HandleToListIndex( Context->LsaContextHandle );
|
|
KerbLockList(&KerbContextList[ListIndex]);
|
|
ListLocked = TRUE;
|
|
|
|
//
|
|
// Make sure that if we aren't trying to remove it we are
|
|
// from the correct process.
|
|
//
|
|
|
|
if (!ClientInfo.HasTcbPrivilege &&
|
|
(Context->ClientProcess != ClientInfo.ProcessID) &&
|
|
(KerberosState == KerberosLsaMode))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Trying to reference a context from another process! %ws, line %d\n", THIS_FILE, __LINE__));
|
|
Found = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the context is expired, don't allow it to be referenced.
|
|
//
|
|
|
|
if (KerbGlobalEnforceTime && !RemoveFromList)
|
|
{
|
|
TimeStamp CurrentTime;
|
|
TimeStamp ContextExpires;
|
|
GetSystemTimeAsFileTime((PFILETIME) &CurrentTime);
|
|
ContextExpires = Context->Lifetime;
|
|
if (KerbGetTime(ContextExpires) < KerbGetTime(CurrentTime))
|
|
{
|
|
D_DebugLog((DEB_WARN, "Trying to reference expired context\n"));
|
|
Found = FALSE;
|
|
Status = SEC_E_CONTEXT_EXPIRED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
KerbReferenceListEntry(
|
|
&KerbContextList[ ListIndex ],
|
|
&Context->ListEntry,
|
|
RemoveFromList
|
|
);
|
|
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
if ( ListLocked )
|
|
{
|
|
KerbUnlockList(&KerbContextList[ListIndex]);
|
|
}
|
|
|
|
if (!Found)
|
|
{
|
|
Context = NULL;
|
|
}
|
|
|
|
*FoundContext = Context ;
|
|
|
|
if ( Context )
|
|
{
|
|
return Status;
|
|
}
|
|
else if (NT_SUCCESS(Status))
|
|
{
|
|
return SEC_E_INVALID_HANDLE ;
|
|
}
|
|
else
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbReferenceContextByLsaHandle
|
|
//
|
|
// Synopsis: Locates a context by lsa context handle and references it
|
|
//
|
|
// Effects: Increments reference count and possible unlinks it from list
|
|
//
|
|
// Arguments: ContextHandle - Lsa Handle of context to reference.
|
|
// RemoveFromList - If TRUE, context will be delinked.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
SECURITY_STATUS
|
|
KerbReferenceContextByLsaHandle(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
IN BOOLEAN RemoveFromList,
|
|
OUT PKERB_CONTEXT *FoundContext
|
|
)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PKERB_CONTEXT Context = NULL;
|
|
BOOLEAN Found = FALSE;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG ListIndex;
|
|
|
|
ListIndex = HandleToListIndex( ContextHandle );
|
|
|
|
KerbLockList(&KerbContextList[ListIndex]);
|
|
|
|
|
|
//
|
|
// Go through the list of logon sessions looking for the correct
|
|
// context
|
|
//
|
|
|
|
for (ListEntry = KerbContextList[ListIndex].List.Flink ;
|
|
ListEntry != &KerbContextList[ListIndex].List ;
|
|
ListEntry = ListEntry->Flink )
|
|
{
|
|
Context = CONTAINING_RECORD(ListEntry, KERB_CONTEXT, ListEntry.Next);
|
|
if (ContextHandle == Context->LsaContextHandle)
|
|
{
|
|
|
|
//
|
|
// Make sure that if we aren't trying to remove it we are
|
|
// from the correct process.
|
|
//
|
|
|
|
//
|
|
// If the context is expired, don't allow it to be referenced.
|
|
//
|
|
|
|
if (KerbGlobalEnforceTime && !RemoveFromList )
|
|
{
|
|
TimeStamp CurrentTime;
|
|
TimeStamp ContextExpires;
|
|
GetSystemTimeAsFileTime((PFILETIME) &CurrentTime );
|
|
ContextExpires = Context->Lifetime;
|
|
if (KerbGetTime(ContextExpires) < KerbGetTime(CurrentTime))
|
|
{
|
|
D_DebugLog((DEB_WARN, "Trying to reference expired context\n"));
|
|
Found = FALSE;
|
|
Status = SEC_E_CONTEXT_EXPIRED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
KerbReferenceListEntry(
|
|
&KerbContextList[ListIndex],
|
|
&Context->ListEntry,
|
|
RemoveFromList
|
|
);
|
|
|
|
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
KerbUnlockList(&KerbContextList[ListIndex]);
|
|
|
|
if (!Found)
|
|
{
|
|
Context = NULL;
|
|
}
|
|
*FoundContext = Context ;
|
|
|
|
if ( Context )
|
|
{
|
|
return Status;
|
|
}
|
|
else if (NT_SUCCESS(Status))
|
|
{
|
|
return SEC_E_INVALID_HANDLE ;
|
|
}
|
|
else
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbReferenceContextByPointer
|
|
//
|
|
// Synopsis: References a context by the context pointer itself.
|
|
//
|
|
// Effects: Increments reference count and possible unlinks it from list
|
|
//
|
|
// Arguments: Context - The context to reference.
|
|
// RemoveFromList - If TRUE, context will be delinked
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
VOID
|
|
KerbReferenceContextByPointer(
|
|
IN PKERB_CONTEXT Context,
|
|
IN BOOLEAN RemoveFromList
|
|
)
|
|
{
|
|
ULONG ListIndex;
|
|
|
|
ListIndex = HandleToListIndex( Context->LsaContextHandle );
|
|
|
|
KerbLockList(&KerbContextList[ListIndex]);
|
|
|
|
KerbReferenceListEntry(
|
|
&KerbContextList[ListIndex],
|
|
&Context->ListEntry,
|
|
RemoveFromList
|
|
);
|
|
|
|
KerbUnlockList(&KerbContextList[ListIndex]);
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreeContext
|
|
//
|
|
// Synopsis: Frees a context that is unlinked
|
|
//
|
|
// Effects: frees all storage associated with the context
|
|
//
|
|
// Arguments: Context - context to free
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: none
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
VOID
|
|
KerbFreeContext(
|
|
IN PKERB_CONTEXT Context
|
|
)
|
|
{
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
if (Context->TokenHandle != NULL)
|
|
{
|
|
NtClose(Context->TokenHandle);
|
|
}
|
|
#endif // WIN32_CHICAGO
|
|
Context->ContextTag = KERB_CONTEXT_TAG_DELETE;
|
|
|
|
KerbFreeKey(&Context->SessionKey);
|
|
KerbFreeKey(&Context->TicketKey);
|
|
if (Context->TicketCacheEntry != NULL)
|
|
{
|
|
KerbDereferenceTicketCacheEntry(Context->TicketCacheEntry);
|
|
}
|
|
if (Context->UserSid != NULL)
|
|
{
|
|
KerbFree(Context->UserSid);
|
|
}
|
|
|
|
KerbFreeString(&Context->ClientName);
|
|
KerbFreeString(&Context->ClientRealm);
|
|
KerbFreeString(&Context->ClientDnsRealm);
|
|
KerbFreeString(&Context->ClientPrincipalName);
|
|
KerbFreeString(&Context->ServerPrincipalName);
|
|
|
|
if( Context->pbMarshalledTargetInfo )
|
|
{
|
|
KerbFree( Context->pbMarshalledTargetInfo );
|
|
}
|
|
|
|
KerbFree(Context);
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbDereferenceContext
|
|
//
|
|
// Synopsis: Dereferences a logon session - if reference count goes
|
|
// to zero it frees the logon session
|
|
//
|
|
// Effects: decrements reference count
|
|
//
|
|
// Arguments: Context - Logon session to dereference
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: none
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
VOID
|
|
KerbDereferenceContext(
|
|
IN PKERB_CONTEXT Context
|
|
)
|
|
{
|
|
ULONG ListIndex;
|
|
|
|
ListIndex = HandleToListIndex( Context->LsaContextHandle );
|
|
|
|
if (KerbDereferenceListEntry(
|
|
&Context->ListEntry,
|
|
&KerbContextList[ListIndex]
|
|
) )
|
|
{
|
|
KerbFreeContext(Context);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreateEmptyContext
|
|
//
|
|
// Synopsis: Creates a context for the server of a datagram authentication
|
|
// session. Since there is no input message, all the fields
|
|
// are initialized to zero.
|
|
//
|
|
// Effects: Allocates and links a context.
|
|
//
|
|
// Arguments: Credential - Credential to hang this context off of
|
|
// ContextFlags - Flags for the context
|
|
// LogonId = LogonId of the creating logon session
|
|
// NewContext - receives referenced context pointer,
|
|
// ContextLifetime - Lifetime for the context.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: NTSTATUS code
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbCreateEmptyContext(
|
|
IN PKERB_CREDENTIAL Credential,
|
|
IN ULONG ContextFlags,
|
|
IN ULONG ContextAttributes,
|
|
IN ULONG NegotitationInfo,
|
|
IN PLUID LogonId,
|
|
OUT PKERB_CONTEXT * NewContext,
|
|
OUT PTimeStamp ContextLifetime
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKERB_CONTEXT Context = NULL;
|
|
|
|
Status = KerbAllocateContext( &Context, FALSE );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Context->CredentialHandle = KerbGetCredentialHandle(Credential);
|
|
Context->ContextFlags = ContextFlags;
|
|
Context->ContextAttributes = ContextAttributes;
|
|
Context->Lifetime = KerbGlobalWillNeverTime;
|
|
Context->CredManCredentials = NULL;
|
|
Context->NegotiationInfo = NegotitationInfo;
|
|
GetSystemTimeAsFileTime((PFILETIME)
|
|
&(Context->StartTime)
|
|
);
|
|
|
|
Context->LogonId = *LogonId;
|
|
*ContextLifetime = Context->Lifetime;
|
|
|
|
|
|
Status = KerbInsertContext(
|
|
Context
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to insert context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
*NewContext = Context;
|
|
|
|
|
|
Cleanup:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Context != NULL)
|
|
{
|
|
KerbFreeContext(Context);
|
|
}
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreateClientContext
|
|
//
|
|
// Synopsis: Creates a context for the client of an authentication
|
|
// session.
|
|
//
|
|
// Effects: Allocates and links a context.
|
|
//
|
|
// Arguments: LogonSession - Logon session to lock when accessing the credential
|
|
// Credential - Credential to hang this context off of
|
|
// TicketCacheEntry - Ticket around which to build this context
|
|
// TargetName - target name supplied by client.
|
|
// Nonce - Nonce used in AP request
|
|
// SubSessionKey - subsession key to use, if present
|
|
// ContextFlags - Flags passed in by client for authentication
|
|
// options.
|
|
// NewContext - receives referenced context pointer,
|
|
// ContextLifetime - Lifetime for the context.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: NTSTATUS code
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbCreateClientContext(
|
|
IN PKERB_LOGON_SESSION LogonSession,
|
|
IN PKERB_CREDENTIAL Credential,
|
|
IN OPTIONAL PKERB_CREDMAN_CRED CredManCredentials,
|
|
IN OPTIONAL PKERB_TICKET_CACHE_ENTRY TicketCacheEntry,
|
|
IN OPTIONAL PUNICODE_STRING TargetName,
|
|
IN ULONG Nonce,
|
|
IN PTimeStamp pAuthenticatorTime,
|
|
IN ULONG ContextFlags,
|
|
IN ULONG ContextAttributes,
|
|
IN OPTIONAL PKERB_ENCRYPTION_KEY SubSessionKey,
|
|
OUT PKERB_CONTEXT * NewContext,
|
|
OUT PTimeStamp ContextLifetime
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKERB_CONTEXT Context = NULL;
|
|
|
|
Status = KerbAllocateContext( &Context, FALSE );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
if (ARGUMENT_PRESENT(TicketCacheEntry))
|
|
{
|
|
//
|
|
// If we are doing datagram, reference the cache entry and
|
|
// store a pointer to it in the context. The ticket cache cannot be
|
|
// read locked at this point because the call to acquire a write
|
|
// lock will block.
|
|
//
|
|
|
|
if ((ContextFlags & ISC_RET_DATAGRAM) != 0)
|
|
{
|
|
|
|
KerbReferenceTicketCacheEntry(TicketCacheEntry);
|
|
Context->TicketCacheEntry = TicketCacheEntry;
|
|
}
|
|
|
|
KerbReadLockTicketCache();
|
|
|
|
//
|
|
// Duplicate the session key into the context
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(SubSessionKey) && (SubSessionKey->keyvalue.value != NULL))
|
|
{
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->SessionKey,
|
|
SubSessionKey
|
|
)))
|
|
{
|
|
KerbUnlockTicketCache();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For datagram, create a key
|
|
//
|
|
|
|
if ((ContextFlags & ISC_RET_DATAGRAM) != 0)
|
|
{
|
|
KERBERR KerbErr;
|
|
|
|
SECPKG_CALL_INFO CallInfo;
|
|
|
|
if (!LsaFunctions->GetCallInfo(&CallInfo))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to get client info: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
KerbUnlockTicketCache();
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// If we are configured for strong encryption & we have said to
|
|
// use it for datagram or this is an inprocess caller, then create
|
|
// a strong key.
|
|
//
|
|
|
|
|
|
D_DebugLog((DEB_TRACE_CTXT,"Making exportable key for datagram client context\n"));
|
|
if ( KerbGlobalUseStrongEncryptionForDatagram ||
|
|
(CallInfo.Attributes & SECPKG_CALL_IN_PROC))
|
|
{
|
|
KerbErr = KerbMakeKey(
|
|
TicketCacheEntry->SessionKey.keytype,
|
|
&Context->SessionKey
|
|
);
|
|
}
|
|
else
|
|
{
|
|
KerbErr = KerbMakeExportableKey(
|
|
TicketCacheEntry->SessionKey.keytype,
|
|
&Context->SessionKey
|
|
);
|
|
}
|
|
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
Status = KerbMapKerbError(KerbErr);
|
|
KerbUnlockTicketCache();
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->SessionKey,
|
|
&TicketCacheEntry->SessionKey
|
|
)))
|
|
{
|
|
KerbUnlockTicketCache();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->TicketKey,
|
|
&TicketCacheEntry->SessionKey
|
|
)))
|
|
{
|
|
KerbUnlockTicketCache();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the client's realm
|
|
//
|
|
|
|
Status = KerbDuplicateString(
|
|
&Context->ClientRealm,
|
|
&TicketCacheEntry->DomainName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KerbUnlockTicketCache();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the client's name.
|
|
//
|
|
|
|
if (!KERB_SUCCESS(KerbConvertKdcNameToString(
|
|
&Context->ClientName,
|
|
TicketCacheEntry->ClientName,
|
|
NULL)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
KerbUnlockTicketCache();
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Context->Lifetime = TicketCacheEntry->EndTime;
|
|
Context->StartTime = TicketCacheEntry->StartTime;
|
|
Context->RenewTime = TicketCacheEntry->RenewUntil;
|
|
Context->EncryptionType = TicketCacheEntry->Ticket.encrypted_part.encryption_type;
|
|
KerbUnlockTicketCache();
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
Context->Lifetime = KerbGlobalWillNeverTime;
|
|
Context->RenewTime = KerbGlobalWillNeverTime;
|
|
|
|
GetSystemTimeAsFileTime((PFILETIME)
|
|
&(Context->StartTime)
|
|
);
|
|
|
|
Context->EncryptionType = KERB_ETYPE_NULL;
|
|
}
|
|
|
|
Context->Nonce = Nonce;
|
|
Context->AuthenticatorTime = *pAuthenticatorTime;
|
|
|
|
//
|
|
// For now, until the server sends us a separate nonce, use the one
|
|
// we generated for receiving data
|
|
//
|
|
|
|
Context->ReceiveNonce = Nonce;
|
|
|
|
Context->CredentialHandle = KerbGetCredentialHandle(Credential);
|
|
|
|
//KerbReadLockLogonSessions(LogonSession); // FESTER: Needed??
|
|
if (ARGUMENT_PRESENT(CredManCredentials))
|
|
{
|
|
Context->ContextAttributes |= KERB_CONTEXT_USING_CREDMAN;
|
|
Context->CredManCredentials = CredManCredentials; // don't ref, as we don't use it..
|
|
}
|
|
else if ((Credential->SuppliedCredentials != NULL))
|
|
{
|
|
Context->ContextAttributes |= KERB_CONTEXT_USED_SUPPLIED_CREDS;
|
|
}
|
|
//KerbUnlockLogonSessions(LogonSession);
|
|
|
|
Context->ContextFlags = ContextFlags;
|
|
Context->ContextAttributes = KERB_CONTEXT_OUTBOUND | ContextAttributes;
|
|
|
|
|
|
//
|
|
// if the caller supplied a target name, stash it in the context
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(TargetName))
|
|
{
|
|
Status = KerbDuplicateString(
|
|
&Context->ServerPrincipalName,
|
|
TargetName
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
*ContextLifetime = Context->Lifetime;
|
|
|
|
Status = KerbInsertContext(
|
|
Context
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to insert context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
*NewContext = Context;
|
|
|
|
|
|
Cleanup:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Context != NULL)
|
|
{
|
|
KerbFreeContext(Context);
|
|
}
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbUpdateServerContext
|
|
//
|
|
// Synopsis: Creates a context for the server of an authentication
|
|
// session.
|
|
//
|
|
// Effects: Allocates and links a context.
|
|
//
|
|
// Arguments: Context - Context to update
|
|
// InternalTicket - Ticket used to create this context.
|
|
// SessionKey - Session key from the ticket.
|
|
// LogonId - Logon ID of the context
|
|
// UserSid - User's sid, held onto by context
|
|
// ContextFlags - SSPI Flags for this context
|
|
// ContextAttributes - Internal attributes of context
|
|
// TokenHandle - Handle the token for this context
|
|
// ContextLifetime - Lifetime for the context.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: NTSTATUS code
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbUpdateServerContext(
|
|
IN PKERB_CONTEXT Context,
|
|
IN PKERB_ENCRYPTED_TICKET InternalTicket,
|
|
IN PKERB_AP_REQUEST ApRequest,
|
|
IN PKERB_ENCRYPTION_KEY SessionKey,
|
|
IN PLUID LogonId,
|
|
IN PSID * UserSid,
|
|
IN ULONG ContextFlags,
|
|
IN ULONG ContextAttributes,
|
|
IN ULONG NegotiationInfo,
|
|
IN ULONG Nonce,
|
|
IN ULONG ReceiveNonce,
|
|
IN OUT PHANDLE TokenHandle,
|
|
IN PUNICODE_STRING ClientName,
|
|
IN PUNICODE_STRING ClientDomain,
|
|
IN PUNICODE_STRING ClientNetbiosDomain,
|
|
OUT PTimeStamp ContextLifetime
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
KerbWriteLockContexts();
|
|
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->SessionKey,
|
|
SessionKey
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If this was not a null session and there is a ticket available,
|
|
// pull interesting information out of the ticket
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(InternalTicket))
|
|
{
|
|
KerbConvertGeneralizedTimeToLargeInt(
|
|
&Context->Lifetime,
|
|
&InternalTicket->endtime,
|
|
0
|
|
);
|
|
|
|
if (InternalTicket->bit_mask & KERB_ENCRYPTED_TICKET_renew_until_present)
|
|
{
|
|
KerbConvertGeneralizedTimeToLargeInt(
|
|
&Context->RenewTime,
|
|
&InternalTicket->KERB_ENCRYPTED_TICKET_renew_until,
|
|
0
|
|
);
|
|
}
|
|
|
|
//
|
|
// Stick the client name in the context
|
|
//
|
|
|
|
Context->ClientName = *ClientName;
|
|
RtlInitUnicodeString(
|
|
ClientName,
|
|
NULL
|
|
);
|
|
|
|
Context->ClientRealm = *ClientNetbiosDomain;
|
|
RtlInitUnicodeString(
|
|
ClientNetbiosDomain,
|
|
NULL
|
|
);
|
|
|
|
|
|
Context->ClientDnsRealm = *ClientDomain;
|
|
RtlInitUnicodeString(
|
|
ClientDomain,
|
|
NULL
|
|
);
|
|
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->TicketKey,
|
|
&InternalTicket->key)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the principal names from the ticket
|
|
//
|
|
|
|
if (!KERB_SUCCESS(KerbConvertPrincipalNameToFullServiceString(
|
|
&Context->ClientPrincipalName,
|
|
&InternalTicket->client_name,
|
|
InternalTicket->client_realm
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!KERB_SUCCESS(KerbConvertPrincipalNameToFullServiceString(
|
|
&Context->ServerPrincipalName,
|
|
&ApRequest->ticket.server_name,
|
|
ApRequest->ticket.realm
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Context->Lifetime = KerbGlobalWillNeverTime;
|
|
Context->RenewTime = KerbGlobalWillNeverTime;
|
|
Context->EncryptionType = KERB_ETYPE_NULL;
|
|
}
|
|
|
|
//
|
|
// If we generated a nonce to send to the client, use it. Otherwise use
|
|
// the receive nonce from the AP req.
|
|
//
|
|
|
|
if (Nonce != 0)
|
|
{
|
|
Context->Nonce = Nonce;
|
|
}
|
|
else
|
|
{
|
|
Context->Nonce = ReceiveNonce;
|
|
}
|
|
|
|
Context->ReceiveNonce = ReceiveNonce;
|
|
|
|
Context->LogonId = *LogonId;
|
|
Context->ContextFlags |= ContextFlags;
|
|
Context->TokenHandle = *TokenHandle;
|
|
Context->UserSid = *UserSid;
|
|
|
|
//
|
|
// Noted we wipe out the previous ContextAttributes here, not sure why
|
|
//
|
|
|
|
D_DebugLog((DEB_TRACE, "KerbUpdateServerContext ContextAttributes was %#x, now %#x\n", Context->ContextAttributes, ContextAttributes | KERB_CONTEXT_INBOUND));
|
|
|
|
Context->ContextAttributes = KERB_CONTEXT_INBOUND;
|
|
Context->ContextAttributes |= ContextAttributes;
|
|
|
|
Context->NegotiationInfo = NegotiationInfo;
|
|
*ContextLifetime = Context->Lifetime;
|
|
|
|
//
|
|
// Null the token handle so the caller does not free it
|
|
//
|
|
|
|
*TokenHandle = NULL;
|
|
*UserSid = NULL;
|
|
|
|
Cleanup:
|
|
KerbUnlockContexts();
|
|
return (Status);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreateServerContext
|
|
//
|
|
// Synopsis: Creates a context for the server of an authentication
|
|
// session.
|
|
//
|
|
// Effects: Allocates and links a context.
|
|
//
|
|
// Arguments: LogonSession - Logon session to lock when accessing the credential
|
|
// Credential - Credential to hang this context off of
|
|
// InternalTicket - Ticket used to create this context.
|
|
// SessionKey - Session key from the ticket.
|
|
// LogonId - Logon ID of the context
|
|
// UserSid - User's SID, held on to in context.
|
|
// ContextFlags - SSPI Flags for this context
|
|
// ContextAttributes - Internal attributes of context
|
|
// TokenHandle - Handle the token for this context
|
|
// NewContext - receives referenced context pointer
|
|
// ContextLifetime - Lifetime for the context.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: NTSTATUS code
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbCreateServerContext(
|
|
IN PKERB_LOGON_SESSION LogonSession,
|
|
IN PKERB_CREDENTIAL Credential,
|
|
IN OPTIONAL PKERB_ENCRYPTED_TICKET InternalTicket,
|
|
IN PKERB_AP_REQUEST ApRequest,
|
|
IN PKERB_ENCRYPTION_KEY SessionKey,
|
|
IN PLUID LogonId,
|
|
IN PSID * UserSid,
|
|
IN ULONG ContextFlags,
|
|
IN ULONG ContextAttributes,
|
|
IN ULONG NegotiationInfo,
|
|
IN ULONG Nonce,
|
|
IN ULONG ReceiveNonce,
|
|
IN OUT PHANDLE TokenHandle,
|
|
IN PUNICODE_STRING ClientName,
|
|
IN PUNICODE_STRING ClientDomain,
|
|
IN PUNICODE_STRING ClientNetbiosDomain,
|
|
OUT PKERB_CONTEXT * NewContext,
|
|
OUT PTimeStamp ContextLifetime
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKERB_CONTEXT Context = NULL;
|
|
|
|
|
|
Status = KerbAllocateContext( &Context, FALSE );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->SessionKey,
|
|
SessionKey
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If this wasn't a null session, stick the info from the ticket into
|
|
// the context.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(InternalTicket))
|
|
{
|
|
KerbConvertGeneralizedTimeToLargeInt(
|
|
&Context->Lifetime,
|
|
&InternalTicket->endtime,
|
|
0
|
|
);
|
|
if (InternalTicket->bit_mask & KERB_ENCRYPTED_TICKET_renew_until_present)
|
|
{
|
|
KerbConvertGeneralizedTimeToLargeInt(
|
|
&Context->RenewTime,
|
|
&InternalTicket->KERB_ENCRYPTED_TICKET_renew_until,
|
|
0
|
|
);
|
|
|
|
}
|
|
|
|
if (InternalTicket->bit_mask & KERB_ENCRYPTED_TICKET_starttime_present)
|
|
{
|
|
KerbConvertGeneralizedTimeToLargeInt(
|
|
&Context->StartTime,
|
|
&InternalTicket->starttime,
|
|
0
|
|
);
|
|
|
|
}
|
|
else // use current time
|
|
{
|
|
GetSystemTimeAsFileTime((PFILETIME)
|
|
&(Context->StartTime)
|
|
);
|
|
}
|
|
|
|
|
|
Context->ClientName = *ClientName;
|
|
RtlInitUnicodeString(
|
|
ClientName,
|
|
NULL
|
|
);
|
|
Context->ClientRealm = *ClientNetbiosDomain;
|
|
RtlInitUnicodeString(
|
|
ClientNetbiosDomain,
|
|
NULL
|
|
);
|
|
|
|
Context->ClientDnsRealm = *ClientDomain;
|
|
RtlInitUnicodeString(
|
|
ClientDomain,
|
|
NULL
|
|
);
|
|
|
|
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->TicketKey,
|
|
&InternalTicket->key)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the principal names from the ticket
|
|
//
|
|
|
|
if (!KERB_SUCCESS(KerbConvertPrincipalNameToFullServiceString(
|
|
&Context->ClientPrincipalName,
|
|
&InternalTicket->client_name,
|
|
InternalTicket->client_realm
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!KERB_SUCCESS(KerbConvertPrincipalNameToFullServiceString(
|
|
&Context->ServerPrincipalName,
|
|
&ApRequest->ticket.server_name,
|
|
ApRequest->ticket.realm
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Context->Lifetime = KerbGlobalWillNeverTime;
|
|
Context->RenewTime = KerbGlobalWillNeverTime;
|
|
|
|
GetSystemTimeAsFileTime((PFILETIME)
|
|
&(Context->StartTime)
|
|
);
|
|
|
|
Context->EncryptionType = KERB_ETYPE_NULL;
|
|
}
|
|
|
|
//
|
|
// If we generated a nonce to send to the client, use it. Otherwise use
|
|
// the receive nonce from the AP req.
|
|
//
|
|
|
|
// if (Nonce != 0)
|
|
// {
|
|
Context->Nonce = Nonce;
|
|
// }
|
|
// else
|
|
// {
|
|
// Context->Nonce = ReceiveNonce;
|
|
// }
|
|
|
|
Context->ReceiveNonce = ReceiveNonce;
|
|
Context->CredentialHandle = KerbGetCredentialHandle(Credential);
|
|
Context->LogonId = *LogonId;
|
|
Context->ContextFlags = ContextFlags;
|
|
Context->TokenHandle = *TokenHandle;
|
|
Context->UserSid = *UserSid;
|
|
Context->NegotiationInfo = NegotiationInfo;
|
|
//
|
|
// Null the token handle so the caller does not free it
|
|
//
|
|
|
|
*TokenHandle = NULL;
|
|
*UserSid = NULL;
|
|
|
|
Context->ContextAttributes = KERB_CONTEXT_INBOUND;
|
|
Context->ContextAttributes |= ContextAttributes;
|
|
|
|
KerbReadLockLogonSessions(LogonSession);
|
|
if (Credential->SuppliedCredentials != NULL)
|
|
{
|
|
Context->ContextAttributes |= KERB_CONTEXT_USED_SUPPLIED_CREDS;
|
|
}
|
|
|
|
|
|
KerbUnlockLogonSessions(LogonSession);
|
|
|
|
*ContextLifetime = Context->Lifetime;
|
|
|
|
Status = KerbInsertContext(
|
|
Context
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to insert context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
*NewContext = Context;
|
|
|
|
|
|
Cleanup:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Context != NULL)
|
|
{
|
|
KerbFreeContext(Context);
|
|
}
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbMapContext
|
|
//
|
|
// Synopsis: Maps a context to the caller's address space
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Context - The context to map
|
|
// CopyToken - If TRUE, duplicate token to client
|
|
// MappedContext - Set to TRUE on success
|
|
// ContextData - Receives a buffer in the LSA's address space
|
|
// with the mapped context.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbMapContext(
|
|
IN PKERB_CONTEXT Context,
|
|
OUT PBOOLEAN MappedContext,
|
|
OUT PSecBuffer ContextData
|
|
)
|
|
{
|
|
NTSTATUS Status ;
|
|
PKERB_PACKED_CONTEXT PackedContext = NULL ;
|
|
ULONG ContextSize ;
|
|
PUCHAR CopyTo ;
|
|
ULONG CurrentOffset ;
|
|
|
|
KerbWriteLockContexts();
|
|
|
|
//
|
|
// If we already mapped the context don't try to do it again. We may
|
|
// be able to map user-mode contexts multiple times, though.
|
|
//
|
|
|
|
if (KerberosState == KerberosLsaMode)
|
|
{
|
|
if ((Context->ContextAttributes & KERB_CONTEXT_MAPPED) != 0)
|
|
{
|
|
KerbUnlockContexts();
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Context->ContextAttributes |= KERB_CONTEXT_MAPPED;
|
|
}
|
|
|
|
|
|
ContextSize = sizeof(KERB_PACKED_CONTEXT) +
|
|
ROUND_UP_COUNT(Context->ClientName.Length, ALIGN_LPDWORD) +
|
|
ROUND_UP_COUNT(Context->ClientRealm.Length, ALIGN_LPDWORD) +
|
|
ROUND_UP_COUNT(Context->SessionKey.keyvalue.length, ALIGN_LPDWORD) +
|
|
ROUND_UP_COUNT(Context->cbMarshalledTargetInfo, ALIGN_LPDWORD) ;
|
|
|
|
PackedContext = (PKERB_PACKED_CONTEXT) KerbAllocate( ContextSize );
|
|
|
|
if (PackedContext == NULL)
|
|
{
|
|
Context->ContextAttributes &= ~(KERB_CONTEXT_MAPPED);
|
|
|
|
KerbUnlockContexts();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
CurrentOffset = sizeof( KERB_PACKED_CONTEXT );
|
|
CopyTo = (PUCHAR) ( PackedContext + 1 );
|
|
|
|
PackedContext->ContextType = KERB_PACKED_CONTEXT_MAP ;
|
|
PackedContext->Pad = 0 ;
|
|
PackedContext->Lifetime = Context->Lifetime ;
|
|
PackedContext->RenewTime = Context->RenewTime ;
|
|
PackedContext->StartTime = Context->StartTime;
|
|
|
|
PackedContext->ClientName.Length = Context->ClientName.Length ;
|
|
PackedContext->ClientName.MaximumLength = Context->ClientName.Length ;
|
|
PackedContext->ClientName.Buffer = CurrentOffset ;
|
|
|
|
RtlCopyMemory(
|
|
CopyTo,
|
|
Context->ClientName.Buffer,
|
|
Context->ClientName.Length );
|
|
|
|
CurrentOffset += ROUND_UP_COUNT(Context->ClientName.Length, ALIGN_LPDWORD) ;
|
|
CopyTo += ROUND_UP_COUNT(Context->ClientName.Length, ALIGN_LPDWORD) ;
|
|
|
|
PackedContext->ClientRealm.Length = Context->ClientRealm.Length ;
|
|
PackedContext->ClientRealm.MaximumLength = Context->ClientRealm.Length ;
|
|
PackedContext->ClientRealm.Buffer = CurrentOffset ;
|
|
|
|
RtlCopyMemory(
|
|
CopyTo,
|
|
Context->ClientRealm.Buffer,
|
|
Context->ClientRealm.Length );
|
|
|
|
CurrentOffset += ROUND_UP_COUNT(Context->ClientRealm.Length, ALIGN_LPDWORD);
|
|
CopyTo += ROUND_UP_COUNT(Context->ClientRealm.Length, ALIGN_LPDWORD) ;
|
|
|
|
PackedContext->LsaContextHandle = (ULONG) Context->LsaContextHandle ;
|
|
PackedContext->LogonId = Context->LogonId ;
|
|
|
|
PackedContext->CredentialHandle = 0 ;
|
|
PackedContext->SessionKeyType = Context->SessionKey.keytype ;
|
|
PackedContext->SessionKeyOffset = CurrentOffset ;
|
|
PackedContext->SessionKeyLength = Context->SessionKey.keyvalue.length ;
|
|
|
|
RtlCopyMemory(
|
|
CopyTo,
|
|
Context->SessionKey.keyvalue.value,
|
|
Context->SessionKey.keyvalue.length );
|
|
|
|
CurrentOffset += ROUND_UP_COUNT(PackedContext->SessionKeyLength, ALIGN_LPDWORD) ;
|
|
CopyTo += ROUND_UP_COUNT(PackedContext->SessionKeyLength, ALIGN_LPDWORD);
|
|
|
|
if( Context->pbMarshalledTargetInfo )
|
|
{
|
|
PackedContext->MarshalledTargetInfo = CurrentOffset;
|
|
PackedContext->MarshalledTargetInfoLength = Context->cbMarshalledTargetInfo;
|
|
|
|
RtlCopyMemory(
|
|
CopyTo,
|
|
Context->pbMarshalledTargetInfo,
|
|
Context->cbMarshalledTargetInfo );
|
|
|
|
CurrentOffset += ROUND_UP_COUNT(PackedContext->MarshalledTargetInfoLength, ALIGN_LPDWORD);
|
|
CopyTo += ROUND_UP_COUNT(PackedContext->MarshalledTargetInfoLength, ALIGN_LPDWORD);
|
|
}
|
|
|
|
PackedContext->Nonce = Context->Nonce ;
|
|
PackedContext->ReceiveNonce = Context->ReceiveNonce ;
|
|
PackedContext->ContextFlags = Context->ContextFlags ;
|
|
PackedContext->ContextAttributes = Context->ContextAttributes ;
|
|
PackedContext->EncryptionType = Context->EncryptionType ;
|
|
|
|
KerbUnlockContexts();
|
|
|
|
//
|
|
// If there is a token in the context, copy it also
|
|
//
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
if ((KerberosState == KerberosLsaMode) && (Context->TokenHandle != NULL))
|
|
{
|
|
HANDLE duplicateHandle;
|
|
|
|
Status = LsaFunctions->DuplicateHandle(
|
|
Context->TokenHandle,
|
|
&duplicateHandle
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to duplicate handle: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
|
|
PackedContext->TokenHandle = HandleToUlong(duplicateHandle);
|
|
NtClose(Context->TokenHandle);
|
|
Context->TokenHandle = NULL;
|
|
}
|
|
else
|
|
{
|
|
PackedContext->TokenHandle = NULL;
|
|
}
|
|
#endif // WIN32_CHICAGO
|
|
|
|
|
|
ContextData->pvBuffer = PackedContext;
|
|
ContextData->cbBuffer = ContextSize;
|
|
|
|
|
|
*MappedContext = TRUE;
|
|
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (PackedContext != NULL)
|
|
{
|
|
KerbFree(PackedContext);
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
#if 0
|
|
NTSTATUS
|
|
KerbMapContext(
|
|
IN PKERB_CONTEXT Context,
|
|
OUT PBOOLEAN MappedContext,
|
|
OUT PSecBuffer ContextData
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKERB_CONTEXT PackedContext = NULL;
|
|
ULONG ContextSize;
|
|
|
|
KerbWriteLockContexts();
|
|
|
|
//
|
|
// If we already mapped the context don't try to do it again. We may
|
|
// be able to map user-mode contexts multiple times, though.
|
|
//
|
|
|
|
if (KerberosState == KerberosLsaMode)
|
|
{
|
|
if ((Context->ContextAttributes & KERB_CONTEXT_MAPPED) != 0)
|
|
{
|
|
KerbUnlockContexts();
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Context->ContextAttributes |= KERB_CONTEXT_MAPPED;
|
|
}
|
|
|
|
|
|
ContextSize = sizeof(KERB_CONTEXT) +
|
|
Context->ClientName.Length +
|
|
Context->ClientRealm.Length +
|
|
Context->SessionKey.keyvalue.length;
|
|
|
|
PackedContext = (PKERB_CONTEXT) KerbAllocate(ContextSize);
|
|
|
|
if (PackedContext == NULL)
|
|
{
|
|
KerbUnlockContexts();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
RtlCopyMemory(
|
|
PackedContext,
|
|
Context,
|
|
sizeof(KERB_CONTEXT)
|
|
);
|
|
|
|
PackedContext->ClientName.Buffer = (LPWSTR) sizeof(KERB_CONTEXT);
|
|
RtlCopyMemory(
|
|
PackedContext+1,
|
|
Context->ClientName.Buffer,
|
|
Context->ClientName.Length
|
|
);
|
|
PackedContext->ClientName.MaximumLength = PackedContext->ClientName.Length;
|
|
|
|
|
|
PackedContext->ClientRealm.Buffer = (LPWSTR) (sizeof(KERB_CONTEXT) + PackedContext->ClientName.Length);
|
|
RtlCopyMemory(
|
|
(PUCHAR) PackedContext + (UINT_PTR) PackedContext->ClientRealm.Buffer,
|
|
Context->ClientRealm.Buffer,
|
|
Context->ClientRealm.Length
|
|
);
|
|
PackedContext->ClientRealm.MaximumLength = PackedContext->ClientRealm.Length;
|
|
|
|
RtlZeroMemory(
|
|
&PackedContext->TicketKey,
|
|
sizeof(KERB_ENCRYPTION_KEY)
|
|
);
|
|
RtlZeroMemory(
|
|
&PackedContext->ClientPrincipalName,
|
|
sizeof(UNICODE_STRING)
|
|
);
|
|
|
|
RtlZeroMemory(
|
|
&PackedContext->ServerPrincipalName,
|
|
sizeof(UNICODE_STRING)
|
|
);
|
|
|
|
//
|
|
// Pack in the session key
|
|
//
|
|
|
|
PackedContext->SessionKey.keyvalue.value = (PUCHAR) PackedContext->ClientRealm.Buffer + PackedContext->ClientRealm.MaximumLength;
|
|
|
|
RtlCopyMemory(
|
|
PackedContext->SessionKey.keyvalue.value + (UINT_PTR) PackedContext,
|
|
Context->SessionKey.keyvalue.value,
|
|
Context->SessionKey.keyvalue.length
|
|
);
|
|
|
|
KerbUnlockContexts();
|
|
|
|
//
|
|
// If there is a token in the context, copy it also
|
|
//
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
if ((KerberosState == KerberosLsaMode) && (Context->TokenHandle != NULL))
|
|
{
|
|
Status = LsaFunctions->DuplicateHandle(
|
|
Context->TokenHandle,
|
|
&PackedContext->TokenHandle
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to duplicate handle: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
NtClose(Context->TokenHandle);
|
|
Context->TokenHandle = NULL;
|
|
}
|
|
else
|
|
{
|
|
PackedContext->TokenHandle = NULL;
|
|
}
|
|
#endif // WIN32_CHICAGO
|
|
|
|
|
|
ContextData->pvBuffer = PackedContext;
|
|
ContextData->cbBuffer = ContextSize;
|
|
|
|
|
|
*MappedContext = TRUE;
|
|
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (PackedContext != NULL)
|
|
{
|
|
KerbFree(PackedContext);
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbGetTokenUser
|
|
//
|
|
// Synopsis: Returns user field from a token
|
|
//
|
|
// Effects: allocates memory with LocalAlloc
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbGetTokenUser(
|
|
HANDLE Token,
|
|
PTOKEN_USER * pTokenUser
|
|
)
|
|
{
|
|
PTOKEN_USER LocalTokenUser = NULL;
|
|
NTSTATUS Status;
|
|
ULONG TokenUserSize = 0;
|
|
|
|
//
|
|
// Query the token user. First pass in NULL to get back the
|
|
// required size.
|
|
//
|
|
|
|
Status = NtQueryInformationToken(
|
|
Token,
|
|
TokenUser,
|
|
NULL,
|
|
0,
|
|
&TokenUserSize
|
|
);
|
|
|
|
if (Status != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
DsysAssert(Status != STATUS_SUCCESS);
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Now allocate the required ammount of memory and try again.
|
|
//
|
|
|
|
LocalTokenUser = (PTOKEN_USER) LocalAlloc(0,TokenUserSize);
|
|
if (LocalTokenUser == NULL)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
Status = NtQueryInformationToken(
|
|
Token,
|
|
TokenUser,
|
|
LocalTokenUser,
|
|
TokenUserSize,
|
|
&TokenUserSize
|
|
);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*pTokenUser = LocalTokenUser;
|
|
}
|
|
else
|
|
{
|
|
LocalFree(LocalTokenUser);
|
|
}
|
|
return(Status);
|
|
}
|
|
#endif // WIN32_CHICAGO
|
|
|
|
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreateTokenDacl
|
|
//
|
|
// Synopsis: Modifies DACL on the context token to grant access to the
|
|
// the caller.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Token - Token to modify
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbCreateTokenDacl(
|
|
HANDLE Token
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PTOKEN_USER ProcessTokenUser = NULL;
|
|
PTOKEN_USER ThreadTokenUser = NULL;
|
|
PTOKEN_USER ImpersonationTokenUser = NULL;
|
|
HANDLE ProcessToken = NULL;
|
|
HANDLE ImpersonationToken = NULL;
|
|
BOOL fInsertImpersonatingUser = FALSE;
|
|
ULONG AclLength;
|
|
PACL NewDacl = NULL;
|
|
SECURITY_DESCRIPTOR SecurityDescriptor;
|
|
|
|
//
|
|
// it's possible that the current thread is impersonating a user.
|
|
// if that's the case, get it's token user, and revert to insure we
|
|
// can open the process token.
|
|
//
|
|
|
|
Status = NtOpenThreadToken(
|
|
NtCurrentThread(),
|
|
TOKEN_QUERY | TOKEN_IMPERSONATE,
|
|
TRUE,
|
|
&ImpersonationToken
|
|
);
|
|
|
|
if( NT_SUCCESS(Status) )
|
|
{
|
|
//
|
|
// stop impersonating.
|
|
//
|
|
|
|
RevertToSelf();
|
|
|
|
//
|
|
// get the token user for the impersonating user.
|
|
//
|
|
|
|
Status = KerbGetTokenUser(
|
|
ImpersonationToken,
|
|
&ImpersonationTokenUser
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open the process token to find out the user sid
|
|
//
|
|
|
|
Status = NtOpenProcessToken(
|
|
NtCurrentProcess(),
|
|
TOKEN_QUERY,
|
|
&ProcessToken
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = KerbGetTokenUser(
|
|
ProcessToken,
|
|
&ProcessTokenUser
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Now get the token user for the thread.
|
|
//
|
|
Status = KerbGetTokenUser(
|
|
Token,
|
|
&ThreadTokenUser
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
AclLength = 4 * sizeof( ACCESS_ALLOWED_ACE ) - 4 * sizeof( ULONG ) +
|
|
RtlLengthSid( ProcessTokenUser->User.Sid ) +
|
|
RtlLengthSid( ThreadTokenUser->User.Sid ) +
|
|
RtlLengthSid( KerbGlobalLocalSystemSid ) +
|
|
RtlLengthSid( KerbGlobalAliasAdminsSid ) +
|
|
sizeof( ACL );
|
|
|
|
//
|
|
// determine if we need to add impersonation token sid onto the token Dacl.
|
|
//
|
|
|
|
if( ImpersonationTokenUser &&
|
|
!RtlEqualSid( ImpersonationTokenUser->User.Sid, ProcessTokenUser->User.Sid ) &&
|
|
!RtlEqualSid( ImpersonationTokenUser->User.Sid, ThreadTokenUser->User.Sid )
|
|
)
|
|
{
|
|
AclLength += (sizeof(ACCESS_ALLOWED_ACE) - sizeof( ULONG )) +
|
|
RtlLengthSid( ImpersonationTokenUser->User.Sid );
|
|
|
|
fInsertImpersonatingUser = TRUE;
|
|
}
|
|
|
|
NewDacl = (PACL) LocalAlloc(0, AclLength );
|
|
|
|
if (NewDacl == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlCreateAcl( NewDacl, AclLength, ACL_REVISION2 );
|
|
DsysAssert(NT_SUCCESS( Status ));
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
NewDacl,
|
|
ACL_REVISION2,
|
|
TOKEN_ALL_ACCESS,
|
|
ProcessTokenUser->User.Sid
|
|
);
|
|
DsysAssert( NT_SUCCESS( Status ));
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
NewDacl,
|
|
ACL_REVISION2,
|
|
TOKEN_ALL_ACCESS,
|
|
ThreadTokenUser->User.Sid
|
|
);
|
|
DsysAssert( NT_SUCCESS( Status ));
|
|
|
|
if( fInsertImpersonatingUser )
|
|
{
|
|
Status = RtlAddAccessAllowedAce (
|
|
NewDacl,
|
|
ACL_REVISION2,
|
|
TOKEN_ALL_ACCESS,
|
|
ImpersonationTokenUser->User.Sid
|
|
);
|
|
DsysAssert( NT_SUCCESS( Status ));
|
|
}
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
NewDacl,
|
|
ACL_REVISION2,
|
|
TOKEN_ALL_ACCESS,
|
|
KerbGlobalAliasAdminsSid
|
|
);
|
|
DsysAssert( NT_SUCCESS( Status ));
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
NewDacl,
|
|
ACL_REVISION2,
|
|
TOKEN_ALL_ACCESS,
|
|
KerbGlobalLocalSystemSid
|
|
);
|
|
DsysAssert( NT_SUCCESS( Status ));
|
|
|
|
Status = RtlCreateSecurityDescriptor (
|
|
&SecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION
|
|
);
|
|
DsysAssert( NT_SUCCESS( Status ));
|
|
|
|
Status = RtlSetDaclSecurityDescriptor(
|
|
&SecurityDescriptor,
|
|
TRUE,
|
|
NewDacl,
|
|
FALSE
|
|
);
|
|
|
|
DsysAssert( NT_SUCCESS( Status ));
|
|
|
|
Status = NtSetSecurityObject(
|
|
Token,
|
|
DACL_SECURITY_INFORMATION,
|
|
&SecurityDescriptor
|
|
);
|
|
|
|
DsysAssert( NT_SUCCESS( Status ));
|
|
|
|
|
|
Cleanup:
|
|
|
|
if( ImpersonationToken != NULL ) {
|
|
|
|
//
|
|
// put the thread token back if we were impersonating.
|
|
//
|
|
|
|
SetThreadToken( NULL, ImpersonationToken );
|
|
NtClose( ImpersonationToken );
|
|
}
|
|
|
|
if (ThreadTokenUser != NULL) {
|
|
LocalFree( ThreadTokenUser );
|
|
}
|
|
|
|
if (ProcessTokenUser != NULL) {
|
|
LocalFree( ProcessTokenUser );
|
|
}
|
|
|
|
if (ImpersonationTokenUser != NULL) {
|
|
LocalFree( ImpersonationTokenUser );
|
|
}
|
|
|
|
if (NewDacl != NULL) {
|
|
LocalFree( NewDacl );
|
|
}
|
|
|
|
if (ProcessToken != NULL)
|
|
{
|
|
NtClose(ProcessToken);
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
#endif // WIN32_CHICAGO
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreateUserModeContext
|
|
//
|
|
// Synopsis: Creates a user-mode context to support impersonation and
|
|
// message integrity and privacy
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbCreateUserModeContext(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
IN PSecBuffer MarshalledContext,
|
|
OUT PKERB_CONTEXT * NewContext
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKERB_CONTEXT Context = NULL;
|
|
PKERB_PACKED_CONTEXT PackedContext ;
|
|
UNICODE_STRING String ;
|
|
KERB_ENCRYPTION_KEY Key ;
|
|
|
|
|
|
if (MarshalledContext->cbBuffer < sizeof(KERB_PACKED_CONTEXT))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Invalid buffer size for marshalled context: was 0x%x, needed 0x%x. %ws, line %d\n",
|
|
MarshalledContext->cbBuffer, sizeof(KERB_CONTEXT), THIS_FILE, __LINE__));
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
PackedContext = (PKERB_PACKED_CONTEXT) MarshalledContext->pvBuffer;
|
|
|
|
|
|
Status = KerbAllocateContext( &Context, TRUE );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Context->Lifetime = PackedContext->Lifetime ;
|
|
Context->RenewTime = PackedContext->RenewTime ;
|
|
Context->StartTime = PackedContext->StartTime;
|
|
|
|
String.Length = PackedContext->ClientName.Length ;
|
|
String.MaximumLength = PackedContext->ClientName.MaximumLength ;
|
|
String.Buffer = (PWSTR)((PUCHAR) PackedContext + PackedContext->ClientName.Buffer );
|
|
|
|
Status = KerbDuplicateString(
|
|
&Context->ClientName,
|
|
&String );
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
goto Cleanup ;
|
|
}
|
|
|
|
String.Length = PackedContext->ClientRealm.Length ;
|
|
String.MaximumLength = PackedContext->ClientRealm.MaximumLength ;
|
|
String.Buffer = (PWSTR)((PUCHAR) PackedContext + PackedContext->ClientRealm.Buffer );
|
|
|
|
Status = KerbDuplicateString(
|
|
&Context->ClientRealm,
|
|
&String );
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
goto Cleanup ;
|
|
}
|
|
|
|
Context->LogonId = PackedContext->LogonId ;
|
|
Context->TokenHandle = (HANDLE) ULongToPtr(PackedContext->TokenHandle);
|
|
Context->CredentialHandle = NULL ;
|
|
Context->Nonce = PackedContext->Nonce ;
|
|
Context->ReceiveNonce = PackedContext->ReceiveNonce ;
|
|
Context->ContextFlags = PackedContext->ContextFlags ;
|
|
Context->ContextAttributes = PackedContext->ContextAttributes ;
|
|
Context->EncryptionType = PackedContext->EncryptionType ;
|
|
|
|
Key.keytype = PackedContext->SessionKeyType ;
|
|
Key.keyvalue.value = (PUCHAR) ((PUCHAR) PackedContext + PackedContext->SessionKeyOffset );
|
|
Key.keyvalue.length = PackedContext->SessionKeyLength ;
|
|
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->SessionKey,
|
|
&Key)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
//
|
|
// Null out string buffers that aren't meant to be copied.
|
|
//
|
|
|
|
Context->ClientPrincipalName.Buffer = NULL;
|
|
Context->ServerPrincipalName.Buffer = NULL;
|
|
Context->TicketKey.keyvalue.value = NULL;
|
|
Context->UserSid = NULL;
|
|
Context->TicketCacheEntry = NULL;
|
|
|
|
|
|
//
|
|
// Modify the DACL on the token to grant access to the caller
|
|
//
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
if (Context->TokenHandle != NULL)
|
|
{
|
|
Status = KerbCreateTokenDacl(
|
|
Context->TokenHandle
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
#endif // WIN32_CHICAGO
|
|
//
|
|
// We didn't copy the ticket here, so don't store the entry
|
|
//
|
|
|
|
Context->TicketCacheEntry = NULL;
|
|
|
|
KerbInitializeListEntry(
|
|
&Context->ListEntry
|
|
);
|
|
|
|
|
|
if ( ContextHandle != 0 )
|
|
{
|
|
Context->LsaContextHandle = ContextHandle;
|
|
} else {
|
|
Context->LsaContextHandle = (ULONG_PTR)Context;
|
|
}
|
|
|
|
Context->cbMarshalledTargetInfo = PackedContext->MarshalledTargetInfoLength;
|
|
if (PackedContext->MarshalledTargetInfo)
|
|
{
|
|
Context->pbMarshalledTargetInfo = (PUCHAR) KerbAllocate( Context->cbMarshalledTargetInfo );
|
|
if (Context->pbMarshalledTargetInfo == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
RtlCopyMemory(
|
|
Context->pbMarshalledTargetInfo,
|
|
(PUCHAR) PackedContext + PackedContext->MarshalledTargetInfo,
|
|
Context->cbMarshalledTargetInfo
|
|
);
|
|
|
|
} else {
|
|
Context->pbMarshalledTargetInfo = NULL;
|
|
}
|
|
|
|
|
|
Status = KerbInsertContext(
|
|
Context
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to insert context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
*NewContext = Context;
|
|
|
|
Cleanup:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Context != NULL)
|
|
{
|
|
KerbFreeContext(Context);
|
|
}
|
|
}
|
|
return(Status);
|
|
|
|
}
|
|
|
|
#if 0
|
|
NTSTATUS
|
|
KerbCreateUserModeContext(
|
|
IN LSA_SEC_HANDLE ContextHandle,
|
|
IN PSecBuffer MarshalledContext,
|
|
OUT PKERB_CONTEXT * NewContext
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKERB_CONTEXT Context = NULL;
|
|
PKERB_CONTEXT LsaContext;
|
|
|
|
if (MarshalledContext->cbBuffer < sizeof(KERB_CONTEXT))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Invalid buffer size for marshalled context: was 0x%x, needed 0x%x. %ws, line %d\n",
|
|
MarshalledContext->cbBuffer, sizeof(KERB_CONTEXT), THIS_FILE, __LINE__));
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
LsaContext = (PKERB_CONTEXT) MarshalledContext->pvBuffer;
|
|
|
|
//
|
|
// Normalize the client name.
|
|
//
|
|
|
|
LsaContext->ClientName.Buffer = (LPWSTR) ((PUCHAR) LsaContext->ClientName.Buffer + (UINT_PTR) LsaContext);
|
|
LsaContext->ClientRealm.Buffer = (LPWSTR) ((PUCHAR) LsaContext->ClientRealm.Buffer + (UINT_PTR) LsaContext);
|
|
LsaContext->SessionKey.keyvalue.value = (PUCHAR) LsaContext->SessionKey.keyvalue.value + (UINT_PTR) LsaContext;
|
|
|
|
Status = KerbAllocateContext( &Context, TRUE );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
*Context = *LsaContext;
|
|
|
|
//
|
|
// Null out string buffers that aren't meant to be copied.
|
|
//
|
|
|
|
Context->ClientName.Buffer = NULL;
|
|
Context->ClientRealm.Buffer = NULL;
|
|
Context->SessionKey.keyvalue.value = NULL;
|
|
Context->UserSid = NULL;
|
|
Context->TicketCacheEntry = NULL;
|
|
|
|
|
|
//
|
|
// Modify the DACL on the token to grant access to the caller
|
|
//
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
if (Context->TokenHandle != NULL)
|
|
{
|
|
Status = KerbCreateTokenDacl(
|
|
Context->TokenHandle
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
#endif // WIN32_CHICAGO
|
|
//
|
|
// We didn't copy the ticket here, so don't store the entry
|
|
//
|
|
|
|
Context->TicketCacheEntry = NULL;
|
|
|
|
KerbInitializeListEntry(
|
|
&Context->ListEntry
|
|
);
|
|
|
|
Context->LsaContextHandle = ContextHandle;
|
|
|
|
Status = KerbDuplicateString(
|
|
&Context->ClientName,
|
|
&LsaContext->ClientName
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = KerbDuplicateString(
|
|
&Context->ClientRealm,
|
|
&LsaContext->ClientRealm
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->SessionKey,
|
|
&LsaContext->SessionKey)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = KerbInsertContext(
|
|
Context
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to insert context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
*NewContext = Context;
|
|
|
|
Cleanup:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Context != NULL)
|
|
{
|
|
KerbFreeContext(Context);
|
|
}
|
|
}
|
|
return(Status);
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbUpdateClientContext
|
|
//
|
|
// Synopsis: updates context with latest info
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbUpdateClientContext(
|
|
IN PKERB_CONTEXT Context,
|
|
IN OPTIONAL PKERB_TICKET_CACHE_ENTRY TicketCacheEntry,
|
|
IN ULONG Nonce,
|
|
IN PTimeStamp pAuthenticatorTime,
|
|
IN ULONG ReceiveNonce,
|
|
IN ULONG ContextFlags,
|
|
IN ULONG ContextAttributes,
|
|
IN OPTIONAL PKERB_ENCRYPTION_KEY SubSessionKey,
|
|
OUT PTimeStamp ContextLifetime
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if (ARGUMENT_PRESENT(TicketCacheEntry))
|
|
{
|
|
KerbReadLockTicketCache();
|
|
}
|
|
|
|
KerbWriteLockContexts();
|
|
|
|
if (ARGUMENT_PRESENT(TicketCacheEntry))
|
|
{
|
|
//
|
|
// Duplicate the session key into the context
|
|
//
|
|
|
|
KerbFreeKey(
|
|
&Context->SessionKey
|
|
);
|
|
|
|
if (ARGUMENT_PRESENT(SubSessionKey) && (SubSessionKey->keyvalue.value != NULL))
|
|
{
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->SessionKey,
|
|
SubSessionKey
|
|
)))
|
|
{
|
|
KerbUnlockTicketCache();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->SessionKey,
|
|
&TicketCacheEntry->SessionKey
|
|
)))
|
|
{
|
|
KerbUnlockTicketCache();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
KerbFreeKey(
|
|
&Context->TicketKey
|
|
);
|
|
|
|
if (!KERB_SUCCESS(KerbDuplicateKey(
|
|
&Context->TicketKey,
|
|
&TicketCacheEntry->SessionKey
|
|
)))
|
|
{
|
|
KerbUnlockTicketCache();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Context->Lifetime = TicketCacheEntry->EndTime;
|
|
Context->RenewTime = TicketCacheEntry->RenewUntil;
|
|
Context->EncryptionType = TicketCacheEntry->Ticket.encrypted_part.encryption_type;
|
|
KerbUnlockTicketCache();
|
|
}
|
|
else
|
|
{
|
|
|
|
Context->Lifetime = KerbGlobalWillNeverTime;
|
|
Context->RenewTime = KerbGlobalWillNeverTime;
|
|
Context->EncryptionType = KERB_ETYPE_NULL;
|
|
}
|
|
|
|
Context->Nonce = Nonce;
|
|
Context->AuthenticatorTime = *pAuthenticatorTime;
|
|
|
|
//
|
|
// If the server sent us a nonce for receiving data, use it. Otherwise use
|
|
// the nonce we generated.
|
|
//
|
|
|
|
// if (ReceiveNonce != 0)
|
|
// {
|
|
Context->ReceiveNonce = ReceiveNonce;
|
|
// }
|
|
// else
|
|
// {
|
|
// Context->ReceiveNonce = Nonce;
|
|
// }
|
|
|
|
//
|
|
// delegation flags are not additive, turn it off before updating it
|
|
//
|
|
|
|
Context->ContextFlags &= ~(ISC_RET_DELEGATE_IF_SAFE | ISC_RET_DELEGATE);
|
|
|
|
Context->ContextFlags |= ContextFlags;
|
|
|
|
|
|
Context->ContextAttributes |= KERB_CONTEXT_OUTBOUND | ContextAttributes;
|
|
|
|
*ContextLifetime = Context->Lifetime;
|
|
|
|
Cleanup:
|
|
KerbUnlockContexts();
|
|
return(Status);
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreateSKeyEntry
|
|
//
|
|
// Synopsis: Create a session key entry
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: pSessionKey - session key that is used, mostly SubKey
|
|
// pExpireTime - time that the session key expires
|
|
//
|
|
// Requires: Memory allocator must zero out memory because KerbFreeSKeyEntry
|
|
// relies on that behavior
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbCreateSKeyEntry(
|
|
IN KERB_ENCRYPTION_KEY* pSessionKey,
|
|
IN FILETIME* pExpireTime
|
|
)
|
|
{
|
|
NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;
|
|
KERB_SESSION_KEY_ENTRY* pSessionKeyEntry = NULL;
|
|
|
|
if (!pSessionKey || !pExpireTime)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
pSessionKeyEntry = (KERB_SESSION_KEY_ENTRY*) KerbAllocate(sizeof(KERB_SESSION_KEY_ENTRY));
|
|
|
|
if (!pSessionKeyEntry)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
pSessionKeyEntry->ExpireTime = *pExpireTime;
|
|
InitializeListHead(&pSessionKeyEntry->ListEntry); // so that un-linking always works
|
|
|
|
NtStatus = KerbMapKerbError(
|
|
KerbDuplicateKey(
|
|
&pSessionKeyEntry->SessionKey,
|
|
pSessionKey
|
|
));
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
if (SafeAcquireResourceExclusive(&KerbSKeyLock, TRUE))
|
|
{
|
|
#if DBG
|
|
ULONG cSKeyEntries = 0;
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
cSKeyEntries = InterlockedIncrement(&KerbcSKeyEntries);
|
|
}
|
|
|
|
DebugLog((DEB_TRACE_LOOPBACK,
|
|
"KerbInsertSKey, status 0x%x, keyexpire %#x:%#x, total %d keys\n",
|
|
NtStatus,
|
|
pSessionKeyEntry->ExpireTime.dwHighDateTime,
|
|
pSessionKeyEntry->ExpireTime.dwLowDateTime,
|
|
cSKeyEntries));
|
|
|
|
#endif
|
|
InsertHeadList(&KerbSKeyList, &pSessionKeyEntry->ListEntry);
|
|
pSessionKeyEntry = NULL;
|
|
SafeReleaseResource(&KerbSKeyLock);
|
|
}
|
|
else
|
|
{
|
|
NtStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
if (pSessionKeyEntry)
|
|
{
|
|
KerbFreeSKeyEntry(pSessionKeyEntry);
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbDoesSKeyExist
|
|
//
|
|
// Synopsis: check whether a session key entry exists the session key list
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: pKey - key to be located
|
|
// pbExist - whether session key entry exists
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: If an entry is found, ppSKeyEntry points to the entry
|
|
// that contains pKey hence should be non null
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbDoesSKeyExist(
|
|
IN KERB_ENCRYPTION_KEY* pKey,
|
|
OUT BOOLEAN* pbExist
|
|
)
|
|
{
|
|
FILETIME CurrentTime = {0};
|
|
NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
if (!pKey || !pbExist)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
*pbExist = FALSE;
|
|
|
|
GetSystemTimeAsFileTime(&CurrentTime);
|
|
|
|
DebugLog((DEB_TRACE_LOOPBACK, "KerbDoesSKeyExist, curtime %#x:%#x\n", CurrentTime.dwHighDateTime, CurrentTime.dwLowDateTime));
|
|
|
|
if (SafeAcquireResourceShared(&KerbSKeyLock, TRUE))
|
|
{
|
|
NtStatus = STATUS_SUCCESS;
|
|
|
|
for (LIST_ENTRY* pListEntry = KerbSKeyList.Flink;
|
|
NT_SUCCESS(NtStatus) && pListEntry != &KerbSKeyList;
|
|
pListEntry = pListEntry->Flink)
|
|
{
|
|
KERB_SESSION_KEY_ENTRY* pSKeyEntry = CONTAINING_RECORD(pListEntry, KERB_SESSION_KEY_ENTRY, ListEntry);
|
|
|
|
//
|
|
// only keys that have not expired are checked
|
|
//
|
|
|
|
if (KerbGetTime(* (TimeStamp*)&pSKeyEntry->ExpireTime) > KerbGetTime(* (TimeStamp*)&CurrentTime))
|
|
{
|
|
BOOLEAN bEqual = FALSE;
|
|
|
|
NtStatus = KerbEqualKey(&pSKeyEntry->SessionKey, pKey, &bEqual);
|
|
|
|
if (NT_SUCCESS(NtStatus) && bEqual)
|
|
{
|
|
//
|
|
// found it
|
|
//
|
|
|
|
DebugLog((DEB_TRACE_LOOPBACK, "KerbDoesSKeyExist, keyexpire %#x:%#x found\n", pSKeyEntry->ExpireTime.dwHighDateTime, pSKeyEntry->ExpireTime.dwLowDateTime));
|
|
|
|
*pbExist = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SafeReleaseResource(&KerbSKeyLock);
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbDeleteSKeyEntry
|
|
//
|
|
// Synopsis: remove a session key entry if it exists
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: pKey - key to be delete
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbDeleteSKeyEntry(
|
|
IN OPTIONAL KERB_ENCRYPTION_KEY* pKey
|
|
)
|
|
{
|
|
if (pKey)
|
|
{
|
|
if (SafeAcquireResourceExclusive(&KerbSKeyLock, TRUE))
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
|
|
for (LIST_ENTRY* pListEntry = KerbSKeyList.Flink;
|
|
NT_SUCCESS(NtStatus) && pListEntry != &KerbSKeyList;
|
|
pListEntry = pListEntry->Flink)
|
|
{
|
|
BOOLEAN bEqual = FALSE;
|
|
|
|
KERB_SESSION_KEY_ENTRY* pSKeyEntry = CONTAINING_RECORD(pListEntry, KERB_SESSION_KEY_ENTRY, ListEntry);
|
|
|
|
NtStatus = KerbEqualKey(&pSKeyEntry->SessionKey, pKey, &bEqual);
|
|
|
|
if (NT_SUCCESS(NtStatus) && bEqual)
|
|
{
|
|
KerbFreeSKeyEntry(pSKeyEntry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
SafeReleaseResource(&KerbSKeyLock);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+-----------------------------------------------------------------------
|
|
//
|
|
// Function: KerbEqualKey
|
|
//
|
|
// Synopsis: Compare two keys
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: pKeyFoo - one key
|
|
// pKeyBar - the other key
|
|
//
|
|
// Returns: NTSTATUS
|
|
//
|
|
// History:
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbEqualKey(
|
|
IN KERB_ENCRYPTION_KEY* pKeyFoo,
|
|
IN KERB_ENCRYPTION_KEY* pKeyBar,
|
|
OUT BOOLEAN* pbEqual
|
|
)
|
|
{
|
|
if (!pKeyFoo || !pKeyBar || !pbEqual)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
*pbEqual = (pKeyFoo->keytype == pKeyBar->keytype) &&
|
|
(pKeyFoo->keyvalue.length == pKeyBar->keyvalue.length) &&
|
|
(pKeyFoo->keyvalue.length == RtlCompareMemory(pKeyFoo->keyvalue.value, pKeyBar->keyvalue.value, pKeyFoo->keyvalue.length));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbTrimSKeyList
|
|
//
|
|
// Synopsis: Release session key entries that have expired
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbTrimSKeyList(
|
|
VOID
|
|
)
|
|
{
|
|
FILETIME CurrentTime = {0};
|
|
BOOLEAN bCleanUpNeeded = FALSE;
|
|
|
|
GetSystemTimeAsFileTime(&CurrentTime);
|
|
|
|
DebugLog((DEB_TRACE_LOOPBACK, "KerbTrimSKeyList, curtime %#x:%#x\n", CurrentTime.dwHighDateTime, CurrentTime.dwLowDateTime));
|
|
|
|
if (SafeAcquireResourceShared(&KerbSKeyLock, TRUE))
|
|
{
|
|
for (LIST_ENTRY* pListEntry = KerbSKeyList.Flink;
|
|
pListEntry != &KerbSKeyList;
|
|
pListEntry = pListEntry->Flink)
|
|
{
|
|
KERB_SESSION_KEY_ENTRY* pSKeyEntry = CONTAINING_RECORD(pListEntry, KERB_SESSION_KEY_ENTRY, ListEntry);
|
|
|
|
if (KerbGetTime(* (TimeStamp*)&pSKeyEntry->ExpireTime) <= KerbGetTime(* (TimeStamp*)&CurrentTime))
|
|
{
|
|
bCleanUpNeeded = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bCleanUpNeeded)
|
|
{
|
|
SafeConvertSharedToExclusive(&KerbSKeyLock);
|
|
|
|
for (LIST_ENTRY* pListEntry = KerbSKeyList.Flink;
|
|
pListEntry != &KerbSKeyList;
|
|
/* updating pListEntry inside the loop */)
|
|
{
|
|
KERB_SESSION_KEY_ENTRY* pSKeyEntry = CONTAINING_RECORD(pListEntry, KERB_SESSION_KEY_ENTRY, ListEntry);
|
|
|
|
//
|
|
// Update next link before pListEntry is deleted
|
|
//
|
|
|
|
pListEntry = pListEntry->Flink;
|
|
|
|
//
|
|
// only delete keys that expired
|
|
//
|
|
|
|
if (KerbGetTime(* (TimeStamp*)&pSKeyEntry->ExpireTime) <= KerbGetTime(* (TimeStamp*)&CurrentTime))
|
|
{
|
|
KerbFreeSKeyEntry(pSKeyEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
SafeReleaseResource(&KerbSKeyLock);
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbSKeyListCleanupCallback
|
|
//
|
|
// Synopsis: Clean up network service session key list
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: pContext - the context parameter, see RtlCreateTimer
|
|
// bTimeOut - whether a timeout has occurred
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbSKeyListCleanupCallback(
|
|
IN VOID* pContext,
|
|
IN BOOLEAN bTimeOut
|
|
)
|
|
{
|
|
DebugLog((DEB_TRACE_LOOPBACK, "KerbSKeyListCleanupCallback is called\n"));
|
|
|
|
KerbTrimSKeyList();
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreeSKeyEntry
|
|
//
|
|
// Synopsis: Free a session key entry
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: pSKeyEntry - session key entry to free
|
|
//
|
|
// Requires: pSKeyEntry->ListEntry must have been initialized properly
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbFreeSKeyEntry(
|
|
IN KERB_SESSION_KEY_ENTRY* pSKeyEntry
|
|
)
|
|
{
|
|
if (pSKeyEntry)
|
|
{
|
|
if (!IsListEmpty(&pSKeyEntry->ListEntry)) // make sure this entry has been linked
|
|
{
|
|
RemoveEntryList(&pSKeyEntry->ListEntry);
|
|
|
|
#if DBG
|
|
LONG cSKeyEntries = 0;
|
|
|
|
cSKeyEntries = InterlockedDecrement(&KerbcSKeyEntries);
|
|
|
|
DebugLog((DEB_TRACE_LOOPBACK, "KerbFreeSKeyEntry, keyexpire %#x:%#x, %d keys left\n",
|
|
pSKeyEntry->ExpireTime.dwHighDateTime,
|
|
pSKeyEntry->ExpireTime.dwLowDateTime,
|
|
cSKeyEntries));
|
|
#endif
|
|
}
|
|
|
|
KerbFreeKey(&pSKeyEntry->SessionKey);
|
|
|
|
KerbFree(pSKeyEntry);
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreateSKeyTimer
|
|
//
|
|
// Synopsis: Create a timer and set the callback to clean up the session
|
|
// key list
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbCreateSKeyTimer(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS NtStatus;
|
|
HANDLE hTimer = NULL;
|
|
|
|
KerbhSKeyTimerQueue = NULL;
|
|
|
|
NtStatus = RtlCreateTimerQueue(&KerbhSKeyTimerQueue);
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
NtStatus = RtlCreateTimer(
|
|
KerbhSKeyTimerQueue,
|
|
&hTimer,
|
|
KerbSKeyListCleanupCallback,
|
|
NULL, // no context
|
|
KERB_DEFAULT_SKEWTIME * 60 * 1000, // 5 min
|
|
KERB_DEFAULT_SKEWTIME * KERB_SKLIST_CALLBACK_FEQ * 60 * 1000, // 50 min
|
|
0
|
|
);
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreeSKeyTimer
|
|
//
|
|
// Synopsis: Free network service key list timer queue
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbFreeSKeyTimer(
|
|
VOID
|
|
)
|
|
{
|
|
if (KerbhSKeyTimerQueue)
|
|
{
|
|
RtlDeleteTimerQueue(KerbhSKeyTimerQueue);
|
|
KerbhSKeyTimerQueue = NULL;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
HandleToListIndex(
|
|
ULONG_PTR ContextHandle
|
|
)
|
|
{
|
|
ASSERT( (KERB_USERLIST_COUNT != 0) );
|
|
ASSERT( (KERB_USERLIST_COUNT & 1) == 0 );
|
|
|
|
ULONG Number ;
|
|
ULONG Hash;
|
|
ULONG HashFinal;
|
|
|
|
Number = (ULONG)ContextHandle;
|
|
|
|
Hash = Number;
|
|
Hash += Number >> 8;
|
|
Hash += Number >> 16;
|
|
Hash += Number >> 24;
|
|
|
|
HashFinal = Hash;
|
|
HashFinal += Hash >> 4;
|
|
|
|
//
|
|
// insure power of two if not one.
|
|
//
|
|
|
|
return ( HashFinal & (KERB_USERLIST_COUNT-1) ) ;
|
|
}
|
|
|
|
|