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.

1132 lines
27 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1996
  6. //
  7. // File: tktcache.cxx
  8. //
  9. // Contents: Ticket cache for Kerberos Package
  10. //
  11. //
  12. // History: 16-April-1996 Created MikeSw
  13. //
  14. //------------------------------------------------------------------------
  15. #include <kerb.hxx>
  16. #define TKTCACHE_ALLOCATE
  17. #include <kerbp.h>
  18. //
  19. // Statistics for trackign hits/misses in cache
  20. //
  21. #define UpdateCacheHits() (InterlockedIncrement(&KerbTicketCacheHits))
  22. #define UpdateCacheMisses() (InterlockedIncrement(&KerbTicketCacheMisses))
  23. //+-------------------------------------------------------------------------
  24. //
  25. // Function: KerbInitTicketCaching
  26. //
  27. // Synopsis: Initialize the ticket caching code
  28. //
  29. // Effects: Creates a RTL_RESOURCE
  30. //
  31. // Arguments: none
  32. //
  33. // Requires:
  34. //
  35. // Returns: STATUS_SUCCESS on success, NTSTATUS from Rtl routines
  36. // on error
  37. //
  38. // Notes:
  39. //
  40. //
  41. //--------------------------------------------------------------------------
  42. NTSTATUS
  43. KerbInitTicketCaching(
  44. VOID
  45. )
  46. {
  47. RtlInitializeResource(&KerberosTicketCacheLock);
  48. KerberosTicketCacheInitialized = TRUE;
  49. return(STATUS_SUCCESS);
  50. }
  51. //+-------------------------------------------------------------------------
  52. //
  53. // Function: KerbInitTicketCache
  54. //
  55. // Synopsis: Initializes a single ticket cache
  56. //
  57. // Effects: initializes list entry
  58. //
  59. // Arguments: TicketCache - The ticket cache to initialize
  60. //
  61. // Requires:
  62. //
  63. // Returns: none
  64. //
  65. // Notes:
  66. //
  67. //
  68. //--------------------------------------------------------------------------
  69. VOID
  70. KerbInitTicketCache(
  71. IN PKERB_TICKET_CACHE TicketCache
  72. )
  73. {
  74. InitializeListHead(&TicketCache->CacheEntries);
  75. }
  76. //+-------------------------------------------------------------------------
  77. //
  78. // Function: KerbFreeTicketCache
  79. //
  80. // Synopsis: Frees the ticket cache global data
  81. //
  82. // Effects:
  83. //
  84. // Arguments: none
  85. //
  86. // Requires:
  87. //
  88. // Returns: none
  89. //
  90. // Notes:
  91. //
  92. //
  93. //--------------------------------------------------------------------------
  94. VOID
  95. KerbFreeTicketCache(
  96. VOID
  97. )
  98. {
  99. // if (KerberosTicketCacheInitialized)
  100. // {
  101. // KerbWriteLockTicketCache();
  102. // RtlDeleteResource(&KerberosTicketCacheLock);
  103. // }
  104. }
  105. //+-------------------------------------------------------------------------
  106. //
  107. // Function: KerbFreeTicketCacheEntry
  108. //
  109. // Synopsis: Frees memory associated with a ticket cache entry
  110. //
  111. // Effects:
  112. //
  113. // Arguments: TicketCacheEntry - The cache entry to free. It must be
  114. // unlinked.
  115. //
  116. // Requires:
  117. //
  118. // Returns: none
  119. //
  120. // Notes:
  121. //
  122. //
  123. //--------------------------------------------------------------------------
  124. VOID
  125. KerbFreeTicketCacheEntry(
  126. IN PKERB_TICKET_CACHE_ENTRY TicketCacheEntry
  127. )
  128. {
  129. KerbFreeKdcName(&TicketCacheEntry->ServiceName);
  130. KerbFreeString(&TicketCacheEntry->DomainName);
  131. KerbFreeString(&TicketCacheEntry->TargetDomainName);
  132. KerbFreeString(&TicketCacheEntry->AltTargetDomainName);
  133. KerbFreeString(&TicketCacheEntry->ClientDomainName);
  134. KerbFreeKdcName(&TicketCacheEntry->ClientName);
  135. KerbFreeKdcName(&TicketCacheEntry->TargetName);
  136. KerbFreeDuplicatedTicket(&TicketCacheEntry->Ticket);
  137. KerbFreeKey(&TicketCacheEntry->SessionKey);
  138. KerbFree(TicketCacheEntry);
  139. }
  140. //+-------------------------------------------------------------------------
  141. //
  142. // Function: KerbDereferenceTicketCacheEntry
  143. //
  144. // Synopsis: Dereferences a ticket cache entry
  145. //
  146. // Effects: Dereferences the ticket 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: TicketCacheEntry - The ticket cache entry to dereference.
  153. //
  154. // Returns: none
  155. //
  156. // Notes:
  157. //
  158. //
  159. //--------------------------------------------------------------------------
  160. VOID
  161. KerbDereferenceTicketCacheEntry(
  162. IN PKERB_TICKET_CACHE_ENTRY TicketCacheEntry
  163. )
  164. {
  165. DsysAssert(TicketCacheEntry->ListEntry.ReferenceCount != 0);
  166. if ( 0 == InterlockedDecrement( (LONG *)&TicketCacheEntry->ListEntry.ReferenceCount )) {
  167. KerbFreeTicketCacheEntry(TicketCacheEntry);
  168. }
  169. return;
  170. }
  171. //+-------------------------------------------------------------------------
  172. //
  173. // Function: KerbReleaseTicketCacheEntry
  174. //
  175. // Synopsis: Release a ticket cache entry
  176. //
  177. // Effects:
  178. //
  179. // Arguments: decrements reference count and delets cache entry if it goes
  180. // to zero
  181. //
  182. // Requires: TicketCacheEntry - The ticket cache entry to dereference.
  183. //
  184. // Returns: none
  185. //
  186. // Notes:
  187. //
  188. //
  189. //--------------------------------------------------------------------------
  190. VOID
  191. KerbReleaseTicketCacheEntry(
  192. IN PKERB_TICKET_CACHE_ENTRY TicketCacheEntry
  193. )
  194. {
  195. if (TicketCacheEntry->Linked)
  196. {
  197. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  198. }
  199. else
  200. {
  201. KerbFreeTicketCacheEntry(TicketCacheEntry);
  202. }
  203. }
  204. //+-------------------------------------------------------------------------
  205. //
  206. // Function: KerbReferenceTicketCacheEntry
  207. //
  208. // Synopsis: References a ticket cache entry
  209. //
  210. // Effects: Increments the reference count on the ticket cache entry
  211. //
  212. // Arguments: TicketCacheEntry - Ticket cache entry to reference
  213. //
  214. // Returns: none
  215. //
  216. // Notes:
  217. //
  218. //
  219. //--------------------------------------------------------------------------
  220. VOID
  221. KerbReferenceTicketCacheEntry(
  222. IN PKERB_TICKET_CACHE_ENTRY TicketCacheEntry
  223. )
  224. {
  225. DsysAssert(TicketCacheEntry->ListEntry.ReferenceCount != 0);
  226. InterlockedIncrement( (LONG *)&TicketCacheEntry->ListEntry.ReferenceCount );
  227. }
  228. //+-------------------------------------------------------------------------
  229. //
  230. // Function: KerbInsertTicketCachEntry
  231. //
  232. // Synopsis: Inserts a ticket cache entry onto a ticket cache
  233. //
  234. // Effects:
  235. //
  236. // Arguments: TicketCache - The ticket cache on which to stick the ticket.
  237. // TicketCacheEntry - The entry to stick in the cache.
  238. //
  239. // Returns: none
  240. //
  241. // Notes:
  242. //
  243. //
  244. //--------------------------------------------------------------------------
  245. VOID
  246. KerbInsertTicketCacheEntry(
  247. IN PKERB_TICKET_CACHE TicketCache,
  248. IN PKERB_TICKET_CACHE_ENTRY TicketCacheEntry
  249. )
  250. {
  251. if ( !InterlockedCompareExchange(
  252. &TicketCacheEntry->Linked,
  253. (LONG)TRUE,
  254. (LONG)FALSE )) {
  255. InterlockedIncrement( (LONG *)&TicketCacheEntry->ListEntry.ReferenceCount );
  256. KerbWriteLockTicketCache();
  257. InsertHeadList(
  258. &TicketCache->CacheEntries,
  259. &TicketCacheEntry->ListEntry.Next
  260. );
  261. KerbUnlockTicketCache();
  262. }
  263. }
  264. //+-------------------------------------------------------------------------
  265. //
  266. // Function: KerbRemoveTicketCachEntry
  267. //
  268. // Synopsis: Removes a ticket cache entry from its ticket cache
  269. //
  270. // Effects:
  271. //
  272. // Arguments: TicketCacheEntry - The entry to yank out of the cache.
  273. //
  274. // Returns: none
  275. //
  276. // Notes:
  277. //
  278. //
  279. //--------------------------------------------------------------------------
  280. VOID
  281. KerbRemoveTicketCacheEntry(
  282. IN PKERB_TICKET_CACHE_ENTRY TicketCacheEntry
  283. )
  284. {
  285. //
  286. // An entry can only be unlinked from the cache once
  287. //
  288. if ( InterlockedCompareExchange(
  289. &TicketCacheEntry->Linked,
  290. (LONG)FALSE,
  291. (LONG)TRUE )) {
  292. DsysAssert(TicketCacheEntry->ListEntry.ReferenceCount != 0);
  293. KerbWriteLockTicketCache();
  294. RemoveEntryList(&TicketCacheEntry->ListEntry.Next);
  295. KerbUnlockTicketCache();
  296. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  297. }
  298. }
  299. //+-------------------------------------------------------------------------
  300. //
  301. // Function: KerbCacheTicket
  302. //
  303. // Synopsis: Caches a ticket in the ticket cache
  304. //
  305. // Effects: creates a cache entry
  306. //
  307. // Arguments: Cache - The cache in which to store the ticket - not needed
  308. // if ticket won't be linked.
  309. // Ticket - The ticket to cache
  310. // KdcReply - The KdcReply corresponding to the ticket cache
  311. // ReplyClientName - Client name from the KDC reply structure
  312. // TargetName - Name of service supplied by client
  313. // LinkEntry - If TRUE, insert cache entry into cache
  314. // NewCacheEntry - receives new cache entry (referenced)
  315. //
  316. // Requires:
  317. //
  318. // Returns:
  319. //
  320. // Notes: The ticket cache owner must be locked for write access
  321. //
  322. //
  323. //--------------------------------------------------------------------------
  324. NTSTATUS
  325. KerbCacheTicket(
  326. IN OPTIONAL PKERB_TICKET_CACHE TicketCache,
  327. IN PKERB_KDC_REPLY KdcReply,
  328. IN OPTIONAL PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody,
  329. IN OPTIONAL PKERB_INTERNAL_NAME TargetName,
  330. IN OPTIONAL PUNICODE_STRING TargetDomainName,
  331. IN ULONG CacheFlags,
  332. IN BOOLEAN LinkEntry,
  333. OUT PKERB_TICKET_CACHE_ENTRY * NewCacheEntry
  334. )
  335. {
  336. PKERB_TICKET_CACHE_ENTRY CacheEntry = NULL;
  337. NTSTATUS Status = STATUS_SUCCESS;
  338. ULONG OldCacheFlags = 0;
  339. *NewCacheEntry = NULL;
  340. CacheEntry = (PKERB_TICKET_CACHE_ENTRY)
  341. KerbAllocate(sizeof(KERB_TICKET_CACHE_ENTRY));
  342. if (CacheEntry == NULL)
  343. {
  344. Status = STATUS_INSUFFICIENT_RESOURCES;
  345. goto Cleanup;
  346. }
  347. //
  348. // Fill in the entries from the KDC reply
  349. //
  350. if (ARGUMENT_PRESENT(KdcReplyBody))
  351. {
  352. CacheEntry->TicketFlags = KerbConvertFlagsToUlong(&KdcReplyBody->flags);
  353. if (!KERB_SUCCESS(KerbDuplicateKey(
  354. &CacheEntry->SessionKey,
  355. &KdcReplyBody->session_key
  356. )))
  357. {
  358. Status = STATUS_INSUFFICIENT_RESOURCES;
  359. goto Cleanup;
  360. }
  361. if (KdcReplyBody->bit_mask & KERB_ENCRYPTED_KDC_REPLY_starttime_present)
  362. {
  363. KerbConvertGeneralizedTimeToLargeInt(
  364. &CacheEntry->StartTime,
  365. &KdcReplyBody->KERB_ENCRYPTED_KDC_REPLY_starttime,
  366. NULL
  367. );
  368. }
  369. else
  370. {
  371. KerbConvertGeneralizedTimeToLargeInt(
  372. &CacheEntry->StartTime,
  373. &KdcReplyBody->authtime,
  374. NULL
  375. );
  376. }
  377. KerbConvertGeneralizedTimeToLargeInt(
  378. &CacheEntry->EndTime,
  379. &KdcReplyBody->endtime,
  380. NULL
  381. );
  382. if (KdcReplyBody->bit_mask & KERB_ENCRYPTED_KDC_REPLY_renew_until_present)
  383. {
  384. KerbConvertGeneralizedTimeToLargeInt(
  385. &CacheEntry->RenewUntil,
  386. &KdcReplyBody->KERB_ENCRYPTED_KDC_REPLY_renew_until,
  387. NULL
  388. );
  389. }
  390. //
  391. // Check to see if the ticket has already expired
  392. //
  393. if (KerbTicketIsExpiring(CacheEntry, FALSE))
  394. {
  395. DebugLog((DEB_ERROR,"Tried to cache an already-expired ticket\n"));
  396. Status = STATUS_TIME_DIFFERENCE_AT_DC;
  397. goto Cleanup;
  398. }
  399. }
  400. //
  401. // Fill in the FullServiceName which is the domain name concatenated with
  402. // the service name.
  403. //
  404. // The full service name is domain name '\' service name.
  405. //
  406. //
  407. // Fill in the domain name and service name separately in the
  408. // cache entry, using the FullServiceName buffer.
  409. //
  410. if (!KERB_SUCCESS(KerbConvertRealmToUnicodeString(
  411. &CacheEntry->DomainName,
  412. &KdcReply->ticket.realm
  413. )))
  414. {
  415. Status = STATUS_INSUFFICIENT_RESOURCES;
  416. goto Cleanup;
  417. }
  418. if (!KERB_SUCCESS(KerbConvertPrincipalNameToKdcName(
  419. &CacheEntry->ServiceName,
  420. &KdcReply->ticket.server_name
  421. )))
  422. {
  423. Status = STATUS_INSUFFICIENT_RESOURCES;
  424. goto Cleanup;
  425. }
  426. //
  427. // Extract the realm name from the principal name
  428. //
  429. Status = KerbExtractDomainName(
  430. &CacheEntry->TargetDomainName,
  431. CacheEntry->ServiceName,
  432. &CacheEntry->DomainName
  433. );
  434. if (!NT_SUCCESS(Status))
  435. {
  436. goto Cleanup;
  437. }
  438. //
  439. // The reply need not include the name
  440. //
  441. if (KdcReply->client_realm != NULL)
  442. {
  443. if (!KERB_SUCCESS(KerbConvertRealmToUnicodeString(
  444. &CacheEntry->ClientDomainName,
  445. &KdcReply->client_realm)))
  446. {
  447. Status = STATUS_INSUFFICIENT_RESOURCES;
  448. goto Cleanup;
  449. }
  450. }
  451. //
  452. // Fill in the target name the client provided, which may be
  453. // different then the service name
  454. //
  455. if (ARGUMENT_PRESENT(TargetName))
  456. {
  457. Status = KerbDuplicateKdcName(
  458. &CacheEntry->TargetName,
  459. TargetName
  460. );
  461. if (!NT_SUCCESS(Status))
  462. {
  463. goto Cleanup;
  464. }
  465. }
  466. if (ARGUMENT_PRESENT(TargetDomainName))
  467. {
  468. Status = KerbDuplicateString(
  469. &CacheEntry->AltTargetDomainName,
  470. TargetDomainName
  471. );
  472. if (!NT_SUCCESS(Status))
  473. {
  474. goto Cleanup;
  475. }
  476. }
  477. //
  478. // Store the client name so we can use the right name
  479. // in later requests.
  480. //
  481. if (KdcReply->client_name.name_string != NULL)
  482. {
  483. if (!KERB_SUCCESS( KerbConvertPrincipalNameToKdcName(
  484. &CacheEntry->ClientName,
  485. &KdcReply->client_name)))
  486. {
  487. Status = STATUS_INSUFFICIENT_RESOURCES;
  488. goto Cleanup;
  489. }
  490. }
  491. if (!KERB_SUCCESS(KerbDuplicateTicket(
  492. &CacheEntry->Ticket,
  493. &KdcReply->ticket
  494. )))
  495. {
  496. Status = STATUS_INSUFFICIENT_RESOURCES;
  497. goto Cleanup;
  498. }
  499. //
  500. // Set the number of references equal to 2 - one for the ticket cache
  501. // and one for the returned pointer
  502. //
  503. CacheEntry->ListEntry.ReferenceCount = 1;
  504. CacheEntry->CacheFlags = CacheFlags;
  505. //
  506. // Before we insert this ticket we want to remove any
  507. // previous instances of tickets to the same service.
  508. //
  509. if (LinkEntry)
  510. {
  511. PKERB_TICKET_CACHE_ENTRY OldCacheEntry = NULL;
  512. if ((CacheFlags & (KERB_TICKET_CACHE_DELEGATION_TGT |
  513. KERB_TICKET_CACHE_PRIMARY_TGT)) != 0)
  514. {
  515. OldCacheEntry = KerbLocateTicketCacheEntryByRealm(
  516. TicketCache,
  517. NULL,
  518. CacheFlags
  519. );
  520. }
  521. else
  522. {
  523. OldCacheEntry = KerbLocateTicketCacheEntry(
  524. TicketCache,
  525. CacheEntry->ServiceName,
  526. &CacheEntry->DomainName
  527. );
  528. }
  529. if (OldCacheEntry != NULL)
  530. {
  531. OldCacheFlags = OldCacheEntry->CacheFlags;
  532. KerbRemoveTicketCacheEntry( OldCacheEntry );
  533. KerbDereferenceTicketCacheEntry( OldCacheEntry );
  534. OldCacheEntry = NULL;
  535. }
  536. //
  537. // If the old ticket was the primary TGT, mark this one as the
  538. // primary TGT as well.
  539. //
  540. CacheEntry->CacheFlags |= (OldCacheFlags &
  541. (KERB_TICKET_CACHE_DELEGATION_TGT |
  542. KERB_TICKET_CACHE_PRIMARY_TGT));
  543. //
  544. // Insert the cache entry into the cache
  545. //
  546. KerbInsertTicketCacheEntry(
  547. TicketCache,
  548. CacheEntry
  549. );
  550. }
  551. //
  552. // Update the statistics
  553. //
  554. UpdateCacheMisses();
  555. *NewCacheEntry = CacheEntry;
  556. Cleanup:
  557. if (!NT_SUCCESS(Status))
  558. {
  559. if (NULL != CacheEntry)
  560. {
  561. KerbFreeTicketCacheEntry(CacheEntry);
  562. }
  563. }
  564. return(Status);
  565. }
  566. //+-------------------------------------------------------------------------
  567. //
  568. // Function: KerbPurgeTicketCache
  569. //
  570. // Synopsis: Purges a cache of all its tickets
  571. //
  572. // Effects: unreferences all tickets in the cache
  573. //
  574. // Arguments: Cache - Ticket cache to purge
  575. //
  576. // Requires:
  577. //
  578. // Returns: none
  579. //
  580. // Notes:
  581. //
  582. //
  583. //--------------------------------------------------------------------------
  584. VOID
  585. KerbPurgeTicketCache(
  586. IN PKERB_TICKET_CACHE Cache
  587. )
  588. {
  589. PKERB_TICKET_CACHE_ENTRY CacheEntry;
  590. KerbWriteLockTicketCache();
  591. while (!IsListEmpty(&Cache->CacheEntries))
  592. {
  593. CacheEntry = CONTAINING_RECORD(
  594. Cache->CacheEntries.Flink,
  595. KERB_TICKET_CACHE_ENTRY,
  596. ListEntry.Next
  597. );
  598. KerbRemoveTicketCacheEntry(
  599. CacheEntry
  600. );
  601. }
  602. KerbUnlockTicketCache();
  603. }
  604. //+-------------------------------------------------------------------------
  605. //
  606. // Function: KerbLocateTicketCacheEntry
  607. //
  608. // Synopsis: References a ticket cache entry by name
  609. //
  610. // Effects: Increments the reference count on the ticket cache entry
  611. //
  612. // Arguments: TicketCache - the ticket cache to search
  613. // FullServiceName - Optionally contains full service name
  614. // of target, including domain name. If it is NULL,
  615. // then the first element in the list is returned.
  616. // RealmName - Realm of service name. If length is zero, is not
  617. // used for comparison.
  618. //
  619. // Requires:
  620. //
  621. // Returns: The referenced cache entry or NULL if it was not found.
  622. //
  623. // Notes: If an invalid entry is found it may be dereferenced
  624. //
  625. //
  626. //--------------------------------------------------------------------------
  627. PKERB_TICKET_CACHE_ENTRY
  628. KerbLocateTicketCacheEntry(
  629. IN PKERB_TICKET_CACHE TicketCache,
  630. IN PKERB_INTERNAL_NAME FullServiceName,
  631. IN PUNICODE_STRING RealmName
  632. )
  633. {
  634. PLIST_ENTRY ListEntry;
  635. PKERB_TICKET_CACHE_ENTRY CacheEntry = NULL;
  636. BOOLEAN Found = FALSE;
  637. BOOLEAN Remove = FALSE;
  638. KerbReadLockTicketCache();
  639. //
  640. // Go through the ticket cache looking for the correct entry
  641. //
  642. for (ListEntry = TicketCache->CacheEntries.Flink ;
  643. ListEntry != &TicketCache->CacheEntries ;
  644. ListEntry = ListEntry->Flink )
  645. {
  646. CacheEntry = CONTAINING_RECORD(ListEntry, KERB_TICKET_CACHE_ENTRY, ListEntry.Next);
  647. if (!ARGUMENT_PRESENT(FullServiceName) ||
  648. KerbEqualKdcNames(
  649. CacheEntry->ServiceName,
  650. FullServiceName
  651. ) ||
  652. ((CacheEntry->TargetName != NULL) &&
  653. KerbEqualKdcNames(
  654. CacheEntry->TargetName,
  655. FullServiceName
  656. ) ) )
  657. {
  658. //
  659. // Make sure they are principals in the same realm
  660. //
  661. if ((RealmName->Length != 0) &&
  662. !RtlEqualUnicodeString(
  663. RealmName,
  664. &CacheEntry->DomainName,
  665. TRUE // case insensitive
  666. ) &&
  667. !RtlEqualUnicodeString(
  668. RealmName,
  669. &CacheEntry->AltTargetDomainName,
  670. TRUE // case insensitive
  671. ))
  672. {
  673. continue;
  674. }
  675. //
  676. // We don't want to return any special tickets.
  677. //
  678. if (CacheEntry->CacheFlags != 0)
  679. {
  680. continue;
  681. }
  682. //
  683. // Check to see if the entry has expired, or is not yet valid. If it has, just
  684. // remove it now.
  685. //
  686. if (KerbTicketIsExpiring(CacheEntry, FALSE ))
  687. {
  688. Remove = TRUE;
  689. Found = FALSE;
  690. }
  691. else
  692. {
  693. Found = TRUE;
  694. }
  695. KerbReferenceTicketCacheEntry(CacheEntry);
  696. break;
  697. }
  698. }
  699. KerbUnlockTicketCache();
  700. if (Remove)
  701. {
  702. KerbRemoveTicketCacheEntry(CacheEntry);
  703. KerbDereferenceTicketCacheEntry(CacheEntry);
  704. }
  705. if (!Found)
  706. {
  707. CacheEntry = NULL;
  708. }
  709. //
  710. // Update the statistics
  711. //
  712. if (Found)
  713. {
  714. UpdateCacheHits();
  715. }
  716. else
  717. {
  718. UpdateCacheMisses();
  719. }
  720. return(CacheEntry);
  721. }
  722. //+-------------------------------------------------------------------------
  723. //
  724. // Function: KerbLocateTicketCacheEntryByRealm
  725. //
  726. // Synopsis: References a ticket cache entry by realm name. This is used
  727. // only for the cache of TGTs
  728. //
  729. // Effects: Increments the reference count on the ticket cache entry
  730. //
  731. // Arguments: TicketCache - the ticket cache to search
  732. // RealmName - Optioanl realm of ticket - if NULL looks for
  733. // initial ticket
  734. // RequiredFlags - any ticket cache flags the return ticket must
  735. // have. If the caller asks for a primary TGT, then this
  736. // API won't do expiration checking.
  737. //
  738. // Requires:
  739. //
  740. // Returns: The referenced cache entry or NULL if it was not found.
  741. //
  742. // Notes: If an invalid entry is found it may be dereferenced
  743. // We we weren't given a RealmName, then we need to look
  744. // through the whole list for the ticket.
  745. //
  746. //
  747. //--------------------------------------------------------------------------
  748. PKERB_TICKET_CACHE_ENTRY
  749. KerbLocateTicketCacheEntryByRealm(
  750. IN PKERB_TICKET_CACHE TicketCache,
  751. IN PUNICODE_STRING RealmName,
  752. IN ULONG RequiredFlags
  753. )
  754. {
  755. PLIST_ENTRY ListEntry;
  756. PKERB_TICKET_CACHE_ENTRY CacheEntry = NULL;
  757. BOOLEAN Found = FALSE, NoRealmSupplied = FALSE;
  758. BOOLEAN Remove = FALSE;
  759. NoRealmSupplied = ((RealmName == NULL) || (RealmName->Length == 0));
  760. KerbReadLockTicketCache();
  761. //
  762. // Go through the ticket cache looking for the correct entry
  763. //
  764. for (ListEntry = TicketCache->CacheEntries.Flink ;
  765. ListEntry != &TicketCache->CacheEntries ;
  766. ListEntry = ListEntry->Flink )
  767. {
  768. CacheEntry = CONTAINING_RECORD(ListEntry, KERB_TICKET_CACHE_ENTRY, ListEntry.Next);
  769. //
  770. // Match if the caller supplied no realm name, the realm matches the
  771. // target domain name, or it matches the alt target domain name
  772. //
  773. if (((RealmName == NULL) || (RealmName->Length == 0)) ||
  774. ((CacheEntry->TargetDomainName.Length != 0) &&
  775. RtlEqualUnicodeString(
  776. &CacheEntry->TargetDomainName,
  777. RealmName,
  778. TRUE
  779. )) ||
  780. ((CacheEntry->AltTargetDomainName.Length != 0) &&
  781. RtlEqualUnicodeString(
  782. &CacheEntry->AltTargetDomainName,
  783. RealmName,
  784. TRUE
  785. )) )
  786. {
  787. //
  788. // Check the required flags are set.
  789. //
  790. if (((CacheEntry->CacheFlags & RequiredFlags) != RequiredFlags) ||
  791. (((CacheEntry->CacheFlags & KERB_TICKET_CACHE_DELEGATION_TGT) != 0) &&
  792. ((RequiredFlags & KERB_TICKET_CACHE_DELEGATION_TGT) == 0)))
  793. {
  794. Found = FALSE;
  795. //
  796. // We need to continue looking
  797. //
  798. continue;
  799. }
  800. //
  801. // Check to see if the entry has expired. If it has, just
  802. // remove it now.
  803. //
  804. if (KerbTicketIsExpiring(CacheEntry, FALSE ))
  805. {
  806. //
  807. // if this is not the primary TGT, go ahead and remove it. We
  808. // want to save the primary TGT so we can renew it.
  809. //
  810. if ((CacheEntry->CacheFlags & KERB_TICKET_CACHE_PRIMARY_TGT) == 0)
  811. {
  812. Remove = TRUE;
  813. Found = FALSE;
  814. }
  815. else
  816. {
  817. //
  818. // If the caller was asking for the primary TGT,
  819. // return it whether or not it expired.
  820. //
  821. if ((RequiredFlags & KERB_TICKET_CACHE_PRIMARY_TGT) != 0 )
  822. {
  823. Found = TRUE;
  824. }
  825. else
  826. {
  827. Found = FALSE;
  828. }
  829. }
  830. if ( Remove || Found )
  831. {
  832. KerbReferenceTicketCacheEntry(CacheEntry);
  833. }
  834. }
  835. else
  836. {
  837. KerbReferenceTicketCacheEntry(CacheEntry);
  838. Found = TRUE;
  839. }
  840. break;
  841. }
  842. }
  843. KerbUnlockTicketCache();
  844. if (Remove)
  845. {
  846. KerbRemoveTicketCacheEntry(CacheEntry);
  847. KerbDereferenceTicketCacheEntry(CacheEntry);
  848. }
  849. if (!Found)
  850. {
  851. CacheEntry = NULL;
  852. }
  853. //
  854. // Update the statistics
  855. //
  856. if (Found)
  857. {
  858. UpdateCacheHits();
  859. }
  860. else
  861. {
  862. UpdateCacheMisses();
  863. }
  864. return(CacheEntry);
  865. }
  866. //+-------------------------------------------------------------------------
  867. //
  868. // Function: KerbTicketIsExpiring
  869. //
  870. // Synopsis: Check if a ticket is expiring
  871. //
  872. // Effects:
  873. //
  874. // Arguments: CacheEntry - Entry to check
  875. // AllowSkew - Expire ticket that aren't outside of skew of
  876. // expiring
  877. //
  878. // Requires:
  879. //
  880. // Returns:
  881. //
  882. // Notes: Ticket cache lock must be held
  883. //
  884. //
  885. //--------------------------------------------------------------------------
  886. BOOLEAN
  887. KerbTicketIsExpiring(
  888. IN PKERB_TICKET_CACHE_ENTRY CacheEntry,
  889. IN BOOLEAN AllowSkew
  890. )
  891. {
  892. TimeStamp CutoffTime;
  893. GetSystemTimeAsFileTime((PFILETIME) &CutoffTime);
  894. //
  895. // We want to make sure we have at least skewtime left on the ticket
  896. //
  897. if (AllowSkew)
  898. {
  899. KerbSetTime(&CutoffTime, KerbGetTime(CutoffTime) + KerbGetTime(KerbGlobalSkewTime));
  900. }
  901. //
  902. // Adjust for server skew time.
  903. //
  904. KerbSetTime(&CutoffTime, KerbGetTime(CutoffTime) + KerbGetTime(CacheEntry->TimeSkew));
  905. if (KerbGetTime(CacheEntry->EndTime) < KerbGetTime(CutoffTime))
  906. {
  907. return(TRUE);
  908. }
  909. else
  910. {
  911. return(FALSE);
  912. }
  913. }
  914. //+-------------------------------------------------------------------------
  915. //
  916. // Function: KerbSetTicketCacheEntryTarget
  917. //
  918. // Synopsis: Sets target name for a cache entry
  919. //
  920. // Effects:
  921. //
  922. // Arguments:
  923. //
  924. // Requires:
  925. //
  926. // Returns:
  927. //
  928. // Notes:
  929. //
  930. //
  931. //--------------------------------------------------------------------------
  932. VOID
  933. KerbSetTicketCacheEntryTarget(
  934. IN PUNICODE_STRING TargetName,
  935. IN PKERB_TICKET_CACHE_ENTRY TicketCacheEntry
  936. )
  937. {
  938. KerbWriteLockTicketCache();
  939. KerbFreeString(&TicketCacheEntry->AltTargetDomainName);
  940. KerbDuplicateString(
  941. &TicketCacheEntry->AltTargetDomainName,
  942. TargetName
  943. );
  944. KerbUnlockTicketCache();
  945. }