//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation 1992 - 1996 // // File: bndcache.cxx // // Contents: Binding cache for Kerberos Package // // // History: 13-August-1996 Created MikeSw // //------------------------------------------------------------------------ #include #define BNDCACHE_ALLOCATE #include //+------------------------------------------------------------------------- // // 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, BINDING_CACHE_LOCK_ENUM ); 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_BND_CACHE, "Purging KDC cache entry Realm: %wZ, Addr: %wZ, DcFlags %x\n", &CacheEntry->RealmName, &CacheEntry->KdcAddress, CacheEntry->DcFlags )); RemoveFromList = TRUE; Found = FALSE; } #if DBG else { D_DebugLog((DEB_TRACE_BND_CACHE, "**Using** KDC cache entry Realm: %wZ, Addr: %wZ, DcFlags %x\n", &CacheEntry->RealmName, &CacheEntry->KdcAddress, CacheEntry->DcFlags )); if ((CacheEntry->DcFlags & DS_CLOSEST_FLAG) == DS_CLOSEST_FLAG) { D_DebugLog((DEB_TRACE_BND_CACHE, "CLOSE DC ")); } else { D_DebugLog((DEB_TRACE_BND_CACHE, "FAR DC ")); } if ((CacheEntry->DcFlags & DS_PDC_FLAG) == DS_PDC_FLAG) { D_DebugLog((DEB_TRACE_BND_CACHE, "-- ** PDC **\n")); } else { D_DebugLog((DEB_TRACE_BND_CACHE, "-- BDC\n")); } } #endif //dbg } 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 ) { IF_DEBUG(DISABLE_BND_CACHE) { DebugLog((DEB_TRACE_BND_CACHE, "KerbInsertBinding binding cache disabled\n")); return STATUS_SUCCESS; } 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_BND_CACHE, "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_BND_CACHE, "Caching as PDC\n")); DesiredFlags = DS_PDC_REQUIRED; } else { D_DebugLog((DEB_TRACE_BND_CACHE, "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); }