windows-nt/Source/XPSP1/NT/ds/security/protocols/kerberos/client2/bndcache.cxx
2020-09-26 16:20:57 +08:00

622 lines
15 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 <kerb.hxx>
#define BNDCACHE_ALLOCATE
#include <kerbp.h>
//+-------------------------------------------------------------------------
//
// Function: KerbInitBindingCache
//
// Synopsis: Initializes the binding cache
//
// Effects: allocates a resources
//
// Arguments: none
//
// Requires:
//
// Returns: STATUS_SUCCESS on success, other error codes on failure
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbInitBindingCache(
VOID
)
{
NTSTATUS Status;
Status = KerbInitializeList( &KerbBindingCache );
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
KerberosBindingCacheInitialized = TRUE;
Cleanup:
if (!NT_SUCCESS(Status))
{
KerbFreeList( &KerbBindingCache );
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbCleanupBindingCache
//
// Synopsis: Frees the binding cache
//
// Effects:
//
// Arguments: none
//
// Requires:
//
// Returns: none
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
KerbCleanupBindingCache(
BOOLEAN FreeList
)
{
PKERB_BINDING_CACHE_ENTRY CacheEntry;
if (KerberosBindingCacheInitialized)
{
KerbLockList(&KerbBindingCache);
//
// Go through the list of bindings and dereference them all
//
while (!IsListEmpty(&KerbBindingCache.List))
{
CacheEntry = CONTAINING_RECORD(
KerbBindingCache.List.Flink,
KERB_BINDING_CACHE_ENTRY,
ListEntry.Next
);
DsysAssert( CacheEntry != NULL );
KerbReferenceListEntry(
&KerbBindingCache,
&CacheEntry->ListEntry,
TRUE
);
KerbDereferenceBindingCacheEntry(CacheEntry);
}
//
// If we want to free the list, orphan the lock, and free the list
// otherwise, proceed on w/ the "fresh" cache.
//
if ( FreeList )
{
KerbFreeList(&KerbBindingCache);
}
else
{
KerbUnlockList(&KerbBindingCache);
}
}
}
//+-------------------------------------------------------------------------
//
// Function: KerbDereferenceBindingCacheEntry
//
// 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
KerbDereferenceBindingCacheEntry(
IN PKERB_BINDING_CACHE_ENTRY BindingCacheEntry
)
{
if (KerbDereferenceListEntry(
&BindingCacheEntry->ListEntry,
&KerbBindingCache
) )
{
KerbFreeBindingCacheEntry(BindingCacheEntry);
}
}
//+-------------------------------------------------------------------------
//
// Function: KerbReferenceBindingCacheEntry
//
// 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
KerbReferenceBindingCacheEntry(
IN PKERB_BINDING_CACHE_ENTRY BindingCacheEntry,
IN BOOLEAN RemoveFromList
)
{
KerbLockList(&KerbBindingCache);
KerbReferenceListEntry(
&KerbBindingCache,
&BindingCacheEntry->ListEntry,
RemoveFromList
);
KerbUnlockList(&KerbBindingCache);
}
//+-------------------------------------------------------------------------
//
// Function: KerbLocateBindingCacheEntry
//
// 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.
// DesiredFlags - Flags desired for binding, such as PDC required
// 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
//
//
//--------------------------------------------------------------------------
PKERB_BINDING_CACHE_ENTRY
KerbLocateBindingCacheEntry(
IN PUNICODE_STRING RealmName,
IN ULONG DesiredFlags,
IN BOOLEAN RemoveFromList
)
{
PLIST_ENTRY ListEntry;
PKERB_BINDING_CACHE_ENTRY CacheEntry = NULL;
BOOLEAN Found = FALSE;
if (DesiredFlags == 0)
{
DesiredFlags = KERB_NO_DC_FLAGS;
}
KerbLockList(&KerbBindingCache);
//
// Go through the binding cache looking for the correct entry
//
for (ListEntry = KerbBindingCache.List.Flink ;
ListEntry != &KerbBindingCache.List ;
ListEntry = ListEntry->Flink )
{
CacheEntry = CONTAINING_RECORD(ListEntry, KERB_BINDING_CACHE_ENTRY, ListEntry.Next);
DsysAssert( CacheEntry != NULL );
if ( RtlEqualUnicodeString( &CacheEntry->RealmName, RealmName,TRUE ) &&
((DesiredFlags & CacheEntry->Flags) == DesiredFlags))
{
Found = TRUE;
//
// Check to see if we should stop using this entry
if (!RemoveFromList)
{
TimeStamp CurrentTime, Timeout;
GetSystemTimeAsFileTime((PFILETIME) &CurrentTime );
if ((CacheEntry->DcFlags & DS_CLOSEST_FLAG) == 0)
{
Timeout = KerbGlobalFarKdcTimeout;
}
else
{
Timeout = KerbGlobalNearKdcTimeout;
}
if (KerbGetTime(CacheEntry->DiscoveryTime) + KerbGetTime(Timeout) < KerbGetTime(CurrentTime))
{
//
// This entry has timed out - it is not close by and we
// don't want to use it for too long, or its time to check
// for a close DC again.
//
// Note: This will have the sideeffect of checking for a new PDC
//
D_DebugLog((DEB_TRACE,
"Purging KDC cache entry Realm: %wZ, Addr: %wZ, DcFlags %x\n",
&CacheEntry->RealmName,
&CacheEntry->KdcAddress,
CacheEntry->DcFlags
));
RemoveFromList = TRUE;
Found = FALSE;
}
else
{
D_DebugLog((DEB_TRACE,
"**Using** KDC cache entry Realm: %wZ, Addr: %wZ, DcFlags %x\n",
&CacheEntry->RealmName,
&CacheEntry->KdcAddress,
CacheEntry->DcFlags
));
#ifdef CACHE_TRACE
if ((CacheEntry->DcFlags & DS_CLOSEST_FLAG) == DS_CLOSEST_FLAG)
{
DebugLog((DEB_ERROR, "CLOSE DC "));
}
else
{
DebugLog((DEB_ERROR, "FAR DC "));
}
if ((CacheEntry->DcFlags & DS_PDC_FLAG) == DS_PDC_FLAG)
{
DebugLog((DEB_ERROR, "-- ** PDC **\n"));
}
else
{
DebugLog((DEB_ERROR, "-- BDC\n"));
}
#endif
}
}
KerbReferenceBindingCacheEntry(
CacheEntry,
RemoveFromList
);
//
// If we aren't returning this, dereference it now
//
if (!Found)
{
KerbDereferenceBindingCacheEntry( CacheEntry );
}
break;
}
}
if (!Found)
{
CacheEntry = NULL;
}
KerbUnlockList(&KerbBindingCache);
return(CacheEntry);
}
//+-------------------------------------------------------------------------
//
// Function: KerbFreeBindingCacheEntry
//
// 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
KerbFreeBindingCacheEntry(
IN PKERB_BINDING_CACHE_ENTRY BindingCacheEntry
)
{
KerbFreeString(&BindingCacheEntry->RealmName);
KerbFreeString(&BindingCacheEntry->KdcAddress);
KerbFree(BindingCacheEntry);
}
//+-------------------------------------------------------------------------
//
// Function: KerbInsertBinding
//
// 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
KerbInsertBinding(
IN PKERB_BINDING_CACHE_ENTRY CacheEntry
)
{
KerbInsertListEntry(
&CacheEntry->ListEntry,
&KerbBindingCache
);
return(STATUS_SUCCESS);
}
//+-------------------------------------------------------------------------
//
// Function: KerbCacheBinding
//
// Synopsis: Caches a binding in the binding cache
//
// Effects: creates a cache entry.
//
// Arguments: RealmName - The realm name of the KDC the binding is to.
// KdcAddress - address of the KDC
// AddressType - Type of address, from DsGetDCName flags
// Flags - These were the desired flags that we asked for
// DcFlags - These are the flags the dc has
// CacheFlags - Special meaning so we don't use the locator bits
// CacheEntry - Receives the new binding cache entry, referenced
//
// Requires:
//
// Returns: STATUS_SUCCESS on success, other error codes on failure
//
// Notes: Locks the binding cache for write access while adding
// the cache entry. Removes a cache entry for the same domain
// before adding this one.
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbCacheBinding(
IN PUNICODE_STRING RealmName,
IN PUNICODE_STRING KdcAddress,
IN ULONG AddressType,
IN ULONG Flags,
IN ULONG DcFlags,
IN ULONG CacheFlags,
OUT PKERB_BINDING_CACHE_ENTRY * NewCacheEntry
)
{
PKERB_BINDING_CACHE_ENTRY CacheEntry = NULL;
PKERB_BINDING_CACHE_ENTRY OldCacheEntry = NULL;
NTSTATUS Status = STATUS_SUCCESS;
ULONG DesiredFlags = KERB_NO_DC_FLAGS;
D_DebugLog((DEB_TRACE,
"Adding Binding Cache Entry - %wZ : %wZ, DcFlags %x CacheFlags %x\n",
RealmName,
KdcAddress,
DcFlags,
CacheFlags
));
Flags &= ~DS_FORCE_REDISCOVERY; //not a valid flag
//
// If we requested a PDC, and this is a PDC, then cache it
// as a PDC. Otherwise, we just got lucky, and we'll use
// the PDC naturally.
//
if ((Flags == DS_PDC_REQUIRED) && ((DcFlags & DS_PDC_FLAG) == DS_PDC_FLAG))
{
D_DebugLog((DEB_TRACE, "Caching as PDC\n"));
DesiredFlags = DS_PDC_REQUIRED;
}
else
{
D_DebugLog((DEB_TRACE, "Caching as BDC\n"));
Flags &= ~DS_PDC_REQUIRED; // clear the flag.
DcFlags &= ~DS_PDC_FLAG;
}
*NewCacheEntry = NULL;
CacheEntry = (PKERB_BINDING_CACHE_ENTRY)
KerbAllocate(sizeof(KERB_BINDING_CACHE_ENTRY));
if (CacheEntry == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
KerbInitializeListEntry(
&CacheEntry->ListEntry
);
GetSystemTimeAsFileTime((PFILETIME)
&CacheEntry->DiscoveryTime
);
Status = KerbDuplicateString(
&CacheEntry->RealmName,
RealmName
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Status = KerbDuplicateString(
&CacheEntry->KdcAddress,
KdcAddress
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
CacheEntry->AddressType = AddressType;
CacheEntry->Flags = ((Flags == 0) ? KERB_NO_DC_FLAGS : Flags);
CacheEntry->DcFlags = DcFlags;
CacheEntry->CacheFlags = CacheFlags;
//
// Before we insert this binding we want to remove any
// previous instances of bindings to the same realm.
//
OldCacheEntry = KerbLocateBindingCacheEntry(
RealmName,
DesiredFlags, // only hammer on PDC entries
TRUE // remove from cache
);
if (OldCacheEntry != NULL)
{
KerbDereferenceBindingCacheEntry( OldCacheEntry );
}
//
// Insert the cache entry into the cache
//
Status = KerbInsertBinding(
CacheEntry
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
*NewCacheEntry = CacheEntry;
Cleanup:
if (!NT_SUCCESS(Status))
{
if (NULL != CacheEntry)
{
KerbFreeBindingCacheEntry(CacheEntry);
}
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbRemoveBindingCacheEntry
//
// Synopsis: removes an entry from the binding cache
//
// Effects:
//
// Arguments: CacheEntry - entry to remove
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
KerbRemoveBindingCacheEntry(
IN PKERB_BINDING_CACHE_ENTRY CacheEntry
)
{
KerbLockList(&KerbBindingCache);
KerbReferenceBindingCacheEntry(
CacheEntry,
TRUE
);
KerbDereferenceBindingCacheEntry(
CacheEntry
);
KerbUnlockList(&KerbBindingCache);
}