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.

643 lines
20 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Copyright (C) 2001, Microsoft Corporation
  4. //
  5. // File: DfsXForest.cxx
  6. //
  7. // Contents: the Dfs xforest info class
  8. //
  9. // Classes: DfsXForest, DfsDomainNameTable
  10. //
  11. // History: Nov, 15 2002, Author: udayh
  12. //
  13. //-----------------------------------------------------------------------------
  14. #include "dfsxforest.hxx"
  15. #include "lm.h"
  16. #include "dfsxforest.tmh"
  17. //
  18. // This file implements DfsXForest class. This class enumerates all
  19. // local domains and cross forest domains, and builds a table of uniqye
  20. // domain names.
  21. //
  22. // Ideally this should be an API such as DsEnumerateDomainTrusts, but
  23. // lacking that we are putting this support here.
  24. //
  25. // Since this is happening so close to RTM, there is minimal code change
  26. // in the original path, and all functionality has been implemented in
  27. // this new class. This is called from DfsDomainInformation, where
  28. // instead of enumerating domains by calling DsEnumerateDomainTrusts we
  29. // enumerate domains by building the DfsXForest instance.
  30. //
  31. // DfsDomainInformation gets the DfsXForest instance, reads all the domains
  32. // from the DfsXForest and destroys the DfsXForest.
  33. //
  34. //
  35. // In future, we may either want to get an API for this. If that does
  36. // not happen, it may be worthwhile changing the DfsDomainInformation
  37. // code to use the DfsXForest instance all the time instead of
  38. // building an array of domain names enumerated using this class.
  39. // That would be a more performance optimal solution.
  40. //
  41. //
  42. DFSSTATUS
  43. DfsXForest::Initialize( DFSSTATUS *pXforestInitStatus )
  44. {
  45. DFSSTATUS Status;
  46. Status = _DomainTable.Initialize();
  47. if (Status == ERROR_SUCCESS)
  48. {
  49. Status = DfsAddLocalDomainsToDomainTable();
  50. }
  51. if (Status == ERROR_SUCCESS)
  52. {
  53. DFSSTATUS DfsCrossForestDomainStatus;
  54. DfsCrossForestDomainStatus = InitializeForestRootName();
  55. DFS_TRACE_ERROR_NORM(DfsCrossForestDomainStatus, REFERRAL_SERVER,
  56. "DfsXForest::InitializeForestRootName, Status 0x%x\n",
  57. DfsCrossForestDomainStatus);
  58. //
  59. // If we cannot get cross forest domain information,
  60. // we continue with just the local information.
  61. //
  62. if (DfsCrossForestDomainStatus == ERROR_SUCCESS)
  63. {
  64. DfsCrossForestDomainStatus = DfsAddCrossForestDomainsToDomainTable();
  65. DFS_TRACE_ERROR_NORM(DfsCrossForestDomainStatus, REFERRAL_SERVER,
  66. "DfsXForest::DfsAddCrossForestDomainsToDomainTable, Status 0x%x\n",
  67. DfsCrossForestDomainStatus);
  68. }
  69. *pXforestInitStatus = DfsCrossForestDomainStatus;
  70. }
  71. return Status;
  72. }
  73. //
  74. // InitializeForestRootName: Get the Root Forest name and store it
  75. // in our private member _ForestRootName.
  76. //
  77. //
  78. DFSSTATUS
  79. DfsXForest::InitializeForestRootName()
  80. {
  81. LSA_OBJECT_ATTRIBUTES ObjectAttributes;
  82. NTSTATUS NtStatus = 0;
  83. DFSSTATUS Status = ERROR_SUCCESS;
  84. LSA_HANDLE hPolicy;
  85. //attempt to open the policy.
  86. RtlInitUnicodeString(&_ForestRootName, NULL);
  87. ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));//object attributes are reserved, so initalize to zeroes.
  88. NtStatus = LsaOpenPolicy( NULL,
  89. &ObjectAttributes,
  90. POLICY_VIEW_LOCAL_INFORMATION,
  91. &hPolicy); //recieves the policy handle
  92. DFS_TRACE_ERROR_HIGH( NtStatus, REFERRAL_SERVER, "LsaOpenPolicy, NtStatus 0x%x\n", NtStatus );
  93. if (NT_SUCCESS(NtStatus))
  94. {
  95. //ask for audit event policy information
  96. PPOLICY_DNS_DOMAIN_INFO info;
  97. NtStatus = LsaQueryInformationPolicy(hPolicy,
  98. PolicyDnsDomainInformation,
  99. (PVOID *)&info);
  100. DFS_TRACE_ERROR_HIGH( NtStatus, REFERRAL_SERVER, "LsaQueryInformationPolicy, NtStatus 0x%x\n", NtStatus );
  101. if (NT_SUCCESS(NtStatus))
  102. {
  103. //
  104. // convert LSA_UNICODE_STRING to normal UNICODE_STRING.
  105. // not that it matters now, but if things should change,
  106. // we will be in trouble.
  107. // Save the forest name and we are done.
  108. //
  109. UNICODE_STRING TempForestName;
  110. TempForestName.Buffer = info->DnsForestName.Buffer;
  111. TempForestName.Length = info->DnsForestName.Length;
  112. TempForestName.MaximumLength = info->DnsForestName.MaximumLength;
  113. Status = DfsCreateUnicodeString( &_ForestRootName,
  114. &TempForestName);
  115. //free policy info structure
  116. LsaFreeMemory((PVOID) info);
  117. }
  118. //Freeing the policy object handle
  119. LsaClose(hPolicy);
  120. }
  121. if (!NT_SUCCESS(NtStatus))
  122. {
  123. Status = RtlNtStatusToDosError(NtStatus);
  124. }
  125. return Status;
  126. }
  127. //
  128. // DfsAddLocalDomainsToDomainTable: enumerate all the local domain
  129. // trusts and add this to the domain table.
  130. //
  131. DFSSTATUS
  132. DfsXForest::DfsAddLocalDomainsToDomainTable()
  133. {
  134. DFSSTATUS Status;
  135. ULONG DsDomainCount = 0;
  136. PDS_DOMAIN_TRUSTS pDsDomainTrusts = NULL;
  137. ULONG Index;
  138. Status = DsEnumerateDomainTrusts( NULL,
  139. DS_DOMAIN_VALID_FLAGS,
  140. &pDsDomainTrusts,
  141. &DsDomainCount );
  142. DFS_TRACE_ERROR_NORM( Status, REFERRAL_SERVER, "DsEnumerateDomainTrusts, Status 0x%x\n", Status );
  143. if (Status == ERROR_SUCCESS)
  144. {
  145. for (Index = 0; (Status == ERROR_SUCCESS) && (Index < DsDomainCount); Index++)
  146. {
  147. if (pDsDomainTrusts[Index].TrustType == TRUST_TYPE_DOWNLEVEL)
  148. continue;
  149. if (Status == ERROR_SUCCESS)
  150. {
  151. if (IsEmptyString(pDsDomainTrusts[Index].NetbiosDomainName) == FALSE)
  152. {
  153. Status = AddDomainToDomainTable(pDsDomainTrusts[Index].NetbiosDomainName, NULL, TRUE);
  154. }
  155. }
  156. if (Status == ERROR_SUCCESS)
  157. {
  158. if (IsEmptyString(pDsDomainTrusts[Index].DnsDomainName) == FALSE)
  159. {
  160. Status = AddDomainToDomainTable(pDsDomainTrusts[Index].DnsDomainName, NULL, FALSE);
  161. }
  162. }
  163. }
  164. NetApiBufferFree(pDsDomainTrusts);
  165. }
  166. return Status;
  167. }
  168. //
  169. // DfsAddForestDomainsToDomainTable: enumerate all the domains in a forest
  170. // and add that to our table.
  171. //
  172. //
  173. DFSSTATUS
  174. DfsXForest::DfsAddForestDomainsToDomainTable( LSA_HANDLE hPolicy,
  175. LPWSTR RootNameString)
  176. {
  177. DFSSTATUS Status;
  178. NTSTATUS NtStatus;
  179. ULONG j;
  180. LSA_UNICODE_STRING LsaRootName;
  181. UNICODE_STRING RootName;
  182. PLSA_FOREST_TRUST_INFORMATION pForestTrustInfo = NULL;
  183. RtlInitUnicodeString(&RootName, RootNameString);
  184. LsaRootName.Length = RootName.Length;
  185. LsaRootName.MaximumLength = RootName.MaximumLength;
  186. LsaRootName.Buffer = RootName.Buffer;
  187. NtStatus = LsaQueryForestTrustInformation( hPolicy,
  188. &LsaRootName,
  189. &pForestTrustInfo);
  190. Status = RtlNtStatusToDosError(NtStatus);
  191. if (Status == ERROR_SUCCESS)
  192. {
  193. for (j = 0; (Status == ERROR_SUCCESS) && (j < pForestTrustInfo->RecordCount); j++ )
  194. {
  195. PLSA_FOREST_TRUST_DOMAIN_INFO pDomainInfo;
  196. if (pForestTrustInfo->Entries[j]->ForestTrustType == ForestTrustDomainInfo)
  197. {
  198. pDomainInfo = &pForestTrustInfo->Entries[j]->ForestTrustData.DomainInfo;
  199. //
  200. // If only the DnsDomainName is empty, add both the dns and
  201. // netbios domain names.
  202. // The reason is: we cannot resolve netbios domain names
  203. // to DCs. We bind to the DNS domain name to resolve netbios
  204. // domains, so we add both netbios and dns names in
  205. // the add domain to table for netbios names.
  206. //
  207. if (IsEmptyUnicodeString((PUNICODE_STRING)&pDomainInfo->DnsName) == FALSE)
  208. {
  209. Status = AddDomainToDomainTable((PUNICODE_STRING)&pDomainInfo->DnsName, NULL, FALSE);
  210. if ((Status == ERROR_SUCCESS) && (IsEmptyUnicodeString((PUNICODE_STRING)&pDomainInfo->NetbiosName) == FALSE))
  211. {
  212. Status = AddDomainToDomainTable((PUNICODE_STRING)&pDomainInfo->NetbiosName,
  213. (PUNICODE_STRING)&pDomainInfo->DnsName,
  214. TRUE);
  215. }
  216. }
  217. }
  218. }
  219. LsaFreeMemory(pForestTrustInfo);
  220. }
  221. return Status;
  222. }
  223. //
  224. // Magic trust flags: KahrenT indicates this is what we need.
  225. //
  226. #define DOMAIN_TRUST_FLAGS DS_DOMAIN_DIRECT_INBOUND
  227. //
  228. // DfsAddCrossForestDomainsToDomainTable: Enumerate each forest that we have
  229. // a trust to, and add that to our table.
  230. //
  231. //
  232. DFSSTATUS
  233. DfsXForest::DfsAddCrossForestDomainsToDomainTable()
  234. {
  235. DFSSTATUS Status;
  236. NTSTATUS NtStatus;
  237. ULONG DsDomainCount = 0;
  238. PDS_DOMAIN_TRUSTS pDsDomainTrusts = NULL;
  239. PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL;
  240. LSA_OBJECT_ATTRIBUTES ObjectAttributes;
  241. LSA_HANDLE hPolicy;
  242. ULONG Index;
  243. //object attributes are reserved, so initalize to zeroes.
  244. ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
  245. Status = DsGetDcName( NULL, //computer name
  246. _ForestRootName.Buffer,
  247. NULL, // domain guid
  248. NULL, // site name
  249. DS_DIRECTORY_SERVICE_REQUIRED | DS_FORCE_REDISCOVERY,
  250. &pDomainControllerInfo );
  251. if (Status == ERROR_SUCCESS)
  252. {
  253. Status = DsEnumerateDomainTrusts( pDomainControllerInfo->DomainControllerName,
  254. DOMAIN_TRUST_FLAGS,
  255. &pDsDomainTrusts,
  256. &DsDomainCount );
  257. if (Status == ERROR_SUCCESS)
  258. {
  259. LSA_UNICODE_STRING LsaDCName;
  260. RtlInitUnicodeString(&LsaDCName,
  261. pDomainControllerInfo->DomainControllerName);
  262. NtStatus = LsaOpenPolicy(&LsaDCName,
  263. &ObjectAttributes,
  264. POLICY_VIEW_LOCAL_INFORMATION,
  265. &hPolicy);
  266. Status = RtlNtStatusToDosError(NtStatus);
  267. if (Status == ERROR_SUCCESS)
  268. {
  269. for (Index = 0; Index < DsDomainCount; Index++)
  270. {
  271. PDS_DOMAIN_TRUSTS pDomainTrust;
  272. DFSSTATUS ForestTrustStatus;
  273. pDomainTrust = &pDsDomainTrusts[Index];
  274. //
  275. // If the domain is not in our forest, and it is
  276. // not MIT kerberos and it has transitive trust,
  277. // we have found a match. Add that forest and its
  278. // domains to our list.
  279. //
  280. if (!(pDomainTrust->Flags & DS_DOMAIN_IN_FOREST) &&
  281. (pDomainTrust->TrustType != TRUST_TYPE_MIT) &&
  282. (pDomainTrust->TrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
  283. {
  284. //
  285. // Ignore status return.
  286. //
  287. // If any one of the domain enumeration fails,
  288. // we dont want to fail the entire domain
  289. // table creation.
  290. //
  291. ForestTrustStatus = DfsAddForestDomainsToDomainTable( hPolicy,
  292. pDomainTrust->DnsDomainName);
  293. }
  294. }
  295. LsaClose(hPolicy);
  296. }
  297. NetApiBufferFree(pDsDomainTrusts);
  298. }
  299. NetApiBufferFree(pDomainControllerInfo);
  300. }
  301. return Status;
  302. }
  303. //
  304. //AddDomainToDomainTable: given a domain name add it to our table
  305. //
  306. DFSSTATUS
  307. DfsXForest::AddDomainToDomainTable(LPWSTR DomainName,
  308. LPWSTR BindDomainName,
  309. BOOLEAN Netbios)
  310. {
  311. UNICODE_STRING DomainNameUnicode;
  312. UNICODE_STRING BindDomainNameUnicode;
  313. DFSSTATUS Status;
  314. RtlInitUnicodeString(&DomainNameUnicode, DomainName);
  315. if (BindDomainName != NULL)
  316. {
  317. RtlInitUnicodeString(&BindDomainNameUnicode, BindDomainName);
  318. }
  319. Status = AddDomainToDomainTable( &DomainNameUnicode,
  320. BindDomainName ? &BindDomainNameUnicode : NULL,
  321. Netbios);
  322. return Status;
  323. }
  324. //
  325. //AddDomainToDomainTable: given a domain name add it to our table
  326. //
  327. DFSSTATUS
  328. DfsXForest::AddDomainToDomainTable(PUNICODE_STRING DomainName,
  329. PUNICODE_STRING BindDomainName,
  330. BOOLEAN Netbios)
  331. {
  332. DFSSTATUS Status;
  333. Status = _DomainTable.AddDomain(DomainName, BindDomainName, Netbios);
  334. //
  335. // Buffer overflow indicates that we went over the hash table limit.
  336. // However, we keep adding so that we have a count of how many we
  337. // missed.
  338. //
  339. if (Status == ERROR_BUFFER_OVERFLOW)
  340. {
  341. Status = ERROR_SUCCESS;
  342. }
  343. return Status;
  344. }
  345. //
  346. // AddDomain: Insert the domain name in our hash table.
  347. //
  348. DFSSTATUS
  349. DfsDomainNameTable::AddDomain(PUNICODE_STRING DomainName,
  350. PUNICODE_STRING BindDomainName,
  351. BOOLEAN Netbios)
  352. {
  353. PDFS_DOMAIN_NAME_DATA pDomainData = NULL;
  354. ULONG DomainDataLength;
  355. DFSSTATUS Status = ERROR_SUCCESS;
  356. USHORT DomainNameLength = DomainName->Length;
  357. USHORT MaxDomainNameLength = DomainNameLength + sizeof(WCHAR);
  358. USHORT BindDomainNameLength = 0;
  359. USHORT MaxBindDomainNameLength = 0;
  360. NTSTATUS NtStatus;
  361. ULONG CurrentSize = 0;
  362. if (_DomainReferralSize > MAX_REFERRAL_SIZE)
  363. {
  364. _DomainsSkipped++;
  365. return ERROR_BUFFER_OVERFLOW;
  366. }
  367. CurrentSize = sizeof(DFS_REFERRAL_V3) + sizeof(UNICODE_PATH_SEP)
  368. + DomainName->Length + sizeof(UNICODE_NULL);
  369. if (BindDomainName != NULL)
  370. {
  371. BindDomainNameLength = BindDomainName->Length;
  372. MaxBindDomainNameLength = BindDomainNameLength + sizeof(WCHAR);
  373. }
  374. DomainDataLength = sizeof(DFS_DOMAIN_NAME_DATA) + MaxDomainNameLength + MaxBindDomainNameLength;
  375. pDomainData = (PDFS_DOMAIN_NAME_DATA) DfsAllocateForDomainTable(DomainDataLength);
  376. if (pDomainData != NULL)
  377. {
  378. RtlZeroMemory(pDomainData, DomainDataLength);
  379. pDomainData->Header.RefCount = 1;
  380. pDomainData->Header.pvKey = (PVOID)&pDomainData->DomainInfo.DomainName;
  381. pDomainData->Header.pData = (PVOID)pDomainData;
  382. pDomainData->DomainInfo.Netbios = Netbios;
  383. RtlInitUnicodeString(&pDomainData->DomainInfo.DomainName, NULL);
  384. pDomainData->DomainInfo.DomainName.Buffer = (LPWSTR)(pDomainData + 1);
  385. pDomainData->DomainInfo.DomainName.MaximumLength = MaxDomainNameLength;
  386. pDomainData->DomainInfo.DomainName.Length = DomainNameLength;
  387. RtlCopyMemory(pDomainData->DomainInfo.DomainName.Buffer,
  388. DomainName->Buffer,
  389. DomainNameLength);
  390. pDomainData->DomainInfo.DomainName.Buffer[DomainNameLength/sizeof(WCHAR)] = 0;
  391. if (BindDomainName)
  392. {
  393. pDomainData->DomainInfo.BindDomainName.Buffer = (LPWSTR)(((ULONG_PTR)(pDomainData + 1)) + MaxDomainNameLength);
  394. pDomainData->DomainInfo.BindDomainName.MaximumLength = MaxBindDomainNameLength;
  395. pDomainData->DomainInfo.BindDomainName.Length = BindDomainNameLength;
  396. RtlCopyMemory(pDomainData->DomainInfo.BindDomainName.Buffer,
  397. BindDomainName->Buffer,
  398. BindDomainNameLength);
  399. pDomainData->DomainInfo.BindDomainName.Buffer[BindDomainNameLength/sizeof(WCHAR)] = 0;
  400. pDomainData->DomainInfo.UseBindDomain = TRUE;
  401. }
  402. }
  403. else
  404. {
  405. Status = ERROR_NOT_ENOUGH_MEMORY;
  406. }
  407. if (Status == ERROR_SUCCESS)
  408. {
  409. NtStatus = SHashInsertKey(_pDomainNameTable,
  410. pDomainData,
  411. &pDomainData->DomainInfo.DomainName,
  412. SHASH_FAIL_IFFOUND);
  413. if (NtStatus != STATUS_SUCCESS)
  414. {
  415. DfsDeallocateForDomainTable( pDomainData );
  416. //
  417. // if we have collision, we already know this name and
  418. // we can ignore this error.
  419. //
  420. if (NtStatus != STATUS_OBJECT_NAME_COLLISION)
  421. {
  422. Status = RtlNtStatusToDosError( NtStatus );
  423. }
  424. }
  425. else
  426. {
  427. //
  428. // we successfully added to the table, bump up our
  429. // DomainReferralSize.
  430. //
  431. InterlockedDecrement(&pDomainData->Header.RefCount);
  432. _DomainReferralSize += CurrentSize;
  433. }
  434. }
  435. return Status;
  436. }
  437. //
  438. // InvalidateDomainTable: Run through the hash and throw out all our
  439. // entries.
  440. //
  441. //
  442. VOID
  443. DfsDomainNameTable::InvalidateDomainTable(VOID)
  444. {
  445. SHASH_ITERATOR Iter;
  446. PDFS_DOMAIN_NAME_DATA pExistingData = NULL;
  447. NTSTATUS NtStatus;
  448. pExistingData = (PDFS_DOMAIN_NAME_DATA) SHashStartEnumerate(&Iter, _pDomainNameTable);
  449. while (pExistingData != NULL)
  450. {
  451. //
  452. // Remove this item. There's nothing we can do if we hit errors
  453. // except to keep going.
  454. //
  455. NtStatus = SHashRemoveKey(_pDomainNameTable,
  456. &pExistingData->DomainInfo.DomainName,
  457. NULL );
  458. //
  459. // ignore status here, since we want to keep going.
  460. //
  461. pExistingData = (PDFS_DOMAIN_NAME_DATA) SHashNextEnumerate(&Iter, _pDomainNameTable);
  462. }
  463. SHashFinishEnumerate(&Iter, _pDomainNameTable);
  464. }
  465. //
  466. // GetCount: Run through our hash and get a count of the number of
  467. // items. I wish we could ask the hash table for the count.
  468. //
  469. ULONG
  470. DfsDomainNameTable::GetCount(VOID)
  471. {
  472. SHASH_ITERATOR Iter;
  473. PDFS_DOMAIN_NAME_DATA pExistingData = NULL;
  474. ULONG nEntries = 0;
  475. pExistingData = (PDFS_DOMAIN_NAME_DATA) SHashStartEnumerate(&Iter, _pDomainNameTable);
  476. while (pExistingData != NULL)
  477. {
  478. nEntries++;
  479. pExistingData = (PDFS_DOMAIN_NAME_DATA) SHashNextEnumerate(&Iter, _pDomainNameTable);
  480. }
  481. SHashFinishEnumerate(&Iter, _pDomainNameTable);
  482. return nEntries;
  483. }
  484. //
  485. // FinishDomainNameEnumerate
  486. //
  487. VOID
  488. DfsDomainNameTable::FinishDomainNameEnumerate( SHASH_ITERATOR *pIter )
  489. {
  490. SHashFinishEnumerate( pIter, _pDomainNameTable);
  491. }
  492. // Allocate and deallocate the cache data entry
  493. PVOID
  494. DfsAllocateForDomainTable(ULONG Size )
  495. {
  496. PVOID RetValue = NULL;
  497. if (Size)
  498. {
  499. RetValue = (PVOID) new BYTE[Size];
  500. if (RetValue != NULL)
  501. {
  502. RtlZeroMemory( RetValue, Size );
  503. }
  504. }
  505. return RetValue;
  506. }
  507. VOID
  508. DfsDeallocateForDomainTable(PVOID pPointer )
  509. {
  510. if (pPointer)
  511. {
  512. delete [] (PBYTE)pPointer;
  513. }
  514. }