Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1758 lines
43 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. #ifdef DEBUG_SUPPORT
  18. static TCHAR THIS_FILE[]=TEXT(__FILE__);
  19. #endif
  20. //+-------------------------------------------------------------------------
  21. //
  22. // Function: KerbInitGlobalS4UCred
  23. //
  24. // Synopsis: Create a KERB_CREDENTIAL structure w/ bogus password for AS
  25. // location of client.
  26. //
  27. //
  28. // Effects:
  29. //
  30. // Arguments:
  31. //
  32. // Requires:
  33. //
  34. // Returns:
  35. //
  36. // Notes:
  37. //
  38. //
  39. //--------------------------------------------------------------------------
  40. NTSTATUS
  41. KerbInitGlobalS4UCred()
  42. {
  43. return STATUS_SUCCESS; // TBD: Come up w/ scheme for global cred..
  44. }
  45. //+-------------------------------------------------------------------------
  46. //
  47. // Function: KerbGetS4UClientIdentity
  48. //
  49. // Synopsis: Attempt to gets TGT for an S4U client for name
  50. // location purposes.
  51. //
  52. //
  53. // Effects:
  54. //
  55. // Arguments: LogonSession - Logon session of the service doing the
  56. // S4U request
  57. //
  58. // Requires:
  59. //
  60. // Returns:
  61. //
  62. // Notes:
  63. //
  64. //
  65. //--------------------------------------------------------------------------
  66. NTSTATUS
  67. KerbGetS4UClientRealm(
  68. IN PKERB_LOGON_SESSION LogonSession,
  69. IN PKERB_INTERNAL_NAME * S4UClientName,
  70. IN OUT PUNICODE_STRING S4UTargetRealm
  71. // TBD: Place for credential handle?
  72. )
  73. {
  74. NTSTATUS Status = STATUS_SUCCESS, LookupStatus = STATUS_SUCCESS;
  75. KERBERR KerbErr;
  76. PKERB_INTERNAL_NAME KdcServiceKdcName = NULL;
  77. PKERB_INTERNAL_NAME ClientName = NULL;
  78. UNICODE_STRING ClientRealm = {0};
  79. UNICODE_STRING CorrectRealm = {0};
  80. ULONG RetryCount = KERB_CLIENT_REFERRAL_MAX;
  81. ULONG RequestFlags = 0;
  82. BOOLEAN UsingSuppliedCreds = FALSE;
  83. BOOLEAN MitRealmLogon = FALSE;
  84. BOOLEAN UsedPrimaryLogonCreds = FALSE;
  85. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
  86. RtlInitUnicodeString(
  87. S4UTargetRealm,
  88. NULL
  89. );
  90. //
  91. // Use our server credentials to start off the AS_REQ process.
  92. // We may get a referral elsewhere, however.
  93. //
  94. Status = KerbGetClientNameAndRealm(
  95. NULL,
  96. &LogonSession->PrimaryCredentials,
  97. UsingSuppliedCreds,
  98. NULL,
  99. &MitRealmLogon,
  100. TRUE,
  101. &ClientName,
  102. &ClientRealm
  103. );
  104. if (!NT_SUCCESS(Status))
  105. {
  106. DebugLog((DEB_ERROR,"Failed to get client name & realm: 0x%x, %ws line %d\n",
  107. Status, THIS_FILE, __LINE__ ));
  108. goto Cleanup;
  109. }
  110. GetTicketRestart:
  111. KerbErr = KerbBuildFullServiceKdcName(
  112. &ClientRealm,
  113. &KerbGlobalKdcServiceName,
  114. KRB_NT_SRV_INST,
  115. &KdcServiceKdcName
  116. );
  117. if (!KERB_SUCCESS(KerbErr))
  118. {
  119. Status = STATUS_INSUFFICIENT_RESOURCES;
  120. goto Cleanup;
  121. }
  122. Status = KerbGetAuthenticationTicket(
  123. LogonSession,
  124. NULL, // KerbGlobalS4UCred,
  125. NULL,
  126. KdcServiceKdcName,
  127. &ClientRealm,
  128. (*S4UClientName),
  129. RequestFlags,
  130. KERB_TICKET_CACHE_PRIMARY_TGT,
  131. &TicketCacheEntry,
  132. NULL,
  133. &CorrectRealm
  134. );
  135. //
  136. // If it failed but gave us another realm to try, go there
  137. //
  138. if (!NT_SUCCESS(Status) && (CorrectRealm.Length != 0))
  139. {
  140. if (--RetryCount != 0)
  141. {
  142. KerbFreeKdcName(&KdcServiceKdcName);
  143. KerbFreeString(&ClientRealm);
  144. ClientRealm = CorrectRealm;
  145. CorrectRealm.Buffer = NULL;
  146. //
  147. // TBD: MIT realm support? See KerbGetTicketGrantingTicket()
  148. // S4UToSelf
  149. goto GetTicketRestart;
  150. }
  151. else
  152. {
  153. // Tbd: Log error here? Max referrals reached..
  154. goto Cleanup;
  155. }
  156. }
  157. //
  158. // TBD: S4UToSelf()
  159. // in KerbGetTgt, we'll be happy to crack the UPN given the [email protected] syntax
  160. // Here, we should just fail out.. Right?
  161. //
  162. //
  163. // If we get STATUS_WRONG_PASSWORD, we succeeded in finding the
  164. // client realm. Otherwise, we're hosed. As the password we used
  165. // is bogus, we should never succeed, btw...
  166. //
  167. DsysAssert(!NT_SUCCESS(Status));
  168. if (Status == STATUS_WRONG_PASSWORD)
  169. {
  170. // fester: define new debug level / trace level
  171. DebugLog((DEB_ERROR, "Found client"));
  172. KerbPrintKdcName(DEB_ERROR, (*S4UClientName));
  173. DebugLog((DEB_ERROR, "\nin realm %wZ\n", &ClientRealm));
  174. *S4UTargetRealm = ClientRealm;
  175. Status = STATUS_SUCCESS;
  176. }
  177. Cleanup:
  178. // if we succeeded, we got the correct realm,
  179. // and we need to pass that back to caller
  180. if (!NT_SUCCESS(Status))
  181. {
  182. KerbFreeString(&ClientRealm);
  183. }
  184. KerbFreeKdcName(&KdcServiceKdcName);
  185. KerbFreeKdcName(&ClientName);
  186. if (NULL != TicketCacheEntry)
  187. {
  188. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  189. // fester: make sure we toss this...
  190. DsysAssert(TicketCacheEntry->ListEntry.ReferenceCount == 1);
  191. }
  192. return(Status);
  193. }
  194. //+-------------------------------------------------------------------------
  195. //
  196. // Function: KerbBuildS4UPreauth
  197. //
  198. // Synopsis: Attempt to gets TGT for an S4U client for name
  199. // location purposes.
  200. //
  201. //
  202. // Effects:
  203. //
  204. // Arguments: LogonSession - Logon session of the service doing the
  205. // S4U request
  206. //
  207. // Requires:
  208. //
  209. // Returns:
  210. //
  211. // Notes:
  212. //
  213. //
  214. //--------------------------------------------------------------------------
  215. KERBERR
  216. KerbBuildS4UPreauth(
  217. IN OUT PKERB_PA_DATA_LIST * PreAuthData,
  218. IN PKERB_INTERNAL_NAME S4UClientName,
  219. IN PUNICODE_STRING S4UClientRealm
  220. )
  221. {
  222. KERBERR KerbErr;
  223. KERB_PA_FOR_USER S4UserPA = {0};
  224. PKERB_PA_DATA_LIST ListElement = NULL;
  225. *PreAuthData = NULL;
  226. KerbErr = KerbConvertKdcNameToPrincipalName(
  227. &S4UserPA.client_name,
  228. S4UClientName
  229. );
  230. if (!KERB_SUCCESS(KerbErr))
  231. {
  232. goto Cleanup;
  233. }
  234. KerbErr = KerbConvertUnicodeStringToRealm(
  235. &S4UserPA.client_realm,
  236. S4UClientRealm
  237. );
  238. if (!KERB_SUCCESS(KerbErr))
  239. {
  240. goto Cleanup;
  241. }
  242. ListElement = (PKERB_PA_DATA_LIST) KerbAllocate(sizeof(KERB_PA_DATA_LIST));
  243. if (ListElement == NULL)
  244. {
  245. goto Cleanup;
  246. }
  247. KerbErr = KerbPackData(
  248. &S4UserPA,
  249. KERB_PA_FOR_USER_PDU,
  250. (PULONG) &ListElement->value.preauth_data.length,
  251. (PUCHAR *) &ListElement->value.preauth_data.value
  252. );
  253. if (!KERB_SUCCESS(KerbErr))
  254. {
  255. goto Cleanup;
  256. }
  257. ListElement->value.preauth_data_type = KRB5_PADATA_S4U;
  258. ListElement->next = NULL;
  259. *PreAuthData = ListElement;
  260. Cleanup:
  261. KerbFreePrincipalName(&S4UserPA.client_name);
  262. KerbFreeRealm(&S4UserPA.client_realm);
  263. return KerbErr;
  264. }
  265. //+-------------------------------------------------------------------------
  266. //
  267. // Function: KerbGetTgtToS4URealm
  268. //
  269. // Synopsis: We need a TGT to the client realm under the caller's cred's
  270. // so we can make a S4U TGS_REQ.
  271. //
  272. //
  273. // Effects:
  274. //
  275. // Arguments: LogonSession - Logon session of the service doing the
  276. // S4U request
  277. //
  278. // Requires:
  279. //
  280. // Returns:
  281. //
  282. // Notes:
  283. //
  284. //
  285. //--------------------------------------------------------------------------
  286. NTSTATUS
  287. KerbGetTgtToS4URealm(
  288. IN PKERB_LOGON_SESSION CallerLogonSession,
  289. IN PKERB_CREDENTIAL Credential,
  290. IN PUNICODE_STRING S4UClientRealm,
  291. IN OUT PKERB_TICKET_CACHE_ENTRY * S4UTgt,
  292. IN ULONG Flags,
  293. IN ULONG TicketOptions,
  294. IN ULONG EncryptionType
  295. )
  296. {
  297. NTSTATUS Status;
  298. ULONG RetryFlags = 0;
  299. BOOLEAN CrossRealm = FALSE, CacheTicket = TRUE;
  300. BOOLEAN TicketCacheLocked = FALSE, LogonSessionsLocked = FALSE;
  301. PKERB_TICKET_CACHE_ENTRY TicketGrantingTicket = NULL;
  302. PKERB_TICKET_CACHE_ENTRY LastTgt = NULL;
  303. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
  304. PKERB_KDC_REPLY KdcReply = NULL;
  305. PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody = NULL;
  306. PKERB_INTERNAL_NAME TargetTgtKdcName = NULL;
  307. UNICODE_STRING ClientRealm = NULL_UNICODE_STRING;
  308. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials = NULL;
  309. *S4UTgt = NULL;
  310. if ((Credential != NULL) && (Credential->SuppliedCredentials != NULL))
  311. {
  312. PrimaryCredentials = Credential->SuppliedCredentials;
  313. }
  314. else
  315. {
  316. PrimaryCredentials = &CallerLogonSession->PrimaryCredentials;
  317. }
  318. Status = KerbGetTgtForService(
  319. CallerLogonSession,
  320. Credential,
  321. NULL,
  322. NULL,
  323. S4UClientRealm,
  324. 0,
  325. &TicketGrantingTicket,
  326. &CrossRealm
  327. );
  328. if (!NT_SUCCESS(Status))
  329. {
  330. goto Cleanup;
  331. }
  332. //
  333. // If this isn't cross realm, then we've got a TGT to the realm.
  334. // return it and bail.
  335. //
  336. if (!CrossRealm)
  337. {
  338. DebugLog((DEB_ERROR, "We have a TGT for %wZ\n", S4UClientRealm));
  339. *S4UTgt = TicketGrantingTicket;
  340. TicketGrantingTicket = NULL;
  341. goto Cleanup;
  342. }
  343. if (!KERB_SUCCESS(KerbBuildFullServiceKdcName(
  344. S4UClientRealm,
  345. &KerbGlobalKdcServiceName,
  346. KRB_NT_SRV_INST,
  347. &TargetTgtKdcName
  348. )))
  349. {
  350. Status = STATUS_INSUFFICIENT_RESOURCES;
  351. goto Cleanup;
  352. }
  353. //
  354. // Copy out the client realm name which is used when obtaining the ticket
  355. //
  356. Status = KerbDuplicateString(
  357. &ClientRealm,
  358. &PrimaryCredentials->DomainName
  359. );
  360. if (!NT_SUCCESS(Status))
  361. {
  362. goto Cleanup;
  363. }
  364. //
  365. // Do some referral chasing to get our ticket grantin ticket.
  366. //
  367. while (!RtlEqualUnicodeString(
  368. S4UClientRealm,
  369. &TicketGrantingTicket->TargetDomainName,
  370. TRUE ))
  371. {
  372. //
  373. // If we just got two TGTs for the same domain, then we must have
  374. // gotten as far as we can. Chances our our RealTargetRealm is a
  375. // variation of what the KDC hands out.
  376. //
  377. if ((LastTgt != NULL) &&
  378. RtlEqualUnicodeString(
  379. &LastTgt->TargetDomainName,
  380. &TicketGrantingTicket->TargetDomainName,
  381. TRUE ))
  382. {
  383. KerbUnlockTicketCache();
  384. KerbSetTicketCacheEntryTarget(
  385. S4UClientRealm,
  386. LastTgt
  387. );
  388. KerbReadLockTicketCache();
  389. TicketCacheLocked = TRUE;
  390. D_DebugLog((DEB_TRACE_CTXT, "Got two TGTs for same realm (%wZ), bailing out of referral loop\n",
  391. &LastTgt->TargetDomainName));
  392. break;
  393. }
  394. D_DebugLog((DEB_TRACE_CTXT, "Getting referral TGT for \n"));
  395. D_KerbPrintKdcName(DEB_TRACE_CTXT, TargetTgtKdcName);
  396. D_KerbPrintKdcName(DEB_TRACE_CTXT, TicketGrantingTicket->ServiceName);
  397. KerbUnlockTicketCache();
  398. TicketCacheLocked = FALSE;
  399. //
  400. // Cleanup old state
  401. //
  402. KerbFreeTgsReply(KdcReply);
  403. KerbFreeKdcReplyBody(KdcReplyBody);
  404. KdcReply = NULL;
  405. KdcReplyBody = NULL;
  406. Status = KerbGetTgsTicket(
  407. &ClientRealm,
  408. TicketGrantingTicket,
  409. TargetTgtKdcName,
  410. FALSE,
  411. TicketOptions,
  412. EncryptionType,
  413. NULL,
  414. NULL, // no PA data here.
  415. NULL, // no tgt reply since target is krbtgt
  416. &KdcReply,
  417. &KdcReplyBody,
  418. &RetryFlags
  419. );
  420. if (!NT_SUCCESS(Status))
  421. {
  422. DebugLog((DEB_WARN,"Failed to get TGS ticket for service 0x%x :",
  423. Status ));
  424. KerbPrintKdcName(DEB_WARN, TargetTgtKdcName );
  425. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  426. //
  427. // We want to map cross-domain failures to failures indicating
  428. // that a KDC could not be found. This means that for Kerberos
  429. // logons, the negotiate code will retry with a different package
  430. //
  431. // if (Status == STATUS_NO_TRUST_SAM_ACCOUNT)
  432. // {
  433. // Status = STATUS_NO_LOGON_SERVERS;
  434. // }
  435. goto Cleanup;
  436. }
  437. //
  438. // Now we have a ticket - lets cache it
  439. //
  440. KerbWriteLockLogonSessions(CallerLogonSession);
  441. LogonSessionsLocked = TRUE;
  442. Status = KerbCacheTicket(
  443. &PrimaryCredentials->AuthenticationTicketCache,
  444. KdcReply,
  445. KdcReplyBody,
  446. NULL, // no target name
  447. NULL, // no targe realm
  448. 0, // no flags
  449. CacheTicket,
  450. &TicketCacheEntry
  451. );
  452. KerbUnlockLogonSessions(CallerLogonSession);
  453. LogonSessionsLocked = FALSE;
  454. if (!NT_SUCCESS(Status))
  455. {
  456. goto Cleanup;
  457. }
  458. if (LastTgt != NULL)
  459. {
  460. KerbDereferenceTicketCacheEntry(LastTgt);
  461. LastTgt = NULL;
  462. }
  463. LastTgt = TicketGrantingTicket;
  464. TicketGrantingTicket = TicketCacheEntry;
  465. TicketCacheEntry = NULL;
  466. KerbReadLockTicketCache();
  467. TicketCacheLocked = TRUE;
  468. } // ** WHILE **
  469. *S4UTgt = TicketGrantingTicket;
  470. TicketGrantingTicket = NULL;
  471. Cleanup:
  472. if (TicketCacheLocked)
  473. {
  474. KerbUnlockTicketCache();
  475. }
  476. if (LogonSessionsLocked)
  477. {
  478. KerbUnlockLogonSessions(CallerLogonSession);
  479. }
  480. KerbFreeTgsReply( KdcReply );
  481. KerbFreeKdcReplyBody( KdcReplyBody );
  482. KerbFreeKdcName( &TargetTgtKdcName );
  483. if (TicketGrantingTicket != NULL)
  484. {
  485. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  486. }
  487. KerbFreeString( &ClientRealm );
  488. return Status;
  489. }
  490. //+-------------------------------------------------------------------------
  491. //
  492. // Function: KerbBuildS4UPreauth
  493. //
  494. // Synopsis: Attempt to gets TGT for an S4U client for name
  495. // location purposes.
  496. //
  497. //
  498. // Effects:
  499. //
  500. // Arguments: LogonSession - Logon session of the service doing the
  501. // S4U request
  502. //
  503. // Requires:
  504. //
  505. // Returns:
  506. //
  507. // Notes:
  508. //
  509. //
  510. //--------------------------------------------------------------------------
  511. NTSTATUS
  512. KerbGetS4UServiceTicket(
  513. IN PKERB_LOGON_SESSION CallerLogonSession,
  514. IN PKERB_LOGON_SESSION NewLogonSession,
  515. IN PKERB_CREDENTIAL Credential,
  516. IN PKERB_INTERNAL_NAME S4UClientName,
  517. IN PUNICODE_STRING S4UClientRealm,
  518. IN OUT PKERB_TICKET_CACHE_ENTRY * S4UTicket,
  519. IN ULONG Flags,
  520. IN ULONG TicketOptions,
  521. IN ULONG EncryptionType
  522. )
  523. {
  524. NTSTATUS Status;
  525. KERBERR KerbErr;
  526. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
  527. PKERB_TICKET_CACHE_ENTRY TicketGrantingTicket = NULL;
  528. PKERB_TICKET_CACHE_ENTRY S4UTgt = NULL;
  529. PKERB_TICKET_CACHE_ENTRY LastTgt = NULL;
  530. PKERB_KDC_REPLY KdcReply = NULL;
  531. PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody = NULL;
  532. PKERB_PA_DATA_LIST S4UPaDataList = NULL;
  533. BOOLEAN LogonSessionsLocked = FALSE;
  534. BOOLEAN TicketCacheLocked = FALSE;
  535. BOOLEAN CrossRealm = FALSE;
  536. PKERB_INTERNAL_NAME RealTargetName = NULL;
  537. PKERB_INTERNAL_NAME TargetName = NULL;
  538. UNICODE_STRING RealTargetRealm = NULL_UNICODE_STRING;
  539. PKERB_INTERNAL_NAME TargetTgtKdcName = NULL;
  540. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials = NULL;
  541. BOOLEAN UsedCredentials = FALSE;
  542. UNICODE_STRING ClientRealm = NULL_UNICODE_STRING;
  543. BOOLEAN CacheTicket = ((Flags & KERB_GET_TICKET_NO_CACHE) == 0);
  544. BOOLEAN PurgeTgt = FALSE;
  545. ULONG ReferralCount = 0;
  546. ULONG RetryFlags = 0;
  547. KERBEROS_MACHINE_ROLE Role;
  548. BOOLEAN fMITRetryAlreadyMade = FALSE;
  549. BOOLEAN TgtRetryMade = FALSE;
  550. BOOLEAN PurgedEntry = FALSE;
  551. //
  552. // Peform S4U TGS_REQ for ourselves
  553. //
  554. Flags |= KERB_GET_TICKET_S4U;
  555. // HACK
  556. TicketOptions |= (KERB_KDC_OPTIONS_name_canonicalize | KERB_KDC_OPTIONS_cname_in_pa_data);
  557. //
  558. // Get our own name, and other globals.
  559. //
  560. KerbGlobalReadLock();
  561. Role = KerbGetGlobalRole();
  562. Status = KerbDuplicateKdcName(
  563. &TargetName,
  564. KerbGlobalMitMachineServiceName
  565. );
  566. KerbGlobalReleaseLock();
  567. if (!NT_SUCCESS(Status))
  568. {
  569. goto Cleanup;
  570. }
  571. //
  572. // Check to see if the credential has any primary credentials
  573. //
  574. TGTRetry:
  575. KerbReadLockLogonSessions(CallerLogonSession);
  576. LogonSessionsLocked = TRUE;
  577. if ((Credential != NULL) && (Credential->SuppliedCredentials != NULL))
  578. {
  579. PrimaryCredentials = Credential->SuppliedCredentials;
  580. UsedCredentials = TRUE;
  581. }
  582. else
  583. {
  584. PrimaryCredentials = &CallerLogonSession->PrimaryCredentials;
  585. }
  586. //
  587. // Here we make sure we have a ticket to the KDC of the user's account
  588. //
  589. if ((Flags & KERB_GET_TICKET_NO_CACHE) == 0)
  590. {
  591. //
  592. // TBD: Create a S4U Ticket cache to hang off of the
  593. // service logon session.
  594. // These tickets have a lifetime of 10 minutes.
  595. //
  596. TicketCacheEntry = KerbLocateTicketCacheEntry(
  597. &PrimaryCredentials->S4UTicketCache,
  598. S4UClientName,
  599. S4UClientRealm
  600. );
  601. }
  602. //
  603. // TBD: More for here?
  604. //
  605. if (TicketCacheEntry != NULL)
  606. {
  607. ULONG TicketFlags;
  608. ULONG CacheTicketFlags;
  609. ULONG CacheEncryptionType;
  610. //
  611. // Check if the flags are present
  612. //
  613. KerbReadLockTicketCache();
  614. CacheTicketFlags = TicketCacheEntry->TicketFlags;
  615. CacheEncryptionType = TicketCacheEntry->Ticket.encrypted_part.encryption_type;
  616. KerbUnlockTicketCache();
  617. TicketFlags = KerbConvertKdcOptionsToTicketFlags(TicketOptions);
  618. if (((CacheTicketFlags & TicketFlags) != TicketFlags) ||
  619. ((EncryptionType != 0) && (CacheEncryptionType != EncryptionType)))
  620. {
  621. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  622. TicketCacheEntry = NULL;
  623. }
  624. else
  625. {
  626. //
  627. // Check the ticket time
  628. //
  629. if (KerbTicketIsExpiring(TicketCacheEntry, TRUE))
  630. {
  631. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  632. TicketCacheEntry = NULL;
  633. }
  634. else
  635. {
  636. *S4UTicket = TicketCacheEntry;
  637. TicketCacheEntry = NULL;
  638. goto Cleanup;
  639. }
  640. }
  641. }
  642. //
  643. // Get a krbtgt/S4URealm ticket
  644. //
  645. Status = KerbGetTgtToS4URealm(
  646. CallerLogonSession,
  647. Credential,
  648. S4UClientRealm,
  649. &TicketGrantingTicket,
  650. Flags, // tbd: support for these options?
  651. TicketOptions,
  652. EncryptionType
  653. );
  654. if (!NT_SUCCESS(Status))
  655. {
  656. DebugLog((DEB_ERROR, "Cannot get S4U Tgt - %x\n", Status));
  657. goto Cleanup;
  658. }
  659. //
  660. // Build the preauth for our TGS req
  661. //
  662. Status = KerbBuildS4UPreauth(
  663. &S4UPaDataList,
  664. S4UClientName,
  665. S4UClientRealm
  666. );
  667. if (!NT_SUCCESS(Status))
  668. {
  669. DebugLog((DEB_ERROR, "KerbBuildS4UPreauth failed - %x\n",Status));
  670. goto Cleanup;
  671. }
  672. //
  673. // If the caller wanted any special options, don't cache this ticket.
  674. //
  675. if ((TicketOptions != 0) || (EncryptionType != 0) || ((Flags & KERB_GET_TICKET_NO_CACHE) != 0))
  676. {
  677. CacheTicket = FALSE;
  678. }
  679. //
  680. // Copy out the client realm name which is used when obtaining the ticket
  681. //
  682. Status = KerbDuplicateString(
  683. &ClientRealm,
  684. &PrimaryCredentials->DomainName
  685. );
  686. if (!NT_SUCCESS(Status))
  687. {
  688. goto Cleanup;
  689. }
  690. KerbUnlockLogonSessions(CallerLogonSession);
  691. LogonSessionsLocked = FALSE;
  692. ReferralRestart:
  693. //
  694. // This is our first S4U TGS_REQ. We'll
  695. // eventually transit back to our realm.
  696. // Note: If we get back a referral, the KDC reply
  697. // is a TGT to another realm, so keep trying, but
  698. // be sure to use that TGT.
  699. //
  700. Status = KerbGetTgsTicket(
  701. &ClientRealm,
  702. TicketGrantingTicket,
  703. TargetName, // TBD: right name?
  704. Flags,
  705. TicketOptions,
  706. EncryptionType,
  707. NULL,
  708. S4UPaDataList,
  709. NULL,
  710. &KdcReply,
  711. &KdcReplyBody,
  712. &RetryFlags
  713. );
  714. //
  715. // We're done w/ S4UTgt. Deref, and check
  716. // for errors
  717. //
  718. if (TicketGrantingTicket != NULL)
  719. {
  720. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  721. //
  722. // Check for bad option TGT purging
  723. //
  724. if (((RetryFlags & KERB_RETRY_WITH_NEW_TGT) != 0) && !TgtRetryMade)
  725. {
  726. DebugLog((DEB_WARN, "Doing TGT retry - %p\n", TicketGrantingTicket));
  727. //
  728. // Unlink && purge bad tgt
  729. //
  730. KerbRemoveTicketCacheEntry(TicketGrantingTicket);
  731. KerbDereferenceTicketCacheEntry(TicketGrantingTicket); // free from list
  732. TgtRetryMade = TRUE;
  733. TicketGrantingTicket = NULL;
  734. goto TGTRetry;
  735. }
  736. TicketGrantingTicket = NULL;
  737. }
  738. if (!NT_SUCCESS(Status))
  739. {
  740. //
  741. // Check for the MIT retry case
  742. //
  743. if (((RetryFlags & KERB_MIT_NO_CANONICALIZE_RETRY) != 0)
  744. && (!fMITRetryAlreadyMade) &&
  745. (Role != KerbRoleRealmlessWksta))
  746. {
  747. Status = KerbMITGetMachineDomain(
  748. CallerLogonSession,
  749. TargetName,
  750. S4UClientRealm,
  751. &TicketGrantingTicket
  752. );
  753. if (!NT_SUCCESS(Status))
  754. {
  755. DebugLog((DEB_ERROR,"Failed Query policy information %ws, line %d\n", THIS_FILE, __LINE__));
  756. goto Cleanup;
  757. }
  758. fMITRetryAlreadyMade = TRUE;
  759. goto TGTRetry;
  760. }
  761. DebugLog((DEB_WARN,"Failed to get TGS ticket for service 0x%x : \n",
  762. Status ));
  763. KerbPrintKdcName(DEB_WARN, TargetName );
  764. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  765. goto Cleanup;
  766. }
  767. //
  768. // Check for referral info in the name
  769. // Should be there if S4Urealm != Our Realm
  770. Status = KerbGetReferralNames(
  771. KdcReplyBody,
  772. TargetName,
  773. &RealTargetRealm
  774. );
  775. if (!NT_SUCCESS(Status))
  776. {
  777. goto Cleanup;
  778. }
  779. //
  780. // If this is not a referral ticket, just cache it and return
  781. // the cache entry.
  782. //
  783. if (RealTargetRealm.Length == 0)
  784. {
  785. //
  786. // Now we have a ticket - lets cache it
  787. //
  788. KerbWriteLockLogonSessions(CallerLogonSession);
  789. LogonSessionsLocked = TRUE;
  790. Status = KerbCacheTicket(
  791. &PrimaryCredentials->S4UTicketCache,
  792. KdcReply,
  793. KdcReplyBody,
  794. TargetName,
  795. S4UClientRealm,
  796. 0,
  797. CacheTicket,
  798. &TicketCacheEntry
  799. );
  800. KerbUnlockLogonSessions(CallerLogonSession);
  801. LogonSessionsLocked = FALSE;
  802. if (!NT_SUCCESS(Status))
  803. {
  804. goto Cleanup;
  805. }
  806. *S4UTicket = TicketCacheEntry;
  807. TicketCacheEntry = NULL;
  808. //
  809. // We're done, so get out of here.
  810. //
  811. goto Cleanup;
  812. }
  813. //
  814. // The server referred us to another domain. Get the service's full
  815. // name from the ticket and try to find a TGT in that domain.
  816. //
  817. Status = KerbDuplicateKdcName(
  818. &RealTargetName,
  819. TargetName
  820. );
  821. if (!NT_SUCCESS(Status))
  822. {
  823. goto Cleanup;
  824. }
  825. D_DebugLog((DEB_TRACE_CTXT, "Got referral ticket for service \n"));
  826. D_KerbPrintKdcName(DEB_TRACE_CTXT,TargetName);
  827. D_DebugLog((DEB_TRACE_CTXT, "in realm \n"));
  828. D_KerbPrintKdcName(DEB_TRACE_CTXT,RealTargetName);
  829. //
  830. // Turn the KDC reply (xrealm tgt w/ s4u pac) into something we can use,
  831. // but *don't* cache it.
  832. //
  833. Status = KerbCacheTicket(
  834. NULL,
  835. KdcReply,
  836. KdcReplyBody,
  837. NULL, // no target name
  838. NULL, // no targe realm
  839. 0, // no flags
  840. FALSE, // just create entry
  841. &TicketCacheEntry
  842. );
  843. if (!NT_SUCCESS(Status))
  844. {
  845. goto Cleanup;
  846. }
  847. TicketGrantingTicket = TicketCacheEntry;
  848. TicketCacheEntry = NULL;
  849. //
  850. // cleanup
  851. //
  852. KerbFreeTgsReply(KdcReply);
  853. KerbFreeKdcReplyBody(KdcReplyBody);
  854. KdcReply = NULL;
  855. KdcReplyBody = NULL;
  856. //
  857. // Now we are in a case where we have a realm to aim for and a TGT. While
  858. // we don't have a TGT for the target realm, get one.
  859. //
  860. if (!KERB_SUCCESS(KerbBuildFullServiceKdcName(
  861. &RealTargetRealm,
  862. &KerbGlobalKdcServiceName,
  863. KRB_NT_SRV_INST,
  864. &TargetTgtKdcName
  865. )))
  866. {
  867. Status = STATUS_INSUFFICIENT_RESOURCES;
  868. goto Cleanup;
  869. }
  870. KerbReadLockTicketCache();
  871. TicketCacheLocked = TRUE;
  872. //
  873. // Referral chasing code block - very important to get right
  874. // If we know the "real" target realm, eg. from GC, then
  875. // we'll walk trusts until we hit "real" target realm.
  876. //
  877. while (!RtlEqualUnicodeString(
  878. &RealTargetRealm,
  879. &TicketGrantingTicket->TargetDomainName,
  880. TRUE ))
  881. {
  882. //
  883. // If we just got two TGTs for the same domain, then we must have
  884. // gotten as far as we can. Chances our our RealTargetRealm is a
  885. // variation of what the KDC hands out.
  886. //
  887. if ((LastTgt != NULL) &&
  888. RtlEqualUnicodeString(
  889. &LastTgt->TargetDomainName,
  890. &TicketGrantingTicket->TargetDomainName,
  891. TRUE ))
  892. {
  893. KerbUnlockTicketCache();
  894. KerbSetTicketCacheEntryTarget(
  895. &RealTargetRealm,
  896. LastTgt
  897. );
  898. KerbReadLockTicketCache();
  899. D_DebugLog((DEB_TRACE_CTXT, "Got two TGTs for same realm (%wZ), bailing out of referral loop\n",
  900. &LastTgt->TargetDomainName));
  901. break;
  902. }
  903. D_DebugLog((DEB_TRACE_CTXT, "Getting referral TGT for \n"));
  904. D_KerbPrintKdcName(DEB_TRACE_CTXT, TargetTgtKdcName);
  905. D_KerbPrintKdcName(DEB_TRACE_CTXT, TicketGrantingTicket->ServiceName);
  906. KerbUnlockTicketCache();
  907. TicketCacheLocked = FALSE;
  908. //
  909. // Cleanup old state
  910. //
  911. KerbFreeTgsReply(KdcReply);
  912. KerbFreeKdcReplyBody(KdcReplyBody);
  913. KdcReply = NULL;
  914. KdcReplyBody = NULL;
  915. Status = KerbGetTgsTicket(
  916. &ClientRealm,
  917. TicketGrantingTicket,
  918. TargetTgtKdcName,
  919. FALSE,
  920. TicketOptions,
  921. EncryptionType,
  922. NULL,
  923. S4UPaDataList,
  924. NULL, // no tgt reply since target is krbtgt
  925. &KdcReply,
  926. &KdcReplyBody,
  927. &RetryFlags
  928. );
  929. if (!NT_SUCCESS(Status))
  930. {
  931. DebugLog((DEB_WARN,"Failed to get TGS ticket for service 0x%x :",
  932. Status ));
  933. KerbPrintKdcName(DEB_WARN, TargetTgtKdcName );
  934. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  935. //
  936. // We want to map cross-domain failures to failures indicating
  937. // that a KDC could not be found. This means that for Kerberos
  938. // logons, the negotiate code will retry with a different package
  939. //
  940. // if (Status == STATUS_NO_TRUST_SAM_ACCOUNT)
  941. // {
  942. // Status = STATUS_NO_LOGON_SERVERS;
  943. // }
  944. goto Cleanup;
  945. }
  946. //
  947. // Now we have a ticket - don't cache it, however
  948. //
  949. Status = KerbCacheTicket(
  950. &PrimaryCredentials->AuthenticationTicketCache,
  951. KdcReply,
  952. KdcReplyBody,
  953. NULL, // no target name
  954. NULL, // no targe realm
  955. 0, // no flags
  956. FALSE,
  957. &TicketCacheEntry
  958. );
  959. if (!NT_SUCCESS(Status))
  960. {
  961. goto Cleanup;
  962. }
  963. if (LastTgt != NULL)
  964. {
  965. KerbFreeTicketCacheEntry(LastTgt);
  966. LastTgt = NULL;
  967. }
  968. LastTgt = TicketGrantingTicket;
  969. TicketGrantingTicket = TicketCacheEntry;
  970. TicketCacheEntry = NULL;
  971. KerbReadLockTicketCache();
  972. TicketCacheLocked = TRUE;
  973. } // ** WHILE **
  974. //
  975. // Now we must have a TGT to our service's domain. Get a ticket
  976. // to the service.
  977. //
  978. // FESTER : put assert in to make sure this tgt is to our realm.
  979. //
  980. // Cleanup old state
  981. //
  982. KerbFreeTgsReply(KdcReply);
  983. KerbFreeKdcReplyBody(KdcReplyBody);
  984. KdcReply = NULL;
  985. KdcReplyBody = NULL;
  986. Status = KerbGetTgsTicket(
  987. &ClientRealm,
  988. TicketGrantingTicket,
  989. TargetName,
  990. FALSE,
  991. TicketOptions,
  992. EncryptionType,
  993. NULL,
  994. S4UPaDataList,
  995. NULL,
  996. &KdcReply,
  997. &KdcReplyBody,
  998. &RetryFlags
  999. );
  1000. if (!NT_SUCCESS(Status))
  1001. {
  1002. DebugLog((DEB_WARN,"Failed to get TGS ticket for service 0x%x ",
  1003. Status ));
  1004. KerbPrintKdcName(DEB_WARN, RealTargetName);
  1005. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  1006. goto Cleanup;
  1007. }
  1008. //
  1009. // Now that we are in the domain to which we were referred, check for referral
  1010. // info in the name
  1011. //
  1012. KerbFreeString(&RealTargetRealm);
  1013. Status = KerbGetReferralNames(
  1014. KdcReplyBody,
  1015. RealTargetName,
  1016. &RealTargetRealm
  1017. );
  1018. if (!NT_SUCCESS(Status))
  1019. {
  1020. goto Cleanup;
  1021. }
  1022. //
  1023. // If this is not a referral ticket, just cache it and return
  1024. // the cache entry.
  1025. //
  1026. if (RealTargetRealm.Length != 0)
  1027. {
  1028. //
  1029. // To prevent loops, we limit the number of referral we'll take
  1030. //
  1031. if (ReferralCount > KerbGlobalMaxReferralCount)
  1032. {
  1033. DebugLog((DEB_ERROR,"Maximum referral count exceeded for name: "));
  1034. KerbPrintKdcName(DEB_ERROR,RealTargetName);
  1035. Status = STATUS_MAX_REFERRALS_EXCEEDED;
  1036. goto Cleanup;
  1037. }
  1038. ReferralCount++;
  1039. //
  1040. // Don't cache the interdomain TGT, as it has PAC info in it.
  1041. //
  1042. Status = KerbCacheTicket(
  1043. NULL,
  1044. KdcReply,
  1045. KdcReplyBody,
  1046. NULL, // no target name
  1047. NULL, // no target realm
  1048. 0, // no flags
  1049. FALSE,
  1050. &TicketCacheEntry
  1051. );
  1052. //
  1053. // Cleanup old state
  1054. //
  1055. KerbFreeTgsReply(KdcReply);
  1056. KerbFreeKdcReplyBody(KdcReplyBody);
  1057. KdcReply = NULL;
  1058. KdcReplyBody = NULL;
  1059. if (!NT_SUCCESS(Status))
  1060. {
  1061. goto Cleanup;
  1062. }
  1063. if (LastTgt != NULL)
  1064. {
  1065. KerbFreeTicketCacheEntry(LastTgt);
  1066. LastTgt = NULL;
  1067. }
  1068. LastTgt = TicketGrantingTicket;
  1069. TicketGrantingTicket = TicketCacheEntry;
  1070. TicketCacheEntry = NULL;
  1071. D_DebugLog((DEB_TRACE_CTXT, "Restart referral:%wZ", &RealTargetRealm));
  1072. goto ReferralRestart;
  1073. }
  1074. //
  1075. // Now we have a ticket - lets cache it
  1076. //
  1077. //
  1078. // Before doing anything, verify that the client name on the ticket
  1079. // is equal to the client name requested during the S4u
  1080. //
  1081. // TBD: Once ticket cache code is ready for this, implement it.
  1082. // Also verify PAC information is correct.
  1083. //
  1084. KerbWriteLockLogonSessions(CallerLogonSession);
  1085. LogonSessionsLocked = TRUE;
  1086. Status = KerbCacheTicket(
  1087. &PrimaryCredentials->S4UTicketCache,
  1088. KdcReply,
  1089. KdcReplyBody,
  1090. TargetName,
  1091. S4UClientRealm,
  1092. 0, // no flags
  1093. CacheTicket,
  1094. &TicketCacheEntry
  1095. );
  1096. KerbUnlockLogonSessions(CallerLogonSession);
  1097. LogonSessionsLocked = FALSE;
  1098. if (!NT_SUCCESS(Status))
  1099. {
  1100. goto Cleanup;
  1101. }
  1102. *S4UTicket = TicketCacheEntry;
  1103. TicketCacheEntry = NULL;
  1104. Cleanup:
  1105. KerbFreeTgsReply( KdcReply );
  1106. KerbFreeKdcReplyBody( KdcReplyBody );
  1107. KerbFreeKdcName( &TargetTgtKdcName );
  1108. KerbFreeString( &RealTargetRealm );
  1109. KerbFreeKdcName(&RealTargetName);
  1110. KerbFreePreAuthData(S4UPaDataList);
  1111. if (TicketCacheLocked)
  1112. {
  1113. KerbUnlockTicketCache();
  1114. }
  1115. if (LogonSessionsLocked)
  1116. {
  1117. KerbUnlockLogonSessions(CallerLogonSession);
  1118. }
  1119. KerbFreeString(&RealTargetRealm);
  1120. //
  1121. // We never cache TGTs in this routine
  1122. // so it's just a blob of memory
  1123. //
  1124. if (TicketGrantingTicket != NULL)
  1125. {
  1126. KerbFreeTicketCacheEntry(TicketGrantingTicket);
  1127. }
  1128. if (LastTgt != NULL)
  1129. {
  1130. KerbFreeTicketCacheEntry(LastTgt);
  1131. LastTgt = NULL;
  1132. }
  1133. //
  1134. // If we still have a pointer to the ticket cache entry, free it now.
  1135. //
  1136. if (TicketCacheEntry != NULL)
  1137. {
  1138. KerbRemoveTicketCacheEntry( TicketCacheEntry );
  1139. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  1140. }
  1141. KerbFreeString(&ClientRealm);
  1142. return(Status);
  1143. }
  1144. //+-------------------------------------------------------------------------
  1145. //
  1146. // Function: KerbCreateS4ULogonSession
  1147. //
  1148. // Synopsis: Creates a logon session to accompany the S4ULogon.
  1149. //
  1150. //
  1151. // Effects:
  1152. //
  1153. // Arguments:
  1154. //
  1155. // Requires:
  1156. //
  1157. // Returns:
  1158. //
  1159. // Notes:
  1160. //
  1161. //
  1162. //--------------------------------------------------------------------------
  1163. NTSTATUS
  1164. KerbCreateS4ULogonSession(
  1165. IN PKERB_INTERNAL_NAME S4UClientName,
  1166. IN PUNICODE_STRING S4UClientRealm,
  1167. IN PLUID pLuid,
  1168. IN OUT PKERB_LOGON_SESSION * LogonSession
  1169. )
  1170. {
  1171. NTSTATUS Status = STATUS_SUCCESS;
  1172. UNICODE_STRING S4UClient = {0};
  1173. *LogonSession = NULL;
  1174. DsysAssert(S4UClientName->NameCount == 1);
  1175. DsysAssert(S4UClientName->NameType == KRB_NT_ENTERPRISE_PRINCIPAL);
  1176. if (!KERB_SUCCESS( KerbConvertKdcNameToString(
  1177. &S4UClient,
  1178. S4UClientName,
  1179. NULL
  1180. )) )
  1181. {
  1182. Status = STATUS_INSUFFICIENT_RESOURCES;
  1183. goto Cleanup;
  1184. }
  1185. Status = KerbCreateLogonSession(
  1186. pLuid,
  1187. &S4UClient,
  1188. S4UClientRealm, // do we need this?
  1189. NULL,
  1190. NULL,
  1191. KERB_LOGON_S4U_SESSION, // fester
  1192. Network,
  1193. LogonSession
  1194. );
  1195. if (!NT_SUCCESS(Status))
  1196. {
  1197. DebugLog((DEB_ERROR, "KerbCreateLogonSession failed %x %ws, line %d\n", Status, THIS_FILE, __LINE__));
  1198. goto Cleanup;
  1199. }
  1200. Cleanup:
  1201. KerbFreeString(&S4UClient);
  1202. return Status;
  1203. }
  1204. //+-------------------------------------------------------------------------
  1205. //
  1206. // Function: KerbS4UToSelfLogon
  1207. //
  1208. // Synopsis: Attempt to gets TGT for an S4U client for name
  1209. // location purposes.
  1210. //
  1211. //
  1212. // Effects:
  1213. //
  1214. // Arguments: LogonSession - Logon session of the service doing the
  1215. // S4U request
  1216. //
  1217. // Requires:
  1218. //
  1219. // Returns:
  1220. //
  1221. // Notes:
  1222. //
  1223. //
  1224. //--------------------------------------------------------------------------
  1225. NTSTATUS
  1226. KerbS4UToSelfLogon(
  1227. IN PVOID ProtocolSubmitBuffer,
  1228. IN PVOID ClientBufferBase,
  1229. IN ULONG SubmitBufferSize,
  1230. OUT PKERB_LOGON_SESSION * NewLogonSession,
  1231. OUT PLUID LogonId,
  1232. OUT PKERB_TICKET_CACHE_ENTRY * WorkstationTicket,
  1233. OUT PKERB_INTERNAL_NAME * S4UClientName,
  1234. OUT PUNICODE_STRING S4UClientRealm
  1235. )
  1236. {
  1237. NTSTATUS Status;
  1238. KERBERR KerbErr;
  1239. PKERB_S4U_LOGON LogonInfo = NULL;
  1240. PKERB_LOGON_SESSION CallerLogonSession = NULL;
  1241. PKERB_INTERNAL_NAME KdcServiceKdcName = NULL;
  1242. SECPKG_CLIENT_INFO ClientInfo;
  1243. ULONG_PTR Offset;
  1244. ULONG Flags = KERB_CRACK_NAME_USE_WKSTA_REALM, ProcessFlags = 0;
  1245. // fester:
  1246. UNICODE_STRING DummyRealm = {0};
  1247. if (!KerbRunningServer())
  1248. {
  1249. D_DebugLog((DEB_ERROR, "Not running server, no S4u!\n"));
  1250. return SEC_E_UNSUPPORTED_FUNCTION;
  1251. }
  1252. *NewLogonSession = NULL;
  1253. *WorkstationTicket = NULL;
  1254. *S4UClientName = NULL;
  1255. RtlInitUnicodeString(
  1256. S4UClientRealm,
  1257. NULL
  1258. );
  1259. Status = LsaFunctions->GetClientInfo(&ClientInfo);
  1260. if (!NT_SUCCESS(Status))
  1261. {
  1262. DebugLog((DEB_ERROR,"Failed to get client information: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  1263. goto Cleanup;
  1264. }
  1265. //
  1266. // TBD: Any other validation code here?
  1267. //
  1268. LogonInfo = (PKERB_S4U_LOGON) ProtocolSubmitBuffer;
  1269. RELOCATE_ONE(&LogonInfo->ClientUpn);
  1270. NULL_RELOCATE_ONE(&LogonInfo->ClientRealm);
  1271. //
  1272. // TBD: put in cache code here, so we can easily locate a ticket we have
  1273. // gotten in the "recent" past.
  1274. //
  1275. //
  1276. // Tbd: Any special name rules to put in here?
  1277. // e.g. if we get a UPN and a realm, which takes
  1278. // precedence?
  1279. //
  1280. //
  1281. // TBD: Convert client name (unicode) into client name (internal)
  1282. //
  1283. Status = KerbProcessTargetNames(
  1284. &LogonInfo->ClientUpn,
  1285. NULL,
  1286. Flags,
  1287. &ProcessFlags,
  1288. S4UClientName,
  1289. &DummyRealm,
  1290. NULL
  1291. );
  1292. // Dummy Realm == info after @ sign
  1293. //DsysAssert(DummyRealm.Length == 0);
  1294. DsysAssert((*S4UClientName)->NameType == KRB_NT_ENTERPRISE_PRINCIPAL);
  1295. if (!NT_SUCCESS(Status))
  1296. {
  1297. goto Cleanup;
  1298. }
  1299. CallerLogonSession = KerbReferenceLogonSession(
  1300. &ClientInfo.LogonId,
  1301. FALSE
  1302. );
  1303. if (NULL == CallerLogonSession)
  1304. {
  1305. D_DebugLog((DEB_ERROR, "Failed to locate caller's logon session - %x\n", ClientInfo.LogonId));
  1306. Status = STATUS_NO_SUCH_LOGON_SESSION;
  1307. DsysAssert(FALSE);
  1308. goto Cleanup;
  1309. }
  1310. //
  1311. // First, we need to get the client's realm from the UPN
  1312. //
  1313. if (LogonInfo->ClientRealm.Length == 0)
  1314. {
  1315. Status = KerbGetS4UClientRealm(
  1316. CallerLogonSession,
  1317. S4UClientName,
  1318. S4UClientRealm
  1319. );
  1320. if (!NT_SUCCESS(Status))
  1321. {
  1322. goto Cleanup;
  1323. }
  1324. }
  1325. else
  1326. {
  1327. Status = KerbDuplicateString(
  1328. S4UClientRealm,
  1329. &LogonInfo->ClientRealm
  1330. );
  1331. if (!NT_SUCCESS(Status))
  1332. {
  1333. goto Cleanup;
  1334. }
  1335. }
  1336. //
  1337. // Allocate a locally unique ID for this logon session. We will
  1338. // create it in the LSA just before returning.
  1339. //
  1340. Status = NtAllocateLocallyUniqueId( LogonId );
  1341. if (!NT_SUCCESS(Status))
  1342. {
  1343. DebugLog((DEB_ERROR,"Failed to allocate locally unique ID: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  1344. goto Cleanup;
  1345. }
  1346. Status = KerbCreateS4ULogonSession(
  1347. (*S4UClientName),
  1348. S4UClientRealm,
  1349. LogonId,
  1350. NewLogonSession
  1351. );
  1352. if (!NT_SUCCESS(Status))
  1353. {
  1354. DebugLog((DEB_ERROR,"Failed to create logon session 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  1355. goto Cleanup;
  1356. }
  1357. Status = KerbGetS4UServiceTicket(
  1358. CallerLogonSession,
  1359. (*NewLogonSession),
  1360. NULL, // tbd: need to put credential here?
  1361. (*S4UClientName),
  1362. S4UClientRealm,
  1363. WorkstationTicket,
  1364. 0, // no flags
  1365. 0, // no ticketoptions
  1366. 0 // no enctype
  1367. );
  1368. if (!NT_SUCCESS(Status))
  1369. {
  1370. goto Cleanup;
  1371. }
  1372. Cleanup:
  1373. if (!NT_SUCCESS(Status))
  1374. {
  1375. //
  1376. // TBD: Negative cache here, based on client name
  1377. //
  1378. KerbFreeString(S4UClientRealm);
  1379. KerbFreeKdcName(S4UClientName);
  1380. *S4UClientName = NULL;
  1381. }
  1382. return Status;
  1383. }