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.

2317 lines
56 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1996
  6. //
  7. // File: refer.cxx
  8. //
  9. // Contents: Routines for interdomain referrals
  10. //
  11. //
  12. // History: 26-Nov-1996 MikeSw Created
  13. //
  14. // Notes: The list of domains could be kept as a splay tree for faster
  15. // searches & inserts.
  16. //
  17. //------------------------------------------------------------------------
  18. #include "kdcsvr.hxx"
  19. #include <lsarpc.h>
  20. extern "C"
  21. {
  22. #include <dns.h> // DNS_MAX_NAME_LENGTH
  23. #include <ntdsa.h> // CrackSingleName
  24. }
  25. LIST_ENTRY KdcDomainList;
  26. RTL_CRITICAL_SECTION KdcDomainListLock;
  27. BOOLEAN KdcDomainListInitialized = FALSE;
  28. LIST_ENTRY KdcReferralCache;
  29. RTL_CRITICAL_SECTION KdcReferralCacheLock;
  30. BOOLEAN KdcReferralCacheInitialized = FALSE;
  31. UNICODE_STRING KdcForestRootDomainName = {0};
  32. #define KdcLockDomainList() (RtlEnterCriticalSection(&KdcDomainListLock))
  33. #define KdcUnlockDomainList() (RtlLeaveCriticalSection(&KdcDomainListLock))
  34. #define KdcLockReferralCache() (RtlEnterCriticalSection(&KdcReferralCacheLock))
  35. #define KdcUnlockReferralCache() (RtlLeaveCriticalSection(&KdcReferralCacheLock))
  36. #define KdcReferenceDomainInfo(_x_) \
  37. InterlockedIncrement(&(_x_)->References)
  38. #define KdcReferenceReferralCacheEntry(_x_) \
  39. InterlockedIncrement(&(_x_)->References)
  40. // temp #defines
  41. #define NEW_KDCEVENT_TRUSTLIST_LOOP 0xC000000C
  42. static TCHAR THIS_FILE[]=TEXT(__FILE__);
  43. #define FILENO FILENO_REFER
  44. //+-------------------------------------------------------------------------
  45. //
  46. // Function: KdcDereferenceReferralCacheEntry
  47. //
  48. // Synopsis: Derefernce a domain info structure. If the reference
  49. // count goes to zero the structure is freed.
  50. //
  51. // Effects:
  52. //
  53. // Arguments:
  54. //
  55. // Requires:
  56. //
  57. // Returns:
  58. //
  59. // Notes:
  60. //
  61. //
  62. //--------------------------------------------------------------------------
  63. VOID
  64. KdcDereferenceReferralCacheEntry(
  65. IN PREFERRAL_CACHE_ENTRY CacheEntry
  66. )
  67. {
  68. if (InterlockedDecrement(&CacheEntry->References) == 0)
  69. {
  70. KdcLockReferralCache();
  71. CacheEntry->ListEntry.Blink->Flink = CacheEntry->ListEntry.Flink;
  72. CacheEntry->ListEntry.Flink->Blink = CacheEntry->ListEntry.Blink;
  73. KdcUnlockReferralCache();
  74. KerbFreeString(&CacheEntry->RealmName);
  75. MIDL_user_free(CacheEntry);
  76. }
  77. }
  78. //+-------------------------------------------------------------------------
  79. //
  80. // Function: KdcDereferenceReferralCacheEntry
  81. //
  82. // Synopsis: Derefernce a domain info structure. If the reference
  83. // count goes to zero the structure is freed.
  84. //
  85. // Effects:
  86. //
  87. // Arguments:
  88. //
  89. // Requires:
  90. //
  91. // Returns:
  92. //
  93. // Notes:
  94. //
  95. //
  96. //--------------------------------------------------------------------------
  97. KERBERR
  98. KdcAddReferralCacheEntry(
  99. IN PUNICODE_STRING RealmName,
  100. IN ULONG CacheFlags
  101. )
  102. {
  103. PREFERRAL_CACHE_ENTRY CacheEntry = NULL;
  104. KERBERR KerbErr;
  105. TimeStamp CurrentTime;
  106. CacheEntry = (PREFERRAL_CACHE_ENTRY) MIDL_user_allocate(sizeof(REFERRAL_CACHE_ENTRY));
  107. if (NULL == CacheEntry)
  108. {
  109. // We're low on memory, non-fatal
  110. return KRB_ERR_GENERIC;
  111. }
  112. KerbErr = KerbDuplicateString(
  113. &(CacheEntry->RealmName),
  114. RealmName
  115. );
  116. if (!KERB_SUCCESS(KerbErr))
  117. {
  118. MIDL_user_free(CacheEntry);
  119. return KerbErr;
  120. }
  121. CacheEntry->CacheFlags = CacheFlags;
  122. // Set cache timeout == 10 minutes
  123. GetSystemTimeAsFileTime((PFILETIME)&CurrentTime);
  124. CacheEntry->EndTime.QuadPart = CurrentTime.QuadPart + (LONGLONG) 60*10*10000000;
  125. InterlockedIncrement(&CacheEntry->References);
  126. KdcLockReferralCache();
  127. InsertHeadList(
  128. &KdcReferralCache,
  129. &(CacheEntry->ListEntry)
  130. );
  131. KdcUnlockReferralCache();
  132. DebugLog((DEB_TRACE, "Added referal cache entry - %wZ State: %x\n",
  133. RealmName, CacheFlags));
  134. return KerbErr;
  135. }
  136. //+-------------------------------------------------------------------------
  137. //
  138. // Function: KdcLookupReferralCacheEntry
  139. //
  140. // Synopsis: Derefernce a domain info structure. If the reference
  141. // count goes to zero the structure is freed.
  142. //
  143. // Effects:
  144. //
  145. // Arguments:
  146. //
  147. // Requires:
  148. //
  149. // Returns:
  150. //
  151. // Notes:
  152. //
  153. //
  154. //--------------------------------------------------------------------------
  155. KERBERR
  156. KdcLocateReferralCacheEntry(
  157. IN PUNICODE_STRING RealmName,
  158. IN ULONG NewFlags,
  159. OUT PULONG CacheState
  160. )
  161. {
  162. KERBERR KerbErr = KDC_ERR_NONE;
  163. PLIST_ENTRY ListEntry;
  164. PREFERRAL_CACHE_ENTRY CacheEntry = NULL;
  165. BOOLEAN ListLocked = FALSE;
  166. BOOLEAN Found = FALSE;
  167. *CacheState = KDC_NO_ENTRY;
  168. KdcLockReferralCache();
  169. ListLocked = TRUE;
  170. //
  171. // Go through the binding cache looking for the correct entry
  172. //
  173. for (ListEntry = KdcReferralCache.Flink ;
  174. ListEntry != KdcReferralCache.Blink ;
  175. ListEntry = ListEntry->Flink )
  176. {
  177. CacheEntry = CONTAINING_RECORD(ListEntry, REFERRAL_CACHE_ENTRY, ListEntry.Flink);
  178. if (RtlEqualUnicodeString(
  179. &CacheEntry->RealmName,
  180. RealmName,
  181. TRUE // case insensitive
  182. ))
  183. {
  184. TimeStamp CurrentTime;
  185. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime );
  186. //
  187. // Update the flags & time on this cache entry
  188. //
  189. if (NewFlags != KDC_NO_ENTRY)
  190. {
  191. CacheEntry->CacheFlags = NewFlags;
  192. CacheEntry->EndTime.QuadPart = CurrentTime.QuadPart + (LONGLONG) 10*60*10000000;
  193. Found = TRUE;
  194. }
  195. else // just a lookup
  196. {
  197. if (KdcGetTime(CacheEntry->EndTime) < KdcGetTime(CurrentTime))
  198. {
  199. DebugLog((DEB_TRACE, "Time: Purging KDC Referral cache entry (%x : refcount %x) for %wZ \n",
  200. CacheEntry,CacheEntry->References, RealmName));
  201. KdcDereferenceReferralCacheEntry(CacheEntry);
  202. }
  203. else // got our flags
  204. {
  205. *CacheState = CacheEntry->CacheFlags;
  206. DebugLog((DEB_TRACE, "Found entry for %wZ, flags - %x\n",
  207. RealmName, *CacheState));
  208. Found = TRUE;
  209. }
  210. }
  211. break;
  212. }
  213. }
  214. // If it wasn't found, but if we asked for any new flags
  215. // we want a new cache entry
  216. if (!Found && (NewFlags != KDC_NO_ENTRY))
  217. {
  218. DebugLog((DEB_TRACE, "Adding referral cache entry - %wZ State: %x\n",
  219. RealmName, NewFlags));
  220. KerbErr = KdcAddReferralCacheEntry(
  221. RealmName,
  222. NewFlags
  223. );
  224. }
  225. if (ListLocked)
  226. {
  227. KdcUnlockReferralCache();
  228. }
  229. return KerbErr;
  230. }
  231. //+-------------------------------------------------------------------------
  232. //
  233. // Function: KdcFreeDomainInfo
  234. //
  235. // Synopsis:
  236. //
  237. // Effects:
  238. //
  239. // Arguments:
  240. //
  241. // Requires:
  242. //
  243. // Returns:
  244. //
  245. // Notes:
  246. //
  247. //
  248. //--------------------------------------------------------------------------
  249. VOID
  250. KdcFreeDomainInfo(
  251. IN PKDC_DOMAIN_INFO DomainInfo
  252. )
  253. {
  254. if (ARGUMENT_PRESENT(DomainInfo))
  255. {
  256. KerbFreeString(&DomainInfo->NetbiosName);
  257. KerbFreeString(&DomainInfo->DnsName);
  258. if (NULL != DomainInfo->Sid)
  259. {
  260. MIDL_user_free(DomainInfo->Sid);
  261. }
  262. MIDL_user_free(DomainInfo);
  263. }
  264. }
  265. //+-------------------------------------------------------------------------
  266. //
  267. // Function: KdcDereferenceDomainInfo
  268. //
  269. // Synopsis: Derefernce a domain info structure. If the reference
  270. // count goes to zero the structure is freed.
  271. //
  272. // Effects:
  273. //
  274. // Arguments:
  275. //
  276. // Requires:
  277. //
  278. // Returns:
  279. //
  280. // Notes:
  281. //
  282. //
  283. //--------------------------------------------------------------------------
  284. VOID
  285. KdcDereferenceDomainInfo(
  286. IN PKDC_DOMAIN_INFO DomainInfo
  287. )
  288. {
  289. if (InterlockedDecrement(&DomainInfo->References) == 0)
  290. {
  291. KdcFreeDomainInfo(DomainInfo);
  292. }
  293. }
  294. //+-------------------------------------------------------------------------
  295. //
  296. // Function: KdcCheckForInterdomainReferral
  297. //
  298. // Synopsis: This function makes a determination that an interdomain referral
  299. // that we're processing is destined for an external forest. This
  300. // is important because we won't have any referral information about
  301. // the destination forest, so we've got to target the root of our
  302. // enterprise instead.
  303. //
  304. // TBD: This function currently uses the CrackSingleName API (with
  305. // composed KRBTGT name to verify we're going for an xforest trust.
  306. // We should cache both positive and negative results so that we can
  307. // eliminate that call.
  308. //
  309. //
  310. // Effects:
  311. //
  312. // Arguments: ReferralTarget - Receives ticket info for target domain
  313. // ReferralRealm - Receives realm name of referral realm, if present
  314. // DestinationDomain - Target domain name
  315. // ExactMatch - The target domain has to be trusted by this domain
  316. //
  317. //
  318. // Requires:
  319. //
  320. // Returns:
  321. //
  322. // Notes:
  323. //
  324. //--------------------------------------------------------------------------
  325. KERBERR
  326. KdcCheckForCrossForestReferral(
  327. OUT PKDC_TICKET_INFO ReferralTarget,
  328. OUT OPTIONAL PUNICODE_STRING ReferralRealm,
  329. OUT PKERB_EXT_ERROR pExtendedError,
  330. IN PUNICODE_STRING DestinationDomain,
  331. IN ULONG NameFlags
  332. )
  333. {
  334. KERBERR KerbErr = KDC_ERR_NONE;
  335. NTSTATUS Status;
  336. LPWSTR KrbtgtSpn = NULL;
  337. UNICODE_STRING ServiceName = {0 , 0, NULL };
  338. WCHAR CrackedDnsDomain [DNS_MAX_NAME_LENGTH + 1];
  339. ULONG CrackedDomainLength = (DNS_MAX_NAME_LENGTH+1) * sizeof(WCHAR);
  340. WCHAR CrackedName[UNLEN+DNS_MAX_NAME_LENGTH + 2];
  341. ULONG CrackedNameLength = ((UNLEN+DNS_MAX_NAME_LENGTH + 2) * sizeof(WCHAR));
  342. ULONG CrackError = 0, CacheFlags = 0;
  343. //
  344. // Is it in the realm list of recent rejectees or
  345. // positive hits?
  346. //
  347. KerbErr = KdcLocateReferralCacheEntry(
  348. DestinationDomain,
  349. 0, // no new flags
  350. &CacheFlags
  351. );
  352. if (CacheFlags == KDC_NO_ENTRY)
  353. {
  354. //
  355. // Compose an SPN related to our KRBTGT account
  356. //
  357. KerbErr = KerbBuildUnicodeSpn(
  358. DestinationDomain,
  359. SecData.KdcServiceName(),
  360. &ServiceName
  361. );
  362. if (!KERB_SUCCESS(KerbErr))
  363. {
  364. goto Cleanup;
  365. }
  366. KrbtgtSpn = KerbBuildNullTerminatedString(&ServiceName);
  367. if (NULL == KrbtgtSpn)
  368. {
  369. KerbErr = KRB_ERR_GENERIC;
  370. goto Cleanup;
  371. }
  372. //
  373. // Look it up
  374. //
  375. Status = CrackSingleName(
  376. DS_SERVICE_PRINCIPAL_NAME, // we know its an SPN
  377. DS_NAME_FLAG_TRUST_REFERRAL | DS_NAME_FLAG_GCVERIFY,
  378. KrbtgtSpn,
  379. DS_UNIQUE_ID_NAME,
  380. &CrackedDomainLength,
  381. CrackedDnsDomain,
  382. &CrackedNameLength,
  383. CrackedName,
  384. &CrackError
  385. );
  386. // Any error, or CrackError other than xforest result
  387. // means we don't know where this referral is headed.
  388. // TBD: Recovery?
  389. if (!NT_SUCCESS(Status) || (CrackError != DS_NAME_ERROR_TRUST_REFERRAL))
  390. {
  391. DebugLog((DEB_ERROR,
  392. "KDC presented w/ a unknown Xrealm TGT (%wZ)\n",
  393. DestinationDomain));
  394. // Add a negative entry
  395. KdcLocateReferralCacheEntry(
  396. DestinationDomain,
  397. KDC_UNTRUSTED_REALM,
  398. &CacheFlags
  399. );
  400. KerbErr = KDC_ERR_PATH_NOT_ACCEPTED;
  401. goto Cleanup;
  402. }
  403. else
  404. {
  405. KdcLocateReferralCacheEntry(
  406. DestinationDomain,
  407. KDC_TRUSTED_REALM,
  408. &CacheFlags
  409. );
  410. }
  411. }
  412. else if (CacheFlags == KDC_UNTRUSTED_REALM)
  413. {
  414. DebugLog((DEB_ERROR,
  415. "Checking for X Forest on Untrusted Realm %wZ\n",
  416. DestinationDomain
  417. ));
  418. KerbErr = KDC_ERR_PATH_NOT_ACCEPTED;
  419. goto Cleanup;
  420. }
  421. //
  422. // Now, we're pretty sure we're going to hit this other forest,
  423. // somewhere. For SPNs, we've got to find the root domain of our forest
  424. // to finish off the x realm transaction. For UPNs, just
  425. // return the cracked domain.
  426. //
  427. if ((NameFlags & KDC_NAME_SERVER) != 0)
  428. {
  429. UNICODE_STRING ForestRoot = {0};
  430. Status = SecData.GetKdcForestRoot(&ForestRoot);
  431. if (!NT_SUCCESS(Status))
  432. {
  433. goto Cleanup;
  434. }
  435. KerbErr = KdcFindReferralTarget(
  436. ReferralTarget,
  437. ReferralRealm,
  438. pExtendedError,
  439. &ForestRoot,
  440. FALSE, // we'll accept closest
  441. FALSE // Outbound.
  442. );
  443. if (!KERB_SUCCESS(KerbErr))
  444. {
  445. DebugLog((DEB_ERROR, "Couldn't find referral info for root of forest \n"));
  446. goto Cleanup;
  447. }
  448. //
  449. // swap w/ our dns domain for referral realm, unless we're
  450. // processing a UPN
  451. //
  452. KerbFreeString(ReferralRealm);
  453. KerbFreeString(&ForestRoot);
  454. }
  455. KerbDuplicateString(
  456. ReferralRealm,
  457. DestinationDomain
  458. );
  459. Cleanup:
  460. KerbFreeString(&ServiceName);
  461. if (KrbtgtSpn != NULL)
  462. {
  463. MIDL_user_free(KrbtgtSpn);
  464. }
  465. return ( KerbErr );
  466. }
  467. //+-------------------------------------------------------------------------
  468. //
  469. // Function: KdcFindReferralTarget
  470. //
  471. // Synopsis: Takes a domain name as a parameter and returns information
  472. // in the closest available domain. For heirarchical links,
  473. // this would be a parent or child. If a cross link is available,
  474. // this might be the other side of a cross link. For inter-
  475. // organization links, this might be a whole different tree
  476. //
  477. // Effects:
  478. //
  479. // Arguments: ReferralTarget - Receives ticket info for target domain
  480. // ReferralRealm - Receives realm name of referral realm, if present
  481. // DestinationDomain - Target domain name
  482. // ExactMatch - The target domain has to be trusted by this domain
  483. //
  484. //
  485. // Requires:
  486. //
  487. // Returns:
  488. //
  489. // Notes:
  490. //
  491. //--------------------------------------------------------------------------
  492. KERBERR
  493. KdcFindReferralTarget(
  494. OUT PKDC_TICKET_INFO ReferralTarget,
  495. OUT OPTIONAL PUNICODE_STRING ReferralRealm,
  496. OUT PKERB_EXT_ERROR pExtendedError,
  497. IN PUNICODE_STRING DestinationDomain,
  498. IN BOOLEAN ExactMatch,
  499. IN BOOLEAN InboundWanted
  500. )
  501. {
  502. KERBERR KerbErr = KDC_ERR_NONE;
  503. PKDC_DOMAIN_INFO DomainInfo = NULL;
  504. PKDC_DOMAIN_INFO ClosestRoute = NULL;
  505. UNICODE_STRING TempRealmName;
  506. BOOLEAN fListLocked = FALSE;
  507. TRACE(KDC, KdcFindReferralTarget, DEB_FUNCTION);
  508. RtlInitUnicodeString(
  509. ReferralRealm,
  510. NULL
  511. );
  512. D_DebugLog((DEB_TRACE,"Generating referral for target %wZ\n",DestinationDomain));
  513. if (InboundWanted)
  514. {
  515. KdcLockDomainList();
  516. KerbErr = KdcLookupDomainName(
  517. &DomainInfo,
  518. DestinationDomain,
  519. &KdcDomainList
  520. );
  521. if (!KERB_SUCCESS(KerbErr) || ((DomainInfo->Flags & KDC_TRUST_INBOUND) == 0))
  522. {
  523. DebugLog((DEB_WARN,"Failed to find inbound referral target %wZ\n",DestinationDomain));
  524. FILL_EXT_ERROR(pExtendedError, STATUS_KDC_UNABLE_TO_REFER, FILENO, __LINE__);
  525. KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN;
  526. fListLocked = TRUE;
  527. goto Cleanup;
  528. }
  529. //
  530. // Set the closest route to be this domain & add a reference for
  531. // the extra pointer
  532. //
  533. KdcReferenceDomainInfo(DomainInfo);
  534. ClosestRoute = DomainInfo;
  535. KdcUnlockDomainList();
  536. }
  537. else
  538. {
  539. //
  540. // Check the list of domains for the target
  541. //
  542. KdcLockDomainList();
  543. KerbErr = KdcLookupDomainRoute(
  544. &DomainInfo,
  545. &ClosestRoute,
  546. DestinationDomain,
  547. &KdcDomainList
  548. );
  549. KdcUnlockDomainList();
  550. if (!KERB_SUCCESS(KerbErr))
  551. {
  552. DebugLog((DEB_WARN,"Failed to find referral target %wZ\n",DestinationDomain));
  553. FILL_EXT_ERROR(pExtendedError, STATUS_KDC_UNABLE_TO_REFER, FILENO, __LINE__);
  554. goto Cleanup;
  555. }
  556. //
  557. // Check to see if we needed & got an exact match
  558. //
  559. if (ExactMatch &&
  560. (DomainInfo != ClosestRoute))
  561. {
  562. DebugLog((DEB_ERROR,"Needed exact match and got a transitively-trusted domain.\n" ));
  563. FILL_EXT_ERROR(pExtendedError, STATUS_KDC_UNABLE_TO_REFER, FILENO, __LINE__);
  564. KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN;
  565. goto Cleanup;
  566. }
  567. }
  568. //
  569. // Return the referral realm, if present
  570. //
  571. if (ARGUMENT_PRESENT(ReferralRealm))
  572. {
  573. if (!NT_SUCCESS(KerbDuplicateString(
  574. ReferralRealm,
  575. &DomainInfo->DnsName
  576. )))
  577. {
  578. KerbErr = KRB_ERR_GENERIC;
  579. goto Cleanup;
  580. }
  581. }
  582. //
  583. // Now get the ticket info for the domain
  584. //
  585. KerbErr = KdcGetTicketInfoForDomain(
  586. ReferralTarget,
  587. pExtendedError,
  588. ClosestRoute,
  589. InboundWanted ? Inbound : Outbound
  590. );
  591. if (!KERB_SUCCESS(KerbErr))
  592. {
  593. DebugLog((DEB_ERROR,"Failed to get ticket info for domain %wZ: 0x%x. %ws, line %d\n",
  594. DestinationDomain, KerbErr , __FILE__, __LINE__ ));
  595. goto Cleanup;
  596. }
  597. Cleanup:
  598. if (DomainInfo != NULL)
  599. {
  600. KdcDereferenceDomainInfo(DomainInfo);
  601. }
  602. if (ClosestRoute != NULL)
  603. {
  604. KdcDereferenceDomainInfo(ClosestRoute);
  605. }
  606. if (fListLocked)
  607. {
  608. KdcUnlockDomainList();
  609. }
  610. if (!KERB_SUCCESS(KerbErr) && ARGUMENT_PRESENT(ReferralRealm))
  611. {
  612. KerbFreeString(ReferralRealm);
  613. }
  614. //
  615. // Remap the error
  616. //
  617. if (KerbErr == KDC_ERR_S_PRINCIPAL_UNKNOWN)
  618. {
  619. KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN;
  620. }
  621. return(KerbErr);
  622. }
  623. //+-------------------------------------------------------------------------
  624. //
  625. // Function: KdcGetTicketInfoForDomain
  626. //
  627. // Synopsis: Retrieves the ticket information for a domain
  628. //
  629. // Effects:
  630. //
  631. // Arguments:
  632. //
  633. // Requires:
  634. //
  635. // Returns:
  636. //
  637. // Notes:
  638. //
  639. //
  640. //--------------------------------------------------------------------------
  641. KERBERR
  642. KdcGetTicketInfoForDomain(
  643. OUT PKDC_TICKET_INFO TicketInfo,
  644. OUT PKERB_EXT_ERROR pExtendedError,
  645. IN PKDC_DOMAIN_INFO DomainInfo,
  646. IN KDC_DOMAIN_INFO_DIRECTION Direction
  647. )
  648. {
  649. PLSAPR_TRUSTED_DOMAIN_INFO TrustInfo = NULL;
  650. PLSAPR_AUTH_INFORMATION AuthInfo = NULL;
  651. PLSAPR_AUTH_INFORMATION OldAuthInfo = NULL;
  652. KERBERR KerbErr = KDC_ERR_NONE;
  653. NTSTATUS Status = STATUS_SUCCESS;
  654. UNICODE_STRING Password;
  655. ULONG PasswordLength = 0;
  656. LARGE_INTEGER CurrentTime;
  657. ULONG cbSid;
  658. TRACE(KDC, KdcGetTicketInfoForDomain, DEB_FUNCTION);
  659. //
  660. // Get information about the domain. Note that we use the dns name
  661. // field. For NT5 domains in the enterprise this will contain the
  662. // real DNS name. For non- tree domains it will contain the name from
  663. // the trusted domain object, so this call should always succeed.
  664. //
  665. Status = LsarQueryTrustedDomainInfoByName(
  666. GlobalPolicyHandle,
  667. (PLSAPR_UNICODE_STRING) &DomainInfo->DnsName,
  668. TrustedDomainAuthInformation,
  669. &TrustInfo
  670. );
  671. if (!NT_SUCCESS(Status))
  672. {
  673. //
  674. // If the domain didn't exist, we have a problem because our
  675. // cache is out of date. Or, we're loooking for our domain.. this
  676. // is always going to return STATUS_OBJECT_NAME_NOT_FOUND
  677. //
  678. //
  679. // WAS BUG: reload the cache -- this is handled in the call to
  680. // LSAIKerberosRegisterTrustNotification(), which will then
  681. // reload the cache using KdcTrustChangeCallback(). As long
  682. // as this callback is solid (?), we should never fail the
  683. // above. If needed, we can revisit. -TS
  684. //
  685. if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
  686. {
  687. DebugLog((DEB_ERROR,"Domain %wZ in cache but object doesn't exist. %ws, line %d\n",
  688. &DomainInfo->DnsName, THIS_FILE, __LINE__ ));
  689. KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN;
  690. }
  691. else
  692. {
  693. DebugLog((DEB_ERROR,"Failed to query domain info for %wZ: 0x%x. %ws, line %d\n",
  694. &DomainInfo->DnsName, Status, THIS_FILE, __LINE__ ));
  695. KerbErr = KRB_ERR_GENERIC;
  696. }
  697. FILL_EXT_ERROR(pExtendedError, Status, FILENO, __LINE__);
  698. goto Cleanup;
  699. }
  700. //
  701. // Note: Kerberos direction is opposite normal direction
  702. //
  703. if (Direction == Outbound)
  704. {
  705. AuthInfo = TrustInfo->TrustedAuthInfo.IncomingAuthenticationInformation;
  706. OldAuthInfo = TrustInfo->TrustedAuthInfo.IncomingPreviousAuthenticationInformation;
  707. }
  708. else
  709. {
  710. AuthInfo = TrustInfo->TrustedAuthInfo.OutgoingAuthenticationInformation;
  711. OldAuthInfo = TrustInfo->TrustedAuthInfo.OutgoingPreviousAuthenticationInformation;
  712. }
  713. if (AuthInfo == NULL)
  714. {
  715. DebugLog((DEB_ERROR,"No auth info for this trust: %wZ. %ws, line %d\n",
  716. &DomainInfo->DnsName, THIS_FILE, __LINE__ ));
  717. FILL_EXT_ERROR(pExtendedError, STATUS_TRUSTED_DOMAIN_FAILURE, FILENO, __LINE__);
  718. KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN;
  719. goto Cleanup;
  720. }
  721. //
  722. // Check the last update time. If the new auth info is too new, we want
  723. // to keep using the old one.
  724. //
  725. if (OldAuthInfo != NULL)
  726. {
  727. GetSystemTimeAsFileTime((PFILETIME)&CurrentTime);
  728. if (CurrentTime.QuadPart - AuthInfo->LastUpdateTime.QuadPart < SecData.KdcDomainPasswordReplSkew().QuadPart)
  729. {
  730. PLSAPR_AUTH_INFORMATION TempAuthInfo;
  731. //
  732. // Swap current & old auth info to encrypt tickets with old password
  733. //
  734. TempAuthInfo = AuthInfo;
  735. AuthInfo = OldAuthInfo;
  736. OldAuthInfo = TempAuthInfo;
  737. }
  738. }
  739. //
  740. // So now that we have the auth info we need to build a ticket info
  741. //
  742. Password.Length = Password.MaximumLength = (USHORT) AuthInfo->AuthInfoLength;
  743. Password.Buffer = (LPWSTR) AuthInfo->AuthInfo;
  744. Status = KdcBuildPasswordList(
  745. &Password,
  746. &DomainInfo->DnsName,
  747. SecData.KdcDnsRealmName(),
  748. DomainTrustAccount,
  749. NULL, // no stored creds
  750. 0, // no stored creds
  751. FALSE, // don't marshall
  752. DomainInfo->Type != TRUST_TYPE_MIT, // don;t include builtin crypt types for mit trusts,
  753. (AuthInfo->AuthType & TRUST_AUTH_TYPE_NT4OWF) ? KERB_PRIMARY_CRED_OWF_ONLY : 0,
  754. Direction,
  755. &TicketInfo->Passwords,
  756. &PasswordLength
  757. );
  758. if (!NT_SUCCESS(Status))
  759. {
  760. FILL_EXT_ERROR(pExtendedError, Status, FILENO, __LINE__);
  761. KerbErr = KRB_ERR_GENERIC;
  762. goto Cleanup;
  763. }
  764. //
  765. // Build the old password list as well
  766. //
  767. if (OldAuthInfo != NULL)
  768. {
  769. Password.Length = Password.MaximumLength = (USHORT) OldAuthInfo->AuthInfoLength;
  770. Password.Buffer = (LPWSTR) OldAuthInfo->AuthInfo;
  771. Status = KdcBuildPasswordList(
  772. &Password,
  773. &DomainInfo->DnsName,
  774. SecData.KdcDnsRealmName(),
  775. DomainTrustAccount,
  776. NULL,
  777. 0,
  778. FALSE, // don't marshall
  779. DomainInfo->Type != TRUST_TYPE_MIT, // don;t include builtin crypt types for mit trusts,
  780. (OldAuthInfo->AuthType & TRUST_AUTH_TYPE_NT4OWF) ? KERB_PRIMARY_CRED_OWF_ONLY : 0,
  781. Direction,
  782. &TicketInfo->OldPasswords,
  783. &PasswordLength
  784. );
  785. if (!NT_SUCCESS(Status))
  786. {
  787. FILL_EXT_ERROR(pExtendedError, Status, FILENO, __LINE__);
  788. KerbErr = KRB_ERR_GENERIC;
  789. goto Cleanup;
  790. }
  791. }
  792. if (!NT_SUCCESS(KerbDuplicateString(
  793. &TicketInfo->AccountName,
  794. &DomainInfo->DnsName
  795. )))
  796. {
  797. KerbErr = KRB_ERR_GENERIC;
  798. goto Cleanup;
  799. }
  800. //
  801. // BUG 73479: need to get ticket options
  802. //
  803. TicketInfo->fTicketOpts = AUTH_REQ_PER_USER_FLAGS |
  804. AUTH_REQ_ALLOW_NOADDRESS |
  805. AUTH_REQ_ALLOW_ENC_TKT_IN_SKEY |
  806. AUTH_REQ_ALLOW_VALIDATE |
  807. AUTH_REQ_OK_AS_DELEGATE;
  808. if ((DomainInfo->Attributes & TRUST_ATTRIBUTE_NON_TRANSITIVE) == 0)
  809. {
  810. TicketInfo->fTicketOpts |= AUTH_REQ_TRANSITIVE_TRUST;
  811. }
  812. TicketInfo->PasswordExpires = tsInfinity;
  813. TicketInfo->UserAccountControl = USER_INTERDOMAIN_TRUST_ACCOUNT;
  814. if (DomainInfo->Sid)
  815. {
  816. cbSid = RtlLengthSid(DomainInfo->Sid);
  817. TicketInfo->TrustSid = (PSID) MIDL_user_allocate(cbSid);
  818. if (TicketInfo->TrustSid == NULL)
  819. {
  820. KerbErr = KRB_ERR_GENERIC;
  821. goto Cleanup;
  822. }
  823. Status = RtlCopySid (
  824. cbSid,
  825. TicketInfo->TrustSid,
  826. DomainInfo->Sid
  827. );
  828. if (!NT_SUCCESS(Status))
  829. {
  830. KerbErr = KRB_ERR_GENERIC;
  831. goto Cleanup;
  832. }
  833. }
  834. TicketInfo->TrustAttributes = DomainInfo->Attributes;
  835. //
  836. // Add trusted forest UNICODE STRING onto ticket info
  837. // if its Xforest
  838. //
  839. if ((DomainInfo->Attributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE ) != 0)
  840. {
  841. TicketInfo->TrustAttributes = TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
  842. KerbErr = KerbDuplicateString(
  843. &(TicketInfo->TrustedForest),
  844. &DomainInfo->DnsName
  845. );
  846. if (!KERB_SUCCESS(KerbErr))
  847. {
  848. goto Cleanup;
  849. }
  850. }
  851. Cleanup:
  852. if (TrustInfo != NULL)
  853. {
  854. LsaIFree_LSAPR_TRUSTED_DOMAIN_INFO(
  855. TrustedDomainAuthInformation,
  856. TrustInfo
  857. );
  858. }
  859. if (!KERB_SUCCESS(KerbErr))
  860. {
  861. FreeTicketInfo(TicketInfo);
  862. }
  863. return(KerbErr);
  864. }
  865. //+-------------------------------------------------------------------------
  866. //
  867. // Function: KdcLookupDomainName
  868. //
  869. // Synopsis: Looks up a domain name in the list of domains and returns
  870. // the domain info
  871. //
  872. // Effects:
  873. //
  874. // Arguments:
  875. //
  876. // Requires:
  877. //
  878. // Returns:
  879. //
  880. // Notes:
  881. //
  882. //
  883. //--------------------------------------------------------------------------
  884. KERBERR
  885. KdcLookupDomainName(
  886. OUT PKDC_DOMAIN_INFO * DomainInfo,
  887. IN PUNICODE_STRING DomainName,
  888. IN PLIST_ENTRY DomainList
  889. )
  890. {
  891. PLIST_ENTRY ListEntry;
  892. PKDC_DOMAIN_INFO Domain;
  893. TRACE(KDC, KdcLookupDomainName, DEB_FUNCTION);
  894. for (ListEntry = DomainList->Flink;
  895. ListEntry != DomainList ;
  896. ListEntry = ListEntry->Flink )
  897. {
  898. Domain = (PKDC_DOMAIN_INFO) CONTAINING_RECORD(ListEntry, KDC_DOMAIN_INFO, Next);
  899. if (KerbCompareUnicodeRealmNames(
  900. DomainName,
  901. &Domain->DnsName
  902. ) || // case insensitive
  903. RtlEqualUnicodeString(
  904. DomainName,
  905. &Domain->NetbiosName,
  906. TRUE)) // case insensitive
  907. {
  908. KdcReferenceDomainInfo(Domain);
  909. *DomainInfo = Domain;
  910. return(KDC_ERR_NONE);
  911. }
  912. }
  913. return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
  914. }
  915. //+-------------------------------------------------------------------------
  916. //
  917. // Function: KdcLookupDomainRoute
  918. //
  919. // Synopsis: Looks up a domain name in the list of domains and returns
  920. // the domain info for the closest domain.
  921. //
  922. // Effects:
  923. //
  924. // Arguments:
  925. //
  926. // Requires:
  927. //
  928. // Returns:
  929. //
  930. // Notes:
  931. //
  932. //
  933. //--------------------------------------------------------------------------
  934. KERBERR
  935. KdcLookupDomainRoute(
  936. OUT PKDC_DOMAIN_INFO * DomainInfo,
  937. OUT PKDC_DOMAIN_INFO * ClosestRoute,
  938. IN PUNICODE_STRING DomainName,
  939. IN PLIST_ENTRY DomainList
  940. )
  941. {
  942. KERBERR KerbErr;
  943. PKDC_DOMAIN_INFO Domain;
  944. TRACE(KDC, KdcLookupDomainRoute, DEB_FUNCTION);
  945. KerbErr = KdcLookupDomainName(
  946. &Domain,
  947. DomainName,
  948. DomainList
  949. );
  950. if (KERB_SUCCESS(KerbErr))
  951. {
  952. if (Domain->ClosestRoute != NULL)
  953. {
  954. *DomainInfo = Domain;
  955. // If the closest route is this domain, then cheat and send back
  956. // the closest domain as the domain requested.
  957. if (KerbCompareUnicodeRealmNames(&(Domain->ClosestRoute->DnsName), SecData.KdcDnsRealmName()))
  958. {
  959. *ClosestRoute = Domain;
  960. }
  961. else
  962. {
  963. *ClosestRoute = Domain->ClosestRoute;
  964. }
  965. KdcReferenceDomainInfo(*ClosestRoute);
  966. return(KDC_ERR_NONE);
  967. }
  968. else
  969. {
  970. KdcDereferenceDomainInfo(Domain);
  971. DebugLog((DEB_WARN,"Asked for referral to %wZ domain, in organization but unreachable\n",
  972. DomainName ));
  973. KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN;
  974. }
  975. }
  976. return(KerbErr);
  977. }
  978. //+-------------------------------------------------------------------------
  979. //
  980. // Function: KdcLookupDomainByDnsName
  981. //
  982. // Synopsis: Looks up a domain name in the list of domains and returns
  983. // the domain info
  984. //
  985. // Effects:
  986. //
  987. // Arguments:
  988. //
  989. // Requires:
  990. //
  991. // Returns:
  992. //
  993. // Notes:
  994. //
  995. //
  996. //--------------------------------------------------------------------------
  997. PKDC_DOMAIN_INFO
  998. KdcLookupDomainByDnsName(
  999. IN PUNICODE_STRING DnsDomainName,
  1000. IN PLIST_ENTRY DomainList
  1001. )
  1002. {
  1003. PLIST_ENTRY ListEntry;
  1004. PKDC_DOMAIN_INFO Domain;
  1005. TRACE(KDC, KdcLookupDomainName, DEB_FUNCTION);
  1006. for (ListEntry = DomainList->Flink;
  1007. ListEntry != DomainList ;
  1008. ListEntry = ListEntry->Flink )
  1009. {
  1010. Domain = (PKDC_DOMAIN_INFO) CONTAINING_RECORD(ListEntry, KDC_DOMAIN_INFO, Next);
  1011. if (KerbCompareUnicodeRealmNames(
  1012. DnsDomainName,
  1013. &Domain->DnsName
  1014. ))
  1015. {
  1016. return(Domain);
  1017. }
  1018. }
  1019. return(NULL);
  1020. }
  1021. #if DBG
  1022. VOID
  1023. DebugDumpDomainList(
  1024. IN PLIST_ENTRY DomainList
  1025. )
  1026. {
  1027. PLIST_ENTRY ListEntry;
  1028. PKDC_DOMAIN_INFO Domain;
  1029. TRACE(KDC, KdcLookupDomainName, DEB_FUNCTION);
  1030. for (ListEntry = DomainList->Flink;
  1031. ListEntry != DomainList ;
  1032. ListEntry = ListEntry->Flink )
  1033. {
  1034. Domain = (PKDC_DOMAIN_INFO) CONTAINING_RECORD(ListEntry, KDC_DOMAIN_INFO, Next);
  1035. DebugLog((DEB_TRACE,"Domain %wZ:\n",&Domain->DnsName));
  1036. if (Domain->ClosestRoute == NULL)
  1037. {
  1038. D_DebugLog((DEB_TRACE,"\t no closest route\n"));
  1039. }
  1040. else
  1041. {
  1042. D_DebugLog((DEB_TRACE,"\t closest route = %wZ\n",&Domain->ClosestRoute->DnsName));
  1043. }
  1044. if (Domain->Parent == NULL)
  1045. {
  1046. D_DebugLog((DEB_TRACE,"\t no parent\n"));
  1047. }
  1048. else
  1049. {
  1050. D_DebugLog((DEB_TRACE,"\t parent = %wZ\n",&Domain->Parent->DnsName));
  1051. }
  1052. }
  1053. }
  1054. #endif // DBG
  1055. //+-------------------------------------------------------------------------
  1056. //
  1057. // Function: KdcRecurseAddTreeTrust
  1058. //
  1059. // Synopsis: Recursively adds a tree trust - adds it and then all its
  1060. // children.
  1061. //
  1062. // Effects: Adds children depth-first
  1063. //
  1064. // Arguments:
  1065. //
  1066. // Requires:
  1067. //
  1068. // Returns:
  1069. //
  1070. // Notes:
  1071. //
  1072. //
  1073. //--------------------------------------------------------------------------
  1074. NTSTATUS
  1075. KdcRecurseAddTreeTrust(
  1076. IN PLIST_ENTRY DomainList,
  1077. IN PLSAPR_TREE_TRUST_INFO TreeTrust,
  1078. IN OPTIONAL PKDC_DOMAIN_INFO DomainInfo
  1079. )
  1080. {
  1081. PKDC_DOMAIN_INFO NewDomainInfo = NULL;
  1082. BOOLEAN Linked = FALSE;
  1083. NTSTATUS Status = STATUS_SUCCESS;
  1084. ULONG Index;
  1085. //
  1086. // Create new root trust
  1087. //
  1088. NewDomainInfo = (PKDC_DOMAIN_INFO) MIDL_user_allocate(sizeof(KDC_DOMAIN_INFO));
  1089. if (NewDomainInfo == NULL)
  1090. {
  1091. Status = STATUS_INSUFFICIENT_RESOURCES;
  1092. goto Cleanup;
  1093. }
  1094. RtlZeroMemory(
  1095. NewDomainInfo,
  1096. sizeof(KDC_DOMAIN_INFO)
  1097. );
  1098. Status = KerbDuplicateString(
  1099. &NewDomainInfo->DnsName,
  1100. (PUNICODE_STRING) &TreeTrust->DnsDomainName
  1101. );
  1102. if (!NT_SUCCESS(Status))
  1103. {
  1104. goto Cleanup;
  1105. }
  1106. //
  1107. // Uppercase the domain name here, as everything in the forest
  1108. // is uplevel.
  1109. //
  1110. Status = RtlUpcaseUnicodeString(
  1111. &NewDomainInfo->DnsName,
  1112. &NewDomainInfo->DnsName,
  1113. FALSE // don't allocate
  1114. );
  1115. if (!NT_SUCCESS(Status))
  1116. {
  1117. goto Cleanup;
  1118. }
  1119. Status = KerbDuplicateString(
  1120. &NewDomainInfo->NetbiosName,
  1121. (PUNICODE_STRING)&TreeTrust->FlatName
  1122. );
  1123. if (!NT_SUCCESS(Status))
  1124. {
  1125. goto Cleanup;
  1126. }
  1127. NewDomainInfo->Parent = DomainInfo;
  1128. //
  1129. // Insert into list
  1130. //
  1131. NewDomainInfo->References = 1;
  1132. InsertTailList(
  1133. DomainList,
  1134. &NewDomainInfo->Next
  1135. );
  1136. Linked = TRUE;
  1137. //
  1138. // Now recursively add all children
  1139. //
  1140. for (Index = 0; Index < TreeTrust->Children ; Index++ )
  1141. {
  1142. Status = KdcRecurseAddTreeTrust(
  1143. DomainList,
  1144. &TreeTrust->ChildDomains[Index],
  1145. NewDomainInfo
  1146. );
  1147. if (!NT_SUCCESS(Status))
  1148. {
  1149. goto Cleanup;
  1150. }
  1151. }
  1152. Cleanup:
  1153. if (!Linked && (NewDomainInfo != NULL))
  1154. {
  1155. KdcFreeDomainInfo(NewDomainInfo);
  1156. }
  1157. return(Status);
  1158. }
  1159. //+-------------------------------------------------------------------------
  1160. //
  1161. // Function: KdcInsertDomainTrustIntoTree
  1162. //
  1163. // Synopsis: Adds trust information to the tree of domains. For domains
  1164. // which are in the tree, trust direction
  1165. //
  1166. // Effects:
  1167. //
  1168. // Arguments:
  1169. //
  1170. // Requires:
  1171. //
  1172. // Returns:
  1173. //
  1174. // Notes:
  1175. //
  1176. //
  1177. //--------------------------------------------------------------------------
  1178. NTSTATUS
  1179. KdcInsertDomainTrustIntoForest(
  1180. IN OUT PLIST_ENTRY DomainList,
  1181. IN PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX NewTrust
  1182. )
  1183. {
  1184. NTSTATUS Status = STATUS_SUCCESS;
  1185. PKDC_DOMAIN_INFO DomainInfo = NULL;
  1186. PKDC_DOMAIN_INFO NewDomainInfo = NULL;
  1187. ULONG cbSid;
  1188. TRACE(KDC, KdcInsertDomainTrustIntoForest, DEB_FUNCTION);
  1189. D_DebugLog((DEB_T_DOMAIN, "Inserting trusted domain into domain list: %wZ\n",&NewTrust->Name));
  1190. //
  1191. // Check to see if the domain is already in the tree
  1192. //
  1193. DomainInfo = KdcLookupDomainByDnsName(
  1194. (PUNICODE_STRING) &NewTrust->Name,
  1195. DomainList
  1196. );
  1197. if (DomainInfo == NULL)
  1198. {
  1199. //
  1200. // Allocate and fill in a new domain structure for this domain.
  1201. // It is not part of the tree so the GUID will be zero.
  1202. //
  1203. NewDomainInfo = (PKDC_DOMAIN_INFO) MIDL_user_allocate(sizeof(KDC_DOMAIN_INFO));
  1204. if (NewDomainInfo == NULL)
  1205. {
  1206. Status = STATUS_INSUFFICIENT_RESOURCES;
  1207. goto Cleanup;
  1208. }
  1209. RtlZeroMemory(
  1210. NewDomainInfo,
  1211. sizeof(KDC_DOMAIN_INFO)
  1212. );
  1213. //
  1214. // Copy in the names of the domain
  1215. //
  1216. Status = KerbDuplicateString(
  1217. &NewDomainInfo->DnsName,
  1218. (PUNICODE_STRING) &NewTrust->Name
  1219. );
  1220. if (!NT_SUCCESS(Status))
  1221. {
  1222. goto Cleanup;
  1223. }
  1224. //
  1225. // If the trust is uplevel, then uppercase
  1226. //
  1227. if (NewTrust->TrustType == TRUST_TYPE_UPLEVEL)
  1228. {
  1229. Status = RtlUpcaseUnicodeString(
  1230. &NewDomainInfo->DnsName,
  1231. &NewDomainInfo->DnsName,
  1232. FALSE // don't allocate
  1233. );
  1234. if (!NT_SUCCESS(Status))
  1235. {
  1236. goto Cleanup;
  1237. }
  1238. }
  1239. Status = KerbDuplicateString(
  1240. &NewDomainInfo->NetbiosName,
  1241. (PUNICODE_STRING) &NewTrust->FlatName
  1242. );
  1243. if (!NT_SUCCESS(Status))
  1244. {
  1245. goto Cleanup;
  1246. }
  1247. NewDomainInfo->References = 1;
  1248. InsertTailList(DomainList, &NewDomainInfo->Next);
  1249. DomainInfo = NewDomainInfo;
  1250. NewDomainInfo = NULL;
  1251. }
  1252. DomainInfo->Attributes = NewTrust->TrustAttributes;
  1253. DomainInfo->Type = NewTrust->TrustType;
  1254. //
  1255. // If this is not an inbound-only trust, the closest route to get here
  1256. // is to go directly here.
  1257. //
  1258. if ((NewTrust->TrustDirection & TRUST_DIRECTION_INBOUND) != 0)
  1259. {
  1260. DomainInfo->ClosestRoute = DomainInfo;
  1261. }
  1262. //
  1263. // Note the confusion of inbound and outbound. For Kerberos inbound is
  1264. // the opposite of for trust objects.
  1265. //
  1266. if ((NewTrust->TrustDirection & TRUST_DIRECTION_OUTBOUND) != 0)
  1267. {
  1268. DomainInfo->Flags |= KDC_TRUST_INBOUND;
  1269. if ((((DomainInfo->Attributes & TRUST_ATTRIBUTE_FILTER_SIDS) != 0) &&
  1270. ((DomainInfo->Type & TRUST_TYPE_UPLEVEL) != 0)) ||
  1271. ((DomainInfo->Attributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) != 0))
  1272. {
  1273. if (NewTrust->Sid != NULL)
  1274. {
  1275. cbSid = RtlLengthSid(NewTrust->Sid);
  1276. DomainInfo->Sid = (PSID) MIDL_user_allocate(cbSid);
  1277. if (DomainInfo->Sid == NULL)
  1278. {
  1279. Status = STATUS_INSUFFICIENT_RESOURCES;
  1280. goto Cleanup;
  1281. }
  1282. Status = RtlCopySid (
  1283. cbSid,
  1284. DomainInfo->Sid,
  1285. NewTrust->Sid
  1286. );
  1287. if (!NT_SUCCESS(Status))
  1288. {
  1289. goto Cleanup;
  1290. }
  1291. if ((DomainInfo->Attributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) != 0)
  1292. {
  1293. SecData.SetCrossForestEnabled(TRUE);
  1294. }
  1295. }
  1296. }
  1297. }
  1298. Cleanup:
  1299. if (NewDomainInfo != NULL)
  1300. {
  1301. KdcFreeDomainInfo(NewDomainInfo);
  1302. }
  1303. return(Status);
  1304. }
  1305. //+-------------------------------------------------------------------------
  1306. //
  1307. // Function: KdcComputeShortestDomainPaths
  1308. //
  1309. // Synopsis: Compute the shortest path for each domain in the tree
  1310. // by traversing up until either the local domain or
  1311. // a parent of it is located, and then traverse down.
  1312. //
  1313. // Effects:
  1314. //
  1315. // Arguments:
  1316. //
  1317. // Requires:
  1318. //
  1319. // Returns:
  1320. //
  1321. // Notes:
  1322. //
  1323. //
  1324. //--------------------------------------------------------------------------
  1325. NTSTATUS
  1326. KdcComputeShortestDomainPaths(
  1327. IN PLIST_ENTRY DomainList
  1328. )
  1329. {
  1330. NTSTATUS Status = STATUS_SUCCESS;
  1331. PKDC_DOMAIN_INFO * ParentList = NULL;
  1332. ULONG CountOfParents = 0, Index;
  1333. PKDC_DOMAIN_INFO LocalDomain;
  1334. PKDC_DOMAIN_INFO WorkingDomain;
  1335. PKDC_DOMAIN_INFO ParentDomain;
  1336. PLIST_ENTRY ListEntry;
  1337. BOOLEAN FoundParent;
  1338. ULONG TouchedIndex = 1;
  1339. TRACE(KDC, KdcComputeShortestDomainPaths, DEB_FUNCTION);
  1340. //
  1341. // If the tree is empty, then there are no shortest paths to compute.
  1342. //
  1343. if (IsListEmpty(DomainList))
  1344. {
  1345. return(STATUS_SUCCESS);
  1346. }
  1347. //
  1348. // Calculate the number of parents & grandparents of the local domain.
  1349. //
  1350. LocalDomain = KdcLookupDomainByDnsName(
  1351. SecData.KdcDnsRealmName(),
  1352. DomainList
  1353. );
  1354. if (LocalDomain == NULL)
  1355. {
  1356. DebugLog((DEB_ERROR,"No forest info for local domain - no transitive trust. %ws, line %d\n", THIS_FILE, __LINE__));
  1357. return(STATUS_SUCCESS);
  1358. }
  1359. LocalDomain->ClosestRoute = LocalDomain;
  1360. WorkingDomain = LocalDomain->Parent;
  1361. while (WorkingDomain != NULL)
  1362. {
  1363. //
  1364. // Stop if we've come to this domain before.
  1365. //
  1366. ReportServiceEvent(
  1367. EVENTLOG_ERROR_TYPE,
  1368. NEW_KDCEVENT_TRUSTLIST_LOOP,
  1369. sizeof(NTSTATUS),
  1370. &Status,
  1371. 0,
  1372. NULL
  1373. );
  1374. if (WorkingDomain->Touched == TouchedIndex)
  1375. {
  1376. DebugLog((DEB_ERROR,"Loop in trust list! %ws, line %d\n", THIS_FILE, __LINE__));
  1377. break;
  1378. }
  1379. WorkingDomain->Touched = TouchedIndex;
  1380. CountOfParents++;
  1381. WorkingDomain = WorkingDomain->Parent;
  1382. }
  1383. //
  1384. // If we had any parents, build an array of all our parents.
  1385. //
  1386. if (CountOfParents != 0)
  1387. {
  1388. ParentList = (PKDC_DOMAIN_INFO *) MIDL_user_allocate(CountOfParents * sizeof(PKDC_DOMAIN_INFO));
  1389. if (ParentList == NULL)
  1390. {
  1391. Status = STATUS_INSUFFICIENT_RESOURCES;
  1392. goto Cleanup;
  1393. }
  1394. //
  1395. // Store each parent in the list.
  1396. //
  1397. Index = 0;
  1398. TouchedIndex++;
  1399. WorkingDomain = LocalDomain->Parent;
  1400. while (WorkingDomain != NULL)
  1401. {
  1402. //
  1403. // Stop if we've come to this domain before.
  1404. //
  1405. if (WorkingDomain->Touched == TouchedIndex)
  1406. {
  1407. DebugLog((DEB_ERROR,"Loop in trust list! %ws, line %d\n", THIS_FILE, __LINE__));
  1408. break;
  1409. }
  1410. //
  1411. // Skip domains that have no domain info. They have probably been
  1412. // deleted.
  1413. //
  1414. WorkingDomain->Touched = TouchedIndex;
  1415. ParentList[Index++] = WorkingDomain;
  1416. WorkingDomain = WorkingDomain->Parent;
  1417. }
  1418. }
  1419. //
  1420. // Now loop through every domain in the tree. For each domain, if it
  1421. // is not trusted, check it against the list of parents. If it is a
  1422. // parent, walk down the list until a trusted domain is found.
  1423. //
  1424. for (ListEntry = DomainList->Flink;
  1425. ListEntry != DomainList;
  1426. ListEntry = ListEntry->Flink )
  1427. {
  1428. WorkingDomain = (PKDC_DOMAIN_INFO) CONTAINING_RECORD(ListEntry, KDC_DOMAIN_INFO, Next);
  1429. ParentDomain = WorkingDomain;
  1430. //
  1431. // Walk up from this domain until we find a common ancestor with
  1432. // the local domain
  1433. //
  1434. TouchedIndex++;
  1435. while (ParentDomain != NULL)
  1436. {
  1437. //
  1438. // Stop if we've come to this domain before.
  1439. //
  1440. if (ParentDomain->Touched == TouchedIndex)
  1441. {
  1442. DebugLog((DEB_ERROR,"Loop in trust list! %ws, line %d\n", THIS_FILE, __LINE__));
  1443. break;
  1444. }
  1445. //
  1446. // Skip domains that have no domain info. They have probably been
  1447. // deleted.
  1448. //
  1449. ParentDomain->Touched = TouchedIndex;
  1450. //
  1451. // If the parent has a closest route, use it
  1452. //
  1453. if (ParentDomain->ClosestRoute != NULL)
  1454. {
  1455. WorkingDomain->ClosestRoute = ParentDomain->ClosestRoute;
  1456. D_DebugLog((DEB_T_DOMAIN, "Shortest route for domain %wZ is %wZ\n",
  1457. &WorkingDomain->DnsName,
  1458. &WorkingDomain->ClosestRoute->DnsName
  1459. ));
  1460. break;
  1461. }
  1462. //
  1463. // Look through the list of parents for this domain to see if it
  1464. // is trusted
  1465. //
  1466. Index = CountOfParents;
  1467. FoundParent = FALSE;
  1468. while (Index > 0)
  1469. {
  1470. Index--;
  1471. if (ParentList[Index] == ParentDomain)
  1472. {
  1473. //
  1474. // We found a domain that is a parent of
  1475. // ours
  1476. //
  1477. FoundParent = TRUE;
  1478. }
  1479. if (FoundParent && (ParentList[Index]->ClosestRoute != NULL))
  1480. {
  1481. WorkingDomain->ClosestRoute = ParentList[Index]->ClosestRoute;
  1482. break;
  1483. }
  1484. }
  1485. if (WorkingDomain->ClosestRoute != NULL)
  1486. {
  1487. D_DebugLog((DEB_T_DOMAIN, "Shortest route for domain %wZ is %wZ\n",
  1488. &WorkingDomain->DnsName,
  1489. &WorkingDomain->ClosestRoute->DnsName
  1490. ));
  1491. break;
  1492. }
  1493. ParentDomain = ParentDomain->Parent;
  1494. }
  1495. }
  1496. Cleanup:
  1497. if (ParentList != NULL)
  1498. {
  1499. MIDL_user_free(ParentList);
  1500. }
  1501. return(Status);
  1502. }
  1503. //+-------------------------------------------------------------------------
  1504. //
  1505. // Function: KerbFreeDomainList
  1506. //
  1507. // Synopsis: Frees a domain list element by element.
  1508. //
  1509. // Effects:
  1510. //
  1511. // Arguments:
  1512. //
  1513. // Requires:
  1514. //
  1515. // Returns:
  1516. //
  1517. // Notes:
  1518. //
  1519. //
  1520. //--------------------------------------------------------------------------
  1521. VOID
  1522. KdcFreeReferralCache(
  1523. IN PLIST_ENTRY ReferralCache
  1524. )
  1525. {
  1526. PREFERRAL_CACHE_ENTRY CacheEntry;
  1527. TRACE(KDC, KdcFreeReferralCache, DEB_FUNCTION);
  1528. if (ReferralCache->Flink != NULL)
  1529. {
  1530. while (!IsListEmpty(ReferralCache))
  1531. {
  1532. CacheEntry = CONTAINING_RECORD(ReferralCache->Flink, REFERRAL_CACHE_ENTRY, ListEntry );
  1533. RemoveEntryList(&CacheEntry->ListEntry);
  1534. InitializeListHead(&CacheEntry->ListEntry);
  1535. KdcDereferenceReferralCacheEntry(CacheEntry);
  1536. }
  1537. }
  1538. }
  1539. //+-------------------------------------------------------------------------
  1540. //
  1541. // Function: KerbFreeDomainList
  1542. //
  1543. // Synopsis: Frees a domain list element by element.
  1544. //
  1545. // Effects:
  1546. //
  1547. // Arguments:
  1548. //
  1549. // Requires:
  1550. //
  1551. // Returns:
  1552. //
  1553. // Notes:
  1554. //
  1555. //
  1556. //--------------------------------------------------------------------------
  1557. VOID
  1558. KdcFreeDomainList(
  1559. IN PLIST_ENTRY DomainList
  1560. )
  1561. {
  1562. PKDC_DOMAIN_INFO DomainInfo;
  1563. TRACE(KDC, KdcFreeDomainList, DEB_FUNCTION);
  1564. if (DomainList->Flink != NULL)
  1565. {
  1566. while (!IsListEmpty(DomainList))
  1567. {
  1568. DomainInfo = CONTAINING_RECORD(DomainList->Flink, KDC_DOMAIN_INFO, Next );
  1569. RemoveEntryList(&DomainInfo->Next);
  1570. InitializeListHead(&DomainInfo->Next);
  1571. KdcDereferenceDomainInfo(DomainInfo);
  1572. }
  1573. }
  1574. }
  1575. #ifdef DBG_BUILD_FOREST
  1576. VOID
  1577. DebugBuildDomainForest(
  1578. OUT PLSAPR_FOREST_TRUST_INFO * ForestInfo
  1579. )
  1580. {
  1581. PLSAPR_FOREST_TRUST_INFO ForestTrustInfo = NULL;
  1582. PLSAPR_TREE_TRUST_INFO ChildDomains = NULL;
  1583. PLSAPR_TREE_TRUST_INFO ChildRoot = NULL;
  1584. UNICODE_STRING TempString;
  1585. ULONG Index;
  1586. LPWSTR MsNames[4] = {L"ntdev.microsoft.com",L"alpamayo.ntdev.microsoft.com",L"annapurna.alpamayo.ntdev.microsoft.com",L"lhotse.annapurna.alpamayo.ntdev.microsoft.com" };
  1587. ForestTrustInfo = (PLSAPR_FOREST_TRUST_INFO) MIDL_user_allocate(sizeof(LSAPR_FOREST_TRUST_INFO));
  1588. RtlInitUnicodeString(
  1589. &TempString,
  1590. MsNames[0]
  1591. );
  1592. KerbDuplicateString( (PUNICODE_STRING)
  1593. &ForestTrustInfo->RootTrust.DnsDomainName,
  1594. &TempString
  1595. );
  1596. KerbDuplicateString( (PUNICODE_STRING)
  1597. &ForestTrustInfo->RootTrust.FlatName,
  1598. &TempString
  1599. );
  1600. ChildRoot = &ForestTrustInfo->RootTrust;
  1601. for (Index = 1; Index < 4 ; Index++ )
  1602. {
  1603. ChildRoot->Children = 1;
  1604. ChildRoot->ChildDomains = (PLSAPR_TREE_TRUST_INFO) MIDL_user_allocate(sizeof(LSAPR_TREE_TRUST_INFO));
  1605. RtlZeroMemory(
  1606. ChildRoot->ChildDomains,
  1607. sizeof(LSAPR_TREE_TRUST_INFO)
  1608. );
  1609. RtlInitUnicodeString(
  1610. &TempString,
  1611. MsNames[Index]
  1612. );
  1613. KerbDuplicateString( (PUNICODE_STRING)
  1614. &ChildRoot->ChildDomains[0].DnsDomainName,
  1615. &TempString
  1616. );
  1617. KerbDuplicateString( (PUNICODE_STRING)
  1618. &ChildRoot->ChildDomains[0].FlatName,
  1619. &TempString
  1620. );
  1621. ChildRoot = &ChildRoot->ChildDomains[0];
  1622. }
  1623. //
  1624. // Should be all done now
  1625. //
  1626. *ForestInfo = ForestTrustInfo;
  1627. }
  1628. #endif // DBG_BUILD_FOREST
  1629. //+-------------------------------------------------------------------------
  1630. //
  1631. // Function: KdcBuildDomainTree
  1632. //
  1633. // Synopsis: Enumerates the list of domains and inserts them
  1634. // all into a tree
  1635. //
  1636. // Effects:
  1637. //
  1638. // Arguments:
  1639. //
  1640. // Requires:
  1641. //
  1642. // Returns:
  1643. //
  1644. // Notes:
  1645. //
  1646. //
  1647. //--------------------------------------------------------------------------
  1648. NTSTATUS
  1649. KdcBuildDomainTree(
  1650. IN OUT PLIST_ENTRY DomainList
  1651. )
  1652. {
  1653. NTSTATUS Status;
  1654. ULONG Index;
  1655. PLSAPR_FOREST_TRUST_INFO ForestInfo = NULL;
  1656. LSAPR_TRUSTED_ENUM_BUFFER_EX TrustedBuffer;
  1657. LSA_ENUMERATION_HANDLE EnumContext = 0;
  1658. ULONG CountReturned;
  1659. TRACE(KDC, KdcBuildDomainList, DEB_FUNCTION);
  1660. InitializeListHead(DomainList);
  1661. RtlZeroMemory(
  1662. &TrustedBuffer,
  1663. sizeof(LSAPR_TRUSTED_ENUM_BUFFER_EX)
  1664. );
  1665. //
  1666. // Call the LSA to enumerate all the trees in the enterprise and insert
  1667. // them into the tree
  1668. //
  1669. #ifndef DBG_BUILD_FOREST
  1670. Status = LsaIQueryForestTrustInfo(
  1671. GlobalPolicyHandle,
  1672. &ForestInfo
  1673. );
  1674. //
  1675. // If we aren't part of a tree, we may get back STATUS_OBJECT_NAME_NOT_FOUND
  1676. // so this is o.k.
  1677. //
  1678. if (!NT_SUCCESS(Status))
  1679. {
  1680. if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
  1681. {
  1682. DebugLog((DEB_WARN,"No trust info (0x%x) continuing\n",Status));
  1683. Status = STATUS_SUCCESS;
  1684. }
  1685. else
  1686. {
  1687. goto Cleanup;
  1688. }
  1689. }
  1690. #else
  1691. DebugBuildDomainForest(&ForestInfo);
  1692. #endif
  1693. //
  1694. // Only use this if the information is usable - it is present
  1695. //
  1696. if (ForestInfo != NULL)
  1697. {
  1698. Status = KdcRecurseAddTreeTrust(
  1699. DomainList,
  1700. &ForestInfo->RootTrust,
  1701. NULL
  1702. );
  1703. if (!NT_SUCCESS(Status))
  1704. {
  1705. goto Cleanup;
  1706. }
  1707. Status = SecData.SetForestRoot(&(ForestInfo->RootTrust.DnsDomainName));
  1708. if (!NT_SUCCESS(Status))
  1709. {
  1710. goto Cleanup;
  1711. }
  1712. }
  1713. //
  1714. // Now add all the trusts in.
  1715. //
  1716. SecData.SetCrossForestEnabled(FALSE); // set this to FALSE for now
  1717. do
  1718. {
  1719. CountReturned = 0;
  1720. Status = LsarEnumerateTrustedDomainsEx(
  1721. GlobalPolicyHandle,
  1722. &EnumContext,
  1723. &TrustedBuffer,
  1724. 0xffffff // preferred maximum length
  1725. );
  1726. if (!NT_ERROR(Status))
  1727. {
  1728. //
  1729. // Call the LSA to enumerate all the trust relationships and integrate
  1730. // them into the tree
  1731. //
  1732. for (Index = 0; (Index < TrustedBuffer.EntriesRead) && NT_SUCCESS(Status) ; Index++ )
  1733. {
  1734. if (TrustedBuffer.EnumerationBuffer[Index].TrustType != TRUST_TYPE_DOWNLEVEL)
  1735. {
  1736. Status = KdcInsertDomainTrustIntoForest(
  1737. DomainList,
  1738. &TrustedBuffer.EnumerationBuffer[Index]
  1739. );
  1740. }
  1741. }
  1742. }
  1743. LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER_EX(&TrustedBuffer);
  1744. RtlZeroMemory(
  1745. &TrustedBuffer,
  1746. sizeof(LSAPR_TRUSTED_ENUM_BUFFER_EX)
  1747. );
  1748. } while (NT_SUCCESS(Status) && (CountReturned != 0));
  1749. if (NT_ERROR(Status))
  1750. {
  1751. if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
  1752. {
  1753. DebugLog((DEB_ERROR,"Failed to enumerate trusted domains: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  1754. goto Cleanup;
  1755. }
  1756. Status = STATUS_SUCCESS;
  1757. }
  1758. //
  1759. // Now compute the shortest path from each domain in the tree.
  1760. //
  1761. Status = KdcComputeShortestDomainPaths(
  1762. DomainList
  1763. );
  1764. if (!NT_SUCCESS(Status))
  1765. {
  1766. goto Cleanup;
  1767. }
  1768. #if DBG
  1769. DebugDumpDomainList(DomainList);
  1770. #endif
  1771. Cleanup:
  1772. if (ForestInfo != NULL)
  1773. {
  1774. LsaIFreeForestTrustInfo(ForestInfo);
  1775. }
  1776. if (!NT_SUCCESS(Status))
  1777. {
  1778. KdcFreeDomainList(DomainList);
  1779. }
  1780. return(Status);
  1781. }
  1782. //+-------------------------------------------------------------------------
  1783. //
  1784. // Function: KdcReloadDomainTree
  1785. //
  1786. // Synopsis: Reloads the domain tree when it has changed
  1787. //
  1788. // Effects:
  1789. //
  1790. // Arguments: Dummy - dummy argument requred for CreateThread calls
  1791. //
  1792. // Requires:
  1793. //
  1794. // Returns:
  1795. //
  1796. // Notes:
  1797. //
  1798. //
  1799. //--------------------------------------------------------------------------
  1800. ULONG
  1801. KdcReloadDomainTree(
  1802. PVOID Dummy
  1803. )
  1804. {
  1805. NTSTATUS Status = STATUS_SUCCESS;
  1806. LIST_ENTRY DomainList;
  1807. TRACE(KDC, KdcBuildDomainList, DEB_FUNCTION);
  1808. InitializeListHead(&DomainList);
  1809. Status = EnterApiCall();
  1810. if (!NT_SUCCESS(Status))
  1811. {
  1812. goto Cleanup;
  1813. }
  1814. if (!KdcReferralCacheInitialized)
  1815. {
  1816. Status = RtlInitializeCriticalSection(&KdcReferralCacheLock);
  1817. if (!NT_SUCCESS(Status))
  1818. {
  1819. goto Cleanup;
  1820. }
  1821. InitializeListHead(&KdcReferralCache);
  1822. KdcReferralCacheInitialized = TRUE;
  1823. }
  1824. D_DebugLog((DEB_TRACE,"About to reload domain tree\n"));
  1825. if (!KdcDomainListInitialized)
  1826. {
  1827. Status = RtlInitializeCriticalSection(&KdcDomainListLock);
  1828. if (!NT_SUCCESS(Status))
  1829. {
  1830. goto Cleanup;
  1831. }
  1832. InitializeListHead(&KdcDomainList);
  1833. KdcDomainListInitialized = TRUE;
  1834. }
  1835. Status = KdcBuildDomainTree(&DomainList);
  1836. if (NT_SUCCESS(Status))
  1837. {
  1838. KdcLockDomainList();
  1839. KdcFreeDomainList(&KdcDomainList);
  1840. KdcDomainList = DomainList;
  1841. DomainList.Flink->Blink = &KdcDomainList;
  1842. DomainList.Blink->Flink = &KdcDomainList;
  1843. KdcUnlockDomainList();
  1844. }
  1845. else
  1846. {
  1847. ReportServiceEvent(
  1848. EVENTLOG_ERROR_TYPE,
  1849. KDCEVENT_DOMAIN_LIST_UPDATE_FAILED,
  1850. sizeof(NTSTATUS),
  1851. &Status,
  1852. 0,
  1853. NULL
  1854. );
  1855. }
  1856. Cleanup:
  1857. LeaveApiCall();
  1858. return((ULONG)Status);
  1859. }
  1860. //+-------------------------------------------------------------------------
  1861. //
  1862. // Function: KdcTrustChangeCallback
  1863. //
  1864. // Synopsis: This is the callback that gets invoked with the Lsa has determined
  1865. // that the trust tree has changed. The call is made asynchronously.
  1866. //
  1867. // Effects: Potentially causes the trust tree to be rebuilt
  1868. //
  1869. // Arguments: DeltaType - Type of change to the trust tree
  1870. //
  1871. // Requires: Nothing
  1872. //
  1873. // Returns: VOID
  1874. //
  1875. // Notes:
  1876. //
  1877. //
  1878. //--------------------------------------------------------------------------
  1879. VOID
  1880. KdcTrustChangeCallback (
  1881. SECURITY_DB_DELTA_TYPE DeltaType
  1882. )
  1883. {
  1884. NTSTATUS Status;
  1885. TRACE(KDC, KdcTrustChangeCallback, DEB_FUNCTION);
  1886. if ( DeltaType == SecurityDbNew || DeltaType == SecurityDbDelete ||
  1887. DeltaType == SecurityDbChange) {
  1888. Status = KdcReloadDomainTree( NULL );
  1889. if (!NT_SUCCESS(Status)) {
  1890. DebugLog((DEB_ERROR,"KdcReloadDomainTree from callback failed with 0x%lx. %ws, line %d\n",
  1891. Status, THIS_FILE, __LINE__));
  1892. }
  1893. }
  1894. }
  1895. VOID
  1896. KdcLockDomainListFn(
  1897. )
  1898. {
  1899. KdcLockDomainList();
  1900. }
  1901. VOID
  1902. KdcUnlockDomainListFn(
  1903. )
  1904. {
  1905. KdcUnlockDomainList();
  1906. }