Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

657 lines
21 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000
  5. //
  6. // File: dsMod.cpp
  7. //
  8. // Contents: Defines the main function and parser tables for the DSMod
  9. // command line utility
  10. //
  11. // History: 06-Sep-2000 JeffJon Created
  12. //
  13. //
  14. //--------------------------------------------------------------------------
  15. #include "pch.h"
  16. #include "cstrings.h"
  17. #include "usage.h"
  18. #include "modtable.h"
  19. #include "resource.h"
  20. //
  21. // Function Declarations
  22. //
  23. HRESULT DoModValidation(PARG_RECORD pCommandArgs);
  24. HRESULT DoMod(PARG_RECORD pCommandArgs, PDSOBJECTTABLEENTRY pObjectEntry);
  25. int __cdecl _tmain( VOID )
  26. {
  27. int argc;
  28. LPTOKEN pToken = NULL;
  29. HRESULT hr = S_OK;
  30. //
  31. // Initialize COM
  32. //
  33. hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  34. if (FAILED(hr))
  35. {
  36. DisplayErrorMessage(g_pszDSCommandName,
  37. NULL,
  38. hr);
  39. return hr;
  40. }
  41. if( !GetCommandInput(&argc,&pToken) )
  42. {
  43. PARG_RECORD pNewCommandArgs = 0;
  44. //
  45. // False loop
  46. //
  47. do
  48. {
  49. if(argc == 1)
  50. {
  51. //
  52. // Display the error message and then break out of the false loop
  53. //
  54. DisplayMessage(USAGE_DSMOD);
  55. hr = E_INVALIDARG;
  56. break;
  57. }
  58. //
  59. // Find which object table entry to use from
  60. // the second command line argument
  61. //
  62. PDSOBJECTTABLEENTRY pObjectEntry = NULL;
  63. UINT idx = 0;
  64. while (true)
  65. {
  66. pObjectEntry = g_DSObjectTable[idx];
  67. if (!pObjectEntry)
  68. {
  69. break;
  70. }
  71. PWSTR pszObjectType = (pToken+1)->GetToken();
  72. if (0 == _wcsicmp(pObjectEntry->pszCommandLineObjectType, pszObjectType))
  73. {
  74. break;
  75. }
  76. idx++;
  77. }
  78. if (!pObjectEntry)
  79. {
  80. //
  81. // Display the error message and then break out of the false loop
  82. //
  83. DisplayMessage(USAGE_DSMOD);
  84. hr = E_INVALIDARG;
  85. break;
  86. }
  87. //
  88. // Now that we have the correct table entry, merge the command line table
  89. // for this object with the common commands
  90. //
  91. hr = MergeArgCommand(DSMOD_COMMON_COMMANDS,
  92. pObjectEntry->pParserTable,
  93. &pNewCommandArgs);
  94. if (FAILED(hr))
  95. {
  96. //
  97. // Display the error message and then break out of the false loop
  98. //
  99. DisplayErrorMessage(g_pszDSCommandName, L"", hr);
  100. break;
  101. }
  102. if (!pNewCommandArgs)
  103. {
  104. //
  105. // Display the usage text and then break out of the false loop
  106. //
  107. DisplayMessage(pObjectEntry->nUsageID);
  108. hr = E_FAIL;
  109. break;
  110. }
  111. PARSE_ERROR Error;
  112. if(!ParseCmd(pNewCommandArgs,
  113. argc-1,
  114. pToken+1,
  115. pObjectEntry->nUsageID,
  116. &Error,
  117. TRUE))
  118. {
  119. if (Error.Error != PARSE_ERROR_HELP_SWITCH)
  120. {
  121. //
  122. // Display the usage text and then break out of the false loop
  123. //
  124. DisplayMessage(pObjectEntry->nUsageID);
  125. }
  126. hr = E_INVALIDARG;
  127. break;
  128. }
  129. else
  130. {
  131. //
  132. // Check to see if we are doing debug spew
  133. //
  134. #ifdef DBG
  135. bool bDebugging = pNewCommandArgs[eCommDebug].bDefined &&
  136. pNewCommandArgs[eCommDebug].nValue;
  137. if (bDebugging)
  138. {
  139. ENABLE_DEBUG_OUTPUT(pNewCommandArgs[eCommDebug].nValue);
  140. }
  141. #else
  142. DISABLE_DEBUG_OUTPUT();
  143. #endif
  144. //
  145. // Be sure that mutually exclusive and dependent switches are correct
  146. //
  147. hr = DoModValidation(pNewCommandArgs);
  148. if (FAILED(hr))
  149. {
  150. DisplayMessage(pObjectEntry->nUsageID);
  151. break;
  152. }
  153. //
  154. // Command line parsing succeeded
  155. //
  156. hr = DoMod(pNewCommandArgs, pObjectEntry);
  157. }
  158. } while (false);
  159. //
  160. // Free the memory associated with the command values
  161. //
  162. if (pNewCommandArgs)
  163. {
  164. FreeCmd(pNewCommandArgs);
  165. }
  166. //
  167. // Free the tokens
  168. //
  169. if (pToken)
  170. {
  171. delete[] pToken;
  172. pToken = 0;
  173. }
  174. }
  175. //
  176. // Uninitialize COM
  177. //
  178. ::CoUninitialize();
  179. return hr;
  180. }
  181. //+--------------------------------------------------------------------------
  182. //
  183. // Function: DoModValidation
  184. //
  185. // Synopsis: Checks to be sure that command line switches that are mutually
  186. // exclusive are not both present and those that are dependent are
  187. // both presetn
  188. //
  189. // Arguments: [pCommandArgs - IN] : the command line argument structure used
  190. // to retrieve the values for each switch
  191. //
  192. // Returns: HRESULT : S_OK if everything succeeded
  193. // E_INVALIDARG if the object entry wasn't found
  194. // Anything else is a failure code from an ADSI call
  195. //
  196. // History: 19-Sep-2000 JeffJon Created
  197. //
  198. //---------------------------------------------------------------------------
  199. HRESULT DoModValidation(PARG_RECORD pCommandArgs)
  200. {
  201. ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoModValidation, hr);
  202. do // false loop
  203. {
  204. //
  205. // Check the user switches
  206. //
  207. if (pCommandArgs[eCommObjectType].bDefined &&
  208. pCommandArgs[eCommObjectType].strValue &&
  209. 0 == _wcsicmp(g_pszUser, pCommandArgs[eCommObjectType].strValue))
  210. {
  211. //
  212. // Can't have user must change password if user can change password is no
  213. //
  214. if ((pCommandArgs[eUserMustchpwd].bDefined &&
  215. pCommandArgs[eUserMustchpwd].bValue) &&
  216. (pCommandArgs[eUserCanchpwd].bDefined &&
  217. !pCommandArgs[eUserCanchpwd].bValue))
  218. {
  219. DisplayErrorMessage(g_pszDSCommandName, NULL, S_OK, IDS_MUSTCHPWD_CANCHPWD_CONFLICT);
  220. hr = E_INVALIDARG;
  221. break;
  222. }
  223. if (pCommandArgs[eUserPwd].bDefined &&
  224. pCommandArgs[eUserPwd].strValue)
  225. {
  226. size_t pwdLen = wcslen(pCommandArgs[eUserPwd].strValue);
  227. if (pwdLen > MAX_PASSWORD_LENGTH - 1)
  228. {
  229. DisplayErrorMessage(g_pszDSCommandName, NULL, S_OK, IDS_PASSWORD_TOO_LONG);
  230. hr = E_INVALIDARG;
  231. break;
  232. }
  233. }
  234. }
  235. } while (false);
  236. return hr;
  237. }
  238. //+--------------------------------------------------------------------------
  239. //
  240. // Function: DoMod
  241. //
  242. // Synopsis: Finds the appropriate object in the object table and fills in
  243. // the attribute values and then applies the changes
  244. //
  245. // Arguments: [pCommandArgs - IN] : the command line argument structure used
  246. // to retrieve the values for each switch
  247. // [pObjectEntry - IN] : pointer to the object table entry for the
  248. // object type that will be modified
  249. //
  250. // Returns: HRESULT : S_OK if everything succeeded
  251. // E_INVALIDARG if the object entry wasn't found
  252. // Anything else is a failure code from an ADSI call
  253. //
  254. // History: 07-Sep-2000 JeffJon Created
  255. //
  256. //---------------------------------------------------------------------------
  257. HRESULT DoMod(PARG_RECORD pCommandArgs, PDSOBJECTTABLEENTRY pObjectEntry)
  258. {
  259. ENTER_FUNCTION_HR(MINIMAL_LOGGING, DoMod, hr);
  260. PADS_ATTR_INFO pAttrs = NULL;
  261. do // false loop
  262. {
  263. if (!pCommandArgs || !pObjectEntry)
  264. {
  265. ASSERT(pCommandArgs && pObjectEntry);
  266. hr = E_INVALIDARG;
  267. break;
  268. }
  269. CDSCmdCredentialObject credentialObject;
  270. if (pCommandArgs[eCommUserName].bDefined)
  271. {
  272. credentialObject.SetUsername(pCommandArgs[eCommUserName].strValue);
  273. credentialObject.SetUsingCredentials(true);
  274. }
  275. if (pCommandArgs[eCommPassword].bDefined)
  276. {
  277. credentialObject.SetPassword(pCommandArgs[eCommPassword].strValue);
  278. credentialObject.SetUsingCredentials(true);
  279. }
  280. //
  281. // Initialize the base paths info from the command line args
  282. //
  283. CDSCmdBasePathsInfo basePathsInfo;
  284. if (pCommandArgs[eCommServer].bDefined)
  285. {
  286. hr = basePathsInfo.InitializeFromName(credentialObject,
  287. pCommandArgs[eCommServer].strValue,
  288. true);
  289. }
  290. else if (pCommandArgs[eCommDomain].bDefined)
  291. {
  292. hr = basePathsInfo.InitializeFromName(credentialObject,
  293. pCommandArgs[eCommDomain].strValue,
  294. false);
  295. }
  296. else
  297. {
  298. hr = basePathsInfo.InitializeFromName(credentialObject, NULL, false);
  299. }
  300. if (FAILED(hr))
  301. {
  302. //
  303. // Display error message and return
  304. //
  305. DEBUG_OUTPUT(MINIMAL_LOGGING, L"CDSBasePathsInfo::InitializeFromName failed: hr = 0x%x", hr);
  306. DisplayErrorMessage(g_pszDSCommandName, NULL, hr);
  307. break;
  308. }
  309. //
  310. // Now that we have the table entry loop through the other command line
  311. // args and see which ones can be applied
  312. //
  313. DWORD dwAttributeCount = 0;
  314. DWORD dwCount = pObjectEntry->dwAttributeCount;
  315. pAttrs = new ADS_ATTR_INFO[dwCount];
  316. if (!pAttrs)
  317. {
  318. //
  319. // Display error message and return
  320. //
  321. DisplayErrorMessage(g_pszDSCommandName, NULL, E_OUTOFMEMORY);
  322. hr = E_OUTOFMEMORY;
  323. break;
  324. }
  325. //
  326. // The DNs or Names should be given as a \0 separated list
  327. // So parse it and loop through each object
  328. //
  329. UINT nStrings = 0;
  330. PWSTR* ppszArray = NULL;
  331. ParseNullSeparatedString(pCommandArgs[eCommObjectDNorName].strValue,
  332. &ppszArray,
  333. &nStrings);
  334. if (nStrings < 1 ||
  335. !ppszArray)
  336. {
  337. //
  338. // Display the usage text and then fail
  339. //
  340. DisplayMessage(pObjectEntry->nUsageID);
  341. hr = E_INVALIDARG;
  342. break;
  343. }
  344. //
  345. // Loop through each of the objects
  346. //
  347. for (UINT nNameIdx = 0; nNameIdx < nStrings; nNameIdx++)
  348. {
  349. dwAttributeCount = 0;
  350. do // false loop
  351. {
  352. //
  353. // Get the objects DN
  354. //
  355. PWSTR pszObjectDN = ppszArray[nNameIdx];
  356. if (!pszObjectDN)
  357. {
  358. //
  359. // Display the usage text and then fail
  360. //
  361. DisplayMessage(pObjectEntry->nUsageID);
  362. hr = E_INVALIDARG;
  363. break;
  364. }
  365. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Object DN = %s", pszObjectDN);
  366. //
  367. // Compose the objects path
  368. //
  369. CComBSTR sbstrObjectPath;
  370. basePathsInfo.ComposePathFromDN(pszObjectDN, sbstrObjectPath);
  371. //
  372. // Verify that the object type matches the one entered on the command line
  373. //
  374. CComPtr<IDirectoryObject> spDirObject;
  375. hr = DSCmdOpenObject(credentialObject,
  376. sbstrObjectPath,
  377. IID_IDirectoryObject,
  378. (void**)&spDirObject,
  379. true);
  380. if (FAILED(hr))
  381. {
  382. //
  383. // Display error message and return
  384. //
  385. DisplayErrorMessage(g_pszDSCommandName,
  386. pszObjectDN,
  387. hr);
  388. break;
  389. }
  390. CComQIPtr<IADs> spADs(spDirObject);
  391. if (!spADs)
  392. {
  393. ASSERT(spADs);
  394. hr = E_INVALIDARG;
  395. DisplayErrorMessage(g_pszDSCommandName,
  396. pszObjectDN,
  397. hr);
  398. break;
  399. }
  400. CComBSTR sbstrClass;
  401. hr = spADs->get_Class(&sbstrClass);
  402. if (FAILED(hr))
  403. {
  404. //
  405. // Display error message and return
  406. //
  407. DisplayErrorMessage(g_pszDSCommandName,
  408. pszObjectDN,
  409. hr);
  410. break;
  411. }
  412. if (_wcsicmp(sbstrClass, pObjectEntry->pszObjectClass))
  413. {
  414. //
  415. // Display error message and return
  416. //
  417. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Command line type does not match object class", dwAttributeCount);
  418. DEBUG_OUTPUT(MINIMAL_LOGGING, L"command line type = %s", pCommandArgs[eCommObjectType].strValue);
  419. DEBUG_OUTPUT(MINIMAL_LOGGING, L"object class = %s", sbstrClass);
  420. DisplayErrorMessage(g_pszDSCommandName,
  421. pszObjectDN,
  422. hr,
  423. IDS_ERRMSG_CLASS_NOT_EQUAL);
  424. hr = E_INVALIDARG;
  425. break;
  426. }
  427. UINT nModificationsAttempted = 0;
  428. for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
  429. {
  430. ASSERT(pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc);
  431. UINT nAttributeIdx = pObjectEntry->pAttributeTable[dwIdx]->nAttributeID;
  432. if (pCommandArgs[nAttributeIdx].bDefined)
  433. {
  434. if (!(pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_DIRTY) ||
  435. pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_NOT_REUSABLE)
  436. {
  437. //
  438. // Call the evaluation function to get the ADS_ATTR_INFO set
  439. //
  440. PADS_ATTR_INFO pNewAttr = NULL;
  441. hr = pObjectEntry->pAttributeTable[dwIdx]->pEvalFunc(pszObjectDN,
  442. basePathsInfo,
  443. credentialObject,
  444. pObjectEntry,
  445. pCommandArgs[nAttributeIdx],
  446. dwIdx,
  447. &pNewAttr);
  448. DEBUG_OUTPUT(MINIMAL_LOGGING, L"pEvalFunc returned hr = 0x%x", hr);
  449. if (SUCCEEDED(hr) && hr != S_FALSE)
  450. {
  451. if (pNewAttr)
  452. {
  453. //
  454. // Mark the attribute entry as DIRTY so that we don't have to
  455. // do the computation for the next object
  456. //
  457. pObjectEntry->pAttributeTable[dwIdx]->dwFlags |= DS_ATTRIBUTE_DIRTY;
  458. //
  459. // Copy the value
  460. //
  461. pAttrs[dwAttributeCount] = *pNewAttr;
  462. dwAttributeCount++;
  463. }
  464. }
  465. else
  466. {
  467. //
  468. // Don't show an error if the eval function returned S_FALSE
  469. //
  470. if (hr != S_FALSE)
  471. {
  472. //
  473. // Display an error
  474. //
  475. DisplayErrorMessage(g_pszDSCommandName,
  476. pszObjectDN,
  477. hr);
  478. }
  479. if (hr == S_FALSE)
  480. {
  481. //
  482. // Return a generic error code so that we don't print the success message
  483. //
  484. hr = E_FAIL;
  485. }
  486. break;
  487. }
  488. }
  489. else
  490. {
  491. //
  492. // Need to count previously retrieved values too
  493. //
  494. dwAttributeCount++;
  495. }
  496. nModificationsAttempted++;
  497. }
  498. }
  499. if (SUCCEEDED(hr) && dwAttributeCount > 0)
  500. {
  501. //
  502. // Now that we have the attributes ready, lets set them in the DS
  503. //
  504. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Setting %d attributes", dwAttributeCount);
  505. #ifdef DBG
  506. DEBUG_OUTPUT(FULL_LOGGING, L"Modified Attributes:");
  507. SpewAttrs(pAttrs, dwAttributeCount);
  508. #endif
  509. DWORD dwAttrsModified = 0;
  510. hr = spDirObject->SetObjectAttributes(pAttrs,
  511. dwAttributeCount,
  512. &dwAttrsModified);
  513. if (FAILED(hr))
  514. {
  515. //
  516. // Display error message and return
  517. //
  518. DEBUG_OUTPUT(MINIMAL_LOGGING, L"SetObjectAttributes failed: hr = 0x%x", hr);
  519. DisplayErrorMessage(g_pszDSCommandName,
  520. pszObjectDN,
  521. hr);
  522. break;
  523. }
  524. DEBUG_OUTPUT(MINIMAL_LOGGING, L"SetObjectAttributes succeeded");
  525. }
  526. else if (SUCCEEDED(hr) && nModificationsAttempted == 0)
  527. {
  528. //
  529. // Display the usage text and then break out of the false loop
  530. //
  531. //
  532. // REVIEW_JEFFJON : this is causing a first-chance exception
  533. //
  534. DisplayMessage(pObjectEntry->nUsageID);
  535. hr = E_INVALIDARG;
  536. break;
  537. }
  538. } while (false);
  539. //
  540. // Loop through the attributes again, clearing any values for
  541. // attribute entries that are marked DS_ATTRIBUTE_NOT_REUSABLE
  542. //
  543. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Cleaning up memory and flags for object %d", nNameIdx);
  544. for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
  545. {
  546. if (pObjectEntry->pAttributeTable[dwIdx]->dwFlags & DS_ATTRIBUTE_NOT_REUSABLE)
  547. {
  548. if (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc &&
  549. ((pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_READ) ||
  550. (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_DIRTY)))
  551. {
  552. //
  553. // Cleanup the memory associated with the value
  554. //
  555. if (pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  556. {
  557. delete[] pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues;
  558. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->adsAttrInfo.pADsValues = NULL;
  559. }
  560. //
  561. // Cleanup the flags so that the attribute will be read for the next object
  562. //
  563. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags &= ~(DS_ATTRIBUTE_READ);
  564. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags &= ~(DS_ATTRIBUTE_DIRTY);
  565. DEBUG_OUTPUT(LEVEL5_LOGGING,
  566. L"Flags for attribute %s = %d",
  567. pObjectEntry->pAttributeTable[dwIdx]->pszName,
  568. pObjectEntry->pAttributeTable[dwIdx]->pAttrDesc->dwFlags);
  569. }
  570. }
  571. }
  572. if (FAILED(hr) && !pCommandArgs[eCommContinue].bDefined)
  573. {
  574. break;
  575. }
  576. //
  577. // Display the success message
  578. //
  579. if (SUCCEEDED(hr) && !pCommandArgs[eCommQuiet].bDefined)
  580. {
  581. DisplaySuccessMessage(g_pszDSCommandName,
  582. ppszArray[nNameIdx]);
  583. }
  584. } // Name for loop
  585. } while (false);
  586. //
  587. // Cleanup
  588. //
  589. if (pAttrs)
  590. {
  591. delete[] pAttrs;
  592. pAttrs = NULL;
  593. }
  594. return hr;
  595. }