windows-nt/Source/XPSP1/NT/ds/security/base/lsa/server/bndcache.cxx
2020-09-26 16:20:57 +08:00

775 lines
16 KiB
C++

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1996
//
// File: bndcache.cxx
//
// Contents: Binding cache for Kerberos Package
//
//
// History: 13-August-1996 Created MikeSw
//
//------------------------------------------------------------------------
#include <lsapch.hxx>
#define BNDCACHE_ALLOCATE
#include <bndcache.h>
//+-------------------------------------------------------------------------
//
// Function: LsapInitializeList
//
// Synopsis: Initializes a lsap list by initializing the lock
// and the list entry.
//
// Effects:
//
// Arguments: List - List to initialize
//
// Requires:
//
// Returns: STATUS_SUCCESS on success or errors from
// RtlInitializeResources
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
LsapInitializeList(
IN PLSAP_LIST List
)
{
NTSTATUS Status = STATUS_SUCCESS;
InitializeListHead(&List->List);
Status = RtlInitializeCriticalSection(
&List->Lock
);
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: LsapFreeList
//
// Synopsis: Frees a lsap list by deleting the associated
// critical section.
//
// Effects: List - the list to free.
//
// Arguments:
//
// Requires:
//
// Returns: none
//
// Notes: The list must be empty before freeing it.
//
//
//--------------------------------------------------------------------------
VOID
LsapFreeList(
IN PLSAP_LIST List
)
{
//
// Make sure the list is empty first
//
DsysAssert(List->List.Flink == List->List.Blink);
RtlDeleteCriticalSection(&List->Lock);
}
//+-------------------------------------------------------------------------
//
// Function: LsapInitializeListEntry
//
// Synopsis: Initializes a newly created list entry for later
// insertion onto the list.
//
// Effects: The reference count is set to one and the links are set
// to NULL.
//
// Arguments: ListEntry - the list entry to initialize
//
// Requires:
//
// Returns: none
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
LsapInitializeListEntry(
IN OUT PLSAP_LIST_ENTRY ListEntry
)
{
ListEntry->ReferenceCount = 1;
ListEntry->Next.Flink = ListEntry->Next.Blink = NULL;
}
//+-------------------------------------------------------------------------
//
// Function: LsapInsertListEntry
//
// Synopsis: Inserts an entry into a lsap list
//
// Effects: increments the reference count on the entry - if the
// list entry was formly referenced it remains referenced.
//
// Arguments: ListEntry - the entry to insert
// List - the list in which to insert the ListEntry
//
// Requires:
//
// Returns: nothing
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
LsapInsertListEntry(
IN PLSAP_LIST_ENTRY ListEntry,
IN PLSAP_LIST List
)
{
ListEntry->ReferenceCount++;
RtlEnterCriticalSection(&List->Lock);
InsertHeadList(
&List->List,
&ListEntry->Next
);
RtlLeaveCriticalSection(&List->Lock);
}
//+-------------------------------------------------------------------------
//
// Function: LsapReferenceListEntry
//
// Synopsis: References a list entry. If the flag RemoveFromList
// has been specified, the entry is unlinked from the
// list.
//
// Effects: bumps the reference count on the entry (unless it is
// being removed from the list)
//
// Arguments:
//
// Requires: The list must be locked when calling this routine
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
LsapReferenceListEntry(
IN PLSAP_LIST List,
IN PLSAP_LIST_ENTRY ListEntry,
IN BOOLEAN RemoveFromList
)
{
//
// If it has already been removed from the list
// don't do it again.
//
if (RemoveFromList && ((ListEntry->Next.Flink != NULL) &&
(ListEntry->Next.Blink != NULL)))
{
RemoveEntryList(&ListEntry->Next);
ListEntry->Next.Flink = NULL;
ListEntry->Next.Blink = NULL;
}
else
{
ListEntry->ReferenceCount++;
}
}
//+-------------------------------------------------------------------------
//
// Function: LsapDereferenceListEntry
//
// Synopsis: Dereferences a list entry and returns a flag indicating
// whether the entry should be freed.
//
// Effects: decrements reference count on list entry
//
// Arguments: ListEntry - the list entry to dereference
// List - the list containing the list entry
//
// Requires:
//
// Returns: TRUE - the list entry should be freed
// FALSE - the list entry is still referenced
//
// Notes:
//
//
//--------------------------------------------------------------------------
BOOLEAN
LsapDereferenceListEntry(
IN PLSAP_LIST_ENTRY ListEntry,
IN PLSAP_LIST List
)
{
BOOLEAN DeleteEntry = FALSE;
RtlEnterCriticalSection(&List->Lock);
ListEntry->ReferenceCount -= 1;
if (ListEntry->ReferenceCount == 0)
{
DeleteEntry = TRUE;
}
RtlLeaveCriticalSection(&List->Lock);
return(DeleteEntry);
}
//+-------------------------------------------------------------------------
//
// Function: LsapInitBindingCache
//
// Synopsis: Initializes the binding cache
//
// Effects: allocates a resources
//
// Arguments: none
//
// Requires:
//
// Returns: STATUS_SUCCESS on success, other error codes
// on failure
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
LsapInitBindingCache(
VOID
)
{
NTSTATUS Status;
Status = LsapInitializeList( &LsapBindingCache );
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
LsapBindingCacheInitialized = TRUE;
Cleanup:
if (!NT_SUCCESS(Status))
{
LsapFreeList( &LsapBindingCache );
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: LsapCleanupBindingCache
//
// Synopsis: Frees the binding cache
//
// Effects:
//
// Arguments: none
//
// Requires:
//
// Returns: none
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
LsapCleanupBindingCache(
VOID
)
{
PLSAP_BINDING_CACHE_ENTRY CacheEntry;
if (LsapBindingCacheInitialized)
{
LsapLockList(&LsapBindingCache);
//
// Go through the list of bindings and dereference them all
//
while (!IsListEmpty(&LsapBindingCache.List))
{
CacheEntry = CONTAINING_RECORD(
LsapBindingCache.List.Flink,
LSAP_BINDING_CACHE_ENTRY,
ListEntry.Next
);
LsapReferenceListEntry(
&LsapBindingCache,
&CacheEntry->ListEntry,
TRUE
);
LsapDereferenceBindingCacheEntry(CacheEntry);
}
LsapFreeList(&LsapBindingCache);
}
}
//+-------------------------------------------------------------------------
//
// Function: LsapDereferenceBindingCacheEntry
//
// Synopsis: Dereferences a binding cache entry
//
// Effects: Dereferences the binding cache entry to make it go away
// when it is no longer being used.
//
// Arguments: decrements reference count and delets cache entry if it goes
// to zero
//
// Requires: BindingCacheEntry - The binding cache entry to dereference.
//
// Returns: none
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
LsapDereferenceBindingCacheEntry(
IN PLSAP_BINDING_CACHE_ENTRY BindingCacheEntry
)
{
if (LsapDereferenceListEntry(
&BindingCacheEntry->ListEntry,
&LsapBindingCache
) )
{
LsapFreeBindingCacheEntry(BindingCacheEntry);
}
}
//+-------------------------------------------------------------------------
//
// Function: LsapReferenceBindingCacheEntry
//
// Synopsis: References a binding cache entry
//
// Effects: Increments the reference count on the binding cache entry
//
// Arguments: BindingCacheEntry - binding cache entry to reference
//
// Requires: The binding cache must be locked
//
// Returns: none
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
LsapReferenceBindingCacheEntry(
IN PLSAP_BINDING_CACHE_ENTRY BindingCacheEntry,
IN BOOLEAN RemoveFromList
)
{
LsapLockList(&LsapBindingCache);
LsapReferenceListEntry(
&LsapBindingCache,
&BindingCacheEntry->ListEntry,
RemoveFromList
);
LsapUnlockList(&LsapBindingCache);
}
//+-------------------------------------------------------------------------
//
// Function: LsapLocateBindingCacheEntry
//
// Synopsis: References a binding cache entry by name
//
// Effects: Increments the reference count on the binding cache entry
//
// Arguments: RealmName - Contains the name of the realm for which to
// obtain a binding handle.
// RemoveFromList - Remove cache entry from cache when found.
//
// Requires:
//
// Returns: The referenced cache entry or NULL if it was not found.
//
// Notes: If an invalid entry is found it may be dereferenced
//
//
//--------------------------------------------------------------------------
PLSAP_BINDING_CACHE_ENTRY
LsapLocateBindingCacheEntry(
IN PUNICODE_STRING RealmName,
IN BOOLEAN RemoveFromList
)
{
PLIST_ENTRY ListEntry;
PLSAP_BINDING_CACHE_ENTRY CacheEntry = NULL;
BOOLEAN Found = FALSE;
LsapLockList(&LsapBindingCache);
//
// Go through the binding cache looking for the correct entry
//
for (ListEntry = LsapBindingCache.List.Flink ;
ListEntry != &LsapBindingCache.List ;
ListEntry = ListEntry->Flink )
{
CacheEntry = CONTAINING_RECORD(ListEntry, LSAP_BINDING_CACHE_ENTRY, ListEntry.Next);
if (RtlEqualUnicodeString(
&CacheEntry->RealmName,
RealmName,
TRUE
))
{
LsapReferenceBindingCacheEntry(
CacheEntry,
RemoveFromList
);
Found = TRUE;
NtQuerySystemTime(
&CacheEntry->LastUsed
);
break;
}
}
if (!Found)
{
CacheEntry = NULL;
}
LsapUnlockList(&LsapBindingCache);
return(CacheEntry);
}
//+-------------------------------------------------------------------------
//
// Function: LsapFreeBindingCacheEntry
//
// Synopsis: Frees memory associated with a binding cache entry
//
// Effects:
//
// Arguments: BindingCacheEntry - The cache entry to free. It must be
// unlinked.
//
// Requires:
//
// Returns: none
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
LsapFreeBindingCacheEntry(
IN PLSAP_BINDING_CACHE_ENTRY BindingCacheEntry
)
{
if ( !BindingCacheEntry )
{
return;
}
LsapFreeString(&BindingCacheEntry->RealmName);
if (BindingCacheEntry->PolicyHandle != NULL)
{
LsaClose(BindingCacheEntry->PolicyHandle);
}
if (BindingCacheEntry->ServerName != NULL) {
//
// Note -- because I_NetLogonAuthData is not supported for NT4 and
// below, ServerName won't always be allocated from NetLogon's MM.
// So, the ServerName allocation is normalized to LocalAlloc/LocalFree
//
LocalFree(BindingCacheEntry->ServerName);
}
if (BindingCacheEntry->ServerPrincipalName != NULL) {
I_NetLogonFree(BindingCacheEntry->ServerPrincipalName);
}
if (BindingCacheEntry->ClientContext != NULL) {
I_NetLogonFree(BindingCacheEntry->ClientContext);
}
LsapFreeLsaHeap(BindingCacheEntry);
}
//+-------------------------------------------------------------------------
//
// Function: LsapInsertBinding
//
// Synopsis: Inserts a binding into the binding cache
//
// Effects: bumps reference count on binding
//
// Arguments: CacheEntry - Cache entry to insert
//
// Requires:
//
// Returns: STATUS_SUCCESS always
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
LsapInsertBinding(
IN PLSAP_BINDING_CACHE_ENTRY CacheEntry
)
{
LsapInsertListEntry(
&CacheEntry->ListEntry,
&LsapBindingCache
);
return(STATUS_SUCCESS);
}
//+-------------------------------------------------------------------------
//
// Function: LsapCacheBinding
//
// Synopsis: Caches a binding in the binding cache
//
// Effects: creates a cache entry.
//
// Arguments: RealmName - The realm name of the LSA the binding is to.
// Handle - LSA policy handle to the target machine.
// ServerName,ServerPrincipalName, ClientContext - authenticated
// rpc parameters needed to be cached for the duration
// of the binding.
// CacheEntry - Receives the new binding cache entry,
// referenced.
//
// Requires:
//
// Returns:
//
// Notes: Locks the binding cache for write access while adding
// the cache entry.
//
//
//--------------------------------------------------------------------------
NTSTATUS
LsapCacheBinding(
IN PUNICODE_STRING RealmName,
IN PLSA_HANDLE Handle,
IN OUT LPWSTR * ServerName,
IN OUT LPWSTR * ServerPrincipalName,
IN OUT PVOID * ClientContext,
OUT PLSAP_BINDING_CACHE_ENTRY * NewCacheEntry
)
{
PLSAP_BINDING_CACHE_ENTRY CacheEntry = NULL;
PLSAP_BINDING_CACHE_ENTRY OldCacheEntry = NULL;
NTSTATUS Status = STATUS_SUCCESS;
*NewCacheEntry = NULL;
CacheEntry = (PLSAP_BINDING_CACHE_ENTRY)
LsapAllocateLsaHeap(sizeof(LSAP_BINDING_CACHE_ENTRY));
if (CacheEntry == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlZeroMemory(
CacheEntry,
sizeof(LSAP_BINDING_CACHE_ENTRY)
);
LsapInitializeListEntry(
&CacheEntry->ListEntry
);
NtQuerySystemTime(
&CacheEntry->LastUsed
);
Status = LsapDuplicateString(
&CacheEntry->RealmName,
RealmName
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
CacheEntry->PolicyHandle = *Handle;
*Handle = NULL;
CacheEntry->ServerName = *ServerName;
*ServerName = NULL;
CacheEntry->ServerPrincipalName = *ServerPrincipalName;
*ServerPrincipalName = NULL;
CacheEntry->ClientContext = *ClientContext;
*ClientContext = NULL;
//
// Before we insert this binding we want to remove any
// previous instances of bindings to the same realm.
//
OldCacheEntry = LsapLocateBindingCacheEntry(
RealmName,
TRUE // remove from cache
);
if (OldCacheEntry != NULL)
{
LsapDereferenceBindingCacheEntry( OldCacheEntry );
}
//
// Insert the cache entry into the cache
//
Status = LsapInsertBinding(
CacheEntry
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
*NewCacheEntry = CacheEntry;
Cleanup:
if (!NT_SUCCESS(Status))
{
if ( CacheEntry )
{
LsapFreeBindingCacheEntry(CacheEntry);
}
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: LsapRemoveBindingCacheEntry
//
// Synopsis: removes an entry from the binding cache
//
// Effects:
//
// Arguments: CacheEntry - entry to remove
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
LsapRemoveBindingCacheEntry(
IN PLSAP_BINDING_CACHE_ENTRY CacheEntry
)
{
LsapLockList(&LsapBindingCache);
LsapReferenceBindingCacheEntry(
CacheEntry,
TRUE
);
LsapDereferenceBindingCacheEntry(
CacheEntry
);
LsapUnlockList(&LsapBindingCache);
}