Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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