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.

5171 lines
141 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 2001
  6. //
  7. // File: kerbs4u.cxx
  8. //
  9. // Contents: Code for doing S4UToSelf() logon.
  10. //
  11. //
  12. // History: 14-March-2001 Created Todds
  13. //
  14. //------------------------------------------------------------------------
  15. #include <kerb.hxx>
  16. #include <kerbp.h>
  17. #include <kerbs4u.h>
  18. extern "C"
  19. {
  20. #include <md5.h>
  21. }
  22. #ifdef DEBUG_SUPPORT
  23. static TCHAR THIS_FILE[]=TEXT(__FILE__);
  24. #endif
  25. #define FILENO FILENO_S4U
  26. //
  27. // Used for ticket leak detection.
  28. //
  29. #if DBG
  30. EXTERN LIST_ENTRY GlobalTicketList;
  31. #endif
  32. EXTERN LONG GlobalTicketListSize;
  33. KERBEROS_LIST KerbS4UCache;
  34. #define KerbLockS4UCache( ) SafeEnterCriticalSection( &KerbS4UCache.Lock )
  35. #define KerbUnlockS4UCache( ) SafeLeaveCriticalSection( &KerbS4UCache.Lock )
  36. //
  37. // S4U Cache management functions.
  38. //
  39. NTSTATUS
  40. KerbInitS4UCache()
  41. {
  42. NTSTATUS Status;
  43. Status = KerbInitializeList(&KerbS4UCache, S4U_CACHE_LOCK_ENUM);
  44. if (NT_SUCCESS( Status ))
  45. {
  46. Status = KerbScheduleS4UCleanup();
  47. }
  48. return Status;
  49. }
  50. VOID
  51. KerbReferenceS4UCacheEntry(
  52. IN PKERB_S4UCACHE_DATA CacheEntry
  53. )
  54. {
  55. DsysAssert(CacheEntry->ListEntry.ReferenceCount != 0);
  56. InterlockedIncrement( (LONG *)&CacheEntry->ListEntry.ReferenceCount );
  57. }
  58. VOID
  59. KerbFreeS4UCacheEntry(
  60. IN PKERB_S4UCACHE_DATA CacheEntry
  61. )
  62. {
  63. if (ARGUMENT_PRESENT(CacheEntry))
  64. {
  65. KerbFree(CacheEntry);
  66. }
  67. }
  68. VOID
  69. KerbDereferenceS4UCacheEntry(
  70. IN PKERB_S4UCACHE_DATA CacheEntry
  71. )
  72. {
  73. DsysAssert(CacheEntry->ListEntry.ReferenceCount != 0);
  74. if ( 0 == InterlockedDecrement( (LONG *)&CacheEntry->ListEntry.ReferenceCount ))
  75. {
  76. KerbFreeS4UCacheEntry(CacheEntry);
  77. }
  78. return;
  79. }
  80. VOID
  81. KerbInsertS4UCacheEntry(
  82. IN PKERB_S4UCACHE_DATA CacheEntry
  83. )
  84. {
  85. if ( !InterlockedCompareExchange(
  86. &CacheEntry->Linked,
  87. (LONG)TRUE,
  88. (LONG)FALSE ))
  89. {
  90. InterlockedIncrement( (LONG *)&CacheEntry->ListEntry.ReferenceCount );
  91. KerbLockS4UCache();
  92. InsertHeadList(
  93. &KerbS4UCache.List,
  94. &CacheEntry->ListEntry.Next
  95. );
  96. KerbUnlockS4UCache();
  97. }
  98. }
  99. VOID
  100. KerbRemoveS4UCacheEntry(
  101. IN PKERB_S4UCACHE_DATA CacheEntry
  102. )
  103. {
  104. //
  105. // An entry can only be unlinked from the cache once
  106. //
  107. if ( InterlockedCompareExchange(
  108. &CacheEntry->Linked,
  109. (LONG)FALSE,
  110. (LONG)TRUE ))
  111. {
  112. DsysAssert(CacheEntry->ListEntry.ReferenceCount != 0);
  113. KerbLockS4UCache();
  114. RemoveEntryList(&CacheEntry->ListEntry.Next);
  115. KerbUnlockS4UCache();
  116. KerbDereferenceS4UCacheEntry(CacheEntry);
  117. }
  118. }
  119. PKERB_S4UCACHE_DATA
  120. KerbLocateS4UCacheEntry(
  121. IN PLUID LogonId,
  122. IN OUT PULONG CacheState
  123. )
  124. {
  125. PKERB_S4UCACHE_DATA Entry = NULL;
  126. PLIST_ENTRY ListEntry = NULL;
  127. TimeStamp CurrentTime;
  128. BOOLEAN Found = FALSE;
  129. *CacheState = 0;
  130. KerbLockS4UCache();
  131. for (ListEntry = KerbS4UCache.List.Flink ;
  132. ListEntry != &KerbS4UCache.List ;
  133. ListEntry = ListEntry->Flink )
  134. {
  135. Entry = CONTAINING_RECORD(ListEntry, KERB_S4UCACHE_DATA, ListEntry.Next);
  136. if (RtlEqualLuid(LogonId, &Entry->LogonId))
  137. {
  138. KerbReferenceS4UCacheEntry(Entry);
  139. *CacheState = Entry->CacheState;
  140. GetSystemTimeAsFileTime( (PFILETIME) &CurrentTime );
  141. if ( KerbGetTime( CurrentTime ) > KerbGetTime( Entry->CacheEndtime ))
  142. {
  143. *CacheState = S4UCACHE_TIMEOUT;
  144. }
  145. Found = TRUE;
  146. break;
  147. }
  148. }
  149. KerbUnlockS4UCache();
  150. if ( !Found )
  151. {
  152. Entry = NULL;
  153. }
  154. return Entry;
  155. }
  156. //+-------------------------------------------------------------------------
  157. //
  158. // Function: CacheMatch
  159. //
  160. // Synopsis: Checks for a S4U Ticket cache match based on flags. Inline function, mainly
  161. // provided for readability.
  162. //
  163. // Effects:
  164. //
  165. //
  166. // Requires:
  167. //
  168. // Returns: The referenced cache entry or NULL if it was not found.
  169. //
  170. // Notes: If an invalid entry is found it may be dereferenced
  171. //
  172. //
  173. //--------------------------------------------------------------------------
  174. BOOLEAN
  175. CacheMatch(
  176. IN PKERB_TICKET_CACHE_ENTRY Entry,
  177. IN PLUID LogonId,
  178. IN PKERB_INTERNAL_NAME ClientName,
  179. IN PUNICODE_STRING ClientRealm,
  180. IN PKERB_INTERNAL_NAME AltClientName,
  181. IN ULONG LookupFlags,
  182. IN OUT BOOLEAN *Remove
  183. )
  184. {
  185. BOOLEAN fRet = FALSE;
  186. *Remove = FALSE;
  187. //
  188. // We must use a ticket which is relative to the calling
  189. // security context.
  190. //
  191. if (!RtlEqualLuid(LogonId, &Entry->EvidenceLogonId))
  192. {
  193. DebugLog((DEB_ERROR, "Cache miss due to luid\n"));
  194. return FALSE;
  195. }
  196. if (KerbTicketIsExpiring(Entry, TRUE ))
  197. {
  198. *Remove = TRUE;
  199. return FALSE;
  200. }
  201. switch (LookupFlags)
  202. {
  203. case S4UTICKETCACHE_FOR_EVIDENCE:
  204. //
  205. // All tickets in the S4U cache are "for evidence",
  206. // unless they're not forwardable..
  207. //
  208. return ((Entry->TicketFlags & KERB_TICKET_FLAGS_forwardable) != 0);
  209. case S4UTICKETCACHE_MATCH_ALL:
  210. //
  211. // This is used when caching a new S4U ticket. In that case, all of the
  212. // fields must match.
  213. //
  214. DsysAssert(ARGUMENT_PRESENT(ClientName));
  215. DsysAssert(ARGUMENT_PRESENT(ClientRealm));
  216. //
  217. // Tickets obtained due ASC created network logons are only used for
  218. // evidence, not for S4U.
  219. //
  220. if (( Entry->CacheFlags & KERB_TICKET_CACHE_ASC_TICKET ) != 0)
  221. {
  222. return FALSE;
  223. }
  224. fRet = RtlEqualUnicodeString(ClientRealm, &Entry->ClientDomainName, TRUE);
  225. if (fRet)
  226. {
  227. fRet = KerbEqualKdcNames(ClientName, Entry->ClientName);
  228. }
  229. if (fRet & (ARGUMENT_PRESENT(AltClientName)))
  230. {
  231. fRet = KerbEqualKdcNames(ClientName, Entry->AltClientName);
  232. }
  233. break;
  234. case S4UTICKETCACHE_USEALTNAME:
  235. //
  236. // Here, we check for a UPN match on a ticket that's
  237. // been "normalized" through a previous S4USelf logon.
  238. //
  239. DsysAssert(ARGUMENT_PRESENT(AltClientName));
  240. //
  241. // Tickets obtained due ASC created network logons are only used for
  242. // evidence, not for S4U.
  243. //
  244. if (( Entry->CacheFlags & KERB_TICKET_CACHE_ASC_TICKET ) != 0)
  245. {
  246. return FALSE;
  247. }
  248. fRet = KerbEqualKdcNames(AltClientName, Entry->AltClientName);
  249. break;
  250. default:
  251. //
  252. // The default, literally. This is a simple cName / Crealm match used
  253. // on S4USelf tickets.
  254. //
  255. //
  256. // Tickets obtained due ASC created network logons are only used for
  257. // evidence, not for S4U.
  258. //
  259. if (( Entry->CacheFlags & KERB_TICKET_CACHE_ASC_TICKET ) != 0)
  260. {
  261. return FALSE;
  262. }
  263. fRet = (KerbEqualKdcNames(ClientName, Entry->ClientName) &
  264. RtlEqualUnicodeString(ClientRealm, &Entry->ClientDomainName, TRUE));
  265. break;
  266. }
  267. return fRet;
  268. }
  269. //+-------------------------------------------------------------------------
  270. //
  271. // Function: KerbLocateS4UTicketCacheEntry
  272. //
  273. // Synopsis: References a ticket cache entry by name
  274. //
  275. // Effects: Increments the reference count on the ticket cache entry
  276. //
  277. // Arguments: TicketCache - the ticket cache to search
  278. // FullServiceName - Optionally contains full service name
  279. // of target, including domain name. If it is NULL,
  280. // then the first element in the list is returned.
  281. // RealmName - Realm of service name. If length is zero, is not
  282. // used for comparison.
  283. //
  284. // Requires:
  285. //
  286. // Returns: The referenced cache entry or NULL if it was not found.
  287. //
  288. // Notes: If an invalid entry is found it may be dereferenced
  289. //
  290. //
  291. //--------------------------------------------------------------------------
  292. PKERB_TICKET_CACHE_ENTRY
  293. KerbLocateS4UTicketCacheEntry(
  294. IN PKERB_TICKET_CACHE TicketCache,
  295. IN PLUID LogonIdToMatch,
  296. IN OPTIONAL PKERB_INTERNAL_NAME ClientName,
  297. IN OPTIONAL PUNICODE_STRING ClientRealm,
  298. IN OPTIONAL PKERB_INTERNAL_NAME AltClientName,
  299. IN ULONG LookupFlags
  300. )
  301. {
  302. PLIST_ENTRY ListEntry;
  303. PKERB_TICKET_CACHE_ENTRY CacheEntry = NULL;
  304. BOOLEAN Found = FALSE;
  305. BOOLEAN Remove = FALSE;
  306. BOOLEAN CleanupNeeded = FALSE;
  307. BOOLEAN Timeout = FALSE;
  308. TimeStamp CurrentTime;
  309. TimeStamp CutoffTime;
  310. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime);
  311. KerbReadLockTicketCache();
  312. KerbSetTime(&CutoffTime, KerbGetTime(TicketCache->LastCleanup) + KerbGetTime(KerbGlobalS4UTicketLifetime));
  313. Timeout = (KerbGetTime(CurrentTime) < KerbGetTime(CutoffTime));
  314. //
  315. // Go through the ticket cache looking for the correct entry
  316. //
  317. for (ListEntry = TicketCache->CacheEntries.Flink ;
  318. ListEntry != &TicketCache->CacheEntries ;
  319. ListEntry = ListEntry->Flink )
  320. {
  321. CacheEntry = CONTAINING_RECORD(ListEntry, KERB_TICKET_CACHE_ENTRY, ListEntry.Next);
  322. if ( CacheMatch(
  323. CacheEntry,
  324. LogonIdToMatch,
  325. ClientName,
  326. ClientRealm,
  327. AltClientName,
  328. LookupFlags,
  329. &Remove
  330. ) )
  331. {
  332. KerbReferenceTicketCacheEntry(CacheEntry);
  333. Found = TRUE;
  334. break;
  335. }
  336. //
  337. // If we detect expired tickets, cleanup the cache. Only do this
  338. // every 15 minutes, though, so we don't end up with a bunch of
  339. // contention on the ticketcache resource in stress conditions.
  340. //
  341. if ( Remove )
  342. {
  343. CleanupNeeded = Timeout;
  344. }
  345. }
  346. KerbUnlockTicketCache();
  347. if (!Found)
  348. {
  349. CacheEntry = NULL;
  350. }
  351. //
  352. // We cleanup here to avoid having to always take the write lock.
  353. //
  354. if ( CleanupNeeded )
  355. {
  356. KerbAgeTickets( TicketCache );
  357. DebugLog((DEB_TRACE_S4U, "Cleanup needed\n"));
  358. }
  359. return(CacheEntry);
  360. }
  361. NTSTATUS
  362. KerbGetCallingLuid(
  363. IN OUT PLUID CallingLuid,
  364. IN OPTIONAL HANDLE hProcess
  365. )
  366. {
  367. NTSTATUS Status, TokenStatus;
  368. SECPKG_CLIENT_INFO ClientInfo;
  369. CLIENT_ID ClientId;
  370. OBJECT_ATTRIBUTES Obja;
  371. HANDLE hClientProcess = NULL;
  372. HANDLE hToken = NULL;
  373. HANDLE ImpersonationToken = NULL;
  374. TOKEN_STATISTICS TokenStats;
  375. DWORD TokenStatsSize = sizeof(TokenStats);
  376. if (!ARGUMENT_PRESENT(hProcess))
  377. {
  378. Status = LsaFunctions->GetClientInfo( &ClientInfo );
  379. if (!NT_SUCCESS( Status ))
  380. {
  381. return ( Status );
  382. }
  383. else if ((ClientInfo.ClientFlags & SECPKG_CLIENT_THREAD_TERMINATED) != 0)
  384. {
  385. return STATUS_ACCESS_DENIED;
  386. }
  387. ClientId.UniqueProcess = (HANDLE)LongToHandle(ClientInfo.ProcessID);
  388. ClientId.UniqueThread = (HANDLE)NULL;
  389. }
  390. else
  391. {
  392. ClientId.UniqueProcess = hProcess;
  393. ClientId.UniqueThread = (HANDLE)NULL;
  394. }
  395. //
  396. // If we're called by an in-proc lsass caller, we could be impersonating.
  397. // Revert, to make sure we're ok to do the below operation.
  398. //
  399. TokenStatus = NtOpenThreadToken(
  400. NtCurrentThread(),
  401. TOKEN_QUERY | TOKEN_IMPERSONATE,
  402. TRUE,
  403. &ImpersonationToken
  404. );
  405. if( NT_SUCCESS(TokenStatus) )
  406. {
  407. //
  408. // stop impersonating.
  409. //
  410. RevertToSelf();
  411. }
  412. InitializeObjectAttributes(
  413. &Obja,
  414. NULL,
  415. 0,
  416. NULL,
  417. NULL
  418. );
  419. Status = NtOpenProcess(
  420. &hClientProcess,
  421. (ACCESS_MASK)PROCESS_QUERY_INFORMATION,
  422. &Obja,
  423. &ClientId
  424. );
  425. if (NT_SUCCESS( Status ))
  426. {
  427. Status = NtOpenProcessToken(
  428. hClientProcess,
  429. TOKEN_QUERY,
  430. &hToken
  431. );
  432. if (NT_SUCCESS( Status ) )
  433. {
  434. Status = NtQueryInformationToken(
  435. hToken,
  436. TokenStatistics,
  437. &TokenStats,
  438. TokenStatsSize,
  439. &TokenStatsSize
  440. );
  441. if (NT_SUCCESS( Status ))
  442. {
  443. RtlCopyLuid(
  444. CallingLuid,
  445. &(TokenStats.AuthenticationId)
  446. );
  447. }
  448. NtClose( hToken );
  449. }
  450. NtClose( hClientProcess );
  451. }
  452. if (!NT_SUCCESS( Status ))
  453. {
  454. D_DebugLog((DEB_ERROR, "Failed to get LUID\n"));
  455. }
  456. if( ImpersonationToken != NULL )
  457. {
  458. //
  459. // put the thread token back if we were impersonating.
  460. //
  461. SetThreadToken( NULL, ImpersonationToken );
  462. NtClose( ImpersonationToken );
  463. }
  464. return ( Status );
  465. }
  466. //+-------------------------------------------------------------------------
  467. //
  468. // Function: KerbInitializeS4UCacheData
  469. //
  470. // Synopsis: Check the S4UCACHE_DATA to see if we can do S4U for this target.
  471. //
  472. // Effects:
  473. //
  474. // Arguments: LogonSession - Logon session of the service doing the
  475. // S4U request
  476. //
  477. // Requires: You must hold the logon session lock.
  478. //
  479. // Returns:
  480. //
  481. // Notes:
  482. //
  483. //
  484. //+-------------------------------------------------------------------------
  485. NTSTATUS
  486. KerbInitializeS4UCacheData(
  487. IN OUT PKERB_S4UCACHE_DATA *CacheData,
  488. IN PLUID LogonId,
  489. IN ULONG CacheFlags
  490. )
  491. {
  492. PKERB_S4UCACHE_DATA LocalData = NULL;
  493. TimeStamp CurrentTime;
  494. *CacheData = NULL;
  495. LocalData = (PKERB_S4UCACHE_DATA) KerbAllocate(sizeof(KERB_S4UCACHE_DATA));
  496. if ( NULL == LocalData )
  497. {
  498. return STATUS_NO_MEMORY;
  499. }
  500. RtlCopyLuid(
  501. &LocalData->LogonId,
  502. LogonId
  503. );
  504. LocalData->CacheState = CacheFlags;
  505. GetSystemTimeAsFileTime( (PFILETIME) &CurrentTime );
  506. KerbSetTime(&LocalData->CacheEndtime, KerbGetTime(CurrentTime) + KerbGetTime( KerbGlobalS4UCacheTimeout ));
  507. KerbInsertS4UCacheEntry(LocalData);
  508. *CacheData = LocalData;
  509. LocalData = NULL;
  510. return STATUS_SUCCESS;
  511. }
  512. //+-------------------------------------------------------------------------
  513. //
  514. // Function: KerbUpdateS4UCacheData
  515. //
  516. // Synopsis: Update the S4UCACHE_DATA
  517. //
  518. // Effects:
  519. //
  520. // Arguments: LogonSession - Logon session of the service doing the
  521. // S4U request
  522. //
  523. // Requires:
  524. //
  525. // Returns:
  526. //
  527. // Notes:
  528. //
  529. //
  530. //+-------------------------------------------------------------------------
  531. NTSTATUS
  532. KerbUpdateS4UCacheData(
  533. IN PKERB_LOGON_SESSION LogonSession,
  534. IN ULONG Flags
  535. )
  536. {
  537. NTSTATUS Status = STATUS_SUCCESS;
  538. TimeStamp CurrentTime;
  539. PKERB_S4UCACHE_DATA CacheData = NULL;
  540. ULONG CacheFlags = 0;
  541. KerbLockS4UCache();
  542. CacheData = KerbLocateS4UCacheEntry(
  543. &LogonSession->LogonId,
  544. &CacheFlags
  545. );
  546. if ( CacheData != NULL )
  547. {
  548. D_DebugLog((DEB_TRACE_S4U, "Updating existing entry %p - %x\n", CacheData, Flags));
  549. CacheData->CacheState = Flags;
  550. GetSystemTimeAsFileTime( (PFILETIME) &CurrentTime );
  551. KerbSetTime(&CacheData->CacheEndtime, KerbGetTime(CurrentTime) + KerbGetTime( KerbGlobalS4UCacheTimeout ));
  552. KerbDereferenceS4UCacheEntry(CacheData);
  553. }
  554. else
  555. {
  556. //
  557. // We don't have one for this logon ID. Create one.
  558. //
  559. Status = KerbInitializeS4UCacheData(
  560. &CacheData,
  561. &LogonSession->LogonId,
  562. Flags
  563. );
  564. }
  565. KerbUnlockS4UCache();
  566. return Status;
  567. }
  568. //+-------------------------------------------------------------------------
  569. //
  570. // Function: KerbCacheS4UTicket
  571. //
  572. // Synopsis: Ensures that the ticket we got back from the KDC actually has
  573. // the information we requested, then caches it.
  574. //
  575. // Effects:
  576. //
  577. // Arguments:
  578. //
  579. // Requires:
  580. //
  581. // Returns:
  582. //
  583. // Notes: This function performs a quick correctness test only before
  584. // caching the S4U Ticket.
  585. // We don't want to take the extra hit to decrypt this
  586. // ticket to get "real" name, as We'll do that later, when
  587. // we build the token (krbtoken.cxx) Later, we should see if
  588. // there's a better way to do this.
  589. //
  590. //
  591. //
  592. //--------------------------------------------------------------------------
  593. NTSTATUS
  594. KerbCacheS4UTicket(
  595. IN PKERB_LOGON_SESSION CallerLogonSession,
  596. IN PKERB_KDC_REPLY KdcReply,
  597. IN PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody,
  598. IN PKERB_INTERNAL_NAME OurTargetName,
  599. IN PKERB_INTERNAL_NAME S4UClientName,
  600. IN OPTIONAL PKERB_INTERNAL_NAME AltS4UClientName,
  601. IN PUNICODE_STRING S4UClientRealm,
  602. IN ULONG CacheFlags,
  603. IN OPTIONAL PKERB_TICKET_CACHE TicketCache,
  604. OUT PKERB_TICKET_CACHE_ENTRY * NewCacheEntry
  605. )
  606. {
  607. NTSTATUS Status = STATUS_LOGON_FAILURE;
  608. KERBERR KerbErr;
  609. BOOLEAN Result = FALSE;
  610. PKERB_TICKET_CACHE_ENTRY CacheEntry = NULL;
  611. PKERB_TICKET_CACHE_ENTRY OldCacheEntry = NULL;
  612. *NewCacheEntry = NULL;
  613. CacheEntry = (PKERB_TICKET_CACHE_ENTRY) KerbAllocate(sizeof(KERB_TICKET_CACHE_ENTRY));
  614. if (CacheEntry == NULL)
  615. {
  616. Status = STATUS_INSUFFICIENT_RESOURCES;
  617. goto Cleanup;
  618. }
  619. CacheEntry->ListEntry.ReferenceCount = 1;
  620. #if DBG
  621. if ( CacheEntry ) {
  622. KerbWriteLockTicketCache();
  623. InsertHeadList(
  624. &GlobalTicketList,
  625. &CacheEntry->GlobalListEntry
  626. );
  627. KerbUnlockTicketCache();
  628. }
  629. #endif
  630. InterlockedIncrement( &GlobalTicketListSize );
  631. //
  632. // Check Client identity - if any of these *don't* match, it means our KDC
  633. // doesn't understand the protocol, and is just giving us a ticket to ourselves,
  634. // from ourselves - e.g. sname = us, cname = us.
  635. //
  636. // If the KDC understands S4U, then sname = us, but cname = client name. Same
  637. // goes for the crealm and the target name.
  638. //
  639. KerbErr = KerbCompareKdcNameToPrincipalName(
  640. &KdcReply->client_name,
  641. S4UClientName,
  642. &Result
  643. );
  644. if (!KERB_SUCCESS(KerbErr))
  645. {
  646. Status = STATUS_INSUFFICIENT_RESOURCES;
  647. goto Cleanup;
  648. }
  649. else if (!Result)
  650. {
  651. KerbReadLockLogonSessions(CallerLogonSession);
  652. //
  653. // Hmm.. Is this the caller logon cname?
  654. //
  655. if (KerbCompareStringToPrincipalName(
  656. &KdcReply->client_name,
  657. &CallerLogonSession->PrimaryCredentials.UserName
  658. ))
  659. {
  660. Status = STATUS_NO_S4U_PROT_SUPPORT;
  661. }
  662. else
  663. {
  664. Status = STATUS_LOGON_FAILURE;
  665. }
  666. KerbUnlockLogonSessions(CallerLogonSession);
  667. DebugLog((DEB_ERROR, "S4UClient name != Reply Client name %x\n", Status));
  668. goto Cleanup;
  669. }
  670. KerbErr = KerbCompareUnicodeRealmToKerbRealm(
  671. &KdcReply->client_realm,
  672. S4UClientRealm,
  673. &Result
  674. );
  675. if (!KERB_SUCCESS(KerbErr))
  676. {
  677. Status = STATUS_INSUFFICIENT_RESOURCES;
  678. goto Cleanup;
  679. }
  680. else if (!Result)
  681. {
  682. KerbReadLockLogonSessions(CallerLogonSession);
  683. KerbErr = KerbCompareUnicodeRealmToKerbRealm(
  684. &KdcReply->client_realm,
  685. &CallerLogonSession->PrimaryCredentials.DomainName,
  686. &Result
  687. );
  688. KerbUnlockLogonSessions(CallerLogonSession);
  689. if (!KERB_SUCCESS(KerbErr))
  690. {
  691. Status = STATUS_INSUFFICIENT_RESOURCES;
  692. goto Cleanup;
  693. }
  694. Status = (Result ? STATUS_NO_S4U_PROT_SUPPORT : STATUS_LOGON_FAILURE);
  695. DebugLog((DEB_ERROR, "S4UClient REALM != Reply Client REALM %x\n", Status));
  696. goto Cleanup;
  697. }
  698. //
  699. // Check target name.
  700. //
  701. KerbErr = KerbCompareKdcNameToPrincipalName(
  702. &KdcReply->ticket.server_name,
  703. OurTargetName,
  704. &Result
  705. );
  706. if (!KERB_SUCCESS(KerbErr))
  707. {
  708. Status = STATUS_INSUFFICIENT_RESOURCES;
  709. goto Cleanup;
  710. }
  711. else if (!Result)
  712. {
  713. DebugLog((DEB_ERROR, "ServerName != TargetName\n"));
  714. Status = STATUS_NO_S4U_PROT_SUPPORT;
  715. goto Cleanup;
  716. }
  717. CacheEntry->TicketFlags = KerbConvertFlagsToUlong(&KdcReplyBody->flags);
  718. if (KdcReplyBody->bit_mask & KERB_ENCRYPTED_KDC_REPLY_starttime_present)
  719. {
  720. KerbConvertGeneralizedTimeToLargeInt(
  721. &CacheEntry->StartTime,
  722. &KdcReplyBody->KERB_ENCRYPTED_KDC_REPLY_starttime,
  723. NULL
  724. );
  725. }
  726. else
  727. {
  728. KerbConvertGeneralizedTimeToLargeInt(
  729. &CacheEntry->StartTime,
  730. &KdcReplyBody->authtime,
  731. NULL
  732. );
  733. }
  734. KerbConvertGeneralizedTimeToLargeInt(
  735. &CacheEntry->EndTime,
  736. &KdcReplyBody->endtime,
  737. NULL
  738. );
  739. if (KdcReplyBody->bit_mask & KERB_ENCRYPTED_KDC_REPLY_renew_until_present)
  740. {
  741. KerbConvertGeneralizedTimeToLargeInt(
  742. &CacheEntry->RenewUntil,
  743. &KdcReplyBody->KERB_ENCRYPTED_KDC_REPLY_renew_until,
  744. NULL
  745. );
  746. }
  747. //
  748. // Time skew check ?
  749. //
  750. Status = KerbDuplicateKdcName(
  751. &CacheEntry->TargetName,
  752. S4UClientName
  753. );
  754. if (!NT_SUCCESS(Status))
  755. {
  756. goto Cleanup;
  757. }
  758. Status = KerbDuplicateStringEx(
  759. &CacheEntry->ClientDomainName,
  760. S4UClientRealm,
  761. FALSE
  762. );
  763. if (!NT_SUCCESS(Status))
  764. {
  765. goto Cleanup;
  766. }
  767. Status = KerbDuplicateKdcName(
  768. &CacheEntry->ClientName,
  769. S4UClientName
  770. );
  771. if (!NT_SUCCESS(Status))
  772. {
  773. goto Cleanup;
  774. }
  775. if (ARGUMENT_PRESENT( AltS4UClientName ))
  776. {
  777. Status = KerbDuplicateKdcName(
  778. &CacheEntry->AltClientName,
  779. S4UClientName
  780. );
  781. if (!NT_SUCCESS(Status))
  782. {
  783. goto Cleanup;
  784. }
  785. }
  786. //
  787. // ServiceName is unused for S4U but other spots in Kerberos expect it
  788. // to be non-NULL. Dupe the ClientName into it as the best match.
  789. //
  790. Status = KerbDuplicateKdcName(
  791. &CacheEntry->ServiceName,
  792. OurTargetName
  793. );
  794. if (!NT_SUCCESS(Status))
  795. {
  796. goto Cleanup;
  797. }
  798. if (!KERB_SUCCESS(KerbDuplicateTicket(
  799. &CacheEntry->Ticket,
  800. &KdcReply->ticket
  801. )))
  802. {
  803. Status = STATUS_INSUFFICIENT_RESOURCES;
  804. goto Cleanup;
  805. }
  806. CacheEntry->EvidenceLogonId = CallerLogonSession->LogonId;
  807. CacheEntry->CacheFlags = CacheFlags | KERB_TICKET_CACHE_S4U_TICKET;
  808. if (ARGUMENT_PRESENT(TicketCache))
  809. {
  810. //
  811. // Before we insert this ticket we want to remove any
  812. // previous instances of tickets to the same service.
  813. //
  814. OldCacheEntry = KerbLocateS4UTicketCacheEntry(
  815. TicketCache,
  816. &CallerLogonSession->LogonId,
  817. S4UClientName,
  818. S4UClientRealm,
  819. AltS4UClientName,
  820. S4UTICKETCACHE_MATCH_ALL
  821. );
  822. if (OldCacheEntry != NULL)
  823. {
  824. D_DebugLog((DEB_TRACE_S4U, "Removing S4U Cache Entry - %p\n", OldCacheEntry));
  825. KerbRemoveTicketCacheEntry( OldCacheEntry );
  826. KerbDereferenceTicketCacheEntry( OldCacheEntry );
  827. }
  828. KerbInsertTicketCacheEntry(
  829. TicketCache,
  830. CacheEntry
  831. );
  832. }
  833. *NewCacheEntry = CacheEntry;
  834. CacheEntry = NULL;
  835. Cleanup:
  836. if (!NT_SUCCESS(Status))
  837. {
  838. if (NULL != CacheEntry)
  839. {
  840. KerbDereferenceTicketCacheEntry(CacheEntry);
  841. }
  842. }
  843. return(Status);
  844. }
  845. //+-------------------------------------------------------------------------
  846. //
  847. // Function: KerbGetS4UClientRealm
  848. //
  849. // Synopsis: Gets the client realm for a given UPN
  850. //
  851. //
  852. // Effects:
  853. //
  854. // Arguments: LogonSession - Logon session of the service doing the
  855. // S4U request
  856. //
  857. // Requires:
  858. //
  859. // Returns:
  860. //
  861. // Notes:
  862. //
  863. //
  864. //--------------------------------------------------------------------------
  865. NTSTATUS
  866. KerbGetS4UClientRealm(
  867. IN PKERB_LOGON_SESSION LogonSession,
  868. IN PKERB_INTERNAL_NAME * S4UClientName,
  869. IN OUT PUNICODE_STRING S4UTargetRealm
  870. // TBD: Place for credential handle?
  871. )
  872. {
  873. NTSTATUS Status = STATUS_SUCCESS;
  874. KERBERR KerbErr;
  875. PKERB_INTERNAL_NAME KdcServiceKdcName = NULL;
  876. PKERB_INTERNAL_NAME ClientName = NULL;
  877. UNICODE_STRING ClientRealm = {0};
  878. UNICODE_STRING CorrectRealm = {0};
  879. ULONG RetryCount = KERB_CLIENT_REFERRAL_MAX;
  880. ULONG RequestFlags = 0;
  881. BOOLEAN MitRealmLogon = FALSE;
  882. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
  883. RtlInitUnicodeString(
  884. S4UTargetRealm,
  885. NULL
  886. );
  887. //
  888. // Use our server credentials to start off the AS_REQ process.
  889. // We may get a referral elsewhere, however.
  890. //
  891. Status = KerbGetClientNameAndRealm(
  892. NULL,
  893. &LogonSession->PrimaryCredentials,
  894. FALSE,
  895. NULL,
  896. &MitRealmLogon,
  897. TRUE,
  898. &ClientName,
  899. &ClientRealm
  900. );
  901. if (!NT_SUCCESS(Status))
  902. {
  903. DebugLog((DEB_ERROR,"Failed to get client name & realm: 0x%x, %ws line %d\n",
  904. Status, THIS_FILE, __LINE__ ));
  905. goto Cleanup;
  906. }
  907. GetTicketRestart:
  908. D_DebugLog((DEB_TRACE_S4U, "KerbGetS4UClientRealm GetTicketRestart ClientRealm %wZ\n", &ClientRealm));
  909. KerbErr = KerbBuildFullServiceKdcName(
  910. &ClientRealm,
  911. &KerbGlobalKdcServiceName,
  912. KRB_NT_SRV_INST,
  913. &KdcServiceKdcName
  914. );
  915. if (!KERB_SUCCESS(KerbErr))
  916. {
  917. Status = STATUS_INSUFFICIENT_RESOURCES;
  918. goto Cleanup;
  919. }
  920. Status = KerbGetAuthenticationTicket(
  921. LogonSession,
  922. NULL,
  923. NULL,
  924. FALSE, // no preauth..
  925. KdcServiceKdcName,
  926. &ClientRealm,
  927. (*S4UClientName),
  928. RequestFlags,
  929. KERB_TICKET_CACHE_PRIMARY_TGT,
  930. &TicketCacheEntry,
  931. NULL,
  932. &CorrectRealm
  933. );
  934. //
  935. // If it failed but gave us another realm to try, go there
  936. //
  937. if (!NT_SUCCESS(Status) && (CorrectRealm.Length != 0))
  938. {
  939. if (--RetryCount != 0)
  940. {
  941. KerbFreeKdcName(&KdcServiceKdcName);
  942. KerbFreeString(&ClientRealm);
  943. ClientRealm = CorrectRealm;
  944. CorrectRealm.Buffer = NULL;
  945. goto GetTicketRestart;
  946. }
  947. else
  948. {
  949. // Tbd: Log error here? Max referrals reached..
  950. goto Cleanup;
  951. }
  952. }
  953. //
  954. // If we get STATUS_WRONG_PASSWORD, we succeeded in finding the
  955. // client realm. Otherwise, we're hosed. As the password we used
  956. // is bogus, we should never succeed, btw...
  957. //
  958. if (Status == STATUS_WRONG_PASSWORD)
  959. {
  960. *S4UTargetRealm = ClientRealm;
  961. Status = STATUS_SUCCESS;
  962. }
  963. Cleanup:
  964. // if we succeeded, we got the correct realm,
  965. // and we need to pass that back to caller
  966. if (!NT_SUCCESS(Status))
  967. {
  968. KerbFreeString(&ClientRealm);
  969. }
  970. KerbFreeKdcName(&KdcServiceKdcName);
  971. KerbFreeKdcName(&ClientName);
  972. if (NULL != TicketCacheEntry)
  973. {
  974. KerbRemoveTicketCacheEntry(TicketCacheEntry);
  975. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  976. }
  977. return(Status);
  978. }
  979. //+-------------------------------------------------------------------------
  980. //
  981. // Function: KerbPackAndSignS4UPreauthData
  982. //
  983. // Synopsis: Attempt to gets TGT for an S4U client for name
  984. // location purposes.
  985. //
  986. //
  987. // Effects:
  988. //
  989. // Arguments: LogonSession - Logon session of the service doing the
  990. // S4U request
  991. //
  992. // Requires:
  993. //
  994. // Returns:
  995. //
  996. // Notes:
  997. //
  998. //
  999. //--------------------------------------------------------------------------
  1000. NTSTATUS
  1001. KerbSignAndPackS4UPreauthData(
  1002. IN OUT PKERB_PA_DATA_LIST * PreAuthData,
  1003. IN PKERB_TICKET_CACHE_ENTRY TicketGrantingTicket,
  1004. IN PKERB_PA_FOR_USER S4UPreauth
  1005. )
  1006. {
  1007. NTSTATUS Status = STATUS_SUCCESS;
  1008. KERBERR KerbErr;
  1009. PKERB_PA_DATA_LIST ListElement = NULL;
  1010. unsigned char Hash[MD5DIGESTLEN];
  1011. *PreAuthData = NULL;
  1012. S4UPreauth->cksum.checksum.value = Hash;
  1013. Status = KerbHashS4UPreauth(
  1014. S4UPreauth,
  1015. &TicketGrantingTicket->SessionKey,
  1016. KERB_CHECKSUM_HMAC_MD5,
  1017. &S4UPreauth->cksum
  1018. );
  1019. if ( !NT_SUCCESS(Status) )
  1020. {
  1021. goto Cleanup;
  1022. }
  1023. ListElement = (PKERB_PA_DATA_LIST) KerbAllocate(sizeof(KERB_PA_DATA_LIST));
  1024. if (ListElement == NULL)
  1025. {
  1026. Status = STATUS_NO_MEMORY;
  1027. goto Cleanup;
  1028. }
  1029. KerbErr = KerbPackData(
  1030. S4UPreauth,
  1031. KERB_PA_FOR_USER_PDU,
  1032. (PULONG) &ListElement->value.preauth_data.length,
  1033. (PUCHAR *) &ListElement->value.preauth_data.value
  1034. );
  1035. if (!KERB_SUCCESS(KerbErr))
  1036. {
  1037. Status = KerbMapKerbError(KerbErr);
  1038. goto Cleanup;
  1039. }
  1040. ListElement->value.preauth_data_type = KRB5_PADATA_FOR_USER;
  1041. ListElement->next = NULL;
  1042. *PreAuthData = ListElement;
  1043. ListElement = NULL;
  1044. Cleanup:
  1045. if (ListElement)
  1046. {
  1047. KerbFreePreAuthData(ListElement);
  1048. }
  1049. return Status;
  1050. }
  1051. //+-------------------------------------------------------------------------
  1052. //
  1053. // Function: KerbBuildS4UPreauth
  1054. //
  1055. // Synopsis: Attempt to gets TGT for an S4U client for name
  1056. // location purposes.
  1057. //
  1058. //
  1059. // Effects:
  1060. //
  1061. // Arguments: LogonSession - Logon session of the service doing the
  1062. // S4U request
  1063. //
  1064. // Requires:
  1065. //
  1066. // Returns:
  1067. //
  1068. // Notes:
  1069. //
  1070. //
  1071. //--------------------------------------------------------------------------
  1072. KERBERR
  1073. KerbBuildS4UPreauth(
  1074. IN OUT PKERB_PA_FOR_USER S4UPreauth,
  1075. IN PKERB_INTERNAL_NAME S4UClientName,
  1076. IN PUNICODE_STRING S4UClientRealm,
  1077. IN OPTIONAL PKERB_MESSAGE_BUFFER AuthorizationData
  1078. )
  1079. {
  1080. KERBERR KerbErr;
  1081. LPSTR PackageName = MICROSOFT_KERBEROS_NAME_A;
  1082. KerbErr = KerbConvertKdcNameToPrincipalName(
  1083. &S4UPreauth->userName,
  1084. S4UClientName
  1085. );
  1086. if (!KERB_SUCCESS(KerbErr))
  1087. {
  1088. goto Cleanup;
  1089. }
  1090. KerbErr = KerbConvertUnicodeStringToRealm(
  1091. &S4UPreauth->userRealm,
  1092. S4UClientRealm
  1093. );
  1094. if (!KERB_SUCCESS(KerbErr))
  1095. {
  1096. goto Cleanup;
  1097. }
  1098. if (ARGUMENT_PRESENT(AuthorizationData))
  1099. {
  1100. S4UPreauth->authorization_data.value = AuthorizationData->Buffer;
  1101. S4UPreauth->authorization_data.length = AuthorizationData->BufferSize;
  1102. S4UPreauth->bit_mask |= KERB_PA_FOR_USER_authorization_data_present;
  1103. }
  1104. S4UPreauth->authentication_package = PackageName;
  1105. Cleanup:
  1106. return KerbErr;
  1107. }
  1108. //+-------------------------------------------------------------------------
  1109. //
  1110. // Function: KerbGetTgtToS4URealm
  1111. //
  1112. // Synopsis: We need a TGT to the client realm under the caller's cred's
  1113. // so we can make a S4U TGS_REQ.
  1114. //
  1115. //
  1116. // Effects:
  1117. //
  1118. // Arguments: LogonSession - Logon session of the service doing the
  1119. // S4U request
  1120. //
  1121. // Requires:
  1122. //
  1123. // Returns:
  1124. //
  1125. // Notes:
  1126. //
  1127. //
  1128. //--------------------------------------------------------------------------
  1129. NTSTATUS
  1130. KerbGetTgtToS4URealm(
  1131. IN PKERB_LOGON_SESSION CallerLogonSession,
  1132. IN PKERB_CREDENTIAL Credential,
  1133. IN PUNICODE_STRING S4UClientRealm,
  1134. IN OUT PKERB_TICKET_CACHE_ENTRY * S4UTgt,
  1135. IN ULONG Flags,
  1136. IN ULONG TicketOptions,
  1137. IN ULONG EncryptionType
  1138. )
  1139. {
  1140. NTSTATUS Status;
  1141. ULONG RetryFlags = 0;
  1142. BOOLEAN CrossRealm = FALSE;
  1143. BOOLEAN TicketCacheLocked = FALSE, LogonSessionsLocked = FALSE;
  1144. PKERB_TICKET_CACHE_ENTRY TicketGrantingTicket = NULL;
  1145. PKERB_TICKET_CACHE_ENTRY LastTgt = NULL;
  1146. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
  1147. PKERB_KDC_REPLY KdcReply = NULL;
  1148. PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody = NULL;
  1149. PKERB_INTERNAL_NAME TargetTgtKdcName = NULL;
  1150. UNICODE_STRING ClientRealm = NULL_UNICODE_STRING;
  1151. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials = NULL;
  1152. *S4UTgt = NULL;
  1153. KerbReadLockLogonSessions(CallerLogonSession);
  1154. LogonSessionsLocked = TRUE;
  1155. if ((Credential != NULL) && (Credential->SuppliedCredentials != NULL))
  1156. {
  1157. PrimaryCredentials = Credential->SuppliedCredentials;
  1158. }
  1159. else
  1160. {
  1161. PrimaryCredentials = &CallerLogonSession->PrimaryCredentials;
  1162. }
  1163. Status = KerbGetTgtForService(
  1164. CallerLogonSession,
  1165. Credential,
  1166. NULL,
  1167. NULL,
  1168. S4UClientRealm,
  1169. 0,
  1170. &TicketGrantingTicket,
  1171. &CrossRealm
  1172. );
  1173. if (!NT_SUCCESS(Status))
  1174. {
  1175. goto Cleanup;
  1176. }
  1177. //
  1178. // If this isn't cross realm, then we've got a TGT to the realm.
  1179. // return it and bail.
  1180. //
  1181. if (!CrossRealm)
  1182. {
  1183. *S4UTgt = TicketGrantingTicket;
  1184. TicketGrantingTicket = NULL;
  1185. goto Cleanup;
  1186. }
  1187. if (!KERB_SUCCESS(KerbBuildFullServiceKdcName(
  1188. S4UClientRealm,
  1189. &KerbGlobalKdcServiceName,
  1190. KRB_NT_SRV_INST,
  1191. &TargetTgtKdcName
  1192. )))
  1193. {
  1194. Status = STATUS_INSUFFICIENT_RESOURCES;
  1195. goto Cleanup;
  1196. }
  1197. //
  1198. // Copy out the client realm name which is used when obtaining the ticket
  1199. //
  1200. Status = KerbDuplicateString(
  1201. &ClientRealm,
  1202. &PrimaryCredentials->DomainName
  1203. );
  1204. if (!NT_SUCCESS(Status))
  1205. {
  1206. goto Cleanup;
  1207. }
  1208. if (LogonSessionsLocked)
  1209. {
  1210. KerbUnlockLogonSessions(CallerLogonSession);
  1211. LogonSessionsLocked = FALSE;
  1212. }
  1213. //
  1214. // Do some referral chasing to get our ticket grantin ticket.
  1215. //
  1216. while (!RtlEqualUnicodeString(
  1217. S4UClientRealm,
  1218. &TicketGrantingTicket->TargetDomainName,
  1219. TRUE ))
  1220. {
  1221. //
  1222. // If we just got two TGTs for the same domain, then we must have
  1223. // gotten as far as we can. Chances our our RealTargetRealm is a
  1224. // variation of what the KDC hands out.
  1225. //
  1226. // Typically, this call handles the Netbios / DNS case, where the
  1227. // TGT we're looking for (netbios form) is one we just got back.
  1228. //
  1229. if ((LastTgt != NULL) &&
  1230. RtlEqualUnicodeString(
  1231. &LastTgt->TargetDomainName,
  1232. &TicketGrantingTicket->TargetDomainName,
  1233. TRUE ))
  1234. {
  1235. if (TicketCacheLocked)
  1236. {
  1237. KerbUnlockTicketCache();
  1238. TicketCacheLocked = FALSE;
  1239. }
  1240. KerbSetTicketCacheEntryTarget(
  1241. S4UClientRealm,
  1242. LastTgt
  1243. );
  1244. D_DebugLog((DEB_TRACE_CTXT, "Got two TGTs for same realm (%wZ), bailing out of referral loop\n",
  1245. &LastTgt->TargetDomainName));
  1246. break;
  1247. }
  1248. D_DebugLog((DEB_TRACE_CTXT, "Getting referral TGT for \n"));
  1249. D_KerbPrintKdcName((DEB_TRACE_CTXT, TargetTgtKdcName));
  1250. D_KerbPrintKdcName((DEB_TRACE_CTXT, TicketGrantingTicket->ServiceName));
  1251. if (TicketCacheLocked)
  1252. {
  1253. KerbUnlockTicketCache();
  1254. TicketCacheLocked = FALSE;
  1255. }
  1256. //
  1257. // Cleanup old state
  1258. //
  1259. KerbFreeTgsReply(KdcReply);
  1260. KerbFreeKdcReplyBody(KdcReplyBody);
  1261. KdcReply = NULL;
  1262. KdcReplyBody = NULL;
  1263. Status = KerbGetTgsTicket(
  1264. &ClientRealm,
  1265. TicketGrantingTicket,
  1266. TargetTgtKdcName,
  1267. FALSE,
  1268. TicketOptions,
  1269. EncryptionType,
  1270. NULL,
  1271. NULL, // no PA data here.
  1272. NULL, // no tgt reply since target is krbtgt
  1273. NULL,
  1274. NULL, // let kdc determine end time
  1275. &KdcReply,
  1276. &KdcReplyBody,
  1277. &RetryFlags
  1278. );
  1279. if (!NT_SUCCESS(Status))
  1280. {
  1281. DebugLog((DEB_WARN,"Failed to get TGS ticket for service 0x%x :",
  1282. Status ));
  1283. KerbPrintKdcName(DEB_WARN, TargetTgtKdcName);
  1284. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  1285. //
  1286. // We want to map cross-domain failures to failures indicating
  1287. // that a KDC could not be found. This means that for Kerberos
  1288. // logons, the negotiate code will retry with a different package
  1289. //
  1290. // if (Status == STATUS_NO_TRUST_SAM_ACCOUNT)
  1291. // {
  1292. // Status = STATUS_NO_LOGON_SERVERS;
  1293. // }
  1294. goto Cleanup;
  1295. }
  1296. //
  1297. // Now we have a ticket - Cache it
  1298. //
  1299. DsysAssert( !LogonSessionsLocked );
  1300. KerbWriteLockLogonSessions(CallerLogonSession);
  1301. LogonSessionsLocked = TRUE;
  1302. Status = KerbCreateTicketCacheEntry(
  1303. KdcReply,
  1304. KdcReplyBody,
  1305. NULL, // no target name
  1306. NULL, // no targe realm
  1307. 0, // no flags
  1308. &CallerLogonSession->PrimaryCredentials.AuthenticationTicketCache,
  1309. NULL, // no credential key
  1310. &TicketCacheEntry
  1311. );
  1312. KerbUnlockLogonSessions(CallerLogonSession);
  1313. LogonSessionsLocked = FALSE;
  1314. if (!NT_SUCCESS(Status))
  1315. {
  1316. goto Cleanup;
  1317. }
  1318. if (LastTgt != NULL)
  1319. {
  1320. KerbDereferenceTicketCacheEntry(LastTgt);
  1321. LastTgt = NULL;
  1322. }
  1323. LastTgt = TicketGrantingTicket;
  1324. TicketGrantingTicket = TicketCacheEntry;
  1325. TicketCacheEntry = NULL;
  1326. DsysAssert( !TicketCacheLocked );
  1327. KerbReadLockTicketCache();
  1328. TicketCacheLocked = TRUE;
  1329. } // ** WHILE **
  1330. *S4UTgt = TicketGrantingTicket;
  1331. TicketGrantingTicket = NULL;
  1332. Cleanup:
  1333. if (TicketCacheLocked)
  1334. {
  1335. KerbUnlockTicketCache();
  1336. }
  1337. if (LogonSessionsLocked)
  1338. {
  1339. KerbUnlockLogonSessions(CallerLogonSession);
  1340. }
  1341. KerbFreeTgsReply( KdcReply );
  1342. KerbFreeKdcReplyBody( KdcReplyBody );
  1343. KerbFreeKdcName( &TargetTgtKdcName );
  1344. if (TicketGrantingTicket != NULL)
  1345. {
  1346. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  1347. }
  1348. if (LastTgt != NULL)
  1349. {
  1350. KerbDereferenceTicketCacheEntry(LastTgt);
  1351. }
  1352. KerbFreeString( &ClientRealm );
  1353. return Status;
  1354. }
  1355. //+-------------------------------------------------------------------------
  1356. //
  1357. // Function: KerbGetS4UTargetName
  1358. //
  1359. // Synopsis: Attempt to gets target for TGS_REQ
  1360. //
  1361. //
  1362. // Effects:
  1363. //
  1364. // Arguments: LogonSession - Logon session of the service doing the
  1365. // S4U request
  1366. //
  1367. // Requires:
  1368. //
  1369. // Returns:
  1370. //
  1371. // Notes:
  1372. //
  1373. //
  1374. //----------------------------------
  1375. NTSTATUS
  1376. KerbGetS4UTargetName(
  1377. IN OUT PKERB_INTERNAL_NAME * NewTarget,
  1378. IN PKERB_PRIMARY_CREDENTIAL Cred,
  1379. IN PLUID CallerLuid,
  1380. IN OUT PBOOLEAN Upn
  1381. )
  1382. {
  1383. NTSTATUS Status = STATUS_SUCCESS;
  1384. USHORT i;
  1385. BOOLEAN Found = FALSE;
  1386. PKERB_INTERNAL_NAME FinalTarget = NULL;
  1387. LUID System = SYSTEM_LUID;
  1388. LUID NetworkService = NETWORKSERVICE_LUID;
  1389. *NewTarget = NULL;
  1390. *Upn = FALSE;
  1391. if (RtlEqualLuid(&System, CallerLuid) ||
  1392. RtlEqualLuid(&NetworkService, CallerLuid))
  1393. {
  1394. KerbGlobalReadLock();
  1395. Status = KerbDuplicateKdcName(
  1396. NewTarget,
  1397. KerbGlobalMitMachineServiceName
  1398. );
  1399. KerbGlobalReleaseLock();
  1400. goto cleanup;
  1401. }
  1402. //
  1403. // This is a non-system caller. We'll want to use a UPN for the
  1404. // target name - do we have a UPN in the primary credential? If not,
  1405. // fake one up based on the "implicit" UPN.
  1406. //
  1407. if (Cred->DomainName.Length == 0)
  1408. {
  1409. //
  1410. // Better have an @ sign in the user name, or we're through.
  1411. //
  1412. for (i=0;i < Cred->UserName.Length ;i++)
  1413. {
  1414. if (Cred->UserName.Buffer[i] == L'@')
  1415. {
  1416. Found = TRUE;
  1417. break;
  1418. }
  1419. }
  1420. if (!Found)
  1421. {
  1422. Status = SEC_E_NO_CREDENTIALS;
  1423. D_DebugLog((DEB_ERROR, "Invalid user name for S4u\n"));
  1424. goto cleanup;
  1425. }
  1426. FinalTarget = (PKERB_INTERNAL_NAME) MIDL_user_allocate(
  1427. KERB_INTERNAL_NAME_SIZE(1) +
  1428. Cred->UserName.MaximumLength
  1429. );
  1430. if (FinalTarget == NULL)
  1431. {
  1432. Status = STATUS_INSUFFICIENT_RESOURCES;
  1433. goto cleanup;
  1434. }
  1435. FinalTarget->NameCount = 1;
  1436. FinalTarget->NameType = KRB_NT_ENTERPRISE_PRINCIPAL;
  1437. Status = KerbDuplicateString(
  1438. &FinalTarget->Names[0],
  1439. &Cred->UserName
  1440. );
  1441. if (!NT_SUCCESS(Status))
  1442. {
  1443. goto cleanup;
  1444. }
  1445. }
  1446. else
  1447. {
  1448. //
  1449. // Craft a UPN out of thin air :)
  1450. //
  1451. PWSTR Offset;
  1452. FinalTarget = (PKERB_INTERNAL_NAME) MIDL_user_allocate(
  1453. KERB_INTERNAL_NAME_SIZE(1) +
  1454. Cred->UserName.Length +
  1455. Cred->DomainName.Length +
  1456. (2*sizeof(WCHAR))
  1457. );
  1458. if (FinalTarget == NULL)
  1459. {
  1460. Status = STATUS_INSUFFICIENT_RESOURCES;
  1461. goto cleanup;
  1462. }
  1463. FinalTarget->NameCount = 1;
  1464. FinalTarget->NameType = KRB_NT_ENTERPRISE_PRINCIPAL;
  1465. FinalTarget->Names[0].Length = Cred->UserName.Length +
  1466. Cred->DomainName.Length +
  1467. sizeof(WCHAR);
  1468. FinalTarget->Names[0].MaximumLength = Cred->UserName.Length +
  1469. Cred->DomainName.Length +
  1470. (2*sizeof(WCHAR));
  1471. FinalTarget->Names[0].Buffer = (PWSTR) RtlOffsetToPointer(FinalTarget, sizeof(KERB_INTERNAL_NAME));
  1472. RtlCopyMemory(
  1473. FinalTarget->Names[0].Buffer,
  1474. Cred->UserName.Buffer,
  1475. Cred->UserName.Length
  1476. );
  1477. Offset = (PWSTR) FinalTarget->Names[0].Buffer + (Cred->UserName.Length/sizeof(WCHAR));
  1478. (*Offset) = L'@';
  1479. Offset++;
  1480. RtlCopyMemory(
  1481. Offset,
  1482. Cred->DomainName.Buffer,
  1483. Cred->DomainName.Length
  1484. );
  1485. }
  1486. *Upn = TRUE;
  1487. *NewTarget = FinalTarget;
  1488. cleanup:
  1489. return Status;
  1490. }
  1491. //+-------------------------------------------------------------------------
  1492. //
  1493. // Function: KerbGetS4UServiceTicket
  1494. //
  1495. // Synopsis: Gets an s4uself service ticket.
  1496. //
  1497. //
  1498. // Effects:
  1499. //
  1500. // Arguments: LogonSession - Logon session of the service doing the
  1501. // S4U request
  1502. //
  1503. // Requires:
  1504. //
  1505. // Returns:
  1506. //
  1507. // Notes:
  1508. //
  1509. //
  1510. //--------------------------------------------------------------------------
  1511. NTSTATUS
  1512. KerbGetS4USelfServiceTicket(
  1513. IN PKERB_LOGON_SESSION CallerLogonSession,
  1514. IN PKERB_LOGON_SESSION NewLogonSession,
  1515. IN PKERB_CREDENTIAL Credential,
  1516. IN PKERB_INTERNAL_NAME S4UClientName,
  1517. IN PKERB_INTERNAL_NAME AltS4UClientName,
  1518. IN PUNICODE_STRING S4UClientRealm,
  1519. IN OUT PKERB_TICKET_CACHE_ENTRY * S4UTicket,
  1520. IN ULONG Flags,
  1521. IN ULONG TicketOptions,
  1522. IN ULONG EncryptionType,
  1523. IN OPTIONAL PKERB_MESSAGE_BUFFER AdditionalAuthData
  1524. )
  1525. {
  1526. NTSTATUS Status;
  1527. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
  1528. PKERB_TICKET_CACHE_ENTRY TicketGrantingTicket = NULL;
  1529. PKERB_TICKET_CACHE_ENTRY LastTgt = NULL;
  1530. PKERB_KDC_REPLY KdcReply = NULL;
  1531. PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody = NULL;
  1532. PKERB_PA_DATA_LIST S4UPaDataList = NULL;
  1533. BOOLEAN LogonSessionsLocked = FALSE;
  1534. ULONG S4UEvidenceTicketCacheFlags = 0;
  1535. KERB_ENCRYPTION_KEY U2UServerSKey = {0};
  1536. KERB_TGT_REPLY FakeTgtReply = {0};
  1537. PKERB_TICKET_CACHE_ENTRY pRealTicketGrantingTicket = NULL;
  1538. UNICODE_STRING NoTargetName = {0};
  1539. PKERB_INTERNAL_NAME RealTargetName = NULL;
  1540. PKERB_INTERNAL_NAME TargetName = NULL;
  1541. UNICODE_STRING RealTargetRealm = NULL_UNICODE_STRING;
  1542. PKERB_INTERNAL_NAME TargetTgtKdcName = NULL;
  1543. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials = NULL;
  1544. BOOLEAN UsedCredentials = FALSE, Upn = FALSE;
  1545. UNICODE_STRING ClientRealm = NULL_UNICODE_STRING;
  1546. BOOLEAN CacheTicket = KerbGlobalCacheS4UTicket;
  1547. ULONG ReferralCount = 0;
  1548. ULONG RetryFlags = 0;
  1549. PLUID CallingLogonId;
  1550. KERB_PA_FOR_USER S4UPreAuth = {0};
  1551. BOOLEAN fMITRetryAlreadyMade = FALSE;
  1552. BOOLEAN TgtRetryMade = FALSE;
  1553. TimeStamp RequestBodyEndTime;
  1554. GetSystemTimeAsFileTime((PFILETIME) &RequestBodyEndTime);
  1555. RequestBodyEndTime.QuadPart += KerbGetTime(KerbGlobalS4UTicketLifetime);
  1556. //
  1557. // Check to see if the credential has any primary credentials
  1558. //
  1559. TGTRetry:
  1560. DsysAssert( !LogonSessionsLocked );
  1561. KerbReadLockLogonSessions(CallerLogonSession);
  1562. LogonSessionsLocked = TRUE;
  1563. if ((Credential != NULL) && (Credential->SuppliedCredentials != NULL))
  1564. {
  1565. PrimaryCredentials = Credential->SuppliedCredentials;
  1566. UsedCredentials = TRUE;
  1567. CallingLogonId = &Credential->LogonId;
  1568. }
  1569. else
  1570. {
  1571. PrimaryCredentials = &CallerLogonSession->PrimaryCredentials;
  1572. CallingLogonId = &CallerLogonSession->LogonId;
  1573. }
  1574. Status = KerbGetS4UTargetName(
  1575. &TargetName,
  1576. PrimaryCredentials,
  1577. CallingLogonId,
  1578. &Upn
  1579. );
  1580. if (!NT_SUCCESS(Status))
  1581. {
  1582. goto Cleanup;
  1583. }
  1584. if ((Flags & (KERB_GET_TICKET_NO_CACHE | KERB_TARGET_DID_ALTNAME_LOOKUP)) == 0)
  1585. {
  1586. TicketCacheEntry = KerbLocateS4UTicketCacheEntry(
  1587. &PrimaryCredentials->S4UTicketCache,
  1588. CallingLogonId,
  1589. S4UClientName,
  1590. S4UClientRealm,
  1591. NULL,
  1592. 0
  1593. );
  1594. }
  1595. else if (( Flags & KERB_GET_TICKET_NO_CACHE ) != 0 )
  1596. {
  1597. CacheTicket = FALSE;
  1598. }
  1599. if (TicketCacheEntry != NULL)
  1600. {
  1601. ULONG TicketFlags;
  1602. ULONG CacheTicketFlags;
  1603. ULONG CacheEncryptionType;
  1604. //
  1605. // Check if the flags are present
  1606. //
  1607. KerbReadLockTicketCache();
  1608. CacheTicketFlags = TicketCacheEntry->TicketFlags;
  1609. CacheEncryptionType = TicketCacheEntry->Ticket.encrypted_part.encryption_type;
  1610. KerbUnlockTicketCache();
  1611. TicketFlags = KerbConvertKdcOptionsToTicketFlags(TicketOptions);
  1612. if (((CacheTicketFlags & TicketFlags) != TicketFlags) ||
  1613. ((EncryptionType != 0) && (CacheEncryptionType != EncryptionType)))
  1614. {
  1615. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  1616. TicketCacheEntry = NULL;
  1617. }
  1618. else
  1619. {
  1620. //
  1621. // Check the ticket time
  1622. //
  1623. if (KerbTicketIsExpiring(TicketCacheEntry, TRUE))
  1624. {
  1625. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  1626. TicketCacheEntry = NULL;
  1627. }
  1628. else
  1629. {
  1630. goto SuccessOut;
  1631. }
  1632. }
  1633. }
  1634. if ( LogonSessionsLocked )
  1635. {
  1636. KerbUnlockLogonSessions(CallerLogonSession);
  1637. LogonSessionsLocked = FALSE;
  1638. }
  1639. //
  1640. // Get a krbtgt/S4URealm ticket
  1641. //
  1642. D_DebugLog((DEB_TRACE_S4U, "KerbGetS4UServiceTicket calling KerbGetTgtToS4URealm ClientRealm %wZ\n", S4UClientRealm));
  1643. Status = KerbGetTgtToS4URealm(
  1644. CallerLogonSession,
  1645. Credential,
  1646. S4UClientRealm,
  1647. &TicketGrantingTicket,
  1648. 0, // tbd: support for these options?
  1649. 0,
  1650. EncryptionType
  1651. );
  1652. if (!NT_SUCCESS(Status))
  1653. {
  1654. DebugLog((DEB_ERROR, "KerbGetS4UServiceTicket cannot get S4U Tgt - %x\n", Status));
  1655. goto Cleanup;
  1656. }
  1657. //
  1658. // Build the preauth for our TGS req
  1659. //
  1660. Status = KerbBuildS4UPreauth(
  1661. &S4UPreAuth,
  1662. S4UClientName,
  1663. S4UClientRealm,
  1664. AdditionalAuthData
  1665. );
  1666. if (!NT_SUCCESS(Status))
  1667. {
  1668. D_DebugLog((DEB_ERROR, "KerbBuildS4UPreauth failed - %x\n",Status));
  1669. goto Cleanup;
  1670. }
  1671. //
  1672. // Pack and sign the preauth based on the session key in the ticketgranting
  1673. // ticket - we'll need to do this now, and for final TGS_REQ
  1674. //
  1675. Status = KerbSignAndPackS4UPreauthData(
  1676. &S4UPaDataList,
  1677. TicketGrantingTicket,
  1678. &S4UPreAuth
  1679. );
  1680. if (!NT_SUCCESS(Status))
  1681. {
  1682. DebugLog((DEB_ERROR, "FAILED to sign S4u preauth (%p) %x\n", TicketGrantingTicket, Status));
  1683. DsysAssert(FALSE);
  1684. goto Cleanup;
  1685. }
  1686. //
  1687. // Copy out the client realm name which is used when obtaining the ticket
  1688. //
  1689. KerbReadLockLogonSessions(CallerLogonSession);
  1690. LogonSessionsLocked = TRUE;
  1691. Status = KerbDuplicateString(
  1692. &ClientRealm,
  1693. &PrimaryCredentials->DomainName
  1694. );
  1695. if (!NT_SUCCESS(Status))
  1696. {
  1697. goto Cleanup;
  1698. }
  1699. DsysAssert( LogonSessionsLocked );
  1700. KerbUnlockLogonSessions(CallerLogonSession);
  1701. LogonSessionsLocked = FALSE;
  1702. ReferralRestart:
  1703. //
  1704. // This is our first S4U TGS_REQ. We'll eventually transit back to our
  1705. // realm. Note: If we get back a referral, the KDC reply is a TGT to
  1706. // another realm, so keep trying, but be sure to use that TGT.
  1707. //
  1708. if (RtlEqualUnicodeString(
  1709. &ClientRealm,
  1710. &TicketGrantingTicket->TargetDomainName,
  1711. TRUE) &&
  1712. (NULL == PrimaryCredentials->Passwords)) // user2user required
  1713. {
  1714. BOOLEAN CrossRealm = FALSE;
  1715. //
  1716. // below call requires ls lock be held...
  1717. //
  1718. KerbReadLockLogonSessions( CallerLogonSession );
  1719. Status = KerbGetTgtForService(
  1720. CallerLogonSession,
  1721. Credential,
  1722. NULL, // no credman cred
  1723. & PrimaryCredentials->DomainName, // supplied realm
  1724. &NoTargetName,
  1725. KERB_TICKET_CACHE_PRIMARY_TGT,
  1726. &pRealTicketGrantingTicket,
  1727. &CrossRealm
  1728. );
  1729. KerbUnlockLogonSessions( CallerLogonSession );
  1730. if (NT_SUCCESS(Status))
  1731. {
  1732. ASSERT(pRealTicketGrantingTicket);
  1733. if (CrossRealm)
  1734. {
  1735. Status = STATUS_INTERNAL_ERROR;
  1736. DebugLog((DEB_ERROR, "KerbGetS4UServiceTicket KLIN(%#x) got unexpected cross realm TGT\n", KLIN(FILENO, __LINE__)));
  1737. }
  1738. else
  1739. {
  1740. FakeTgtReply.version = KERBEROS_VERSION;
  1741. FakeTgtReply.message_type = KRB_TGT_REP;
  1742. FakeTgtReply.ticket = pRealTicketGrantingTicket->Ticket;
  1743. }
  1744. }
  1745. if (!NT_SUCCESS(Status))
  1746. {
  1747. DebugLog((DEB_ERROR, "KerbGetS4UServiceTicket KLIN(%#x) failed to get tgt\n", KLIN(FILENO, __LINE__)));
  1748. goto Cleanup;
  1749. }
  1750. }
  1751. D_DebugLog((DEB_TRACE, "KerbGetS4UServiceTicket KLIN(%#x) calling KerbGetTgsTicket (ReferralRestart)\n", KLIN(FILENO, __LINE__)));
  1752. Status = KerbGetTgsTicket(
  1753. &ClientRealm,
  1754. TicketGrantingTicket,
  1755. TargetName,
  1756. Flags,
  1757. TicketOptions,
  1758. EncryptionType,
  1759. NULL,
  1760. S4UPaDataList,
  1761. pRealTicketGrantingTicket ? &FakeTgtReply : NULL,
  1762. NULL,
  1763. &RequestBodyEndTime,
  1764. &KdcReply,
  1765. &KdcReplyBody,
  1766. &RetryFlags
  1767. );
  1768. if (STATUS_USER2USER_REQUIRED == Status)
  1769. {
  1770. Status = STATUS_SUCCESS;
  1771. if (!pRealTicketGrantingTicket)
  1772. {
  1773. BOOLEAN CrossRealm = FALSE;
  1774. KerbReadLockLogonSessions( CallerLogonSession );
  1775. Status = KerbGetTgtForService(
  1776. CallerLogonSession,
  1777. Credential,
  1778. NULL, // no credman cred
  1779. & PrimaryCredentials->DomainName, // supplied realm
  1780. &NoTargetName,
  1781. KERB_TICKET_CACHE_PRIMARY_TGT,
  1782. &pRealTicketGrantingTicket,
  1783. &CrossRealm
  1784. );
  1785. KerbUnlockLogonSessions( CallerLogonSession );
  1786. if (NT_SUCCESS(Status) )
  1787. {
  1788. ASSERT(pRealTicketGrantingTicket);
  1789. if (!CrossRealm)
  1790. {
  1791. FakeTgtReply.version = KERBEROS_VERSION;
  1792. FakeTgtReply.message_type = KRB_TGT_REP;
  1793. FakeTgtReply.ticket = pRealTicketGrantingTicket->Ticket;
  1794. }
  1795. else
  1796. {
  1797. Status = STATUS_INTERNAL_ERROR;
  1798. DebugLog((DEB_ERROR, "KerbGetS4UServiceTicket KLIN(%#x) got unexpected cross realm TGT\n", KLIN(FILENO, __LINE__)));
  1799. }
  1800. }
  1801. if (!NT_SUCCESS(Status))
  1802. {
  1803. DebugLog((DEB_ERROR, "KerbGetS4UServiceTicket KLIN(%#x) failed to get tgt\n", KLIN(FILENO, __LINE__)));
  1804. goto Cleanup;
  1805. }
  1806. }
  1807. if (NT_SUCCESS(Status))
  1808. {
  1809. D_DebugLog((DEB_TRACE_U2U, "KerbGetS4UServiceTicket KLIN(%#x) calling KerbGetTgsTicket\n", KLIN(FILENO, __LINE__)));
  1810. Status = KerbGetTgsTicket(
  1811. &ClientRealm,
  1812. TicketGrantingTicket,
  1813. TargetName,
  1814. FALSE,
  1815. TicketOptions,
  1816. EncryptionType,
  1817. NULL,
  1818. S4UPaDataList,
  1819. &FakeTgtReply,
  1820. NULL,
  1821. &RequestBodyEndTime,
  1822. &KdcReply,
  1823. &KdcReplyBody,
  1824. &RetryFlags
  1825. );
  1826. }
  1827. if (!NT_SUCCESS(Status))
  1828. {
  1829. DebugLog((DEB_ERROR, "KerbGetS4UServiceTicket failed to get TGS ticket for service vis u2u 0x%x\n", Status));
  1830. goto Cleanup;
  1831. }
  1832. }
  1833. if (NT_SUCCESS(Status) && pRealTicketGrantingTicket)
  1834. {
  1835. KerbReadLockTicketCache();
  1836. KerbDuplicateKey(&U2UServerSKey, &(pRealTicketGrantingTicket->SessionKey));
  1837. S4UEvidenceTicketCacheFlags |= KERB_TICKET_CACHE_TKT_ENC_IN_SKEY;
  1838. KerbUnlockTicketCache();
  1839. }
  1840. //
  1841. // We're done w/ S4UTgt. Deref, and check
  1842. // for errors
  1843. //
  1844. if (TicketGrantingTicket != NULL)
  1845. {
  1846. //
  1847. // Check for bad option TGT purging
  1848. //
  1849. if (((RetryFlags & KERB_RETRY_WITH_NEW_TGT) != 0) && !TgtRetryMade)
  1850. {
  1851. DebugLog((DEB_WARN, "Doing TGT retry - %p\n", TicketGrantingTicket));
  1852. //
  1853. // Unlink && purge bad tgt
  1854. //
  1855. KerbRemoveTicketCacheEntry(TicketGrantingTicket);
  1856. KerbDereferenceTicketCacheEntry(TicketGrantingTicket); // free from list
  1857. TgtRetryMade = TRUE;
  1858. TicketGrantingTicket = NULL;
  1859. goto TGTRetry;
  1860. }
  1861. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  1862. TicketGrantingTicket = NULL;
  1863. }
  1864. if (!NT_SUCCESS(Status))
  1865. {
  1866. //
  1867. // Check for the MIT retry case
  1868. // TBD: Clean this "code" up...
  1869. //
  1870. if (((RetryFlags & KERB_MIT_NO_CANONICALIZE_RETRY) != 0)
  1871. && (!fMITRetryAlreadyMade))
  1872. {
  1873. Status = KerbMITGetMachineDomain(
  1874. TargetName,
  1875. S4UClientRealm,
  1876. &TicketGrantingTicket
  1877. );
  1878. if (!NT_SUCCESS(Status))
  1879. {
  1880. DebugLog((DEB_ERROR,"Failed Query policy information %ws, line %d\n", THIS_FILE, __LINE__));
  1881. goto Cleanup;
  1882. }
  1883. fMITRetryAlreadyMade = TRUE;
  1884. goto TGTRetry;
  1885. }
  1886. DebugLog((DEB_WARN, "Failed to get TGS ticket for service 0x%x : \n", Status));
  1887. KerbPrintKdcName(DEB_WARN, TargetName);
  1888. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  1889. goto Cleanup;
  1890. }
  1891. //
  1892. // Check for referral info in the name
  1893. // Should be there if S4Urealm != Our Realm
  1894. Status = KerbGetReferralNames(
  1895. KdcReplyBody,
  1896. TargetName,
  1897. &RealTargetRealm
  1898. );
  1899. if (!NT_SUCCESS(Status))
  1900. {
  1901. goto Cleanup;
  1902. }
  1903. //
  1904. // If this is not a referral ticket, just cache it and return
  1905. // the cache entry.
  1906. //
  1907. if (RealTargetRealm.Length == 0)
  1908. {
  1909. //
  1910. // Now we have a ticket - lets cache it
  1911. //
  1912. KerbWriteLockLogonSessions(CallerLogonSession);
  1913. Status = KerbCacheS4UTicket(
  1914. CallerLogonSession,
  1915. KdcReply,
  1916. KdcReplyBody,
  1917. TargetName,
  1918. S4UClientName,
  1919. AltS4UClientName,
  1920. S4UClientRealm,
  1921. S4UEvidenceTicketCacheFlags,
  1922. CacheTicket ? &PrimaryCredentials->S4UTicketCache : NULL,
  1923. &TicketCacheEntry
  1924. );
  1925. KerbUnlockLogonSessions(CallerLogonSession);
  1926. if (!NT_SUCCESS(Status))
  1927. {
  1928. goto Cleanup;
  1929. }
  1930. //
  1931. // We're done, so get out of here.
  1932. //
  1933. goto SuccessOut;
  1934. }
  1935. //
  1936. // The server referred us to another domain. Get the service's full
  1937. // name from the ticket and try to find a TGT in that domain.
  1938. //
  1939. Status = KerbDuplicateKdcName(
  1940. &RealTargetName,
  1941. TargetName
  1942. );
  1943. if (!NT_SUCCESS(Status))
  1944. {
  1945. goto Cleanup;
  1946. }
  1947. D_DebugLog((DEB_TRACE_CTXT, "KerbGetS4UServiceTicket got referral ticket for service "));
  1948. D_KerbPrintKdcName((DEB_TRACE_CTXT, TargetName));
  1949. D_DebugLog((DEB_TRACE_CTXT, "in realm %wZ ", &RealTargetRealm));
  1950. //
  1951. // Turn the KDC reply (xrealm tgt w/ s4u pac) into something we can use,
  1952. // but *don't* cache it.
  1953. //
  1954. Status = KerbCreateTicketCacheEntry(
  1955. KdcReply,
  1956. KdcReplyBody,
  1957. NULL, // no target name
  1958. NULL,
  1959. 0, // no flags
  1960. NULL, // don't cache
  1961. NULL, // no credential key
  1962. &TicketCacheEntry
  1963. );
  1964. if (!NT_SUCCESS(Status))
  1965. {
  1966. goto Cleanup;
  1967. }
  1968. TicketGrantingTicket = TicketCacheEntry;
  1969. TicketCacheEntry = NULL;
  1970. //
  1971. // cleanup
  1972. //
  1973. KerbFreeTgsReply(KdcReply);
  1974. KerbFreeKdcReplyBody(KdcReplyBody);
  1975. KdcReply = NULL;
  1976. KdcReplyBody = NULL;
  1977. //
  1978. // Now we are in a case where we have a realm to aim for and a TGT. While
  1979. // we don't have a TGT for the target realm, get one.
  1980. //
  1981. if (!KERB_SUCCESS(KerbBuildFullServiceKdcName(
  1982. &RealTargetRealm,
  1983. &KerbGlobalKdcServiceName,
  1984. KRB_NT_SRV_INST,
  1985. &TargetTgtKdcName
  1986. )))
  1987. {
  1988. Status = STATUS_INSUFFICIENT_RESOURCES;
  1989. goto Cleanup;
  1990. }
  1991. //
  1992. // Referral chasing code block - very important to get right
  1993. // If we know the "real" target realm, eg. from GC, then
  1994. // we'll walk trusts until we hit "real" target realm.
  1995. //
  1996. while (!RtlEqualUnicodeString(
  1997. &RealTargetRealm,
  1998. &TicketGrantingTicket->TargetDomainName,
  1999. TRUE))
  2000. {
  2001. //
  2002. // If we just got two TGTs for the same domain, then we must have
  2003. // gotten as far as we can. Chances our our RealTargetRealm is a
  2004. // variation of what the KDC hands out.
  2005. //
  2006. D_DebugLog((DEB_TRACE_CTXT, "RealTargetRealm %wZ\n", &RealTargetRealm));
  2007. D_DebugLog((DEB_TRACE_CTXT, "TGT %wZ\n", &TicketGrantingTicket->TargetDomainName));
  2008. if ((LastTgt != NULL) &&
  2009. RtlEqualUnicodeString(
  2010. &LastTgt->TargetDomainName,
  2011. &TicketGrantingTicket->TargetDomainName,
  2012. TRUE ))
  2013. {
  2014. KerbSetTicketCacheEntryTarget(
  2015. &RealTargetRealm,
  2016. LastTgt
  2017. );
  2018. D_DebugLog((DEB_TRACE_CTXT, "Got two TGTs for same realm (%wZ), S4U\n",
  2019. &LastTgt->TargetDomainName));
  2020. break;
  2021. }
  2022. D_DebugLog((DEB_TRACE_CTXT, "KerbGetS4UServiceTicket getting referral TGT for TargetTgtKdcName "));
  2023. D_KerbPrintKdcName((DEB_TRACE_CTXT, TargetTgtKdcName));
  2024. //
  2025. // Cleanup old state
  2026. //
  2027. KerbFreeTgsReply(KdcReply);
  2028. KerbFreeKdcReplyBody(KdcReplyBody);
  2029. KdcReply = NULL;
  2030. KdcReplyBody = NULL;
  2031. Status = KerbGetTgsTicket(
  2032. &ClientRealm,
  2033. TicketGrantingTicket,
  2034. TargetTgtKdcName,
  2035. FALSE,
  2036. TicketOptions,
  2037. EncryptionType,
  2038. NULL,
  2039. NULL,
  2040. NULL, // no tgt reply since target is krbtgt
  2041. NULL,
  2042. &RequestBodyEndTime,
  2043. &KdcReply,
  2044. &KdcReplyBody,
  2045. &RetryFlags
  2046. );
  2047. if (!NT_SUCCESS(Status))
  2048. {
  2049. DebugLog((DEB_WARN, "KerbGetTgsTicket failed to get TGS ticket for service 0x%x :", Status));
  2050. KerbPrintKdcName(DEB_WARN, TargetTgtKdcName);
  2051. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  2052. //
  2053. // We want to map cross-domain failures to failures indicating
  2054. // that a KDC could not be found. This means that for Kerberos
  2055. // logons, the negotiate code will retry with a different package
  2056. //
  2057. // if (Status == STATUS_NO_TRUST_SAM_ACCOUNT)
  2058. // {
  2059. // Status = STATUS_NO_LOGON_SERVERS;
  2060. // }
  2061. goto Cleanup;
  2062. }
  2063. //
  2064. // Now we have a ticket - don't cache it, however, as it has the
  2065. // user's PAC, and shouldn't be re-used later.
  2066. //
  2067. Status = KerbCreateTicketCacheEntry(
  2068. KdcReply,
  2069. KdcReplyBody,
  2070. NULL,
  2071. NULL,
  2072. 0, // no flags
  2073. NULL,
  2074. NULL, // no credential key
  2075. &TicketCacheEntry
  2076. );
  2077. if (!NT_SUCCESS(Status))
  2078. {
  2079. goto Cleanup;
  2080. }
  2081. if (LastTgt != NULL)
  2082. {
  2083. KerbDereferenceTicketCacheEntry(LastTgt);
  2084. LastTgt = NULL;
  2085. }
  2086. LastTgt = TicketGrantingTicket;
  2087. TicketGrantingTicket = TicketCacheEntry;
  2088. TicketCacheEntry = NULL;
  2089. } // ** WHILE **
  2090. //
  2091. // Now we must have a TGT to our service's domain. Get a ticket
  2092. // to the service.
  2093. //
  2094. //
  2095. // Cleanup old state
  2096. //
  2097. KerbFreeTgsReply(KdcReply);
  2098. KerbFreeKdcReplyBody(KdcReplyBody);
  2099. KerbFreePreAuthData(S4UPaDataList);
  2100. KdcReply = NULL;
  2101. KdcReplyBody = NULL;
  2102. S4UPaDataList = NULL;
  2103. //
  2104. // Pack and sign the preauth based on the session key in the ticketgranting
  2105. // ticket
  2106. //
  2107. Status = KerbSignAndPackS4UPreauthData(
  2108. &S4UPaDataList,
  2109. TicketGrantingTicket,
  2110. &S4UPreAuth
  2111. );
  2112. if (!NT_SUCCESS(Status))
  2113. {
  2114. DebugLog((DEB_ERROR, "Error signing S4u preauth (%p) %x\n", TicketGrantingTicket, Status));
  2115. DsysAssert(FALSE);
  2116. goto Cleanup;
  2117. }
  2118. if (NULL != PrimaryCredentials->Passwords) // no user2user
  2119. {
  2120. D_DebugLog((DEB_TRACE, "KerbGetS4UServiceTicket xForest final calling KerbGetTgsTicket\n"));
  2121. Status = KerbGetTgsTicket(
  2122. &ClientRealm,
  2123. TicketGrantingTicket,
  2124. TargetName,
  2125. FALSE,
  2126. TicketOptions,
  2127. EncryptionType,
  2128. NULL,
  2129. S4UPaDataList,
  2130. NULL,
  2131. NULL,
  2132. &RequestBodyEndTime,
  2133. &KdcReply,
  2134. &KdcReplyBody,
  2135. &RetryFlags
  2136. );
  2137. }
  2138. if ((NULL == PrimaryCredentials->Passwords) || (STATUS_USER2USER_REQUIRED == Status))
  2139. {
  2140. BOOLEAN CrossRealm = FALSE;
  2141. if (!pRealTicketGrantingTicket)
  2142. {
  2143. KerbReadLockLogonSessions( CallerLogonSession );
  2144. Status = KerbGetTgtForService(
  2145. CallerLogonSession,
  2146. Credential,
  2147. NULL, // no credman cred
  2148. & PrimaryCredentials->DomainName, // supplied realm
  2149. &NoTargetName,
  2150. KERB_TICKET_CACHE_PRIMARY_TGT,
  2151. &pRealTicketGrantingTicket,
  2152. &CrossRealm
  2153. );
  2154. KerbUnlockLogonSessions( CallerLogonSession );
  2155. if (NT_SUCCESS(Status))
  2156. {
  2157. ASSERT(pRealTicketGrantingTicket);
  2158. if (CrossRealm)
  2159. {
  2160. Status = STATUS_INTERNAL_ERROR;
  2161. DebugLog((DEB_ERROR, "KerbGetS4UServiceTicket got unexpected cross realm TGT\n"));
  2162. }
  2163. else
  2164. {
  2165. FakeTgtReply.version = KERBEROS_VERSION;
  2166. FakeTgtReply.message_type = KRB_TGT_REP;
  2167. FakeTgtReply.ticket = pRealTicketGrantingTicket->Ticket;
  2168. }
  2169. }
  2170. }
  2171. if (NT_SUCCESS(Status))
  2172. {
  2173. D_DebugLog((DEB_TRACE_U2U, "KerbGetS4UServiceTicket xForest final u2u calling KerbGetTgsTicket\n"));
  2174. Status = KerbGetTgsTicket(
  2175. &ClientRealm,
  2176. TicketGrantingTicket,
  2177. TargetName,
  2178. FALSE,
  2179. TicketOptions,
  2180. EncryptionType,
  2181. NULL,
  2182. S4UPaDataList,
  2183. &FakeTgtReply,
  2184. NULL,
  2185. &RequestBodyEndTime,
  2186. &KdcReply,
  2187. &KdcReplyBody,
  2188. &RetryFlags
  2189. );
  2190. }
  2191. if (NT_SUCCESS(Status))
  2192. {
  2193. ASSERT((S4UEvidenceTicketCacheFlags & KERB_TICKET_CACHE_TKT_ENC_IN_SKEY) == 0);
  2194. KerbReadLockTicketCache();
  2195. KerbDuplicateKey(&U2UServerSKey, &(pRealTicketGrantingTicket->SessionKey));
  2196. S4UEvidenceTicketCacheFlags |= KERB_TICKET_CACHE_TKT_ENC_IN_SKEY;
  2197. KerbUnlockTicketCache();
  2198. }
  2199. if (!NT_SUCCESS(Status))
  2200. {
  2201. DebugLog((DEB_ERROR, "KerbGetS4UServiceTicket failed to get TGS ticket for service vis u2u 0x%x\n", Status));
  2202. goto Cleanup;
  2203. }
  2204. }
  2205. else if (!NT_SUCCESS(Status))
  2206. {
  2207. DebugLog((DEB_WARN,"Failed to get TGS ticket for service 0x%x ",
  2208. Status ));
  2209. KerbPrintKdcName(DEB_WARN, RealTargetName);
  2210. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  2211. goto Cleanup;
  2212. }
  2213. //
  2214. // Now that we are in the domain to which we were referred, check for referral
  2215. // info in the name
  2216. //
  2217. KerbFreeString(&RealTargetRealm);
  2218. Status = KerbGetReferralNames(
  2219. KdcReplyBody,
  2220. RealTargetName,
  2221. &RealTargetRealm
  2222. );
  2223. if (!NT_SUCCESS(Status))
  2224. {
  2225. goto Cleanup;
  2226. }
  2227. //
  2228. // If this is not a referral ticket, just cache it and return
  2229. // the cache entry.
  2230. //
  2231. if (RealTargetRealm.Length != 0)
  2232. {
  2233. //
  2234. // To prevent loops, we limit the number of referral we'll take
  2235. //
  2236. if (ReferralCount > KerbGlobalMaxReferralCount)
  2237. {
  2238. DebugLog((DEB_ERROR, "KerbGetS4UServiceTicket Maximum referral count exceeded for name: "));
  2239. KerbPrintKdcName(DEB_ERROR, RealTargetName);
  2240. Status = STATUS_MAX_REFERRALS_EXCEEDED;
  2241. goto Cleanup;
  2242. }
  2243. ReferralCount++;
  2244. //
  2245. // Don't cache the interdomain TGT, as it has PAC info in it.
  2246. //
  2247. Status = KerbCreateTicketCacheEntry(
  2248. KdcReply,
  2249. KdcReplyBody,
  2250. NULL, // no target name
  2251. NULL, // no target realm
  2252. 0, // no flags
  2253. NULL, // don't cache
  2254. NULL, // no credential key
  2255. &TicketCacheEntry
  2256. );
  2257. //
  2258. // Cleanup old state
  2259. //
  2260. KerbFreeTgsReply(KdcReply);
  2261. KerbFreeKdcReplyBody(KdcReplyBody);
  2262. KerbFreePreAuthData(S4UPaDataList);
  2263. KdcReply = NULL;
  2264. KdcReplyBody = NULL;
  2265. S4UPaDataList = NULL;
  2266. if (!NT_SUCCESS(Status))
  2267. {
  2268. goto Cleanup;
  2269. }
  2270. if (LastTgt != NULL)
  2271. {
  2272. KerbDereferenceTicketCacheEntry(LastTgt);
  2273. LastTgt = NULL;
  2274. }
  2275. LastTgt = TicketGrantingTicket;
  2276. TicketGrantingTicket = TicketCacheEntry;
  2277. TicketCacheEntry = NULL;
  2278. D_DebugLog((DEB_TRACE_CTXT, "KerbGetS4UServiceTicket restart referral: %wZ", &RealTargetRealm));
  2279. Status = KerbSignAndPackS4UPreauthData(
  2280. &S4UPaDataList,
  2281. TicketGrantingTicket,
  2282. &S4UPreAuth
  2283. );
  2284. if (!NT_SUCCESS(Status))
  2285. {
  2286. DebugLog((DEB_ERROR, "Error signing S4u preauth (%p) %x\n", TicketGrantingTicket, Status));
  2287. DsysAssert(FALSE);
  2288. goto Cleanup;
  2289. }
  2290. goto ReferralRestart;
  2291. }
  2292. //
  2293. // Now we have a ticket - lets cache it
  2294. //
  2295. //
  2296. // Before doing anything, verify that the client name on the ticket
  2297. // is equal to the client name requested during the S4u
  2298. //
  2299. KerbWriteLockLogonSessions(CallerLogonSession);
  2300. Status = KerbCacheS4UTicket(
  2301. CallerLogonSession,
  2302. KdcReply,
  2303. KdcReplyBody,
  2304. TargetName,
  2305. S4UClientName,
  2306. AltS4UClientName,
  2307. S4UClientRealm,
  2308. S4UEvidenceTicketCacheFlags,
  2309. CacheTicket ? &PrimaryCredentials->S4UTicketCache : NULL,
  2310. &TicketCacheEntry
  2311. );
  2312. KerbUnlockLogonSessions(CallerLogonSession);
  2313. if (!NT_SUCCESS(Status))
  2314. {
  2315. goto Cleanup;
  2316. }
  2317. SuccessOut:
  2318. if (TicketCacheEntry && (S4UEvidenceTicketCacheFlags & KERB_TICKET_CACHE_TKT_ENC_IN_SKEY))
  2319. {
  2320. KerbWriteLockTicketCache();
  2321. ASSERT(TicketCacheEntry->CacheFlags & KERB_TICKET_CACHE_TKT_ENC_IN_SKEY);
  2322. KerbFreeKey(&(TicketCacheEntry->SessionKey));
  2323. DebugLog((DEB_TRACE_U2U, "KerbGetS4UServiceTicket returning u2u SKEY\n"));
  2324. KerbDuplicateKey(&TicketCacheEntry->SessionKey, &U2UServerSKey);
  2325. KerbUnlockTicketCache();
  2326. }
  2327. *S4UTicket = TicketCacheEntry;
  2328. TicketCacheEntry = NULL;
  2329. Cleanup:
  2330. KerbFreeTgsReply( KdcReply );
  2331. KerbFreeKdcReplyBody( KdcReplyBody );
  2332. KerbFreeKdcName( &TargetTgtKdcName );
  2333. KerbFreeString( &RealTargetRealm );
  2334. KerbFreeKdcName(&RealTargetName);
  2335. KerbFreeKdcName(&TargetName);
  2336. KerbFreePrincipalName(&S4UPreAuth.userName);
  2337. KerbFreeRealm(&S4UPreAuth.userRealm);
  2338. KerbFreePreAuthData(S4UPaDataList);
  2339. if (pRealTicketGrantingTicket)
  2340. {
  2341. KerbDereferenceTicketCacheEntry(pRealTicketGrantingTicket);
  2342. }
  2343. if (LogonSessionsLocked)
  2344. {
  2345. KerbUnlockLogonSessions(CallerLogonSession);
  2346. }
  2347. KerbFreeString(&RealTargetRealm);
  2348. //
  2349. // We never cache TGTs in this routine
  2350. // so it's just a blob of memory
  2351. //
  2352. if (TicketGrantingTicket != NULL)
  2353. {
  2354. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  2355. }
  2356. if (LastTgt != NULL)
  2357. {
  2358. KerbDereferenceTicketCacheEntry(LastTgt);
  2359. }
  2360. //
  2361. // If we still have a pointer to the ticket cache entry, free it now.
  2362. //
  2363. if (TicketCacheEntry != NULL)
  2364. {
  2365. KerbRemoveTicketCacheEntry( TicketCacheEntry );
  2366. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  2367. }
  2368. KerbFreeString(&ClientRealm);
  2369. KerbFreeKey(&U2UServerSKey);
  2370. return (Status);
  2371. }
  2372. //+-------------------------------------------------------------------------
  2373. //
  2374. // Function: KerbPrepareEvidenceTicket
  2375. //
  2376. // Synopsis: Decrypts a ticket w/ key1, and encrypts it w/ key2
  2377. //
  2378. // Effects:
  2379. //
  2380. // Arguments: DecryptKey - key used to decrypt ticket
  2381. // EncryptionKey - key used to re-encrypt ticket
  2382. // Ticket - the ticket to decrypt / re-encrypt
  2383. //
  2384. // Requires:
  2385. //
  2386. // Returns:
  2387. //
  2388. // Notes:
  2389. //
  2390. //
  2391. //--------------------------------------------------------------------------
  2392. NTSTATUS
  2393. KerbPrepareEvidenceTicket(
  2394. IN PKERB_ENCRYPTION_KEY DecryptKey,
  2395. IN PKERB_ENCRYPTION_KEY EncryptionKey,
  2396. IN OUT PKERB_TICKET Ticket
  2397. )
  2398. {
  2399. KERBERR KerbErr;
  2400. NTSTATUS Status = STATUS_LOGON_FAILURE;
  2401. PUCHAR EncryptedPart = NULL;
  2402. ULONG EncryptSize;
  2403. SafeAllocaAllocate( EncryptedPart, Ticket->encrypted_part.cipher_text.length );
  2404. if (EncryptedPart == NULL)
  2405. {
  2406. return STATUS_NO_MEMORY;
  2407. }
  2408. EncryptSize = Ticket->encrypted_part.cipher_text.length;
  2409. KerbErr = KerbDecryptDataEx(
  2410. &Ticket->encrypted_part,
  2411. DecryptKey,
  2412. KERB_TICKET_SALT,
  2413. &EncryptSize,
  2414. EncryptedPart
  2415. );
  2416. if (!KERB_SUCCESS( KerbErr ))
  2417. {
  2418. goto Cleanup;
  2419. }
  2420. //
  2421. // Free up the old encrypted_part.
  2422. //
  2423. if ( Ticket->encrypted_part.cipher_text.value != NULL )
  2424. {
  2425. MIDL_user_free( Ticket->encrypted_part.cipher_text.value );
  2426. Ticket->encrypted_part.cipher_text.value = NULL;
  2427. Ticket->encrypted_part.cipher_text.length = 0;
  2428. }
  2429. //
  2430. // allocate sufficient space for the encrypted data.
  2431. //
  2432. KerbErr = KerbAllocateEncryptionBufferWrapper(
  2433. EncryptionKey->keytype,
  2434. EncryptSize,
  2435. &Ticket->encrypted_part.cipher_text.length,
  2436. &Ticket->encrypted_part.cipher_text.value
  2437. );
  2438. if (!KERB_SUCCESS(KerbErr))
  2439. {
  2440. goto Cleanup;
  2441. }
  2442. KerbErr = KerbEncryptDataEx(
  2443. &Ticket->encrypted_part,
  2444. EncryptSize,
  2445. EncryptedPart,
  2446. KERB_NO_KEY_VERSION,
  2447. KERB_TICKET_SALT,
  2448. EncryptionKey
  2449. );
  2450. if (!KERB_SUCCESS( KerbErr ))
  2451. {
  2452. goto Cleanup;
  2453. }
  2454. Status = STATUS_SUCCESS;
  2455. Cleanup:
  2456. if ( !KERB_SUCCESS(KerbErr) &&
  2457. ( Ticket->encrypted_part.cipher_text.value != NULL) )
  2458. {
  2459. MIDL_user_free( Ticket->encrypted_part.cipher_text.value );
  2460. Ticket->encrypted_part.cipher_text.value = NULL;
  2461. Ticket->encrypted_part.cipher_text.length = 0;
  2462. }
  2463. SafeAllocaFree( EncryptedPart );
  2464. return Status;
  2465. }
  2466. //+-------------------------------------------------------------------------
  2467. //
  2468. // Function: KerbGetTicketByS4UProxy
  2469. //
  2470. // Synopsis: Gets a ticket to a service and handles cross-domain referrals
  2471. //
  2472. // Effects:
  2473. //
  2474. // Arguments: LogonSession - the logon session to use for ticket caching
  2475. // and the identity of the caller.
  2476. // TargetName - Name of the target for which to obtain a ticket.
  2477. // TargetDomainName - Domain name of target
  2478. // Flags - Flags about the request
  2479. // TicketOptions - KDC options flags
  2480. // EncryptionType - optional Requested encryption type
  2481. // ErrorMessage - Error message from an AP request containing hints
  2482. // for next ticket.
  2483. // AuthorizationData - Optional authorization data to stick
  2484. // in the ticket.
  2485. // TgtReply - TGT to use for getting a ticket with enc_tkt_in_skey
  2486. // TicketCacheEntry - Receives a referenced ticket cache entry.
  2487. //
  2488. // Requires:
  2489. //
  2490. // Returns:
  2491. //
  2492. // Notes:
  2493. //
  2494. //
  2495. //--------------------------------------------------------------------------
  2496. NTSTATUS
  2497. KerbGetServiceTicketByS4UProxy(
  2498. IN PKERB_LOGON_SESSION LogonSession,
  2499. IN PKERB_LOGON_SESSION CallerLogonSession,
  2500. IN PKERB_CREDENTIAL Credential,
  2501. IN PKERB_TICKET_CACHE_ENTRY EvidenceTicketCacheEntry,
  2502. IN PKERB_INTERNAL_NAME TargetName,
  2503. IN PUNICODE_STRING TargetDomainName,
  2504. IN OPTIONAL PKERB_SPN_CACHE_ENTRY SpnCacheEntry,
  2505. IN ULONG Flags,
  2506. IN OPTIONAL ULONG TicketOptions,
  2507. IN OPTIONAL ULONG EncryptionType,
  2508. IN OPTIONAL PKERB_ERROR ErrorMessage,
  2509. IN OPTIONAL PKERB_AUTHORIZATION_DATA AuthorizationData,
  2510. IN OPTIONAL PKERB_TGT_REPLY TgtReply,
  2511. OUT PKERB_TICKET_CACHE_ENTRY * NewCacheEntry,
  2512. OUT LPGUID pLogonGuid OPTIONAL
  2513. )
  2514. {
  2515. NTSTATUS Status = STATUS_SUCCESS;
  2516. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
  2517. PKERB_TICKET_CACHE_ENTRY TicketGrantingTicket = NULL;
  2518. PKERB_TICKET_CACHE_ENTRY PrimaryTgt = NULL;
  2519. PKERB_TICKET_CACHE_ENTRY LastTgt = NULL;
  2520. PKERB_KDC_REPLY KdcReply = NULL;
  2521. PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody = NULL;
  2522. KERB_TICKET EvidenceTicket = {0};
  2523. PKERB_ENCRYPTION_KEY DecryptKey = NULL;
  2524. BOOLEAN LogonSessionsLocked = FALSE;
  2525. BOOLEAN TicketCacheLocked = FALSE;
  2526. PKERB_INTERNAL_NAME RealTargetName = NULL;
  2527. UNICODE_STRING RealTargetRealm = NULL_UNICODE_STRING;
  2528. UNICODE_STRING SpnTargetRealm = NULL_UNICODE_STRING;
  2529. PKERB_INTERNAL_NAME TargetTgtKdcName = NULL;
  2530. PKERB_PRIMARY_CREDENTIAL ProxyServerCreds = NULL;
  2531. PKERB_PRIMARY_CREDENTIAL PrimaryCreds = NULL;
  2532. UNICODE_STRING ClientRealm = NULL_UNICODE_STRING;
  2533. BOOLEAN CacheTicket = TRUE;
  2534. ULONG ReferralCount = 0;
  2535. ULONG RetryFlags = 0;
  2536. ULONG S4UFlags = S4UCACHE_S4U_AVAILABLE;
  2537. BOOLEAN CacheBasedFailure = FALSE, Update = FALSE;
  2538. TimeStamp RequestBodyEndTime;
  2539. GetSystemTimeAsFileTime((PFILETIME) &RequestBodyEndTime);
  2540. RequestBodyEndTime.QuadPart += KerbGetTime(KerbGlobalS4UTicketLifetime);
  2541. //
  2542. // Get our credentials - TBD: Fester: Supplied creds irrelevant?
  2543. //
  2544. DsysAssert( !LogonSessionsLocked );
  2545. KerbReadLockLogonSessions( LogonSession );
  2546. PrimaryCreds = &LogonSession->PrimaryCredentials;
  2547. LogonSessionsLocked = TRUE;
  2548. ProxyServerCreds = &CallerLogonSession->PrimaryCredentials;
  2549. //
  2550. // Make sure the name is not zero length
  2551. //
  2552. if ((TargetName->NameCount == 0) ||
  2553. (TargetName->Names[0].Length == 0))
  2554. {
  2555. D_DebugLog((DEB_ERROR,"Kdc GetServiceTicket: trying to crack zero length name.\n"));
  2556. Status = SEC_E_TARGET_UNKNOWN;
  2557. goto Cleanup;
  2558. }
  2559. //
  2560. // First check the ticket cache for this logon session. We don't look
  2561. // for the target principal name because we can't be assured that this
  2562. // is a valid principal name for the target. If we are doing user-to-
  2563. // user, don't use the cache because the tgt key may have changed
  2564. //
  2565. if ((TgtReply == NULL) && ((Flags & KERB_GET_TICKET_NO_CACHE) == 0))
  2566. {
  2567. TicketCacheEntry = KerbLocateTicketCacheEntry(
  2568. &PrimaryCreds->ServerTicketCache,
  2569. TargetName,
  2570. TargetDomainName
  2571. );
  2572. }
  2573. else
  2574. {
  2575. //
  2576. // We don't want to cache user-to-user tickets
  2577. //
  2578. CacheTicket = FALSE;
  2579. }
  2580. if (TicketCacheEntry != NULL)
  2581. {
  2582. //
  2583. // If we were given an error message that indicated a bad password
  2584. // throw away the cached ticket
  2585. //
  2586. if (ARGUMENT_PRESENT(ErrorMessage) && ((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_MODIFIED))
  2587. {
  2588. KerbRemoveTicketCacheEntry(TicketCacheEntry);
  2589. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  2590. TicketCacheEntry = NULL;
  2591. }
  2592. else
  2593. {
  2594. ULONG TicketFlags;
  2595. ULONG CacheTicketFlags;
  2596. ULONG CacheEncryptionType;
  2597. //
  2598. // Check if the flags are present
  2599. //
  2600. KerbReadLockTicketCache();
  2601. CacheTicketFlags = TicketCacheEntry->TicketFlags;
  2602. CacheEncryptionType = TicketCacheEntry->Ticket.encrypted_part.encryption_type;
  2603. KerbUnlockTicketCache();
  2604. TicketFlags = KerbConvertKdcOptionsToTicketFlags(TicketOptions);
  2605. if (((CacheTicketFlags & TicketFlags) != TicketFlags) ||
  2606. ((EncryptionType != 0) && (CacheEncryptionType != EncryptionType)))
  2607. {
  2608. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  2609. TicketCacheEntry = NULL;
  2610. }
  2611. else
  2612. {
  2613. //
  2614. // Check the ticket time
  2615. //
  2616. if (KerbTicketIsExpiring(TicketCacheEntry, TRUE))
  2617. {
  2618. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  2619. TicketCacheEntry = NULL;
  2620. }
  2621. else
  2622. {
  2623. *NewCacheEntry = TicketCacheEntry;
  2624. D_DebugLog((DEB_TRACE_S4U,"Found S4U ticket cache entry %x\n", TicketCacheEntry));
  2625. D_KerbPrintKdcName((DEB_TRACE_S4U, TicketCacheEntry->TargetName));
  2626. D_DebugLog((DEB_TRACE_S4U,"Realm - %wZ\n", &TicketCacheEntry->DomainName));
  2627. TicketCacheEntry = NULL;
  2628. goto Cleanup;
  2629. }
  2630. }
  2631. }
  2632. }
  2633. //
  2634. // Determine the state of the SPNCache using information in the credential.
  2635. // Only do this if we've not been handed
  2636. //
  2637. if ( ARGUMENT_PRESENT(SpnCacheEntry) && TargetDomainName->Buffer == NULL )
  2638. {
  2639. Status = KerbGetSpnCacheStatus(
  2640. SpnCacheEntry,
  2641. ProxyServerCreds,
  2642. &SpnTargetRealm
  2643. );
  2644. if (NT_SUCCESS( Status ))
  2645. {
  2646. KerbFreeString(&RealTargetRealm);
  2647. RealTargetRealm = SpnTargetRealm;
  2648. RtlZeroMemory(&SpnTargetRealm, sizeof(UNICODE_STRING));
  2649. D_DebugLog((DEB_TRACE_SPN_CACHE, "Found SPN cache entry - %wZ\n", &RealTargetRealm));
  2650. D_KerbPrintKdcName((DEB_TRACE_SPN_CACHE, TargetName));
  2651. }
  2652. else if ( Status != STATUS_NO_MATCH )
  2653. {
  2654. D_DebugLog((DEB_TRACE_SPN_CACHE, "KerbGetSpnCacheStatus failed %x\n", Status));
  2655. D_DebugLog((DEB_TRACE_SPN_CACHE, "TargetName: \n"));
  2656. D_KerbPrintKdcName((DEB_TRACE_SPN_CACHE, TargetName));
  2657. CacheBasedFailure = TRUE;
  2658. goto Cleanup;
  2659. }
  2660. Status = STATUS_SUCCESS;
  2661. }
  2662. //
  2663. // If the caller wanted any special options, don't cache this ticket.
  2664. //
  2665. if ((TicketOptions != 0) || (EncryptionType != 0) || ((Flags & KERB_GET_TICKET_NO_CACHE) != 0))
  2666. {
  2667. CacheTicket = FALSE;
  2668. }
  2669. DsysAssert( LogonSessionsLocked );
  2670. KerbUnlockLogonSessions(LogonSession);
  2671. LogonSessionsLocked = FALSE;
  2672. //
  2673. // No cached entry was found so go ahead and call the KDC to
  2674. // get the ticket.
  2675. //
  2676. //
  2677. // First call always starts w/ TGT to our domain. This allows us to
  2678. // determine if we've met the requirements of the S4U logon, e.g. is the
  2679. // target name appropriate?
  2680. //
  2681. KerbReadLockLogonSessions( CallerLogonSession );
  2682. PrimaryTgt = KerbLocateTicketCacheEntryByRealm(
  2683. &ProxyServerCreds->AuthenticationTicketCache,
  2684. NULL,
  2685. KERB_TICKET_CACHE_PRIMARY_TGT
  2686. );
  2687. KerbUnlockLogonSessions( CallerLogonSession );
  2688. if ( NULL == PrimaryTgt )
  2689. {
  2690. Status = KerbGetTicketGrantingTicket(
  2691. CallerLogonSession,
  2692. NULL,
  2693. NULL,
  2694. NULL,
  2695. &PrimaryTgt,
  2696. NULL // don't return credential key
  2697. );
  2698. if (!NT_SUCCESS( Status ))
  2699. {
  2700. D_DebugLog((DEB_ERROR, "Failed to get primary TGT from logon session\n"));
  2701. goto Cleanup;
  2702. }
  2703. }
  2704. //
  2705. // Copy out the client realm name which is used when obtaining the ticket
  2706. //
  2707. Status = KerbDuplicateString(
  2708. &ClientRealm,
  2709. &ProxyServerCreds->DomainName
  2710. );
  2711. if (!NT_SUCCESS(Status))
  2712. {
  2713. goto Cleanup;
  2714. }
  2715. //
  2716. // Prepare the evidence ticket.
  2717. //
  2718. if (!KERB_SUCCESS( KerbDuplicateTicket(
  2719. &EvidenceTicket,
  2720. &EvidenceTicketCacheEntry->Ticket
  2721. )))
  2722. {
  2723. Status = STATUS_NO_MEMORY;
  2724. goto Cleanup;
  2725. }
  2726. //
  2727. // Now its's time to make the request.
  2728. //
  2729. D_DebugLog((DEB_TRACE_S4U, "KerbGetServiceTicketByS4UProxy EvidenceTicketCacheEntry %p\n", EvidenceTicketCacheEntry));
  2730. Status = KerbGetTgsTicket(
  2731. &ClientRealm,
  2732. PrimaryTgt,
  2733. TargetName,
  2734. Flags,
  2735. TicketOptions,
  2736. EncryptionType,
  2737. NULL,
  2738. NULL, // no pa data
  2739. TgtReply, // This is for the service directly, so use TGT
  2740. &EvidenceTicket,
  2741. &RequestBodyEndTime,
  2742. &KdcReply,
  2743. &KdcReplyBody,
  2744. &RetryFlags
  2745. );
  2746. if (!NT_SUCCESS( Status ))
  2747. {
  2748. DebugLog((DEB_WARN, "Failed S4Uproxy request %x(%x) \n", Status, RetryFlags));
  2749. goto Cleanup;
  2750. }
  2751. //
  2752. // Check for referral info in the name
  2753. //
  2754. Status = KerbGetReferralNames(
  2755. KdcReplyBody,
  2756. TargetName,
  2757. &RealTargetRealm
  2758. );
  2759. if (!NT_SUCCESS(Status))
  2760. {
  2761. goto Cleanup;
  2762. }
  2763. //
  2764. // If this is not a referral ticket, just cache it and return
  2765. // the cache entry.
  2766. //
  2767. if ( RealTargetRealm.Length == 0 )
  2768. {
  2769. //
  2770. // Now we have a ticket - lets cache it
  2771. //
  2772. KerbReadLockLogonSessions(LogonSession);
  2773. Status = KerbCreateTicketCacheEntry(
  2774. KdcReply,
  2775. KdcReplyBody,
  2776. TargetName,
  2777. TargetDomainName,
  2778. 0,
  2779. CacheTicket ? &PrimaryCreds->ServerTicketCache : NULL,
  2780. NULL, // no credential key
  2781. &TicketCacheEntry
  2782. );
  2783. KerbUnlockLogonSessions( LogonSession );
  2784. if (!NT_SUCCESS( Status ))
  2785. {
  2786. goto Cleanup;
  2787. }
  2788. *NewCacheEntry = TicketCacheEntry;
  2789. TicketCacheEntry = NULL;
  2790. Update = TRUE;
  2791. //
  2792. // We're done, so get out of here.
  2793. //
  2794. goto Cleanup;
  2795. }
  2796. //
  2797. // REFERRAL - The service we're talking to does not live in the caller's
  2798. // account domain.
  2799. //
  2800. //
  2801. // Get the real target name
  2802. //
  2803. Status = KerbDuplicateKdcName(
  2804. &RealTargetName,
  2805. TargetName
  2806. );
  2807. if (!NT_SUCCESS(Status))
  2808. {
  2809. goto Cleanup;
  2810. }
  2811. D_DebugLog((DEB_TRACE_S4U, "Got referral ticket for service \n"));
  2812. D_KerbPrintKdcName((DEB_TRACE_S4U,TargetName));
  2813. D_DebugLog((DEB_TRACE_S4U, "in realm \n"));
  2814. D_KerbPrintKdcName((DEB_TRACE_S4U,RealTargetName));
  2815. //
  2816. // Generate a ticket cache entry for the interdomain
  2817. // TGT accompanying this KdcReply.
  2818. //
  2819. Status = KerbCreateTicketCacheEntry(
  2820. KdcReply,
  2821. KdcReplyBody,
  2822. NULL, // no target name
  2823. NULL, // no target realm
  2824. 0, // no flags
  2825. NULL, // don't cache this one.
  2826. NULL, // don't copy cred key
  2827. &TicketCacheEntry
  2828. );
  2829. if (!NT_SUCCESS( Status ))
  2830. {
  2831. goto Cleanup;
  2832. }
  2833. //
  2834. // We've got a new TGT from the referral.
  2835. // Create a plaintext version of our evidence ticket, and
  2836. // use the session key from the TGT to create an encrypted ticket.
  2837. //
  2838. KerbDereferenceTicketCacheEntry(PrimaryTgt);
  2839. TicketGrantingTicket = TicketCacheEntry;
  2840. TicketCacheEntry = NULL;
  2841. KerbReadLockLogonSessions( CallerLogonSession );
  2842. DecryptKey = KerbGetKeyFromList(
  2843. CallerLogonSession->PrimaryCredentials.Passwords,
  2844. TicketGrantingTicket->SessionKey.keytype
  2845. );
  2846. KerbUnlockLogonSessions( CallerLogonSession );
  2847. if ( DecryptKey == NULL )
  2848. {
  2849. DebugLog((DEB_ERROR, "Could not find a key to decrypt evidence ticket\n"));
  2850. goto Cleanup;
  2851. }
  2852. Status = KerbPrepareEvidenceTicket(
  2853. DecryptKey,
  2854. &TicketGrantingTicket->SessionKey,
  2855. &EvidenceTicket
  2856. );
  2857. if ( Status == STATUS_LOGON_FAILURE )
  2858. {
  2859. //
  2860. // Old Key?
  2861. //
  2862. KerbReadLockLogonSessions( CallerLogonSession );
  2863. if (CallerLogonSession->PrimaryCredentials.OldPasswords != NULL)
  2864. {
  2865. DecryptKey = KerbGetKeyFromList(
  2866. CallerLogonSession->PrimaryCredentials.OldPasswords,
  2867. TicketGrantingTicket->SessionKey.keytype
  2868. );
  2869. if (DecryptKey != NULL )
  2870. {
  2871. Status = KerbPrepareEvidenceTicket(
  2872. DecryptKey,
  2873. &TicketGrantingTicket->SessionKey,
  2874. &EvidenceTicket
  2875. );
  2876. }
  2877. }
  2878. KerbUnlockLogonSessions( CallerLogonSession );
  2879. }
  2880. if (!NT_SUCCESS(Status))
  2881. {
  2882. D_DebugLog((DEB_ERROR, "Failed to prepare evidence ticket\n"));
  2883. goto Cleanup;
  2884. }
  2885. DecryptKey = &TicketGrantingTicket->SessionKey;
  2886. //
  2887. // Now we are in a case where we have a realm to aim for and a TGT. While
  2888. // we don't have a TGT for the target realm, get one.
  2889. //
  2890. ReferralRestart:
  2891. if (!KERB_SUCCESS(KerbBuildFullServiceKdcName(
  2892. &RealTargetRealm,
  2893. &KerbGlobalKdcServiceName,
  2894. KRB_NT_SRV_INST,
  2895. &TargetTgtKdcName
  2896. )))
  2897. {
  2898. Status = STATUS_INSUFFICIENT_RESOURCES;
  2899. goto Cleanup;
  2900. }
  2901. DsysAssert(!TicketCacheLocked);
  2902. KerbReadLockTicketCache();
  2903. TicketCacheLocked = TRUE;
  2904. //
  2905. // Referral chasing code block - very important to get right
  2906. // If we know the "real" target realm, eg. from GC, then
  2907. // we'll walk trusts until we hit "real" target realm.
  2908. //
  2909. while (!RtlEqualUnicodeString(
  2910. &RealTargetRealm,
  2911. &TicketGrantingTicket->TargetDomainName,
  2912. TRUE ))
  2913. {
  2914. //
  2915. // If we just got two TGTs for the same domain, then we must have
  2916. // gotten as far as we can. Chances our our RealTargetRealm is a
  2917. // variation of what the KDC hands out.
  2918. //
  2919. if ((LastTgt != NULL) &&
  2920. RtlEqualUnicodeString(
  2921. &LastTgt->TargetDomainName,
  2922. &TicketGrantingTicket->TargetDomainName,
  2923. TRUE ))
  2924. {
  2925. KerbUnlockTicketCache();
  2926. KerbSetTicketCacheEntryTarget(
  2927. &RealTargetRealm,
  2928. LastTgt
  2929. );
  2930. KerbReadLockTicketCache();
  2931. TicketCacheLocked = TRUE;
  2932. D_DebugLog((DEB_ERROR, "Got two TGTs for same realm (%wZ), bailing out of referral loop\n",
  2933. &LastTgt->TargetDomainName));
  2934. break;
  2935. }
  2936. D_DebugLog((DEB_TRACE_S4U, "Getting referral TGT for \n"));
  2937. D_KerbPrintKdcName((DEB_TRACE_S4U, TargetTgtKdcName));
  2938. D_KerbPrintKdcName((DEB_TRACE_S4U, TicketGrantingTicket->ServiceName));
  2939. KerbUnlockTicketCache();
  2940. TicketCacheLocked = FALSE;
  2941. //
  2942. // Cleanup old state
  2943. //
  2944. KerbFreeTgsReply(KdcReply);
  2945. KerbFreeKdcReplyBody(KdcReplyBody);
  2946. KdcReply = NULL;
  2947. KdcReplyBody = NULL;
  2948. Status = KerbGetTgsTicket(
  2949. &ClientRealm,
  2950. TicketGrantingTicket,
  2951. TargetTgtKdcName,
  2952. FALSE,
  2953. TicketOptions,
  2954. EncryptionType,
  2955. AuthorizationData,
  2956. NULL, // no pa data
  2957. NULL, // no tgt reply since target is krbtgt
  2958. &EvidenceTicket,
  2959. NULL,
  2960. &KdcReply,
  2961. &KdcReplyBody,
  2962. &RetryFlags
  2963. );
  2964. if (!NT_SUCCESS(Status))
  2965. {
  2966. DebugLog((DEB_WARN,"Failed to get TGS ticket for service 0x%x :",
  2967. Status ));
  2968. KerbPrintKdcName(DEB_WARN, TargetTgtKdcName );
  2969. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  2970. goto Cleanup;
  2971. }
  2972. KerbReadLockLogonSessions(LogonSession);
  2973. LogonSessionsLocked = TRUE;
  2974. Status = KerbCreateTicketCacheEntry(
  2975. KdcReply,
  2976. KdcReplyBody,
  2977. NULL, // no target name
  2978. NULL, // no targe realm
  2979. 0, // no flags
  2980. NULL, // no cache
  2981. NULL, // no cred key
  2982. &TicketCacheEntry
  2983. );
  2984. KerbUnlockLogonSessions(LogonSession);
  2985. LogonSessionsLocked = FALSE;
  2986. if (!NT_SUCCESS(Status))
  2987. {
  2988. goto Cleanup;
  2989. }
  2990. if (LastTgt != NULL)
  2991. {
  2992. KerbDereferenceTicketCacheEntry(LastTgt);
  2993. LastTgt = NULL;
  2994. }
  2995. LastTgt = TicketGrantingTicket;
  2996. TicketGrantingTicket = TicketCacheEntry;
  2997. TicketCacheEntry = NULL;
  2998. Status = KerbPrepareEvidenceTicket(
  2999. DecryptKey,
  3000. &TicketGrantingTicket->SessionKey,
  3001. &EvidenceTicket
  3002. );
  3003. if (!NT_SUCCESS( Status ))
  3004. {
  3005. D_DebugLog((DEB_ERROR, "Failed to prepare evidence ticket\n"));
  3006. goto Cleanup;
  3007. }
  3008. DecryptKey = &TicketGrantingTicket->SessionKey;
  3009. KerbReadLockTicketCache();
  3010. TicketCacheLocked = TRUE;
  3011. } // ** WHILE **
  3012. DsysAssert(TicketCacheLocked);
  3013. KerbUnlockTicketCache();
  3014. TicketCacheLocked = FALSE;
  3015. //
  3016. // Now we must have a TGT to the destination domain. Get a ticket
  3017. // to the service.
  3018. //
  3019. //
  3020. // Cleanup old state
  3021. //
  3022. KerbFreeTgsReply(KdcReply);
  3023. KerbFreeKdcReplyBody(KdcReplyBody);
  3024. KdcReply = NULL;
  3025. KdcReplyBody = NULL;
  3026. RetryFlags = 0;
  3027. Status = KerbGetTgsTicket(
  3028. &ClientRealm,
  3029. TicketGrantingTicket,
  3030. RealTargetName,
  3031. FALSE,
  3032. TicketOptions,
  3033. EncryptionType,
  3034. AuthorizationData,
  3035. NULL, // no pa data
  3036. TgtReply,
  3037. &EvidenceTicket,
  3038. &RequestBodyEndTime,
  3039. &KdcReply,
  3040. &KdcReplyBody,
  3041. &RetryFlags
  3042. );
  3043. if (!NT_SUCCESS(Status))
  3044. {
  3045. DebugLog((DEB_WARN,"Failed to get TGS S4U ticket for service 0x%x ",
  3046. Status ));
  3047. KerbPrintKdcName(DEB_WARN, RealTargetName);
  3048. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  3049. goto Cleanup;
  3050. }
  3051. //
  3052. // Now that we are in the domain to which we were referred, check for referral
  3053. // info in the name
  3054. //
  3055. KerbFreeString(&RealTargetRealm);
  3056. Status = KerbGetReferralNames(
  3057. KdcReplyBody,
  3058. RealTargetName,
  3059. &RealTargetRealm
  3060. );
  3061. if (!NT_SUCCESS(Status))
  3062. {
  3063. goto Cleanup;
  3064. }
  3065. //
  3066. // If this is not a referral ticket, just cache it and return
  3067. // the cache entry.
  3068. //
  3069. if (RealTargetRealm.Length != 0)
  3070. {
  3071. //
  3072. // To prevent loops, we limit the number of referral we'll take
  3073. //
  3074. if (ReferralCount > KerbGlobalMaxReferralCount)
  3075. {
  3076. D_DebugLog((DEB_ERROR,"Maximum referral count exceeded for name: "));
  3077. D_KerbPrintKdcName((DEB_ERROR,RealTargetName));
  3078. Status = STATUS_MAX_REFERRALS_EXCEEDED;
  3079. goto Cleanup;
  3080. }
  3081. ReferralCount++;
  3082. Status = KerbCreateTicketCacheEntry(
  3083. KdcReply,
  3084. KdcReplyBody,
  3085. NULL, // no target name
  3086. NULL, // no target realm
  3087. 0, // no flags
  3088. NULL, // no cache
  3089. NULL, // no cred key
  3090. &TicketCacheEntry
  3091. );
  3092. //
  3093. // Cleanup old state
  3094. //
  3095. KerbFreeTgsReply(KdcReply);
  3096. KerbFreeKdcReplyBody(KdcReplyBody);
  3097. KdcReply = NULL;
  3098. KdcReplyBody = NULL;
  3099. if (!NT_SUCCESS(Status))
  3100. {
  3101. goto Cleanup;
  3102. }
  3103. if (LastTgt != NULL)
  3104. {
  3105. KerbDereferenceTicketCacheEntry(LastTgt);
  3106. LastTgt = NULL;
  3107. }
  3108. LastTgt = TicketGrantingTicket;
  3109. TicketGrantingTicket = TicketCacheEntry;
  3110. TicketCacheEntry = NULL;
  3111. Status = KerbPrepareEvidenceTicket(
  3112. DecryptKey,
  3113. &TicketGrantingTicket->SessionKey,
  3114. &EvidenceTicket
  3115. );
  3116. if (!NT_SUCCESS( Status ))
  3117. {
  3118. DebugLog((DEB_ERROR, "Failed to prepare evidence ticket %x\n", Status));
  3119. goto Cleanup;
  3120. }
  3121. DecryptKey = &TicketGrantingTicket->SessionKey;
  3122. D_DebugLog((DEB_TRACE_S4U, "Restart referral:%wZ", &RealTargetRealm));
  3123. goto ReferralRestart;
  3124. }
  3125. KerbReadLockLogonSessions(LogonSession);
  3126. LogonSessionsLocked = TRUE;
  3127. //
  3128. // We've got our S4U service ticket. Cache it.
  3129. //
  3130. Status = KerbCreateTicketCacheEntry(
  3131. KdcReply,
  3132. KdcReplyBody,
  3133. TargetName,
  3134. TargetDomainName,
  3135. 0, // no flags
  3136. CacheTicket ? &PrimaryCreds->ServerTicketCache : NULL,
  3137. NULL, // no cred key
  3138. &TicketCacheEntry
  3139. );
  3140. KerbUnlockLogonSessions(LogonSession);
  3141. LogonSessionsLocked = FALSE;
  3142. if (!NT_SUCCESS(Status))
  3143. {
  3144. goto Cleanup;
  3145. }
  3146. D_DebugLog((DEB_TRACE_S4U, "Got the S4U ticket (%p)\n", TicketCacheEntry));
  3147. *NewCacheEntry = TicketCacheEntry;
  3148. TicketCacheEntry = NULL;
  3149. Update = TRUE;
  3150. Cleanup:
  3151. //
  3152. // Bad or unlocatable SPN -- Don't update if we got the value from the cache, though.
  3153. //
  3154. if (( TargetName->NameType == KRB_NT_SRV_INST ) &&
  3155. ( NT_SUCCESS(Status) || Status == STATUS_NO_TRUST_SAM_ACCOUNT ) &&
  3156. ( !CacheBasedFailure ))
  3157. {
  3158. NTSTATUS Tmp;
  3159. ULONG UpdateValue = KERB_SPN_UNKNOWN;
  3160. PUNICODE_STRING Realm = NULL;
  3161. if ( NT_SUCCESS( Status ))
  3162. {
  3163. Realm = &(*NewCacheEntry)->TargetDomainName;
  3164. UpdateValue = KERB_SPN_KNOWN;
  3165. }
  3166. Tmp = KerbUpdateSpnCacheEntry(
  3167. SpnCacheEntry,
  3168. TargetName,
  3169. ProxyServerCreds,
  3170. UpdateValue,
  3171. Realm
  3172. );
  3173. }
  3174. //
  3175. // Update the S4U cache
  3176. //
  3177. if (( RetryFlags & ( KERB_RETRY_DISABLE_S4U | KERB_RETRY_NO_S4UMATCH)) != 0)
  3178. {
  3179. S4UFlags = ((RetryFlags & KERB_RETRY_DISABLE_S4U) ?
  3180. S4UCACHE_S4U_UNAVAILABLE : S4UCACHE_S4U_AVAILABLE );
  3181. //
  3182. // Allow downgrade to NTLM.
  3183. //
  3184. Status = SEC_E_NO_CREDENTIALS;
  3185. Update = TRUE;
  3186. }
  3187. if ( Update )
  3188. {
  3189. KerbUpdateS4UCacheData(
  3190. CallerLogonSession,
  3191. S4UFlags
  3192. );
  3193. }
  3194. KerbFreeTgsReply( KdcReply );
  3195. KerbFreeKdcReplyBody( KdcReplyBody );
  3196. KerbFreeKdcName( &TargetTgtKdcName );
  3197. KerbFreeString( &RealTargetRealm );
  3198. KerbFreeKdcName(&RealTargetName);
  3199. KerbFreeString( &SpnTargetRealm);
  3200. KerbFreeDuplicatedTicket( &EvidenceTicket );
  3201. if (TicketCacheLocked)
  3202. {
  3203. KerbUnlockTicketCache();
  3204. }
  3205. if (LogonSessionsLocked)
  3206. {
  3207. KerbUnlockLogonSessions(LogonSession);
  3208. }
  3209. KerbFreeString(&RealTargetRealm);
  3210. if (TicketGrantingTicket != NULL)
  3211. {
  3212. if (Status == STATUS_WRONG_PASSWORD)
  3213. {
  3214. KerbRemoveTicketCacheEntry(
  3215. TicketGrantingTicket
  3216. );
  3217. }
  3218. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  3219. }
  3220. if (LastTgt != NULL)
  3221. {
  3222. KerbDereferenceTicketCacheEntry(LastTgt);
  3223. LastTgt = NULL;
  3224. }
  3225. KerbFreeString(&ClientRealm);
  3226. //
  3227. // If we still have a pointer to the ticket cache entry, free it now.
  3228. //
  3229. if (TicketCacheEntry != NULL)
  3230. {
  3231. KerbRemoveTicketCacheEntry( TicketCacheEntry );
  3232. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  3233. }
  3234. return(Status);
  3235. }
  3236. //+-------------------------------------------------------------------------
  3237. //
  3238. // Function: KerbCreateS4ULogonSession
  3239. //
  3240. // Synopsis: Creates a logon session to accompany the S4ULogon.
  3241. //
  3242. //
  3243. // Effects:
  3244. //
  3245. // Arguments:
  3246. //
  3247. // Requires:
  3248. //
  3249. // Returns:
  3250. //
  3251. // Notes:
  3252. //
  3253. //
  3254. //--------------------------------------------------------------------------
  3255. NTSTATUS
  3256. KerbCreateS4ULogonSession(
  3257. IN PKERB_INTERNAL_NAME S4UClientName,
  3258. IN PUNICODE_STRING S4UClientRealm,
  3259. IN PLUID pLuid,
  3260. IN OUT PKERB_LOGON_SESSION * LogonSession
  3261. )
  3262. {
  3263. NTSTATUS Status = STATUS_SUCCESS;
  3264. UNICODE_STRING S4UClient = {0};
  3265. *LogonSession = NULL;
  3266. if (!KERB_SUCCESS( KerbConvertKdcNameToString(
  3267. &S4UClient,
  3268. S4UClientName,
  3269. NULL
  3270. )) )
  3271. {
  3272. Status = STATUS_INSUFFICIENT_RESOURCES;
  3273. goto Cleanup;
  3274. }
  3275. Status = KerbCreateLogonSession(
  3276. pLuid,
  3277. &S4UClient,
  3278. S4UClientRealm,
  3279. NULL,
  3280. NULL,
  3281. 0,
  3282. KERB_LOGON_S4U_SESSION,
  3283. FALSE,
  3284. LogonSession
  3285. );
  3286. if (!NT_SUCCESS(Status))
  3287. {
  3288. DebugLog((DEB_ERROR, "KerbCreateLogonSession failed %x %ws, line %d\n", Status, THIS_FILE, __LINE__));
  3289. goto Cleanup;
  3290. }
  3291. Cleanup:
  3292. KerbFreeString(&S4UClient);
  3293. return Status;
  3294. }
  3295. //+-------------------------------------------------------------------------
  3296. //
  3297. // Function: KerbS4UToSelfLogon
  3298. //
  3299. // Synopsis: Attempt to gets TGT for an S4U client for name
  3300. // location purposes.
  3301. //
  3302. //
  3303. // Effects:
  3304. //
  3305. // Arguments: LogonSession - Logon session of the service doing the
  3306. // S4U request
  3307. //
  3308. // Requires:
  3309. //
  3310. // Returns:
  3311. //
  3312. // Notes:
  3313. //
  3314. //
  3315. //--------------------------------------------------------------------------
  3316. NTSTATUS
  3317. KerbS4UToSelfLogon(
  3318. IN PVOID ProtocolSubmitBuffer,
  3319. IN PVOID ClientBufferBase,
  3320. IN ULONG SubmitBufferSize,
  3321. OUT PKERB_LOGON_SESSION * NewLogonSession,
  3322. OUT PLUID LogonId,
  3323. OUT PKERB_TICKET_CACHE_ENTRY * WorkstationTicket,
  3324. OUT PKERB_INTERNAL_NAME * S4UClientName,
  3325. OUT PUNICODE_STRING S4UClientRealm,
  3326. OUT PLUID AlternateLuid
  3327. )
  3328. {
  3329. NTSTATUS Status;
  3330. PKERB_S4U_LOGON LogonInfo = NULL;
  3331. PKERB_LOGON_SESSION CallerLogonSession = NULL;
  3332. PKERB_TICKET_CACHE_ENTRY Duplicate = NULL;
  3333. SECPKG_CLIENT_INFO ClientInfo;
  3334. PKERB_INTERNAL_NAME AltClientName = NULL;
  3335. ULONG Flags = KERB_CRACK_NAME_USE_WKSTA_REALM, ProcessFlags = 0;
  3336. LUID LocalService = LOCALSERVICE_LUID;
  3337. LUID CallerLuid = SYSTEM_LUID;
  3338. LUID NetworkService = NETWORKSERVICE_LUID;
  3339. PKERB_TICKET_CACHE_ENTRY LocalTicket = NULL;
  3340. UNICODE_STRING DummyRealm = {0};
  3341. *NewLogonSession = NULL;
  3342. *WorkstationTicket = NULL;
  3343. *S4UClientName = NULL;
  3344. RtlInitUnicodeString(
  3345. S4UClientRealm,
  3346. NULL
  3347. );
  3348. Status = LsaFunctions->GetClientInfo(&ClientInfo);
  3349. if (!NT_SUCCESS(Status))
  3350. {
  3351. D_DebugLog((DEB_ERROR,"Failed to get client information: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  3352. goto Cleanup;
  3353. }
  3354. else if ((ClientInfo.ClientFlags & SECPKG_CLIENT_THREAD_TERMINATED) != 0)
  3355. {
  3356. Status = STATUS_ACCESS_DENIED;
  3357. goto Cleanup;
  3358. }
  3359. //
  3360. // TBD: Is this correct? Local service LUIDs have no network creds,
  3361. // so we've got to fail S4U to Self.
  3362. // (OR - Do we use local system?)
  3363. // NETWORK uses local system luid, so we're ok w/ that
  3364. //
  3365. if (RtlEqualLuid(&LocalService, &ClientInfo.LogonId))
  3366. {
  3367. D_DebugLog((DEB_ERROR, "Failing S4U due to LocalService\n"));
  3368. Status = SEC_E_NO_CREDENTIALS;
  3369. goto Cleanup;
  3370. }
  3371. else if (!RtlEqualLuid(&NetworkService, &ClientInfo.LogonId))
  3372. {
  3373. RtlCopyLuid(&CallerLuid, &ClientInfo.LogonId);
  3374. }
  3375. //
  3376. // Use this LUID for decrypting S4U Service ticket
  3377. //
  3378. RtlCopyLuid(AlternateLuid, &CallerLuid);
  3379. LogonInfo = (PKERB_S4U_LOGON) ProtocolSubmitBuffer;
  3380. RELOCATE_ONE(&LogonInfo->ClientUpn);
  3381. NULL_RELOCATE_ONE(&LogonInfo->ClientRealm);
  3382. //
  3383. // Make sure we have enough room to add a NULL to the end of the UPN
  3384. //
  3385. if (LogonInfo->ClientUpn.Length > KERB_MAX_UNICODE_STRING)
  3386. {
  3387. Status = STATUS_NAME_TOO_LONG;
  3388. goto Cleanup;
  3389. }
  3390. D_DebugLog((DEB_TRACE_S4U, "KerbS4UToSelfLogon ClientUpn %wZ, ClientRealm %wZ, Flags %#x\n",
  3391. &LogonInfo->ClientUpn, &LogonInfo->ClientRealm, LogonInfo->Flags));
  3392. Status = KerbProcessTargetNames(
  3393. &LogonInfo->ClientUpn,
  3394. NULL,
  3395. Flags,
  3396. &ProcessFlags,
  3397. S4UClientName,
  3398. &DummyRealm,
  3399. NULL
  3400. );
  3401. if (!NT_SUCCESS(Status))
  3402. {
  3403. goto Cleanup;
  3404. }
  3405. CallerLogonSession = KerbReferenceLogonSession(
  3406. &ClientInfo.LogonId,
  3407. FALSE
  3408. );
  3409. if (NULL == CallerLogonSession)
  3410. {
  3411. D_DebugLog((DEB_ERROR, "Failed to locate caller's logon session - %x\n", ClientInfo.LogonId));
  3412. Status = STATUS_NO_SUCH_LOGON_SESSION;
  3413. goto Cleanup;
  3414. }
  3415. //
  3416. // First, we need to get the client's realm from the UPN
  3417. //
  3418. if (LogonInfo->ClientRealm.Length == 0)
  3419. {
  3420. //
  3421. // Make sure that our processed name is correct type - don't accept
  3422. // NT4 style names as user names.
  3423. //
  3424. AltClientName = (*S4UClientName);
  3425. if ( AltClientName->NameType != KRB_NT_ENTERPRISE_PRINCIPAL )
  3426. {
  3427. DebugLog((DEB_ERROR, "Wrong name type passed to S4U (%x)\n", AltClientName->NameType));
  3428. Status = STATUS_INVALID_ACCOUNT_NAME;
  3429. goto Cleanup;
  3430. }
  3431. Flags |= KERB_TARGET_DID_ALTNAME_LOOKUP;
  3432. //
  3433. // pre-empt burning a TGT request to find caller realm,
  3434. // if we find it in our cache.
  3435. //
  3436. KerbReadLockLogonSessions( CallerLogonSession );
  3437. LocalTicket = KerbLocateS4UTicketCacheEntry(
  3438. &CallerLogonSession->PrimaryCredentials.S4UTicketCache,
  3439. &ClientInfo.LogonId,
  3440. NULL,
  3441. NULL,
  3442. AltClientName,
  3443. S4UTICKETCACHE_USEALTNAME
  3444. );
  3445. KerbUnlockLogonSessions( CallerLogonSession );
  3446. if ( LocalTicket == NULL )
  3447. {
  3448. Status = KerbGetS4UClientRealm(
  3449. CallerLogonSession,
  3450. S4UClientName,
  3451. S4UClientRealm
  3452. );
  3453. if (!NT_SUCCESS(Status))
  3454. {
  3455. goto Cleanup;
  3456. }
  3457. }
  3458. else
  3459. {
  3460. //
  3461. // Found the s4u ticket in the callers s4u cache - get the info
  3462. // we need from it.
  3463. //
  3464. Status = KerbDuplicateString(
  3465. S4UClientRealm,
  3466. &LocalTicket->ClientDomainName
  3467. );
  3468. if (!NT_SUCCESS(Status))
  3469. {
  3470. goto Cleanup;
  3471. }
  3472. }
  3473. }
  3474. else
  3475. {
  3476. Status = KerbDuplicateString(
  3477. S4UClientRealm,
  3478. &LogonInfo->ClientRealm
  3479. );
  3480. if (!NT_SUCCESS(Status))
  3481. {
  3482. goto Cleanup;
  3483. }
  3484. }
  3485. //
  3486. // Allocate a locally unique ID for this logon session. We will
  3487. // create it in the LSA just before returning.
  3488. //
  3489. Status = NtAllocateLocallyUniqueId( LogonId );
  3490. if (!NT_SUCCESS(Status))
  3491. {
  3492. goto Cleanup;
  3493. }
  3494. Status = KerbCreateS4ULogonSession(
  3495. (*S4UClientName),
  3496. S4UClientRealm,
  3497. LogonId,
  3498. NewLogonSession
  3499. );
  3500. if (!NT_SUCCESS(Status))
  3501. {
  3502. DebugLog((DEB_ERROR,"Failed to create logon session 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  3503. goto Cleanup;
  3504. }
  3505. if ( LocalTicket == NULL )
  3506. {
  3507. Status = KerbGetS4USelfServiceTicket(
  3508. CallerLogonSession,
  3509. (*NewLogonSession),
  3510. NULL, // tbd: need to put credential here?
  3511. (*S4UClientName),
  3512. AltClientName,
  3513. S4UClientRealm,
  3514. WorkstationTicket,
  3515. 0, // no flags
  3516. 0, // no ticketoptions
  3517. 0, // no enctype
  3518. NULL // auth data
  3519. );
  3520. if (!NT_SUCCESS(Status))
  3521. {
  3522. goto Cleanup;
  3523. }
  3524. }
  3525. else
  3526. {
  3527. *WorkstationTicket = LocalTicket;
  3528. LocalTicket = NULL;
  3529. }
  3530. KerbWriteLockLogonSessions( (*NewLogonSession) );
  3531. //
  3532. // Is this going to be a "delegatable" logon session?
  3533. //
  3534. if (((*WorkstationTicket)->TicketFlags & KERB_TICKET_FLAGS_forwardable) != 0)
  3535. {
  3536. (*NewLogonSession)->LogonSessionFlags |= KERB_LOGON_DELEGATE_OK;
  3537. }
  3538. #if DBG
  3539. else
  3540. {
  3541. D_DebugLog((DEB_TRACE_S4U, "Created non-delegatable logon session via s4u logon\n"));
  3542. }
  3543. #endif
  3544. Status = KerbDuplicateTicketCacheEntry(
  3545. (*WorkstationTicket),
  3546. &Duplicate
  3547. );
  3548. if (!NT_SUCCESS(Status))
  3549. {
  3550. KerbUnlockLogonSessions((*NewLogonSession));
  3551. goto Cleanup;
  3552. }
  3553. KerbInsertTicketCacheEntry(
  3554. &((*NewLogonSession)->PrimaryCredentials.S4UTicketCache),
  3555. Duplicate
  3556. );
  3557. KerbDereferenceTicketCacheEntry( Duplicate );
  3558. KerbUnlockLogonSessions((*NewLogonSession));
  3559. Cleanup:
  3560. if (!NT_SUCCESS(Status))
  3561. {
  3562. //
  3563. // TBD: Negative cache here, based on client name
  3564. //
  3565. KerbFreeString(S4UClientRealm);
  3566. KerbFreeKdcName(S4UClientName);
  3567. *S4UClientName = NULL;
  3568. }
  3569. KerbFreeString( &DummyRealm );
  3570. if ( LocalTicket != NULL )
  3571. {
  3572. KerbDereferenceTicketCacheEntry( LocalTicket );
  3573. }
  3574. return Status;
  3575. }
  3576. //+-------------------------------------------------------------------------
  3577. //
  3578. // Function: KerbGetS4UProxyEvidence
  3579. //
  3580. // Synopsis: Get our evidence ticket from the logonsession. Its either a
  3581. // service ticket (for ASC logon sessions), or a S4UToSelf request
  3582. // if we don't have a service ticket.
  3583. //
  3584. // Effects:
  3585. //
  3586. // Arguments: LogonSession - Logon session of the service doing the
  3587. // S4U request
  3588. //
  3589. // Requires:
  3590. //
  3591. // Returns:
  3592. //
  3593. // Notes:
  3594. //
  3595. //
  3596. //--------------------------------------------------------------------------
  3597. NTSTATUS
  3598. KerbGetS4UProxyEvidence(
  3599. IN PKERB_LOGON_SESSION LogonSession,
  3600. IN PKERB_INTERNAL_NAME TargetName,
  3601. IN ULONG ClientProcess,
  3602. IN OUT PKERB_LOGON_SESSION * CallingLogonSession,
  3603. IN OUT PKERB_TICKET_CACHE_ENTRY * TicketCacheEntry
  3604. )
  3605. {
  3606. NTSTATUS Status;
  3607. ULONG Flags = KERB_CRACK_NAME_USE_WKSTA_REALM, ProcessFlags = 0;
  3608. ULONG LogonSessionFlags;
  3609. BOOLEAN LogonSessionLocked = FALSE;
  3610. UNICODE_STRING TmpRealm = {0};
  3611. LUID LogonId;
  3612. PKERB_TICKET_CACHE_ENTRY NewCacheEntry = NULL;
  3613. PKERB_INTERNAL_NAME ClientName = NULL;
  3614. PKERB_LOGON_SESSION CallerLogonSession = NULL;
  3615. *TicketCacheEntry = NULL;
  3616. *CallingLogonSession = NULL;
  3617. Status = KerbGetCallingLuid(
  3618. &LogonId,
  3619. ((HANDLE) LongToHandle(ClientProcess))
  3620. );
  3621. if (!NT_SUCCESS( Status ))
  3622. {
  3623. goto Cleanup;
  3624. }
  3625. if (!KerbAllowedForS4UProxy( &LogonId ))
  3626. {
  3627. Status = STATUS_NOT_SUPPORTED;
  3628. goto Cleanup;
  3629. }
  3630. //
  3631. // Get caller's logon session.
  3632. //
  3633. CallerLogonSession = KerbReferenceLogonSession(
  3634. &LogonId,
  3635. FALSE
  3636. );
  3637. if (NULL == CallerLogonSession)
  3638. {
  3639. DsysAssert( FALSE );
  3640. Status = STATUS_NO_SUCH_LOGON_SESSION;
  3641. goto Cleanup;
  3642. }
  3643. KerbReadLockLogonSessions( LogonSession );
  3644. LogonSessionFlags = LogonSession->LogonSessionFlags;
  3645. NewCacheEntry = KerbLocateS4UTicketCacheEntry(
  3646. &LogonSession->PrimaryCredentials.S4UTicketCache,
  3647. &LogonId,
  3648. NULL, // just get the ticket used for S4U.
  3649. NULL,
  3650. NULL,
  3651. S4UTICKETCACHE_FOR_EVIDENCE
  3652. );
  3653. KerbUnlockLogonSessions( LogonSession );
  3654. if ( NewCacheEntry == NULL )
  3655. {
  3656. //
  3657. // What's this? An ASC logon sessoin w/o a ticket?
  3658. //
  3659. if (( LogonSessionFlags & KERB_LOGON_DELEGATE_OK ) == 0 )
  3660. {
  3661. DebugLog((DEB_ERROR, "Non Fwdable logon session used in S4u\n"));
  3662. Status = STATUS_NO_SUCH_LOGON_SESSION;
  3663. goto Cleanup; // right thing? Or fall through?
  3664. }
  3665. DsysAssert( !LogonSessionLocked );
  3666. KerbReadLockLogonSessions( LogonSession );
  3667. LogonSessionLocked = TRUE;
  3668. Status = KerbProcessTargetNames(
  3669. &LogonSession->PrimaryCredentials.UserName,
  3670. NULL,
  3671. Flags,
  3672. &ProcessFlags,
  3673. &ClientName,
  3674. &TmpRealm,
  3675. NULL
  3676. );
  3677. if (!NT_SUCCESS( Status ))
  3678. {
  3679. goto Cleanup;
  3680. }
  3681. KerbFreeString( &TmpRealm );
  3682. Status = KerbDuplicateString(
  3683. &TmpRealm,
  3684. &LogonSession->PrimaryCredentials.DomainName
  3685. );
  3686. if (!NT_SUCCESS( Status ))
  3687. {
  3688. goto Cleanup;
  3689. }
  3690. DsysAssert( LogonSessionLocked );
  3691. KerbUnlockLogonSessions( LogonSession );
  3692. LogonSessionLocked = FALSE;
  3693. Status = KerbGetS4USelfServiceTicket(
  3694. CallerLogonSession,
  3695. LogonSession,
  3696. NULL,
  3697. ClientName,
  3698. NULL, // no alt client name
  3699. &TmpRealm,
  3700. &NewCacheEntry,
  3701. 0,
  3702. 0,
  3703. 0,
  3704. NULL
  3705. );
  3706. if (!NT_SUCCESS(Status))
  3707. {
  3708. DebugLog((DEB_ERROR, "KerbGetS4UProxyEvidence failed to get S4U ticket - %x\n", Status));
  3709. goto Cleanup;
  3710. }
  3711. ASSERT(NewCacheEntry);
  3712. if (NewCacheEntry->CacheFlags & KERB_TICKET_CACHE_TKT_ENC_IN_SKEY)
  3713. {
  3714. DebugLog((DEB_ERROR, "KerbGetS4UProxyEvidence does not allow u2u evidence key\n"));
  3715. Status = SEC_E_NO_CREDENTIALS;
  3716. goto Cleanup;
  3717. }
  3718. if ((NewCacheEntry->TicketFlags & KERB_TICKET_FLAGS_forwardable) == 0)
  3719. {
  3720. KerbWriteLockLogonSessions( LogonSession );
  3721. LogonSession->LogonSessionFlags &= ~KERB_LOGON_DELEGATE_OK;
  3722. KerbUnlockLogonSessions( LogonSession );
  3723. DebugLog((DEB_TRACE_S4U, "KerbGetS4UProxyEvidence created non-delegatable logon session via s4u logon\n"));
  3724. Status = SEC_E_NO_CREDENTIALS;
  3725. goto Cleanup;
  3726. }
  3727. PKERB_TICKET_CACHE_ENTRY Duplicate;
  3728. Status = KerbDuplicateTicketCacheEntry(
  3729. NewCacheEntry,
  3730. &Duplicate
  3731. );
  3732. if (!NT_SUCCESS( Status ))
  3733. {
  3734. goto Cleanup;
  3735. }
  3736. KerbWriteLockLogonSessions ( LogonSession );
  3737. LogonSession->LogonSessionFlags |= KERB_LOGON_DELEGATE_OK;
  3738. KerbInsertTicketCacheEntry(
  3739. &LogonSession->PrimaryCredentials.S4UTicketCache,
  3740. Duplicate
  3741. );
  3742. KerbUnlockLogonSessions( LogonSession );
  3743. KerbDereferenceTicketCacheEntry( Duplicate );
  3744. Duplicate = NULL;
  3745. }
  3746. //
  3747. // don't allow TKT encrypted in SKEY as evidence ticket for S4UProxy
  3748. //
  3749. else if (NewCacheEntry->CacheFlags & KERB_TICKET_CACHE_TKT_ENC_IN_SKEY)
  3750. {
  3751. DebugLog((DEB_ERROR, "KerbGetS4UProxyEvidence does not allow evidence ticket encrypted in SKEY\n"));
  3752. Status = SEC_E_NO_CREDENTIALS;
  3753. goto Cleanup;
  3754. }
  3755. *CallingLogonSession = CallerLogonSession;
  3756. CallerLogonSession = NULL;
  3757. *TicketCacheEntry = NewCacheEntry;
  3758. NewCacheEntry = NULL;
  3759. Cleanup:
  3760. if (NewCacheEntry)
  3761. {
  3762. KerbDereferenceTicketCacheEntry(NewCacheEntry);
  3763. }
  3764. if ( CallerLogonSession != NULL )
  3765. {
  3766. KerbDereferenceLogonSession( CallerLogonSession );
  3767. }
  3768. if ( LogonSessionLocked )
  3769. {
  3770. KerbUnlockLogonSessions( LogonSession );
  3771. }
  3772. KerbFreeKdcName( &ClientName );
  3773. KerbFreeString( &TmpRealm );
  3774. return Status;
  3775. }
  3776. //+-------------------------------------------------------------------------
  3777. //
  3778. // Function: KerbS4UQueryWorker
  3779. //
  3780. // Synopsis: Handles a S4U Query
  3781. //
  3782. // Effects:
  3783. //
  3784. // Arguments: TaskHandle - handle to the task (for rescheduling, etc.)
  3785. // TaskItem - task context
  3786. //
  3787. // Requires:
  3788. //
  3789. // Returns: Nothing
  3790. //
  3791. // Notes:
  3792. //
  3793. //
  3794. //--------------------------------------------------------------------------
  3795. void
  3796. KerbS4UCleanupWorker(
  3797. void * TaskHandle,
  3798. void * TaskItem
  3799. )
  3800. {
  3801. PKERB_S4UCACHE_DATA Entry = NULL;
  3802. PLIST_ENTRY ListEntry = NULL;
  3803. TimeStamp CurrentTime;
  3804. GetSystemTimeAsFileTime( (PFILETIME) &CurrentTime );
  3805. KerbLockS4UCache();
  3806. for (ListEntry = KerbS4UCache.List.Flink ;
  3807. ListEntry != &KerbS4UCache.List ;
  3808. ListEntry = ListEntry->Flink )
  3809. {
  3810. Entry = CONTAINING_RECORD(ListEntry, KERB_S4UCACHE_DATA, ListEntry.Next);
  3811. if ( KerbGetTime( CurrentTime ) > KerbGetTime( Entry->CacheEndtime ))
  3812. {
  3813. ListEntry = ListEntry->Blink;
  3814. D_DebugLog(( DEB_TRACE_S4U, "Aging out %p\n", Entry ));
  3815. KerbRemoveS4UCacheEntry(Entry);
  3816. }
  3817. #if DBG
  3818. else
  3819. {
  3820. D_DebugLog(( DEB_TRACE_S4U, "NOT aging %p\n", Entry));
  3821. }
  3822. #endif
  3823. }
  3824. KerbUnlockS4UCache();
  3825. }
  3826. //+-------------------------------------------------------------------------
  3827. //
  3828. // Function: KerbS4UQueryWorker
  3829. //
  3830. // Synopsis: Handles a S4U Query
  3831. //
  3832. // Effects:
  3833. //
  3834. // Arguments: TaskHandle - handle to the task (for rescheduling, etc.)
  3835. // TaskItem - task context
  3836. //
  3837. // Requires:
  3838. //
  3839. // Returns: Nothing
  3840. //
  3841. // Notes:
  3842. //
  3843. //
  3844. //--------------------------------------------------------------------------
  3845. void
  3846. KerbS4UQueryWorker(
  3847. void * TaskHandle,
  3848. void * TaskItem
  3849. )
  3850. {
  3851. NTSTATUS Status = STATUS_SUCCESS;
  3852. PKERB_LOGON_SESSION LogonSession = (PKERB_LOGON_SESSION) TaskItem;
  3853. PKERB_INTERNAL_NAME TargetName = NULL;
  3854. PKERB_TICKET_CACHE_ENTRY ServiceTicket = NULL;
  3855. PKERB_TICKET_CACHE_ENTRY S4UTicket = NULL;
  3856. PKERB_TICKET_CACHE_ENTRY Tgt = NULL;
  3857. UNICODE_STRING TargetRealm = NULL_UNICODE_STRING;
  3858. UNICODE_STRING TempName = NULL_UNICODE_STRING;
  3859. BOOLEAN LogonSessionLocked = FALSE, Upn = FALSE, CrossRealm = FALSE;
  3860. KERB_TGT_REPLY TgtReply ={0};
  3861. PKERB_ENCRYPTION_KEY EncryptKey = NULL;
  3862. //
  3863. // Get a service ticket to ourselves. This will be used in the proxy
  3864. // request.
  3865. //
  3866. #if DBG
  3867. SYSTEMTIME st;
  3868. GetLocalTime(&st);
  3869. DebugLog((DEB_TRACE_S4U, "Firing off S4UQuery worker LS %p\n", LogonSession));
  3870. DebugLog((DEB_TRACE_S4U, "Current time: %02d:%02d:%02d\n", (ULONG)st.wHour, (ULONG)st.wMinute, (ULONG)st.wSecond));
  3871. #endif
  3872. KerbReferenceLogonSessionByPointer(LogonSession, FALSE);
  3873. KerbWriteLockLogonSessions( LogonSession );
  3874. LogonSessionLocked = TRUE;
  3875. Status = KerbGetS4UTargetName(
  3876. &TargetName,
  3877. &LogonSession->PrimaryCredentials,
  3878. &LogonSession->LogonId,
  3879. &Upn
  3880. );
  3881. if (!NT_SUCCESS( Status ))
  3882. {
  3883. goto Cleanup;
  3884. }
  3885. Status = KerbDuplicateStringEx(
  3886. &TargetRealm,
  3887. &LogonSession->PrimaryCredentials.DomainName,
  3888. FALSE
  3889. );
  3890. KerbUnlockLogonSessions( LogonSession );
  3891. LogonSessionLocked = FALSE;
  3892. if (!NT_SUCCESS( Status ))
  3893. {
  3894. goto Cleanup;
  3895. }
  3896. //
  3897. // Targetting a UPN usually requires U2U. Get our TGT.
  3898. //
  3899. if ( Upn )
  3900. {
  3901. KerbReadLockLogonSessions( LogonSession );
  3902. Status = KerbGetTgtForService(
  3903. LogonSession,
  3904. NULL,
  3905. NULL,
  3906. NULL,
  3907. &TempName, // no target realm
  3908. KERB_TICKET_CACHE_PRIMARY_TGT,
  3909. &Tgt,
  3910. &CrossRealm
  3911. );
  3912. KerbUnlockLogonSessions( LogonSession );
  3913. if (!NT_SUCCESS(Status) || CrossRealm )
  3914. {
  3915. DebugLog((DEB_ERROR, "KerbBuildTgtReply failed to get TGT, CrossRealm ? %s\n", CrossRealm ? "true" : "false"));
  3916. Status = STATUS_USER2USER_REQUIRED;
  3917. goto Cleanup;
  3918. }
  3919. TgtReply.version = KERBEROS_VERSION;
  3920. TgtReply.message_type = KRB_TGT_REP;
  3921. TgtReply.ticket = Tgt->Ticket;
  3922. }
  3923. Status = KerbGetServiceTicket(
  3924. LogonSession,
  3925. NULL,
  3926. NULL,
  3927. TargetName,
  3928. &TargetRealm,
  3929. NULL,
  3930. 0,
  3931. 0,
  3932. 0,
  3933. NULL,
  3934. NULL,
  3935. (Upn ? &TgtReply : NULL),
  3936. &ServiceTicket,
  3937. NULL
  3938. );
  3939. if ( !NT_SUCCESS(Status) )
  3940. {
  3941. DebugLog((DEB_TRACE_S4U, "Couldn't get service ticket in S4UQueryWorker %x\n", Status));
  3942. goto Cleanup;
  3943. }
  3944. //
  3945. // U2U + S4UProxy won't work. Target teh local machine.
  3946. //
  3947. if ( Upn )
  3948. {
  3949. KerbFreeKdcName( &TargetName );
  3950. KerbFreeString( &TargetRealm );
  3951. KerbGlobalReadLock();
  3952. Status = KerbDuplicateKdcName(
  3953. &TargetName,
  3954. KerbGlobalMitMachineServiceName
  3955. );
  3956. if (NT_SUCCESS( Status ))
  3957. {
  3958. Status = KerbDuplicateString(
  3959. &TargetRealm,
  3960. &KerbGlobalDnsDomainName
  3961. );
  3962. }
  3963. KerbGlobalReleaseLock();
  3964. if (!NT_SUCCESS( Status ))
  3965. {
  3966. goto Cleanup;
  3967. }
  3968. //
  3969. // We can't use U2U + S4UProxy... Decrypt the ticket, and re-encrypt w/ our password.
  3970. //
  3971. EncryptKey = KerbGetKeyFromList(
  3972. LogonSession->PrimaryCredentials.Passwords,
  3973. ServiceTicket->Ticket.encrypted_part.encryption_type
  3974. );
  3975. if ( EncryptKey == NULL)
  3976. {
  3977. goto Cleanup;
  3978. }
  3979. Status = KerbPrepareEvidenceTicket(
  3980. &Tgt->SessionKey,
  3981. EncryptKey,
  3982. &ServiceTicket->Ticket
  3983. );
  3984. if (!NT_SUCCESS( Status ))
  3985. {
  3986. DebugLog((DEB_ERROR, "Couldn't encrypt evidence ticket %x\n", Status));
  3987. goto Cleanup;
  3988. }
  3989. }
  3990. Status = KerbGetServiceTicketByS4UProxy(
  3991. LogonSession,
  3992. LogonSession,
  3993. NULL,
  3994. ServiceTicket,
  3995. TargetName,
  3996. &TargetRealm,
  3997. NULL,
  3998. KERB_GET_TICKET_NO_CACHE,
  3999. 0,
  4000. 0,
  4001. NULL,
  4002. NULL,
  4003. NULL,
  4004. &S4UTicket,
  4005. NULL
  4006. );
  4007. if (!NT_SUCCESS( Status ))
  4008. {
  4009. goto Cleanup;
  4010. }
  4011. Cleanup:
  4012. //
  4013. // Make sure to turn off one shot bit, so further requests can process
  4014. //
  4015. if ( !LogonSessionLocked )
  4016. {
  4017. KerbWriteLockLogonSessions( LogonSession );
  4018. }
  4019. LogonSession->LogonSessionFlags &= ~KERB_LOGON_ONE_SHOT;
  4020. KerbUnlockLogonSessions( LogonSession );
  4021. KerbDereferenceLogonSession(LogonSession);
  4022. #if DBG
  4023. D_DebugLog((DEB_TRACE_S4U, "Result %x :: ", Status));
  4024. if (KerbAllowedForS4UProxy(&LogonSession->LogonId))
  4025. {
  4026. DebugLog((DEB_TRACE_S4U, "Allowed\n"));
  4027. }
  4028. else
  4029. {
  4030. DebugLog((DEB_TRACE_S4U, "NOT Allowed\n"));
  4031. }
  4032. #endif
  4033. KerbFreeKdcName( &TargetName );
  4034. if ( ServiceTicket )
  4035. {
  4036. KerbDereferenceTicketCacheEntry( ServiceTicket );
  4037. }
  4038. if ( S4UTicket )
  4039. {
  4040. KerbDereferenceTicketCacheEntry( S4UTicket );
  4041. }
  4042. if ( Tgt )
  4043. {
  4044. KerbDereferenceTicketCacheEntry( Tgt );
  4045. }
  4046. return;
  4047. }
  4048. //+-------------------------------------------------------------------------
  4049. //
  4050. // Function: KerbS4UTaskCleanup
  4051. //
  4052. // Synopsis: Destroys a S4U query task
  4053. //
  4054. // Effects:
  4055. //
  4056. // Arguments: TaskItem - cache entry to destroy
  4057. //
  4058. // Requires:
  4059. //
  4060. // Returns: Nothing
  4061. //
  4062. // Notes:
  4063. //
  4064. //
  4065. //--------------------------------------------------------------------------
  4066. void
  4067. KerbS4UTaskCleanup(
  4068. void * TaskItem
  4069. )
  4070. {
  4071. PKERB_LOGON_SESSION LogonSession = (PKERB_LOGON_SESSION) TaskItem;
  4072. if ( LogonSession )
  4073. {
  4074. //
  4075. // Release the refcount held by the task worker.
  4076. //
  4077. KerbDereferenceLogonSession(LogonSession);
  4078. }
  4079. }
  4080. //+-------------------------------------------------------------------------
  4081. //
  4082. // Function: KerbScheduleS4UQuery
  4083. //
  4084. // Synopsis: Sets up a task in the priority queue so that we can make
  4085. // periodic attempts at S4UProxy, and determine if there are
  4086. // any targets we're qualified (as indicated by the e_data).
  4087. // See gettgs.cxx / KerbUnpackAdditionalTickets for more info
  4088. // on this functionality.
  4089. //
  4090. // Effects:
  4091. //
  4092. // Arguments: LogonSession - Logon session of the service doing the
  4093. // S4U request
  4094. //
  4095. // Requires:
  4096. //
  4097. // Returns:
  4098. //
  4099. // Notes:
  4100. //
  4101. //
  4102. //--------------------------------------------------------------------------
  4103. NTSTATUS
  4104. KerbScheduleS4UQuery(
  4105. IN PKERB_LOGON_SESSION LogonSession,
  4106. IN LONG Interval,
  4107. IN BOOLEAN Periodic
  4108. )
  4109. {
  4110. NTSTATUS Status = STATUS_SUCCESS;
  4111. ULONG LogonSessionFlags;
  4112. KerbReadLockLogonSessions( LogonSession );
  4113. LogonSessionFlags = LogonSession->LogonSessionFlags;
  4114. KerbUnlockLogonSessions( LogonSession );
  4115. //
  4116. // If we've already scheduled a query, bail now, as we
  4117. // don't need to create a new task entry.
  4118. //
  4119. if ((LogonSessionFlags & KERB_LOGON_ONE_SHOT) != 0)
  4120. {
  4121. return Status;
  4122. }
  4123. D_DebugLog((DEB_TRACE_S4U, "Adding %s task to %p\n", (Periodic ? "periodic" : "oneshot"), LogonSession));
  4124. Status = KerbAddScavengerTask(
  4125. Periodic,
  4126. Interval,
  4127. 0, // no special processing flags
  4128. KerbS4UQueryWorker,
  4129. KerbS4UTaskCleanup,
  4130. LogonSession,
  4131. NULL
  4132. );
  4133. if (!NT_SUCCESS(Status))
  4134. {
  4135. D_DebugLog((DEB_ERROR, "Failed to schedule scavenger task (%x) for LS %p\n", Status, LogonSession));
  4136. }
  4137. else
  4138. {
  4139. //
  4140. // Add a reference for the task...
  4141. //
  4142. if ( !Periodic )
  4143. {
  4144. KerbWriteLockLogonSessions( LogonSession );
  4145. LogonSession->LogonSessionFlags |= KERB_LOGON_ONE_SHOT;
  4146. KerbUnlockLogonSessions( LogonSession );
  4147. }
  4148. KerbReferenceLogonSessionByPointer(LogonSession, FALSE);
  4149. }
  4150. return Status;
  4151. }
  4152. //+-------------------------------------------------------------------------
  4153. //
  4154. // Function: KerbScheduleS4UCleanup
  4155. //
  4156. // Synopsis: Walks the list to cleanup any old, unreferenced entries.
  4157. //
  4158. // Effects:
  4159. //
  4160. // Arguments:
  4161. //
  4162. // Requires:
  4163. //
  4164. // Returns:
  4165. //
  4166. // Notes:
  4167. //
  4168. //
  4169. //--------------------------------------------------------------------------
  4170. NTSTATUS
  4171. KerbScheduleS4UCleanup()
  4172. {
  4173. NTSTATUS Status = STATUS_SUCCESS;
  4174. LONG Interval = 60 * 1000 * 60; // once per hour
  4175. Status = KerbAddScavengerTask(
  4176. TRUE,
  4177. Interval,
  4178. 0, // no special processing flags
  4179. KerbS4UCleanupWorker,
  4180. NULL,
  4181. NULL,
  4182. NULL
  4183. );
  4184. if (!NT_SUCCESS(Status))
  4185. {
  4186. D_DebugLog((DEB_ERROR, "Failed to schedule scavenger task (%x) \n", Status));
  4187. }
  4188. return Status;
  4189. }
  4190. //+-------------------------------------------------------------------------
  4191. //
  4192. // Function: KerbAllowedForS4UProxy
  4193. //
  4194. // Synopsis: Check the S4UCACHE_DATA to see if we can do S4U for this target.
  4195. // This will only get called in the AcceptSecurityContext() code path
  4196. // where we'll need to see if we need to cache tickets. Cache them,
  4197. // for now, but kick off a worker thread to see if there's any need
  4198. // to cache tickets in the future.
  4199. //
  4200. // Effects:
  4201. //
  4202. // Arguments: LogonSession - Logon session of the service doing the
  4203. // S4U request
  4204. //
  4205. // Requires:
  4206. //
  4207. // Returns:
  4208. //
  4209. // Notes:
  4210. //
  4211. //
  4212. //+-------------------------------------------------------------------------
  4213. BOOLEAN
  4214. KerbAllowedForS4UProxy(
  4215. IN PLUID LogonId
  4216. )
  4217. {
  4218. ULONG CacheState = 0;
  4219. PKERB_S4UCACHE_DATA S4UCacheEntry = NULL;
  4220. BOOLEAN fRet = TRUE, Update = FALSE;
  4221. PKERB_LOGON_SESSION LogonSession = NULL;
  4222. LUID LocalService = LOCALSERVICE_LUID;
  4223. LUID Anonymous = ANONYMOUS_LOGON_LUID;
  4224. KERBEROS_MACHINE_ROLE Role;
  4225. Role = KerbGetGlobalRole();
  4226. //
  4227. // S4UToProxy is not allowed for anything but
  4228. // server products.
  4229. //
  4230. if (!KerbGlobalRunningServer ||
  4231. RtlEqualLuid(LogonId, &LocalService) ||
  4232. RtlEqualLuid(LogonId, &Anonymous) ||
  4233. Role < KerbRoleWorkstation )
  4234. {
  4235. return FALSE;
  4236. }
  4237. S4UCacheEntry = KerbLocateS4UCacheEntry(
  4238. LogonId,
  4239. &CacheState
  4240. );
  4241. if ( NULL != S4UCacheEntry )
  4242. {
  4243. if (( CacheState & S4UCACHE_S4U_UNAVAILABLE ) != 0)
  4244. {
  4245. D_DebugLog((DEB_TRACE_S4U, "Luid %x:%x can't do S4u\n", LogonId->HighPart, LogonId->LowPart));
  4246. fRet = FALSE;
  4247. }
  4248. if (( CacheState & S4UCACHE_TIMEOUT ) != 0)
  4249. {
  4250. Update = TRUE;
  4251. }
  4252. KerbDereferenceS4UCacheEntry(S4UCacheEntry);
  4253. }
  4254. else
  4255. {
  4256. Update = TRUE;
  4257. }
  4258. if ( Update )
  4259. {
  4260. LONG Period = 1;
  4261. LogonSession = KerbReferenceLogonSession(LogonId, FALSE);
  4262. if ( LogonSession )
  4263. {
  4264. KerbReadLockLogonSessions( LogonSession );
  4265. if (( LogonSession->LogonSessionFlags & KERB_LOGON_LOCAL_ONLY ) != 0)
  4266. {
  4267. KerbUnlockLogonSessions( LogonSession );
  4268. DebugLog((DEB_TRACE_S4U, "Local account %p - s4u not allowed\n", LogonSession ));
  4269. fRet = FALSE;
  4270. }
  4271. else
  4272. {
  4273. KerbUnlockLogonSessions( LogonSession );
  4274. //
  4275. // Do this async in 1sec to keep from blocking
  4276. // ASC.
  4277. //
  4278. KerbScheduleS4UQuery(LogonSession, Period, FALSE);
  4279. KerbDereferenceLogonSession( LogonSession );
  4280. }
  4281. }
  4282. else
  4283. {
  4284. fRet = FALSE;
  4285. }
  4286. }
  4287. return fRet;
  4288. }