Source code of Windows XP (NT5)
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.

1237 lines
29 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1996
  6. //
  7. // File: bndcache.cxx
  8. //
  9. // Contents: spn cache for Kerberos Package
  10. //
  11. //
  12. // History: 13-August-1996 Created MikeSw
  13. //
  14. //------------------------------------------------------------------------
  15. #include <kerb.hxx>
  16. #include <kerbp.h>
  17. #include <spncache.h>
  18. //
  19. // TBD: Switch this to a table & resource, or entries for
  20. // each SPN prefix.
  21. //
  22. BOOLEAN KerberosSpnCacheInitialized = FALSE;
  23. KERBEROS_LIST KerbSpnCache;
  24. LONG SpnCount;
  25. #define KerbWriteLockSpnCache() KerbLockList(&KerbSpnCache);
  26. #define KerbReadLockSpnCache() KerbLockList(&KerbSpnCache);
  27. #define KerbUnlockSpnCache() KerbUnlockList(&KerbSpnCache);
  28. #define KerbWriteLockSpnCacheEntry(_x_) RtlAcquireResourceExclusive( &_x_->ResultLock, TRUE)
  29. #define KerbReadLockSpnCacheEntry(_x_) RtlAcquireResourceShared( &_x_->ResultLock, TRUE)
  30. #define KerbUnlockSpnCacheEntry(_x_) RtlReleaseResource( &_x_->ResultLock)
  31. #define KerbConvertSpnCacheEntryReadToWriteLock(_x_) RtlConvertSharedToExclusive( &_x_->ResultLock )
  32. //+-------------------------------------------------------------------------
  33. //
  34. // Function: KerbInitSpnCache
  35. //
  36. // Synopsis: Initializes the SPN cache
  37. //
  38. // Effects: allocates a resources
  39. //
  40. // Arguments: none
  41. //
  42. // Requires:
  43. //
  44. // Returns: STATUS_SUCCESS on success, other error codes on failure
  45. //
  46. // Notes:
  47. //
  48. //
  49. //--------------------------------------------------------------------------
  50. NTSTATUS
  51. KerbInitSpnCache(
  52. VOID
  53. )
  54. {
  55. NTSTATUS Status;
  56. Status = KerbInitializeList( &KerbSpnCache );
  57. if (!NT_SUCCESS(Status))
  58. {
  59. goto Cleanup;
  60. }
  61. KerberosSpnCacheInitialized = TRUE;
  62. Cleanup:
  63. if (!NT_SUCCESS(Status))
  64. {
  65. KerbFreeList( &KerbSpnCache );
  66. }
  67. return(Status);
  68. }
  69. //+-------------------------------------------------------------------------
  70. //
  71. // Function: KerbCleanupSpnCache
  72. //
  73. // Synopsis: Frees the Spn cache
  74. //
  75. // Effects:
  76. //
  77. // Arguments: none
  78. //
  79. // Requires:
  80. //
  81. // Returns: none
  82. //
  83. // Notes:
  84. //
  85. //
  86. //--------------------------------------------------------------------------
  87. VOID
  88. KerbCleanupSpnCache(
  89. VOID
  90. )
  91. {
  92. PKERB_SPN_CACHE_ENTRY CacheEntry;
  93. DebugLog((DEB_TRACE_SPN_CACHE, "Cleaning up SPN cache\n"));
  94. if (KerberosSpnCacheInitialized)
  95. {
  96. KerbWriteLockSpnCache();
  97. while (!IsListEmpty(&KerbSpnCache.List))
  98. {
  99. CacheEntry = CONTAINING_RECORD(
  100. KerbSpnCache.List.Flink,
  101. KERB_SPN_CACHE_ENTRY,
  102. ListEntry.Next
  103. );
  104. KerbReferenceListEntry(
  105. &KerbSpnCache,
  106. &CacheEntry->ListEntry,
  107. TRUE
  108. );
  109. KerbDereferenceSpnCacheEntry(CacheEntry);
  110. }
  111. KerbUnlockSpnCache();
  112. }
  113. }
  114. //+-------------------------------------------------------------------------
  115. //
  116. // Function: KerbCleanupResult
  117. //
  118. // Synopsis: Cleans up result entry
  119. //
  120. // Effects:
  121. //
  122. // Arguments:
  123. //
  124. // Requires:
  125. //
  126. // Returns: none
  127. //
  128. // Notes:
  129. //
  130. //
  131. //+-------------------------------------------------------------------------
  132. VOID
  133. KerbCleanupResult(
  134. IN PSPN_CACHE_RESULT Result
  135. )
  136. {
  137. KerbFreeString(&Result->AccountRealm);
  138. KerbFreeString(&Result->TargetRealm);
  139. }
  140. //+-------------------------------------------------------------------------
  141. //
  142. // Function: KerbPurgeResultByIndex
  143. //
  144. // Synopsis: Removes
  145. //
  146. // Effects: Dereferences the spn cache entry to make it go away
  147. // when it is no longer being used.
  148. //
  149. // Arguments: decrements reference count and delets cache entry if it goes
  150. // to zero
  151. //
  152. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  153. //
  154. // Returns: none
  155. //
  156. // Notes:
  157. //
  158. //
  159. //+-------------------------------------------------------------------------
  160. VOID
  161. KerbPurgeResultByIndex(
  162. IN PKERB_SPN_CACHE_ENTRY CacheEntry,
  163. IN ULONG IndexToPurge
  164. )
  165. {
  166. ULONG i;
  167. DebugLog((DEB_ERROR, "Purging %p, %i\n", CacheEntry, IndexToPurge));
  168. KerbCleanupResult(&CacheEntry->Results[IndexToPurge]);
  169. CacheEntry->ResultCount--;
  170. for (i = IndexToPurge; i < CacheEntry->ResultCount; i++)
  171. {
  172. CacheEntry->Results[i] = CacheEntry->Results[i+1];
  173. }
  174. //
  175. // Zero out fields in last entry so we don't leak on an error path (or free
  176. // bogus info) if we reuse the entry...
  177. //
  178. RtlZeroMemory(
  179. &CacheEntry->Results[i],
  180. sizeof(SPN_CACHE_RESULT)
  181. );
  182. }
  183. //+-------------------------------------------------------------------------
  184. //
  185. // Function: KerbDereferenceSpnCacheEntry
  186. //
  187. // Synopsis: Dereferences a spn cache entry
  188. //
  189. // Effects: Dereferences the spn cache entry to make it go away
  190. // when it is no longer being used.
  191. //
  192. // Arguments: decrements reference count and delets cache entry if it goes
  193. // to zero
  194. //
  195. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  196. //
  197. // Returns: none
  198. //
  199. // Notes:
  200. //
  201. //
  202. //--------------------------------------------------------------------------
  203. VOID
  204. KerbDereferenceSpnCacheEntry(
  205. IN PKERB_SPN_CACHE_ENTRY SpnCacheEntry
  206. )
  207. {
  208. if (KerbDereferenceListEntry(
  209. &SpnCacheEntry->ListEntry,
  210. &KerbSpnCache
  211. ) )
  212. {
  213. KerbFreeSpnCacheEntry(SpnCacheEntry);
  214. }
  215. }
  216. //+-------------------------------------------------------------------------
  217. //
  218. // Function: KerbReferenceSpnCacheEntry
  219. //
  220. // Synopsis: References a spn cache entry
  221. //
  222. // Effects: Increments the reference count on the spn cache entry
  223. //
  224. // Arguments: SpnCacheEntry - spn cache entry to reference
  225. //
  226. // Requires: The spn cache must be locked
  227. //
  228. // Returns: none
  229. //
  230. // Notes:
  231. //
  232. //
  233. //--------------------------------------------------------------------------
  234. VOID
  235. KerbReferenceSpnCacheEntry(
  236. IN PKERB_SPN_CACHE_ENTRY SpnCacheEntry,
  237. IN BOOLEAN RemoveFromList
  238. )
  239. {
  240. KerbWriteLockSpnCache();
  241. KerbReferenceListEntry(
  242. &KerbSpnCache,
  243. &SpnCacheEntry->ListEntry,
  244. RemoveFromList
  245. );
  246. KerbUnlockSpnCache();
  247. }
  248. //+-------------------------------------------------------------------------
  249. //
  250. // Function: KerbAgeResults
  251. //
  252. // Synopsis: Ages out a given cache entry's result list. Used
  253. // to reduce the result list to a manageable size, and
  254. // as a scavenger to cleanup orphaned / unused entries.
  255. //
  256. // Effects: Increments the reference count on the spn cache entry
  257. //
  258. // Arguments: SpnCacheEntry - spn cache entry to reference
  259. //
  260. // Requires: The spn cache must be locked
  261. //
  262. // Returns: none
  263. //
  264. // Notes:
  265. //
  266. //
  267. //+-------------------------------------------------------------------------
  268. VOID
  269. KerbAgeResults(
  270. IN PKERB_SPN_CACHE_ENTRY CacheEntry
  271. )
  272. {
  273. TimeStamp CurrentTime, BackoffTime;
  274. ULONG i;
  275. LONG Interval;
  276. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime);
  277. //
  278. // Age out everything older than GlobalSpnCacheTimeout first.
  279. //
  280. for ( i = 0; i < CacheEntry->ResultCount; i++ )
  281. {
  282. if (KerbGetTime(CacheEntry->Results[i].CacheStartTime) + KerbGetTime(KerbGlobalSpnCacheTimeout) < KerbGetTime(CurrentTime))
  283. {
  284. D_DebugLog((DEB_TRACE_SPN_CACHE, "removing %x %p\n"));
  285. KerbPurgeResultByIndex(CacheEntry, i);
  286. }
  287. }
  288. if ( CacheEntry->ResultCount < MAX_RESULTS )
  289. {
  290. return;
  291. }
  292. for ( Interval = 13; Interval > 0; Interval -= 4)
  293. {
  294. KerbSetTimeInMinutes(&BackoffTime, Interval);
  295. for ( i=0; i < CacheEntry->ResultCount ; i++ )
  296. {
  297. if (KerbGetTime(CacheEntry->Results[i].CacheStartTime) + KerbGetTime(BackoffTime) < KerbGetTime(CurrentTime))
  298. {
  299. D_DebugLog((DEB_TRACE_SPN_CACHE, "removing %x %p\n"));
  300. KerbPurgeResultByIndex(CacheEntry, i);
  301. }
  302. }
  303. if ( CacheEntry->ResultCount < MAX_RESULTS )
  304. {
  305. return;
  306. }
  307. }
  308. //
  309. // Still have MAX_RESULTS after all that geezzz..
  310. //
  311. DebugLog((DEB_ERROR, "Can't get below MAX_RESULTS (%p) \n", CacheEntry ));
  312. DsysAssert(FALSE);
  313. for ( i=0; i < CacheEntry->ResultCount ; i++ )
  314. {
  315. KerbPurgeResultByIndex(CacheEntry, i);
  316. }
  317. return;
  318. }
  319. //+-------------------------------------------------------------------------
  320. //
  321. // Function: KerbTaskSpnCacheScavenger
  322. //
  323. // Synopsis: Cleans up any old SPN cache entries. Triggered by 30 minute
  324. // task.
  325. //
  326. // Effects:
  327. //
  328. // Arguments: SpnCacheEntry - spn cache entry to reference
  329. //
  330. // Requires: The spn cache entry must be locked
  331. //
  332. // Returns: none
  333. //
  334. // Notes:
  335. //
  336. //
  337. //
  338. VOID
  339. KerbSpnCacheScavenger()
  340. {
  341. PKERB_SPN_CACHE_ENTRY CacheEntry = NULL;
  342. PLIST_ENTRY ListEntry;
  343. BOOLEAN FreeMe = FALSE;
  344. KerbWriteLockSpnCache();
  345. for (ListEntry = KerbSpnCache.List.Flink ;
  346. ListEntry != &KerbSpnCache.List ;
  347. ListEntry = ListEntry->Flink )
  348. {
  349. CacheEntry = CONTAINING_RECORD(ListEntry, KERB_SPN_CACHE_ENTRY, ListEntry.Next);
  350. KerbWriteLockSpnCacheEntry( CacheEntry );
  351. KerbAgeResults(CacheEntry);
  352. //
  353. // Time to pull this one from list.
  354. //
  355. if ( CacheEntry->ResultCount == 0 )
  356. {
  357. ListEntry = ListEntry->Blink;
  358. KerbReferenceSpnCacheEntry(
  359. CacheEntry,
  360. TRUE
  361. );
  362. FreeMe = TRUE;
  363. }
  364. KerbUnlockSpnCacheEntry( CacheEntry );
  365. //
  366. // Pull the list reference.
  367. //
  368. if ( FreeMe )
  369. {
  370. KerbDereferenceSpnCacheEntry( CacheEntry );
  371. FreeMe = FALSE;
  372. }
  373. }
  374. KerbUnlockSpnCache();
  375. }
  376. //+-------------------------------------------------------------------------
  377. //
  378. // Function: KerbAddCacheResult
  379. //
  380. // Synopsis: Uses registry to create
  381. //
  382. // Effects: Increments the reference count on the spn cache entry
  383. //
  384. // Arguments: SpnCacheEntry - spn cache entry to reference
  385. //
  386. // Requires: The spn cache resource must be locked
  387. //
  388. // Returns: none
  389. //
  390. // Notes:
  391. //
  392. //
  393. //--------------------------------------------------------------------------
  394. NTSTATUS
  395. KerbAddCacheResult(
  396. IN PKERB_SPN_CACHE_ENTRY CacheEntry,
  397. IN PKERB_PRIMARY_CREDENTIAL AccountCredential,
  398. IN ULONG UpdateFlags,
  399. IN OPTIONAL PUNICODE_STRING NewRealm
  400. )
  401. {
  402. NTSTATUS Status = STATUS_SUCCESS;
  403. PSPN_CACHE_RESULT Result = NULL;
  404. D_DebugLog((DEB_TRACE_SPN_CACHE, "KerbAddCacheResult add domain %wZ to _KERB_SPN_CACHE_ENTRY %p (UpdateFlags %#x), NewRealm %wZ for ", &AccountCredential->DomainName, CacheEntry, UpdateFlags, NewRealm));
  405. D_KerbPrintKdcName(DEB_TRACE_SPN_CACHE, CacheEntry->Spn);
  406. //
  407. // If we don't have an account realm w/ this credential (e.g someone
  408. // supplied you a UPN to acquirecredentialshandle, don't use the
  409. // spn cache.
  410. //
  411. if ( AccountCredential->DomainName.Length == 0 )
  412. {
  413. return STATUS_NOT_SUPPORTED;
  414. }
  415. //
  416. // First off, see if we're hitting the limits for our array.
  417. // We shouldn't ever get close to MAX_RESULTS, but if we do,
  418. // age out the least current CacheResult.
  419. //
  420. if ( (CacheEntry->ResultCount + 1) == MAX_RESULTS )
  421. {
  422. KerbAgeResults(CacheEntry);
  423. }
  424. Result = &CacheEntry->Results[CacheEntry->ResultCount];
  425. Status = KerbDuplicateStringEx(
  426. &Result->AccountRealm,
  427. &AccountCredential->DomainName,
  428. FALSE
  429. );
  430. if (!NT_SUCCESS( Status ))
  431. {
  432. goto Cleanup;
  433. }
  434. if (ARGUMENT_PRESENT( NewRealm ))
  435. {
  436. D_DebugLog((DEB_TRACE_SPN_CACHE, "Known - realm %wZ\n", NewRealm));
  437. DsysAssert(UpdateFlags != KERB_SPN_UNKNOWN);
  438. Status = KerbDuplicateStringEx(
  439. &Result->TargetRealm,
  440. NewRealm,
  441. FALSE
  442. );
  443. if (!NT_SUCCESS( Status ))
  444. {
  445. goto Cleanup;
  446. }
  447. }
  448. #if DBG
  449. else
  450. {
  451. DsysAssert(UpdateFlags != KERB_SPN_KNOWN);
  452. }
  453. #endif
  454. Result->CacheFlags = UpdateFlags;
  455. GetSystemTimeAsFileTime((PFILETIME) &Result->CacheStartTime);
  456. CacheEntry->ResultCount++;
  457. Cleanup:
  458. if (!NT_SUCCESS( Status ) )
  459. {
  460. if ( Result != NULL )
  461. {
  462. KerbCleanupResult( Result );
  463. }
  464. }
  465. return Status;
  466. }
  467. //+-------------------------------------------------------------------------
  468. //
  469. // Function: KerbBuildSpnCacheEntry
  470. //
  471. // Synopsis: Builds a spn cache entry
  472. //
  473. // Effects:
  474. //
  475. // Arguments:
  476. //
  477. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  478. //
  479. // Returns: none
  480. //
  481. // Notes:
  482. //
  483. //
  484. //--------------------------------------------------------------------------
  485. NTSTATUS
  486. KerbCreateSpnCacheEntry(
  487. IN PKERB_INTERNAL_NAME Spn,
  488. IN PKERB_PRIMARY_CREDENTIAL AccountCredential,
  489. IN ULONG UpdateFlags,
  490. IN OPTIONAL PUNICODE_STRING NewRealm,
  491. IN OUT PKERB_SPN_CACHE_ENTRY* NewCacheEntry
  492. )
  493. {
  494. NTSTATUS Status;
  495. PKERB_SPN_CACHE_ENTRY CacheEntry = NULL;
  496. BOOLEAN FreeResource = FALSE;
  497. *NewCacheEntry = NULL;
  498. CacheEntry = (PKERB_SPN_CACHE_ENTRY) KerbAllocate( sizeof(KERB_SPN_CACHE_ENTRY) );
  499. if ( CacheEntry == NULL )
  500. {
  501. Status = STATUS_NO_MEMORY;
  502. goto Cleanup;
  503. }
  504. Status = KerbDuplicateKdcName(
  505. &CacheEntry->Spn,
  506. Spn
  507. );
  508. if (!NT_SUCCESS(Status))
  509. {
  510. goto Cleanup;
  511. }
  512. Status = KerbAddCacheResult(
  513. CacheEntry,
  514. AccountCredential,
  515. UpdateFlags,
  516. NewRealm
  517. );
  518. if (!NT_SUCCESS( Status ))
  519. {
  520. goto Cleanup;
  521. }
  522. KerbInitializeListEntry( &CacheEntry->ListEntry );
  523. __try
  524. {
  525. RtlInitializeResource( &CacheEntry->ResultLock );
  526. }
  527. __except(EXCEPTION_EXECUTE_HANDLER)
  528. {
  529. Status = STATUS_INSUFFICIENT_RESOURCES;
  530. goto Cleanup;
  531. }
  532. FreeResource = TRUE;
  533. KerbInsertSpnCacheEntry(CacheEntry);
  534. *NewCacheEntry = CacheEntry;
  535. CacheEntry = NULL;
  536. InterlockedIncrement( &SpnCount );
  537. Cleanup:
  538. if (!NT_SUCCESS(Status) && ( CacheEntry ))
  539. {
  540. KerbCleanupResult(&CacheEntry->Results[0]);
  541. KerbFreeKdcName( &CacheEntry->Spn );
  542. if ( FreeResource )
  543. {
  544. RtlDeleteResource( &CacheEntry->ResultLock );
  545. }
  546. KerbFree(CacheEntry);
  547. }
  548. return Status;
  549. }
  550. //+-------------------------------------------------------------------------
  551. //
  552. // Function: KerbScanResults
  553. //
  554. // Synopsis: Scans result list.
  555. //
  556. // Effects:
  557. //
  558. // Arguments:
  559. //
  560. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  561. //
  562. // Returns: none
  563. //
  564. // Notes:
  565. //
  566. //
  567. //---
  568. BOOLEAN
  569. KerbScanResults(
  570. IN PKERB_SPN_CACHE_ENTRY CacheEntry,
  571. IN PUNICODE_STRING Realm,
  572. IN OUT PULONG Index
  573. )
  574. {
  575. BOOLEAN Found = FALSE;
  576. ULONG i;
  577. for ( i=0; i < CacheEntry->ResultCount; i++)
  578. {
  579. if (RtlEqualUnicodeString(
  580. &CacheEntry->Results[i].AccountRealm,
  581. Realm,
  582. TRUE
  583. ))
  584. {
  585. Found = TRUE;
  586. *Index = i;
  587. break;
  588. }
  589. }
  590. return Found;
  591. }
  592. //+-------------------------------------------------------------------------
  593. //
  594. // Function: KerbUpdateSpnCacheEntry
  595. //
  596. // Synopsis: Updates a spn cache entry
  597. //
  598. // Effects:
  599. //
  600. // Arguments:
  601. //
  602. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  603. //
  604. // Returns: none
  605. //
  606. // Notes:
  607. //
  608. //
  609. //--------------------------------------------------------------------------
  610. NTSTATUS
  611. KerbUpdateSpnCacheEntry(
  612. IN OPTIONAL PKERB_SPN_CACHE_ENTRY ExistingCacheEntry,
  613. IN PKERB_INTERNAL_NAME Spn,
  614. IN PKERB_PRIMARY_CREDENTIAL AccountCredential,
  615. IN ULONG UpdateFlags,
  616. IN OPTIONAL PUNICODE_STRING NewRealm
  617. )
  618. {
  619. PKERB_SPN_CACHE_ENTRY CacheEntry = ExistingCacheEntry;
  620. NTSTATUS Status = STATUS_SUCCESS;
  621. BOOLEAN Found = FALSE, Update = FALSE;
  622. ULONG Index = 0;
  623. //
  624. // We're not using SPN cache
  625. //
  626. if (KerbGlobalSpnCacheTimeout.QuadPart == 0 || !KerberosSpnCacheInitialized )
  627. {
  628. return STATUS_SUCCESS;
  629. }
  630. //
  631. // If we didn't have a cache entry before, see if we do now, or create
  632. // one if necessary.
  633. //
  634. if (!ARGUMENT_PRESENT( ExistingCacheEntry ))
  635. {
  636. KerbWriteLockSpnCache();
  637. CacheEntry = KerbLocateSpnCacheEntry( Spn );
  638. if ( CacheEntry == NULL)
  639. {
  640. Status = KerbCreateSpnCacheEntry(
  641. Spn,
  642. AccountCredential,
  643. UpdateFlags,
  644. NewRealm,
  645. &CacheEntry
  646. );
  647. if (NT_SUCCESS(Status))
  648. {
  649. //
  650. // All done, get outta here.
  651. //
  652. D_DebugLog((DEB_TRACE_SPN_CACHE, "Created new cache entry %p (%x) \n", CacheEntry, UpdateFlags));
  653. D_KerbPrintKdcName(DEB_TRACE_SPN_CACHE, Spn);
  654. D_DebugLog((DEB_TRACE_SPN_CACHE, "%wZ\n", &AccountCredential->DomainName));
  655. KerbDereferenceSpnCacheEntry( CacheEntry );
  656. }
  657. KerbUnlockSpnCache();
  658. return Status;
  659. }
  660. KerbUnlockSpnCache();
  661. }
  662. //
  663. // Got an existing entry - update it.
  664. //
  665. KerbReadLockSpnCacheEntry( CacheEntry );
  666. if (KerbScanResults(
  667. CacheEntry,
  668. &AccountCredential->DomainName,
  669. &Index
  670. ))
  671. {
  672. Found = TRUE;
  673. Update = (( CacheEntry->Results[Index].CacheFlags & UpdateFlags) != UpdateFlags);
  674. }
  675. KerbUnlockSpnCacheEntry( CacheEntry );
  676. //
  677. // To avoid always taking the write lock, we'll need to rescan the result
  678. // list under a write lock.
  679. //
  680. if ( Update )
  681. {
  682. KerbWriteLockSpnCacheEntry( CacheEntry );
  683. if (KerbScanResults(
  684. CacheEntry,
  685. &AccountCredential->DomainName,
  686. &Index
  687. ))
  688. {
  689. //
  690. // Hasn't been updated or removed in the small time slice above. Update.
  691. //
  692. if (( CacheEntry->Results[Index].CacheFlags & UpdateFlags) != UpdateFlags )
  693. {
  694. D_DebugLog((
  695. DEB_TRACE_SPN_CACHE,
  696. "KerbUpdateSpnCacheEntry changing _KERB_SPN_CACHE_ENTRY %p Result Index %#x: AccountRealm %wZ, TargetRealm %wZ, NewRealm %wZ, CacheFlags %#x to CacheFlags %#x for ",
  697. CacheEntry,
  698. Index,
  699. &CacheEntry->Results[Index].AccountRealm,
  700. &CacheEntry->Results[Index].TargetRealm,
  701. NewRealm,
  702. CacheEntry->Results[Index].CacheFlags,
  703. UpdateFlags
  704. ));
  705. D_KerbPrintKdcName(DEB_TRACE_SPN_CACHE, CacheEntry->Spn);
  706. CacheEntry->Results[Index].CacheFlags = UpdateFlags;
  707. GetSystemTimeAsFileTime( (LPFILETIME) &CacheEntry->Results[Index].CacheStartTime );
  708. KerbFreeString(&CacheEntry->Results[Index].TargetRealm);
  709. if (ARGUMENT_PRESENT( NewRealm ))
  710. {
  711. DsysAssert( UpdateFlags == KERB_SPN_KNOWN );
  712. Status = KerbDuplicateStringEx(
  713. &CacheEntry->Results[Index].TargetRealm,
  714. NewRealm,
  715. FALSE
  716. );
  717. if (!NT_SUCCESS( Status ))
  718. {
  719. KerbUnlockSpnCacheEntry( CacheEntry );
  720. goto Cleanup;
  721. }
  722. }
  723. }
  724. }
  725. else
  726. {
  727. Found = FALSE;
  728. }
  729. KerbUnlockSpnCacheEntry( CacheEntry );
  730. }
  731. if (!Found)
  732. {
  733. KerbWriteLockSpnCacheEntry ( CacheEntry );
  734. //
  735. // Still not found
  736. //
  737. if (!KerbScanResults( CacheEntry, &AccountCredential->DomainName, &Index ))
  738. {
  739. Status = KerbAddCacheResult(
  740. CacheEntry,
  741. AccountCredential,
  742. UpdateFlags,
  743. NewRealm
  744. );
  745. }
  746. KerbUnlockSpnCacheEntry( CacheEntry );
  747. if (!NT_SUCCESS(Status))
  748. {
  749. goto Cleanup;
  750. }
  751. }
  752. Cleanup:
  753. //
  754. // Created a new cache entry, referenced w/i this function.
  755. //
  756. if (!ARGUMENT_PRESENT( ExistingCacheEntry ) && CacheEntry )
  757. {
  758. KerbDereferenceSpnCacheEntry( CacheEntry );
  759. }
  760. return Status;
  761. }
  762. //+-------------------------------------------------------------------------
  763. //
  764. // Function: KerbLocateSpnCacheEntry
  765. //
  766. // Synopsis: References a spn cache entry by name
  767. //
  768. // Effects: Increments the reference count on the spn cache entry
  769. //
  770. // Arguments: RealmName - Contains the name of the realm for which to
  771. // obtain a binding handle.
  772. // DesiredFlags - Flags desired for binding, such as PDC required
  773. // RemoveFromList - Remove cache entry from cache when found.
  774. //
  775. // Requires:
  776. //
  777. // Returns: The referenced cache entry or NULL if it was not found.
  778. //
  779. // Notes: If an invalid entry is found it may be dereferenced
  780. //
  781. //
  782. //--------------------------------------------------------------------------
  783. PKERB_SPN_CACHE_ENTRY
  784. KerbLocateSpnCacheEntry(
  785. IN PKERB_INTERNAL_NAME Spn
  786. )
  787. {
  788. PLIST_ENTRY ListEntry;
  789. PKERB_SPN_CACHE_ENTRY CacheEntry = NULL;
  790. BOOLEAN Found = FALSE;
  791. if (Spn->NameType != KRB_NT_SRV_INST)
  792. {
  793. return NULL;
  794. }
  795. //
  796. // We're not using SPN cache
  797. //
  798. if (KerbGlobalSpnCacheTimeout.QuadPart == 0 || !KerberosSpnCacheInitialized )
  799. {
  800. return NULL;
  801. }
  802. //
  803. // Scale the cache by aging out old entries.
  804. //
  805. if ( SpnCount > MAX_CACHE_ENTRIES )
  806. {
  807. KerbSpnCacheScavenger();
  808. }
  809. KerbReadLockSpnCache();
  810. //
  811. // Go through the spn cache looking for the correct entry
  812. //
  813. for (ListEntry = KerbSpnCache.List.Flink ;
  814. ListEntry != &KerbSpnCache.List ;
  815. ListEntry = ListEntry->Flink )
  816. {
  817. CacheEntry = CONTAINING_RECORD(ListEntry, KERB_SPN_CACHE_ENTRY, ListEntry.Next);
  818. if (KerbEqualKdcNames(CacheEntry->Spn,Spn))
  819. {
  820. KerbReferenceSpnCacheEntry(
  821. CacheEntry,
  822. FALSE
  823. );
  824. D_DebugLog((DEB_TRACE_SPN_CACHE, "SpnCacheEntry %p\n", CacheEntry));
  825. Found = TRUE;
  826. break;
  827. }
  828. }
  829. if (!Found)
  830. {
  831. CacheEntry = NULL;
  832. }
  833. KerbUnlockSpnCache();
  834. return(CacheEntry);
  835. }
  836. //+-------------------------------------------------------------------------
  837. //
  838. // Function: KerbCleanupResultList
  839. //
  840. // Synopsis: Frees memory associated with a result list
  841. //
  842. // Effects:
  843. //
  844. // Arguments: SpnCacheEntry - The cache entry to free. It must be
  845. // unlinked, and the Resultlock must be held.
  846. //
  847. // Requires:
  848. //
  849. // Returns: none
  850. //
  851. // Notes:
  852. //
  853. //
  854. //--------------------------------------------------------------------------
  855. VOID
  856. KerbCleanupResultList(
  857. IN PKERB_SPN_CACHE_ENTRY CacheEntry
  858. )
  859. {
  860. for (ULONG i = 0; i < CacheEntry->ResultCount; i++)
  861. {
  862. KerbCleanupResult(&CacheEntry->Results[i]);
  863. }
  864. CacheEntry->ResultCount = 0;
  865. }
  866. //+-------------------------------------------------------------------------
  867. //
  868. // Function: KerbFreeSpnCacheEntry
  869. //
  870. // Synopsis: Frees memory associated with a spn cache entry
  871. //
  872. // Effects:
  873. //
  874. // Arguments: SpnCacheEntry - The cache entry to free. It must be
  875. // unlinked.
  876. //
  877. // Requires:
  878. //
  879. // Returns: none
  880. //
  881. // Notes:
  882. //
  883. //
  884. //--------------------------------------------------------------------------
  885. VOID
  886. KerbFreeSpnCacheEntry(
  887. IN PKERB_SPN_CACHE_ENTRY SpnCacheEntry
  888. )
  889. {
  890. //
  891. // Must be unlinked..
  892. //
  893. DsysAssert(SpnCacheEntry->ListEntry.Next.Flink == NULL);
  894. DsysAssert(SpnCacheEntry->ListEntry.Next.Blink == NULL);
  895. KerbWriteLockSpnCacheEntry(SpnCacheEntry);
  896. KerbCleanupResultList(SpnCacheEntry);
  897. KerbUnlockSpnCacheEntry(SpnCacheEntry);
  898. RtlDeleteResource(&SpnCacheEntry->ResultLock);
  899. KerbFreeKdcName(&SpnCacheEntry->Spn);
  900. KerbFree(SpnCacheEntry);
  901. InterlockedDecrement( &SpnCount );
  902. }
  903. //+-------------------------------------------------------------------------
  904. //
  905. // Function: KerbInsertBinding
  906. //
  907. // Synopsis: Inserts a binding into the spn cache
  908. //
  909. // Effects: bumps reference count on binding
  910. //
  911. // Arguments: CacheEntry - Cache entry to insert
  912. //
  913. // Requires:
  914. //
  915. // Returns: STATUS_SUCCESS always
  916. //
  917. // Notes:
  918. //
  919. //
  920. //--------------------------------------------------------------------------
  921. NTSTATUS
  922. KerbInsertSpnCacheEntry(
  923. IN PKERB_SPN_CACHE_ENTRY CacheEntry
  924. )
  925. {
  926. KerbInsertListEntry(
  927. &CacheEntry->ListEntry,
  928. &KerbSpnCache
  929. );
  930. return(STATUS_SUCCESS);
  931. }
  932. //+-------------------------------------------------------------------------
  933. //
  934. // Function: KerbGetSpnCacheStatus
  935. //
  936. // Synopsis: Gets the status of a cache entry for a given realm.
  937. //
  938. // Effects: Returns STATUS_NO_SAM_TRUST_RELATIONSHIP for unknown SPNs,
  939. // STATUS_NO_MATCH, if we're missing a realm result, or
  940. // STATUS_SUCCESS ++ dupe the SPNREalm for the "real" realm
  941. // of the SPN relative to the account realm.
  942. //
  943. // Arguments: CacheEntry - SPN cache entry from ProcessTargetName()
  944. // Credential - Primary cred for account realm.
  945. // SpnRealm - IN OUT Filled in w/ target realm of SPN
  946. //
  947. //
  948. // Requires:
  949. //
  950. // Returns:
  951. //
  952. // Notes:
  953. //
  954. //
  955. //--------------------------------------------------------------------------
  956. NTSTATUS
  957. KerbGetSpnCacheStatus(
  958. IN PKERB_SPN_CACHE_ENTRY CacheEntry,
  959. IN PKERB_PRIMARY_CREDENTIAL Credential,
  960. IN OUT PUNICODE_STRING SpnRealm
  961. )
  962. {
  963. NTSTATUS Status = STATUS_NO_MATCH;;
  964. ULONG i;
  965. TimeStamp CurrentTime;
  966. BOOLEAN Purge = FALSE;
  967. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime);
  968. //
  969. // Read Lock the spn cache entry
  970. //
  971. KerbReadLockSpnCacheEntry( CacheEntry );
  972. if (KerbScanResults(
  973. CacheEntry,
  974. &Credential->DomainName,
  975. &i
  976. ))
  977. {
  978. if (CacheEntry->Results[i].CacheFlags & KERB_SPN_UNKNOWN)
  979. {
  980. //
  981. // Check and see if this timestamp has expired.
  982. //
  983. if (KerbGetTime(CacheEntry->Results[i].CacheStartTime) + KerbGetTime(KerbGlobalSpnCacheTimeout) < KerbGetTime(CurrentTime))
  984. {
  985. Purge = TRUE;
  986. Status = STATUS_SUCCESS;
  987. }
  988. else
  989. {
  990. Status = STATUS_NO_TRUST_SAM_ACCOUNT;
  991. DebugLog((DEB_WARN, "SPN not found\n"));
  992. KerbPrintKdcName(DEB_WARN, CacheEntry->Spn);
  993. }
  994. }
  995. else if (CacheEntry->Results[i].CacheFlags & KERB_SPN_KNOWN)
  996. {
  997. Status = KerbDuplicateStringEx(
  998. SpnRealm,
  999. &CacheEntry->Results[i].TargetRealm,
  1000. FALSE
  1001. );
  1002. D_DebugLog((DEB_TRACE_SPN_CACHE, "Found %wZ\n", SpnRealm));
  1003. D_KerbPrintKdcName(DEB_TRACE_SPN_CACHE, CacheEntry->Spn);
  1004. }
  1005. }
  1006. KerbUnlockSpnCacheEntry( CacheEntry );
  1007. if (!NT_SUCCESS( Status ))
  1008. {
  1009. return Status;
  1010. }
  1011. //
  1012. // Take the write lock, and verify that we still need to purge the above
  1013. // result.
  1014. //
  1015. if ( Purge )
  1016. {
  1017. KerbWriteLockSpnCacheEntry( CacheEntry );
  1018. if (KerbScanResults(
  1019. CacheEntry,
  1020. &Credential->DomainName,
  1021. &i
  1022. ))
  1023. {
  1024. if (KerbGetTime(CacheEntry->Results[i].CacheStartTime) +
  1025. KerbGetTime(KerbGlobalSpnCacheTimeout) < KerbGetTime(CurrentTime))
  1026. {
  1027. D_DebugLog((DEB_TRACE_SPN_CACHE, "Purging %p due to time\n", &CacheEntry->Results[i]));
  1028. KerbPurgeResultByIndex( CacheEntry, i );
  1029. }
  1030. }
  1031. KerbUnlockSpnCacheEntry( CacheEntry );
  1032. Status = STATUS_NO_MATCH;
  1033. }
  1034. return Status;
  1035. }