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.

547 lines
12 KiB

  1. // Active Directory Display Specifier Upgrade Tool
  2. //
  3. // Copyright (c) 2001 Microsoft Corporation
  4. //
  5. // class Analyst: analyzes the display specifiers, logs the findings, and
  6. // compiles a set of corrective actions.
  7. //
  8. // 9 Mar 2001 sburns
  9. #include "headers.hxx"
  10. #include "resource.h"
  11. #include "AdsiHelpers.hpp"
  12. #include "Analyst.hpp"
  13. #include "Amanuensis.hpp"
  14. #include "Repairer.hpp"
  15. #include "ChangedObjectHandlerList.hpp"
  16. #include "ChangedObjectHandler.hpp"
  17. Analyst::Analyst(
  18. const String& targetDomainControllerName,
  19. Amanuensis& amanuensis_,
  20. Repairer& repairer_)
  21. :
  22. targetDcName(targetDomainControllerName),
  23. ldapPrefix(),
  24. rootDse(0),
  25. // alias the objects
  26. amanuensis(amanuensis_),
  27. repairer(repairer_)
  28. {
  29. LOG_CTOR(Analyst);
  30. ASSERT(!targetDcName.empty());
  31. }
  32. // basic idea: if the error is critical and analysis should not continue, set
  33. // hr to a failure value, and break out, propagating the error backward. If
  34. // the error is non-critical and analysis should continue, log the error, skip
  35. // the current operation, and set hr to S_FALSE.
  36. HRESULT
  37. AssessErrorSeverity(HRESULT hrIn)
  38. {
  39. HRESULT hr = hrIn;
  40. if (SUCCEEDED(hr))
  41. {
  42. return hr;
  43. }
  44. switch (hr)
  45. {
  46. case 0:
  47. {
  48. }
  49. // CODEWORK: we need to define what errors are critical...
  50. default:
  51. {
  52. // do nothing
  53. break;
  54. }
  55. }
  56. return hr;
  57. }
  58. HRESULT
  59. Analyst::AnalyzeDisplaySpecifiers()
  60. {
  61. LOG_FUNCTION(Analyst::AnalyzeDisplaySpecifiers);
  62. HRESULT hr = S_OK;
  63. do
  64. {
  65. Computer targetDc(targetDcName);
  66. hr = targetDc.Refresh();
  67. if (FAILED(hr))
  68. {
  69. amanuensis.AddErrorEntry(
  70. hr,
  71. String::format(
  72. IDS_CANT_TARGET_MACHINE,
  73. targetDcName.c_str()));
  74. break;
  75. }
  76. if (!targetDc.IsDomainController())
  77. {
  78. amanuensis.AddEntry(
  79. String::format(
  80. IDS_TARGET_IS_NOT_DC,
  81. targetDcName.c_str()));
  82. break;
  83. }
  84. String dcName = targetDc.GetActivePhysicalFullDnsName();
  85. ldapPrefix = L"LDAP://" + dcName + L"/";
  86. //
  87. // Find the DN of the configuration container.
  88. //
  89. // Bind to the rootDSE object. We will keep this binding handle
  90. // open for the duration of the analysis and repair phases in order
  91. // to keep a server session open. If we decide to pass creds to the
  92. // AdsiOpenObject call in a later revision, then by keeping the
  93. // session open we will not need to pass the password to subsequent
  94. // AdsiOpenObject calls.
  95. hr = AdsiOpenObject<IADs>(ldapPrefix + L"RootDSE", rootDse);
  96. if (FAILED(hr))
  97. {
  98. amanuensis.AddErrorEntry(
  99. hr,
  100. String::format(
  101. IDS_UNABLE_TO_CONNECT_TO_DC,
  102. dcName.c_str()));
  103. break;
  104. }
  105. // read the configuration naming context.
  106. _variant_t variant;
  107. hr =
  108. rootDse->Get(
  109. AutoBstr(LDAP_OPATT_CONFIG_NAMING_CONTEXT_W),
  110. &variant);
  111. if (FAILED(hr))
  112. {
  113. LOG(L"can't read config NC");
  114. amanuensis.AddErrorEntry(
  115. hr,
  116. IDS_UNABLE_TO_READ_DIRECTORY_INFO);
  117. break;
  118. }
  119. String configNc = V_BSTR(&variant);
  120. LOG(configNc);
  121. ASSERT(!configNc.empty());
  122. //
  123. // Here we go...
  124. //
  125. hr = AnalyzeDisplaySpecifierContainers(configNc);
  126. BREAK_ON_FAILED_HRESULT(hr);
  127. }
  128. while (0);
  129. LOG_HRESULT(hr);
  130. return hr;
  131. }
  132. HRESULT
  133. Analyst::AnalyzeDisplaySpecifierContainers(const String& configurationDn)
  134. {
  135. LOG_FUNCTION2(Analyst::AnalyzeDisplaySpecifierContainers, configurationDn);
  136. ASSERT(!configurationDn.empty());
  137. HRESULT hr = S_OK;
  138. static const int LOCALEIDS[] =
  139. {
  140. // a list of all the non-english locale IDs that we support
  141. 0x401,
  142. 0x404,
  143. 0x405,
  144. 0x406,
  145. 0x407,
  146. 0x408,
  147. 0x40b,
  148. 0x40c,
  149. 0x40d,
  150. 0x40e,
  151. 0x410,
  152. 0x411,
  153. 0x412,
  154. 0x413,
  155. 0x414,
  156. 0x415,
  157. 0x416,
  158. 0x419,
  159. 0x41d,
  160. 0x41f,
  161. 0x804,
  162. 0x816,
  163. 0xc0a,
  164. 0
  165. };
  166. // compose the LDAP path of the display specifiers container
  167. String rootContainerDn = L"CN=DisplaySpecifiers," + configurationDn;
  168. for (
  169. int i = 0;
  170. (i < sizeof(LOCALEIDS) / sizeof(int))
  171. && LOCALEIDS[i];
  172. ++i)
  173. {
  174. hr = AnalyzeDisplaySpecifierContainer(LOCALEIDS[i], rootContainerDn);
  175. BREAK_ON_FAILED_HRESULT(hr);
  176. }
  177. LOG_HRESULT(hr);
  178. return hr;
  179. }
  180. HRESULT
  181. Analyst::AnalyzeDisplaySpecifierContainer(
  182. int localeId,
  183. const String& rootContainerDn)
  184. {
  185. LOG_FUNCTION2(
  186. Analyst::AnalyzeDisplaySpecifierContainer,
  187. rootContainerDn);
  188. ASSERT(!rootContainerDn.empty());
  189. ASSERT(localeId);
  190. HRESULT hr = S_OK;
  191. do
  192. {
  193. String childContainerDn =
  194. ldapPrefix
  195. + String::format(L"CN=%1!3x!,", localeId) + rootContainerDn;
  196. // Attempt to bind to the container.
  197. SmartInterface<IADs> iads(0);
  198. hr = AdsiOpenObject<IADs>(childContainerDn, iads);
  199. if (hr == E_ADS_UNKNOWN_OBJECT)
  200. {
  201. // The container object does not exist. This is possible because
  202. // the user has manually removed the container, or because it
  203. // was never created due to an aboted post-dcpromo import of the
  204. // display specifiers when the forest root dc was first promoted.
  205. repairer.AddCreateContainerWorkItem(localeId);
  206. hr = S_OK;
  207. break;
  208. }
  209. BREAK_ON_FAILED_HRESULT(hr);
  210. // At this point, the bind succeeded, so the child container exists.
  211. // So now we want to examine objects in that container.
  212. hr =
  213. AnalyzeDisplaySpecifierObjects(
  214. localeId,
  215. childContainerDn);
  216. }
  217. while (0);
  218. LOG_HRESULT(hr);
  219. hr = AssessErrorSeverity(hr);
  220. return hr;
  221. }
  222. HRESULT
  223. Analyst::AnalyzeDisplaySpecifierObjects(
  224. int localeId,
  225. const String& containerDn)
  226. {
  227. LOG_FUNCTION2(Analyst::AnalyzeDisplaySpecifierObjects, containerDn);
  228. ASSERT(localeId);
  229. ASSERT(!containerDn.empty());
  230. HRESULT hr = S_OK;
  231. do
  232. {
  233. // Part 1: deal with new objects added in Whistler
  234. hr = AnalyzeAddedObjects(localeId, containerDn);
  235. hr = AssessErrorSeverity(hr);
  236. BREAK_ON_FAILED_HRESULT(hr);
  237. // Part 2: deal with objects that have changed from Win2k to Whistler
  238. hr = AnalyzeChangedObjects(localeId, containerDn);
  239. hr = AssessErrorSeverity(hr);
  240. BREAK_ON_FAILED_HRESULT(hr);
  241. // Part 3: deal with objects that have been deleted in whistler
  242. // This part is easy: there are no deletions.
  243. }
  244. while (0);
  245. LOG_HRESULT(hr);
  246. return hr;
  247. }
  248. bool
  249. RepairWasRunPreviously()
  250. {
  251. LOG_FUNCTION(RepairWasRunPreviously);
  252. bool result = false;
  253. // CODEWORK: need to complete
  254. LOG_BOOL(result);
  255. return result;
  256. }
  257. HRESULT
  258. Analyst::AnalyzeAddedObjects(
  259. int localeId,
  260. const String& containerDn)
  261. {
  262. LOG_FUNCTION2(Analyst::AnalyzeAddedObjects, containerDn);
  263. ASSERT(localeId);
  264. ASSERT(!containerDn.empty());
  265. HRESULT hr = S_OK;
  266. do
  267. {
  268. static const String ADDED_OBJECTS[] =
  269. {
  270. L"msMQ-Custom-Recipient-Display",
  271. L"msMQ-Group-Display",
  272. L"msCOM-PartitionSet-Display",
  273. L"msCOM-Partition-Display",
  274. L"lostAndFound-Display",
  275. L"inetOrgPerson-Display",
  276. L"",
  277. };
  278. for (
  279. int i = 0;
  280. i < (sizeof(ADDED_OBJECTS) / sizeof(String))
  281. && !ADDED_OBJECTS[i].empty();
  282. ++i)
  283. {
  284. String objectName = ADDED_OBJECTS[i];
  285. String objectPath =
  286. ldapPrefix + L"CN=" + objectName + L"," + containerDn;
  287. SmartInterface<IADs> iads(0);
  288. hr = AdsiOpenObject<IADs>(objectPath, iads);
  289. if (hr == E_ADS_UNKNOWN_OBJECT)
  290. {
  291. // The object does not exist. This is what we expect. We want
  292. // to add the object in the repair phase.
  293. repairer.AddCreateObjectWorkItem(localeId, objectName);
  294. hr = S_OK;
  295. continue;
  296. }
  297. else if (SUCCEEDED(hr))
  298. {
  299. // The object already exists. Well, that's not expected, unless
  300. // we've already run the tool.
  301. if (!RepairWasRunPreviously())
  302. {
  303. // we didn't create the object. If the user did, they did
  304. // it manually, and we don't support that.
  305. // cause the existing object to be deleted
  306. repairer.AddDeleteObjectWorkItem(localeId, objectName);
  307. // cause a new, replacement object to be created.
  308. repairer.AddCreateObjectWorkItem(localeId, objectName);
  309. hr = S_OK;
  310. continue;
  311. }
  312. }
  313. else
  314. {
  315. ASSERT(FAILED(hr));
  316. LOG(L"Unexpected error attempting to bind to " + objectName);
  317. amanuensis.AddErrorEntry(
  318. hr,
  319. String::format(
  320. IDS_ERROR_BINDING_TO_OBJECT,
  321. objectName.c_str(),
  322. objectPath.c_str()));
  323. // move on to the next object
  324. hr = S_FALSE;
  325. continue;
  326. }
  327. }
  328. BREAK_ON_FAILED_HRESULT(hr);
  329. }
  330. while (0);
  331. LOG_HRESULT(hr);
  332. return hr;
  333. }
  334. HRESULT
  335. Analyst::AnalyzeChangedObjects(
  336. int localeId,
  337. const String& containerDn)
  338. {
  339. LOG_FUNCTION2(Analyst::AnalyzeChangedObjects, containerDn);
  340. ASSERT(localeId);
  341. ASSERT(!containerDn.empty());
  342. HRESULT hr = S_OK;
  343. static const ChangedObjectHandlerList handlers;
  344. for (
  345. ChangedObjectHandlerList::iterator i = handlers.begin();
  346. i != handlers.end();
  347. ++i)
  348. {
  349. hr = AnalyzeChangedObject(localeId, containerDn, **i);
  350. hr = AssessErrorSeverity(hr);
  351. BREAK_ON_FAILED_HRESULT(hr);
  352. }
  353. LOG_HRESULT(hr);
  354. return hr;
  355. }
  356. HRESULT
  357. Analyst::AnalyzeChangedObject(
  358. int localeId,
  359. const String& containerDn,
  360. const ChangedObjectHandler& changeHandler)
  361. {
  362. LOG_FUNCTION2(Analyst::AnalyzeChangedObject, changeHandler.GetObjectName());
  363. ASSERT(localeId);
  364. ASSERT(!containerDn.empty());
  365. HRESULT hr = S_OK;
  366. do
  367. {
  368. String objectName = changeHandler.GetObjectName();
  369. String objectPath =
  370. ldapPrefix + L"CN=" + objectName + L"," + containerDn;
  371. SmartInterface<IADs> iads(0);
  372. hr = AdsiOpenObject<IADs>(objectPath, iads);
  373. if (hr == E_ADS_UNKNOWN_OBJECT)
  374. {
  375. // The object does not exist. This is possible because the user has
  376. // manually removed the container, or because it was never created
  377. // due to an aboted post-dcpromo import of the display specifiers
  378. // when the forest root dc was first promoted.
  379. // Add a work item to create the missing object
  380. repairer.AddCreateObjectWorkItem(localeId, objectName);
  381. hr = S_OK;
  382. break;
  383. }
  384. if (FAILED(hr))
  385. {
  386. // any other error is quittin' time.
  387. break;
  388. }
  389. // At this point, the display specifier object exists. Determine if
  390. // if has been touched since its creation.
  391. // Compare usnCreated to usnChanged
  392. _variant_t variant;
  393. hr = iads->Get(AutoBstr(L"usnCreated"), &variant);
  394. if (FAILED(hr))
  395. {
  396. LOG(L"Error reading usnCreated");
  397. break;
  398. }
  399. // CODEWORK: need to complete this
  400. hr = changeHandler.HandleChange(
  401. localeId,
  402. containerDn,
  403. iads,
  404. amanuensis,
  405. repairer);
  406. }
  407. while (0);
  408. LOG_HRESULT(hr);
  409. return hr;
  410. }