Leaked source code of windows server 2003
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.

2053 lines
49 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. BOOLEAN HostToRealmUsed = FALSE;
  33. BOOLEAN HostToRealmInitialized = FALSE;
  34. RTL_AVL_TABLE HostToRealmTable;
  35. SAFE_RESOURCE HostToRealmLock;
  36. #define HostToRealmReadLock() SafeAcquireResourceShared(&HostToRealmLock, TRUE)
  37. #define HostToRealmWriteLock() SafeAcquireResourceExclusive(&HostToRealmLock, TRUE)
  38. #define HostToRealmUnlock() SafeReleaseResource(&HostToRealmLock)
  39. //
  40. // Allocation Routines for our table.
  41. //
  42. PVOID
  43. NTAPI
  44. KerbTableAllocateRoutine(
  45. struct _RTL_AVL_TABLE * Table,
  46. CLONG ByteSize
  47. )
  48. {
  49. UNREFERENCED_PARAMETER( Table );
  50. return KerbAllocate( ByteSize );
  51. }
  52. void
  53. NTAPI
  54. KerbTableFreeRoutine(
  55. struct _RTL_AVL_TABLE * Table,
  56. PVOID Buffer
  57. )
  58. {
  59. UNREFERENCED_PARAMETER( Table );
  60. KerbFree( Buffer );
  61. }
  62. //+-------------------------------------------------------------------------
  63. //
  64. // Function: KerbStringComparisonRoutine
  65. //
  66. // Synopsis: Used in tables to compare unicode strings
  67. //
  68. // Effects:
  69. //
  70. // Arguments:
  71. //
  72. //
  73. // Requires:
  74. //
  75. // Returns: none
  76. //
  77. // Notes:
  78. //
  79. //
  80. //--
  81. RTL_GENERIC_COMPARE_RESULTS
  82. NTAPI
  83. KerbTableStringComparisonRoutine(
  84. struct _RTL_AVL_TABLE * Table,
  85. PVOID FirstStruct,
  86. PVOID SecondStruct
  87. )
  88. {
  89. INT Result;
  90. UNICODE_STRING *String1, *String2;
  91. UNREFERENCED_PARAMETER( Table );
  92. ASSERT( FirstStruct );
  93. ASSERT( SecondStruct );
  94. String1 = ( UNICODE_STRING * )FirstStruct;
  95. String2 = ( UNICODE_STRING * )SecondStruct;
  96. Result = RtlCompareUnicodeString(
  97. String1,
  98. String2,
  99. TRUE
  100. );
  101. if ( Result < 0 ) {
  102. return GenericLessThan;
  103. } else if ( Result > 0 ) {
  104. return GenericGreaterThan;
  105. } else {
  106. return GenericEqual;
  107. }
  108. }
  109. //+-------------------------------------------------------------------------
  110. //
  111. // Function: KerbPurgeHostToRealmTable
  112. //
  113. // Synopsis: Creates an entry and puts it in the HostToRealmTable
  114. //
  115. // Effects:
  116. //
  117. // Arguments: none
  118. //
  119. // Requires:
  120. //
  121. // Returns: STATUS_SUCCESS on success, other error codes on failure
  122. //
  123. // Notes:
  124. //
  125. //
  126. //--------------------------------------------------------------------------
  127. VOID
  128. KerbPurgeHostToRealmTable()
  129. {
  130. PHOST_TO_REALM_KEY Key = NULL;
  131. BOOLEAN fDeleted;
  132. HostToRealmWriteLock();
  133. HostToRealmUsed = FALSE;
  134. for ( Key = ( PHOST_TO_REALM_KEY )RtlEnumerateGenericTableAvl( &HostToRealmTable, TRUE );
  135. Key != NULL;
  136. Key = ( PHOST_TO_REALM_KEY )RtlEnumerateGenericTableAvl( &HostToRealmTable, TRUE ))
  137. {
  138. fDeleted = RtlDeleteElementGenericTableAvl( &HostToRealmTable, (PVOID) &Key->SpnSuffix );
  139. DsysAssert( fDeleted );
  140. }
  141. HostToRealmUnlock();
  142. }
  143. //+-------------------------------------------------------------------------
  144. //
  145. // Function: KerbAddHostToRealmMapping
  146. //
  147. // Synopsis: Creates an entry and puts it in the HostToRealmTable
  148. //
  149. // Effects:
  150. //
  151. // Arguments: none
  152. //
  153. // Requires:
  154. //
  155. // Returns: STATUS_SUCCESS on success, other error codes on failure
  156. //
  157. // Notes:
  158. //
  159. //
  160. //--------------------------------------------------------------------------
  161. BOOLEAN
  162. KerbAddHostToRealmMapping(
  163. HKEY hKey,
  164. LPWSTR Realm
  165. )
  166. {
  167. BOOLEAN fRet = FALSE;
  168. ULONG Type;
  169. ULONG WinError = ERROR_SUCCESS;
  170. NTSTATUS Status;
  171. PBYTE Data = NULL;
  172. PWCHAR pCh;
  173. ULONG DataSize = 0;
  174. WCHAR Value[] = KERB_HOST_TO_REALM_VAL;
  175. UNICODE_STRING RealmString = {0};
  176. ULONG index, StringCount = 0;
  177. RtlInitUnicodeString(
  178. &RealmString,
  179. Realm
  180. );
  181. Status = RtlUpcaseUnicodeString(
  182. &RealmString,
  183. &RealmString,
  184. FALSE
  185. );
  186. if (!NT_SUCCESS(Status))
  187. {
  188. goto Cleanup;
  189. }
  190. //
  191. // First query the SPN strings under this realm key.
  192. //
  193. WinError = RegQueryValueEx(
  194. hKey,
  195. Value,
  196. NULL,
  197. &Type,
  198. NULL,
  199. &DataSize
  200. );
  201. if ( Type != REG_MULTI_SZ )
  202. {
  203. D_DebugLog((DEB_ERROR, "Wrong registry type \n"));
  204. goto Cleanup;
  205. }
  206. if ((WinError == ERROR_MORE_DATA) || (WinError == ERROR_SUCCESS))
  207. {
  208. SafeAllocaAllocate( Data, DataSize );
  209. if (Data == NULL)
  210. {
  211. goto Cleanup;
  212. }
  213. WinError = RegQueryValueEx(
  214. hKey,
  215. Value,
  216. NULL,
  217. &Type,
  218. Data,
  219. &DataSize
  220. );
  221. }
  222. if ( WinError != ERROR_SUCCESS )
  223. {
  224. goto Cleanup;
  225. }
  226. pCh = (PWCHAR) Data;
  227. //
  228. // Now parse out the reg_multi_sz to create the keys for the table.
  229. //
  230. for ( index = 0; index < DataSize; index += sizeof(WCHAR), pCh++ )
  231. {
  232. if ( (*pCh) == L'\0' )
  233. {
  234. StringCount++;
  235. if (*(pCh+1) == L'\0')
  236. {
  237. break;
  238. }
  239. }
  240. }
  241. //
  242. // Build the keys, one for each SPN substring entry.
  243. // These keys are contigous blobs - to speed up table lookups, and
  244. // confine them to a page of memory.
  245. //
  246. pCh = (PWCHAR) Data;
  247. for ( index = 0; index < StringCount ; index++ )
  248. {
  249. ULONG uSize = (2*sizeof(UNICODE_STRING));
  250. UNICODE_STRING SpnSuffix = {0};
  251. PHOST_TO_REALM_KEY Key = NULL, NewKey = NULL;
  252. PBYTE tmp;
  253. RtlInitUnicodeString(
  254. &SpnSuffix,
  255. pCh
  256. );
  257. Status = RtlUpcaseUnicodeString(
  258. &SpnSuffix,
  259. &SpnSuffix,
  260. FALSE
  261. );
  262. if (!NT_SUCCESS(Status))
  263. {
  264. goto Cleanup;
  265. }
  266. uSize += ROUND_UP_COUNT(SpnSuffix.MaximumLength, ALIGN_LPDWORD);
  267. uSize += RealmString.MaximumLength;
  268. Key = (PHOST_TO_REALM_KEY) KerbAllocate(uSize);
  269. if (NULL == Key)
  270. {
  271. goto Cleanup;
  272. }
  273. tmp = (PBYTE) &Key->NameBuffer;
  274. Key->SpnSuffix.Length = SpnSuffix.Length;
  275. Key->SpnSuffix.MaximumLength = SpnSuffix.MaximumLength;
  276. Key->SpnSuffix.Buffer = (PWCHAR) tmp;
  277. RtlCopyMemory(
  278. tmp,
  279. SpnSuffix.Buffer,
  280. SpnSuffix.MaximumLength
  281. );
  282. tmp += ROUND_UP_COUNT(SpnSuffix.MaximumLength, ALIGN_LPDWORD);
  283. Key->TargetRealm.Length = RealmString.Length;
  284. Key->TargetRealm.MaximumLength = RealmString.MaximumLength;
  285. Key->TargetRealm.Buffer = (PWCHAR) tmp;
  286. RtlCopyMemory(
  287. tmp,
  288. RealmString.Buffer,
  289. RealmString.MaximumLength
  290. );
  291. NewKey = (PHOST_TO_REALM_KEY) RtlInsertElementGenericTableAvl(
  292. &HostToRealmTable,
  293. Key,
  294. uSize,
  295. NULL
  296. );
  297. KerbFree(Key);
  298. if (NewKey == NULL)
  299. {
  300. D_DebugLog((DEB_ERROR, "Insert into table failed\n"));
  301. goto Cleanup;
  302. }
  303. NewKey->SpnSuffix.Buffer = NewKey->NameBuffer;
  304. NewKey->TargetRealm.Buffer = NewKey->NameBuffer +
  305. (ROUND_UP_COUNT(SpnSuffix.MaximumLength, ALIGN_LPDWORD) / sizeof(WCHAR));
  306. pCh += (SpnSuffix.MaximumLength / sizeof(WCHAR));
  307. DebugLog((DEB_TRACE_SPN_CACHE, "NewKey %p\n", NewKey));
  308. }
  309. HostToRealmUsed = TRUE;
  310. fRet = TRUE;
  311. Cleanup:
  312. SafeAllocaFree( Data );
  313. return fRet;
  314. }
  315. //+-------------------------------------------------------------------------
  316. //
  317. // Function: KerbRefreshHostToRealmTable
  318. //
  319. // Synopsis: Used as a registry callback routine for cleaning, and reusing
  320. // hosttorealm cache.
  321. // Effects:
  322. //
  323. // Arguments: none
  324. //
  325. // Requires:
  326. //
  327. // Returns: void
  328. //
  329. // Notes:
  330. //
  331. //
  332. //----
  333. VOID
  334. KerbRefreshHostToRealmTable()
  335. {
  336. if (!HostToRealmInitialized)
  337. {
  338. return;
  339. }
  340. KerbPurgeHostToRealmTable();
  341. KerbCreateHostToRealmMappings();
  342. }
  343. //+-------------------------------------------------------------------------
  344. //
  345. // Function: KerbInitHostToRealmTable
  346. //
  347. // Synopsis: Initializes the host to realm table.
  348. //
  349. // Effects: allocates a resources
  350. //
  351. // Arguments: none
  352. //
  353. // Requires:
  354. //
  355. // Returns: STATUS_SUCCESS on success, other error codes on failure
  356. //
  357. // Notes:
  358. //
  359. //
  360. //--------------------------------------------------------------------------
  361. NTSTATUS
  362. KerbInitHostToRealmTable(
  363. VOID
  364. )
  365. {
  366. NTSTATUS Status = STATUS_SUCCESS;;
  367. //
  368. // Initialize the host to realm mapping
  369. // table
  370. //
  371. RtlInitializeGenericTableAvl(
  372. &HostToRealmTable,
  373. KerbTableStringComparisonRoutine,
  374. KerbTableAllocateRoutine,
  375. KerbTableFreeRoutine,
  376. NULL
  377. );
  378. __try
  379. {
  380. SafeInitializeResource(&HostToRealmLock, HOST_2_REALM_LIST_LOCK);
  381. }
  382. __except(EXCEPTION_EXECUTE_HANDLER)
  383. {
  384. Status = STATUS_INSUFFICIENT_RESOURCES;
  385. goto Cleanup;
  386. }
  387. HostToRealmInitialized = TRUE;
  388. KerbCreateHostToRealmMappings();
  389. Cleanup:
  390. return(Status);
  391. }
  392. #if DBG
  393. #define MAX_NAMES 8
  394. LPWSTR Prefix[MAX_NAMES] = { L"CIFS", L"HTTP", L"RPCSS", L"HOST", L"GC", L"LDAP", L"DNS", L"???"};
  395. ULONG NameCount[MAX_NAMES];
  396. ULONG Tracking[MAX_NAMES];
  397. //
  398. // Fester: Remove this when we figure out a good pattern of
  399. // SPN usage.
  400. //
  401. VOID
  402. KerbLogSpnStats(
  403. PKERB_INTERNAL_NAME Spn
  404. )
  405. {
  406. ULONG i;
  407. UNICODE_STRING Namelist;
  408. if (Spn->NameCount < MAX_NAMES)
  409. {
  410. Tracking[Spn->NameCount]++;
  411. }
  412. for (i = 0; i < (MAX_NAMES - 1); i++)
  413. {
  414. RtlInitUnicodeString(
  415. &Namelist,
  416. Prefix[i]
  417. );
  418. if (RtlEqualUnicodeString(
  419. &Spn->Names[0],
  420. &Namelist,
  421. TRUE
  422. ))
  423. {
  424. NameCount[i]++;
  425. return;
  426. }
  427. }
  428. //
  429. // Count this as a miss
  430. //
  431. NameCount[MAX_NAMES-1]++;
  432. }
  433. #endif
  434. //+-------------------------------------------------------------------------
  435. //
  436. // Function: KerbInitSpnCache
  437. //
  438. // Synopsis: Initializes the SPN cache
  439. //
  440. // Effects: allocates a resources
  441. //
  442. // Arguments: none
  443. //
  444. // Requires:
  445. //
  446. // Returns: STATUS_SUCCESS on success, other error codes on failure
  447. //
  448. // Notes:
  449. //
  450. //
  451. //--------------------------------------------------------------------------
  452. NTSTATUS
  453. KerbInitSpnCache(
  454. VOID
  455. )
  456. {
  457. NTSTATUS Status;
  458. Status = KerbInitializeList( &KerbSpnCache, SPN_CACHE_LOCK_ENUM );
  459. if (!NT_SUCCESS(Status))
  460. {
  461. goto Cleanup;
  462. }
  463. Status = KerbInitHostToRealmTable();
  464. if (!NT_SUCCESS(Status))
  465. {
  466. goto Cleanup;
  467. }
  468. KerberosSpnCacheInitialized = TRUE;
  469. Cleanup:
  470. if (!NT_SUCCESS(Status))
  471. {
  472. KerbFreeList( &KerbSpnCache );
  473. }
  474. return(Status);
  475. }
  476. //+-------------------------------------------------------------------------
  477. //
  478. // Function: KerbCleanupSpnCache
  479. //
  480. // Synopsis: Frees the Spn cache
  481. //
  482. // Effects:
  483. //
  484. // Arguments: none
  485. //
  486. // Requires:
  487. //
  488. // Returns: none
  489. //
  490. // Notes:
  491. //
  492. //
  493. //--------------------------------------------------------------------------
  494. VOID
  495. KerbCleanupSpnCache(
  496. VOID
  497. )
  498. {
  499. PKERB_SPN_CACHE_ENTRY CacheEntry;
  500. DebugLog((DEB_TRACE_SPN_CACHE, "Cleaning up SPN cache\n"));
  501. if (KerberosSpnCacheInitialized)
  502. {
  503. KerbWriteLockSpnCache();
  504. while (!IsListEmpty(&KerbSpnCache.List))
  505. {
  506. CacheEntry = CONTAINING_RECORD(
  507. KerbSpnCache.List.Flink,
  508. KERB_SPN_CACHE_ENTRY,
  509. ListEntry.Next
  510. );
  511. KerbReferenceListEntry(
  512. &KerbSpnCache,
  513. &CacheEntry->ListEntry,
  514. TRUE
  515. );
  516. KerbDereferenceSpnCacheEntry(CacheEntry);
  517. }
  518. KerbUnlockSpnCache();
  519. }
  520. }
  521. //+-------------------------------------------------------------------------
  522. //
  523. // Function: KerbParseDnsName
  524. //
  525. // Synopsis: Parse Dns name from left to right, at each "."
  526. //
  527. // Effects:
  528. //
  529. // Arguments:
  530. //
  531. // Requires:
  532. //
  533. // Returns:
  534. //
  535. // Notes: The unicode string passed in will be modified - use a copy if
  536. // you still need the string.
  537. //
  538. //
  539. //--
  540. BOOLEAN
  541. KerbParseDnsName(
  542. IN UNICODE_STRING * Name
  543. )
  544. {
  545. USHORT Index;
  546. BOOLEAN Found = FALSE;
  547. ASSERT( Name );
  548. ASSERT( Name->Length > 0 );
  549. ASSERT( Name->Buffer != NULL );
  550. for ( Index = 0 ; Index < Name->Length ; Index += sizeof( WCHAR )) {
  551. if ( Name->Buffer[Index / sizeof( WCHAR )] == L'.' ) {
  552. Found = TRUE;
  553. Index += sizeof( WCHAR );
  554. break;
  555. }
  556. }
  557. ASSERT( !Found || Index < Name->Length );
  558. Name->Buffer += Index / sizeof( WCHAR );
  559. Name->Length = Name->Length - Index;
  560. Name->MaximumLength = Name->MaximumLength - Index;
  561. ASSERT( Found || Name->Length == 0 );
  562. return ( Found );
  563. }
  564. //+-------------------------------------------------------------------------
  565. //
  566. // Function: KerbSpnSubstringMatch
  567. //
  568. // Synopsis: Attempts to match an SPN to a known realm mapping
  569. //
  570. // Effects: Returns a new target realm.
  571. //
  572. // Arguments: decrements reference count and delets cache entry if it goes
  573. // to zero
  574. //
  575. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  576. //
  577. // Returns: none
  578. //
  579. // Notes:
  580. //
  581. //
  582. //--
  583. NTSTATUS
  584. KerbSpnSubstringMatch(
  585. IN PKERB_INTERNAL_NAME Spn,
  586. IN OUT PUNICODE_STRING TargetRealm
  587. )
  588. {
  589. UNICODE_STRING RemainingParts ={0};
  590. NTSTATUS Status = STATUS_SUCCESS;
  591. PHOST_TO_REALM_KEY MatchedRealm = NULL;
  592. PWCHAR FreeMe;
  593. //
  594. // The SPN must have at least 2 parts (host / machine . realm )
  595. //
  596. if ( !HostToRealmUsed || Spn->NameCount < 2 )
  597. {
  598. Status = STATUS_NO_MATCH;
  599. return Status;
  600. }
  601. //
  602. // Try first component.
  603. //
  604. SafeAllocaAllocate( RemainingParts.Buffer, Spn->Names[1].MaximumLength );
  605. if ( RemainingParts.Buffer == NULL)
  606. {
  607. return STATUS_NO_MEMORY;
  608. }
  609. FreeMe = RemainingParts.Buffer; // note: KerbParseDnsName will move buffer ptr.
  610. RtlCopyMemory(
  611. RemainingParts.Buffer,
  612. Spn->Names[1].Buffer,
  613. Spn->Names[1].MaximumLength
  614. );
  615. RemainingParts.Length = Spn->Names[1].Length;
  616. RemainingParts.MaximumLength = Spn->Names[1].MaximumLength;
  617. //
  618. // All realms are stored in UPPERCASE in the table.
  619. //
  620. if (!NT_SUCCESS(RtlUpcaseUnicodeString(&RemainingParts,&Spn->Names[1],FALSE)))
  621. {
  622. SafeAllocaFree( FreeMe );
  623. return STATUS_INTERNAL_ERROR;
  624. }
  625. HostToRealmReadLock();
  626. do
  627. {
  628. MatchedRealm = (PHOST_TO_REALM_KEY) RtlLookupElementGenericTableAvl(
  629. &HostToRealmTable,
  630. &RemainingParts
  631. );
  632. } while (( MatchedRealm == NULL ) && KerbParseDnsName(&RemainingParts));
  633. SafeAllocaFree( FreeMe );
  634. if ( MatchedRealm == NULL )
  635. {
  636. D_DebugLog((DEB_TRACE_SPN_CACHE, "Missed cache for %wZ\n", &Spn->Names[1]));
  637. HostToRealmUnlock();
  638. return STATUS_NO_MATCH;
  639. }
  640. D_DebugLog((DEB_TRACE_SPN_CACHE, "HIT cache for %wZ\n", &Spn->Names[1]));
  641. D_DebugLog((DEB_TRACE_SPN_CACHE, "Realm %wZ\n", &MatchedRealm->TargetRealm));
  642. Status = KerbDuplicateString(
  643. TargetRealm,
  644. &MatchedRealm->TargetRealm
  645. );
  646. HostToRealmUnlock();
  647. return Status;
  648. }
  649. //+-------------------------------------------------------------------------
  650. //
  651. // Function: KerbCreateSpnMappings
  652. //
  653. // Synopsis: Uses registry to create
  654. //
  655. // Effects: Increments the reference count on the spn cache entry
  656. //
  657. // Arguments: SpnCacheEntry - spn cache entry to reference
  658. //
  659. // Requires: The spn cache must be locked
  660. //
  661. // Returns: none
  662. //
  663. // Notes:
  664. //
  665. //
  666. //--------------------------------------------------------------------------
  667. #define MAX_KEY_NAME 512
  668. VOID
  669. KerbCreateHostToRealmMappings()
  670. {
  671. DWORD WinError, KeyIndex = 0;
  672. NTSTATUS Status = STATUS_SUCCESS;
  673. HKEY Key = NULL, SubKey = NULL;
  674. WCHAR KeyName[MAX_KEY_NAME];
  675. DWORD KeyNameSize = MAX_KEY_NAME;
  676. if (!HostToRealmInitialized)
  677. {
  678. DsysAssert(FALSE);
  679. return;
  680. }
  681. HostToRealmWriteLock();
  682. WinError = RegOpenKeyEx(
  683. HKEY_LOCAL_MACHINE,
  684. KERB_HOST_TO_REALM_KEY,
  685. 0,
  686. KEY_READ,
  687. &Key
  688. );
  689. if (WinError != ERROR_SUCCESS)
  690. {
  691. goto Cleanup;
  692. }
  693. while (TRUE)
  694. {
  695. WinError = RegEnumKeyExW(
  696. Key,
  697. KeyIndex,
  698. KeyName,
  699. &KeyNameSize,
  700. NULL,
  701. NULL,
  702. NULL,
  703. NULL
  704. );
  705. if (WinError != ERROR_SUCCESS)
  706. {
  707. D_DebugLog((DEB_ERROR, "RegEnumKeyExW failed - %x\n", WinError));
  708. goto Cleanup;
  709. }
  710. WinError = RegOpenKeyExW(
  711. Key,
  712. KeyName,
  713. 0,
  714. KEY_READ,
  715. &SubKey
  716. );
  717. if (WinError != ERROR_SUCCESS)
  718. {
  719. goto Cleanup;
  720. }
  721. Status = KerbAddHostToRealmMapping(
  722. SubKey,
  723. KeyName
  724. );
  725. if (!NT_SUCCESS(Status))
  726. {
  727. DebugLog((DEB_ERROR,"KerbAddHostToRealmMapping failed! - %x\n", Status));
  728. goto LocalCleanup;
  729. }
  730. LocalCleanup:
  731. if (SubKey != NULL)
  732. {
  733. RegCloseKey(SubKey);
  734. SubKey = NULL;
  735. }
  736. KeyIndex++;
  737. KeyNameSize = MAX_KEY_NAME;
  738. } // ** WHILE **
  739. Cleanup:
  740. if (SubKey != NULL)
  741. {
  742. RegCloseKey(SubKey);
  743. }
  744. if (Key != NULL)
  745. {
  746. RegCloseKey(Key);
  747. }
  748. HostToRealmUnlock();
  749. }
  750. //+-------------------------------------------------------------------------
  751. //
  752. // Function: KerbCleanupResult
  753. //
  754. // Synopsis: Cleans up result entry
  755. //
  756. // Effects:
  757. //
  758. // Arguments:
  759. //
  760. // Requires:
  761. //
  762. // Returns: none
  763. //
  764. // Notes:
  765. //
  766. //
  767. //+-------------------------------------------------------------------------
  768. VOID
  769. KerbCleanupResult(
  770. IN PSPN_CACHE_RESULT Result
  771. )
  772. {
  773. KerbFreeString(&Result->AccountRealm);
  774. KerbFreeString(&Result->TargetRealm);
  775. }
  776. //+-------------------------------------------------------------------------
  777. //
  778. // Function: KerbPurgeResultByIndex
  779. //
  780. // Synopsis: Removes
  781. //
  782. // Effects: Dereferences the spn cache entry to make it go away
  783. // when it is no longer being used.
  784. //
  785. // Arguments: decrements reference count and delets cache entry if it goes
  786. // to zero
  787. //
  788. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  789. //
  790. // Returns: none
  791. //
  792. // Notes:
  793. //
  794. //
  795. //+-------------------------------------------------------------------------
  796. VOID
  797. KerbPurgeResultByIndex(
  798. IN PKERB_SPN_CACHE_ENTRY CacheEntry,
  799. IN ULONG IndexToPurge
  800. )
  801. {
  802. ULONG i;
  803. DebugLog((DEB_ERROR, "Purging %p, %i\n", CacheEntry, IndexToPurge));
  804. KerbCleanupResult(&CacheEntry->Results[IndexToPurge]);
  805. CacheEntry->ResultCount--;
  806. for (i = IndexToPurge; i < CacheEntry->ResultCount; i++)
  807. {
  808. CacheEntry->Results[i] = CacheEntry->Results[i+1];
  809. }
  810. //
  811. // Zero out fields in last entry so we don't leak on an error path (or free
  812. // bogus info) if we reuse the entry...
  813. //
  814. RtlZeroMemory(
  815. &CacheEntry->Results[i],
  816. sizeof(SPN_CACHE_RESULT)
  817. );
  818. }
  819. //+-------------------------------------------------------------------------
  820. //
  821. // Function: KerbDereferenceSpnCacheEntry
  822. //
  823. // Synopsis: Dereferences a spn cache entry
  824. //
  825. // Effects: Dereferences the spn cache entry to make it go away
  826. // when it is no longer being used.
  827. //
  828. // Arguments: decrements reference count and delets cache entry if it goes
  829. // to zero
  830. //
  831. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  832. //
  833. // Returns: none
  834. //
  835. // Notes:
  836. //
  837. //
  838. //--------------------------------------------------------------------------
  839. VOID
  840. KerbDereferenceSpnCacheEntry(
  841. IN PKERB_SPN_CACHE_ENTRY SpnCacheEntry
  842. )
  843. {
  844. if (KerbDereferenceListEntry(
  845. &SpnCacheEntry->ListEntry,
  846. &KerbSpnCache
  847. ) )
  848. {
  849. KerbFreeSpnCacheEntry(SpnCacheEntry);
  850. }
  851. }
  852. //+-------------------------------------------------------------------------
  853. //
  854. // Function: KerbReferenceSpnCacheEntry
  855. //
  856. // Synopsis: References a spn cache entry
  857. //
  858. // Effects: Increments the reference count on the spn cache entry
  859. //
  860. // Arguments: SpnCacheEntry - spn cache entry to reference
  861. //
  862. // Requires: The spn cache must be locked
  863. //
  864. // Returns: none
  865. //
  866. // Notes:
  867. //
  868. //
  869. //--------------------------------------------------------------------------
  870. VOID
  871. KerbReferenceSpnCacheEntry(
  872. IN PKERB_SPN_CACHE_ENTRY SpnCacheEntry,
  873. IN BOOLEAN RemoveFromList
  874. )
  875. {
  876. KerbWriteLockSpnCache();
  877. KerbReferenceListEntry(
  878. &KerbSpnCache,
  879. &SpnCacheEntry->ListEntry,
  880. RemoveFromList
  881. );
  882. KerbUnlockSpnCache();
  883. }
  884. //+-------------------------------------------------------------------------
  885. //
  886. // Function: KerbAgeResults
  887. //
  888. // Synopsis: Ages out a given cache entry's result list. Used
  889. // to reduce the result list to a manageable size, and
  890. // as a scavenger to cleanup orphaned / unused entries.
  891. //
  892. // Effects: Increments the reference count on the spn cache entry
  893. //
  894. // Arguments: SpnCacheEntry - spn cache entry to reference
  895. //
  896. // Requires: The spn cache must be locked
  897. //
  898. // Returns: none
  899. //
  900. // Notes:
  901. //
  902. //
  903. //+-------------------------------------------------------------------------
  904. VOID
  905. KerbAgeResults(
  906. IN PKERB_SPN_CACHE_ENTRY CacheEntry
  907. )
  908. {
  909. TimeStamp CurrentTime, BackoffTime;
  910. ULONG i;
  911. LONG Interval;
  912. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime);
  913. //
  914. // Age out everything older than GlobalSpnCacheTimeout first.
  915. //
  916. for ( i = 0; i < CacheEntry->ResultCount; i++ )
  917. {
  918. if (KerbGetTime(CacheEntry->Results[i].CacheStartTime) + KerbGetTime(KerbGlobalSpnCacheTimeout) < KerbGetTime(CurrentTime))
  919. {
  920. D_DebugLog((DEB_TRACE_SPN_CACHE, "removing %x %p\n"));
  921. KerbPurgeResultByIndex(CacheEntry, i);
  922. }
  923. }
  924. if ( CacheEntry->ResultCount < MAX_RESULTS )
  925. {
  926. return;
  927. }
  928. for ( Interval = 13; Interval > 0; Interval -= 4)
  929. {
  930. KerbSetTimeInMinutes(&BackoffTime, Interval);
  931. for ( i=0; i < CacheEntry->ResultCount ; i++ )
  932. {
  933. if (KerbGetTime(CacheEntry->Results[i].CacheStartTime) + KerbGetTime(BackoffTime) < KerbGetTime(CurrentTime))
  934. {
  935. D_DebugLog((DEB_TRACE_SPN_CACHE, "removing %x %p\n"));
  936. KerbPurgeResultByIndex(CacheEntry, i);
  937. }
  938. }
  939. if ( CacheEntry->ResultCount < MAX_RESULTS )
  940. {
  941. return;
  942. }
  943. }
  944. //
  945. // Still have MAX_RESULTS after all that geezzz..
  946. //
  947. DebugLog((DEB_ERROR, "Can't get below MAX_RESULTS (%p) \n", CacheEntry ));
  948. DsysAssert(FALSE);
  949. for ( i=0; i < CacheEntry->ResultCount ; i++ )
  950. {
  951. KerbPurgeResultByIndex(CacheEntry, i);
  952. }
  953. return;
  954. }
  955. //+-------------------------------------------------------------------------
  956. //
  957. // Function: KerbTaskSpnCacheScavenger
  958. //
  959. // Synopsis: Cleans up any old SPN cache entries. Triggered by 30 minute
  960. // task.
  961. //
  962. // Effects:
  963. //
  964. // Arguments: SpnCacheEntry - spn cache entry to reference
  965. //
  966. // Requires: The spn cache entry must be locked
  967. //
  968. // Returns: none
  969. //
  970. // Notes:
  971. //
  972. //
  973. //
  974. VOID
  975. KerbSpnCacheScavenger()
  976. {
  977. PKERB_SPN_CACHE_ENTRY CacheEntry = NULL;
  978. PLIST_ENTRY ListEntry;
  979. BOOLEAN FreeMe = FALSE;
  980. KerbWriteLockSpnCache();
  981. for (ListEntry = KerbSpnCache.List.Flink ;
  982. ListEntry != &KerbSpnCache.List ;
  983. ListEntry = ListEntry->Flink )
  984. {
  985. CacheEntry = CONTAINING_RECORD(ListEntry, KERB_SPN_CACHE_ENTRY, ListEntry.Next);
  986. KerbWriteLockSpnCacheEntry( CacheEntry );
  987. KerbAgeResults(CacheEntry);
  988. //
  989. // Time to pull this one from list.
  990. //
  991. if ( CacheEntry->ResultCount == 0 )
  992. {
  993. ListEntry = ListEntry->Blink;
  994. KerbReferenceSpnCacheEntry(
  995. CacheEntry,
  996. TRUE
  997. );
  998. FreeMe = TRUE;
  999. }
  1000. KerbUnlockSpnCacheEntry( CacheEntry );
  1001. //
  1002. // Pull the list reference.
  1003. //
  1004. if ( FreeMe )
  1005. {
  1006. KerbDereferenceSpnCacheEntry( CacheEntry );
  1007. FreeMe = FALSE;
  1008. }
  1009. }
  1010. KerbUnlockSpnCache();
  1011. }
  1012. //+-------------------------------------------------------------------------
  1013. //
  1014. // Function: KerbAddCacheResult
  1015. //
  1016. // Synopsis: Uses registry to create
  1017. //
  1018. // Effects: Increments the reference count on the spn cache entry
  1019. //
  1020. // Arguments: SpnCacheEntry - spn cache entry to reference
  1021. //
  1022. // Requires: The spn cache resource must be locked
  1023. //
  1024. // Returns: none
  1025. //
  1026. // Notes:
  1027. //
  1028. //
  1029. //--------------------------------------------------------------------------
  1030. NTSTATUS
  1031. KerbAddCacheResult(
  1032. IN PKERB_SPN_CACHE_ENTRY CacheEntry,
  1033. IN PKERB_PRIMARY_CREDENTIAL AccountCredential,
  1034. IN ULONG UpdateFlags,
  1035. IN OPTIONAL PUNICODE_STRING NewRealm
  1036. )
  1037. {
  1038. NTSTATUS Status = STATUS_SUCCESS;
  1039. PSPN_CACHE_RESULT Result = NULL;
  1040. 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));
  1041. D_KerbPrintKdcName((DEB_TRACE_SPN_CACHE, CacheEntry->Spn));
  1042. //
  1043. // If we don't have an account realm w/ this credential (e.g someone
  1044. // supplied you a UPN to acquirecredentialshandle, don't use the
  1045. // spn cache.
  1046. //
  1047. if ( AccountCredential->DomainName.Length == 0 )
  1048. {
  1049. return STATUS_NOT_SUPPORTED;
  1050. }
  1051. //
  1052. // First off, see if we're hitting the limits for our array.
  1053. // We shouldn't ever get close to MAX_RESULTS, but if we do,
  1054. // age out the least current CacheResult.
  1055. //
  1056. if ( (CacheEntry->ResultCount + 1) == MAX_RESULTS )
  1057. {
  1058. KerbAgeResults(CacheEntry);
  1059. }
  1060. Result = &CacheEntry->Results[CacheEntry->ResultCount];
  1061. Status = KerbDuplicateStringEx(
  1062. &Result->AccountRealm,
  1063. &AccountCredential->DomainName,
  1064. FALSE
  1065. );
  1066. if (!NT_SUCCESS( Status ))
  1067. {
  1068. goto Cleanup;
  1069. }
  1070. if (ARGUMENT_PRESENT( NewRealm ))
  1071. {
  1072. D_DebugLog((DEB_TRACE_SPN_CACHE, "Known - realm %wZ\n", NewRealm));
  1073. DsysAssert(UpdateFlags != KERB_SPN_UNKNOWN);
  1074. Status = KerbDuplicateStringEx(
  1075. &Result->TargetRealm,
  1076. NewRealm,
  1077. FALSE
  1078. );
  1079. if (!NT_SUCCESS( Status ))
  1080. {
  1081. goto Cleanup;
  1082. }
  1083. }
  1084. #if DBG
  1085. else
  1086. {
  1087. DsysAssert(UpdateFlags != KERB_SPN_KNOWN);
  1088. }
  1089. #endif
  1090. Result->CacheFlags = UpdateFlags;
  1091. GetSystemTimeAsFileTime((PFILETIME) &Result->CacheStartTime);
  1092. CacheEntry->ResultCount++;
  1093. Cleanup:
  1094. if (!NT_SUCCESS( Status ) )
  1095. {
  1096. if ( Result != NULL )
  1097. {
  1098. KerbCleanupResult( Result );
  1099. }
  1100. }
  1101. return Status;
  1102. }
  1103. //+-------------------------------------------------------------------------
  1104. //
  1105. // Function: KerbBuildSpnCacheEntry
  1106. //
  1107. // Synopsis: Builds a spn cache entry
  1108. //
  1109. // Effects:
  1110. //
  1111. // Arguments:
  1112. //
  1113. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  1114. //
  1115. // Returns: none
  1116. //
  1117. // Notes:
  1118. //
  1119. //
  1120. //--------------------------------------------------------------------------
  1121. NTSTATUS
  1122. KerbCreateSpnCacheEntry(
  1123. IN PKERB_INTERNAL_NAME Spn,
  1124. IN PKERB_PRIMARY_CREDENTIAL AccountCredential,
  1125. IN ULONG UpdateFlags,
  1126. IN OPTIONAL PUNICODE_STRING NewRealm,
  1127. IN OUT PKERB_SPN_CACHE_ENTRY* NewCacheEntry
  1128. )
  1129. {
  1130. NTSTATUS Status;
  1131. PKERB_SPN_CACHE_ENTRY CacheEntry = NULL;
  1132. BOOLEAN FreeResource = FALSE;
  1133. *NewCacheEntry = NULL;
  1134. CacheEntry = (PKERB_SPN_CACHE_ENTRY) KerbAllocate( sizeof(KERB_SPN_CACHE_ENTRY) );
  1135. if ( CacheEntry == NULL )
  1136. {
  1137. Status = STATUS_NO_MEMORY;
  1138. goto Cleanup;
  1139. }
  1140. Status = KerbDuplicateKdcName(
  1141. &CacheEntry->Spn,
  1142. Spn
  1143. );
  1144. if (!NT_SUCCESS(Status))
  1145. {
  1146. goto Cleanup;
  1147. }
  1148. Status = KerbAddCacheResult(
  1149. CacheEntry,
  1150. AccountCredential,
  1151. UpdateFlags,
  1152. NewRealm
  1153. );
  1154. if (!NT_SUCCESS( Status ))
  1155. {
  1156. goto Cleanup;
  1157. }
  1158. KerbInitializeListEntry( &CacheEntry->ListEntry );
  1159. __try
  1160. {
  1161. RtlInitializeResource( &CacheEntry->ResultLock );
  1162. }
  1163. __except(EXCEPTION_EXECUTE_HANDLER)
  1164. {
  1165. Status = STATUS_INSUFFICIENT_RESOURCES;
  1166. goto Cleanup;
  1167. }
  1168. FreeResource = TRUE;
  1169. KerbInsertSpnCacheEntry(CacheEntry);
  1170. *NewCacheEntry = CacheEntry;
  1171. CacheEntry = NULL;
  1172. InterlockedIncrement( &SpnCount );
  1173. Cleanup:
  1174. if (!NT_SUCCESS(Status) && ( CacheEntry ))
  1175. {
  1176. KerbCleanupResult(&CacheEntry->Results[0]);
  1177. KerbFreeKdcName( &CacheEntry->Spn );
  1178. if ( FreeResource )
  1179. {
  1180. RtlDeleteResource( &CacheEntry->ResultLock );
  1181. }
  1182. KerbFree(CacheEntry);
  1183. }
  1184. return Status;
  1185. }
  1186. //+-------------------------------------------------------------------------
  1187. //
  1188. // Function: KerbScanResults
  1189. //
  1190. // Synopsis: Scans result list.
  1191. //
  1192. // Effects:
  1193. //
  1194. // Arguments:
  1195. //
  1196. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  1197. //
  1198. // Returns: none
  1199. //
  1200. // Notes:
  1201. //
  1202. //
  1203. //---
  1204. BOOLEAN
  1205. KerbScanResults(
  1206. IN PKERB_SPN_CACHE_ENTRY CacheEntry,
  1207. IN PUNICODE_STRING Realm,
  1208. IN OUT PULONG Index
  1209. )
  1210. {
  1211. BOOLEAN Found = FALSE;
  1212. ULONG i;
  1213. for ( i=0; i < CacheEntry->ResultCount; i++)
  1214. {
  1215. if (RtlEqualUnicodeString(
  1216. &CacheEntry->Results[i].AccountRealm,
  1217. Realm,
  1218. TRUE
  1219. ))
  1220. {
  1221. Found = TRUE;
  1222. *Index = i;
  1223. break;
  1224. }
  1225. }
  1226. return Found;
  1227. }
  1228. //+-------------------------------------------------------------------------
  1229. //
  1230. // Function: KerbUpdateSpnCacheEntry
  1231. //
  1232. // Synopsis: Updates a spn cache entry
  1233. //
  1234. // Effects:
  1235. //
  1236. // Arguments:
  1237. //
  1238. // Requires: SpnCacheEntry - The spn cache entry to dereference.
  1239. //
  1240. // Returns: none
  1241. //
  1242. // Notes:
  1243. //
  1244. //
  1245. //--------------------------------------------------------------------------
  1246. NTSTATUS
  1247. KerbUpdateSpnCacheEntry(
  1248. IN OPTIONAL PKERB_SPN_CACHE_ENTRY ExistingCacheEntry,
  1249. IN PKERB_INTERNAL_NAME Spn,
  1250. IN PKERB_PRIMARY_CREDENTIAL AccountCredential,
  1251. IN ULONG UpdateFlags,
  1252. IN OPTIONAL PUNICODE_STRING NewRealm
  1253. )
  1254. {
  1255. PKERB_SPN_CACHE_ENTRY CacheEntry = ExistingCacheEntry;
  1256. NTSTATUS Status = STATUS_SUCCESS;
  1257. BOOLEAN Found = FALSE, Update = FALSE;
  1258. ULONG Index = 0;
  1259. //
  1260. // We're not using SPN cache
  1261. //
  1262. if (KerbGlobalSpnCacheTimeout.QuadPart == 0 || !KerberosSpnCacheInitialized )
  1263. {
  1264. return STATUS_SUCCESS;
  1265. }
  1266. //
  1267. // If we didn't have a cache entry before, see if we do now, or create
  1268. // one if necessary.
  1269. //
  1270. if (!ARGUMENT_PRESENT( ExistingCacheEntry ))
  1271. {
  1272. KerbWriteLockSpnCache();
  1273. CacheEntry = KerbLocateSpnCacheEntry( Spn );
  1274. if ( CacheEntry == NULL)
  1275. {
  1276. Status = KerbCreateSpnCacheEntry(
  1277. Spn,
  1278. AccountCredential,
  1279. UpdateFlags,
  1280. NewRealm,
  1281. &CacheEntry
  1282. );
  1283. if (NT_SUCCESS(Status))
  1284. {
  1285. //
  1286. // All done, get outta here.
  1287. //
  1288. D_DebugLog((DEB_TRACE_SPN_CACHE, "Created new cache entry %p (%x) \n", CacheEntry, UpdateFlags));
  1289. D_KerbPrintKdcName((DEB_TRACE_SPN_CACHE, Spn));
  1290. D_DebugLog((DEB_TRACE_SPN_CACHE, "%wZ\n", &AccountCredential->DomainName));
  1291. KerbDereferenceSpnCacheEntry( CacheEntry );
  1292. }
  1293. KerbUnlockSpnCache();
  1294. return Status;
  1295. }
  1296. KerbUnlockSpnCache();
  1297. }
  1298. //
  1299. // Got an existing entry - update it.
  1300. //
  1301. KerbReadLockSpnCacheEntry( CacheEntry );
  1302. if (KerbScanResults(
  1303. CacheEntry,
  1304. &AccountCredential->DomainName,
  1305. &Index
  1306. ))
  1307. {
  1308. Found = TRUE;
  1309. Update = (( CacheEntry->Results[Index].CacheFlags & UpdateFlags) != UpdateFlags);
  1310. }
  1311. KerbUnlockSpnCacheEntry( CacheEntry );
  1312. //
  1313. // To avoid always taking the write lock, we'll need to rescan the result
  1314. // list under a write lock.
  1315. //
  1316. if ( Update )
  1317. {
  1318. KerbWriteLockSpnCacheEntry( CacheEntry );
  1319. if (KerbScanResults(
  1320. CacheEntry,
  1321. &AccountCredential->DomainName,
  1322. &Index
  1323. ))
  1324. {
  1325. //
  1326. // Hasn't been updated or removed in the small time slice above. Update.
  1327. //
  1328. if (( CacheEntry->Results[Index].CacheFlags & UpdateFlags) != UpdateFlags )
  1329. {
  1330. D_DebugLog((
  1331. DEB_TRACE_SPN_CACHE,
  1332. "KerbUpdateSpnCacheEntry changing _KERB_SPN_CACHE_ENTRY %p Result Index %#x: AccountRealm %wZ, TargetRealm %wZ, NewRealm %wZ, CacheFlags %#x to CacheFlags %#x for ",
  1333. CacheEntry,
  1334. Index,
  1335. &CacheEntry->Results[Index].AccountRealm,
  1336. &CacheEntry->Results[Index].TargetRealm,
  1337. NewRealm,
  1338. CacheEntry->Results[Index].CacheFlags,
  1339. UpdateFlags
  1340. ));
  1341. D_KerbPrintKdcName((DEB_TRACE_SPN_CACHE, CacheEntry->Spn));
  1342. CacheEntry->Results[Index].CacheFlags = UpdateFlags;
  1343. GetSystemTimeAsFileTime( (LPFILETIME) &CacheEntry->Results[Index].CacheStartTime );
  1344. KerbFreeString(&CacheEntry->Results[Index].TargetRealm);
  1345. if (ARGUMENT_PRESENT( NewRealm ))
  1346. {
  1347. DsysAssert( UpdateFlags == KERB_SPN_KNOWN );
  1348. Status = KerbDuplicateStringEx(
  1349. &CacheEntry->Results[Index].TargetRealm,
  1350. NewRealm,
  1351. FALSE
  1352. );
  1353. if (!NT_SUCCESS( Status ))
  1354. {
  1355. KerbUnlockSpnCacheEntry( CacheEntry );
  1356. goto Cleanup;
  1357. }
  1358. }
  1359. }
  1360. }
  1361. else
  1362. {
  1363. Found = FALSE;
  1364. }
  1365. KerbUnlockSpnCacheEntry( CacheEntry );
  1366. }
  1367. if (!Found)
  1368. {
  1369. KerbWriteLockSpnCacheEntry ( CacheEntry );
  1370. //
  1371. // Still not found
  1372. //
  1373. if (!KerbScanResults( CacheEntry, &AccountCredential->DomainName, &Index ))
  1374. {
  1375. Status = KerbAddCacheResult(
  1376. CacheEntry,
  1377. AccountCredential,
  1378. UpdateFlags,
  1379. NewRealm
  1380. );
  1381. }
  1382. KerbUnlockSpnCacheEntry( CacheEntry );
  1383. if (!NT_SUCCESS(Status))
  1384. {
  1385. goto Cleanup;
  1386. }
  1387. }
  1388. Cleanup:
  1389. //
  1390. // Created a new cache entry, referenced w/i this function.
  1391. //
  1392. if (!ARGUMENT_PRESENT( ExistingCacheEntry ) && CacheEntry )
  1393. {
  1394. KerbDereferenceSpnCacheEntry( CacheEntry );
  1395. }
  1396. return Status;
  1397. }
  1398. //+-------------------------------------------------------------------------
  1399. //
  1400. // Function: KerbLocateSpnCacheEntry
  1401. //
  1402. // Synopsis: References a spn cache entry by name
  1403. //
  1404. // Effects: Increments the reference count on the spn cache entry
  1405. //
  1406. // Arguments: RealmName - Contains the name of the realm for which to
  1407. // obtain a binding handle.
  1408. // DesiredFlags - Flags desired for binding, such as PDC required
  1409. // RemoveFromList - Remove cache entry from cache when found.
  1410. //
  1411. // Requires:
  1412. //
  1413. // Returns: The referenced cache entry or NULL if it was not found.
  1414. //
  1415. // Notes: If an invalid entry is found it may be dereferenced
  1416. //
  1417. //
  1418. //--------------------------------------------------------------------------
  1419. PKERB_SPN_CACHE_ENTRY
  1420. KerbLocateSpnCacheEntry(
  1421. IN PKERB_INTERNAL_NAME Spn
  1422. )
  1423. {
  1424. PLIST_ENTRY ListEntry;
  1425. PKERB_SPN_CACHE_ENTRY CacheEntry = NULL;
  1426. BOOLEAN Found = FALSE;
  1427. if (Spn->NameType != KRB_NT_SRV_INST)
  1428. {
  1429. return NULL;
  1430. }
  1431. #if DBG
  1432. KerbLogSpnStats(Spn);
  1433. #endif
  1434. //
  1435. // We're not using SPN cache
  1436. //
  1437. if (KerbGlobalSpnCacheTimeout.QuadPart == 0 || !KerberosSpnCacheInitialized )
  1438. {
  1439. return NULL;
  1440. }
  1441. //
  1442. // Scale the cache by aging out old entries.
  1443. //
  1444. if ( SpnCount > MAX_CACHE_ENTRIES )
  1445. {
  1446. KerbSpnCacheScavenger();
  1447. }
  1448. KerbReadLockSpnCache();
  1449. //
  1450. // Go through the spn cache looking for the correct entry
  1451. //
  1452. for (ListEntry = KerbSpnCache.List.Flink ;
  1453. ListEntry != &KerbSpnCache.List ;
  1454. ListEntry = ListEntry->Flink )
  1455. {
  1456. CacheEntry = CONTAINING_RECORD(ListEntry, KERB_SPN_CACHE_ENTRY, ListEntry.Next);
  1457. if (KerbEqualKdcNames(CacheEntry->Spn,Spn))
  1458. {
  1459. KerbReferenceSpnCacheEntry(
  1460. CacheEntry,
  1461. FALSE
  1462. );
  1463. D_DebugLog((DEB_TRACE_SPN_CACHE, "SpnCacheEntry %p\n", CacheEntry));
  1464. Found = TRUE;
  1465. break;
  1466. }
  1467. }
  1468. if (!Found)
  1469. {
  1470. CacheEntry = NULL;
  1471. }
  1472. KerbUnlockSpnCache();
  1473. return(CacheEntry);
  1474. }
  1475. //+-------------------------------------------------------------------------
  1476. //
  1477. // Function: KerbCleanupResultList
  1478. //
  1479. // Synopsis: Frees memory associated with a result list
  1480. //
  1481. // Effects:
  1482. //
  1483. // Arguments: SpnCacheEntry - The cache entry to free. It must be
  1484. // unlinked, and the Resultlock must be held.
  1485. //
  1486. // Requires:
  1487. //
  1488. // Returns: none
  1489. //
  1490. // Notes:
  1491. //
  1492. //
  1493. //--------------------------------------------------------------------------
  1494. VOID
  1495. KerbCleanupResultList(
  1496. IN PKERB_SPN_CACHE_ENTRY CacheEntry
  1497. )
  1498. {
  1499. for (ULONG i = 0; i < CacheEntry->ResultCount; i++)
  1500. {
  1501. KerbCleanupResult(&CacheEntry->Results[i]);
  1502. }
  1503. CacheEntry->ResultCount = 0;
  1504. }
  1505. //+-------------------------------------------------------------------------
  1506. //
  1507. // Function: KerbFreeSpnCacheEntry
  1508. //
  1509. // Synopsis: Frees memory associated with a spn cache entry
  1510. //
  1511. // Effects:
  1512. //
  1513. // Arguments: SpnCacheEntry - The cache entry to free. It must be
  1514. // unlinked.
  1515. //
  1516. // Requires:
  1517. //
  1518. // Returns: none
  1519. //
  1520. // Notes:
  1521. //
  1522. //
  1523. //--------------------------------------------------------------------------
  1524. VOID
  1525. KerbFreeSpnCacheEntry(
  1526. IN PKERB_SPN_CACHE_ENTRY SpnCacheEntry
  1527. )
  1528. {
  1529. //
  1530. // Must be unlinked..
  1531. //
  1532. DsysAssert(SpnCacheEntry->ListEntry.Next.Flink == NULL);
  1533. DsysAssert(SpnCacheEntry->ListEntry.Next.Blink == NULL);
  1534. KerbWriteLockSpnCacheEntry(SpnCacheEntry);
  1535. KerbCleanupResultList(SpnCacheEntry);
  1536. KerbUnlockSpnCacheEntry(SpnCacheEntry);
  1537. RtlDeleteResource(&SpnCacheEntry->ResultLock);
  1538. KerbFreeKdcName(&SpnCacheEntry->Spn);
  1539. KerbFree(SpnCacheEntry);
  1540. InterlockedDecrement( &SpnCount );
  1541. }
  1542. //+-------------------------------------------------------------------------
  1543. //
  1544. // Function: KerbInsertBinding
  1545. //
  1546. // Synopsis: Inserts a binding into the spn cache
  1547. //
  1548. // Effects: bumps reference count on binding
  1549. //
  1550. // Arguments: CacheEntry - Cache entry to insert
  1551. //
  1552. // Requires:
  1553. //
  1554. // Returns: STATUS_SUCCESS always
  1555. //
  1556. // Notes:
  1557. //
  1558. //
  1559. //--------------------------------------------------------------------------
  1560. NTSTATUS
  1561. KerbInsertSpnCacheEntry(
  1562. IN PKERB_SPN_CACHE_ENTRY CacheEntry
  1563. )
  1564. {
  1565. IF_DEBUG(DISABLE_SPN_CACHE)
  1566. {
  1567. DebugLog((DEB_TRACE_SPN_CACHE, "KerbInsertSpnCacheEntry spn cache disabled\n"));
  1568. return STATUS_SUCCESS;
  1569. }
  1570. KerbInsertListEntry(
  1571. &CacheEntry->ListEntry,
  1572. &KerbSpnCache
  1573. );
  1574. return(STATUS_SUCCESS);
  1575. }
  1576. //+-------------------------------------------------------------------------
  1577. //
  1578. // Function: KerbGetSpnCacheStatus
  1579. //
  1580. // Synopsis: Gets the status of a cache entry for a given realm.
  1581. //
  1582. // Effects: Returns STATUS_NO_SAM_TRUST_RELATIONSHIP for unknown SPNs,
  1583. // STATUS_NO_MATCH, if we're missing a realm result, or
  1584. // STATUS_SUCCESS ++ dupe the SPNREalm for the "real" realm
  1585. // of the SPN relative to the account realm.
  1586. //
  1587. // Arguments: CacheEntry - SPN cache entry from ProcessTargetName()
  1588. // Credential - Primary cred for account realm.
  1589. // SpnRealm - IN OUT Filled in w/ target realm of SPN
  1590. //
  1591. //
  1592. // Requires:
  1593. //
  1594. // Returns:
  1595. //
  1596. // Notes:
  1597. //
  1598. //
  1599. //--------------------------------------------------------------------------
  1600. NTSTATUS
  1601. KerbGetSpnCacheStatus(
  1602. IN PKERB_SPN_CACHE_ENTRY CacheEntry,
  1603. IN PKERB_PRIMARY_CREDENTIAL Credential,
  1604. IN OUT PUNICODE_STRING SpnRealm
  1605. )
  1606. {
  1607. NTSTATUS Status = STATUS_NO_MATCH;;
  1608. ULONG i;
  1609. TimeStamp CurrentTime;
  1610. BOOLEAN Purge = FALSE;
  1611. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime);
  1612. //
  1613. // Read Lock the spn cache entry
  1614. //
  1615. KerbReadLockSpnCacheEntry( CacheEntry );
  1616. if (KerbScanResults(
  1617. CacheEntry,
  1618. &Credential->DomainName,
  1619. &i
  1620. ))
  1621. {
  1622. if (CacheEntry->Results[i].CacheFlags & KERB_SPN_UNKNOWN)
  1623. {
  1624. //
  1625. // Check and see if this timestamp has expired.
  1626. //
  1627. if (KerbGetTime(CacheEntry->Results[i].CacheStartTime) + KerbGetTime(KerbGlobalSpnCacheTimeout) < KerbGetTime(CurrentTime))
  1628. {
  1629. Purge = TRUE;
  1630. Status = STATUS_SUCCESS;
  1631. }
  1632. else
  1633. {
  1634. Status = STATUS_NO_TRUST_SAM_ACCOUNT;
  1635. DebugLog((DEB_WARN, "SPN not found\n"));
  1636. KerbPrintKdcName(DEB_WARN, CacheEntry->Spn);
  1637. }
  1638. }
  1639. else if (CacheEntry->Results[i].CacheFlags & KERB_SPN_KNOWN)
  1640. {
  1641. Status = KerbDuplicateStringEx(
  1642. SpnRealm,
  1643. &CacheEntry->Results[i].TargetRealm,
  1644. FALSE
  1645. );
  1646. D_DebugLog((DEB_TRACE_SPN_CACHE, "Found %wZ\n", SpnRealm));
  1647. D_KerbPrintKdcName((DEB_TRACE_SPN_CACHE, CacheEntry->Spn));
  1648. }
  1649. }
  1650. KerbUnlockSpnCacheEntry( CacheEntry );
  1651. if (!NT_SUCCESS( Status ))
  1652. {
  1653. return Status;
  1654. }
  1655. //
  1656. // Take the write lock, and verify that we still need to purge the above
  1657. // result.
  1658. //
  1659. if ( Purge )
  1660. {
  1661. KerbWriteLockSpnCacheEntry( CacheEntry );
  1662. if (KerbScanResults(
  1663. CacheEntry,
  1664. &Credential->DomainName,
  1665. &i
  1666. ))
  1667. {
  1668. if (KerbGetTime(CacheEntry->Results[i].CacheStartTime) +
  1669. KerbGetTime(KerbGlobalSpnCacheTimeout) < KerbGetTime(CurrentTime))
  1670. {
  1671. D_DebugLog((DEB_TRACE_SPN_CACHE, "Purging %p due to time\n", &CacheEntry->Results[i]));
  1672. KerbPurgeResultByIndex( CacheEntry, i );
  1673. }
  1674. }
  1675. KerbUnlockSpnCacheEntry( CacheEntry );
  1676. Status = STATUS_NO_MATCH;
  1677. }
  1678. return Status;
  1679. }