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.

7427 lines
210 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1996
  6. //
  7. // File: kerbtick.cxx
  8. //
  9. // Contents: Routines for obtaining and manipulating tickets
  10. //
  11. //
  12. // History: 23-April-1996 Created MikeSw
  13. // 26-Sep-1998 ChandanS
  14. // Added more debugging support etc.
  15. // 15-Oct-1999 ChandanS
  16. // Send more choice of encryption types.
  17. //
  18. //------------------------------------------------------------------------
  19. #include <kerb.hxx>
  20. #include <kerbp.h>
  21. #include <userapi.h> // for GSS support routines
  22. #include <kerbpass.h>
  23. #include <krbaudit.h>
  24. extern "C"
  25. {
  26. #include <stdlib.h> // abs()
  27. }
  28. #include <utils.hxx>
  29. #ifdef RETAIL_LOG_SUPPORT
  30. static TCHAR THIS_FILE[]=TEXT(__FILE__);
  31. #endif
  32. #define FILENO FILENO_KERBTICK
  33. #ifndef WIN32_CHICAGO // We don't do server side stuff
  34. #include <authen.hxx>
  35. CAuthenticatorList * Authenticators;
  36. #endif // WIN32_CHICAGO // We don't do server side stuff
  37. #include <lsaitf.h>
  38. //+-------------------------------------------------------------------------
  39. //
  40. // Function: KerbRenewTicket
  41. //
  42. // Synopsis: renews a ticket
  43. //
  44. // Effects: Tries to renew a ticket
  45. //
  46. // Arguments: LogonSession - LogonSession for user, contains ticket caches
  47. // and locks
  48. // Credentials - Present if the ticket being renewed is hanging
  49. // off a credential structure
  50. // CredManCredentials - Credman credential
  51. // Ticket - Ticket to renew
  52. // IsTgt - Whether the ticket is a TGT
  53. // NewTicket - Receives the renewed ticket
  54. //
  55. // Requires:
  56. //
  57. // Returns:
  58. //
  59. // Notes:
  60. //
  61. //
  62. //--------------------------------------------------------------------------
  63. NTSTATUS
  64. KerbRenewTicket(
  65. IN PKERB_LOGON_SESSION LogonSession,
  66. IN OPTIONAL PKERB_CREDENTIAL Credentials,
  67. IN OPTIONAL PKERB_CREDMAN_CRED CredManCredentials,
  68. IN PKERB_TICKET_CACHE_ENTRY Ticket,
  69. IN BOOLEAN IsTgt,
  70. OUT PKERB_TICKET_CACHE_ENTRY *NewTicket
  71. )
  72. {
  73. NTSTATUS Status;
  74. PKERB_INTERNAL_NAME ServiceName = NULL;
  75. PKERB_KDC_REPLY KdcReply = NULL;
  76. PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody = NULL;
  77. UNICODE_STRING ServiceRealm = NULL_UNICODE_STRING;
  78. BOOLEAN TicketCacheLocked = FALSE;
  79. BOOLEAN LogonSessionsLocked = FALSE;
  80. PKERB_PRIMARY_CREDENTIAL PrimaryCred;
  81. ULONG CacheFlags = 0;
  82. ULONG RetryFlags = 0;
  83. *NewTicket = NULL;
  84. //
  85. // Copy the names out of the input structures so we can
  86. // unlock the structures while going over the network.
  87. //
  88. KerbReadLockTicketCache();
  89. TicketCacheLocked = TRUE;
  90. //
  91. // If the renew time is not much bigger than the end time, don't bother
  92. // renewing
  93. //
  94. if (KerbGetTime(Ticket->EndTime) + KerbGetTime(KerbGlobalSkewTime) >= KerbGetTime(Ticket->RenewUntil))
  95. {
  96. Status = STATUS_UNSUCCESSFUL;
  97. goto Cleanup;
  98. }
  99. CacheFlags = Ticket->CacheFlags;
  100. Status = KerbDuplicateString(
  101. &ServiceRealm,
  102. &Ticket->DomainName
  103. );
  104. if (!NT_SUCCESS(Status))
  105. {
  106. goto Cleanup;
  107. }
  108. Status = KerbDuplicateKdcName(
  109. &ServiceName,
  110. Ticket->ServiceName
  111. );
  112. if (!NT_SUCCESS(Status))
  113. {
  114. goto Cleanup;
  115. }
  116. if ((Ticket->TicketFlags & KERB_TICKET_FLAGS_renewable) == 0)
  117. {
  118. Status = STATUS_ILLEGAL_FUNCTION;
  119. DebugLog((DEB_ERROR, "KerbRenewTicket trying to renew a non renewable ticket to"));
  120. KerbPrintKdcName(DEB_ERROR, ServiceName);
  121. goto Cleanup;
  122. }
  123. KerbUnlockTicketCache();
  124. TicketCacheLocked = FALSE;
  125. Status = KerbGetTgsTicket(
  126. &ServiceRealm,
  127. Ticket,
  128. ServiceName,
  129. 0, // no flags
  130. KERB_KDC_OPTIONS_renew,
  131. 0, // no encryption type
  132. NULL, // no authorization data
  133. NULL, // no PaDataList
  134. NULL, // no tgt reply
  135. NULL, // no evidence ticket
  136. NULL, // no endtime
  137. &KdcReply,
  138. &KdcReplyBody,
  139. &RetryFlags // no retry is necessary
  140. );
  141. if (!NT_SUCCESS(Status))
  142. {
  143. DebugLog((DEB_ERROR, "KerbRenewTicket failed to get TGS ticket for service 0x%x ", Status));
  144. KerbPrintKdcName(DEB_ERROR, ServiceName);
  145. goto Cleanup;
  146. }
  147. KerbWriteLockLogonSessions(LogonSession);
  148. LogonSessionsLocked = TRUE;
  149. if ((Credentials != NULL) && (Credentials->SuppliedCredentials != NULL))
  150. {
  151. PrimaryCred = Credentials->SuppliedCredentials;
  152. }
  153. else if (CredManCredentials)
  154. {
  155. PrimaryCred = CredManCredentials->SuppliedCredentials;
  156. }
  157. else
  158. {
  159. PrimaryCred = &LogonSession->PrimaryCredentials;
  160. }
  161. Status = KerbCreateTicketCacheEntry(
  162. KdcReply,
  163. KdcReplyBody,
  164. ServiceName,
  165. &ServiceRealm,
  166. CacheFlags,
  167. (IsTgt ? &PrimaryCred->AuthenticationTicketCache : &PrimaryCred->ServerTicketCache),
  168. NULL,
  169. NewTicket
  170. );
  171. if (!NT_SUCCESS(Status))
  172. {
  173. DebugLog((DEB_WARN, "KerbRenewTicket failed to create a ticket cache entry for service %#x ", Status));
  174. KerbPrintKdcName(DEB_WARN, ServiceName);
  175. goto Cleanup;
  176. }
  177. Cleanup:
  178. if (TicketCacheLocked)
  179. {
  180. KerbUnlockTicketCache();
  181. }
  182. if (LogonSessionsLocked)
  183. {
  184. KerbUnlockLogonSessions(LogonSession);
  185. }
  186. KerbFreeTgsReply(KdcReply);
  187. KerbFreeKdcReplyBody(KdcReplyBody);
  188. KerbFreeKdcName(&ServiceName);
  189. KerbFreeString(&ServiceRealm);
  190. return(Status);
  191. }
  192. //+-------------------------------------------------------------------------
  193. //
  194. // Function: KerbRefreshPrimaryTgt
  195. //
  196. // Synopsis: Obtains a new TGT for a logon session or credential
  197. //
  198. //
  199. // Effects: does a new AS exchange with the KDC to get a TGT for the client
  200. //
  201. // Arguments:
  202. //
  203. // Requires:
  204. //
  205. // Returns:
  206. //
  207. // Notes:
  208. //
  209. //
  210. //--------------------------------------------------------------------------
  211. NTSTATUS
  212. KerbRefreshPrimaryTgt(
  213. IN PKERB_LOGON_SESSION LogonSession,
  214. IN OPTIONAL PKERB_CREDENTIAL Credentials,
  215. IN OPTIONAL PKERB_CREDMAN_CRED CredManCredentials,
  216. IN OPTIONAL PUNICODE_STRING SuppRealm,
  217. IN OPTIONAL PKERB_TICKET_CACHE_ENTRY OldTgt,
  218. IN BOOLEAN GetInitialPrimaryTgt
  219. )
  220. {
  221. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  222. if (ARGUMENT_PRESENT(OldTgt))
  223. {
  224. PKERB_TICKET_CACHE_ENTRY NewTgt = NULL;
  225. DebugLog((DEB_WARN, "KerbRefreshPrimaryTgt attempting to renew primary TGT\n"));
  226. Status = KerbRenewTicket(
  227. LogonSession,
  228. Credentials,
  229. CredManCredentials,
  230. OldTgt,
  231. TRUE, // it is a TGT
  232. &NewTgt
  233. );
  234. if (NewTgt)
  235. {
  236. KerbDereferenceTicketCacheEntry(NewTgt);
  237. }
  238. }
  239. if (!NT_SUCCESS(Status) && GetInitialPrimaryTgt)
  240. {
  241. DebugLog((DEB_WARN, "KerbRefreshPrimaryTgt getting new TGT for account\n"));
  242. Status = KerbGetTicketForCredential(
  243. LogonSession,
  244. Credentials,
  245. CredManCredentials,
  246. SuppRealm
  247. );
  248. }
  249. return(Status);
  250. }
  251. //+-------------------------------------------------------------------------
  252. //
  253. // Function: KerbGetTgtForService
  254. //
  255. // Synopsis: Gets a TGT for the domain of the specified service. If a
  256. // cached one is available, it uses that one. Otherwise it
  257. // calls the KDC to acquire it.
  258. //
  259. // Effects:
  260. //
  261. // Arguments: LogonSession - Logon session for which to acquire a ticket
  262. // Credentials - If present & contains supp. creds, use them
  263. // for the ticket cache
  264. // SuppRealm - This is a supplied realm for which to acquire
  265. // a TGT, this may or may not be the same as the
  266. // TargetDomain.
  267. // TargetDomain - Realm of service for which to acquire a TGT
  268. // NewCacheEntry - Receives a referenced ticket cache entry for
  269. // TGT
  270. // CrossRealm - TRUE if target is known to be in a different realm
  271. //
  272. // Requires: The primary credentials be locked
  273. //
  274. // Returns: Kerberos errors, NT status codes.
  275. //
  276. // Notes:
  277. //
  278. //
  279. //--------------------------------------------------------------------------
  280. NTSTATUS
  281. KerbGetTgtForService(
  282. IN PKERB_LOGON_SESSION LogonSession,
  283. IN PKERB_CREDENTIAL Credential,
  284. IN OPTIONAL PKERB_CREDMAN_CRED CredManCredentials,
  285. IN OPTIONAL PUNICODE_STRING SuppRealm,
  286. IN PUNICODE_STRING TargetDomain,
  287. IN ULONG TargetFlags,
  288. OUT PKERB_TICKET_CACHE_ENTRY * NewCacheEntry,
  289. OUT PBOOLEAN CrossRealm
  290. )
  291. {
  292. NTSTATUS Status = STATUS_SUCCESS;
  293. PKERB_TICKET_CACHE_ENTRY CacheEntry;
  294. BOOLEAN DoRetry = TRUE;
  295. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials;
  296. *CrossRealm = FALSE;
  297. *NewCacheEntry = NULL;
  298. D_DebugLog((DEB_TRACE, "KerbGetTgtForService TargetFlags %#x, SuppRealm %wZ, TargetFlags %wZ\n", TargetFlags, SuppRealm, TargetDomain));
  299. if (ARGUMENT_PRESENT(Credential) && (Credential->SuppliedCredentials != NULL))
  300. {
  301. PrimaryCredentials = Credential->SuppliedCredentials;
  302. }
  303. else if (ARGUMENT_PRESENT(CredManCredentials))
  304. {
  305. PrimaryCredentials = CredManCredentials->SuppliedCredentials;
  306. }
  307. else
  308. {
  309. PrimaryCredentials = &LogonSession->PrimaryCredentials;
  310. }
  311. if (TargetDomain->Length != 0)
  312. {
  313. CacheEntry = KerbLocateTicketCacheEntryByRealm(
  314. &PrimaryCredentials->AuthenticationTicketCache,
  315. TargetDomain,
  316. 0
  317. );
  318. if (CacheEntry != NULL)
  319. {
  320. *NewCacheEntry = CacheEntry;
  321. goto Cleanup;
  322. }
  323. }
  324. //
  325. // Well, we didn't find one to the other domain. Return a TGT for our
  326. // domain.
  327. //
  328. Retry:
  329. CacheEntry = KerbLocateTicketCacheEntryByRealm(
  330. &PrimaryCredentials->AuthenticationTicketCache,
  331. SuppRealm,
  332. KERB_TICKET_CACHE_PRIMARY_TGT
  333. );
  334. if (CacheEntry != NULL)
  335. {
  336. //
  337. // The first pass, make sure the ticket has a little bit of life.
  338. // The second pass, we don't ask for as much
  339. //
  340. if (!KerbTicketIsExpiring(CacheEntry, DoRetry))
  341. {
  342. Status = STATUS_SUCCESS;
  343. *NewCacheEntry = CacheEntry;
  344. //
  345. // If the TGT is not for the domain of the service,
  346. // this is cross realm. If we used the SPN cache, we're
  347. // obviously missing a ticket, and we need to restart the
  348. // referral process.
  349. //
  350. //
  351. if ((TargetDomain->Length != 0) &&
  352. (TargetFlags & KERB_TARGET_USED_SPN_CACHE) == 0)
  353. {
  354. *CrossRealm = TRUE;
  355. }
  356. goto Cleanup;
  357. }
  358. }
  359. //
  360. // Try to obtain a new TGT
  361. //
  362. if (DoRetry)
  363. {
  364. PKERB_PRIMARY_CREDENTIAL PrimaryCred;
  365. if ((Credential != NULL) && (Credential->SuppliedCredentials != NULL))
  366. {
  367. PrimaryCred = Credential->SuppliedCredentials;
  368. }
  369. else if (CredManCredentials)
  370. {
  371. PrimaryCred = CredManCredentials->SuppliedCredentials;
  372. }
  373. else
  374. {
  375. PrimaryCred = &LogonSession->PrimaryCredentials;
  376. }
  377. //
  378. // Unlock the logon session so we can try to get a new TGT
  379. //
  380. KerbUnlockLogonSessions(LogonSession);
  381. DebugLog((DEB_WARN, "KerbGetTgtForService getting new TGT for account\n"));
  382. if (KerbHaveKeyMaterials(NULL, PrimaryCred))
  383. {
  384. Status = KerbGetTicketForCredential(
  385. LogonSession,
  386. Credential,
  387. CredManCredentials,
  388. SuppRealm
  389. );
  390. }
  391. else // no key materials? renew the existing TGT
  392. {
  393. Status = KerbRefreshPrimaryTgt(
  394. LogonSession,
  395. Credential,
  396. CredManCredentials,
  397. SuppRealm,
  398. CacheEntry,
  399. PrimaryCred->PublicKeyCreds != NULL // get initial primary TGT for smartcard credential
  400. );
  401. if (!NT_SUCCESS(Status))
  402. {
  403. DebugLog((DEB_WARN, "KerbGetTgtForService falied to refresh TGT %#x for %wZ@%wZ\n", Status, &PrimaryCred->UserName, &PrimaryCred->DomainName));
  404. DoRetry = FALSE; // do not remove the existing cache entry and allow retry
  405. }
  406. }
  407. if (CacheEntry != NULL)
  408. {
  409. // pull the old TGT from the list, if its been replaced
  410. if (NT_SUCCESS(Status) && DoRetry)
  411. {
  412. KerbRemoveTicketCacheEntry(CacheEntry);
  413. }
  414. KerbDereferenceTicketCacheEntry(CacheEntry);
  415. CacheEntry = NULL;
  416. }
  417. KerbReadLockLogonSessions(LogonSession);
  418. if (!NT_SUCCESS(Status) && DoRetry)
  419. {
  420. DebugLog((DEB_ERROR, "KerbGetTgtForService failed to refresh primary TGT: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__ ));
  421. goto Cleanup;
  422. }
  423. DoRetry = FALSE;
  424. goto Retry;
  425. }
  426. if (NT_SUCCESS(Status))
  427. {
  428. Status = SEC_E_NO_CREDENTIALS; // can not get TGT, possibly TGT expired
  429. }
  430. Cleanup:
  431. if (!NT_SUCCESS(Status) && (CacheEntry != NULL))
  432. {
  433. KerbDereferenceTicketCacheEntry(CacheEntry);
  434. *NewCacheEntry = NULL;
  435. }
  436. return (Status);
  437. }
  438. //+-------------------------------------------------------------------------
  439. //
  440. // Function: KerbBuildKerbCred
  441. //
  442. // Synopsis: Builds a marshalled KERB_CRED structure
  443. //
  444. // Effects: allocates destination with MIDL_user_allocate
  445. //
  446. // Arguments: Ticket - The ticket of the session key to seal the
  447. // encrypted portion (OPTIONAL)
  448. // DelegationTicket - The ticket to marshall into the cred message
  449. // MarshalledKerbCred - Receives a marshalled KERB_CRED structure
  450. // KerbCredSizes - Receives size, in bytes, of marshalled
  451. // KERB_CRED.
  452. //
  453. // Requires:
  454. //
  455. // Returns:
  456. //
  457. // Notes:
  458. //
  459. //
  460. //--------------------------------------------------------------------------
  461. NTSTATUS
  462. KerbBuildKerbCred(
  463. IN OPTIONAL PKERB_TICKET_CACHE_ENTRY Ticket,
  464. IN PKERB_TICKET_CACHE_ENTRY DelegationTicket,
  465. OUT PUCHAR * MarshalledKerbCred,
  466. OUT PULONG KerbCredSize
  467. )
  468. {
  469. NTSTATUS Status = STATUS_SUCCESS;
  470. KERBERR KerbErr;
  471. KERB_CRED KerbCred;
  472. KERB_CRED_INFO_LIST CredInfo;
  473. KERB_ENCRYPTED_CRED EncryptedCred;
  474. KERB_CRED_TICKET_LIST TicketList;
  475. PUCHAR MarshalledEncryptPart = NULL;
  476. ULONG MarshalledEncryptSize;
  477. ULONG ConvertedFlags;
  478. //
  479. // Initialize the structures so they can be freed later.
  480. //
  481. *MarshalledKerbCred = NULL;
  482. *KerbCredSize = 0;
  483. RtlZeroMemory(
  484. &KerbCred,
  485. sizeof(KERB_CRED)
  486. );
  487. RtlZeroMemory(
  488. &EncryptedCred,
  489. sizeof(KERB_ENCRYPTED_CRED)
  490. );
  491. RtlZeroMemory(
  492. &CredInfo,
  493. sizeof(KERB_CRED_INFO_LIST)
  494. );
  495. RtlZeroMemory(
  496. &TicketList,
  497. sizeof(KERB_CRED_TICKET_LIST)
  498. );
  499. KerbCred.version = KERBEROS_VERSION;
  500. KerbCred.message_type = KRB_CRED;
  501. //
  502. // First stick the ticket into the ticket list.
  503. //
  504. KerbReadLockTicketCache();
  505. TicketList.next= NULL;
  506. TicketList.value = DelegationTicket->Ticket;
  507. KerbCred.tickets = &TicketList;
  508. //
  509. // Now build the KERB_CRED_INFO for this ticket
  510. //
  511. CredInfo.value.key = DelegationTicket->SessionKey;
  512. KerbConvertLargeIntToGeneralizedTime(
  513. &CredInfo.value.endtime,
  514. NULL,
  515. &DelegationTicket->EndTime
  516. );
  517. CredInfo.value.bit_mask |= endtime_present;
  518. KerbConvertLargeIntToGeneralizedTime(
  519. &CredInfo.value.starttime,
  520. NULL,
  521. &DelegationTicket->StartTime
  522. );
  523. CredInfo.value.bit_mask |= KERB_CRED_INFO_starttime_present;
  524. KerbConvertLargeIntToGeneralizedTime(
  525. &CredInfo.value.KERB_CRED_INFO_renew_until,
  526. NULL,
  527. &DelegationTicket->RenewUntil
  528. );
  529. CredInfo.value.bit_mask |= KERB_CRED_INFO_renew_until_present;
  530. ConvertedFlags = KerbConvertUlongToFlagUlong(DelegationTicket->TicketFlags);
  531. CredInfo.value.flags.value = (PUCHAR) &ConvertedFlags;
  532. CredInfo.value.flags.length = 8 * sizeof(ULONG);
  533. CredInfo.value.bit_mask |= flags_present;
  534. //
  535. // The following fields are marked as optional but treated
  536. // as mandatory by the MIT implementation of Kerberos and
  537. // therefore we provide them.
  538. //
  539. KerbErr = KerbConvertKdcNameToPrincipalName(
  540. &CredInfo.value.principal_name,
  541. DelegationTicket->ClientName
  542. );
  543. if (!KERB_SUCCESS(KerbErr))
  544. {
  545. Status = KerbMapKerbError(KerbErr);
  546. goto Cleanup;
  547. }
  548. CredInfo.value.bit_mask |= principal_name_present;
  549. KerbErr = KerbConvertKdcNameToPrincipalName(
  550. &CredInfo.value.service_name,
  551. DelegationTicket->ServiceName
  552. );
  553. if (!KERB_SUCCESS(KerbErr))
  554. {
  555. Status = KerbMapKerbError(KerbErr);
  556. goto Cleanup;
  557. }
  558. CredInfo.value.bit_mask |= service_name_present;
  559. //
  560. // We are assuming that because we are sending a TGT the
  561. // client realm is the same as the server realm. If we ever
  562. // send non-tgt or cross-realm tgt, this needs to be fixed.
  563. //
  564. KerbErr = KerbConvertUnicodeStringToRealm(
  565. &CredInfo.value.principal_realm,
  566. &DelegationTicket->ClientDomainName
  567. );
  568. if (!KERB_SUCCESS(KerbErr))
  569. {
  570. goto Cleanup;
  571. }
  572. //
  573. // The realms are the same, so don't allocate both
  574. //
  575. CredInfo.value.service_realm = CredInfo.value.principal_realm;
  576. CredInfo.value.bit_mask |= principal_realm_present | service_realm_present;
  577. EncryptedCred.ticket_info = &CredInfo;
  578. //
  579. // Now encrypted the encrypted cred into the cred
  580. //
  581. if (!KERB_SUCCESS(KerbPackEncryptedCred(
  582. &EncryptedCred,
  583. &MarshalledEncryptSize,
  584. &MarshalledEncryptPart
  585. )))
  586. {
  587. Status = STATUS_INSUFFICIENT_RESOURCES;
  588. goto Cleanup;
  589. }
  590. //
  591. // If we are doing DES encryption, then we are talking with an non-NT
  592. // server. Hence, don't encrypt the kerb-cred.
  593. //
  594. // Additionally, if service ticket == NULL, don't encrypt kerb cred
  595. //
  596. if (!ARGUMENT_PRESENT(Ticket))
  597. {
  598. KerbCred.encrypted_part.cipher_text.length = MarshalledEncryptSize;
  599. KerbCred.encrypted_part.cipher_text.value = MarshalledEncryptPart;
  600. KerbCred.encrypted_part.encryption_type = 0;
  601. MarshalledEncryptPart = NULL;
  602. }
  603. else if( KERB_IS_DES_ENCRYPTION(Ticket->SessionKey.keytype))
  604. {
  605. KerbCred.encrypted_part.cipher_text.length = MarshalledEncryptSize;
  606. KerbCred.encrypted_part.cipher_text.value = MarshalledEncryptPart;
  607. KerbCred.encrypted_part.encryption_type = 0;
  608. MarshalledEncryptPart = NULL;
  609. }
  610. else
  611. {
  612. //
  613. // Now get the encryption overhead
  614. //
  615. KerbErr = KerbAllocateEncryptionBufferWrapper(
  616. Ticket->SessionKey.keytype,
  617. MarshalledEncryptSize,
  618. &KerbCred.encrypted_part.cipher_text.length,
  619. &KerbCred.encrypted_part.cipher_text.value
  620. );
  621. if (!KERB_SUCCESS(KerbErr))
  622. {
  623. Status = KerbMapKerbError(KerbErr);
  624. goto Cleanup;
  625. }
  626. //
  627. // Encrypt the data.
  628. //
  629. KerbErr = KerbEncryptDataEx(
  630. &KerbCred.encrypted_part,
  631. MarshalledEncryptSize,
  632. MarshalledEncryptPart,
  633. KERB_NO_KEY_VERSION,
  634. KERB_CRED_SALT,
  635. &Ticket->SessionKey
  636. );
  637. if (!KERB_SUCCESS(KerbErr))
  638. {
  639. Status = STATUS_INSUFFICIENT_RESOURCES;
  640. goto Cleanup;
  641. }
  642. }
  643. //
  644. // Now we have to marshall the whole KERB_CRED
  645. //
  646. if (!KERB_SUCCESS(KerbPackKerbCred(
  647. &KerbCred,
  648. KerbCredSize,
  649. MarshalledKerbCred
  650. )))
  651. {
  652. Status = STATUS_INSUFFICIENT_RESOURCES;
  653. goto Cleanup;
  654. }
  655. Cleanup:
  656. KerbUnlockTicketCache();
  657. KerbFreePrincipalName(&CredInfo.value.service_name);
  658. KerbFreePrincipalName(&CredInfo.value.principal_name);
  659. KerbFreeRealm(&CredInfo.value.principal_realm);
  660. if (MarshalledEncryptPart != NULL)
  661. {
  662. MIDL_user_free(MarshalledEncryptPart);
  663. }
  664. if (KerbCred.encrypted_part.cipher_text.value != NULL)
  665. {
  666. MIDL_user_free(KerbCred.encrypted_part.cipher_text.value);
  667. }
  668. return(Status);
  669. }
  670. //+-------------------------------------------------------------------------
  671. //
  672. // Function: KerbGetDelegationTgt
  673. //
  674. // Synopsis: Gets a TGT to delegate to another service. This TGT
  675. // is marked as forwarded and does not include any
  676. // client addresses
  677. //
  678. // Effects:
  679. //
  680. // Arguments:
  681. //
  682. // Requires: Logon sesion must be read-locked
  683. //
  684. // Returns:
  685. //
  686. // Notes: This gets a delegation TGT & caches it under the realm name
  687. // "$$Delegation Ticket$$". When we look for it again later,
  688. // it should be discoverable under the same name.
  689. //
  690. //
  691. //--------------------------------------------------------------------------
  692. NTSTATUS
  693. KerbGetDelegationTgt(
  694. IN PKERB_LOGON_SESSION LogonSession,
  695. IN PKERB_PRIMARY_CREDENTIAL Credentials,
  696. IN PKERB_TICKET_CACHE_ENTRY ServicetTicket,
  697. OUT PKERB_TICKET_CACHE_ENTRY * DelegationTgt
  698. )
  699. {
  700. PKERB_TICKET_CACHE_ENTRY TicketGrantingTicket = NULL;
  701. PKERB_INTERNAL_NAME TgsName = NULL;
  702. UNICODE_STRING TgsRealm = {0};
  703. PKERB_KDC_REPLY KdcReply = NULL;
  704. PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody = NULL;
  705. BOOLEAN LogonSessionLocked = TRUE;
  706. NTSTATUS Status = STATUS_SUCCESS;
  707. UNICODE_STRING CacheName;
  708. BOOLEAN CacheTicket = TRUE;
  709. ULONG RetryFlags = 0;
  710. RtlInitUnicodeString(
  711. &CacheName,
  712. L"$$Delegation Ticket$$"
  713. );
  714. *DelegationTgt = KerbLocateTicketCacheEntryByRealm(
  715. &Credentials->AuthenticationTicketCache,
  716. &CacheName,
  717. KERB_TICKET_CACHE_DELEGATION_TGT
  718. );
  719. if (*DelegationTgt != NULL )
  720. {
  721. KerbReadLockTicketCache();
  722. //
  723. // when use cached tgt, make sure the encryption_type matches and
  724. // life time is no less than that of the service ticket
  725. //
  726. if ((ServicetTicket->Ticket.encrypted_part.encryption_type != (*DelegationTgt)->Ticket.encrypted_part.encryption_type)
  727. || (KerbGetTime((*DelegationTgt)->EndTime) < KerbGetTime(ServicetTicket->EndTime)))
  728. {
  729. KerbUnlockTicketCache();
  730. KerbDereferenceTicketCacheEntry(*DelegationTgt);
  731. *DelegationTgt = NULL;
  732. CacheTicket = FALSE;
  733. }
  734. else
  735. {
  736. KerbUnlockTicketCache();
  737. goto Cleanup;
  738. }
  739. }
  740. TicketGrantingTicket = KerbLocateTicketCacheEntryByRealm(
  741. &Credentials->AuthenticationTicketCache,
  742. &Credentials->DomainName, // take the logon TGT
  743. 0
  744. );
  745. if ((TicketGrantingTicket == NULL) ||
  746. ((TicketGrantingTicket->TicketFlags & KERB_TICKET_FLAGS_forwardable) == 0) )
  747. {
  748. DebugLog((DEB_WARN, "Trying to delegate but no forwardable TGT\n"));
  749. Status = SEC_E_NO_CREDENTIALS;
  750. goto Cleanup;
  751. }
  752. //
  753. // Get the TGT service name from the TGT
  754. //
  755. KerbReadLockTicketCache();
  756. Status = KerbDuplicateKdcName(
  757. &TgsName,
  758. TicketGrantingTicket->ServiceName
  759. );
  760. if (NT_SUCCESS(Status))
  761. {
  762. Status = KerbDuplicateString(
  763. &TgsRealm,
  764. &TicketGrantingTicket->DomainName
  765. );
  766. }
  767. KerbUnlockTicketCache();
  768. if (!NT_SUCCESS(Status))
  769. {
  770. goto Cleanup;
  771. }
  772. DsysAssert( LogonSessionLocked );
  773. KerbUnlockLogonSessions(LogonSession);
  774. LogonSessionLocked = FALSE;
  775. //
  776. // Now try to get the ticket.
  777. //
  778. Status = KerbGetTgsTicket(
  779. &TgsRealm,
  780. TicketGrantingTicket,
  781. TgsName,
  782. TRUE, // no name canonicalization, no GC lookups.
  783. KERB_KDC_OPTIONS_forwarded | KERB_DEFAULT_TICKET_FLAGS,
  784. ServicetTicket->Ticket.encrypted_part.encryption_type, // no encryption type
  785. NULL, // no authorization data
  786. NULL, // no pa data
  787. NULL, // no tgt reply
  788. NULL, // no evidence ticket
  789. NULL, // let kdc determine end time
  790. &KdcReply,
  791. &KdcReplyBody,
  792. &RetryFlags
  793. );
  794. if (!NT_SUCCESS(Status))
  795. {
  796. goto Cleanup;
  797. }
  798. DsysAssert( !LogonSessionLocked );
  799. KerbReadLockLogonSessions(LogonSession);
  800. LogonSessionLocked = TRUE;
  801. Status = KerbCreateTicketCacheEntry(
  802. KdcReply,
  803. KdcReplyBody,
  804. NULL, // no target name
  805. &CacheName,
  806. KERB_TICKET_CACHE_DELEGATION_TGT,
  807. CacheTicket ? &Credentials->AuthenticationTicketCache : NULL,
  808. NULL, // no credential key
  809. DelegationTgt
  810. );
  811. if (!NT_SUCCESS(Status))
  812. {
  813. goto Cleanup;
  814. }
  815. Cleanup:
  816. KerbFreeTgsReply (KdcReply);
  817. KerbFreeKdcReplyBody (KdcReplyBody);
  818. if (TicketGrantingTicket != NULL)
  819. {
  820. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  821. }
  822. KerbFreeKdcName(&TgsName);
  823. KerbFreeString(&TgsRealm);
  824. if (!LogonSessionLocked)
  825. {
  826. KerbReadLockLogonSessions(LogonSession);
  827. }
  828. return(Status);
  829. }
  830. //+-------------------------------------------------------------------------
  831. //
  832. // Function: KerbBuildGssChecskum
  833. //
  834. // Synopsis: Builds the GSS checksum to go in an AP request
  835. //
  836. // Effects: Allocates a checksum with KerbAllocate
  837. //
  838. // Arguments: ContextFlags - Requested context flags
  839. // LogonSession - LogonSession to be used for delegation
  840. // GssChecksum - Receives the new checksum
  841. // ApOptions - Receives the requested AP options
  842. //
  843. // Requires:
  844. //
  845. // Returns:
  846. //
  847. // Notes: The logon session is locked when this is called.
  848. //
  849. //
  850. //--------------------------------------------------------------------------
  851. NTSTATUS
  852. KerbBuildGssChecksum(
  853. IN PKERB_LOGON_SESSION LogonSession,
  854. IN PKERB_PRIMARY_CREDENTIAL PrimaryCredentials,
  855. IN PKERB_TICKET_CACHE_ENTRY Ticket,
  856. IN OUT PULONG ContextFlags,
  857. OUT PKERB_CHECKSUM GssChecksum,
  858. OUT PULONG ApOptions,
  859. IN PSEC_CHANNEL_BINDINGS pChannelBindings
  860. )
  861. {
  862. NTSTATUS Status = STATUS_SUCCESS;
  863. PKERB_GSS_CHECKSUM ChecksumBody = NULL;
  864. PKERB_TICKET_CACHE_ENTRY TicketGrantingTicket = NULL;
  865. ULONG ChecksumSize = GSS_CHECKSUM_SIZE;
  866. ULONG KerbCredSize = 0 ;
  867. PUCHAR KerbCred = NULL;
  868. BOOL OkAsDelegate = FALSE, OkToTrustMitKdc = FALSE;
  869. PKERB_MIT_REALM MitRealm = NULL;
  870. BOOLEAN UsedAlternateName;
  871. ULONG BindHash[4];
  872. *ApOptions = 0;
  873. //
  874. // If we are doing delegation, built a KERB_CRED message to return
  875. //
  876. if (*ContextFlags & (ISC_RET_DELEGATE | ISC_RET_DELEGATE_IF_SAFE))
  877. {
  878. KerbReadLockTicketCache();
  879. OkAsDelegate = ((Ticket->TicketFlags & KERB_TICKET_FLAGS_ok_as_delegate) != 0) ? TRUE : FALSE;
  880. if (KerbLookupMitRealm(
  881. &Ticket->DomainName,
  882. &MitRealm,
  883. &UsedAlternateName))
  884. {
  885. if ((MitRealm->Flags & KERB_MIT_REALM_TRUSTED_FOR_DELEGATION) != 0)
  886. {
  887. OkToTrustMitKdc = TRUE;
  888. }
  889. }
  890. KerbUnlockTicketCache();
  891. if (OkAsDelegate || OkToTrustMitKdc)
  892. {
  893. D_DebugLog((DEB_TRACE, "KerbBuildGssChecksum asked for delegate if safe, and getting delegation TGT\n"));
  894. //
  895. // Check to see if we have a TGT
  896. //
  897. Status = KerbGetDelegationTgt(
  898. LogonSession,
  899. PrimaryCredentials,
  900. Ticket,
  901. &TicketGrantingTicket
  902. );
  903. if (!NT_SUCCESS(Status))
  904. {
  905. //
  906. // Turn off the delegate flag for building the token.
  907. //
  908. *ContextFlags &= ~ISC_RET_DELEGATE;
  909. *ContextFlags &= ~ISC_RET_DELEGATE_IF_SAFE;
  910. DebugLog((DEB_WARN, "KerbBuildGssChecksum failed to get delegation TGT: 0x%x\n", Status));
  911. Status = STATUS_SUCCESS;
  912. }
  913. else
  914. {
  915. //
  916. // Build the KERB_CRED message
  917. //
  918. Status = KerbBuildKerbCred(
  919. Ticket,
  920. TicketGrantingTicket,
  921. &KerbCred,
  922. &KerbCredSize
  923. );
  924. if (!NT_SUCCESS(Status))
  925. {
  926. D_DebugLog((DEB_ERROR, "Failed to build KERB_CRED: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__));
  927. goto Cleanup;
  928. }
  929. //ChecksumSize= sizeof(KERB_GSS_CHECKSUM) - ANYSIZE_ARRAY * sizeof(UCHAR) + KerbCredSize;
  930. ChecksumSize = GSS_DELEGATE_CHECKSUM_SIZE + KerbCredSize;
  931. //
  932. // And if only the DELEGATE_IF_SAFE flag was on, turn on the
  933. // real delegate flag:
  934. //
  935. *ContextFlags |= ISC_RET_DELEGATE ;
  936. }
  937. }
  938. else
  939. {
  940. //
  941. // Turn off the delegate flag for building the token.
  942. //
  943. *ContextFlags &= ~ISC_RET_DELEGATE;
  944. *ContextFlags &= ~ISC_RET_DELEGATE_IF_SAFE;
  945. }
  946. }
  947. ChecksumBody = (PKERB_GSS_CHECKSUM) KerbAllocate(ChecksumSize);
  948. if (ChecksumBody == NULL)
  949. {
  950. Status = STATUS_INSUFFICIENT_RESOURCES;
  951. goto Cleanup;
  952. }
  953. //
  954. // Convert the requested flags to AP options.
  955. //
  956. if ((*ContextFlags & ISC_RET_MUTUAL_AUTH) != 0)
  957. {
  958. *ApOptions |= KERB_AP_OPTIONS_mutual_required;
  959. ChecksumBody->GssFlags |= GSS_C_MUTUAL_FLAG;
  960. }
  961. if ((*ContextFlags & ISC_RET_USE_SESSION_KEY) != 0)
  962. {
  963. *ApOptions |= KERB_AP_OPTIONS_use_session_key;
  964. }
  965. if ((*ContextFlags & ISC_RET_USED_DCE_STYLE) != 0)
  966. {
  967. ChecksumBody->GssFlags |= GSS_C_DCE_STYLE;
  968. }
  969. if ((*ContextFlags & ISC_RET_SEQUENCE_DETECT) != 0)
  970. {
  971. ChecksumBody->GssFlags |= GSS_C_SEQUENCE_FLAG;
  972. }
  973. if ((*ContextFlags & ISC_RET_REPLAY_DETECT) != 0)
  974. {
  975. ChecksumBody->GssFlags |= GSS_C_REPLAY_FLAG;
  976. }
  977. if ((*ContextFlags & ISC_RET_CONFIDENTIALITY) != 0)
  978. {
  979. ChecksumBody->GssFlags |= GSS_C_CONF_FLAG;
  980. }
  981. if ((*ContextFlags & ISC_RET_INTEGRITY) != 0)
  982. {
  983. ChecksumBody->GssFlags |= GSS_C_INTEG_FLAG;
  984. }
  985. if ((*ContextFlags & ISC_RET_IDENTIFY) != 0)
  986. {
  987. ChecksumBody->GssFlags |= GSS_C_IDENTIFY_FLAG;
  988. }
  989. if ((*ContextFlags & ISC_RET_EXTENDED_ERROR) != 0)
  990. {
  991. ChecksumBody->GssFlags |= GSS_C_EXTENDED_ERROR_FLAG;
  992. }
  993. if ((*ContextFlags & ISC_RET_DELEGATE) != 0)
  994. {
  995. ChecksumBody->GssFlags |= GSS_C_DELEG_FLAG;
  996. ChecksumBody->Delegation = 1;
  997. ChecksumBody->DelegationLength = (USHORT) KerbCredSize;
  998. RtlCopyMemory(
  999. ChecksumBody->DelegationInfo,
  1000. KerbCred,
  1001. KerbCredSize
  1002. );
  1003. }
  1004. ChecksumBody->BindLength = 0x10;
  1005. //
  1006. // (viz. Windows Bugs 94818)
  1007. // If channel bindings are absent, BindHash should be {0,0,0,0}
  1008. //
  1009. if( pChannelBindings == NULL )
  1010. {
  1011. RtlZeroMemory( ChecksumBody->BindHash, ChecksumBody->BindLength );
  1012. }
  1013. else
  1014. {
  1015. Status = KerbComputeGssBindHash( pChannelBindings, (PUCHAR)BindHash );
  1016. if( !NT_SUCCESS(Status) )
  1017. {
  1018. goto Cleanup;
  1019. }
  1020. RtlCopyMemory( ChecksumBody->BindHash, BindHash, ChecksumBody->BindLength );
  1021. }
  1022. GssChecksum->checksum_type = GSS_CHECKSUM_TYPE;
  1023. GssChecksum->checksum.length = ChecksumSize;
  1024. GssChecksum->checksum.value = (PUCHAR) ChecksumBody;
  1025. ChecksumBody = NULL;
  1026. Cleanup:
  1027. if (!NT_SUCCESS(Status))
  1028. {
  1029. if (ChecksumBody != NULL)
  1030. {
  1031. KerbFree(ChecksumBody);
  1032. }
  1033. }
  1034. if (TicketGrantingTicket != NULL)
  1035. {
  1036. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  1037. }
  1038. if (KerbCred != NULL)
  1039. {
  1040. MIDL_user_free(KerbCred);
  1041. }
  1042. return(Status);
  1043. }
  1044. //+-------------------------------------------------------------------------
  1045. //
  1046. // Function: KerbBuildApRequest
  1047. //
  1048. // Synopsis: Builds an AP request message from a logon session and a
  1049. // ticket cache entry.
  1050. //
  1051. // Effects:
  1052. //
  1053. // Arguments: LogonSession - Logon session used to build this AP request
  1054. // Credential - Optional credential for use with supplemental credentials
  1055. // TicketCacheEntry - Ticket with which to build the AP request
  1056. // ErrorMessage - Optionally contains error message from last AP request
  1057. // ContextFlags - Flags passed in by client indicating
  1058. // authentication requirements. If the flags can't
  1059. // be supported they will be turned off.
  1060. // MarshalledApReqest - Receives a marshalled AP request allocated
  1061. // with KerbAllocate
  1062. // ApRequestSize - Length of the AP reques structure in bytes
  1063. // Nonce - Nonce used for this request. if non-zero, then the
  1064. // nonce supplied by the caller will be used.
  1065. // SubSessionKey - if generated, returns a sub-session key in AP request
  1066. // pAuthenticatorTime - timestamp placed on the AP request
  1067. //
  1068. // Requires:
  1069. //
  1070. // Returns:
  1071. //
  1072. // Notes:
  1073. //
  1074. //
  1075. //--------------------------------------------------------------------------
  1076. NTSTATUS
  1077. KerbBuildApRequest(
  1078. IN PKERB_LOGON_SESSION LogonSession,
  1079. IN OPTIONAL PKERB_CREDENTIAL Credential,
  1080. IN OPTIONAL PKERB_CREDMAN_CRED CredManCredentials,
  1081. IN PKERB_TICKET_CACHE_ENTRY TicketCacheEntry,
  1082. IN OPTIONAL PKERB_ERROR ErrorMessage,
  1083. IN ULONG ContextAttributes,
  1084. IN OUT PULONG ContextFlags,
  1085. OUT PUCHAR * MarshalledApRequest,
  1086. OUT PULONG ApRequestSize,
  1087. OUT PULONG Nonce,
  1088. OUT OPTIONAL PTimeStamp pAuthenticatorTime,
  1089. IN OUT PKERB_ENCRYPTION_KEY SubSessionKey,
  1090. IN PSEC_CHANNEL_BINDINGS pChannelBindings
  1091. )
  1092. {
  1093. NTSTATUS Status;
  1094. ULONG ApOptions = 0;
  1095. KERBERR KerbErr;
  1096. KERB_CHECKSUM GssChecksum = {0};
  1097. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials = NULL;
  1098. TimeStamp ServerTime;
  1099. ULONG RequestSize;
  1100. PUCHAR RequestWithHeader = NULL;
  1101. PUCHAR RequestStart;
  1102. gss_OID MechId;
  1103. BOOLEAN LogonSessionLocked = FALSE;
  1104. *ApRequestSize = 0;
  1105. *MarshalledApRequest = NULL;
  1106. //
  1107. // If we have an error message, use it to compute a skew time to adjust
  1108. // local time by
  1109. //
  1110. if (ARGUMENT_PRESENT(ErrorMessage))
  1111. {
  1112. TimeStamp CurrentTime;
  1113. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime);
  1114. KerbWriteLockTicketCache();
  1115. //
  1116. // Update the skew cache the first time we fail to a server
  1117. //
  1118. if ((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_SKEW)
  1119. {
  1120. if (KerbGetTime(TicketCacheEntry->TimeSkew) == 0)
  1121. {
  1122. KerbUpdateSkewTime(TRUE);
  1123. }
  1124. }
  1125. KerbConvertGeneralizedTimeToLargeInt(
  1126. &ServerTime,
  1127. &ErrorMessage->server_time,
  1128. ErrorMessage->server_usec
  1129. );
  1130. KerbSetTime(&TicketCacheEntry->TimeSkew, KerbGetTime(ServerTime) - KerbGetTime(CurrentTime));
  1131. KerbUnlockTicketCache();
  1132. }
  1133. //
  1134. // Allocate a nonce if we don't have one already.
  1135. //
  1136. if (*Nonce == 0)
  1137. {
  1138. *Nonce = KerbAllocateNonce();
  1139. }
  1140. D_DebugLog((DEB_TRACE,"BuildApRequest using nonce 0x%x\n",*Nonce));
  1141. DsysAssert( !LogonSessionLocked );
  1142. KerbReadLockLogonSessions(LogonSession);
  1143. LogonSessionLocked = TRUE;
  1144. if (ARGUMENT_PRESENT(Credential))
  1145. {
  1146. if (Credential->SuppliedCredentials != NULL)
  1147. {
  1148. PrimaryCredentials = Credential->SuppliedCredentials;
  1149. }
  1150. }
  1151. // use cred manager creds if present
  1152. if (ARGUMENT_PRESENT(CredManCredentials))
  1153. {
  1154. PrimaryCredentials = CredManCredentials->SuppliedCredentials;
  1155. }
  1156. if (PrimaryCredentials == NULL)
  1157. {
  1158. PrimaryCredentials = &LogonSession->PrimaryCredentials;
  1159. }
  1160. //
  1161. // get the GSS checksum
  1162. //
  1163. Status = KerbBuildGssChecksum(
  1164. LogonSession,
  1165. PrimaryCredentials,
  1166. TicketCacheEntry,
  1167. ContextFlags,
  1168. &GssChecksum,
  1169. &ApOptions,
  1170. pChannelBindings
  1171. );
  1172. if (!NT_SUCCESS(Status))
  1173. {
  1174. D_DebugLog((DEB_ERROR,"Failed to build GSS checksum: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  1175. goto Cleanup;
  1176. }
  1177. //
  1178. // If are an export client, create a subsession key. For datagram,
  1179. // the caller passes in a pre-generated session key.
  1180. //
  1181. //
  1182. // For datagram, they subsession key has already been set so we don't need
  1183. // to create one here.
  1184. //
  1185. if ((((*ContextFlags & ISC_RET_DATAGRAM) == 0)) ||
  1186. (((*ContextFlags & (ISC_RET_USED_DCE_STYLE | ISC_RET_MUTUAL_AUTH)) == 0) && !KerbGlobalUseStrongEncryptionForDatagram))
  1187. {
  1188. KERBERR KerbErr;
  1189. //
  1190. // First free the Subsession key, if there was one.
  1191. //
  1192. KerbFreeKey(SubSessionKey);
  1193. KerbErr = KerbMakeKey(
  1194. TicketCacheEntry->SessionKey.keytype,
  1195. SubSessionKey
  1196. );
  1197. if (!KERB_SUCCESS(KerbErr))
  1198. {
  1199. Status = KerbMapKerbError(KerbErr);
  1200. goto Cleanup;
  1201. }
  1202. }
  1203. //
  1204. // Create the AP request
  1205. //
  1206. KerbReadLockTicketCache();
  1207. //
  1208. // Bug 464930: the KDC could generate a NULL client name,
  1209. // but doing so is clearly wrong
  1210. //
  1211. if ( TicketCacheEntry->ClientName == NULL ) {
  1212. Status = STATUS_INVALID_PARAMETER;
  1213. KerbUnlockTicketCache();
  1214. goto Cleanup;
  1215. }
  1216. KerbErr = KerbCreateApRequest(
  1217. TicketCacheEntry->ClientName,
  1218. &TicketCacheEntry->ClientDomainName,
  1219. &TicketCacheEntry->SessionKey,
  1220. (SubSessionKey->keyvalue.value != NULL) ? SubSessionKey : NULL,
  1221. *Nonce,
  1222. pAuthenticatorTime,
  1223. &TicketCacheEntry->Ticket,
  1224. ApOptions,
  1225. &GssChecksum,
  1226. &TicketCacheEntry->TimeSkew,
  1227. FALSE, // not a KDC request
  1228. ApRequestSize,
  1229. MarshalledApRequest
  1230. );
  1231. KerbUnlockTicketCache();
  1232. DsysAssert( LogonSessionLocked );
  1233. KerbUnlockLogonSessions(LogonSession);
  1234. LogonSessionLocked = FALSE;
  1235. if (!KERB_SUCCESS(KerbErr))
  1236. {
  1237. D_DebugLog((DEB_ERROR,"Failed to create AP request: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
  1238. Status = STATUS_INSUFFICIENT_RESOURCES;
  1239. goto Cleanup;
  1240. }
  1241. //
  1242. // If we aren't doing DCE style, add in the GSS token headers now
  1243. //
  1244. if ((*ContextFlags & ISC_RET_USED_DCE_STYLE) != 0)
  1245. {
  1246. goto Cleanup;
  1247. }
  1248. //
  1249. // Pick the correct OID
  1250. //
  1251. if ((ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0)
  1252. {
  1253. MechId = gss_mech_krb5_u2u;
  1254. }
  1255. else
  1256. {
  1257. MechId = gss_mech_krb5_new;
  1258. }
  1259. RequestSize = g_token_size(MechId, *ApRequestSize);
  1260. RequestWithHeader = (PUCHAR) KerbAllocate(RequestSize);
  1261. if (RequestWithHeader == NULL)
  1262. {
  1263. Status = STATUS_INSUFFICIENT_RESOURCES;
  1264. goto Cleanup;
  1265. }
  1266. //
  1267. // the g_make_token_header will reset this to point to the end of the
  1268. // header
  1269. //
  1270. RequestStart = RequestWithHeader;
  1271. g_make_token_header(
  1272. MechId,
  1273. *ApRequestSize,
  1274. &RequestStart,
  1275. KG_TOK_CTX_AP_REQ
  1276. );
  1277. DsysAssert(RequestStart - RequestWithHeader + *ApRequestSize == RequestSize);
  1278. RtlCopyMemory(
  1279. RequestStart,
  1280. *MarshalledApRequest,
  1281. *ApRequestSize
  1282. );
  1283. KerbFree(*MarshalledApRequest);
  1284. *MarshalledApRequest = RequestWithHeader;
  1285. *ApRequestSize = RequestSize;
  1286. RequestWithHeader = NULL;
  1287. Cleanup:
  1288. if (LogonSessionLocked)
  1289. {
  1290. KerbUnlockLogonSessions(LogonSession);
  1291. }
  1292. if (GssChecksum.checksum.value != NULL)
  1293. {
  1294. KerbFree(GssChecksum.checksum.value);
  1295. }
  1296. if (!NT_SUCCESS(Status) && (*MarshalledApRequest != NULL))
  1297. {
  1298. KerbFree(*MarshalledApRequest);
  1299. *MarshalledApRequest = NULL;
  1300. }
  1301. return(Status);
  1302. }
  1303. //+-------------------------------------------------------------------------
  1304. //
  1305. // Function: KerbBuildNullSessionApRequest
  1306. //
  1307. // Synopsis: builds an AP request for a null session
  1308. //
  1309. // Effects:
  1310. //
  1311. // Arguments:
  1312. //
  1313. // Requires:
  1314. //
  1315. // Returns:
  1316. //
  1317. // Notes:
  1318. //
  1319. //
  1320. //--------------------------------------------------------------------------
  1321. NTSTATUS
  1322. KerbBuildNullSessionApRequest(
  1323. OUT PUCHAR * MarshalledApRequest,
  1324. OUT PULONG ApRequestSize
  1325. )
  1326. {
  1327. NTSTATUS Status = STATUS_SUCCESS;
  1328. KERBERR KerbErr;
  1329. KERB_AP_REQUEST ApRequest;
  1330. UNICODE_STRING NullString = CONSTANT_UNICODE_STRING(L"");
  1331. UCHAR TempBuffer[1];
  1332. ULONG RequestSize;
  1333. PUCHAR RequestWithHeader = NULL;
  1334. PUCHAR RequestStart;
  1335. RtlZeroMemory(
  1336. &ApRequest,
  1337. sizeof(KERB_AP_REQUEST)
  1338. );
  1339. TempBuffer[0] = '\0';
  1340. //
  1341. // Fill in the AP request structure.
  1342. //
  1343. ApRequest.version = KERBEROS_VERSION;
  1344. ApRequest.message_type = KRB_AP_REQ;
  1345. //
  1346. // Fill in mandatory fields - ASN1/OSS requires this
  1347. //
  1348. if (!KERB_SUCCESS(KerbConvertStringToPrincipalName(
  1349. &ApRequest.ticket.server_name,
  1350. &NullString,
  1351. KRB_NT_UNKNOWN
  1352. )))
  1353. {
  1354. Status = STATUS_INSUFFICIENT_RESOURCES;
  1355. goto Cleanup;
  1356. }
  1357. if (!KERB_SUCCESS(KerbConvertUnicodeStringToRealm(
  1358. &ApRequest.ticket.realm,
  1359. &NullString
  1360. )))
  1361. {
  1362. Status = STATUS_INSUFFICIENT_RESOURCES;
  1363. goto Cleanup;
  1364. }
  1365. ApRequest.ticket.encrypted_part.cipher_text.length = 1;
  1366. ApRequest.ticket.encrypted_part.cipher_text.value = TempBuffer;
  1367. ApRequest.authenticator.cipher_text.length = 1;
  1368. ApRequest.authenticator.cipher_text.value = TempBuffer;
  1369. //
  1370. // Now marshall the request
  1371. //
  1372. KerbErr = KerbPackApRequest(
  1373. &ApRequest,
  1374. ApRequestSize,
  1375. MarshalledApRequest
  1376. );
  1377. if (!KERB_SUCCESS(KerbErr))
  1378. {
  1379. D_DebugLog((DEB_ERROR,"Failed to pack AP request: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
  1380. Status = STATUS_INSUFFICIENT_RESOURCES;
  1381. goto Cleanup;
  1382. }
  1383. //
  1384. // Since we never do null sessions with user-to-user, we don't have
  1385. // to worry about which mech id to use
  1386. //
  1387. RequestSize = g_token_size((gss_OID) gss_mech_krb5_new, *ApRequestSize);
  1388. RequestWithHeader = (PUCHAR) KerbAllocate(RequestSize);
  1389. if (RequestWithHeader == NULL)
  1390. {
  1391. Status = STATUS_INSUFFICIENT_RESOURCES;
  1392. goto Cleanup;
  1393. }
  1394. //
  1395. // the g_make_token_header will reset this to point to the end of the
  1396. // header
  1397. //
  1398. RequestStart = RequestWithHeader;
  1399. g_make_token_header(
  1400. (gss_OID) gss_mech_krb5_new,
  1401. *ApRequestSize,
  1402. &RequestStart,
  1403. KG_TOK_CTX_AP_REQ
  1404. );
  1405. DsysAssert(RequestStart - RequestWithHeader + *ApRequestSize == RequestSize);
  1406. RtlCopyMemory(
  1407. RequestStart,
  1408. *MarshalledApRequest,
  1409. *ApRequestSize
  1410. );
  1411. KerbFree(*MarshalledApRequest);
  1412. *MarshalledApRequest = RequestWithHeader;
  1413. *ApRequestSize = RequestSize;
  1414. RequestWithHeader = NULL;
  1415. Cleanup:
  1416. if (!NT_SUCCESS(Status))
  1417. {
  1418. if (*MarshalledApRequest != NULL)
  1419. {
  1420. MIDL_user_free(*MarshalledApRequest);
  1421. *MarshalledApRequest = NULL;
  1422. }
  1423. }
  1424. KerbFreeRealm(&ApRequest.ticket.realm);
  1425. KerbFreePrincipalName(&ApRequest.ticket.server_name);
  1426. return(Status);
  1427. }
  1428. //+-------------------------------------------------------------------------
  1429. //
  1430. // Function: KerbMakeSocketCall
  1431. //
  1432. // Synopsis: Contains logic for sending a message to a KDC in the
  1433. // specified realm on a specified port
  1434. //
  1435. // Effects:
  1436. //
  1437. // Arguments:
  1438. //
  1439. // Requires:
  1440. //
  1441. // Returns:
  1442. //
  1443. // Notes:
  1444. //
  1445. //
  1446. //--------------------------------------------------------------------------
  1447. NTSTATUS
  1448. KerbMakeSocketCall(
  1449. IN PUNICODE_STRING RealmName,
  1450. IN OPTIONAL PUNICODE_STRING AccountName,
  1451. IN BOOLEAN CallPDC,
  1452. IN BOOLEAN UseTcp,
  1453. IN BOOLEAN CallKpasswd,
  1454. IN PKERB_MESSAGE_BUFFER RequestMessage,
  1455. IN PKERB_MESSAGE_BUFFER ReplyMessage,
  1456. IN OPTIONAL PKERB_BINDING_CACHE_ENTRY OptionalBindingHandle,
  1457. IN ULONG AdditionalFlags,
  1458. OUT PBOOLEAN CalledPDC
  1459. )
  1460. {
  1461. NTSTATUS Status = STATUS_SUCCESS;
  1462. KERBERR KerbErr = KDC_ERR_NONE;
  1463. ULONG Retries;
  1464. PKERB_BINDING_CACHE_ENTRY BindingHandle = NULL;
  1465. ULONG DesiredFlags;
  1466. ULONG Timeout = KerbGlobalKdcCallTimeout;
  1467. //
  1468. // If the caller wants a PDC, so be it
  1469. //
  1470. *CalledPDC = FALSE;
  1471. if (CallPDC)
  1472. {
  1473. DesiredFlags = DS_PDC_REQUIRED;
  1474. }
  1475. else
  1476. {
  1477. DesiredFlags = 0;
  1478. }
  1479. //
  1480. // Now actually get the ticket. We will retry twice.
  1481. //
  1482. if ((AdditionalFlags & DS_FORCE_REDISCOVERY) != 0)
  1483. {
  1484. DesiredFlags |= DS_FORCE_REDISCOVERY;
  1485. D_DebugLog((DEB_TRACE,"KerbMakeSocketCall() caller wants rediscovery!\n"));
  1486. }
  1487. Retries = 0;
  1488. do
  1489. {
  1490. //
  1491. // don't force retry the first time
  1492. //
  1493. if (Retries > 0)
  1494. {
  1495. DesiredFlags |= DS_FORCE_REDISCOVERY;
  1496. Timeout += KerbGlobalKdcCallBackoff;
  1497. }
  1498. // Use ADSI supplied info, then retry using cached version
  1499. if (ARGUMENT_PRESENT(OptionalBindingHandle) && (Retries == 0))
  1500. {
  1501. BindingHandle = OptionalBindingHandle;
  1502. }
  1503. else
  1504. {
  1505. Status = KerbGetKdcBinding(
  1506. RealmName,
  1507. AccountName,
  1508. DesiredFlags,
  1509. CallKpasswd,
  1510. UseTcp,
  1511. &BindingHandle
  1512. );
  1513. }
  1514. if (!NT_SUCCESS(Status))
  1515. {
  1516. D_DebugLog((DEB_WARN,"Failed to get KDC binding for %wZ: 0x%x\n",
  1517. RealmName, Status));
  1518. goto Cleanup;
  1519. }
  1520. //
  1521. // If the KDC doesn't support TCP, don't use TCP. Otherwise
  1522. // use it if the sending buffer is too big, or if it was already
  1523. // set.
  1524. //
  1525. if ((BindingHandle->CacheFlags & KERB_BINDING_NO_TCP) != 0)
  1526. {
  1527. UseTcp = FALSE;
  1528. }
  1529. else
  1530. {
  1531. if (RequestMessage->BufferSize > KerbGlobalMaxDatagramSize)
  1532. {
  1533. UseTcp = TRUE;
  1534. }
  1535. }
  1536. //
  1537. // Lock the binding while we make the call
  1538. //
  1539. if (!*CalledPDC)
  1540. {
  1541. *CalledPDC = (BindingHandle->Flags & DS_PDC_REQUIRED) ? (BOOLEAN) TRUE : (BOOLEAN) FALSE;
  1542. }
  1543. #ifndef WIN32_CHICAGO
  1544. if ((BindingHandle->CacheFlags & KERB_BINDING_LOCAL) != 0)
  1545. {
  1546. NTSTATUS TokenStatus;
  1547. HANDLE ImpersonationToken = NULL;
  1548. // Are we impersonating?
  1549. TokenStatus = NtOpenThreadToken(
  1550. NtCurrentThread(),
  1551. TOKEN_QUERY | TOKEN_IMPERSONATE,
  1552. TRUE,
  1553. &ImpersonationToken
  1554. );
  1555. if( NT_SUCCESS(TokenStatus) )
  1556. {
  1557. //
  1558. // stop impersonating.
  1559. //
  1560. RevertToSelf();
  1561. }
  1562. KERB_MESSAGE_BUFFER KdcReplyMessage = {0};
  1563. if (!CallKpasswd)
  1564. {
  1565. D_DebugLog((DEB_TRACE,"Calling kdc directly\n"));
  1566. DsysAssert(KerbKdcGetTicket != NULL);
  1567. KerbErr = (*KerbKdcGetTicket)(
  1568. NULL, // no context,
  1569. NULL, // no client address
  1570. NULL, // no server address
  1571. RequestMessage,
  1572. &KdcReplyMessage
  1573. );
  1574. }
  1575. else
  1576. {
  1577. DsysAssert(KerbKdcChangePassword != NULL);
  1578. KerbErr = (*KerbKdcChangePassword)(
  1579. NULL, // no context,
  1580. NULL, // no client address
  1581. NULL, // no server address
  1582. RequestMessage,
  1583. &KdcReplyMessage
  1584. );
  1585. }
  1586. if( ImpersonationToken != NULL ) {
  1587. //
  1588. // put the thread token back if we were impersonating.
  1589. //
  1590. SetThreadToken( NULL, ImpersonationToken );
  1591. NtClose( ImpersonationToken );
  1592. }
  1593. if (KerbErr != KDC_ERR_NOT_RUNNING)
  1594. {
  1595. //
  1596. // Copy the data so it can be freed with MIDL_user_free.
  1597. //
  1598. if ((0 != KdcReplyMessage.BufferSize) && (NULL != KdcReplyMessage.Buffer))
  1599. {
  1600. ReplyMessage->BufferSize = KdcReplyMessage.BufferSize;
  1601. ReplyMessage->Buffer = (PUCHAR) MIDL_user_allocate(
  1602. KdcReplyMessage.BufferSize);
  1603. if (ReplyMessage->Buffer != NULL)
  1604. {
  1605. RtlCopyMemory(
  1606. ReplyMessage->Buffer,
  1607. KdcReplyMessage.Buffer,
  1608. KdcReplyMessage.BufferSize
  1609. );
  1610. }
  1611. else
  1612. {
  1613. Status = STATUS_INSUFFICIENT_RESOURCES;
  1614. }
  1615. (*KerbKdcFreeMemory)(KdcReplyMessage.Buffer);
  1616. }
  1617. else
  1618. {
  1619. DebugLog((DEB_ERROR, "KerbMakeSocketCall direct kdc call returned %#x and received no error message KdcReplyMessage.BufferSize %#x KdcReplyMessage.Buffer %p\n",
  1620. KerbErr, KdcReplyMessage.BufferSize, KdcReplyMessage.Buffer));
  1621. Status = STATUS_INSUFFICIENT_RESOURCES; // no message returned from KDC
  1622. }
  1623. goto Cleanup;
  1624. }
  1625. else
  1626. {
  1627. //
  1628. // The KDC said it wasn't running.
  1629. //
  1630. KerbKdcStarted = FALSE;
  1631. Status = STATUS_NETLOGON_NOT_STARTED;
  1632. //
  1633. // Get rid of the binding handle so we don't use it again.
  1634. // Don't whack supplied optional binding handle though
  1635. //
  1636. if (BindingHandle != OptionalBindingHandle)
  1637. {
  1638. KerbRemoveBindingCacheEntry( BindingHandle );
  1639. }
  1640. }
  1641. }
  1642. else
  1643. #endif // WIN32_CHICAGO
  1644. {
  1645. DebugLog((DEB_TRACE_BND_CACHE, "Calling kdc %wZ for realm %S\n", &BindingHandle->KdcAddress, RealmName->Buffer));
  1646. Status = KerbCallKdc(
  1647. &BindingHandle->KdcAddress,
  1648. BindingHandle->AddressType,
  1649. Timeout,
  1650. !UseTcp,
  1651. CallKpasswd ? (USHORT) KERB_KPASSWD_PORT : (USHORT) KERB_KDC_PORT,
  1652. RequestMessage,
  1653. ReplyMessage
  1654. );
  1655. if (!NT_SUCCESS(Status) )
  1656. {
  1657. //
  1658. // If the request used UDP and we got an invalid buffer size error,
  1659. // try again with TCP.
  1660. //
  1661. if ((Status == STATUS_INVALID_BUFFER_SIZE) && (!UseTcp)) {
  1662. if ((BindingHandle->CacheFlags & KERB_BINDING_NO_TCP) == 0)
  1663. {
  1664. UseTcp = TRUE;
  1665. Status = KerbCallKdc(
  1666. &BindingHandle->KdcAddress,
  1667. BindingHandle->AddressType,
  1668. Timeout,
  1669. !UseTcp,
  1670. CallKpasswd ? (USHORT) KERB_KPASSWD_PORT : (USHORT) KERB_KDC_PORT,
  1671. RequestMessage,
  1672. ReplyMessage
  1673. );
  1674. }
  1675. }
  1676. if (!NT_SUCCESS(Status))
  1677. {
  1678. DebugLog((DEB_ERROR, "Failed to get make call to KDC %wZ: 0x%x. %ws, line %d\n",
  1679. &BindingHandle->KdcAddress, Status, THIS_FILE, __LINE__));
  1680. //
  1681. // The call itself failed, so the binding handle was bad.
  1682. // Free it now, unless supplied as optional binding handle.
  1683. //
  1684. if (BindingHandle != OptionalBindingHandle)
  1685. {
  1686. KerbRemoveBindingCacheEntry( BindingHandle );
  1687. }
  1688. }
  1689. }
  1690. }
  1691. if (BindingHandle != OptionalBindingHandle)
  1692. {
  1693. KerbDereferenceBindingCacheEntry( BindingHandle );
  1694. Retries++;
  1695. }
  1696. BindingHandle = NULL;
  1697. } while ( !NT_SUCCESS(Status) && (Retries < KerbGlobalKdcSendRetries) );
  1698. Cleanup:
  1699. //
  1700. // monitor UDP transmission quality.
  1701. //
  1702. if (!UseTcp)
  1703. {
  1704. if ( !NT_SUCCESS(Status) &&
  1705. ( Retries == KerbGlobalKdcSendRetries ))
  1706. {
  1707. DebugLog((DEB_ERROR, "We look to be timing out on UDP \n"));
  1708. KerbReportTransportError(Status);
  1709. }
  1710. else if (NT_SUCCESS(Status) && ( Retries < KerbGlobalKdcSendRetries ))
  1711. {
  1712. KerbResetTransportCounter();
  1713. }
  1714. }
  1715. if ((BindingHandle != NULL) && (BindingHandle != OptionalBindingHandle))
  1716. {
  1717. KerbDereferenceBindingCacheEntry(BindingHandle);
  1718. }
  1719. return(Status);
  1720. }
  1721. //+-------------------------------------------------------------------------
  1722. //
  1723. // Function: KerbMakeKdcCall
  1724. //
  1725. // Synopsis: Contains logic for calling a KDC including binding and
  1726. // retrying.
  1727. //
  1728. // Effects:
  1729. //
  1730. // Arguments:
  1731. //
  1732. // Requires:
  1733. //
  1734. // Returns:
  1735. //
  1736. // Notes:
  1737. //
  1738. //
  1739. //--------------------------------------------------------------------------
  1740. NTSTATUS
  1741. KerbMakeKdcCall(
  1742. IN PUNICODE_STRING RealmName,
  1743. IN OPTIONAL PUNICODE_STRING AccountName,
  1744. IN BOOLEAN CallPDC,
  1745. IN BOOLEAN UseTcp,
  1746. IN PKERB_MESSAGE_BUFFER RequestMessage,
  1747. IN PKERB_MESSAGE_BUFFER ReplyMessage,
  1748. IN ULONG AdditionalFlags,
  1749. OUT PBOOLEAN CalledPDC
  1750. )
  1751. {
  1752. if (ARGUMENT_PRESENT(AccountName))
  1753. {
  1754. D_DebugLog((DEB_ERROR, "[trace info] Making DsGetDcName w/ account name\n"));
  1755. }
  1756. return(KerbMakeSocketCall(
  1757. RealmName,
  1758. AccountName,
  1759. CallPDC,
  1760. UseTcp,
  1761. FALSE, // don't call Kpasswd
  1762. RequestMessage,
  1763. ReplyMessage,
  1764. NULL, // optional binding cache handle, for kpasswd only
  1765. AdditionalFlags,
  1766. CalledPDC
  1767. ));
  1768. }
  1769. //+-------------------------------------------------------------------------
  1770. //
  1771. // Function: KerbComputeTgsChecksum
  1772. //
  1773. // Synopsis: computes the checksum of a TGS request body by marshalling
  1774. // the request and the checksumming it.
  1775. //
  1776. // Effects: Allocates destination with KerbAllocate().
  1777. //
  1778. // Arguments:
  1779. //
  1780. // Requires:
  1781. //
  1782. // Returns:
  1783. //
  1784. // Notes:
  1785. //
  1786. //
  1787. //--------------------------------------------------------------------------
  1788. NTSTATUS
  1789. KerbComputeTgsChecksum(
  1790. IN PKERB_KDC_REQUEST_BODY RequestBody,
  1791. IN PKERB_ENCRYPTION_KEY Key,
  1792. IN ULONG ChecksumType,
  1793. OUT PKERB_CHECKSUM Checksum
  1794. )
  1795. {
  1796. PCHECKSUM_FUNCTION ChecksumFunction;
  1797. PCHECKSUM_BUFFER CheckBuffer = NULL;
  1798. KERB_MESSAGE_BUFFER MarshalledRequestBody = {0, NULL};
  1799. NTSTATUS Status;
  1800. RtlZeroMemory(
  1801. Checksum,
  1802. sizeof(KERB_CHECKSUM)
  1803. );
  1804. Status = CDLocateCheckSum(
  1805. ChecksumType,
  1806. &ChecksumFunction
  1807. );
  1808. if (!NT_SUCCESS(Status))
  1809. {
  1810. goto Cleanup;
  1811. }
  1812. //
  1813. // Allocate enough space for the checksum
  1814. //
  1815. Checksum->checksum.value = (PUCHAR) KerbAllocate(ChecksumFunction->CheckSumSize);
  1816. if (Checksum->checksum.value == NULL)
  1817. {
  1818. Status = STATUS_INSUFFICIENT_RESOURCES;
  1819. goto Cleanup;
  1820. }
  1821. Checksum->checksum.length = ChecksumFunction->CheckSumSize;
  1822. Checksum->checksum_type = (int) ChecksumType;
  1823. // don't av here.
  1824. if ((ChecksumType == KERB_CHECKSUM_REAL_CRC32) ||
  1825. (ChecksumType == KERB_CHECKSUM_CRC32))
  1826. {
  1827. if (ChecksumFunction->Initialize)
  1828. {
  1829. Status = ChecksumFunction->Initialize(
  1830. 0,
  1831. &CheckBuffer
  1832. );
  1833. }
  1834. else
  1835. {
  1836. Status = STATUS_CRYPTO_SYSTEM_INVALID;
  1837. }
  1838. }
  1839. else
  1840. {
  1841. if (ChecksumFunction->InitializeEx2)
  1842. {
  1843. Status = ChecksumFunction->InitializeEx2(
  1844. Key->keyvalue.value,
  1845. (ULONG) Key->keyvalue.length,
  1846. NULL,
  1847. KERB_TGS_REQ_AP_REQ_AUTH_CKSUM_SALT,
  1848. &CheckBuffer
  1849. );
  1850. }
  1851. else if (ChecksumFunction->InitializeEx)
  1852. {
  1853. Status = ChecksumFunction->InitializeEx(
  1854. Key->keyvalue.value,
  1855. (ULONG) Key->keyvalue.length,
  1856. KERB_TGS_REQ_AP_REQ_AUTH_CKSUM_SALT,
  1857. &CheckBuffer
  1858. );
  1859. }
  1860. else
  1861. {
  1862. Status = STATUS_CRYPTO_SYSTEM_INVALID;
  1863. }
  1864. }
  1865. if (!NT_SUCCESS(Status))
  1866. {
  1867. goto Cleanup;
  1868. }
  1869. if (!KERB_SUCCESS(KerbPackData(
  1870. RequestBody,
  1871. KERB_MARSHALLED_REQUEST_BODY_PDU,
  1872. &MarshalledRequestBody.BufferSize,
  1873. &MarshalledRequestBody.Buffer
  1874. )))
  1875. {
  1876. Status = STATUS_INSUFFICIENT_RESOURCES;
  1877. goto Cleanup;
  1878. }
  1879. //
  1880. // Now checksum the buffer
  1881. //
  1882. ChecksumFunction->Sum(
  1883. CheckBuffer,
  1884. MarshalledRequestBody.BufferSize,
  1885. MarshalledRequestBody.Buffer
  1886. );
  1887. ChecksumFunction->Finalize(
  1888. CheckBuffer,
  1889. Checksum->checksum.value
  1890. );
  1891. Cleanup:
  1892. if (CheckBuffer != NULL)
  1893. {
  1894. ChecksumFunction->Finish(&CheckBuffer);
  1895. }
  1896. if (MarshalledRequestBody.Buffer != NULL)
  1897. {
  1898. MIDL_user_free(MarshalledRequestBody.Buffer);
  1899. }
  1900. if (!NT_SUCCESS(Status) && (Checksum->checksum.value != NULL))
  1901. {
  1902. MIDL_user_free(Checksum->checksum.value);
  1903. Checksum->checksum.value = NULL;
  1904. }
  1905. return(Status);
  1906. }
  1907. //+-------------------------------------------------------------------------
  1908. //
  1909. // Function: KerbGetTgsTicket
  1910. //
  1911. // Synopsis: Gets a ticket to the specified target with the specified
  1912. // options.
  1913. //
  1914. // Effects:
  1915. //
  1916. // Arguments: ClientRealm
  1917. // TicketGrantingTicket - TGT to use for the TGS request
  1918. // TargetName - Name of the target for which to obtain a ticket.
  1919. // TicketOptions - Optionally contains requested KDC options flags
  1920. // Flags
  1921. // TicketOptions
  1922. // EncryptionType - Optionally contains requested encryption type
  1923. // AuthorizationData - Optional authorization data to stick
  1924. // in the ticket.
  1925. // KdcReply - the ticket to be used for getting a ticket with
  1926. // the enc_tkt_in_skey flag.
  1927. // ReplyBody - Receives the kdc reply.
  1928. // pRetryFlags
  1929. //
  1930. // Requires:
  1931. //
  1932. // Returns: Kerberos errors and NT errors
  1933. //
  1934. // Notes:
  1935. //
  1936. //
  1937. //--------------------------------------------------------------------------
  1938. NTSTATUS
  1939. KerbGetTgsTicket(
  1940. IN PUNICODE_STRING ClientRealm,
  1941. IN PKERB_TICKET_CACHE_ENTRY TicketGrantingTicket,
  1942. IN PKERB_INTERNAL_NAME TargetName,
  1943. IN ULONG Flags,
  1944. IN OPTIONAL ULONG TicketOptions,
  1945. IN OPTIONAL ULONG EncryptionType,
  1946. IN OPTIONAL PKERB_AUTHORIZATION_DATA AuthorizationData,
  1947. IN OPTIONAL PKERB_PA_DATA_LIST PADataList,
  1948. IN OPTIONAL PKERB_TGT_REPLY TgtReply,
  1949. IN OPTIONAL PKERB_TICKET EvidenceTicket,
  1950. IN OPTIONAL PTimeStamp OptionalEndTime,
  1951. OUT PKERB_KDC_REPLY * KdcReply,
  1952. OUT PKERB_ENCRYPTED_KDC_REPLY * ReplyBody,
  1953. OUT PULONG pRetryFlags
  1954. )
  1955. {
  1956. NTSTATUS Status = STATUS_SUCCESS;
  1957. ULONG Index = 0;
  1958. KERB_KDC_REQUEST TicketRequest;
  1959. PKERB_KDC_REQUEST_BODY RequestBody = &TicketRequest.request_body;
  1960. PULONG CryptVector = NULL;
  1961. ULONG EncryptionTypeCount = 0;
  1962. PKERB_EXT_ERROR pExtendedError = NULL;
  1963. ULONG Nonce;
  1964. TimeStamp AuthenticatorTime = {0};
  1965. KERB_PA_DATA_LIST ApRequest = {0};
  1966. KERBERR KerbErr;
  1967. KERB_MESSAGE_BUFFER RequestMessage = {0, NULL};
  1968. KERB_MESSAGE_BUFFER ReplyMessage = {0, NULL};
  1969. BOOLEAN DoRetryGetTicket = FALSE;
  1970. BOOLEAN RetriedOnce = FALSE;
  1971. UNICODE_STRING TempDomainName = NULL_UNICODE_STRING;
  1972. KERB_CHECKSUM RequestChecksum = {0};
  1973. BOOLEAN CalledPdc;
  1974. KERB_TICKET_LIST TicketList[2];
  1975. ULONG KdcOptions = 0;
  1976. ULONG KdcFlagOptions;
  1977. PKERB_MIT_REALM MitRealm = NULL;
  1978. BOOLEAN UsedAlternateName = FALSE;
  1979. BOOLEAN UseTcp = FALSE;
  1980. BOOLEAN fMitRealmPossibleRetry = FALSE;
  1981. D_DebugLog((DEB_TRACE, "KerbGetTgsTicket Flags %#x, ClientRealm %wZ, Tgt DomainName %wZ, Tgt TargetDomainName %wZ, TgtReply %p, EvidenceTicket %p, TargetName ",
  1982. Flags, ClientRealm, &TicketGrantingTicket->DomainName, &TicketGrantingTicket->TargetDomainName, TgtReply, EvidenceTicket));
  1983. D_KerbPrintKdcName((DEB_TRACE, TargetName));
  1984. BOOLEAN TicketCacheLocked = FALSE;
  1985. KERB_ENCRYPTED_DATA EncAuthData = {0};
  1986. #ifdef RESTRICTED_TOKEN
  1987. if (AuthorizationData != NULL)
  1988. {
  1989. Status = KerbBuildEncryptedAuthData(
  1990. &EncAuthData,
  1991. TicketGrantingTicket,
  1992. AuthorizationData
  1993. );
  1994. if (!NT_SUCCESS(Status))
  1995. {
  1996. D_DebugLog((DEB_ERROR,"Failed to encrypt auth data: 0x%x\n",Status));
  1997. goto Cleanup;
  1998. }
  1999. }
  2000. #endif
  2001. //
  2002. // This is the retry point if we need to retry getting a TGS ticket
  2003. //
  2004. RetryGetTicket:
  2005. RtlZeroMemory(
  2006. &ApRequest,
  2007. sizeof(KERB_PA_DATA_LIST)
  2008. );
  2009. RtlZeroMemory(
  2010. &RequestChecksum,
  2011. sizeof(KERB_CHECKSUM)
  2012. );
  2013. RtlZeroMemory(
  2014. &TicketRequest,
  2015. sizeof(KERB_KDC_REQUEST)
  2016. );
  2017. *KdcReply = NULL;
  2018. *ReplyBody = NULL;
  2019. //
  2020. // Fill in the ticket request with the defaults.
  2021. //
  2022. if (!ARGUMENT_PRESENT( OptionalEndTime ))
  2023. {
  2024. KerbConvertLargeIntToGeneralizedTime(
  2025. &RequestBody->endtime,
  2026. NULL,
  2027. &KerbGlobalWillNeverTime // use HasNeverTime instead
  2028. );
  2029. }
  2030. else
  2031. {
  2032. KerbConvertLargeIntToGeneralizedTime(
  2033. &RequestBody->endtime,
  2034. NULL,
  2035. OptionalEndTime
  2036. );
  2037. }
  2038. KerbConvertLargeIntToGeneralizedTime(
  2039. &RequestBody->KERB_KDC_REQUEST_BODY_renew_until,
  2040. NULL,
  2041. &KerbGlobalHasNeverTime // use HasNeverTime instead
  2042. );
  2043. //
  2044. // If the caller supplied kdc options, use those
  2045. //
  2046. if (TicketOptions != 0)
  2047. {
  2048. KdcOptions = TicketOptions;
  2049. }
  2050. else
  2051. {
  2052. //
  2053. // Some missing (TGT) ticket options will result in a ticket not being
  2054. // granted. Others (such as name_canon.) will be usable by W2k KDCs
  2055. // Make sure we can modify these so we can turn "on" various options
  2056. // later.
  2057. //
  2058. KdcOptions = (KERB_DEFAULT_TICKET_FLAGS &
  2059. TicketGrantingTicket->TicketFlags) |
  2060. KerbGlobalKdcOptions;
  2061. }
  2062. Nonce = KerbAllocateNonce();
  2063. RequestBody->nonce = Nonce;
  2064. if (AuthorizationData != NULL)
  2065. {
  2066. RequestBody->enc_authorization_data = EncAuthData;
  2067. RequestBody->bit_mask |= enc_authorization_data_present;
  2068. }
  2069. //
  2070. // Build crypt vector.
  2071. //
  2072. //
  2073. // First get the count of encryption types
  2074. //
  2075. (VOID) CDBuildIntegrityVect( &EncryptionTypeCount, NULL );
  2076. //
  2077. // Now allocate the crypt vector
  2078. //
  2079. SafeAllocaAllocate(CryptVector, sizeof(ULONG) * EncryptionTypeCount);
  2080. if (CryptVector == NULL)
  2081. {
  2082. Status = STATUS_INSUFFICIENT_RESOURCES;
  2083. goto Cleanup;
  2084. }
  2085. //
  2086. // Now get the list of encrypt types
  2087. //
  2088. (VOID) CDBuildIntegrityVect( &EncryptionTypeCount, CryptVector );
  2089. if (EncryptionTypeCount == 0)
  2090. {
  2091. Status = STATUS_INSUFFICIENT_RESOURCES;
  2092. goto Cleanup;
  2093. }
  2094. //
  2095. // If caller didn't specify a favorite etype, send all that we support
  2096. //
  2097. if (EncryptionType != 0)
  2098. {
  2099. //
  2100. // Swap the first one with the encryption type requested.
  2101. // do this only if the first isn't already what is requested.
  2102. //
  2103. UINT i = 0;
  2104. ULONG FirstOne = CryptVector[0];
  2105. if (CryptVector[i] != EncryptionType)
  2106. {
  2107. CryptVector[i] = EncryptionType;
  2108. for ( i = 1; i < EncryptionTypeCount;i++)
  2109. {
  2110. if (CryptVector[i] == EncryptionType)
  2111. {
  2112. CryptVector[i] = FirstOne;
  2113. break;
  2114. }
  2115. }
  2116. }
  2117. }
  2118. //
  2119. // convert the array to a crypt list in the request
  2120. //
  2121. if (!KERB_SUCCESS(KerbConvertArrayToCryptList(
  2122. &RequestBody->encryption_type,
  2123. CryptVector,
  2124. EncryptionTypeCount,
  2125. FALSE)))
  2126. {
  2127. Status = STATUS_INSUFFICIENT_RESOURCES;
  2128. goto Cleanup;
  2129. }
  2130. //
  2131. // If a TGT reply is present, stick the TGT into the ticket in the
  2132. // additional tickets field and include set the enc_tkt_in_skey option.
  2133. // The ticket that comes back will be encrypted with the session key
  2134. // from the supplied TGT.
  2135. //
  2136. // For S4U, we present the evidence ticket.
  2137. //
  2138. // See gettgs.cxx, UnpackAdditionalTickets() for details.
  2139. //
  2140. Index = 0;
  2141. if (ARGUMENT_PRESENT( TgtReply ))
  2142. {
  2143. ASSERT(Index == 0);
  2144. D_DebugLog((DEB_TRACE_U2U, "KerbGetTgsTicket setting KERB_KDC_OPTIONS_enc_tkt_in_skey (index %d)\n", Index));
  2145. TicketList[Index].next = NULL;
  2146. TicketList[Index].value = TgtReply->ticket;
  2147. KdcOptions |= KERB_KDC_OPTIONS_enc_tkt_in_skey;
  2148. //
  2149. // increment Index
  2150. //
  2151. Index++;
  2152. }
  2153. if (ARGUMENT_PRESENT( EvidenceTicket))
  2154. {
  2155. ASSERT(Index < 2);
  2156. D_DebugLog((DEB_TRACE, "KerbGetTgsTicket setting KERB_KDC_OPTIONS_cname_in_addl_tkt (index %d)\n", Index));
  2157. if (Index > 0)
  2158. {
  2159. TicketList[Index - 1].next = &TicketList[Index];
  2160. }
  2161. TicketList[Index].next = NULL;
  2162. TicketList[Index].value = (*EvidenceTicket);
  2163. KdcOptions |= KERB_KDC_OPTIONS_cname_in_addl_tkt;
  2164. }
  2165. if ((KdcOptions & (KERB_KDC_OPTIONS_enc_tkt_in_skey | KERB_KDC_OPTIONS_cname_in_addl_tkt)) != 0)
  2166. {
  2167. RequestBody->additional_tickets = &TicketList[0];
  2168. RequestBody->bit_mask |= additional_tickets_present;
  2169. }
  2170. //
  2171. // Fill in the strings in the ticket request
  2172. //
  2173. if (!KERB_SUCCESS(KerbConvertKdcNameToPrincipalName(
  2174. &RequestBody->KERB_KDC_REQUEST_BODY_server_name,
  2175. TargetName
  2176. )))
  2177. {
  2178. Status = STATUS_INSUFFICIENT_RESOURCES;
  2179. goto Cleanup;
  2180. }
  2181. RequestBody->bit_mask |= KERB_KDC_REQUEST_BODY_server_name_present;
  2182. //
  2183. // Copy the domain name so we don't need to hold the lock
  2184. //
  2185. Status = KerbDuplicateString(
  2186. &TempDomainName,
  2187. &TicketGrantingTicket->TargetDomainName
  2188. );
  2189. if (!NT_SUCCESS(Status))
  2190. {
  2191. goto Cleanup;
  2192. }
  2193. //
  2194. // Check if this is an MIT kdc or if the spn had a realm in it (e.g,
  2195. // a/b/c@realm - if so, turn off name canonicalization
  2196. //
  2197. if ((Flags & KERB_GET_TICKET_NO_CANONICALIZE) != 0)
  2198. {
  2199. KdcOptions &= ~KERB_KDC_OPTIONS_name_canonicalize;
  2200. }
  2201. else if (KerbLookupMitRealm(
  2202. &TempDomainName,
  2203. &MitRealm,
  2204. &UsedAlternateName
  2205. ))
  2206. {
  2207. //
  2208. // So the user is getting a ticket from an MIT realm. This means
  2209. // if the MIT realm flags don't indicate that name canonicalization
  2210. // is supported then we don't ask for name canonicalization
  2211. //
  2212. if ((MitRealm->Flags & KERB_MIT_REALM_DOES_CANONICALIZE) == 0)
  2213. {
  2214. fMitRealmPossibleRetry = TRUE;
  2215. KdcOptions &= ~KERB_KDC_OPTIONS_name_canonicalize;
  2216. }
  2217. else
  2218. {
  2219. KdcOptions |= KERB_KDC_OPTIONS_name_canonicalize;
  2220. }
  2221. }
  2222. KdcFlagOptions = KerbConvertUlongToFlagUlong(KdcOptions);
  2223. RequestBody->kdc_options.length = sizeof(ULONG) * 8;
  2224. RequestBody->kdc_options.value = (PUCHAR) &KdcFlagOptions;
  2225. //
  2226. // Marshall the request and compute a checksum of it
  2227. //
  2228. if (!KERB_SUCCESS(KerbConvertUnicodeStringToRealm(
  2229. &RequestBody->realm,
  2230. &TempDomainName
  2231. )))
  2232. {
  2233. Status = STATUS_INSUFFICIENT_RESOURCES;
  2234. goto Cleanup;
  2235. }
  2236. DsysAssert( !TicketCacheLocked );
  2237. KerbReadLockTicketCache();
  2238. TicketCacheLocked = TRUE;
  2239. //
  2240. // Now compute a checksum of that data
  2241. //
  2242. Status = KerbComputeTgsChecksum(
  2243. RequestBody,
  2244. &TicketGrantingTicket->SessionKey,
  2245. (MitRealm != NULL) ? MitRealm->ApReqChecksumType : KERB_DEFAULT_AP_REQ_CSUM,
  2246. &RequestChecksum
  2247. );
  2248. if (!NT_SUCCESS(Status))
  2249. {
  2250. KerbUnlockTicketCache();
  2251. TicketCacheLocked = FALSE;
  2252. goto Cleanup;
  2253. }
  2254. //
  2255. // Create the AP request to the KDC for the ticket to the service
  2256. //
  2257. RetryWithTcp:
  2258. //
  2259. // Lock the ticket cache while we access the cached tickets
  2260. //
  2261. KerbErr = KerbCreateApRequest(
  2262. TicketGrantingTicket->ClientName,
  2263. ClientRealm,
  2264. &TicketGrantingTicket->SessionKey,
  2265. NULL, // no subsessionkey
  2266. Nonce,
  2267. &AuthenticatorTime,
  2268. &TicketGrantingTicket->Ticket,
  2269. 0, // no AP options
  2270. &RequestChecksum,
  2271. &TicketGrantingTicket->TimeSkew, // server time
  2272. TRUE, // kdc request
  2273. (PULONG) &ApRequest.value.preauth_data.length,
  2274. &ApRequest.value.preauth_data.value
  2275. );
  2276. DsysAssert( TicketCacheLocked );
  2277. KerbUnlockTicketCache();
  2278. TicketCacheLocked = FALSE;
  2279. if (!KERB_SUCCESS(KerbErr))
  2280. {
  2281. D_DebugLog((DEB_ERROR,"Failed to create authenticator: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
  2282. Status = STATUS_INSUFFICIENT_RESOURCES;
  2283. goto Cleanup;
  2284. }
  2285. ApRequest.next = NULL;
  2286. ApRequest.value.preauth_data_type = KRB5_PADATA_TGS_REQ;
  2287. TicketRequest.KERB_KDC_REQUEST_preauth_data = &ApRequest;
  2288. TicketRequest.bit_mask |= KERB_KDC_REQUEST_preauth_data_present;
  2289. // Insert additonal preauth into list
  2290. if (ARGUMENT_PRESENT(PADataList))
  2291. {
  2292. // better be NULL padatalist
  2293. ApRequest.next = PADataList;
  2294. }
  2295. else
  2296. {
  2297. ApRequest.next = NULL;
  2298. }
  2299. //
  2300. // Marshall the request
  2301. //
  2302. TicketRequest.version = KERBEROS_VERSION;
  2303. TicketRequest.message_type = KRB_TGS_REQ;
  2304. //
  2305. // Pack the request
  2306. //
  2307. KerbErr = KerbPackTgsRequest(
  2308. &TicketRequest,
  2309. &RequestMessage.BufferSize,
  2310. &RequestMessage.Buffer
  2311. );
  2312. if (!KERB_SUCCESS(KerbErr))
  2313. {
  2314. Status = STATUS_INSUFFICIENT_RESOURCES;
  2315. goto Cleanup;
  2316. }
  2317. //
  2318. // Now actually get the ticket. We will retry once.
  2319. //
  2320. Status = KerbMakeKdcCall(
  2321. &TempDomainName,
  2322. NULL, // **NEVER* call w/ account
  2323. FALSE, // don't require PDC
  2324. UseTcp,
  2325. &RequestMessage,
  2326. &ReplyMessage,
  2327. 0, // no additional flags (yet)
  2328. &CalledPdc
  2329. );
  2330. if (!NT_SUCCESS(Status))
  2331. {
  2332. DebugLog((DEB_ERROR,"Failed to call KDC for TGS request: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  2333. goto Cleanup;
  2334. }
  2335. KerbErr = KerbUnpackTgsReply(
  2336. ReplyMessage.Buffer,
  2337. ReplyMessage.BufferSize,
  2338. KdcReply
  2339. );
  2340. if (!KERB_SUCCESS(KerbErr))
  2341. {
  2342. PKERB_ERROR ErrorMessage = NULL;
  2343. DebugLog((DEB_WARN, "KerbGetTgsTicket failed to unpack KDC reply: 0x%x\n", KerbErr));
  2344. //
  2345. // Try to unpack it as kerb_error
  2346. //
  2347. KerbErr = KerbUnpackKerbError(
  2348. ReplyMessage.Buffer,
  2349. ReplyMessage.BufferSize,
  2350. &ErrorMessage
  2351. );
  2352. if (KERB_SUCCESS(KerbErr))
  2353. {
  2354. if (ErrorMessage->bit_mask & error_data_present)
  2355. {
  2356. KerbUnpackErrorData(
  2357. ErrorMessage,
  2358. &pExtendedError
  2359. );
  2360. }
  2361. KerbErr = (KERBERR) ErrorMessage->error_code;
  2362. KerbReportKerbError(
  2363. TargetName,
  2364. &TempDomainName,
  2365. NULL,
  2366. NULL,
  2367. KLIN(FILENO, __LINE__),
  2368. ErrorMessage,
  2369. KerbErr,
  2370. pExtendedError,
  2371. FALSE
  2372. );
  2373. //
  2374. // Check for time skew. If we got a skew error, record the time
  2375. // skew between here and the KDC in the ticket so we can retry
  2376. // with the correct time.
  2377. //
  2378. if (KerbErr == KRB_AP_ERR_SKEW)
  2379. {
  2380. TimeStamp CurrentTime;
  2381. TimeStamp KdcTime;
  2382. //
  2383. // Only update failures with the same ticket once
  2384. //
  2385. if (KerbGetTime(TicketGrantingTicket->TimeSkew) == 0)
  2386. {
  2387. KerbUpdateSkewTime(TRUE);
  2388. }
  2389. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime);
  2390. KerbConvertGeneralizedTimeToLargeInt(
  2391. &KdcTime,
  2392. &ErrorMessage->server_time,
  2393. ErrorMessage->server_usec
  2394. );
  2395. KerbWriteLockTicketCache();
  2396. #if 0
  2397. D_DebugLog(( DEB_WARN, "KDC time : \n" ));
  2398. DebugDisplayTime( DEB_WARN, (PFILETIME)&KdcTime);
  2399. D_DebugLog(( DEB_WARN, "Current time : \n" ));
  2400. DebugDisplayTime( DEB_WARN, (PFILETIME)&CurrentTime);
  2401. #endif
  2402. KerbSetTime(&TicketGrantingTicket->TimeSkew, KerbGetTime(KdcTime) - KerbGetTime(CurrentTime));
  2403. KerbUnlockTicketCache();
  2404. DoRetryGetTicket = TRUE;
  2405. }
  2406. else if ((KerbErr == KRB_ERR_RESPONSE_TOO_BIG) && (!UseTcp))
  2407. {
  2408. //
  2409. // The KDC response was too big to fit in a datagram. If
  2410. // we aren't already using TCP use it now. Clean up allocated memory from UDP try
  2411. //
  2412. UseTcp = TRUE;
  2413. KerbFreeKerbError(ErrorMessage);
  2414. ErrorMessage = NULL;
  2415. MIDL_user_free(ReplyMessage.Buffer);
  2416. ReplyMessage.Buffer = NULL;
  2417. MIDL_user_free(RequestMessage.Buffer);
  2418. RequestMessage.Buffer = NULL;
  2419. MIDL_user_free(ApRequest.value.preauth_data.value);
  2420. ApRequest.value.preauth_data.value = NULL;
  2421. DsysAssert( !TicketCacheLocked );
  2422. KerbReadLockTicketCache();
  2423. TicketCacheLocked = TRUE;
  2424. goto RetryWithTcp;
  2425. }
  2426. else if (KerbErr == KDC_ERR_S_PRINCIPAL_UNKNOWN)
  2427. {
  2428. if (EXT_CLIENT_INFO_PRESENT(pExtendedError) && (STATUS_USER2USER_REQUIRED == pExtendedError->status))
  2429. {
  2430. DebugLog((DEB_WARN, "KerbGetTgsTicket received KDC_ERR_S_PRINCIPAL_UNKNOWN and STATUS_USER2USER_REQUIRED\n"));
  2431. Status = STATUS_USER2USER_REQUIRED;
  2432. KerbFreeKerbError(ErrorMessage);
  2433. goto Cleanup;
  2434. }
  2435. //
  2436. // This looks to be the MIT Realm retry case, where name canonicalization
  2437. // is not on and the PRINCIPAL_UNKNOWN was returned by the MIT KDC
  2438. //
  2439. else if (fMitRealmPossibleRetry)
  2440. {
  2441. *pRetryFlags |= KERB_MIT_NO_CANONICALIZE_RETRY;
  2442. D_DebugLog((DEB_TRACE, "KerbGetTgsTicket KerbCallKdc this is the MIT retry case\n"));
  2443. }
  2444. }
  2445. //
  2446. // Semi-hack here. Bad option rarely is returned, and usually
  2447. // indicates your TGT is about to expire. TKT_EXPIRED is also
  2448. // potentially recoverable. Check the e_data to
  2449. // see if we should blow away TGT to fix TGS problem.
  2450. //
  2451. else if ((KerbErr == KDC_ERR_BADOPTION) ||
  2452. (KerbErr == KRB_AP_ERR_TKT_EXPIRED))
  2453. {
  2454. if (NULL != pExtendedError)
  2455. {
  2456. Status = pExtendedError->status;
  2457. if ( Status == STATUS_TIME_DIFFERENCE_AT_DC )
  2458. {
  2459. *pRetryFlags |= KERB_RETRY_WITH_NEW_TGT;
  2460. D_DebugLog((DEB_TRACE, "Hit bad option retry case - %x \n", KerbErr));
  2461. }
  2462. else if ( Status == STATUS_NO_MATCH )
  2463. {
  2464. DebugLog((DEB_TRACE_S4U, "No match on S4UTarget\n"));
  2465. *pRetryFlags |= KERB_RETRY_NO_S4UMATCH;
  2466. KerbFreeKerbError(ErrorMessage);
  2467. goto Cleanup;
  2468. }
  2469. else if ( Status == STATUS_NOT_SUPPORTED )
  2470. {
  2471. D_DebugLog((DEB_TRACE_S4U, "No S4U available\n"));
  2472. *pRetryFlags |= KERB_RETRY_DISABLE_S4U;
  2473. KerbFreeKerbError(ErrorMessage);
  2474. goto Cleanup;
  2475. }
  2476. }
  2477. else
  2478. {
  2479. //
  2480. // If we get BADOPTION on an S4U request, we've got to
  2481. // assume that our server doesn't support it. There are some
  2482. // fringe cases where this may not be a reliable mechanism,
  2483. // however. FESTER - investigate other BADOPTION cases
  2484. //
  2485. D_DebugLog((DEB_TRACE_S4U, "No S4U available\n"));
  2486. *pRetryFlags |= KERB_RETRY_DISABLE_S4U;
  2487. }
  2488. }
  2489. //
  2490. // Per bug 315833, we purge on these errors as well
  2491. //
  2492. else if ((KerbErr == KDC_ERR_C_OLD_MAST_KVNO) ||
  2493. (KerbErr == KDC_ERR_TGT_REVOKED) ||
  2494. (KerbErr == KDC_ERR_NEVER_VALID) ||
  2495. (KerbErr == KRB_AP_ERR_BAD_INTEGRITY))
  2496. {
  2497. *pRetryFlags |= KERB_RETRY_WITH_NEW_TGT;
  2498. D_DebugLog((DEB_TRACE, " KerbGetTgsTicket got error requiring new tgt\n"));
  2499. if (EXT_CLIENT_INFO_PRESENT(pExtendedError))
  2500. {
  2501. Status = pExtendedError->status;
  2502. }
  2503. else
  2504. {
  2505. Status = KerbMapKerbError(KerbErr);
  2506. }
  2507. DebugLog((DEB_WARN, "KerbGetTgsTicket failed w/ error %x, status %x\n", KerbErr, Status));
  2508. KerbFreeKerbError(ErrorMessage);
  2509. goto Cleanup;
  2510. }
  2511. else if ( KerbErr == KDC_ERR_CLIENT_REVOKED )
  2512. {
  2513. if (EXT_CLIENT_INFO_PRESENT(pExtendedError))
  2514. {
  2515. Status = pExtendedError->status;
  2516. }
  2517. else
  2518. {
  2519. Status = KerbMapKerbError(KerbErr);
  2520. }
  2521. DebugLog((DEB_WARN, "KerbGetTgsTicket CLIENTREVOKED status %x\n",Status));
  2522. KerbFreeKerbError(ErrorMessage);
  2523. goto Cleanup;
  2524. }
  2525. else if ( KerbErr == KDC_ERR_POLICY )
  2526. {
  2527. if (EXT_CLIENT_INFO_PRESENT(pExtendedError))
  2528. {
  2529. Status = pExtendedError->status;
  2530. }
  2531. else
  2532. {
  2533. Status = KerbMapKerbError(KerbErr);
  2534. }
  2535. DebugLog((DEB_WARN, "KerbGetTgsTicket failed w/ error %x, status %x\n", KerbErr, Status));
  2536. KerbFreeKerbError(ErrorMessage);
  2537. goto Cleanup;
  2538. }
  2539. else if (KerbErr == KDC_ERR_NONE)
  2540. {
  2541. DebugLog((DEB_ERROR, "KerbGetTgsTicket KerbCallKdc: error KDC_ERR_NONE\n"));
  2542. KerbErr = KRB_ERR_GENERIC;
  2543. }
  2544. KerbFreeKerbError(ErrorMessage);
  2545. DebugLog((DEB_WARN, "KerbGetTgsTicket KerbCallKdc: error 0x%x\n", KerbErr));
  2546. Status = KerbMapKerbError(KerbErr);
  2547. }
  2548. else
  2549. {
  2550. Status = STATUS_LOGON_FAILURE;
  2551. }
  2552. goto Cleanup;
  2553. }
  2554. //
  2555. // Now unpack the reply body:
  2556. //
  2557. KerbReadLockTicketCache();
  2558. KerbErr = KerbUnpackKdcReplyBody(
  2559. &(*KdcReply)->encrypted_part,
  2560. &TicketGrantingTicket->SessionKey,
  2561. KERB_ENCRYPTED_TGS_REPLY_PDU,
  2562. ReplyBody
  2563. );
  2564. KerbUnlockTicketCache();
  2565. if (!KERB_SUCCESS(KerbErr))
  2566. {
  2567. D_DebugLog((DEB_ERROR, "KerbGetTgsTicket failed to decrypt KDC reply body: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
  2568. Status = STATUS_LOGON_FAILURE;
  2569. goto Cleanup;
  2570. }
  2571. //
  2572. // Verify the nonce is correct:
  2573. //
  2574. if (RequestBody->nonce != (*ReplyBody)->nonce)
  2575. {
  2576. D_DebugLog((DEB_ERROR, "KerbGetTgsTicket AS Nonces don't match: 0x%x vs 0x%x. %ws, line %d\n",
  2577. RequestBody->nonce,
  2578. (*ReplyBody)->nonce, THIS_FILE, __LINE__));
  2579. Status = STATUS_LOGON_FAILURE;
  2580. goto Cleanup;
  2581. }
  2582. Cleanup:
  2583. if (EncAuthData.cipher_text.value != NULL)
  2584. {
  2585. MIDL_user_free(EncAuthData.cipher_text.value);
  2586. }
  2587. if (RequestChecksum.checksum.value != NULL)
  2588. {
  2589. KerbFree(RequestChecksum.checksum.value);
  2590. }
  2591. SafeAllocaFree(CryptVector);
  2592. CryptVector = NULL;
  2593. KerbFreeCryptList(
  2594. RequestBody->encryption_type
  2595. );
  2596. if (ReplyMessage.Buffer != NULL)
  2597. {
  2598. MIDL_user_free(ReplyMessage.Buffer);
  2599. ReplyMessage.Buffer = NULL;
  2600. }
  2601. if (RequestMessage.Buffer != NULL)
  2602. {
  2603. MIDL_user_free(RequestMessage.Buffer);
  2604. RequestMessage.Buffer = NULL;
  2605. }
  2606. if (ApRequest.value.preauth_data.value != NULL)
  2607. {
  2608. MIDL_user_free(ApRequest.value.preauth_data.value);
  2609. ApRequest.value.preauth_data.value = NULL;
  2610. }
  2611. KerbFreePrincipalName(
  2612. &RequestBody->KERB_KDC_REQUEST_BODY_server_name
  2613. );
  2614. KerbFreeRealm(
  2615. &RequestBody->realm
  2616. );
  2617. KerbFreeString(
  2618. &TempDomainName
  2619. );
  2620. if (NULL != pExtendedError)
  2621. {
  2622. KerbFreeData(KERB_EXT_ERROR_PDU, pExtendedError);
  2623. pExtendedError = NULL;
  2624. }
  2625. //
  2626. // If we should retry getting the ticket and we haven't already retried
  2627. // once, try again.
  2628. //
  2629. if (DoRetryGetTicket && !RetriedOnce)
  2630. {
  2631. RetriedOnce = TRUE;
  2632. goto RetryGetTicket;
  2633. }
  2634. return(Status);
  2635. }
  2636. //+-------------------------------------------------------------------------
  2637. //
  2638. // Function: KerbGetReferralNames
  2639. //
  2640. // Synopsis: Gets the referral names from a KDC reply. If none are present,
  2641. // returned strings are empty.
  2642. //
  2643. // Effects:
  2644. //
  2645. // Arguments:
  2646. //
  2647. // Requires:
  2648. //
  2649. // Returns:
  2650. //
  2651. // Notes:
  2652. //
  2653. //
  2654. //--------------------------------------------------------------------------
  2655. NTSTATUS
  2656. KerbGetReferralNames(
  2657. IN PKERB_ENCRYPTED_KDC_REPLY KdcReply,
  2658. IN PKERB_INTERNAL_NAME OriginalTargetName,
  2659. OUT PUNICODE_STRING ReferralRealm
  2660. )
  2661. {
  2662. NTSTATUS Status = STATUS_SUCCESS;
  2663. PKERB_PA_DATA_LIST PaEntry;
  2664. PKERB_PA_SERV_REFERRAL ReferralInfo = NULL;
  2665. KERBERR KerbErr;
  2666. PKERB_INTERNAL_NAME TargetName = NULL;
  2667. PKERB_INTERNAL_NAME KpasswdName = NULL;
  2668. RtlInitUnicodeString(
  2669. ReferralRealm,
  2670. NULL
  2671. );
  2672. PaEntry = (PKERB_PA_DATA_LIST) KdcReply->encrypted_pa_data;
  2673. //
  2674. // Search the list for the referral infromation
  2675. //
  2676. while (PaEntry != NULL)
  2677. {
  2678. if (PaEntry->value.preauth_data_type == KRB5_PADATA_REFERRAL_INFO)
  2679. {
  2680. break;
  2681. }
  2682. PaEntry = PaEntry->next;
  2683. }
  2684. if (PaEntry == NULL)
  2685. {
  2686. //
  2687. // Check to see if the server name is krbtgt - if it is, then
  2688. // this is a referral.
  2689. //
  2690. KerbErr = KerbConvertPrincipalNameToKdcName(
  2691. &TargetName,
  2692. &KdcReply->server_name
  2693. );
  2694. if (!KERB_SUCCESS(KerbErr))
  2695. {
  2696. Status = KerbMapKerbError(KerbErr);
  2697. goto Cleanup;
  2698. }
  2699. //
  2700. // Build the service name for the ticket
  2701. //
  2702. Status = KerbBuildKpasswdName(
  2703. &KpasswdName
  2704. );
  2705. if (!NT_SUCCESS(Status))
  2706. {
  2707. goto Cleanup;
  2708. }
  2709. if ((TargetName->NameCount == 2) &&
  2710. RtlEqualUnicodeString(
  2711. &KerbGlobalKdcServiceName,
  2712. &TargetName->Names[0],
  2713. FALSE // not case sensitive
  2714. ) &&
  2715. !(KerbEqualKdcNames(
  2716. OriginalTargetName,
  2717. TargetName) ||
  2718. KerbEqualKdcNames(
  2719. OriginalTargetName,
  2720. KpasswdName) ))
  2721. {
  2722. //
  2723. // This is a referral, so set the referral name to the
  2724. // second portion of the name
  2725. //
  2726. Status = KerbDuplicateString(
  2727. ReferralRealm,
  2728. &TargetName->Names[1]
  2729. );
  2730. }
  2731. KerbFreeKdcName(&TargetName);
  2732. KerbFreeKdcName(&KpasswdName);
  2733. return(Status);
  2734. }
  2735. //
  2736. // Now try to unpack the data
  2737. //
  2738. KerbErr = KerbUnpackData(
  2739. PaEntry->value.preauth_data.value,
  2740. PaEntry->value.preauth_data.length,
  2741. KERB_PA_SERV_REFERRAL_PDU,
  2742. (PVOID *) &ReferralInfo
  2743. );
  2744. if (!KERB_SUCCESS( KerbErr ))
  2745. {
  2746. D_DebugLog((DEB_ERROR,"Failed to decode referral info: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
  2747. Status = STATUS_INSUFFICIENT_RESOURCES;
  2748. goto Cleanup;
  2749. }
  2750. if (!KERB_SUCCESS(KerbConvertRealmToUnicodeString(
  2751. ReferralRealm,
  2752. &ReferralInfo->referred_server_realm
  2753. )))
  2754. {
  2755. Status = STATUS_INSUFFICIENT_RESOURCES;
  2756. goto Cleanup;
  2757. }
  2758. Cleanup:
  2759. KerbFreeKdcName(&TargetName);
  2760. KerbFreeKdcName(&KpasswdName);
  2761. if (ReferralInfo != NULL)
  2762. {
  2763. KerbFreeData(
  2764. KERB_PA_SERV_REFERRAL_PDU,
  2765. ReferralInfo
  2766. );
  2767. }
  2768. if (!NT_SUCCESS(Status))
  2769. {
  2770. KerbFreeString(
  2771. ReferralRealm
  2772. );
  2773. }
  2774. return(Status);
  2775. }
  2776. //+-------------------------------------------------------------------------
  2777. //
  2778. // Function: KerbMITGetMachineDomain
  2779. //
  2780. // Synopsis: Determines if the machine is in a Windows 2000 domain and
  2781. // if it is then the function attempts to get a TGT for this
  2782. // domain with the passed in credentials.
  2783. //
  2784. // Effects:
  2785. //
  2786. // Arguments: LogonSession - the logon session to use for ticket caching
  2787. // and the identity of the caller.
  2788. // Credential - the credential of the caller
  2789. // TargetName - Name of the target for which to obtain a ticket.
  2790. // TargetDomainName - Domain name of target
  2791. // ClientRealm - the realm of the machine which the retry will use
  2792. // TicketGrantingTicket - Will be freed if non-NULL
  2793. //
  2794. // Requires:
  2795. //
  2796. // Returns:
  2797. //
  2798. // Notes:
  2799. //
  2800. //
  2801. //--------------------------------------------------------------------------
  2802. NTSTATUS
  2803. KerbMITGetMachineDomain(
  2804. IN PKERB_INTERNAL_NAME TargetName,
  2805. IN OUT PUNICODE_STRING TargetDomainName,
  2806. IN OUT PKERB_TICKET_CACHE_ENTRY *TicketGrantingTicket
  2807. )
  2808. {
  2809. NTSTATUS Status = STATUS_SUCCESS;
  2810. PLSAPR_POLICY_DNS_DOMAIN_INFO DnsDomainInfo = NULL;
  2811. PLSAPR_POLICY_INFORMATION Policy = NULL;
  2812. KERBEROS_MACHINE_ROLE Role;
  2813. Role = KerbGetGlobalRole();
  2814. //
  2815. // We're not part of a domain, so bail out here.
  2816. //
  2817. if (Role == KerbRoleRealmlessWksta)
  2818. {
  2819. Status = STATUS_NO_TRUST_SAM_ACCOUNT;
  2820. goto Cleanup;
  2821. }
  2822. Status = I_LsaIQueryInformationPolicyTrusted(
  2823. PolicyDnsDomainInformation,
  2824. &Policy
  2825. );
  2826. if (!NT_SUCCESS(Status))
  2827. {
  2828. D_DebugLog((DEB_TRACE,"Failed Query policy %x %ws, line %d\n", THIS_FILE, __LINE__));
  2829. goto Cleanup;
  2830. }
  2831. DnsDomainInfo = &Policy->PolicyDnsDomainInfo;
  2832. //
  2833. // make sure the computer is a member of an NT domain
  2834. //
  2835. if ((DnsDomainInfo->DnsDomainName.Length != 0) && (DnsDomainInfo->Sid != NULL))
  2836. {
  2837. //
  2838. // make the client realm the domain of the computer
  2839. //
  2840. KerbFreeString(TargetDomainName);
  2841. RtlZeroMemory(TargetDomainName, sizeof(UNICODE_STRING));
  2842. Status = KerbDuplicateString(
  2843. TargetDomainName,
  2844. (PUNICODE_STRING)&DnsDomainInfo->DnsDomainName
  2845. );
  2846. if (!NT_SUCCESS(Status))
  2847. {
  2848. goto Cleanup;
  2849. }
  2850. (VOID) RtlUpcaseUnicodeString( TargetDomainName,
  2851. TargetDomainName,
  2852. FALSE);
  2853. if (*TicketGrantingTicket != NULL)
  2854. {
  2855. KerbDereferenceTicketCacheEntry(*TicketGrantingTicket);
  2856. *TicketGrantingTicket = NULL;
  2857. }
  2858. }
  2859. else
  2860. {
  2861. Status = STATUS_NO_TRUST_SAM_ACCOUNT;
  2862. }
  2863. Cleanup:
  2864. if (Policy != NULL)
  2865. {
  2866. I_LsaIFree_LSAPR_POLICY_INFORMATION(
  2867. PolicyDnsDomainInformation,
  2868. Policy
  2869. );
  2870. }
  2871. return(Status);
  2872. }
  2873. //+-------------------------------------------------------------------------
  2874. //
  2875. // Function: KerbGetServiceTicket
  2876. //
  2877. // Synopsis: Gets a ticket to a service and handles cross-domain referrals
  2878. //
  2879. // Effects:
  2880. //
  2881. // Arguments: LogonSession - the logon session to use for ticket caching
  2882. // and the identity of the caller.
  2883. // TargetName - Name of the target for which to obtain a ticket.
  2884. // TargetDomainName - Domain name of target
  2885. // Flags - Flags about the request
  2886. // TicketOptions - KDC options flags
  2887. // EncryptionType - optional Requested encryption type
  2888. // ErrorMessage - Error message from an AP request containing hints
  2889. // for next ticket.
  2890. // AuthorizationData - Optional authorization data to stick
  2891. // in the ticket.
  2892. // TgtReply - TGT to use for getting a ticket with enc_tkt_in_skey
  2893. // TicketCacheEntry - Receives a referenced ticket cache entry.
  2894. //
  2895. // Requires:
  2896. //
  2897. // Returns:
  2898. //
  2899. // Notes:
  2900. //
  2901. //
  2902. //--------------------------------------------------------------------------
  2903. NTSTATUS
  2904. KerbGetServiceTicket(
  2905. IN PKERB_LOGON_SESSION LogonSession,
  2906. IN PKERB_CREDENTIAL Credential,
  2907. IN OPTIONAL PKERB_CREDMAN_CRED CredManCredentials,
  2908. IN PKERB_INTERNAL_NAME TargetName,
  2909. IN PUNICODE_STRING TargetDomainName,
  2910. IN OPTIONAL PKERB_SPN_CACHE_ENTRY SpnCacheEntry,
  2911. IN ULONG Flags,
  2912. IN OPTIONAL ULONG TicketOptions,
  2913. IN OPTIONAL ULONG EncryptionType,
  2914. IN OPTIONAL PKERB_ERROR ErrorMessage,
  2915. IN OPTIONAL PKERB_AUTHORIZATION_DATA AuthorizationData,
  2916. IN OPTIONAL PKERB_TGT_REPLY TgtReply,
  2917. OUT PKERB_TICKET_CACHE_ENTRY * NewCacheEntry,
  2918. OUT LPGUID pLogonGuid OPTIONAL
  2919. )
  2920. {
  2921. NTSTATUS Status = STATUS_SUCCESS;
  2922. NTSTATUS AuditStatus = STATUS_SUCCESS;
  2923. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
  2924. PKERB_TICKET_CACHE_ENTRY TicketGrantingTicket = NULL;
  2925. PKERB_TICKET_CACHE_ENTRY LastTgt = NULL;
  2926. PKERB_KDC_REPLY KdcReply = NULL;
  2927. PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody = NULL;
  2928. BOOLEAN LogonSessionsLocked = FALSE;
  2929. BOOLEAN TicketCacheLocked = FALSE;
  2930. BOOLEAN CrossRealm = FALSE;
  2931. PKERB_INTERNAL_NAME RealTargetName = NULL;
  2932. UNICODE_STRING RealTargetRealm = NULL_UNICODE_STRING;
  2933. PKERB_INTERNAL_NAME TargetTgtKdcName = NULL;
  2934. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials = NULL;
  2935. BOOLEAN UsedCredentials = FALSE;
  2936. UNICODE_STRING ClientRealm = NULL_UNICODE_STRING;
  2937. UNICODE_STRING SpnTargetRealm = NULL_UNICODE_STRING;
  2938. BOOLEAN CacheTicket = TRUE;
  2939. ULONG ReferralCount = 0;
  2940. ULONG RetryFlags = 0;
  2941. BOOLEAN fMITRetryAlreadyMade = FALSE;
  2942. BOOLEAN TgtRetryMade = FALSE;
  2943. BOOLEAN CacheBasedFailure = FALSE;
  2944. GUID LogonGuid = { 0 };
  2945. //
  2946. // Check to see if the credential has any primary credentials
  2947. //
  2948. TGTRetry:
  2949. KerbFreeTgsReply(KdcReply);
  2950. KerbFreeKdcReplyBody(KdcReplyBody);
  2951. KdcReply = NULL;
  2952. KdcReplyBody = NULL;
  2953. DsysAssert( !LogonSessionsLocked );
  2954. KerbReadLockLogonSessions(LogonSession);
  2955. LogonSessionsLocked = TRUE;
  2956. if ((Credential != NULL) && (Credential->SuppliedCredentials != NULL))
  2957. {
  2958. PrimaryCredentials = Credential->SuppliedCredentials;
  2959. UsedCredentials = TRUE;
  2960. }
  2961. else if (CredManCredentials != NULL)
  2962. {
  2963. PrimaryCredentials = CredManCredentials->SuppliedCredentials;
  2964. UsedCredentials = TRUE;
  2965. }
  2966. else
  2967. {
  2968. PrimaryCredentials = &LogonSession->PrimaryCredentials;
  2969. UsedCredentials = ((LogonSession->LogonSessionFlags & KERB_LOGON_NEW_CREDENTIALS) != 0);
  2970. }
  2971. //
  2972. // Make sure the name is not zero length
  2973. //
  2974. if ((TargetName->NameCount == 0) ||
  2975. (TargetName->Names[0].Length == 0))
  2976. {
  2977. D_DebugLog((DEB_ERROR, "KerbGetServiceTicket trying to crack zero length name.\n"));
  2978. Status = SEC_E_TARGET_UNKNOWN;
  2979. goto Cleanup;
  2980. }
  2981. //
  2982. // First check the ticket cache for this logon session. We don't look
  2983. // for the target principal name because we can't be assured that this
  2984. // is a valid principal name for the target. If we are doing user-to-
  2985. // user, don't use the cache because the tgt key may have changed
  2986. //
  2987. if ((TgtReply == NULL) && ((Flags & KERB_GET_TICKET_NO_CACHE) == 0))
  2988. {
  2989. TicketCacheEntry = KerbLocateTicketCacheEntry(
  2990. &PrimaryCredentials->ServerTicketCache,
  2991. TargetName,
  2992. TargetDomainName
  2993. );
  2994. }
  2995. else
  2996. {
  2997. //
  2998. // We don't want to cache user-to-user tickets
  2999. //
  3000. CacheTicket = FALSE;
  3001. }
  3002. if (TicketCacheEntry != NULL)
  3003. {
  3004. //
  3005. // If we were given an error message that indicated a bad password
  3006. // through away the cached ticket
  3007. //
  3008. //
  3009. // If we were given an error message that indicated a bad password
  3010. // through away the cached ticket
  3011. //
  3012. if (ARGUMENT_PRESENT(ErrorMessage) && ((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_MODIFIED))
  3013. {
  3014. KerbRemoveTicketCacheEntry(TicketCacheEntry);
  3015. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  3016. TicketCacheEntry = NULL;
  3017. }
  3018. else
  3019. {
  3020. ULONG TicketFlags;
  3021. ULONG CacheTicketFlags;
  3022. ULONG CacheEncryptionType;
  3023. //
  3024. // Check if the flags are present
  3025. //
  3026. KerbReadLockTicketCache();
  3027. CacheTicketFlags = TicketCacheEntry->TicketFlags;
  3028. CacheEncryptionType = TicketCacheEntry->Ticket.encrypted_part.encryption_type;
  3029. KerbUnlockTicketCache();
  3030. TicketFlags = KerbConvertKdcOptionsToTicketFlags(TicketOptions);
  3031. if (((CacheTicketFlags & TicketFlags) != TicketFlags) ||
  3032. ((EncryptionType != 0) && (CacheEncryptionType != EncryptionType)))
  3033. {
  3034. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  3035. TicketCacheEntry = NULL;
  3036. }
  3037. else
  3038. {
  3039. //
  3040. // Check the ticket time
  3041. //
  3042. if (KerbTicketIsExpiring(TicketCacheEntry, TRUE))
  3043. {
  3044. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  3045. TicketCacheEntry = NULL;
  3046. }
  3047. else
  3048. {
  3049. *NewCacheEntry = TicketCacheEntry;
  3050. D_DebugLog((DEB_TRACE_REFERRAL, "KerbGetServiceTicket found ticket cache entry %#x, TargetName: ", TicketCacheEntry));
  3051. D_KerbPrintKdcName((DEB_TRACE_REFERRAL, TicketCacheEntry->TargetName));
  3052. D_DebugLog((DEB_TRACE_REFERRAL, "KerbGetServiceTicket target Realm: %wZ\n", &TicketCacheEntry->DomainName));
  3053. TicketCacheEntry = NULL;
  3054. goto Cleanup;
  3055. }
  3056. }
  3057. }
  3058. }
  3059. //
  3060. // If the caller wanted any special options, don't cache this ticket.
  3061. //
  3062. if ((TicketOptions != 0) || (EncryptionType != 0) || ((Flags & KERB_GET_TICKET_NO_CACHE) != 0))
  3063. {
  3064. CacheTicket = FALSE;
  3065. }
  3066. //
  3067. // No cached entry was found so go ahead and call the KDC to
  3068. // get the ticket.
  3069. //
  3070. //
  3071. // Determine the state of the SPNCache using information in the credential.
  3072. // Only do this if we've not been handed
  3073. //
  3074. if ( ARGUMENT_PRESENT(SpnCacheEntry) && TargetDomainName->Buffer == NULL )
  3075. {
  3076. Status = KerbGetSpnCacheStatus(
  3077. SpnCacheEntry,
  3078. PrimaryCredentials,
  3079. &SpnTargetRealm
  3080. );
  3081. if (NT_SUCCESS( Status ))
  3082. {
  3083. KerbFreeString(&RealTargetRealm);
  3084. RealTargetRealm = SpnTargetRealm;
  3085. RtlZeroMemory(&SpnTargetRealm, sizeof(UNICODE_STRING));
  3086. D_DebugLog((DEB_TRACE_SPN_CACHE, "Found SPN cache entry - %wZ\n", &RealTargetRealm));
  3087. D_KerbPrintKdcName((DEB_TRACE_SPN_CACHE, TargetName));
  3088. }
  3089. else if ( Status != STATUS_NO_MATCH )
  3090. {
  3091. D_DebugLog((DEB_TRACE_SPN_CACHE, "KerbGetSpnCacheStatus failed %x\n", Status));
  3092. D_DebugLog((DEB_TRACE_SPN_CACHE, "TargetName: \n"));
  3093. D_KerbPrintKdcName((DEB_TRACE_SPN_CACHE, TargetName));
  3094. CacheBasedFailure = TRUE;
  3095. goto Cleanup;
  3096. }
  3097. Status = STATUS_SUCCESS;
  3098. }
  3099. //
  3100. // First get a TGT to the correct KDC. If a principal name was provided,
  3101. // use it instead of the target name.
  3102. //
  3103. // There could also be a host to realm mapping taking place here. If so,
  3104. // realtargetrealm != NULL.
  3105. //
  3106. Status = KerbGetTgtForService(
  3107. LogonSession,
  3108. Credential,
  3109. CredManCredentials,
  3110. NULL,
  3111. (RealTargetRealm.Buffer == NULL ? TargetDomainName : &RealTargetRealm),
  3112. Flags,
  3113. &TicketGrantingTicket,
  3114. &CrossRealm
  3115. );
  3116. if (!NT_SUCCESS(Status))
  3117. {
  3118. DebugLog((DEB_ERROR, "Failed to get TGT for service: 0x%x :", Status ));
  3119. KerbPrintKdcName(DEB_ERROR, TargetName);
  3120. DebugLog((DEB_ERROR, "%ws, line %d\n", THIS_FILE, __LINE__));
  3121. goto Cleanup;
  3122. }
  3123. //
  3124. // Copy out the client realm name which is used when obtaining the ticket
  3125. //
  3126. Status = KerbDuplicateString(
  3127. &ClientRealm,
  3128. &PrimaryCredentials->DomainName
  3129. );
  3130. if (!NT_SUCCESS(Status))
  3131. {
  3132. goto Cleanup;
  3133. }
  3134. DsysAssert( LogonSessionsLocked );
  3135. KerbUnlockLogonSessions(LogonSession);
  3136. LogonSessionsLocked = FALSE;
  3137. ReferralRestart:
  3138. D_DebugLog((DEB_TRACE_REFERRAL, "KerbGetServiceTicket ReferralRestart\n"));
  3139. D_DebugLog((DEB_TRACE_REFERRAL, "ClientRealm %wZ\n, TargetName ", &ClientRealm));
  3140. D_KerbPrintKdcName((DEB_TRACE_REFERRAL, TargetName));
  3141. D_DebugLog((DEB_TRACE_REFERRAL, "\n ", &ClientRealm));
  3142. //
  3143. // If this is not cross realm (meaning we have a TGT to the corect domain),
  3144. // try to get a ticket directly to the service
  3145. //
  3146. if (!CrossRealm)
  3147. {
  3148. Status = KerbGetTgsTicket(
  3149. &ClientRealm,
  3150. TicketGrantingTicket,
  3151. TargetName,
  3152. Flags,
  3153. TicketOptions,
  3154. EncryptionType,
  3155. AuthorizationData,
  3156. NULL, // no pa data
  3157. TgtReply, // This is for the service directly, so use TGT
  3158. NULL, // no evidence ticket
  3159. NULL, // let kdc determine end time
  3160. &KdcReply,
  3161. &KdcReplyBody,
  3162. &RetryFlags
  3163. );
  3164. if (!NT_SUCCESS(Status))
  3165. {
  3166. //
  3167. // Check for bad option TGT purging
  3168. //
  3169. if (((RetryFlags & KERB_RETRY_WITH_NEW_TGT) != 0) && !TgtRetryMade)
  3170. {
  3171. DebugLog((DEB_WARN, "Doing TGT retry - %p\n", TicketGrantingTicket));
  3172. //
  3173. // Unlink && purge bad tgt
  3174. //
  3175. KerbRemoveTicketCacheEntry(TicketGrantingTicket);
  3176. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  3177. TicketGrantingTicket = NULL;
  3178. TgtRetryMade = TRUE;
  3179. goto TGTRetry;
  3180. }
  3181. //
  3182. // Check for the MIT retry case
  3183. //
  3184. if (((RetryFlags & KERB_MIT_NO_CANONICALIZE_RETRY) != 0)
  3185. && (!fMITRetryAlreadyMade))
  3186. {
  3187. Status = KerbMITGetMachineDomain(
  3188. TargetName,
  3189. TargetDomainName,
  3190. &TicketGrantingTicket
  3191. );
  3192. if (!NT_SUCCESS(Status))
  3193. {
  3194. D_DebugLog((DEB_TRACE,"Failed Query policy information %ws, line %d\n", THIS_FILE, __LINE__));
  3195. goto Cleanup;
  3196. }
  3197. fMITRetryAlreadyMade = TRUE;
  3198. Flags &= ~KERB_TARGET_USED_SPN_CACHE;
  3199. goto TGTRetry;
  3200. }
  3201. DebugLog((DEB_WARN,"Failed to get TGS ticket for service 0x%x : \n",
  3202. Status ));
  3203. KerbPrintKdcName(DEB_WARN, TargetName);
  3204. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  3205. goto Cleanup;
  3206. }
  3207. //
  3208. // Check for referral info in the name
  3209. //
  3210. KerbFreeString(&RealTargetRealm);
  3211. Status = KerbGetReferralNames(
  3212. KdcReplyBody,
  3213. TargetName,
  3214. &RealTargetRealm
  3215. );
  3216. if (!NT_SUCCESS(Status))
  3217. {
  3218. goto Cleanup;
  3219. }
  3220. //
  3221. // If this is not a referral ticket, just cache it and return
  3222. // the cache entry.
  3223. //
  3224. if (RealTargetRealm.Length == 0)
  3225. {
  3226. //
  3227. // Now we have a ticket - lets cache it
  3228. //
  3229. KerbReadLockLogonSessions(LogonSession);
  3230. Status = KerbCreateTicketCacheEntry(
  3231. KdcReply,
  3232. KdcReplyBody,
  3233. TargetName,
  3234. TargetDomainName,
  3235. 0,
  3236. CacheTicket ? &PrimaryCredentials->ServerTicketCache : NULL,
  3237. NULL, // no credential key
  3238. &TicketCacheEntry
  3239. );
  3240. KerbUnlockLogonSessions(LogonSession);
  3241. if (!NT_SUCCESS(Status))
  3242. {
  3243. goto Cleanup;
  3244. }
  3245. *NewCacheEntry = TicketCacheEntry;
  3246. TicketCacheEntry = NULL;
  3247. //
  3248. // We're done, so get out of here.
  3249. //
  3250. goto Cleanup;
  3251. }
  3252. //
  3253. // The server referred us to another domain. Get the service's full
  3254. // name from the ticket and try to find a TGT in that domain.
  3255. //
  3256. Status = KerbDuplicateKdcName(
  3257. &RealTargetName,
  3258. TargetName
  3259. );
  3260. if (!NT_SUCCESS(Status))
  3261. {
  3262. goto Cleanup;
  3263. }
  3264. D_DebugLog((DEB_TRACE_REFERRAL, "Got referral ticket for service \n"));
  3265. D_KerbPrintKdcName((DEB_TRACE_REFERRAL, TargetName));
  3266. D_DebugLog((DEB_TRACE_REFERRAL, "in realm %wZ\n", &RealTargetRealm ));
  3267. D_KerbPrintKdcName((DEB_TRACE_REFERRAL, RealTargetName));
  3268. //
  3269. // Cache the interdomain TGT
  3270. //
  3271. DsysAssert( !LogonSessionsLocked );
  3272. KerbReadLockLogonSessions(LogonSession);
  3273. LogonSessionsLocked = TRUE;
  3274. Status = KerbCreateTicketCacheEntry(
  3275. KdcReply,
  3276. KdcReplyBody,
  3277. NULL, // no target name
  3278. NULL, // no target realm
  3279. 0, // no flags
  3280. CacheTicket ? &PrimaryCredentials->AuthenticationTicketCache : NULL,
  3281. NULL, // no credential key
  3282. &TicketCacheEntry
  3283. );
  3284. if (!NT_SUCCESS(Status))
  3285. {
  3286. goto Cleanup;
  3287. }
  3288. //
  3289. // Dereference the old ticket-granting ticket, and use
  3290. // the one from the referral. This allows us to chase
  3291. // the proper referral path.
  3292. //
  3293. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  3294. TicketGrantingTicket = TicketCacheEntry;
  3295. TicketCacheEntry = NULL;
  3296. DsysAssert( LogonSessionsLocked );
  3297. KerbUnlockLogonSessions(LogonSession);
  3298. LogonSessionsLocked = FALSE;
  3299. }
  3300. else
  3301. {
  3302. //
  3303. // Set the real names to equal the supplied names
  3304. //
  3305. Status = KerbDuplicateKdcName(
  3306. &RealTargetName,
  3307. TargetName
  3308. );
  3309. if (!NT_SUCCESS(Status))
  3310. {
  3311. goto Cleanup;
  3312. }
  3313. //
  3314. // Don't overwrite if we're doing a referral, or if we're missing
  3315. // a TGT for the target domain name.
  3316. //
  3317. if (RealTargetRealm.Buffer == NULL)
  3318. {
  3319. Status = KerbDuplicateString(
  3320. &RealTargetRealm,
  3321. TargetDomainName
  3322. );
  3323. if (!NT_SUCCESS(Status))
  3324. {
  3325. goto Cleanup;
  3326. }
  3327. }
  3328. }
  3329. //
  3330. // Now we are in a case where we have a realm to aim for and a TGT. While
  3331. // we don't have a TGT for the target realm, get one.
  3332. //
  3333. if (!KERB_SUCCESS(KerbBuildFullServiceKdcName(
  3334. &RealTargetRealm,
  3335. &KerbGlobalKdcServiceName,
  3336. KRB_NT_SRV_INST,
  3337. &TargetTgtKdcName
  3338. )))
  3339. {
  3340. Status = STATUS_INSUFFICIENT_RESOURCES;
  3341. goto Cleanup;
  3342. }
  3343. DsysAssert( !TicketCacheLocked );
  3344. KerbReadLockTicketCache();
  3345. TicketCacheLocked = TRUE;
  3346. //
  3347. // Referral chasing code block - very important to get right
  3348. // If we know the "real" target realm, eg. from GC, then
  3349. // we'll walk trusts until we hit "real" target realm.
  3350. //
  3351. while (!RtlEqualUnicodeString(
  3352. &RealTargetRealm,
  3353. &TicketGrantingTicket->TargetDomainName,
  3354. TRUE ))
  3355. {
  3356. //
  3357. // If we just got two TGTs for the same domain, then we must have
  3358. // gotten as far as we can. Chances our our RealTargetRealm is a
  3359. // variation of what the KDC hands out.
  3360. //
  3361. if ((LastTgt != NULL) &&
  3362. RtlEqualUnicodeString(
  3363. &LastTgt->TargetDomainName,
  3364. &TicketGrantingTicket->TargetDomainName,
  3365. TRUE ))
  3366. {
  3367. DsysAssert( TicketCacheLocked );
  3368. KerbUnlockTicketCache();
  3369. TicketCacheLocked = FALSE;
  3370. KerbSetTicketCacheEntryTarget(
  3371. &RealTargetRealm,
  3372. LastTgt
  3373. );
  3374. DsysAssert( !TicketCacheLocked );
  3375. KerbReadLockTicketCache();
  3376. TicketCacheLocked = TRUE;
  3377. D_DebugLog((DEB_TRACE_REFERRAL, "Got two TGTs for same realm (%wZ), bailing out of referral loop\n",
  3378. &LastTgt->TargetDomainName));
  3379. break;
  3380. }
  3381. D_DebugLog((DEB_TRACE_REFERRAL, "Getting referral TGT for \n"));
  3382. D_KerbPrintKdcName((DEB_TRACE_REFERRAL, TargetTgtKdcName));
  3383. D_KerbPrintKdcName((DEB_TRACE_REFERRAL, TicketGrantingTicket->ServiceName));
  3384. DsysAssert( TicketCacheLocked );
  3385. KerbUnlockTicketCache();
  3386. TicketCacheLocked = FALSE;
  3387. //
  3388. // Cleanup old state
  3389. //
  3390. KerbFreeTgsReply(KdcReply);
  3391. KerbFreeKdcReplyBody(KdcReplyBody);
  3392. KdcReply = NULL;
  3393. KdcReplyBody = NULL;
  3394. D_DebugLog((DEB_TRACE_REFERRAL, "TGT TargetDomain %wZ\n", &TicketGrantingTicket->TargetDomainName));
  3395. D_DebugLog((DEB_TRACE_REFERRAL, "TGT Domain %wZ\n", &TicketGrantingTicket->DomainName));
  3396. Status = KerbGetTgsTicket(
  3397. &ClientRealm,
  3398. TicketGrantingTicket,
  3399. TargetTgtKdcName,
  3400. FALSE,
  3401. TicketOptions,
  3402. EncryptionType,
  3403. AuthorizationData,
  3404. NULL, // no pa data
  3405. NULL, // no tgt reply since target is krbtgt
  3406. NULL, // no evidence ticket
  3407. NULL, // let kdc determine end time
  3408. &KdcReply,
  3409. &KdcReplyBody,
  3410. &RetryFlags
  3411. );
  3412. if (!NT_SUCCESS(Status))
  3413. {
  3414. DebugLog((DEB_WARN,"Failed to get TGS ticket for service 0x%x :",
  3415. Status ));
  3416. KerbPrintKdcName(DEB_WARN, TargetTgtKdcName);
  3417. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  3418. //
  3419. // We want to map cross-domain failures to failures indicating
  3420. // that a KDC could not be found. This means that for Kerberos
  3421. // logons, the negotiate code will retry with a different package
  3422. //
  3423. // if (Status == STATUS_NO_TRUST_SAM_ACCOUNT)
  3424. // {
  3425. // Status = STATUS_NO_LOGON_SERVERS;
  3426. // }
  3427. goto Cleanup;
  3428. }
  3429. //
  3430. // Now we have a ticket - lets cache it
  3431. //
  3432. KerbReadLockLogonSessions(LogonSession);
  3433. Status = KerbCreateTicketCacheEntry(
  3434. KdcReply,
  3435. KdcReplyBody,
  3436. NULL, // no target name
  3437. NULL, // no targe realm
  3438. 0, // no flags
  3439. CacheTicket ? &PrimaryCredentials->AuthenticationTicketCache : NULL,
  3440. NULL, // no credential key
  3441. &TicketCacheEntry
  3442. );
  3443. KerbUnlockLogonSessions(LogonSession);
  3444. if (!NT_SUCCESS(Status))
  3445. {
  3446. goto Cleanup;
  3447. }
  3448. //
  3449. // Make sure we're not in a referral chasing loop.
  3450. //
  3451. // Basically, this means that the DomainName of
  3452. // the last TGT != the TargetDomain of the new
  3453. // tgt.
  3454. //
  3455. if (RtlEqualUnicodeString(&TicketGrantingTicket->DomainName, &TicketCacheEntry->TargetDomainName, TRUE) &&
  3456. !RtlEqualUnicodeString(&TicketGrantingTicket->TargetDomainName, &TicketCacheEntry->TargetDomainName, TRUE))
  3457. {
  3458. DebugLog((DEB_ERROR, "Referral loop TO : %wZ\n", &TicketCacheEntry->DomainName ));
  3459. DebugLog((DEB_ERROR, "TO : %wZ\n", &TicketCacheEntry->TargetDomainName));
  3460. DebugLog((DEB_ERROR, "TKE %p TGT %p\n", TicketCacheEntry, TicketGrantingTicket));
  3461. Status = STATUS_DOMAIN_TRUST_INCONSISTENT;
  3462. goto Cleanup;
  3463. }
  3464. if (LastTgt != NULL)
  3465. {
  3466. KerbDereferenceTicketCacheEntry(LastTgt);
  3467. LastTgt = NULL;
  3468. }
  3469. LastTgt = TicketGrantingTicket;
  3470. TicketGrantingTicket = TicketCacheEntry;
  3471. TicketCacheEntry = NULL;
  3472. DsysAssert( !TicketCacheLocked );
  3473. KerbReadLockTicketCache();
  3474. TicketCacheLocked = TRUE;
  3475. } // ** WHILE **
  3476. DsysAssert(TicketCacheLocked);
  3477. KerbUnlockTicketCache();
  3478. TicketCacheLocked = FALSE;
  3479. //
  3480. // Now we must have a TGT to the destination domain. Get a ticket
  3481. // to the service.
  3482. //
  3483. //
  3484. // Cleanup old state
  3485. //
  3486. KerbFreeTgsReply(KdcReply);
  3487. KerbFreeKdcReplyBody(KdcReplyBody);
  3488. KdcReply = NULL;
  3489. KdcReplyBody = NULL;
  3490. RetryFlags = 0;
  3491. Status = KerbGetTgsTicket(
  3492. &ClientRealm,
  3493. TicketGrantingTicket,
  3494. RealTargetName,
  3495. FALSE,
  3496. TicketOptions,
  3497. EncryptionType,
  3498. AuthorizationData,
  3499. NULL, // no pa data
  3500. TgtReply,
  3501. NULL, // no evidence ticket
  3502. NULL, // let kdc determine end time
  3503. &KdcReply,
  3504. &KdcReplyBody,
  3505. &RetryFlags
  3506. );
  3507. if (!NT_SUCCESS(Status))
  3508. {
  3509. //
  3510. // Check for bad option TGT purging
  3511. //
  3512. if (((RetryFlags & KERB_RETRY_WITH_NEW_TGT) != 0) && !TgtRetryMade)
  3513. {
  3514. DebugLog((DEB_WARN, "Doing TGT retry - %p\n", TicketGrantingTicket));
  3515. //
  3516. // Unlink && purge bad tgt
  3517. //
  3518. KerbRemoveTicketCacheEntry(TicketGrantingTicket); // free from list
  3519. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  3520. TicketGrantingTicket = NULL;
  3521. TgtRetryMade = TRUE;
  3522. goto TGTRetry;
  3523. }
  3524. DebugLog((DEB_WARN,"Failed to get TGS ticket for service 0x%x ",
  3525. Status ));
  3526. KerbPrintKdcName(DEB_WARN, RealTargetName);
  3527. DebugLog((DEB_WARN, "%ws, line %d\n", THIS_FILE, __LINE__));
  3528. goto Cleanup;
  3529. }
  3530. //
  3531. // Now that we are in the domain to which we were referred, check for referral
  3532. // info in the name
  3533. //
  3534. KerbFreeString(&RealTargetRealm);
  3535. Status = KerbGetReferralNames(
  3536. KdcReplyBody,
  3537. RealTargetName,
  3538. &RealTargetRealm
  3539. );
  3540. if (!NT_SUCCESS(Status))
  3541. {
  3542. goto Cleanup;
  3543. }
  3544. //
  3545. // If this is not a referral ticket, just cache it and return
  3546. // the cache entry.
  3547. //
  3548. if (RealTargetRealm.Length != 0)
  3549. {
  3550. //
  3551. // To prevent loops, we limit the number of referral we'll take
  3552. //
  3553. if (ReferralCount > KerbGlobalMaxReferralCount)
  3554. {
  3555. D_DebugLog((DEB_ERROR, "Maximum referral count exceeded for name: "));
  3556. D_KerbPrintKdcName((DEB_ERROR, RealTargetName));
  3557. Status = STATUS_MAX_REFERRALS_EXCEEDED;
  3558. goto Cleanup;
  3559. }
  3560. ReferralCount++;
  3561. //
  3562. // Cache the interdomain TGT
  3563. //
  3564. KerbReadLockLogonSessions(LogonSession);
  3565. Status = KerbCreateTicketCacheEntry(
  3566. KdcReply,
  3567. KdcReplyBody,
  3568. NULL, // no target name
  3569. NULL, // no target realm
  3570. 0, // no flags
  3571. CacheTicket ? &PrimaryCredentials->AuthenticationTicketCache : NULL,
  3572. NULL, // no credential key
  3573. &TicketCacheEntry
  3574. );
  3575. KerbUnlockLogonSessions(LogonSession);
  3576. if (RtlEqualUnicodeString(&TicketGrantingTicket->DomainName, &TicketCacheEntry->TargetDomainName, TRUE) &&
  3577. !RtlEqualUnicodeString(&TicketGrantingTicket->TargetDomainName, &TicketCacheEntry->TargetDomainName, TRUE))
  3578. {
  3579. DebugLog((DEB_ERROR, "Referral loop (2) FROM : %wZ\n", &TicketCacheEntry->DomainName ));
  3580. DebugLog((DEB_ERROR, "TO : %wZ\n", &TicketCacheEntry->TargetDomainName));
  3581. DebugLog((DEB_ERROR, "TKE %p tgt %p\n", TicketCacheEntry, TicketGrantingTicket));
  3582. Status = STATUS_DOMAIN_TRUST_INCONSISTENT;
  3583. goto Cleanup;
  3584. }
  3585. //
  3586. // Cleanup old state
  3587. //
  3588. KerbFreeTgsReply(KdcReply);
  3589. KerbFreeKdcReplyBody(KdcReplyBody);
  3590. KdcReply = NULL;
  3591. KdcReplyBody = NULL;
  3592. if (!NT_SUCCESS(Status))
  3593. {
  3594. goto Cleanup;
  3595. }
  3596. if (LastTgt != NULL)
  3597. {
  3598. KerbDereferenceTicketCacheEntry(LastTgt);
  3599. LastTgt = NULL;
  3600. }
  3601. LastTgt = TicketGrantingTicket;
  3602. TicketGrantingTicket = TicketCacheEntry;
  3603. TicketCacheEntry = NULL;
  3604. D_DebugLog((DEB_TRACE_REFERRAL, "Restart referral:%wZ", &RealTargetRealm));
  3605. goto ReferralRestart;
  3606. }
  3607. KerbReadLockLogonSessions(LogonSession);
  3608. Status = KerbCreateTicketCacheEntry(
  3609. KdcReply,
  3610. KdcReplyBody,
  3611. TargetName,
  3612. TargetDomainName,
  3613. TgtReply ? KERB_TICKET_CACHE_TKT_ENC_IN_SKEY : 0, // no flags
  3614. CacheTicket ? &PrimaryCredentials->ServerTicketCache : NULL,
  3615. NULL, // no credential key
  3616. &TicketCacheEntry
  3617. );
  3618. KerbUnlockLogonSessions(LogonSession);
  3619. if (!NT_SUCCESS(Status))
  3620. {
  3621. goto Cleanup;
  3622. }
  3623. *NewCacheEntry = TicketCacheEntry;
  3624. TicketCacheEntry = NULL;
  3625. Cleanup:
  3626. if ( NT_SUCCESS( Status ) )
  3627. {
  3628. //
  3629. // Generate the logon GUID
  3630. //
  3631. AuditStatus = KerbGetLogonGuid(
  3632. PrimaryCredentials,
  3633. KdcReplyBody,
  3634. &LogonGuid
  3635. );
  3636. //
  3637. // return the logon GUID if requested
  3638. //
  3639. if ( NT_SUCCESS(AuditStatus) && pLogonGuid )
  3640. {
  3641. *pLogonGuid = LogonGuid;
  3642. }
  3643. //
  3644. // generate SE_AUDITID_LOGON_USING_EXPLICIT_CREDENTIALS
  3645. // if explicit credentials were used for this logon.
  3646. //
  3647. if ( UsedCredentials )
  3648. {
  3649. (void) KerbGenerateAuditForLogonUsingExplicitCreds(
  3650. LogonSession,
  3651. PrimaryCredentials,
  3652. &LogonGuid,
  3653. TargetName
  3654. );
  3655. }
  3656. }
  3657. //
  3658. // Bad or unlocatable SPN -- Don't update if we got the value from the cache, though.
  3659. //
  3660. if (( TargetName->NameType == KRB_NT_SRV_INST ) &&
  3661. ( NT_SUCCESS(Status) || Status == STATUS_NO_TRUST_SAM_ACCOUNT ) &&
  3662. ( !CacheBasedFailure ))
  3663. {
  3664. NTSTATUS Tmp;
  3665. ULONG UpdateValue = KERB_SPN_UNKNOWN;
  3666. PUNICODE_STRING Realm = NULL;
  3667. if ( NT_SUCCESS( Status ))
  3668. {
  3669. Realm = &(*NewCacheEntry)->TargetDomainName;
  3670. UpdateValue = KERB_SPN_KNOWN;
  3671. }
  3672. Tmp = KerbUpdateSpnCacheEntry(
  3673. SpnCacheEntry,
  3674. TargetName,
  3675. PrimaryCredentials,
  3676. UpdateValue,
  3677. Realm
  3678. );
  3679. }
  3680. KerbFreeTgsReply( KdcReply );
  3681. KerbFreeKdcReplyBody( KdcReplyBody );
  3682. KerbFreeKdcName( &TargetTgtKdcName );
  3683. KerbFreeString( &RealTargetRealm );
  3684. KerbFreeString( &SpnTargetRealm );
  3685. KerbFreeKdcName(&RealTargetName);
  3686. if (TicketCacheLocked)
  3687. {
  3688. KerbUnlockTicketCache();
  3689. }
  3690. if (LogonSessionsLocked)
  3691. {
  3692. KerbUnlockLogonSessions(LogonSession);
  3693. }
  3694. KerbFreeString(&RealTargetRealm);
  3695. if (TicketGrantingTicket != NULL)
  3696. {
  3697. if (Status == STATUS_WRONG_PASSWORD)
  3698. {
  3699. KerbRemoveTicketCacheEntry(
  3700. TicketGrantingTicket
  3701. );
  3702. }
  3703. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  3704. }
  3705. if (LastTgt != NULL)
  3706. {
  3707. KerbDereferenceTicketCacheEntry(LastTgt);
  3708. LastTgt = NULL;
  3709. }
  3710. KerbFreeString(&ClientRealm);
  3711. //
  3712. // If we still have a pointer to the ticket cache entry, free it now.
  3713. //
  3714. if (TicketCacheEntry != NULL)
  3715. {
  3716. KerbRemoveTicketCacheEntry( TicketCacheEntry );
  3717. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  3718. }
  3719. return(Status);
  3720. }
  3721. #ifndef WIN32_CHICAGO
  3722. //+-------------------------------------------------------------------------
  3723. //
  3724. // Function: KerbCountPasswords
  3725. //
  3726. // Synopsis: Determines how many passwords are in a extra cred list.
  3727. //
  3728. // Effects:
  3729. //
  3730. // Arguments:
  3731. //
  3732. // Requires: Readlock of cred list
  3733. //
  3734. // Returns:
  3735. //
  3736. // Notes:
  3737. //
  3738. //
  3739. //--------------------------------------------------------------------------
  3740. ULONG
  3741. KerbCountPasswords(
  3742. PEXTRA_CRED_LIST ExtraCredentials
  3743. )
  3744. {
  3745. ULONG Count = 0;
  3746. PKERB_EXTRA_CRED ExtraCred = NULL;
  3747. PLIST_ENTRY ListEntry;
  3748. for ( ListEntry = ExtraCredentials->CredList.List.Flink ;
  3749. ( ListEntry != &ExtraCredentials->CredList.List );
  3750. ListEntry = ListEntry->Flink )
  3751. {
  3752. ExtraCred = CONTAINING_RECORD(ListEntry, KERB_EXTRA_CRED, ListEntry.Next);
  3753. if ( ExtraCred->Passwords )
  3754. {
  3755. Count++;
  3756. }
  3757. if ( ExtraCred->OldPasswords )
  3758. {
  3759. Count++;
  3760. }
  3761. }
  3762. return Count;
  3763. }
  3764. //+-------------------------------------------------------------------------
  3765. //
  3766. // Function: KerbBuildKeyList
  3767. //
  3768. // Synopsis: Used for conglomerating a list of keys for use in
  3769. // decrypting AP_REQ.
  3770. //
  3771. // Effects:
  3772. //
  3773. // Arguments:
  3774. //
  3775. // Requires: Readlock logon session
  3776. //
  3777. // Returns:
  3778. //
  3779. // Notes: In order to optimize perf, we order this 1. current password,
  3780. // 2. "extra" credentials, 3. old passwords
  3781. //
  3782. //
  3783. //--------------------------------------------------------------------------
  3784. NTSTATUS
  3785. KerbBuildKeyArray(
  3786. IN OPTIONAL PKERB_LOGON_SESSION LogonSession,
  3787. IN PKERB_PRIMARY_CREDENTIAL PrimaryCred,
  3788. IN ULONG Etype,
  3789. IN OUT PKERB_ENCRYPTION_KEY *KeyArray,
  3790. IN OUT PULONG KeyCount
  3791. )
  3792. {
  3793. NTSTATUS Status = STATUS_SUCCESS;
  3794. PLIST_ENTRY ListEntry;
  3795. PKERB_EXTRA_CRED ExtraCred = NULL;
  3796. ULONG LocalKeyCount = 0;
  3797. PKERB_ENCRYPTION_KEY Key = NULL;
  3798. Key = KerbGetKeyFromList(
  3799. PrimaryCred->Passwords,
  3800. Etype
  3801. );
  3802. if ( Key == NULL )
  3803. {
  3804. Status = SEC_E_NO_CREDENTIALS;
  3805. goto cleanup;
  3806. }
  3807. KeyArray[LocalKeyCount++] = Key;
  3808. //
  3809. // Scan the logon session for extra creds
  3810. //
  3811. if (ARGUMENT_PRESENT( LogonSession ))
  3812. {
  3813. for ( ListEntry = LogonSession->ExtraCredentials.CredList.List.Flink ;
  3814. ( ListEntry != &LogonSession->ExtraCredentials.CredList.List );
  3815. ListEntry = ListEntry->Flink )
  3816. {
  3817. ExtraCred = CONTAINING_RECORD(ListEntry, KERB_EXTRA_CRED, ListEntry.Next);
  3818. Key = KerbGetKeyFromList(
  3819. ExtraCred->Passwords,
  3820. Etype
  3821. );
  3822. if ( Key == NULL )
  3823. {
  3824. continue;
  3825. }
  3826. KeyArray[LocalKeyCount++] = Key;
  3827. Key = KerbGetKeyFromList(
  3828. ExtraCred->OldPasswords,
  3829. Etype
  3830. );
  3831. if ( Key == NULL )
  3832. {
  3833. continue;
  3834. }
  3835. KeyArray[LocalKeyCount++] = Key;
  3836. }
  3837. }
  3838. //
  3839. // Fill in old password. If its NULL, we haven't allocated space
  3840. //
  3841. if ( PrimaryCred->OldPasswords != NULL )
  3842. {
  3843. Key = KerbGetKeyFromList(
  3844. PrimaryCred->OldPasswords,
  3845. Etype
  3846. );
  3847. if ( Key != NULL )
  3848. {
  3849. KeyArray[LocalKeyCount++] = Key;
  3850. }
  3851. }
  3852. DsysAssert(LocalKeyCount <= (*KeyCount));
  3853. *KeyCount = LocalKeyCount;
  3854. cleanup:
  3855. return Status;
  3856. }
  3857. //+-------------------------------------------------------------------------
  3858. //
  3859. // Function: KerbHaveKeyMaterials
  3860. //
  3861. // Synopsis: Used to check for conglomerating a list of keys for use in
  3862. // decrypting AP_REQ.
  3863. //
  3864. // Effects:
  3865. //
  3866. // Arguments:
  3867. //
  3868. // Requires: Readlock logon session
  3869. //
  3870. // Returns:
  3871. //
  3872. // Notes: In order to optimize perf, we order this 1. current password,
  3873. // 2. "extra" credentials, 3. old passwords
  3874. //
  3875. //
  3876. //--------------------------------------------------------------------------
  3877. BOOL
  3878. KerbHaveKeyMaterials(
  3879. IN OPTIONAL PKERB_LOGON_SESSION LogonSession,
  3880. IN PKERB_PRIMARY_CREDENTIAL PrimaryCred
  3881. )
  3882. {
  3883. return (PrimaryCred->Passwords != NULL) ||
  3884. (LogonSession && LogonSession->ExtraCredentials.Count) ||
  3885. (PrimaryCred->OldPasswords != NULL);
  3886. }
  3887. //+-------------------------------------------------------------------------
  3888. //
  3889. // Function: KerbCreateApRequest
  3890. //
  3891. // Synopsis: builds an AP request message
  3892. //
  3893. // Effects: allocates memory with MIDL_user_allocate
  3894. //
  3895. // Arguments: ClientName - Name of client
  3896. // ClientRealm - Realm of client
  3897. // SessionKey - Session key for the ticket
  3898. // SubSessionKey - obtional sub Session key for the authenticator
  3899. // Nonce - Nonce to use in authenticator
  3900. // pAuthenticatorTime - time stamp used for AP request (generated in KerbCreateAuthenticator)
  3901. // ServiceTicket - Ticket for service to put in request
  3902. // ApOptions - Options to stick in AP request
  3903. // GssChecksum - Checksum for GSS compatibility containing
  3904. // context options and delegation info.
  3905. // KdcRequest - if TRUE, this is an AP request for a TGS req
  3906. // ServerSkewTime - Optional skew of server's time
  3907. // RequestSize - Receives size of the marshalled request
  3908. // Request - Receives the marshalled request
  3909. //
  3910. // Requires:
  3911. //
  3912. // Returns: KDC_ERR_NONE on success, KRB_ERR_GENERIC on memory or
  3913. // marshalling failure
  3914. //
  3915. // Notes:
  3916. //
  3917. //
  3918. //--------------------------------------------------------------------------
  3919. KERBERR
  3920. KerbCreateApRequest(
  3921. IN PKERB_INTERNAL_NAME ClientName,
  3922. IN PUNICODE_STRING ClientRealm,
  3923. IN PKERB_ENCRYPTION_KEY SessionKey,
  3924. IN OPTIONAL PKERB_ENCRYPTION_KEY SubSessionKey,
  3925. IN ULONG Nonce,
  3926. OUT OPTIONAL PTimeStamp pAuthenticatorTime,
  3927. IN PKERB_TICKET ServiceTicket,
  3928. IN ULONG ApOptions,
  3929. IN OPTIONAL PKERB_CHECKSUM GssChecksum,
  3930. IN OPTIONAL PTimeStamp ServerSkewTime,
  3931. IN BOOLEAN KdcRequest,
  3932. OUT PULONG RequestSize,
  3933. OUT PUCHAR * Request
  3934. )
  3935. {
  3936. KERBERR KerbErr = KDC_ERR_NONE;
  3937. KERB_AP_REQUEST ApRequest;
  3938. ULONG ApFlags;
  3939. *Request = NULL;
  3940. RtlZeroMemory(
  3941. &ApRequest,
  3942. sizeof(KERB_AP_REQUEST)
  3943. );
  3944. //
  3945. // Fill in the AP request structure.
  3946. //
  3947. ApRequest.version = KERBEROS_VERSION;
  3948. ApRequest.message_type = KRB_AP_REQ;
  3949. ApFlags = KerbConvertUlongToFlagUlong(ApOptions);
  3950. ApRequest.ap_options.value = (PUCHAR) &ApFlags;
  3951. ApRequest.ap_options.length = sizeof(ULONG) * 8;
  3952. ApRequest.ticket = *ServiceTicket;
  3953. //
  3954. // Create the authenticator for the request
  3955. //
  3956. KerbErr = KerbCreateAuthenticator(
  3957. SessionKey,
  3958. Nonce,
  3959. pAuthenticatorTime,
  3960. ClientName,
  3961. ClientRealm,
  3962. ServerSkewTime,
  3963. SubSessionKey,
  3964. GssChecksum,
  3965. KdcRequest,
  3966. &ApRequest.authenticator
  3967. );
  3968. if (!KERB_SUCCESS(KerbErr))
  3969. {
  3970. DebugLog((DEB_ERROR,"Failed to build authenticator: 0x%x\n",
  3971. KerbErr ));
  3972. goto Cleanup;
  3973. }
  3974. //
  3975. // Now marshall the request
  3976. //
  3977. KerbErr = KerbPackApRequest(
  3978. &ApRequest,
  3979. RequestSize,
  3980. Request
  3981. );
  3982. if (!KERB_SUCCESS(KerbErr))
  3983. {
  3984. DebugLog((DEB_ERROR,"Failed to pack AP request: 0x%x\n",KerbErr));
  3985. goto Cleanup;
  3986. }
  3987. Cleanup:
  3988. if (ApRequest.authenticator.cipher_text.value != NULL)
  3989. {
  3990. MIDL_user_free(ApRequest.authenticator.cipher_text.value);
  3991. }
  3992. return(KerbErr);
  3993. }
  3994. //+-------------------------------------------------------------------------
  3995. //
  3996. // Function: KerbVerifyApRequest
  3997. //
  3998. // Synopsis: Verifies that an AP request message is valid
  3999. //
  4000. // Effects: decrypts ticket in AP request
  4001. //
  4002. // Arguments: RequestMessage - Marshalled AP request message
  4003. // RequestSize - Size in bytes of request message
  4004. // LogonSession - Logon session for server
  4005. // Credential - Credential for server containing
  4006. // supplied credentials
  4007. // UseSuppliedCreds - If TRUE, use creds from credential
  4008. // ApRequest - Receives unmarshalled AP request
  4009. // NewTicket - Receives ticket from AP request
  4010. // NewAuthenticator - receives new authenticator from AP request
  4011. // SessionKey -receives the session key from the ticket
  4012. // ContextFlags - receives the requested flags for the
  4013. // context.
  4014. // pChannelBindings - pChannelBindings supplied by app to check
  4015. // against hashed ones in AP_REQ
  4016. //
  4017. // Requires:
  4018. //
  4019. // Returns:
  4020. //
  4021. // Notes:
  4022. //
  4023. //
  4024. //--------------------------------------------------------------------------
  4025. NTSTATUS
  4026. KerbVerifyApRequest(
  4027. IN OPTIONAL PKERB_CONTEXT Context,
  4028. IN PUCHAR RequestMessage,
  4029. IN ULONG RequestSize,
  4030. IN PKERB_LOGON_SESSION LogonSession,
  4031. IN PKERB_CREDENTIAL Credential,
  4032. IN BOOLEAN UseSuppliedCreds,
  4033. IN BOOLEAN CheckForReplay,
  4034. OUT PKERB_AP_REQUEST * ApRequest,
  4035. OUT PKERB_ENCRYPTED_TICKET * NewTicket,
  4036. OUT PKERB_AUTHENTICATOR * NewAuthenticator,
  4037. OUT PKERB_ENCRYPTION_KEY SessionKey,
  4038. OUT PKERB_ENCRYPTION_KEY TicketKey,
  4039. OUT PKERB_ENCRYPTION_KEY ServerKey,
  4040. OUT PULONG ContextFlags,
  4041. OUT PULONG ContextAttributes,
  4042. OUT PKERBERR ReturnKerbErr,
  4043. IN PSEC_CHANNEL_BINDINGS pChannelBindings
  4044. )
  4045. {
  4046. KERBERR KerbErr = KDC_ERR_NONE, TmpErr = KDC_ERR_NONE;
  4047. NTSTATUS Status = STATUS_SUCCESS;
  4048. PKERB_AP_REQUEST Request = NULL;
  4049. UNICODE_STRING ServerName[3] = {0};
  4050. ULONG NameCount = 0;
  4051. BOOLEAN UseSubKey = FALSE;
  4052. BOOLEAN LockAcquired = FALSE, UsedExtraCreds = FALSE;
  4053. PKERB_GSS_CHECKSUM GssChecksum;
  4054. BOOLEAN TicketCacheLocked = FALSE;
  4055. PKERB_ENCRYPTION_KEY* KeyArray = NULL;
  4056. ULONG KeyCount = 0;
  4057. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials;
  4058. ULONG StrippedRequestSize = RequestSize;
  4059. PUCHAR StrippedRequest = RequestMessage;
  4060. PKERB_TICKET_CACHE_ENTRY CacheEntry = NULL;
  4061. ULONG i, ApOptions = 0;
  4062. ULONG BindHash[4];
  4063. *ApRequest = NULL;
  4064. *ContextFlags = 0;
  4065. *NewTicket = NULL;
  4066. *NewAuthenticator = NULL;
  4067. *ReturnKerbErr = KDC_ERR_NONE;
  4068. RtlZeroMemory(
  4069. SessionKey,
  4070. sizeof(KERB_ENCRYPTION_KEY)
  4071. );
  4072. *TicketKey = *SessionKey;
  4073. *ServerKey = *SessionKey;
  4074. //
  4075. // First unpack the KDC request.
  4076. //
  4077. //
  4078. // Verify the GSSAPI header
  4079. //
  4080. D_DebugLog((DEB_TRACE, "KerbVerifyApRequest UseSuppliedCreds %s, CheckForReplay %s\n",
  4081. UseSuppliedCreds ? "true" : "false", CheckForReplay ? "true" : "false"));
  4082. if (!g_verify_token_header(
  4083. (gss_OID) gss_mech_krb5_new,
  4084. (INT *) &StrippedRequestSize,
  4085. &StrippedRequest,
  4086. KG_TOK_CTX_AP_REQ,
  4087. RequestSize
  4088. ))
  4089. {
  4090. StrippedRequestSize = RequestSize;
  4091. StrippedRequest = RequestMessage;
  4092. //
  4093. // Check if this is user-to-user kerberos
  4094. //
  4095. if (g_verify_token_header(
  4096. gss_mech_krb5_u2u,
  4097. (INT *) &StrippedRequestSize,
  4098. &StrippedRequest,
  4099. KG_TOK_CTX_TGT_REQ,
  4100. RequestSize))
  4101. {
  4102. //
  4103. // Return now because there is no AP request. Return a distinct
  4104. // success code so the caller knows to reparse the request as
  4105. // a TGT request.
  4106. //
  4107. D_DebugLog((DEB_TRACE_U2U, "KerbVerifyApRequest got TGT reqest\n"));
  4108. return (STATUS_REPARSE_OBJECT);
  4109. }
  4110. else
  4111. {
  4112. StrippedRequestSize = RequestSize;
  4113. StrippedRequest = RequestMessage;
  4114. if (!g_verify_token_header( // check for a user-to-user AP request
  4115. gss_mech_krb5_u2u,
  4116. (INT *) &StrippedRequestSize,
  4117. &StrippedRequest,
  4118. KG_TOK_CTX_AP_REQ,
  4119. RequestSize))
  4120. {
  4121. //
  4122. // BUG 454895: remove when not needed for compatibility
  4123. //
  4124. //
  4125. // if that didn't work, just use the token as it is.
  4126. //
  4127. StrippedRequest = RequestMessage;
  4128. StrippedRequestSize = RequestSize;
  4129. D_DebugLog((DEB_TRACE,"KerbVerifyApRequest didn't find GSS header on AP request\n"));
  4130. }
  4131. }
  4132. }
  4133. KerbErr = KerbUnpackApRequest(
  4134. StrippedRequest,
  4135. StrippedRequestSize,
  4136. &Request
  4137. );
  4138. if (!KERB_SUCCESS(KerbErr))
  4139. {
  4140. D_DebugLog((DEB_ERROR,"Failed to unpack AP request: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
  4141. Status = STATUS_INSUFFICIENT_RESOURCES;
  4142. goto Cleanup;
  4143. }
  4144. //
  4145. // Check for a null session
  4146. //
  4147. if ((Request->version == KERBEROS_VERSION) &&
  4148. (Request->message_type == KRB_AP_REQ) &&
  4149. (Request->ticket.encrypted_part.cipher_text.length == 1) &&
  4150. (*Request->ticket.encrypted_part.cipher_text.value == '\0') &&
  4151. (Request->authenticator.cipher_text.length == 1) &&
  4152. (*Request->authenticator.cipher_text.value == '\0'))
  4153. {
  4154. //
  4155. // We have a null session. Not much to do here.
  4156. //
  4157. Status = STATUS_SUCCESS;
  4158. RtlZeroMemory(
  4159. SessionKey,
  4160. sizeof(KERB_ENCRYPTION_KEY)
  4161. );
  4162. *ContextFlags |= ISC_RET_NULL_SESSION;
  4163. goto Cleanup;
  4164. }
  4165. DsysAssert( !LockAcquired );
  4166. KerbReadLockLogonSessions(LogonSession);
  4167. LockAcquired = TRUE;
  4168. if ( UseSuppliedCreds )
  4169. {
  4170. PrimaryCredentials = Credential->SuppliedCredentials;
  4171. }
  4172. else
  4173. {
  4174. PrimaryCredentials = &LogonSession->PrimaryCredentials;
  4175. KerbLockList(&LogonSession->ExtraCredentials.CredList);
  4176. if ( LogonSession->ExtraCredentials.Count )
  4177. {
  4178. KeyCount += KerbCountPasswords(&LogonSession->ExtraCredentials);
  4179. UsedExtraCreds = TRUE;
  4180. }
  4181. else
  4182. {
  4183. KerbUnlockList(&LogonSession->ExtraCredentials.CredList);
  4184. }
  4185. }
  4186. //
  4187. // Check for existence of a password and use_session_key
  4188. //
  4189. ApOptions = KerbConvertFlagsToUlong( &Request->ap_options);
  4190. D_DebugLog((DEB_TRACE, "KerbVerifyApRequest AP options = 0x%x\n", ApOptions));
  4191. if ((ApOptions & KERB_AP_OPTIONS_use_session_key) == 0)
  4192. {
  4193. if (!KerbHaveKeyMaterials((UsedExtraCreds ? LogonSession : NULL), PrimaryCredentials))
  4194. {
  4195. Status = SEC_E_NO_CREDENTIALS;
  4196. *ReturnKerbErr = KRB_AP_ERR_USER_TO_USER_REQUIRED;
  4197. goto Cleanup;
  4198. }
  4199. else
  4200. {
  4201. //
  4202. // If someone's added credentials to a non-joined machine,then
  4203. // there's a possibility that we don't have a pwd w/ our primary
  4204. // credentials structure.
  4205. //
  4206. if (PrimaryCredentials->Passwords != NULL)
  4207. {
  4208. KeyCount++;
  4209. }
  4210. if (PrimaryCredentials->OldPasswords != NULL)
  4211. {
  4212. KeyCount++;
  4213. }
  4214. }
  4215. }
  4216. if (!KERB_SUCCESS(KerbBuildFullServiceName(
  4217. &PrimaryCredentials->DomainName,
  4218. &PrimaryCredentials->UserName,
  4219. &ServerName[NameCount++]
  4220. )))
  4221. {
  4222. Status = STATUS_INSUFFICIENT_RESOURCES;
  4223. goto Cleanup;
  4224. }
  4225. ServerName[NameCount++] = PrimaryCredentials->UserName;
  4226. if (Credential->CredentialName.Length != 0)
  4227. {
  4228. ServerName[NameCount++] = Credential->CredentialName;
  4229. }
  4230. //
  4231. // Now Check the ticket
  4232. //
  4233. //
  4234. // If this is use_session key, get the key from the tgt
  4235. //
  4236. if ((ApOptions & KERB_AP_OPTIONS_use_session_key) != 0)
  4237. {
  4238. D_DebugLog((DEB_TRACE_U2U, "KerbVerifyApRequest verifying ticket with TGT session key\n"));
  4239. *ContextAttributes |= KERB_CONTEXT_USER_TO_USER;
  4240. //
  4241. // If we have a context, try to get the TGT from it.
  4242. //
  4243. if (ARGUMENT_PRESENT(Context))
  4244. {
  4245. KerbReadLockContexts();
  4246. CacheEntry = Context->TicketCacheEntry;
  4247. KerbUnlockContexts();
  4248. }
  4249. //
  4250. // If there is no TGT in the context, try getting one from the
  4251. // logon session.
  4252. //
  4253. if (CacheEntry == NULL)
  4254. {
  4255. //
  4256. // Locate the TGT for the principal, this can never happen!
  4257. //
  4258. DebugLog((DEB_WARN, "KerbVerifyApRequest tried to request TGT on credential without a TGT\n"));
  4259. CacheEntry = KerbLocateTicketCacheEntryByRealm(
  4260. &PrimaryCredentials->AuthenticationTicketCache,
  4261. &PrimaryCredentials->DomainName, // get initial ticket
  4262. 0
  4263. );
  4264. }
  4265. else
  4266. {
  4267. KerbReferenceTicketCacheEntry(
  4268. CacheEntry
  4269. );
  4270. }
  4271. if (CacheEntry == NULL)
  4272. {
  4273. DebugLog((DEB_ERROR, "KerbVerifyApRequest tried to request TGT on credential without a TGT\n"));
  4274. *ReturnKerbErr = KRB_AP_ERR_NO_TGT;
  4275. Status = SEC_E_NO_CREDENTIALS;
  4276. goto Cleanup;
  4277. }
  4278. DsysAssert( !TicketCacheLocked );
  4279. KerbReadLockTicketCache();
  4280. TicketCacheLocked = TRUE;
  4281. KeyCount = 1;
  4282. SafeAllocaAllocate(KeyArray, ( sizeof(PKERB_ENCRYPTION_KEY) * KeyCount ));
  4283. if (KeyArray == NULL)
  4284. {
  4285. Status = STATUS_INSUFFICIENT_RESOURCES;
  4286. goto Cleanup;
  4287. }
  4288. KeyArray[0] = &CacheEntry->SessionKey;
  4289. }
  4290. else
  4291. {
  4292. SafeAllocaAllocate(KeyArray, ( sizeof(PKERB_ENCRYPTION_KEY) * KeyCount ));
  4293. if (KeyArray == NULL)
  4294. {
  4295. Status = STATUS_INSUFFICIENT_RESOURCES;
  4296. goto Cleanup;
  4297. }
  4298. RtlZeroMemory(KeyArray, ( sizeof(PKERB_ENCRYPTION_KEY) * KeyCount ));
  4299. Status = KerbBuildKeyArray(
  4300. (UsedExtraCreds ? LogonSession : NULL),
  4301. PrimaryCredentials,
  4302. Request->ticket.encrypted_part.encryption_type,
  4303. KeyArray,
  4304. &KeyCount
  4305. );
  4306. if (!NT_SUCCESS(Status))
  4307. {
  4308. D_DebugLog((DEB_ERROR, "Couldn't find server key of type 0x%x. %ws, line %d\n",
  4309. Request->ticket.encrypted_part.encryption_type, THIS_FILE, __LINE__ ));
  4310. goto Cleanup;
  4311. }
  4312. }
  4313. for ( i = 0; i < KeyCount ; i++ )
  4314. {
  4315. TmpErr = KerbCheckTicket(
  4316. &Request->ticket,
  4317. &Request->authenticator,
  4318. KeyArray[i],
  4319. Authenticators,
  4320. &KerbGlobalSkewTime,
  4321. NameCount,
  4322. ServerName,
  4323. &PrimaryCredentials->DomainName,
  4324. CheckForReplay,
  4325. FALSE, // not a KDC request
  4326. NewTicket,
  4327. NewAuthenticator,
  4328. TicketKey,
  4329. SessionKey,
  4330. &UseSubKey
  4331. );
  4332. if (KERB_SUCCESS( TmpErr ))
  4333. {
  4334. D_DebugLog((DEB_TRACE, "KerbVerifyApRequest ticket check succeeded using key %x\n", i));
  4335. break;
  4336. }
  4337. else if ( TmpErr == KRB_AP_ERR_MODIFIED )
  4338. {
  4339. continue;
  4340. }
  4341. else
  4342. {
  4343. Status = KerbMapKerbError( TmpErr );
  4344. *ReturnKerbErr = TmpErr;
  4345. goto Cleanup;
  4346. }
  4347. }
  4348. if (!KERB_SUCCESS( TmpErr ))
  4349. {
  4350. DebugLog((DEB_ERROR, "KerbVerifyApRequest failed to check ticket %x %p\n", TmpErr, Request));
  4351. Status = KerbMapKerbError( TmpErr );
  4352. *ReturnKerbErr = TmpErr;
  4353. goto Cleanup;
  4354. }
  4355. *ReturnKerbErr = TmpErr;
  4356. //
  4357. // Copy the key that was used.
  4358. //
  4359. if (!KERB_SUCCESS(KerbDuplicateKey(
  4360. ServerKey,
  4361. KeyArray[i])))
  4362. {
  4363. Status = STATUS_INSUFFICIENT_RESOURCES;
  4364. goto Cleanup;
  4365. }
  4366. if (UsedExtraCreds)
  4367. {
  4368. KerbUnlockList(&LogonSession->ExtraCredentials.CredList);
  4369. UsedExtraCreds = FALSE;
  4370. }
  4371. //
  4372. // Get the context flags out of the authenticator and the AP request
  4373. //
  4374. if ((((*NewAuthenticator)->bit_mask & checksum_present) != 0) &&
  4375. ((*NewAuthenticator)->checksum.checksum_type == GSS_CHECKSUM_TYPE) &&
  4376. ((*NewAuthenticator)->checksum.checksum.length >= GSS_CHECKSUM_SIZE))
  4377. {
  4378. GssChecksum = (PKERB_GSS_CHECKSUM) (*NewAuthenticator)->checksum.checksum.value;
  4379. if (GssChecksum->GssFlags & GSS_C_MUTUAL_FLAG)
  4380. {
  4381. //
  4382. // Make sure this is also present in the AP request
  4383. //
  4384. if ((ApOptions & KERB_AP_OPTIONS_mutual_required) == 0)
  4385. {
  4386. DebugLog((DEB_ERROR,"KerbVerifyApRequest sent AP_mutual_req but not GSS_C_MUTUAL_FLAG. %ws, line %d\n", THIS_FILE, __LINE__));
  4387. Status = STATUS_INVALID_PARAMETER;
  4388. goto Cleanup;
  4389. }
  4390. }
  4391. if (GssChecksum->GssFlags & GSS_C_DCE_STYLE)
  4392. {
  4393. *ContextFlags |= ISC_RET_USED_DCE_STYLE;
  4394. }
  4395. if (GssChecksum->GssFlags & GSS_C_REPLAY_FLAG)
  4396. {
  4397. *ContextFlags |= ISC_RET_REPLAY_DETECT;
  4398. }
  4399. if (GssChecksum->GssFlags & GSS_C_SEQUENCE_FLAG)
  4400. {
  4401. *ContextFlags |= ISC_RET_SEQUENCE_DETECT;
  4402. }
  4403. if (GssChecksum->GssFlags & GSS_C_CONF_FLAG)
  4404. {
  4405. *ContextFlags |= (ISC_RET_CONFIDENTIALITY |
  4406. ISC_RET_INTEGRITY |
  4407. ISC_RET_SEQUENCE_DETECT |
  4408. ISC_RET_REPLAY_DETECT );
  4409. }
  4410. if (GssChecksum->GssFlags & GSS_C_INTEG_FLAG)
  4411. {
  4412. *ContextFlags |= ISC_RET_INTEGRITY;
  4413. }
  4414. if (GssChecksum->GssFlags & GSS_C_IDENTIFY_FLAG)
  4415. {
  4416. *ContextFlags |= ISC_RET_IDENTIFY;
  4417. }
  4418. if (GssChecksum->GssFlags & GSS_C_DELEG_FLAG)
  4419. {
  4420. *ContextFlags |= ISC_RET_DELEGATE;
  4421. }
  4422. if (GssChecksum->GssFlags & GSS_C_EXTENDED_ERROR_FLAG)
  4423. {
  4424. *ContextFlags |= ISC_RET_EXTENDED_ERROR;
  4425. }
  4426. if( pChannelBindings != NULL )
  4427. {
  4428. Status = KerbComputeGssBindHash( pChannelBindings, (PUCHAR)BindHash );
  4429. if( !NT_SUCCESS(Status) )
  4430. {
  4431. goto Cleanup;
  4432. }
  4433. if( RtlCompareMemory( BindHash,
  4434. GssChecksum->BindHash,
  4435. GssChecksum->BindLength )
  4436. != GssChecksum->BindLength )
  4437. {
  4438. Status = STATUS_BAD_BINDINGS;
  4439. goto Cleanup;
  4440. }
  4441. }
  4442. }
  4443. if ((ApOptions & KERB_AP_OPTIONS_use_session_key) != 0)
  4444. {
  4445. *ContextFlags |= ISC_RET_USE_SESSION_KEY;
  4446. }
  4447. if ((ApOptions & KERB_AP_OPTIONS_mutual_required) != 0)
  4448. {
  4449. *ContextFlags |= ISC_RET_MUTUAL_AUTH;
  4450. }
  4451. *ApRequest = Request;
  4452. Request = NULL;
  4453. Cleanup:
  4454. if (TicketCacheLocked)
  4455. {
  4456. KerbUnlockTicketCache();
  4457. }
  4458. if (CacheEntry)
  4459. {
  4460. KerbDereferenceTicketCacheEntry(CacheEntry);
  4461. }
  4462. if (UsedExtraCreds)
  4463. {
  4464. KerbUnlockList(&LogonSession->ExtraCredentials.CredList);
  4465. }
  4466. if (LockAcquired)
  4467. {
  4468. KerbUnlockLogonSessions(LogonSession);
  4469. }
  4470. if (Request != NULL)
  4471. {
  4472. //
  4473. // If the client didn't want mutual-auth, then it won't be expecting
  4474. // a response message so don't bother with the kerb error. By setting
  4475. // KerbErr to KDC_ERR_NONE we won't send a message back to the client.
  4476. //
  4477. if ( (ApOptions & KERB_AP_OPTIONS_mutual_required) == 0 )
  4478. {
  4479. *ReturnKerbErr = KDC_ERR_NONE;
  4480. }
  4481. }
  4482. if ( KeyArray != NULL )
  4483. {
  4484. SafeAllocaFree( KeyArray );
  4485. }
  4486. KerbFreeApRequest(Request);
  4487. KerbFreeString(&ServerName[0]);
  4488. if (!NT_SUCCESS(Status))
  4489. {
  4490. KerbFreeKey(TicketKey);
  4491. }
  4492. return (Status);
  4493. }
  4494. #endif // WIN32_CHICAGO
  4495. //+-------------------------------------------------------------------------
  4496. //
  4497. // Function: KerbMarshallApReply
  4498. //
  4499. // Synopsis: Takes a reply and reply body and encrypts and marshalls them
  4500. // into a return message
  4501. //
  4502. // Effects: Allocates output buffer
  4503. //
  4504. // Arguments: Reply - The outer reply to marshall
  4505. // ReplyBody - The reply body to marshall
  4506. // SessionKey - Session key to encrypt reply
  4507. // ContextFlags - Flags for context
  4508. // PackedReply - Recives marshalled reply buffer
  4509. // PackedReplySize - Receives size in bytes of marshalled reply
  4510. //
  4511. // Requires:
  4512. //
  4513. // Returns: STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES
  4514. //
  4515. // Notes:
  4516. //
  4517. //
  4518. //--------------------------------------------------------------------------
  4519. NTSTATUS
  4520. KerbMarshallApReply(
  4521. IN PKERB_AP_REPLY Reply,
  4522. IN PKERB_ENCRYPTED_AP_REPLY ReplyBody,
  4523. IN PKERB_ENCRYPTION_KEY SessionKey,
  4524. IN ULONG ContextFlags,
  4525. IN ULONG ContextAttributes,
  4526. OUT PUCHAR * PackedReply,
  4527. OUT PULONG PackedReplySize
  4528. )
  4529. {
  4530. NTSTATUS Status = STATUS_SUCCESS;
  4531. ULONG PackedApReplySize;
  4532. PUCHAR PackedApReply = NULL;
  4533. ULONG ReplySize;
  4534. PUCHAR ReplyWithHeader = NULL;
  4535. PUCHAR ReplyStart;
  4536. KERBERR KerbErr;
  4537. gss_OID_desc * MechId;
  4538. if (!KERB_SUCCESS(KerbPackApReplyBody(
  4539. ReplyBody,
  4540. &PackedApReplySize,
  4541. &PackedApReply
  4542. )))
  4543. {
  4544. Status = STATUS_INSUFFICIENT_RESOURCES;
  4545. goto Cleanup;
  4546. }
  4547. //
  4548. // Now encrypt the response
  4549. //
  4550. KerbErr = KerbAllocateEncryptionBufferWrapper(
  4551. SessionKey->keytype,
  4552. PackedApReplySize,
  4553. &Reply->encrypted_part.cipher_text.length,
  4554. &Reply->encrypted_part.cipher_text.value
  4555. );
  4556. if (!KERB_SUCCESS(KerbErr))
  4557. {
  4558. D_DebugLog((DEB_ERROR,"Failed to get encryption overhead. 0x%x. %ws, line %d\n", KerbErr, THIS_FILE, __LINE__));
  4559. Status = KerbMapKerbError(KerbErr);
  4560. goto Cleanup;
  4561. }
  4562. if (!KERB_SUCCESS(KerbEncryptDataEx(
  4563. &Reply->encrypted_part,
  4564. PackedApReplySize,
  4565. PackedApReply,
  4566. KERB_NO_KEY_VERSION,
  4567. KERB_AP_REP_SALT,
  4568. SessionKey
  4569. )))
  4570. {
  4571. D_DebugLog((DEB_ERROR,"Failed to encrypt AP Reply. %ws, line %d\n", THIS_FILE, __LINE__));
  4572. Status = STATUS_INSUFFICIENT_RESOURCES;
  4573. goto Cleanup;
  4574. }
  4575. //
  4576. // Now pack the reply into the output buffer
  4577. //
  4578. if (!KERB_SUCCESS(KerbPackApReply(
  4579. Reply,
  4580. PackedReplySize,
  4581. PackedReply
  4582. )))
  4583. {
  4584. Status = STATUS_INSUFFICIENT_RESOURCES;
  4585. goto Cleanup;
  4586. }
  4587. //
  4588. // If we aren't doing DCE style, add in the GSS token headers now
  4589. //
  4590. if ((ContextFlags & ISC_RET_USED_DCE_STYLE) != 0)
  4591. {
  4592. goto Cleanup;
  4593. }
  4594. if ((ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0)
  4595. {
  4596. MechId = gss_mech_krb5_u2u;
  4597. }
  4598. else
  4599. {
  4600. MechId = gss_mech_krb5_new;
  4601. }
  4602. ReplySize = g_token_size(
  4603. MechId,
  4604. *PackedReplySize);
  4605. ReplyWithHeader = (PUCHAR) KerbAllocate(ReplySize);
  4606. if (ReplyWithHeader == NULL)
  4607. {
  4608. Status = STATUS_INSUFFICIENT_RESOURCES;
  4609. goto Cleanup;
  4610. }
  4611. //
  4612. // the g_make_token_header will reset this to point to the end of the
  4613. // header
  4614. //
  4615. ReplyStart = ReplyWithHeader;
  4616. g_make_token_header(
  4617. MechId,
  4618. *PackedReplySize,
  4619. &ReplyStart,
  4620. KG_TOK_CTX_AP_REP
  4621. );
  4622. DsysAssert(ReplyStart - ReplyWithHeader + *PackedReplySize == ReplySize);
  4623. RtlCopyMemory(
  4624. ReplyStart,
  4625. *PackedReply,
  4626. *PackedReplySize
  4627. );
  4628. KerbFree(*PackedReply);
  4629. *PackedReply = ReplyWithHeader;
  4630. *PackedReplySize = ReplySize;
  4631. ReplyWithHeader = NULL;
  4632. Cleanup:
  4633. if (Reply->encrypted_part.cipher_text.value != NULL)
  4634. {
  4635. MIDL_user_free(Reply->encrypted_part.cipher_text.value);
  4636. Reply->encrypted_part.cipher_text.value = NULL;
  4637. }
  4638. if (PackedApReply != NULL)
  4639. {
  4640. MIDL_user_free(PackedApReply);
  4641. }
  4642. if (!NT_SUCCESS(Status) && (*PackedReply != NULL))
  4643. {
  4644. KerbFree(*PackedReply);
  4645. *PackedReply = NULL;
  4646. }
  4647. return(Status);
  4648. }
  4649. //+-------------------------------------------------------------------------
  4650. //
  4651. // Function: KerbBuildApReply
  4652. //
  4653. // Synopsis: Builds an AP reply message if mutual authentication is
  4654. // desired.
  4655. //
  4656. // Effects: InternalAuthenticator - Authenticator from the AP request
  4657. // this reply is for.
  4658. // Request - The AP request to which to reply.
  4659. // ContextFlags - Contains context flags from the AP request.
  4660. // SessionKey - The session key to use to build the reply,.
  4661. // receives the new session key (if KERB_AP_USE_SKEY
  4662. // is negotiated).
  4663. // NewReply - Receives the AP reply.
  4664. // NewReplySize - Receives the size of the AP reply.
  4665. //
  4666. // Arguments:
  4667. //
  4668. // Requires:
  4669. //
  4670. // Returns: STATUS_SUCCESS, STATUS_INSUFFICIENT_MEMORY, or errors from
  4671. // KIEncryptData
  4672. //
  4673. // Notes:
  4674. //
  4675. //
  4676. //--------------------------------------------------------------------------
  4677. NTSTATUS
  4678. KerbBuildApReply(
  4679. IN PKERB_AUTHENTICATOR InternalAuthenticator,
  4680. IN PKERB_AP_REQUEST Request,
  4681. IN ULONG ContextFlags,
  4682. IN ULONG ContextAttributes,
  4683. IN PKERB_ENCRYPTION_KEY TicketKey,
  4684. IN OUT PKERB_ENCRYPTION_KEY SessionKey,
  4685. OUT PULONG Nonce,
  4686. OUT PUCHAR * NewReply,
  4687. OUT PULONG NewReplySize
  4688. )
  4689. {
  4690. NTSTATUS Status = STATUS_SUCCESS;
  4691. KERB_AP_REPLY Reply = {0};
  4692. KERB_ENCRYPTED_AP_REPLY ReplyBody = {0};
  4693. KERB_ENCRYPTION_KEY NewSessionKey = {0};
  4694. *NewReply = NULL;
  4695. *NewReplySize = 0;
  4696. Reply.version = KERBEROS_VERSION;
  4697. Reply.message_type = KRB_AP_REP;
  4698. ReplyBody.client_time = InternalAuthenticator->client_time;
  4699. ReplyBody.client_usec = InternalAuthenticator->client_usec;
  4700. //
  4701. // Generate a new nonce for the reply
  4702. //
  4703. *Nonce = KerbAllocateNonce();
  4704. D_DebugLog((DEB_TRACE,"BuildApReply using nonce 0x%x\n",*Nonce));
  4705. if (*Nonce != 0)
  4706. {
  4707. ReplyBody.KERB_ENCRYPTED_AP_REPLY_sequence_number = (int) *Nonce;
  4708. ReplyBody.bit_mask |= KERB_ENCRYPTED_AP_REPLY_sequence_number_present;
  4709. }
  4710. //
  4711. // If the client wants to use a session key, create one now
  4712. //
  4713. if ((InternalAuthenticator->bit_mask & KERB_AUTHENTICATOR_subkey_present) != 0 )
  4714. {
  4715. KERBERR KerbErr;
  4716. //
  4717. // If the client sent us an export-strength subkey, use it
  4718. //
  4719. if (KerbIsKeyExportable(
  4720. &InternalAuthenticator->KERB_AUTHENTICATOR_subkey
  4721. ))
  4722. {
  4723. D_DebugLog((DEB_TRACE_CTXT,"Client sent exportable key, using it on server on server\n"));
  4724. if (!KERB_SUCCESS(KerbDuplicateKey(
  4725. &NewSessionKey,
  4726. &InternalAuthenticator->KERB_AUTHENTICATOR_subkey
  4727. )))
  4728. {
  4729. Status = STATUS_INSUFFICIENT_RESOURCES;
  4730. goto Cleanup;
  4731. }
  4732. }
  4733. else
  4734. {
  4735. //
  4736. // If we are export-strength, create our own key. Otherwise use
  4737. // the client's key.
  4738. //
  4739. D_DebugLog((DEB_TRACE_CTXT,"Client sent strong key, using it on server on server\n"));
  4740. KerbErr = KerbDuplicateKey(
  4741. &NewSessionKey,
  4742. &InternalAuthenticator->KERB_AUTHENTICATOR_subkey
  4743. );
  4744. if (!KERB_SUCCESS(KerbErr))
  4745. {
  4746. Status = KerbMapKerbError(KerbErr);
  4747. goto Cleanup;
  4748. }
  4749. }
  4750. ReplyBody.KERB_ENCRYPTED_AP_REPLY_subkey = NewSessionKey;
  4751. ReplyBody.bit_mask |= KERB_ENCRYPTED_AP_REPLY_subkey_present;
  4752. }
  4753. else
  4754. {
  4755. KERBERR KerbErr;
  4756. //
  4757. // Create a subkey ourselves if we are export strength
  4758. //
  4759. KerbErr = KerbMakeKey(
  4760. Request->authenticator.encryption_type,
  4761. &NewSessionKey
  4762. );
  4763. if (!KERB_SUCCESS(KerbErr))
  4764. {
  4765. Status = KerbMapKerbError(KerbErr);
  4766. goto Cleanup;
  4767. }
  4768. ReplyBody.KERB_ENCRYPTED_AP_REPLY_subkey = NewSessionKey;
  4769. ReplyBody.bit_mask |= KERB_ENCRYPTED_AP_REPLY_subkey_present;
  4770. }
  4771. Status = KerbMarshallApReply(
  4772. &Reply,
  4773. &ReplyBody,
  4774. TicketKey,
  4775. ContextFlags,
  4776. ContextAttributes,
  4777. NewReply,
  4778. NewReplySize
  4779. );
  4780. if (!NT_SUCCESS(Status))
  4781. {
  4782. goto Cleanup;
  4783. }
  4784. //
  4785. // If they asked for a session key, replace our current session key
  4786. // with it.
  4787. //
  4788. if (NewSessionKey.keyvalue.value != NULL)
  4789. {
  4790. KerbFreeKey(SessionKey);
  4791. *SessionKey = NewSessionKey;
  4792. RtlZeroMemory(
  4793. &NewSessionKey,
  4794. sizeof(KERB_ENCRYPTION_KEY)
  4795. );
  4796. }
  4797. Cleanup:
  4798. if (!NT_SUCCESS(Status))
  4799. {
  4800. KerbFreeKey(&NewSessionKey);
  4801. }
  4802. return(Status);
  4803. }
  4804. //+-------------------------------------------------------------------------
  4805. //
  4806. // Function: KerbBuildThirdLegApReply
  4807. //
  4808. // Synopsis: Builds a third leg AP reply message if DCE-style
  4809. // authentication is desired.
  4810. //
  4811. // Effects: Context - Context for which to build this message.
  4812. // NewReply - Receives the AP reply.
  4813. // NewReplySize - Receives the size of the AP reply.
  4814. //
  4815. // Arguments:
  4816. //
  4817. // Requires:
  4818. //
  4819. // Returns: STATUS_SUCCESS, STATUS_INSUFFICIENT_MEMORY, or errors from
  4820. // KIEncryptData
  4821. //
  4822. // Notes:
  4823. //
  4824. //
  4825. //--------------------------------------------------------------------------
  4826. NTSTATUS
  4827. KerbBuildThirdLegApReply(
  4828. IN PKERB_CONTEXT Context,
  4829. IN ULONG ReceiveNonce,
  4830. OUT PUCHAR * NewReply,
  4831. OUT PULONG NewReplySize
  4832. )
  4833. {
  4834. NTSTATUS Status = STATUS_SUCCESS;
  4835. KERB_AP_REPLY Reply;
  4836. KERB_ENCRYPTED_AP_REPLY ReplyBody;
  4837. TimeStamp CurrentTime;
  4838. RtlZeroMemory(
  4839. &Reply,
  4840. sizeof(KERB_AP_REPLY)
  4841. );
  4842. RtlZeroMemory(
  4843. &ReplyBody,
  4844. sizeof(KERB_ENCRYPTED_AP_REPLY)
  4845. );
  4846. *NewReply = NULL;
  4847. *NewReplySize = 0;
  4848. Reply.version = KERBEROS_VERSION;
  4849. Reply.message_type = KRB_AP_REP;
  4850. GetSystemTimeAsFileTime((PFILETIME)
  4851. &CurrentTime
  4852. );
  4853. KerbConvertLargeIntToGeneralizedTimeWrapper(
  4854. &ReplyBody.client_time,
  4855. &ReplyBody.client_usec,
  4856. &CurrentTime
  4857. );
  4858. ReplyBody.KERB_ENCRYPTED_AP_REPLY_sequence_number = ReceiveNonce;
  4859. ReplyBody.bit_mask |= KERB_ENCRYPTED_AP_REPLY_sequence_number_present;
  4860. D_DebugLog((DEB_TRACE,"Building third leg AP reply with nonce 0x%x\n",ReceiveNonce));
  4861. //
  4862. // We already negotiated context flags so don't bother filling them in
  4863. // now.
  4864. //
  4865. KerbReadLockContexts();
  4866. DsysAssert((int) Context->EncryptionType == Context->TicketKey.keytype);
  4867. Status = KerbMarshallApReply(
  4868. &Reply,
  4869. &ReplyBody,
  4870. &Context->TicketKey,
  4871. Context->ContextFlags,
  4872. Context->ContextAttributes,
  4873. NewReply,
  4874. NewReplySize
  4875. );
  4876. KerbUnlockContexts();
  4877. if (!NT_SUCCESS(Status))
  4878. {
  4879. goto Cleanup;
  4880. }
  4881. Cleanup:
  4882. return (Status);
  4883. }
  4884. //+-------------------------------------------------------------------------
  4885. //
  4886. // Function: KerbKerbTimeEqual
  4887. //
  4888. // Synopsis: Compares two KERB_TIME values
  4889. //
  4890. // Effects:
  4891. //
  4892. // Arguments:
  4893. //
  4894. // Requires:
  4895. //
  4896. // Returns: TRUE - times are the same; otherwise FALSE
  4897. //
  4898. // Notes:
  4899. //
  4900. //
  4901. //--------------------------------------------------------------------------
  4902. BOOLEAN
  4903. KerbKerbTimeEqual(
  4904. PKERB_TIME pt1,
  4905. PKERB_TIME pt2
  4906. )
  4907. {
  4908. if (pt1->day != pt2->day)
  4909. return (FALSE);
  4910. if (pt1->diff != pt2->diff)
  4911. return (FALSE);
  4912. if (pt1->hour != pt2->hour)
  4913. return (FALSE);
  4914. if (pt1->millisecond != pt2->millisecond)
  4915. return (FALSE);
  4916. if (pt1->minute != pt2->minute)
  4917. return (FALSE);
  4918. if (pt1->month != pt2->month)
  4919. return (FALSE);
  4920. if (pt1->second != pt2->second)
  4921. return (FALSE);
  4922. if (pt1->universal != pt2->universal)
  4923. return (FALSE);
  4924. if (pt1->year != pt2->year)
  4925. return (FALSE);
  4926. return (TRUE);
  4927. }
  4928. //+-------------------------------------------------------------------------
  4929. //
  4930. // Function: KerbVerifyApReply
  4931. //
  4932. // Synopsis: Verifies an AP reply corresponds to an AP request
  4933. //
  4934. // Effects: Decrypts the AP reply
  4935. //
  4936. // Arguments:
  4937. //
  4938. // Requires:
  4939. //
  4940. // Returns: STATUS_SUCCESS or STATUS_LOGON_FAILURE
  4941. //
  4942. // Notes:
  4943. //
  4944. //
  4945. //--------------------------------------------------------------------------
  4946. NTSTATUS
  4947. KerbVerifyApReply(
  4948. IN PKERB_CONTEXT Context,
  4949. IN PUCHAR PackedReply,
  4950. IN ULONG PackedReplySize,
  4951. OUT PULONG Nonce
  4952. )
  4953. {
  4954. NTSTATUS Status = STATUS_SUCCESS;
  4955. KERBERR KerbErr;
  4956. PKERB_AP_REPLY Reply = NULL;
  4957. PKERB_ENCRYPTED_AP_REPLY ReplyBody = NULL;
  4958. BOOLEAN ContextsLocked = FALSE;
  4959. ULONG StrippedReplySize = PackedReplySize;
  4960. PUCHAR StrippedReply = PackedReply;
  4961. gss_OID_desc * MechId;
  4962. KERB_TIME AP_REQ_client_time;
  4963. ASN1int32_t AP_REQ_client_usec;
  4964. //
  4965. // Verify the GSSAPI header
  4966. //
  4967. KerbReadLockContexts();
  4968. if ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) == 0)
  4969. {
  4970. if ((Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0)
  4971. {
  4972. MechId = gss_mech_krb5_u2u;
  4973. }
  4974. else
  4975. {
  4976. MechId = gss_mech_krb5_new;
  4977. }
  4978. if (!g_verify_token_header(
  4979. (gss_OID) MechId,
  4980. (INT *) &StrippedReplySize,
  4981. &StrippedReply,
  4982. KG_TOK_CTX_AP_REP,
  4983. PackedReplySize
  4984. ))
  4985. {
  4986. //
  4987. // BUG 454895: remove when not needed for compatibility
  4988. //
  4989. //
  4990. // if that didn't work, just use the token as it is.
  4991. //
  4992. StrippedReply = PackedReply;
  4993. StrippedReplySize = PackedReplySize;
  4994. D_DebugLog((DEB_WARN,"Didn't find GSS header on AP Reply\n"));
  4995. }
  4996. }
  4997. KerbUnlockContexts();
  4998. if (!KERB_SUCCESS(KerbUnpackApReply(
  4999. StrippedReply,
  5000. StrippedReplySize,
  5001. &Reply
  5002. )))
  5003. {
  5004. D_DebugLog((DEB_WARN, "Failed to unpack AP reply, %ws, %d\n", THIS_FILE, __LINE__));
  5005. Status = STATUS_INSUFFICIENT_RESOURCES;
  5006. goto Cleanup;
  5007. }
  5008. if ((Reply->version != KERBEROS_VERSION) ||
  5009. (Reply->message_type != KRB_AP_REP))
  5010. {
  5011. D_DebugLog((DEB_ERROR,"Illegal version or message as AP reply: 0x%x, 0x%x. %ws, line %d\n",
  5012. Reply->version, Reply->message_type, THIS_FILE, __LINE__ ));
  5013. Status = STATUS_LOGON_FAILURE;
  5014. goto Cleanup;
  5015. }
  5016. //
  5017. // Now decrypt the encrypted part.
  5018. //
  5019. KerbReadLockTicketCache();
  5020. KerbWriteLockContexts();
  5021. ContextsLocked = TRUE;
  5022. KerbErr = KerbDecryptDataEx(
  5023. &Reply->encrypted_part,
  5024. &Context->TicketKey,
  5025. KERB_AP_REP_SALT,
  5026. (PULONG) &Reply->encrypted_part.cipher_text.length,
  5027. Reply->encrypted_part.cipher_text.value
  5028. );
  5029. KerbUnlockTicketCache();
  5030. if (!KERB_SUCCESS(KerbErr))
  5031. {
  5032. DebugLog((DEB_ERROR, "Failed to decrypt AP reply: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
  5033. if (KerbErr == KRB_ERR_GENERIC)
  5034. {
  5035. Status = STATUS_INSUFFICIENT_RESOURCES;
  5036. }
  5037. else
  5038. {
  5039. Status = STATUS_LOGON_FAILURE;
  5040. }
  5041. goto Cleanup;
  5042. }
  5043. //
  5044. // Decode the contents now
  5045. //
  5046. if (!KERB_SUCCESS(KerbUnpackApReplyBody(
  5047. Reply->encrypted_part.cipher_text.value,
  5048. Reply->encrypted_part.cipher_text.length,
  5049. &ReplyBody)))
  5050. {
  5051. DebugLog((DEB_ERROR, "Failed to unpack AP reply body. %ws, line %d\n", THIS_FILE, __LINE__));
  5052. Status = STATUS_INSUFFICIENT_RESOURCES;
  5053. goto Cleanup;
  5054. }
  5055. //
  5056. // Check the mutual auth with ctime/utime reply from remote
  5057. // This will check only the ISC KERB AP_Requests & AP_Responses
  5058. // We may want to add in more support into the DCE path in ASC() - TBD
  5059. //
  5060. if ((Context->ContextAttributes & KERB_CONTEXT_OUTBOUND) != 0)
  5061. {
  5062. KerbConvertLargeIntToGeneralizedTimeWrapper(
  5063. &AP_REQ_client_time,
  5064. &AP_REQ_client_usec,
  5065. &Context->AuthenticatorTime
  5066. );
  5067. if ((AP_REQ_client_usec != ReplyBody->client_usec) ||
  5068. (!KerbKerbTimeEqual(&AP_REQ_client_time, &(ReplyBody->client_time))))
  5069. {
  5070. DebugLog((DEB_ERROR,"AP Authenticator times match Failed!\n"));
  5071. D_DebugLog((DEB_ERROR,"Context %d/%d/%d %d:%d:%d.%d\n",
  5072. AP_REQ_client_time.month, AP_REQ_client_time.day, AP_REQ_client_time.year,
  5073. AP_REQ_client_time.hour, AP_REQ_client_time.minute, AP_REQ_client_time.second,
  5074. AP_REQ_client_usec));
  5075. D_DebugLog((DEB_ERROR,"AP_Reply %d/%d/%d %d:%d:%d.%d\n",
  5076. ReplyBody->client_time.month, ReplyBody->client_time.day, ReplyBody->client_time.year,
  5077. ReplyBody->client_time.hour, ReplyBody->client_time.minute, ReplyBody->client_time.second,
  5078. ReplyBody->client_usec));
  5079. // ASSERT(0);
  5080. // Kerb error KRB_AP_ERR_MUT_FAIL
  5081. Status = STATUS_MUTUAL_AUTHENTICATION_FAILED;
  5082. goto Cleanup;
  5083. }
  5084. }
  5085. if ((ReplyBody->bit_mask & KERB_ENCRYPTED_AP_REPLY_sequence_number_present) != 0)
  5086. {
  5087. *Nonce = ReplyBody->KERB_ENCRYPTED_AP_REPLY_sequence_number;
  5088. D_DebugLog((DEB_TRACE,"Verifying AP reply: AP nonce = 0x%x, context nonce = 0x%x, receive nonce = 0x%x\n",
  5089. *Nonce,
  5090. Context->Nonce,
  5091. Context->ReceiveNonce
  5092. ));
  5093. //
  5094. // If this is a third-leg AP reply, verify the nonce.
  5095. //
  5096. if ((Context->ContextAttributes & KERB_CONTEXT_INBOUND) != 0)
  5097. {
  5098. if (*Nonce != Context->Nonce)
  5099. {
  5100. D_DebugLog((DEB_ERROR,"Nonce in third-leg AP rep didn't match context: 0x%x vs 0x%x\n",
  5101. *Nonce, Context->Nonce ));
  5102. Status = STATUS_LOGON_FAILURE;
  5103. goto Cleanup;
  5104. }
  5105. }
  5106. }
  5107. else
  5108. {
  5109. *Nonce = 0;
  5110. }
  5111. //
  5112. // Check to see if a new session key was sent back. If it was, stick it
  5113. // in the context.
  5114. //
  5115. if ((ReplyBody->bit_mask & KERB_ENCRYPTED_AP_REPLY_subkey_present) != 0)
  5116. {
  5117. KerbFreeKey(&Context->SessionKey);
  5118. if (!KERB_SUCCESS(KerbDuplicateKey(
  5119. &Context->SessionKey,
  5120. &ReplyBody->KERB_ENCRYPTED_AP_REPLY_subkey
  5121. )))
  5122. {
  5123. Status = STATUS_INSUFFICIENT_RESOURCES;
  5124. goto Cleanup;
  5125. }
  5126. }
  5127. Status = STATUS_SUCCESS;
  5128. Cleanup:
  5129. if (ContextsLocked)
  5130. {
  5131. KerbUnlockContexts();
  5132. }
  5133. if (Reply != NULL)
  5134. {
  5135. KerbFreeApReply(Reply);
  5136. }
  5137. if (ReplyBody != NULL)
  5138. {
  5139. KerbFreeApReplyBody(ReplyBody);
  5140. }
  5141. return(Status);
  5142. }
  5143. //+-------------------------------------------------------------------------
  5144. //
  5145. // Function: KerbInitGlobalVariables
  5146. //
  5147. // Synopsis: Initializes global variables
  5148. //
  5149. // Effects:
  5150. //
  5151. // Arguments: none
  5152. //
  5153. // Requires: NTSTATUS code
  5154. //
  5155. // Returns:
  5156. //
  5157. // Notes:
  5158. //
  5159. //
  5160. //--------------------------------------------------------------------------
  5161. NTSTATUS
  5162. KerbInitGlobalVariables(
  5163. VOID
  5164. )
  5165. {
  5166. NTSTATUS Status = STATUS_SUCCESS;
  5167. #ifndef WIN32_CHICAGO
  5168. LPNET_CONFIG_HANDLE ConfigHandle = NULL;
  5169. BOOL TempBool;
  5170. #endif // WIN32_CHICAGO
  5171. ULONG SkewTimeInMinutes = KERB_DEFAULT_SKEWTIME;
  5172. NET_API_STATUS NetStatus = ERROR_SUCCESS;
  5173. ULONG FarKdcTimeout = KERB_BINDING_FAR_DC_TIMEOUT;
  5174. ULONG NearKdcTimeout = KERB_BINDING_NEAR_DC_TIMEOUT;
  5175. ULONG SpnCacheTimeout = KERB_SPN_CACHE_TIMEOUT;
  5176. ULONG S4UCacheTimeout = KERB_S4U_CACHE_TIMEOUT;
  5177. ULONG S4UTicketLifetime = KERB_S4U_TICKET_LIFETIME;
  5178. //
  5179. // Initialize the kerberos token source
  5180. //
  5181. RtlCopyMemory(
  5182. KerberosSource.SourceName,
  5183. "Kerberos",
  5184. sizeof( KerberosSource.SourceName )
  5185. );
  5186. NtAllocateLocallyUniqueId(&KerberosSource.SourceIdentifier);
  5187. KerbGlobalKdcWaitTime = KERB_KDC_WAIT_TIME;
  5188. KerbGlobalKdcCallTimeout = KERB_KDC_CALL_TIMEOUT;
  5189. KerbGlobalKdcCallBackoff = KERB_KDC_CALL_TIMEOUT_BACKOFF;
  5190. KerbGlobalMaxDatagramSize = KERB_MAX_DATAGRAM_SIZE;
  5191. KerbGlobalKdcSendRetries = KERB_MAX_RETRIES;
  5192. KerbGlobalMaxReferralCount = KERB_MAX_REFERRAL_COUNT;
  5193. KerbGlobalUseStrongEncryptionForDatagram = KERB_DEFAULT_USE_STRONG_ENC_DG;
  5194. KerbGlobalDefaultPreauthEtype = KERB_ETYPE_RC4_HMAC_NT;
  5195. KerbGlobalMaxTokenSize = KERBEROS_MAX_TOKEN;
  5196. #ifndef WIN32_CHICAGO
  5197. //
  5198. // Set the max authenticator age to be less than the allowed skew time
  5199. // on debug builds so we can have widely varying time on machines but
  5200. // don't build up a huge list of authenticators.
  5201. //
  5202. NetStatus = NetpOpenConfigDataWithPath(
  5203. &ConfigHandle,
  5204. NULL, // no server name
  5205. KERB_PATH,
  5206. TRUE // read only
  5207. );
  5208. if (NetStatus == ERROR_SUCCESS)
  5209. {
  5210. NetStatus = NetpGetConfigDword(
  5211. ConfigHandle,
  5212. KERB_PARAMETER_SKEWTIME,
  5213. KERB_DEFAULT_SKEWTIME,
  5214. &SkewTimeInMinutes
  5215. );
  5216. if (!NT_SUCCESS(NetStatus))
  5217. {
  5218. Status = NetpApiStatusToNtStatus(NetStatus);
  5219. goto Cleanup;
  5220. }
  5221. NetStatus = NetpGetConfigDword(
  5222. ConfigHandle,
  5223. KERB_PARAMETER_MAX_TOKEN_SIZE,
  5224. KERBEROS_MAX_TOKEN,
  5225. &KerbGlobalMaxTokenSize
  5226. );
  5227. if (!NT_SUCCESS(NetStatus))
  5228. {
  5229. Status = NetpApiStatusToNtStatus(NetStatus);
  5230. goto Cleanup;
  5231. }
  5232. //
  5233. // Get the far timeout for the kdc
  5234. //
  5235. NetStatus = NetpGetConfigDword(
  5236. ConfigHandle,
  5237. KERB_PARAMETER_FAR_KDC_TIMEOUT,
  5238. KERB_BINDING_FAR_DC_TIMEOUT,
  5239. &FarKdcTimeout
  5240. );
  5241. if (!NT_SUCCESS(NetStatus))
  5242. {
  5243. Status = NetpApiStatusToNtStatus(NetStatus);
  5244. goto Cleanup;
  5245. }
  5246. //
  5247. // Get the near timeout for the kdc
  5248. //
  5249. NetStatus = NetpGetConfigDword(
  5250. ConfigHandle,
  5251. KERB_PARAMETER_NEAR_KDC_TIMEOUT,
  5252. KERB_BINDING_NEAR_DC_TIMEOUT,
  5253. &NearKdcTimeout
  5254. );
  5255. if (!NT_SUCCESS(NetStatus))
  5256. {
  5257. Status = NetpApiStatusToNtStatus(NetStatus);
  5258. goto Cleanup;
  5259. }
  5260. //
  5261. // Get the SPN cache lifetime
  5262. //
  5263. NetStatus = NetpGetConfigDword(
  5264. ConfigHandle,
  5265. KERB_PARAMETER_SPN_CACHE_TIMEOUT,
  5266. KERB_SPN_CACHE_TIMEOUT,
  5267. &SpnCacheTimeout
  5268. );
  5269. if (!NT_SUCCESS(NetStatus))
  5270. {
  5271. Status = NetpApiStatusToNtStatus(NetStatus);
  5272. goto Cleanup;
  5273. }
  5274. //
  5275. // Get the S4U cache lifetime
  5276. //
  5277. NetStatus = NetpGetConfigDword(
  5278. ConfigHandle,
  5279. KERB_PARAMETER_S4UCACHE_TIMEOUT,
  5280. KERB_S4U_CACHE_TIMEOUT,
  5281. &S4UCacheTimeout
  5282. );
  5283. if (!NT_SUCCESS(NetStatus))
  5284. {
  5285. Status = NetpApiStatusToNtStatus(NetStatus);
  5286. goto Cleanup;
  5287. }
  5288. //
  5289. // Get the S4U ticket and ticket cache - must be at least 5
  5290. // minutes long, though, or you'll get all sorts of bogus errors.
  5291. //
  5292. NetStatus = NetpGetConfigDword(
  5293. ConfigHandle,
  5294. KERB_PARAMETER_S4UTICKET_LIFETIME,
  5295. KERB_S4U_TICKET_LIFETIME,
  5296. &S4UTicketLifetime
  5297. );
  5298. if (!NT_SUCCESS(NetStatus))
  5299. {
  5300. Status = NetpApiStatusToNtStatus(NetStatus);
  5301. goto Cleanup;
  5302. }
  5303. if ( S4UTicketLifetime < KERB_MIN_S4UTICKET_LIFETIME )
  5304. {
  5305. S4UTicketLifetime = KERB_MIN_S4UTICKET_LIFETIME;
  5306. }
  5307. NetStatus = NetpGetConfigBool(
  5308. ConfigHandle,
  5309. KERB_PARAMETER_CACHE_S4UTICKET,
  5310. KERB_DEFAULT_CACHE_S4UTICKET,
  5311. &TempBool
  5312. );
  5313. if (!NT_SUCCESS(NetStatus))
  5314. {
  5315. Status = NetpApiStatusToNtStatus(NetStatus);
  5316. goto Cleanup;
  5317. }
  5318. KerbGlobalCacheS4UTicket = (BOOLEAN)(TempBool != 0 );
  5319. //
  5320. // Get the wait time for the service to start
  5321. //
  5322. NetStatus = NetpGetConfigDword(
  5323. ConfigHandle,
  5324. KERB_PARAMETER_START_TIME,
  5325. KERB_KDC_WAIT_TIME,
  5326. &KerbGlobalKdcWaitTime
  5327. );
  5328. if (!NT_SUCCESS(NetStatus))
  5329. {
  5330. Status = NetpApiStatusToNtStatus(NetStatus);
  5331. goto Cleanup;
  5332. }
  5333. NetStatus = NetpGetConfigDword(
  5334. ConfigHandle,
  5335. KERB_PARAMETER_KDC_CALL_TIMEOUT,
  5336. KERB_KDC_CALL_TIMEOUT,
  5337. &KerbGlobalKdcCallTimeout
  5338. );
  5339. if (!NT_SUCCESS(NetStatus))
  5340. {
  5341. Status = NetpApiStatusToNtStatus(NetStatus);
  5342. goto Cleanup;
  5343. }
  5344. NetStatus = NetpGetConfigDword(
  5345. ConfigHandle,
  5346. KERB_PARAMETER_MAX_UDP_PACKET,
  5347. KERB_MAX_DATAGRAM_SIZE,
  5348. &KerbGlobalMaxDatagramSize
  5349. );
  5350. if (!NT_SUCCESS(NetStatus))
  5351. {
  5352. Status = NetpApiStatusToNtStatus(NetStatus);
  5353. goto Cleanup;
  5354. }
  5355. else
  5356. {
  5357. //
  5358. // UDP packets have a 2-byte length field so under no
  5359. // circumstances can they be bigger than 64K
  5360. //
  5361. KerbGlobalMaxDatagramSize = min( KerbGlobalMaxDatagramSize, 64*1024 );
  5362. }
  5363. NetStatus = NetpGetConfigDword(
  5364. ConfigHandle,
  5365. KERB_PARAMETER_KDC_BACKOFF_TIME,
  5366. KERB_KDC_CALL_TIMEOUT_BACKOFF,
  5367. &KerbGlobalKdcCallBackoff
  5368. );
  5369. if (!NT_SUCCESS(NetStatus))
  5370. {
  5371. Status = NetpApiStatusToNtStatus(NetStatus);
  5372. goto Cleanup;
  5373. }
  5374. NetStatus = NetpGetConfigDword(
  5375. ConfigHandle,
  5376. KERB_PARAMETER_MAX_REFERRAL_COUNT,
  5377. KERB_MAX_REFERRAL_COUNT,
  5378. &KerbGlobalMaxReferralCount
  5379. );
  5380. if (!NT_SUCCESS(NetStatus))
  5381. {
  5382. Status = NetpApiStatusToNtStatus(NetStatus);
  5383. goto Cleanup;
  5384. }
  5385. NetStatus = NetpGetConfigDword(
  5386. ConfigHandle,
  5387. KERB_PARAMETER_KDC_SEND_RETRIES,
  5388. KERB_MAX_RETRIES,
  5389. &KerbGlobalKdcSendRetries
  5390. );
  5391. if (!NT_SUCCESS(NetStatus))
  5392. {
  5393. Status = NetpApiStatusToNtStatus(NetStatus);
  5394. goto Cleanup;
  5395. }
  5396. NetStatus = NetpGetConfigDword(
  5397. ConfigHandle,
  5398. KERB_PARAMETER_DEFAULT_ETYPE,
  5399. KerbGlobalDefaultPreauthEtype,
  5400. &KerbGlobalDefaultPreauthEtype
  5401. );
  5402. if (!NT_SUCCESS(NetStatus))
  5403. {
  5404. Status = NetpApiStatusToNtStatus(NetStatus);
  5405. goto Cleanup;
  5406. }
  5407. NetStatus = NetpGetConfigDword(
  5408. ConfigHandle,
  5409. KERB_PARAMETER_REQUEST_OPTIONS,
  5410. KERB_ADDITIONAL_KDC_OPTIONS,
  5411. &KerbGlobalKdcOptions
  5412. );
  5413. if (!NT_SUCCESS(NetStatus))
  5414. {
  5415. Status = NetpApiStatusToNtStatus(NetStatus);
  5416. goto Cleanup;
  5417. }
  5418. //
  5419. // BUG 454981: get this from the same place as NTLM
  5420. //
  5421. NetStatus = NetpGetConfigBool(
  5422. ConfigHandle,
  5423. KERB_PARAMETER_STRONG_ENC_DG,
  5424. KERB_DEFAULT_USE_STRONG_ENC_DG,
  5425. &TempBool
  5426. );
  5427. if (!NT_SUCCESS(NetStatus))
  5428. {
  5429. Status = NetpApiStatusToNtStatus(NetStatus);
  5430. goto Cleanup;
  5431. }
  5432. KerbGlobalUseStrongEncryptionForDatagram = (BOOLEAN)(TempBool != 0 );
  5433. }
  5434. #endif // WIN32_CHICAGO
  5435. KerbSetTimeInMinutes(&KerbGlobalSkewTime, SkewTimeInMinutes);
  5436. KerbSetTimeInMinutes(&KerbGlobalFarKdcTimeout,FarKdcTimeout);
  5437. KerbSetTimeInMinutes(&KerbGlobalNearKdcTimeout, NearKdcTimeout);
  5438. KerbSetTimeInMinutes(&KerbGlobalSpnCacheTimeout, SpnCacheTimeout);
  5439. KerbSetTimeInMinutes(&KerbGlobalS4UCacheTimeout, S4UCacheTimeout);
  5440. KerbSetTimeInMinutes(&KerbGlobalS4UTicketLifetime, S4UTicketLifetime);
  5441. goto Cleanup; // needed for WIN32_CHICAGO builds to use Cleanup label
  5442. Cleanup:
  5443. #ifndef WIN32_CHICAGO
  5444. if (ConfigHandle != NULL)
  5445. {
  5446. NetpCloseConfigData( ConfigHandle );
  5447. }
  5448. #endif // WIN32_CHICAGO
  5449. return(Status);
  5450. }
  5451. //+-------------------------------------------------------------------------
  5452. //
  5453. // Function: KerbInitTicketHandling
  5454. //
  5455. // Synopsis: Initializes ticket handling, such as authenticator list
  5456. //
  5457. // Effects:
  5458. //
  5459. // Arguments: none
  5460. //
  5461. // Requires: NTSTATUS code
  5462. //
  5463. // Returns:
  5464. //
  5465. // Notes:
  5466. //
  5467. //
  5468. //--------------------------------------------------------------------------
  5469. NTSTATUS
  5470. KerbInitTicketHandling(
  5471. VOID
  5472. )
  5473. {
  5474. NTSTATUS Status = STATUS_SUCCESS;
  5475. // Set global variables
  5476. Status = KerbInitGlobalVariables();
  5477. if (!NT_SUCCESS(Status))
  5478. {
  5479. goto Cleanup;
  5480. }
  5481. #ifndef WIN32_CHICAGO
  5482. Authenticators = new CAuthenticatorList( KerbGlobalSkewTime );
  5483. if (Authenticators == NULL)
  5484. {
  5485. Status = STATUS_INSUFFICIENT_RESOURCES;
  5486. goto Cleanup;
  5487. }
  5488. Status = Authenticators->Init();
  5489. if (!NT_SUCCESS(Status))
  5490. {
  5491. goto Cleanup;
  5492. }
  5493. #endif WIN32_CHICAGO
  5494. //
  5495. // Initialize the time skew code
  5496. //
  5497. Status = KerbInitializeSkewState();
  5498. if (!NT_SUCCESS(Status))
  5499. {
  5500. goto Cleanup;
  5501. }
  5502. Cleanup:
  5503. return(Status);
  5504. }
  5505. //+-------------------------------------------------------------------------
  5506. //
  5507. // Function: KerbCleanupTicketHandling
  5508. //
  5509. // Synopsis: cleans up ticket handling state, such as the
  5510. // list of authenticators.
  5511. //
  5512. // Effects:
  5513. //
  5514. // Arguments:
  5515. //
  5516. // Requires:
  5517. //
  5518. // Returns: none
  5519. //
  5520. // Notes:
  5521. //
  5522. //
  5523. //--------------------------------------------------------------------------
  5524. VOID
  5525. KerbCleanupTicketHandling(
  5526. VOID
  5527. )
  5528. {
  5529. #ifndef WIN32_CHICAGO
  5530. if (Authenticators != NULL)
  5531. {
  5532. delete Authenticators;
  5533. }
  5534. #endif // WIN32_CHICAGO
  5535. }
  5536. //+-------------------------------------------------------------------------
  5537. //
  5538. // Function: KerbBuildTgtRequest
  5539. //
  5540. // Synopsis: Creates a tgt request for user-to-user authentication
  5541. //
  5542. // Effects:
  5543. //
  5544. // Arguments:
  5545. //
  5546. // Requires:
  5547. //
  5548. // Returns:
  5549. //
  5550. // Notes:
  5551. //
  5552. //
  5553. //--------------------------------------------------------------------------
  5554. NTSTATUS
  5555. KerbBuildTgtRequest(
  5556. IN PKERB_INTERNAL_NAME TargetName,
  5557. IN PUNICODE_STRING TargetRealm,
  5558. OUT PULONG ContextAttributes,
  5559. OUT PUCHAR * MarshalledTgtRequest,
  5560. OUT PULONG TgtRequestSize
  5561. )
  5562. {
  5563. KERB_TGT_REQUEST Request = {0};
  5564. KERBERR KerbErr = KDC_ERR_NONE;
  5565. NTSTATUS Status = STATUS_SUCCESS;
  5566. PBYTE TempRequest = NULL;
  5567. PBYTE RequestStart;
  5568. ULONG TempRequestSize = 0;
  5569. D_DebugLog((DEB_TRACE_U2U, "KerbBuildTgtRequest TargetRealm %wZ, TargetName ", TargetRealm));
  5570. D_KerbPrintKdcName((DEB_TRACE_U2U, TargetName));
  5571. //
  5572. // First build the request
  5573. //
  5574. Request.version = KERBEROS_VERSION;
  5575. Request.message_type = KRB_TGT_REQ;
  5576. if (TargetName->NameCount > 0)
  5577. {
  5578. KerbErr = KerbConvertKdcNameToPrincipalName(
  5579. &Request.KERB_TGT_REQUEST_server_name,
  5580. TargetName
  5581. );
  5582. if (!KERB_SUCCESS(KerbErr))
  5583. {
  5584. Status = KerbMapKerbError(KerbErr);
  5585. goto Cleanup;
  5586. }
  5587. Request.bit_mask |= KERB_TGT_REQUEST_server_name_present;
  5588. }
  5589. else
  5590. {
  5591. *ContextAttributes |= KERB_CONTEXT_REQ_SERVER_NAME;
  5592. }
  5593. if (TargetRealm->Length > 0)
  5594. {
  5595. KerbErr = KerbConvertUnicodeStringToRealm(
  5596. &Request.server_realm,
  5597. TargetRealm
  5598. );
  5599. if (!KERB_SUCCESS(KerbErr))
  5600. {
  5601. Status = KerbMapKerbError(KerbErr);
  5602. goto Cleanup;
  5603. }
  5604. Request.bit_mask |= server_realm_present;
  5605. }
  5606. else
  5607. {
  5608. *ContextAttributes |= KERB_CONTEXT_REQ_SERVER_REALM;
  5609. }
  5610. //
  5611. // Encode the request
  5612. //
  5613. KerbErr = KerbPackData(
  5614. &Request,
  5615. KERB_TGT_REQUEST_PDU,
  5616. &TempRequestSize,
  5617. &TempRequest
  5618. );
  5619. if (!KERB_SUCCESS(KerbErr))
  5620. {
  5621. Status = KerbMapKerbError(KerbErr);
  5622. goto Cleanup;
  5623. }
  5624. //
  5625. // Now add on the space for the OID
  5626. //
  5627. *TgtRequestSize = g_token_size(
  5628. gss_mech_krb5_u2u,
  5629. TempRequestSize
  5630. );
  5631. *MarshalledTgtRequest = (PBYTE) MIDL_user_allocate(
  5632. *TgtRequestSize
  5633. );
  5634. if (*MarshalledTgtRequest == NULL)
  5635. {
  5636. Status = STATUS_INSUFFICIENT_RESOURCES;
  5637. goto Cleanup;
  5638. }
  5639. //
  5640. // Add the token ID & mechanism
  5641. //
  5642. RequestStart = *MarshalledTgtRequest;
  5643. g_make_token_header(
  5644. gss_mech_krb5_u2u,
  5645. TempRequestSize,
  5646. &RequestStart,
  5647. KG_TOK_CTX_TGT_REQ
  5648. );
  5649. RtlCopyMemory(
  5650. RequestStart,
  5651. TempRequest,
  5652. TempRequestSize
  5653. );
  5654. Status = STATUS_SUCCESS;
  5655. Cleanup:
  5656. if (TempRequest != NULL )
  5657. {
  5658. MIDL_user_free(TempRequest);
  5659. }
  5660. KerbFreePrincipalName(
  5661. &Request.KERB_TGT_REQUEST_server_name
  5662. );
  5663. if ((Request.bit_mask & server_realm_present) != 0)
  5664. {
  5665. KerbFreeRealm(
  5666. &Request.server_realm
  5667. );
  5668. }
  5669. return (Status);
  5670. }
  5671. //+-------------------------------------------------------------------------
  5672. //
  5673. // Function: KerbBuildTgtReply
  5674. //
  5675. // Synopsis: Builds a TGT reply with the appropriate options set
  5676. //
  5677. // Effects:
  5678. //
  5679. // Arguments:
  5680. //
  5681. // Requires: The logonsession / credential must be LOCKD!
  5682. //
  5683. // Returns:
  5684. //
  5685. // Notes:
  5686. //
  5687. //
  5688. //--------------------------------------------------------------------------
  5689. NTSTATUS
  5690. KerbBuildTgtReply(
  5691. IN PKERB_LOGON_SESSION LogonSession,
  5692. IN PKERB_CREDENTIAL Credentials,
  5693. IN PUNICODE_STRING pSuppRealm,
  5694. OUT PKERBERR ReturnedError,
  5695. OUT OPTIONAL PBYTE * MarshalledReply,
  5696. OUT OPTIONAL PULONG ReplySize,
  5697. OUT PKERB_TICKET_CACHE_ENTRY * TgtUsed
  5698. )
  5699. {
  5700. NTSTATUS Status = STATUS_SUCCESS;
  5701. KERBERR KerbErr = KDC_ERR_NONE;
  5702. KERB_TICKET_CACHE_ENTRY* TicketGrantingTicket = NULL;
  5703. KERB_TGT_REPLY Reply = {0};
  5704. UNICODE_STRING TempName = {0};
  5705. BOOLEAN CrossRealm = FALSE;
  5706. *TgtUsed = NULL; ;
  5707. D_DebugLog((DEB_TRACE_U2U, "KerbBuildTgtReply SuppRealm %wZ\n", pSuppRealm));
  5708. Status = KerbGetTgtForService(
  5709. LogonSession,
  5710. Credentials,
  5711. NULL, // no credman on the server side
  5712. pSuppRealm, // SuppRealm is the server's realm
  5713. &TempName, // no target realm
  5714. KERB_TICKET_CACHE_PRIMARY_TGT,
  5715. &TicketGrantingTicket,
  5716. &CrossRealm
  5717. );
  5718. if (!NT_SUCCESS(Status) || CrossRealm)
  5719. {
  5720. DebugLog((DEB_ERROR, "KerbBuildTgtReply failed to get TGT: %#x, CrossRealm ? %s, SuppRealm %wZ\n", Status, CrossRealm ? "true" : "false", pSuppRealm));
  5721. *ReturnedError = KRB_AP_ERR_NO_TGT;
  5722. Status = STATUS_USER2USER_REQUIRED;
  5723. goto Cleanup;
  5724. }
  5725. Reply.version = KERBEROS_VERSION;
  5726. Reply.message_type = KRB_TGT_REP;
  5727. KerbReadLockTicketCache();
  5728. Reply.ticket = TicketGrantingTicket->Ticket;
  5729. //
  5730. // Marshall the output
  5731. //
  5732. KerbErr = KerbPackData(
  5733. &Reply,
  5734. KERB_TGT_REPLY_PDU,
  5735. ReplySize,
  5736. MarshalledReply
  5737. );
  5738. KerbUnlockTicketCache();
  5739. if (!KERB_SUCCESS(KerbErr))
  5740. {
  5741. Status = KerbMapKerbError(KerbErr);
  5742. goto Cleanup;
  5743. }
  5744. *TgtUsed = TicketGrantingTicket;
  5745. TicketGrantingTicket = NULL;
  5746. Cleanup:
  5747. if (TicketGrantingTicket != NULL)
  5748. {
  5749. KerbDereferenceTicketCacheEntry(TicketGrantingTicket);
  5750. }
  5751. KerbFreeString(&TempName);
  5752. return(Status);
  5753. }
  5754. //+-------------------------------------------------------------------------
  5755. //
  5756. // Function: KerbBuildTgtErrorReply
  5757. //
  5758. // Synopsis: Builds a TgtReply message for use in a KERB_ERROR message
  5759. //
  5760. // Effects:
  5761. //
  5762. // Arguments:
  5763. //
  5764. // Requires:
  5765. //
  5766. // Returns:
  5767. //
  5768. // Notes:
  5769. //
  5770. //
  5771. //--------------------------------------------------------------------------
  5772. NTSTATUS
  5773. KerbBuildTgtErrorReply(
  5774. IN PKERB_LOGON_SESSION LogonSession,
  5775. IN PKERB_CREDENTIAL Credential,
  5776. IN BOOLEAN UseSuppliedCreds,
  5777. IN OUT PKERB_CONTEXT Context,
  5778. OUT PULONG ReplySize,
  5779. OUT PBYTE * Reply
  5780. )
  5781. {
  5782. NTSTATUS Status = STATUS_SUCCESS;
  5783. KERBERR KerbErr = KDC_ERR_NONE;
  5784. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials;
  5785. PKERB_TICKET_CACHE_ENTRY TgtUsed = NULL, OldTgt = NULL;
  5786. KerbReadLockLogonSessions(LogonSession);
  5787. if ( UseSuppliedCreds )
  5788. {
  5789. PrimaryCredentials = Credential->SuppliedCredentials;
  5790. }
  5791. else
  5792. {
  5793. PrimaryCredentials = &LogonSession->PrimaryCredentials;
  5794. }
  5795. Status = KerbBuildTgtReply(
  5796. LogonSession,
  5797. Credential,
  5798. &PrimaryCredentials->DomainName,
  5799. &KerbErr,
  5800. Reply,
  5801. ReplySize,
  5802. &TgtUsed
  5803. );
  5804. KerbUnlockLogonSessions(LogonSession);
  5805. //
  5806. //Store the cache entry in the context
  5807. //
  5808. if (NT_SUCCESS(Status))
  5809. {
  5810. KerbWriteLockContexts();
  5811. OldTgt = Context->TicketCacheEntry;
  5812. Context->TicketCacheEntry = TgtUsed;
  5813. //
  5814. // On the error path, do not set KERB_CONTEXT_USER_TO_USER because the
  5815. // client do not expect user2user at this moment
  5816. //
  5817. // Context->ContextAttributes |= KERB_CONTEXT_USER_TO_USER;
  5818. //
  5819. KerbUnlockContexts();
  5820. DebugLog((DEB_TRACE_U2U, "KerbHandleTgtRequest (TGT in error reply) saving ASC context->TicketCacheEntry, TGT is %p, was %p\n", TgtUsed, OldTgt));
  5821. TgtUsed = NULL;
  5822. if (OldTgt != NULL)
  5823. {
  5824. KerbDereferenceTicketCacheEntry(OldTgt);
  5825. }
  5826. }
  5827. return (Status);
  5828. }
  5829. //+-------------------------------------------------------------------------
  5830. //
  5831. // Function: KerbHandleTgtRequest
  5832. //
  5833. // Synopsis: Processes a request for a TGT. It will verify the supplied
  5834. // principal names and marshall a TGT response structure
  5835. //
  5836. // Effects:
  5837. //
  5838. // Arguments:
  5839. //
  5840. // Requires:
  5841. //
  5842. // Returns:
  5843. //
  5844. // Notes:
  5845. //
  5846. //
  5847. //--------------------------------------------------------------------------
  5848. NTSTATUS
  5849. KerbHandleTgtRequest(
  5850. IN PKERB_LOGON_SESSION LogonSession,
  5851. IN PKERB_CREDENTIAL Credential,
  5852. IN BOOLEAN UseSuppliedCreds,
  5853. IN PUCHAR RequestMessage,
  5854. IN ULONG RequestSize,
  5855. IN ULONG ContextRequirements,
  5856. IN PSecBuffer OutputToken,
  5857. IN PLUID LogonId,
  5858. OUT PULONG ContextAttributes,
  5859. OUT PKERB_CONTEXT * Context,
  5860. OUT PTimeStamp ContextLifetime,
  5861. OUT PKERBERR ReturnedError
  5862. )
  5863. {
  5864. ULONG StrippedRequestSize;
  5865. PUCHAR StrippedRequest;
  5866. KERBERR KerbErr = KDC_ERR_NONE;
  5867. NTSTATUS Status = STATUS_SUCCESS;
  5868. PKERB_TGT_REQUEST Request = NULL;
  5869. BOOLEAN LockAcquired = FALSE;
  5870. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials;
  5871. PKERB_TICKET_CACHE_ENTRY pOldTgt = NULL;
  5872. ULONG ReplySize = 0;
  5873. PBYTE MarshalledReply = NULL;
  5874. ULONG FinalSize;
  5875. PBYTE ReplyStart;
  5876. PKERB_TICKET_CACHE_ENTRY TgtUsed = NULL;
  5877. D_DebugLog((DEB_TRACE_U2U, "KerbHandleTgtRequest UseSuppliedCreds %s, ContextRequirements %#x\n",
  5878. UseSuppliedCreds ? "true" : "false", ContextRequirements));
  5879. StrippedRequestSize = RequestSize;
  5880. StrippedRequest = RequestMessage;
  5881. *ReturnedError = KDC_ERR_NONE;
  5882. //
  5883. // We need an output token
  5884. //
  5885. if (OutputToken == NULL)
  5886. {
  5887. return(SEC_E_INVALID_TOKEN);
  5888. }
  5889. //
  5890. // Check if this is user-to-user kerberos
  5891. //
  5892. if (g_verify_token_header(
  5893. gss_mech_krb5_u2u,
  5894. (INT *) &StrippedRequestSize,
  5895. &StrippedRequest,
  5896. KG_TOK_CTX_TGT_REQ,
  5897. RequestSize))
  5898. {
  5899. *ContextAttributes |= ASC_RET_USE_SESSION_KEY;
  5900. }
  5901. else
  5902. {
  5903. Status = SEC_E_INVALID_TOKEN;
  5904. goto Cleanup;
  5905. }
  5906. //
  5907. // Decode the tgt request message.
  5908. //
  5909. KerbErr = KerbUnpackData(
  5910. StrippedRequest,
  5911. StrippedRequestSize,
  5912. KERB_TGT_REQUEST_PDU,
  5913. (PVOID *) &Request
  5914. );
  5915. if (!KERB_SUCCESS(KerbErr))
  5916. {
  5917. D_DebugLog((DEB_ERROR,"Failed to decode TGT request: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
  5918. Status = KerbMapKerbError(KerbErr);
  5919. goto Cleanup;
  5920. }
  5921. DsysAssert( !LockAcquired );
  5922. KerbReadLockLogonSessions(LogonSession);
  5923. LockAcquired = TRUE;
  5924. if ( UseSuppliedCreds )
  5925. {
  5926. PrimaryCredentials = Credential->SuppliedCredentials;
  5927. }
  5928. else
  5929. {
  5930. PrimaryCredentials = &LogonSession->PrimaryCredentials;
  5931. }
  5932. //
  5933. // Check the supplied principal name and realm to see if it matches
  5934. // out credentials
  5935. //
  5936. //
  5937. // We don't need to verify the server name because the client can do
  5938. // that.
  5939. //
  5940. //
  5941. // Allocate a context
  5942. //
  5943. Status = KerbCreateEmptyContext(
  5944. Credential,
  5945. ASC_RET_USE_SESSION_KEY, // indicating user-to-user
  5946. KERB_CONTEXT_USER_TO_USER | KERB_CONTEXT_INBOUND,
  5947. 0, // no nego info here.
  5948. LogonId,
  5949. Context,
  5950. ContextLifetime
  5951. );
  5952. DebugLog((DEB_TRACE_U2U, "KerbHandleTgtRequest (TGT in TGT reply) USER2USER-INBOUND set %#x\n", Status));
  5953. if (!NT_SUCCESS(Status))
  5954. {
  5955. goto Cleanup;
  5956. }
  5957. Status = KerbBuildTgtReply(
  5958. LogonSession,
  5959. Credential,
  5960. &PrimaryCredentials->DomainName,
  5961. ReturnedError,
  5962. &MarshalledReply,
  5963. &ReplySize,
  5964. &TgtUsed
  5965. );
  5966. if (!NT_SUCCESS(Status))
  5967. {
  5968. goto Cleanup;
  5969. }
  5970. //
  5971. // Put it in the context for later use
  5972. //
  5973. KerbWriteLockContexts();
  5974. pOldTgt = (*Context)->TicketCacheEntry;
  5975. (*Context)->TicketCacheEntry = TgtUsed;
  5976. KerbUnlockContexts();
  5977. DebugLog((DEB_TRACE_U2U, "KerbHandleTgtRequest (TGT in TGT reply) saving ASC context->TicketCacheEntry, TGT is %p, was %p\n", TgtUsed, pOldTgt));
  5978. TgtUsed = NULL;
  5979. if (pOldTgt)
  5980. {
  5981. KerbDereferenceTicketCacheEntry(pOldTgt);
  5982. pOldTgt = NULL;
  5983. }
  5984. //
  5985. // Now build the output message
  5986. //
  5987. FinalSize = g_token_size(
  5988. gss_mech_krb5_u2u,
  5989. ReplySize
  5990. );
  5991. if ((ContextRequirements & ASC_REQ_ALLOCATE_MEMORY) == 0)
  5992. {
  5993. if (OutputToken->cbBuffer < FinalSize)
  5994. {
  5995. D_DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n",
  5996. OutputToken->cbBuffer,ReplySize, THIS_FILE, __LINE__ ));
  5997. Status = STATUS_BUFFER_TOO_SMALL;
  5998. goto Cleanup;
  5999. }
  6000. }
  6001. else
  6002. {
  6003. OutputToken->pvBuffer = KerbAllocate(FinalSize);
  6004. if (OutputToken->pvBuffer == NULL)
  6005. {
  6006. Status = STATUS_INSUFFICIENT_RESOURCES;
  6007. goto Cleanup;
  6008. }
  6009. *ContextAttributes |= ISC_RET_ALLOCATED_MEMORY;
  6010. }
  6011. ReplyStart = (PUCHAR) OutputToken->pvBuffer;
  6012. g_make_token_header(
  6013. gss_mech_krb5_u2u,
  6014. ReplySize,
  6015. &ReplyStart,
  6016. KG_TOK_CTX_TGT_REP
  6017. );
  6018. RtlCopyMemory(
  6019. ReplyStart,
  6020. MarshalledReply,
  6021. ReplySize
  6022. );
  6023. OutputToken->cbBuffer = FinalSize;
  6024. KerbWriteLockContexts();
  6025. (*Context)->ContextState = TgtReplySentState;
  6026. KerbUnlockContexts();
  6027. Cleanup:
  6028. if (LockAcquired)
  6029. {
  6030. KerbUnlockLogonSessions(LogonSession);
  6031. }
  6032. if (TgtUsed != NULL)
  6033. {
  6034. KerbDereferenceTicketCacheEntry(TgtUsed);
  6035. }
  6036. if (MarshalledReply != NULL)
  6037. {
  6038. MIDL_user_free(MarshalledReply);
  6039. }
  6040. if (Request != NULL)
  6041. {
  6042. KerbFreeData(KERB_TGT_REQUEST_PDU, Request);
  6043. }
  6044. return(Status);
  6045. }
  6046. //+-------------------------------------------------------------------------
  6047. //
  6048. // Function: KerbUnpackTgtReply
  6049. //
  6050. // Synopsis: Unpacks a TGT reply and verifies contents, sticking
  6051. // reply into context.
  6052. //
  6053. // Effects:
  6054. //
  6055. // Arguments:
  6056. //
  6057. // Requires:
  6058. //
  6059. // Returns:
  6060. //
  6061. // Notes:
  6062. //
  6063. //
  6064. //--------------------------------------------------------------------------
  6065. NTSTATUS
  6066. KerbUnpackTgtReply(
  6067. IN PKERB_CONTEXT Context,
  6068. IN PUCHAR ReplyMessage,
  6069. IN ULONG ReplySize,
  6070. OUT PKERB_INTERNAL_NAME * TargetName,
  6071. OUT PUNICODE_STRING TargetRealm,
  6072. OUT PKERB_TGT_REPLY * Reply
  6073. )
  6074. {
  6075. NTSTATUS Status = STATUS_SUCCESS;
  6076. KERBERR KerbErr = KDC_ERR_NONE;
  6077. UNICODE_STRING LocalTargetRealm = {0};
  6078. PUCHAR StrippedReply = ReplyMessage;
  6079. ULONG StrippedReplySize = ReplySize;
  6080. ULONG ContextAttributes;
  6081. *Reply = NULL;
  6082. KerbReadLockContexts();
  6083. ContextAttributes = Context->ContextAttributes;
  6084. KerbUnlockContexts();
  6085. D_DebugLog((DEB_TRACE_U2U, "KerbUnpackTgtReply is User2User set in ContextAttributes? %s\n", ContextAttributes & KERB_CONTEXT_USER_TO_USER ? "yes" : "no"));
  6086. //
  6087. // Verify the OID header on the response. If this wasn't a user-to-user
  6088. // context then the message came from a KERB_ERROR message and won't
  6089. // have the OID header.
  6090. //
  6091. if ((ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0)
  6092. {
  6093. if (!g_verify_token_header(
  6094. gss_mech_krb5_u2u,
  6095. (INT *) &StrippedReplySize,
  6096. &StrippedReply,
  6097. KG_TOK_CTX_TGT_REP,
  6098. ReplySize))
  6099. {
  6100. D_DebugLog((DEB_WARN, "KerbUnpackTgtReply failed to verify u2u token header\n"));
  6101. Status = SEC_E_INVALID_TOKEN;
  6102. goto Cleanup;
  6103. }
  6104. }
  6105. else
  6106. {
  6107. StrippedReply = ReplyMessage;
  6108. StrippedReplySize = ReplySize;
  6109. //
  6110. // this is an error tgt reply
  6111. //
  6112. KerbWriteLockContexts();
  6113. Context->ContextFlags |= ISC_RET_USE_SESSION_KEY;
  6114. //
  6115. // KERB_CONTEXT_USER_TO_USER needs to be set
  6116. //
  6117. Context->ContextAttributes |= KERB_CONTEXT_USER_TO_USER;
  6118. KerbUnlockContexts();
  6119. DebugLog((DEB_TRACE_U2U, "KerbUnpackTgtReply (TGT in error reply) USER2USER-OUTBOUND set\n"));
  6120. }
  6121. //
  6122. // Decode the response
  6123. //
  6124. KerbErr = KerbUnpackData(
  6125. StrippedReply,
  6126. StrippedReplySize,
  6127. KERB_TGT_REPLY_PDU,
  6128. (PVOID *) Reply
  6129. );
  6130. if (!KERB_SUCCESS(KerbErr))
  6131. {
  6132. Status = KerbMapKerbError(KerbErr);
  6133. goto Cleanup;
  6134. }
  6135. //
  6136. // Pull the target name & realm out of the TGT reply message
  6137. //
  6138. KerbErr = KerbConvertRealmToUnicodeString(
  6139. &LocalTargetRealm,
  6140. &(*Reply)->ticket.realm
  6141. );
  6142. if (!KERB_SUCCESS(KerbErr))
  6143. {
  6144. Status = KerbMapKerbError(KerbErr);
  6145. goto Cleanup;
  6146. }
  6147. //
  6148. // If we were asked to get the server & realm name, use them now
  6149. //
  6150. //
  6151. // BUG 455793: we also use them if we weren't passed a target name on this
  6152. // call. Since we don't require names to be passed, though, this is
  6153. // a security problem, as mutual authentication is no longer guaranteed.
  6154. //
  6155. if (((ContextAttributes & KERB_CONTEXT_REQ_SERVER_REALM) != 0) ||
  6156. (TargetRealm->Length == 0))
  6157. {
  6158. KerbFreeString(
  6159. TargetRealm
  6160. );
  6161. *TargetRealm = LocalTargetRealm;
  6162. LocalTargetRealm.Buffer = NULL;
  6163. }
  6164. if (((ContextAttributes & KERB_CONTEXT_REQ_SERVER_NAME) != 0) ||
  6165. (((*TargetName)->NameCount == 1) && ((*TargetName)->Names[0].Length == 0)))
  6166. {
  6167. ULONG ProcessFlags = 0;
  6168. UNICODE_STRING TempRealm = {0};
  6169. KerbFreeKdcName(
  6170. TargetName
  6171. );
  6172. Status = KerbProcessTargetNames(
  6173. &Context->ServerPrincipalName,
  6174. NULL, // no local target name
  6175. 0, // no flags
  6176. &ProcessFlags,
  6177. TargetName,
  6178. &TempRealm,
  6179. NULL
  6180. );
  6181. if (!NT_SUCCESS(Status))
  6182. {
  6183. goto Cleanup;
  6184. }
  6185. KerbFreeString(&TempRealm);
  6186. }
  6187. Cleanup:
  6188. if (!NT_SUCCESS(Status))
  6189. {
  6190. if (*Reply != NULL)
  6191. {
  6192. KerbFreeData(
  6193. KERB_TGT_REPLY_PDU,
  6194. *Reply
  6195. );
  6196. *Reply = NULL;
  6197. }
  6198. }
  6199. if (LocalTargetRealm.Buffer != NULL)
  6200. {
  6201. KerbFreeString(
  6202. &LocalTargetRealm
  6203. );
  6204. }
  6205. return(Status);
  6206. }
  6207. //+-------------------------------------------------------------------------
  6208. //
  6209. // Function: KerbComputeGssBindHash
  6210. //
  6211. // Synopsis: Computes the Channel Bindings Hash for GSSAPI
  6212. //
  6213. // Effects:
  6214. //
  6215. // Arguments:
  6216. //
  6217. // Requires: At least 16 bytes allocated to HashBuffer
  6218. //
  6219. // Returns:
  6220. //
  6221. // Notes:
  6222. // (viz. RFC1964)
  6223. // MD5 hash of channel bindings, taken over all non-null
  6224. // components of bindings, in order of declaration.
  6225. // Integer fields within channel bindings are represented
  6226. // in little-endian order for the purposes of the MD5
  6227. // calculation.
  6228. //
  6229. //--------------------------------------------------------------------------
  6230. NTSTATUS
  6231. KerbComputeGssBindHash(
  6232. IN PSEC_CHANNEL_BINDINGS pChannelBindings,
  6233. OUT PUCHAR HashBuffer
  6234. )
  6235. {
  6236. NTSTATUS Status = STATUS_SUCCESS;
  6237. PCHECKSUM_FUNCTION MD5Check = NULL;
  6238. PCHECKSUM_BUFFER MD5ScratchBuffer = NULL;
  6239. //
  6240. // Locate the MD5 Hash Function
  6241. //
  6242. Status = CDLocateCheckSum(KERB_CHECKSUM_MD5, &MD5Check);
  6243. if( !NT_SUCCESS(Status) )
  6244. {
  6245. D_DebugLog( (DEB_ERROR,
  6246. "Failure Locating MD5: 0x%x. %ws, line %d\n",
  6247. Status,
  6248. THIS_FILE,
  6249. __LINE__) );
  6250. goto Cleanup;
  6251. }
  6252. //
  6253. // Initialize the Buffer
  6254. //
  6255. Status = MD5Check->Initialize(0, &MD5ScratchBuffer);
  6256. if( !NT_SUCCESS(Status) )
  6257. {
  6258. D_DebugLog( (DEB_ERROR,
  6259. "Failure initializing MD5: 0x%x. %ws, line %d\n",
  6260. Status,
  6261. THIS_FILE,
  6262. __LINE__) );
  6263. goto Cleanup;
  6264. }
  6265. //
  6266. // Build the MD5 hash
  6267. //
  6268. Status = MD5Check->Sum(MD5ScratchBuffer,
  6269. sizeof(DWORD),
  6270. (PUCHAR) &pChannelBindings->dwInitiatorAddrType );
  6271. if( !NT_SUCCESS(Status) )
  6272. {
  6273. D_DebugLog( (DEB_ERROR,
  6274. "Failure building MD5: 0x%x. %ws, line %d\n",
  6275. Status,
  6276. THIS_FILE,
  6277. __LINE__) );
  6278. }
  6279. Status = MD5Check->Sum(MD5ScratchBuffer,
  6280. sizeof(DWORD),
  6281. (PUCHAR) &pChannelBindings->cbInitiatorLength );
  6282. if( !NT_SUCCESS(Status) )
  6283. {
  6284. D_DebugLog( (DEB_ERROR,
  6285. "Failure building MD5: 0x%x. %ws, line %d\n",
  6286. Status,
  6287. THIS_FILE,
  6288. __LINE__) );
  6289. }
  6290. if( pChannelBindings->cbInitiatorLength )
  6291. {
  6292. Status = MD5Check->Sum(MD5ScratchBuffer,
  6293. pChannelBindings->cbInitiatorLength,
  6294. (PUCHAR) pChannelBindings + pChannelBindings->dwInitiatorOffset);
  6295. if( !NT_SUCCESS(Status) )
  6296. {
  6297. D_DebugLog( (DEB_ERROR,
  6298. "Failure building MD5: 0x%x. %ws, line %d\n",
  6299. Status,
  6300. THIS_FILE,
  6301. __LINE__) );
  6302. }
  6303. }
  6304. Status = MD5Check->Sum(MD5ScratchBuffer,
  6305. sizeof(DWORD),
  6306. (PUCHAR) &pChannelBindings->dwAcceptorAddrType);
  6307. if( !NT_SUCCESS(Status) )
  6308. {
  6309. D_DebugLog( (DEB_ERROR,
  6310. "Failure building MD5: 0x%x. %ws, line %d\n",
  6311. Status,
  6312. THIS_FILE,
  6313. __LINE__) );
  6314. }
  6315. Status = MD5Check->Sum(MD5ScratchBuffer,
  6316. sizeof(DWORD),
  6317. (PUCHAR) &pChannelBindings->cbAcceptorLength);
  6318. if( !NT_SUCCESS(Status) )
  6319. {
  6320. D_DebugLog( (DEB_ERROR,
  6321. "Failure building MD5: 0x%x. %ws, line %d\n",
  6322. Status,
  6323. THIS_FILE,
  6324. __LINE__) );
  6325. }
  6326. if( pChannelBindings->cbAcceptorLength)
  6327. {
  6328. Status = MD5Check->Sum(MD5ScratchBuffer,
  6329. pChannelBindings->cbAcceptorLength,
  6330. (PUCHAR) pChannelBindings + pChannelBindings->dwAcceptorOffset);
  6331. if( !NT_SUCCESS(Status) )
  6332. {
  6333. D_DebugLog( (DEB_ERROR,
  6334. "Failure building MD5: 0x%x. %ws, line %d\n",
  6335. Status,
  6336. THIS_FILE,
  6337. __LINE__) );
  6338. }
  6339. }
  6340. Status = MD5Check->Sum(MD5ScratchBuffer,
  6341. sizeof(DWORD),
  6342. (PUCHAR) &pChannelBindings->cbApplicationDataLength);
  6343. if( !NT_SUCCESS(Status) )
  6344. {
  6345. D_DebugLog( (DEB_ERROR,
  6346. "Failure building MD5: 0x%x. %ws, line %d\n",
  6347. Status,
  6348. THIS_FILE,
  6349. __LINE__) );
  6350. }
  6351. if( pChannelBindings->cbApplicationDataLength)
  6352. {
  6353. Status = MD5Check->Sum(MD5ScratchBuffer,
  6354. pChannelBindings->cbApplicationDataLength,
  6355. (PUCHAR) pChannelBindings + pChannelBindings->dwApplicationDataOffset);
  6356. if( !NT_SUCCESS(Status) )
  6357. {
  6358. D_DebugLog( (DEB_ERROR,
  6359. "Failure building MD5: 0x%x. %ws, line %d\n",
  6360. Status,
  6361. THIS_FILE,
  6362. __LINE__) );
  6363. }
  6364. }
  6365. //
  6366. // Copy the hash results into the checksum field
  6367. //
  6368. DsysAssert( MD5Check->CheckSumSize == 4*sizeof(ULONG) );
  6369. Status = MD5Check->Finalize( MD5ScratchBuffer, HashBuffer );
  6370. if( !NT_SUCCESS(Status) )
  6371. {
  6372. D_DebugLog( (DEB_ERROR,
  6373. "Failure Finalizing MD5: 0x%x. %ws, line %d\n",
  6374. Status,
  6375. THIS_FILE,
  6376. __LINE__) );
  6377. goto Cleanup;
  6378. }
  6379. Cleanup:
  6380. if( MD5Check != NULL )
  6381. {
  6382. MD5Check->Finish( &MD5ScratchBuffer );
  6383. }
  6384. return Status;
  6385. }