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

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