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.

470 lines
12 KiB

  1. // Copyright (C) 2000 Microsoft Corporation
  2. //
  3. // Non-domain Naming Context checking code
  4. //
  5. // 13 July 2000 sburns, from code supplied by jeffparh
  6. #include "headers.hxx"
  7. #include "state.hpp"
  8. #include "resource.h"
  9. #include "NonDomainNc.hpp"
  10. #ifdef LOGGING_BUILD
  11. #define LOG_LDAP(msg, ldap) LOG(msg); LOG(String::format(L"LDAP error %1!ld!", (ldap)))
  12. #else
  13. #define LOG_LDAP(msg, ldap)
  14. #endif
  15. HRESULT
  16. LdapToHresult(int ldapError)
  17. {
  18. // CODEWORK: I'm told that ldap_get_option for LDAP_OPT_SERVER_ERROR or
  19. // LDAP_OPT_SERVER_EXT_ERROR (or perhaps LDAP_OPT_ERROR_STRING?) will give
  20. // an error result with "higher fidelity"
  21. return Win32ToHresult(::LdapMapErrorToWin32(ldapError));
  22. }
  23. // provided by jeffparh
  24. DWORD
  25. IsLastReplicaOfNC(
  26. IN LDAP * hld,
  27. IN LPWSTR pszConfigNC,
  28. IN LPWSTR pszNC,
  29. IN LPWSTR pszNtdsDsaDN,
  30. OUT BOOL * pfIsLastReplica
  31. )
  32. /*++
  33. Routine Description:
  34. Determine whether any DSAs other than that with DN pszNtdsDsaDN hold
  35. replicas of a particular NC.
  36. Arguments:
  37. hld (IN) - LDAP handle to execute search with.
  38. pszConfigNC (IN) - DN of the config NC. Used as a base for the search.
  39. pszNC (IN) - NC for which to check for other replicas.
  40. pszNtdsDsaDN (IN) - DN of the DSA object known to currently have a replica
  41. of the NC. We are specifically looking for replicas *other than* this
  42. one.
  43. pfIsLastReplica (OUT) - On successful return, TRUE iff no DSAs hold replicas
  44. of pszNC other than that with DN pszNtdsDsaDN.
  45. Return Values:
  46. Win error.
  47. --*/
  48. {
  49. LOG_FUNCTION2(IsLastReplicaOfNC, pszNC ? pszNC : L"(null)");
  50. ASSERT(hld);
  51. ASSERT(pszConfigNC);
  52. ASSERT(pszNC);
  53. ASSERT(pszNtdsDsaDN);
  54. ASSERT(pfIsLastReplica);
  55. if (
  56. !hld
  57. || !pszConfigNC
  58. || !pszNC
  59. || !pszNtdsDsaDN
  60. || !pfIsLastReplica)
  61. {
  62. return ERROR_INVALID_PARAMETER;
  63. }
  64. // Just checking for existence -- don't really want any attributes
  65. // returned.
  66. static LPWSTR rgpszDsaAttrsToRead[] = {
  67. L"__invalid_attribute_name__",
  68. NULL
  69. };
  70. // FUTURE-2002/03/22-BrettSh - NO_DOT_NET_BETA3_COMPAT_NEEDED - When
  71. // we don't need to be compat with Beta3, we can change this section:
  72. // (|(msDS-HasMasterNCs=%ls)(hasMasterNCs=%ls))
  73. // to:
  74. // (msDS-HasMasterNCs=%ls)
  75. static WCHAR szFilterFormat[]
  76. = L"(&(objectCategory=ntdsDsa)(|(msDS-HasMasterNCs=%ls)(hasMasterNCs=%ls))(!(distinguishedName=%ls)))";
  77. *pfIsLastReplica = TRUE;
  78. int ldStatus = 0;
  79. DWORD err = 0;
  80. LDAPMessage * pDsaResults = NULL;
  81. LDAPMessage * pDsaEntry = NULL;
  82. size_t cchFilter;
  83. PWSTR pszFilter;
  84. LDAP_TIMEVAL lTimeout = {3*60, 0}; // three minutes
  85. do
  86. {
  87. cchFilter = sizeof(szFilterFormat) / sizeof(*szFilterFormat)
  88. // ISSUE-2002/02/27-sburns if the swprintf below is replaced with
  89. // String::format, then these wcslen calls can be eliminated.
  90. + wcslen(pszNtdsDsaDN)
  91. + wcslen(pszNC)
  92. + wcslen(pszNC);
  93. pszFilter = (PWSTR) new BYTE[sizeof(WCHAR) * cchFilter];
  94. // ISSUE-2002/02/27-sburns should use strsafe function here, or
  95. // String::format
  96. swprintf(pszFilter, szFilterFormat, pszNC, pszNC, pszNtdsDsaDN);
  97. // Search config NC for any ntdsDsa object that hosts this NC other
  98. // than that with dn pszNtdsDsaDN. Note that we cap the search at one
  99. // returned object -- we're not really trying to enumerate, just
  100. // checking for existence.
  101. ldStatus = ldap_search_ext_sW(hld, pszConfigNC, LDAP_SCOPE_SUBTREE,
  102. pszFilter, rgpszDsaAttrsToRead, 0,
  103. NULL, NULL, &lTimeout, 1, &pDsaResults);
  104. if (pDsaResults)
  105. {
  106. // Ignore any error (such as LDAP_SIZELIMIT_EXCEEDED) when the
  107. // search returns results.
  108. ldStatus = 0;
  109. pDsaEntry = ldap_first_entry(hld, pDsaResults);
  110. *pfIsLastReplica = (NULL == pDsaEntry);
  111. } else if (ldStatus)
  112. {
  113. // Search failed and returned no results.
  114. LOG_LDAP(L"Config NC search failed", ldStatus);
  115. break;
  116. } else
  117. {
  118. // No error, no results. This shouldn't happen.
  119. LOG("ldap_search_ext_sW returned no results and no error!");
  120. ASSERT(false);
  121. }
  122. }
  123. while (0);
  124. if (NULL != pDsaResults) {
  125. ldap_msgfree(pDsaResults);
  126. }
  127. if (pszFilter)
  128. {
  129. delete[] pszFilter;
  130. }
  131. if (!err && ldStatus) {
  132. err = LdapMapErrorToWin32(ldStatus);
  133. }
  134. return err;
  135. }
  136. // S_OK if this machine (the localhost) is the last replica of at least one
  137. // non-domain NC, S_FALSE if not, or error otherwise. If S_OK, then the
  138. // StringList will contain the DNs of the non domain NCs for which this
  139. // machine is the last replica.
  140. //
  141. // based on code from jeffparh
  142. //
  143. // hld (IN) - LDAP handle bound to DSA to evaluate.
  144. //
  145. // result (OUT) - string list to receive DNs of the non-domain NCs.
  146. HRESULT
  147. IsLastNdncReplica(LDAP* hld, StringList& result)
  148. {
  149. LOG_FUNCTION(IsLastNdncReplica);
  150. ASSERT(hld);
  151. ASSERT(result.empty());
  152. HRESULT hr = S_FALSE;
  153. LDAPMessage* rootResults = 0;
  154. PWSTR* configNc = 0;
  155. PWSTR* schemaNc = 0;
  156. PWSTR* domainNc = 0;
  157. PWSTR* masterNcs = 0;
  158. PWSTR* ntdsDsaDn = 0;
  159. do
  160. {
  161. // Gather basic rootDSE info.
  162. static PWSTR ROOT_ATTRS_TO_READ[] =
  163. {
  164. LDAP_OPATT_NAMING_CONTEXTS_W,
  165. LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W,
  166. LDAP_OPATT_CONFIG_NAMING_CONTEXT_W,
  167. LDAP_OPATT_SCHEMA_NAMING_CONTEXT_W,
  168. LDAP_OPATT_DS_SERVICE_NAME_W,
  169. 0
  170. };
  171. LOG(L"Calling ldap_search_s");
  172. int ldStatus =
  173. ldap_search_sW(
  174. hld,
  175. 0,
  176. LDAP_SCOPE_BASE,
  177. L"(objectClass=*)",
  178. ROOT_ATTRS_TO_READ,
  179. 0,
  180. &rootResults);
  181. if (ldStatus)
  182. {
  183. LOG_LDAP(L"RootDSE search failed", ldStatus);
  184. hr = LdapToHresult(ldStatus);
  185. break;
  186. }
  187. configNc = ldap_get_valuesW(hld, rootResults, LDAP_OPATT_CONFIG_NAMING_CONTEXT_W);
  188. schemaNc = ldap_get_valuesW(hld, rootResults, LDAP_OPATT_SCHEMA_NAMING_CONTEXT_W);
  189. domainNc = ldap_get_valuesW(hld, rootResults, LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W);
  190. masterNcs = ldap_get_valuesW(hld, rootResults, LDAP_OPATT_NAMING_CONTEXTS_W);
  191. ntdsDsaDn = ldap_get_valuesW(hld, rootResults, LDAP_OPATT_DS_SERVICE_NAME_W);
  192. if (
  193. (0 == configNc)
  194. || (0 == schemaNc)
  195. || (0 == domainNc)
  196. || (0 == masterNcs)
  197. || (0 == ntdsDsaDn))
  198. {
  199. LOG(L"Can't find key rootDSE attributes!");
  200. hr = Win32ToHresult(ERROR_DS_UNAVAILABLE);
  201. break;
  202. }
  203. // There is only one value for each of these attributes...
  204. ASSERT(1 == ldap_count_valuesW(configNc));
  205. ASSERT(1 == ldap_count_valuesW(schemaNc));
  206. ASSERT(1 == ldap_count_valuesW(domainNc));
  207. ASSERT(1 == ldap_count_valuesW(ntdsDsaDn));
  208. DWORD masterNcCount = ldap_count_valuesW(masterNcs);
  209. LOG(String::format(L"masterNcCount = %1!d!", masterNcCount));
  210. // '3' => 1 nc for config, 1 nc for schema, 1 nc for this DC's own
  211. // domain.
  212. if (masterNcCount <= 3)
  213. {
  214. // DSA holds no master NCs other than config, schema, and its own
  215. // domain. Thus, it is not the last replica of any NDNC.
  216. LOG(L"This dsa holds no master NCs other than config, schema, and domain");
  217. ASSERT(3 == masterNcCount);
  218. ASSERT(0 == ldStatus);
  219. ASSERT(hr == S_FALSE);
  220. break;
  221. }
  222. // Loop through non-config/schema/domain NCs to determine those for
  223. // which the DSA is the last replica.
  224. for (int i = 0; 0 != masterNcs[i]; ++i)
  225. {
  226. PWSTR nc = masterNcs[i];
  227. LOG(L"Evaluating " + String(nc));
  228. ASSERT(nc);
  229. ASSERT(configNc);
  230. ASSERT(*configNc);
  231. ASSERT(schemaNc);
  232. ASSERT(*schemaNc);
  233. ASSERT(domainNc);
  234. ASSERT(*domainNc);
  235. if (
  236. // REVIEWED-2002/02/27-sburns we're properly checking these
  237. // strings for null in the for loop test and in a check above
  238. // (in addition to the ASSERTs)
  239. (0 != wcscmp(nc, *configNc))
  240. && (0 != wcscmp(nc, *schemaNc))
  241. && (0 != wcscmp(nc, *domainNc)))
  242. {
  243. // A non-config/schema/domain NC.
  244. LOG(L"Calling IsLastReplicaOfNC on " + String(nc));
  245. BOOL isLastReplica = FALSE;
  246. DWORD err =
  247. IsLastReplicaOfNC(
  248. hld,
  249. *configNc,
  250. nc,
  251. *ntdsDsaDn,
  252. &isLastReplica);
  253. if (err)
  254. {
  255. LOG(L"IsLastReplicaOfNC() failed");
  256. hr = Win32ToHresult(err);
  257. break;
  258. }
  259. if (isLastReplica)
  260. {
  261. // This DSA is indeed the last replica of this particular
  262. // NC. Return the DN of this NC to our caller.
  263. LOG(L"last replica of " + String(nc));
  264. result.push_back(nc);
  265. }
  266. else
  267. {
  268. LOG(L"not last replica of " + String(nc));
  269. }
  270. }
  271. }
  272. // If we broke out of the prior loop with an error, jump out to the
  273. // cleanup section.
  274. BREAK_ON_FAILED_HRESULT(hr);
  275. hr = result.size() > 0 ? S_OK : S_FALSE;
  276. }
  277. while (0);
  278. if (rootResults)
  279. {
  280. ldap_msgfree(rootResults);
  281. }
  282. if (0 != configNc)
  283. {
  284. ldap_value_freeW(configNc);
  285. }
  286. if (0 != schemaNc)
  287. {
  288. ldap_value_freeW(schemaNc);
  289. }
  290. if (0 != domainNc)
  291. {
  292. ldap_value_freeW(domainNc);
  293. }
  294. if (0 != masterNcs)
  295. {
  296. ldap_value_freeW(masterNcs);
  297. }
  298. if (0 != ntdsDsaDn)
  299. {
  300. ldap_value_freeW(ntdsDsaDn);
  301. }
  302. #ifdef LOGGING_BUILD
  303. LOG_HRESULT(hr);
  304. for (
  305. StringList::iterator i = result.begin();
  306. i != result.end();
  307. ++i)
  308. {
  309. LOG(*i);
  310. }
  311. #endif
  312. return hr;
  313. }
  314. // S_OK if this machine (the localhost) is the last replica of at least one
  315. // non-domain NC, S_FALSE if not, or error otherwise.
  316. //
  317. // result - If S_OK is returned, receives the DNs of the non domain NCs for
  318. // which this machine is the last replica. Should be empty on entry.
  319. HRESULT
  320. IsLastNonDomainNamingContextReplica(StringList& result)
  321. {
  322. LOG_FUNCTION(IsLastNonDomainNamingContextReplica);
  323. ASSERT(result.empty());
  324. result.clear();
  325. HRESULT hr = S_FALSE;
  326. LDAP* hld = 0;
  327. do
  328. {
  329. // Connect to target DSA.
  330. LOG(L"Calling ldap_open");
  331. hld = ldap_openW(L"localhost", LDAP_PORT);
  332. if (!hld)
  333. {
  334. LOG("Cannot open LDAP connection to localhost");
  335. hr = Win32ToHresult(ERROR_DS_UNAVAILABLE);
  336. break;
  337. }
  338. // Bind using logged-in user's credentials.
  339. int ldStatus = ldap_bind_s(hld, 0, 0, LDAP_AUTH_NEGOTIATE);
  340. if (ldStatus)
  341. {
  342. LOG_LDAP(L"LDAP bind failed", ldStatus);
  343. hr = LdapToHresult(ldStatus);
  344. break;
  345. }
  346. // go do the real work
  347. hr = IsLastNdncReplica(hld, result);
  348. }
  349. while (0);
  350. if (hld)
  351. {
  352. ldap_unbind(hld);
  353. }
  354. LOG_HRESULT(hr);
  355. return hr;
  356. }