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.

1700 lines
50 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. delegate.c
  5. Abstract:
  6. This Module implements the delegation tool, which allows for the management
  7. of access to DS objects
  8. Author:
  9. Mac McLain (MacM) 10-02-96
  10. Environment:
  11. User Mode
  12. Revision History:
  13. --*/
  14. #include <delegate.h>
  15. __cdecl main(
  16. IN INT argc,
  17. IN CHAR *argv[]
  18. )
  19. /*++
  20. Routine Description:
  21. The MAIN for this executable
  22. Arguments:
  23. argc - The count of arguments
  24. argv - The list of arguments
  25. Return Value:
  26. 0 - Success
  27. 1 - Failure
  28. --*/
  29. {
  30. DWORD dwErr = ERROR_SUCCESS;
  31. PWSTR pwszObjPath = NULL;
  32. ULONG fAccessFlags = 0;
  33. PWSTR rgwszObjIds[UNKNOWN_ID];
  34. PACTRL_ACCESSW rgpDefObjAccess[MAX_DEF_ACCESS_ID + 1];
  35. PACTRL_ACCESSW pCurrentAccess = NULL;
  36. PACTRL_ACCESSW pAccess = NULL;
  37. DWORD i;
  38. DWORD cUsed;
  39. memset(rgwszObjIds, 0, sizeof(rgwszObjIds));
  40. memset(rgpDefObjAccess, 0, sizeof(rgpDefObjAccess));
  41. //
  42. // Temporary inclusion, until the new ADVAPI32.DLL is built
  43. //
  44. AccProvInit(dwErr);
  45. if(dwErr != ERROR_SUCCESS)
  46. {
  47. fprintf(stderr,
  48. "Failed to initialize the security apis: %lu\n",
  49. dwErr);
  50. }
  51. //
  52. // Ok, parse the command line
  53. //
  54. if(argc < 2)
  55. {
  56. Usage();
  57. exit(1);
  58. }
  59. //
  60. // See if we need help
  61. //
  62. if(strlen(argv[1]) == 2 && IS_ARG_SWITCH(argv[1]) && argv[1][1] == '?')
  63. {
  64. Usage();
  65. exit(1);
  66. }
  67. //
  68. // Ok, convert our OU parameter into a WIDE string, so we can do what we
  69. // have to
  70. //
  71. if(dwErr == ERROR_SUCCESS)
  72. {
  73. dwErr = ConvertStringAToStringW(argv[1], &pwszObjPath);
  74. }
  75. //
  76. // Ok, first, we initialize our ID list from the DS schema
  77. //
  78. if(dwErr == ERROR_SUCCESS)
  79. {
  80. dwErr = InitializeIdAndAccessLists(pwszObjPath,
  81. rgwszObjIds,
  82. rgpDefObjAccess);
  83. }
  84. //
  85. // Make sure we're actually dealing with an OU
  86. //
  87. if(dwErr == ERROR_SUCCESS)
  88. {
  89. BOOL fIsOU = FALSE;
  90. dwErr = IsPathOU(pwszObjPath,
  91. &fIsOU);
  92. if(dwErr == ERROR_SUCCESS)
  93. {
  94. if(fIsOU == FALSE)
  95. {
  96. fprintf(stderr,
  97. "%ws is not an Organizational Unit\n",
  98. pwszObjPath);
  99. dwErr = ERROR_INVALID_PARAMETER;
  100. }
  101. }
  102. else
  103. {
  104. fprintf(stderr,
  105. "Failed to determine the status of %ws\n",
  106. pwszObjPath);
  107. }
  108. }
  109. else
  110. {
  111. fprintf(stderr,"Initialization failed\n");
  112. }
  113. //
  114. // First pass through the command line. We'll read off our flags. We
  115. // need this to determine whether to do the initial read or not
  116. //
  117. if(dwErr == ERROR_SUCCESS)
  118. {
  119. //
  120. // First, go through and look for all of our flags
  121. //
  122. for(i = 2; i < (DWORD)argc; i++)
  123. {
  124. if(IS_ARG_SWITCH(argv[i]))
  125. {
  126. if(_stricmp(argv[i] + 1, "T") == 0 ||
  127. _stricmp(argv[i] + 1, "reseT") == 0)
  128. {
  129. fAccessFlags |= D_REPLACE;
  130. }
  131. else if(_stricmp(argv[i] + 1, "I") == 0 ||
  132. _stricmp(argv[i] + 1, "Inherit") == 0)
  133. {
  134. fAccessFlags |= D_INHERIT;
  135. }
  136. else if(_stricmp(argv[i] + 1, "P") == 0 ||
  137. _stricmp(argv[i] + 1, "Protected") == 0)
  138. {
  139. fAccessFlags |= D_PROTECT;
  140. }
  141. }
  142. }
  143. }
  144. //
  145. // See if we need to read the current access, which is if we are simply
  146. // displaying the current security, or editing the existing security
  147. //
  148. if(dwErr == ERROR_SUCCESS && (argc == 2 ||
  149. (fAccessFlags & D_REPLACE) == 0))
  150. {
  151. //
  152. // GetNamedSecurityInfoEx is a NT 5 API
  153. //
  154. dwErr = GetNamedSecurityInfoEx(pwszObjPath,
  155. SE_DS_OBJECT_ALL,
  156. DACL_SECURITY_INFORMATION,
  157. L"Windows NT Access Provider",
  158. NULL,
  159. &pCurrentAccess,
  160. NULL,
  161. NULL,
  162. NULL);
  163. if(dwErr == ERROR_SUCCESS)
  164. {
  165. //
  166. // See if we were supposed to display it
  167. //
  168. if(argc == 2)
  169. {
  170. DumpAccess(pwszObjPath,
  171. pCurrentAccess,
  172. rgwszObjIds);
  173. }
  174. }
  175. else
  176. {
  177. fprintf(stderr,
  178. "Failed to read the current security from %ws\n",
  179. pwszObjPath);
  180. }
  181. }
  182. //
  183. // Ok, now process the command line again, and do the necessary operations
  184. //
  185. if(dwErr == ERROR_SUCCESS)
  186. {
  187. //
  188. // First, go through and look for all of our flags
  189. //
  190. i = 2;
  191. while(dwErr == ERROR_SUCCESS && i < (DWORD)argc)
  192. {
  193. if(IS_ARG_SWITCH(argv[i]))
  194. {
  195. if(_stricmp(argv[i] + 1, "T") == 0 ||
  196. _stricmp(argv[i] + 1, "reseT") == 0)
  197. {
  198. //
  199. // already processed above
  200. //
  201. }
  202. else if(_stricmp(argv[i] + 1, "I") == 0 ||
  203. _stricmp(argv[i] + 1, "Inherit") == 0)
  204. {
  205. //
  206. // already processed above
  207. //
  208. }
  209. else if(_stricmp(argv[i] + 1, "P") == 0 ||
  210. _stricmp(argv[i] + 1, "Protected") == 0)
  211. {
  212. //
  213. // already processed above
  214. //
  215. }
  216. else if(_stricmp(argv[i] + 1, "R") == 0 ||
  217. _stricmp(argv[i] + 1, "Revoke") == 0)
  218. {
  219. dwErr = ProcessCmdlineUsers(pCurrentAccess,
  220. argv,
  221. argc,
  222. i,
  223. REVOKE,
  224. fAccessFlags,
  225. rgwszObjIds,
  226. rgpDefObjAccess,
  227. &cUsed,
  228. &pAccess);
  229. if(dwErr == ERROR_SUCCESS)
  230. {
  231. LocalFree(pCurrentAccess);
  232. pCurrentAccess = pAccess;
  233. i += cUsed;
  234. }
  235. }
  236. else if(_stricmp(argv[i] + 1, "G") == 0 ||
  237. _stricmp(argv[i] + 1, "Grant") == 0)
  238. {
  239. dwErr = ProcessCmdlineUsers(pCurrentAccess,
  240. argv,
  241. argc,
  242. i,
  243. GRANT,
  244. fAccessFlags,
  245. rgwszObjIds,
  246. rgpDefObjAccess,
  247. &cUsed,
  248. &pAccess);
  249. if(dwErr == ERROR_SUCCESS)
  250. {
  251. LocalFree(pCurrentAccess);
  252. pCurrentAccess = pAccess;
  253. i += cUsed;
  254. }
  255. }
  256. else if(_stricmp(argv[i] + 1, "D") == 0 ||
  257. _stricmp(argv[i] + 1, "Deny") == 0)
  258. {
  259. dwErr = ProcessCmdlineUsers(pCurrentAccess,
  260. argv,
  261. argc,
  262. i,
  263. DENY,
  264. fAccessFlags,
  265. rgwszObjIds,
  266. rgpDefObjAccess,
  267. &cUsed,
  268. &pAccess);
  269. if(dwErr == ERROR_SUCCESS)
  270. {
  271. LocalFree(pCurrentAccess);
  272. pCurrentAccess = pAccess;
  273. i += cUsed;
  274. }
  275. }
  276. else
  277. {
  278. //
  279. // Some unknown command line parameter
  280. //
  281. fprintf(stderr,
  282. "Unrecognized command line parameter: %s\n",
  283. argv[i]);
  284. dwErr = ERROR_INVALID_PARAMETER;
  285. }
  286. }
  287. i++;
  288. }
  289. }
  290. //
  291. // Finally, set the access as requested
  292. //
  293. if(dwErr == ERROR_SUCCESS && pAccess != NULL)
  294. {
  295. //
  296. // SetNamedSecurityInfoEx is a NT 5 API
  297. //
  298. dwErr = SetNamedSecurityInfoEx(pwszObjPath,
  299. SE_DS_OBJECT_ALL,
  300. DACL_SECURITY_INFORMATION,
  301. L"Windows NT Access Provider",
  302. pCurrentAccess,
  303. NULL,
  304. NULL,
  305. NULL,
  306. NULL);
  307. if(dwErr != ERROR_SUCCESS)
  308. {
  309. fprintf(stderr,
  310. "Delegate failed to write the new access to %ws\n",
  311. pwszObjPath);
  312. }
  313. }
  314. //
  315. // Last little informative message...
  316. //
  317. if(dwErr == ERROR_PATH_NOT_FOUND)
  318. {
  319. fprintf(stderr,
  320. "DELEGATE did not recognize %ws as a DS path\n",
  321. pwszObjPath);
  322. }
  323. //
  324. // Free all our allocated memory
  325. //
  326. FreeIdAndAccessList(rgwszObjIds,
  327. rgpDefObjAccess);
  328. LocalFree(pwszObjPath);
  329. LocalFree(pCurrentAccess);
  330. if(dwErr == ERROR_SUCCESS)
  331. {
  332. fprintf(stdout,
  333. "The command completed successfully.\n");
  334. }
  335. return(dwErr == ERROR_SUCCESS ? 0 : 1);
  336. }
  337. VOID
  338. DumpAccess (
  339. IN PWSTR pwszObject,
  340. IN PACTRL_ACCESSW pAccess,
  341. IN PWSTR *ppwszIDs
  342. )
  343. /*++
  344. Routine Description:
  345. This routine will display the given actrl_access list to stdout
  346. Arguments:
  347. pwszObject - The path to the object being displayed
  348. pAccess - The access list to display
  349. ppwszIDs - The list of property/control ids read from the schema. Used
  350. to assign a name to the property list.
  351. Return Value:
  352. VOID
  353. --*/
  354. {
  355. ULONG iProp, iEnt, i;
  356. ULONG Inherit;
  357. ACCESS_RIGHTS Access;
  358. PWSTR pwszTag = NULL;
  359. PWSTR pwszPropertyTag = L"Object or Property:";
  360. PWSTR rgwszInheritTags[] = {L"None",
  361. L"Object",
  362. L"Container",
  363. L"Inherit, no propagate",
  364. L"Inherit only",
  365. L"Inherited"};
  366. PWSTR rgwszAccessTags[] = {L"None",
  367. L"Delete",
  368. L"Read Security Information",
  369. L"Change Security Information",
  370. L"Change owner",
  371. L"Synchronize",
  372. L"Open Object",
  373. L"Create Child",
  374. L"Delete Child",
  375. L"List contents",
  376. L"Write Self",
  377. L"Read Property",
  378. L"Write Property"};
  379. ACCESS_RIGHTS rgAccess[] = {0,
  380. ACTRL_DELETE,
  381. ACTRL_READ_CONTROL,
  382. ACTRL_CHANGE_ACCESS,
  383. ACTRL_CHANGE_OWNER,
  384. ACTRL_SYNCHRONIZE,
  385. ACTRL_DS_OPEN,
  386. ACTRL_DS_CREATE_CHILD,
  387. ACTRL_DS_DELETE_CHILD,
  388. ACTRL_DS_LIST,
  389. ACTRL_DS_SELF,
  390. ACTRL_DS_READ_PROP,
  391. ACTRL_DS_WRITE_PROP};
  392. PWSTR rgwszPropTags[] = {L"User object",
  393. L"Group object",
  394. L"Printer object",
  395. L"Volume object",
  396. L"Organizational Unit object",
  397. L"Change group membership property",
  398. L"Change password property",
  399. L"Account control property",
  400. L"Local Group object"};
  401. //
  402. // These [currently string versions of valid] IDs are currently planned to
  403. // be publicly defined for the product. They are included below only due
  404. // to the fact that is no current public definition (as it is not
  405. // necessary for anyone else to need them), and the delegate tool needs
  406. // to be able to display a friendly name for it. DO NOT RELY ON THE
  407. // FOLLOWING DEFINITIONS REMAINING CONSTANT.
  408. //
  409. PWSTR rgwszDSControlIds[] = {
  410. L"ab721a50-1e2f-11d0-9819-00aa0040529b",
  411. L"ab721a51-1e2f-11d0-9819-00aa0040529b"};
  412. PWSTR rgwszDSControlTrags[] = {
  413. L"List Domain Accounts",
  414. L"Lookup Domains"
  415. };
  416. //
  417. // Don't dump something that doesn't exist...
  418. //
  419. if(pAccess == NULL)
  420. {
  421. return;
  422. }
  423. fprintf(stdout, "Displaying access list for object %ws\n", pwszObject);
  424. fprintf(stdout, "\tNumber of property lists: %lu\n", pAccess->cEntries);
  425. for(iProp = 0; iProp < pAccess->cEntries; iProp++)
  426. {
  427. if(pAccess->pPropertyAccessList[iProp].lpProperty != NULL)
  428. {
  429. pwszTag = NULL;
  430. //
  431. // Find it in our list, so we can display the right value
  432. //
  433. for(i = 0; i < UNKNOWN_ID; i++)
  434. {
  435. if(_wcsicmp(pAccess->pPropertyAccessList[iProp].lpProperty,
  436. ppwszIDs[i]) == 0)
  437. {
  438. pwszTag = rgwszPropTags[i];
  439. break;
  440. }
  441. }
  442. //
  443. // Look up the list of DS control rights
  444. //
  445. for(i = 0;
  446. i < sizeof(rgwszDSControlIds) / sizeof(PWSTR) &&
  447. pwszTag == NULL;
  448. i++)
  449. {
  450. if(_wcsicmp(pAccess->pPropertyAccessList[iProp].lpProperty,
  451. rgwszDSControlIds[i]) == 0)
  452. {
  453. pwszTag = rgwszDSControlTrags[i];
  454. pwszPropertyTag = L"DS Control right id:";
  455. break;
  456. }
  457. }
  458. if(pwszTag == NULL)
  459. {
  460. fprintf(stdout,
  461. "\t\tUnrecognized property whose id is %ws\n",
  462. pAccess->pPropertyAccessList[iProp].lpProperty);
  463. }
  464. else
  465. {
  466. fprintf(stdout, "\t\t%ws %ws\n", pwszPropertyTag, pwszTag);
  467. }
  468. }
  469. else
  470. {
  471. fprintf(stdout, "\t\tObject: %ws\n", pwszObject);
  472. }
  473. //
  474. // Is it protected?
  475. //
  476. if(pAccess->pPropertyAccessList[iProp].fListFlags != 0)
  477. {
  478. if((pAccess->pPropertyAccessList[iProp].fListFlags &
  479. ACTRL_ACCESS_PROTECTED) != 0)
  480. {
  481. fprintf(stdout,"\t\tAccess list is protected\n");
  482. }
  483. }
  484. if(pAccess->pPropertyAccessList[iProp].pAccessEntryList == NULL)
  485. {
  486. fprintf(stdout,"\t\tpAccessEntryList: NULL\n");
  487. }
  488. else
  489. {
  490. PACTRL_ACCESS_ENTRYW pAE= pAccess->pPropertyAccessList[iProp].
  491. pAccessEntryList->pAccessList;
  492. fprintf(stdout,
  493. "\t\t\t%lu Access Entries for this object or property\n",
  494. pAccess->pPropertyAccessList[iProp].pAccessEntryList->
  495. cEntries);
  496. for(iEnt = 0;
  497. iEnt < pAccess->pPropertyAccessList[iProp].
  498. pAccessEntryList->cEntries;
  499. iEnt++)
  500. {
  501. //
  502. // Type of entry
  503. //
  504. if(pAE[iEnt].fAccessFlags == ACTRL_ACCESS_ALLOWED)
  505. {
  506. fprintf(stdout,
  507. "\t\t\t[%lu] Access Allowed entry\n",
  508. iEnt);
  509. }
  510. else if(pAE[iEnt].fAccessFlags == ACTRL_ACCESS_DENIED)
  511. {
  512. fprintf(stdout,
  513. "\t\t\t[%lu] Access Denied entry\n",
  514. iEnt);
  515. }
  516. else
  517. {
  518. fprintf(stdout,"\t\t\t[%lu]", iEnt);
  519. if((pAE[iEnt].fAccessFlags & ACTRL_AUDIT_SUCCESS) != 0)
  520. {
  521. fprintf(stdout,"Success Audit");
  522. }
  523. if((pAE[iEnt].fAccessFlags & ACTRL_AUDIT_FAILURE) != 0)
  524. {
  525. if((pAE[iEnt].fAccessFlags & ACTRL_AUDIT_SUCCESS) != 0)
  526. {
  527. fprintf(stdout," | ");
  528. }
  529. fprintf(stdout,"Failure Audit");
  530. }
  531. fprintf(stdout," entry\n");
  532. }
  533. //
  534. // User name
  535. //
  536. fprintf(stdout,"\t\t\t\tUser: %ws\n",
  537. pAE[iEnt].Trustee.ptstrName);
  538. //
  539. // Access rights
  540. //
  541. fprintf(stdout,"\t\t\t\tAccess: ");
  542. Access = pAE[iEnt].Access;
  543. if(Access == 0)
  544. {
  545. fprintf(stdout,"%ws\n", rgwszAccessTags[0]);
  546. }
  547. else
  548. {
  549. for(i = 1;
  550. i < sizeof(rgwszAccessTags) / sizeof(PWSTR);
  551. i++)
  552. {
  553. if((Access & rgAccess[i]) != 0)
  554. {
  555. fprintf(stdout,"%ws", rgwszAccessTags[i]);
  556. Access &= ~(rgAccess[i]);
  557. if(Access != 0)
  558. {
  559. fprintf(stdout,
  560. " |\n\t\t\t\t ");
  561. }
  562. }
  563. }
  564. if(Access != 0)
  565. {
  566. fprintf(stdout,
  567. "Unrecognized rights: 0x%lx\n",
  568. Access);
  569. }
  570. fprintf(stdout,"\n");
  571. }
  572. //
  573. // Inheritance
  574. //
  575. fprintf(stdout,"\t\t\t\tInheritance: ");
  576. Inherit = pAE[iEnt].Inheritance;
  577. if(Inherit == 0)
  578. {
  579. fprintf(stdout,"%ws\n", rgwszInheritTags[0]);
  580. }
  581. else
  582. {
  583. for(i = 0;
  584. i < sizeof(rgwszInheritTags) / sizeof(PWSTR);
  585. i++)
  586. {
  587. if((Inherit & 1 << i) != 0)
  588. {
  589. fprintf(stdout,"%ws", rgwszInheritTags[i + 1]);
  590. Inherit &= ~(1 << i);
  591. if(Inherit == 0)
  592. {
  593. fprintf(stdout,"\n");
  594. }
  595. else
  596. {
  597. fprintf(stdout,
  598. " |\n\t\t\t\t ");
  599. }
  600. }
  601. }
  602. }
  603. if(pAE[iEnt].lpInheritProperty != NULL)
  604. {
  605. pwszTag = NULL;
  606. //
  607. // Find it in our list, so we can display the right value
  608. //
  609. for(i = 0; i < UNKNOWN_ID; i++)
  610. {
  611. if(_wcsicmp(pAE[iEnt].lpInheritProperty,
  612. ppwszIDs[i]) == 0)
  613. {
  614. pwszTag = rgwszPropTags[i];
  615. break;
  616. }
  617. }
  618. if(pwszTag == NULL)
  619. {
  620. fprintf(stdout,
  621. "\t\t\t\tUnrecognized inherit to object "
  622. "whose id is %ws\n",
  623. pAE[iEnt].lpInheritProperty);
  624. }
  625. else
  626. {
  627. fprintf(stdout,
  628. "\t\t\t\tObject to inherit to: %ws\n",
  629. pwszTag);
  630. }
  631. }
  632. }
  633. }
  634. printf("\n");
  635. }
  636. }
  637. VOID
  638. Usage (
  639. )
  640. /*++
  641. Routine Description:
  642. This routine will display the expected command line usage
  643. Arguments:
  644. None
  645. Return Value:
  646. VOID
  647. --*/
  648. {
  649. fprintf(stdout,
  650. "Delegates administrative privileges on a directory OU\n");
  651. fprintf(stdout, "\n");
  652. fprintf(stdout,
  653. "DELEGATE <ou> [/T] [/I] [/P] [/G user:perm] [/D user:perm [...]] "
  654. "[/R user [...]]\n");
  655. fprintf(stdout, "\n");
  656. fprintf(stdout," <ou>\tOU to modify or display the rights for\n");
  657. fprintf(stdout," /T\tReplace the access instead of editing it.\n");
  658. fprintf(stdout," /I\tInherit to all subcontainers in the directory.\n");
  659. fprintf(stdout," /P\tMark the object as protected following the operation\n");
  660. fprintf(stdout," /G user:perm\tGrant specified user admin access rights.\n");
  661. fprintf(stdout," /D user:perm\tDeny specified user admin access rights.\n");
  662. fprintf(stdout," \tPerm can be:\n");
  663. fprintf(stdout," \t\tAbility to create/manage objects in this container\n");
  664. fprintf(stdout," \t\t\t%2s Create/Manage All object types\n",D_ALL);
  665. fprintf(stdout," \t\t\t%2s Create/Manage Users\n", D_USER);
  666. fprintf(stdout," \t\t\t%2s Create/Manage Groups\n", D_GROUP);
  667. fprintf(stdout," \t\t\t%2s Create/Manage Printers\n", D_PRINT);
  668. fprintf(stdout," \t\t\t%2s Create/Manage Volumes\n", D_VOL);
  669. fprintf(stdout," \t\t\t%2s Create/Manage OUs\n", D_OU);
  670. fprintf(stdout," \t\tAbility to modify specific user or group "
  671. "properties\n");
  672. fprintf(stdout," \t\t\t%2s Change Group membership for "
  673. "all groups\n", D_MEMBERS);
  674. fprintf(stdout," \t\t\t%2s Set User Passwords\n", D_PASSWD);
  675. fprintf(stdout," \t\t\t%2s Enable/Disable user accounts\n", D_ENABLE);
  676. fprintf(stdout, "\n");
  677. fprintf(stdout," /R user Revoke\tSpecified user's access rights (only valid "
  678. "without /E).\n");
  679. fprintf(stdout, "\n");
  680. fprintf(stdout,"You can specify more than one user in a command and "
  681. "more than one perm per user, seperated by a , (comma).\n");
  682. }
  683. DWORD
  684. ConvertStringAToStringW (
  685. IN PSTR pszString,
  686. OUT PWSTR *ppwszString
  687. )
  688. /*++
  689. Routine Description:
  690. This routine will convert an ASCII string to a UNICODE string.
  691. The returned string buffer must be freed via a call to LocalFree
  692. Arguments:
  693. pszString - The string to convert
  694. ppwszString - Where the converted string is returned
  695. Return Value:
  696. ERROR_SUCCESS - Success
  697. ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
  698. --*/
  699. {
  700. if(pszString == NULL)
  701. {
  702. *ppwszString = NULL;
  703. }
  704. else
  705. {
  706. ULONG cLen = strlen(pszString);
  707. *ppwszString = (PWSTR)LocalAlloc(LMEM_FIXED,sizeof(WCHAR) *
  708. (mbstowcs(NULL, pszString, cLen + 1) + 1));
  709. if(*ppwszString != NULL)
  710. {
  711. mbstowcs(*ppwszString,
  712. pszString,
  713. cLen + 1);
  714. }
  715. else
  716. {
  717. return(ERROR_NOT_ENOUGH_MEMORY);
  718. }
  719. }
  720. return(ERROR_SUCCESS);
  721. }
  722. DWORD
  723. ConvertStringWToStringA (
  724. IN PWSTR pwszString,
  725. OUT PSTR *ppszString
  726. )
  727. /*++
  728. Routine Description:
  729. This routine will convert a UNICODE string to an ANSI string.
  730. The returned string buffer must be freed via a call to LocalFree
  731. Arguments:
  732. pwszString - The string to convert
  733. ppszString - Where the converted string is returned
  734. Return Value:
  735. ERROR_SUCCESS - Success
  736. ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
  737. --*/
  738. {
  739. if(pwszString == NULL)
  740. {
  741. *ppszString = NULL;
  742. }
  743. else
  744. {
  745. ULONG cLen = wcslen(pwszString);
  746. *ppszString = (PSTR)LocalAlloc(LMEM_FIXED,sizeof(CHAR) *
  747. (wcstombs(NULL, pwszString, cLen + 1) + 1));
  748. if(*ppszString != NULL)
  749. {
  750. wcstombs(*ppszString,
  751. pwszString,
  752. cLen + 1);
  753. }
  754. else
  755. {
  756. return(ERROR_NOT_ENOUGH_MEMORY);
  757. }
  758. }
  759. return(ERROR_SUCCESS);
  760. }
  761. DWORD
  762. InitializeIdAndAccessLists (
  763. IN PWSTR pwszOU,
  764. IN PWSTR *ppwszObjIdList,
  765. IN PACTRL_ACCESS *ppDefObjAccessList
  766. )
  767. /*++
  768. Routine Description:
  769. This routine will read the list of object ids from the schema for the
  770. object types as indicated by DELEGATE_OBJ_ID enumeration.
  771. The returned access list needs to be processed by FreeIdList.
  772. Arguments:
  773. pwszOU - Information on the domain for which to query the schema
  774. ppwszObjIdList - The list of object ids to initialize. The list must
  775. already exist and must of the proper size
  776. Return Value:
  777. ERROR_SUCCESS - Success
  778. ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
  779. ERROR_INVALID_PARAMETER - The OU given was not correct
  780. --*/
  781. {
  782. DWORD dwErr = ERROR_SUCCESS;
  783. DWORD i;
  784. PSTR pszSchemaPath = NULL;
  785. PLDAP pLDAP;
  786. //
  787. // Build a list of attributes to read
  788. //
  789. PSTR pszAttribs[] = {"User", // USER_ID
  790. "Group", // GROUP_ID
  791. "Print-Queue", // PRINT_ID
  792. "Volume", // VOLUME_ID
  793. "Organizational-Unit", // OU_ID
  794. "Member", // MEMBER_ID
  795. "User-Password", // PASSWD_ID
  796. "User-Account-Control", // ACCTCTRL_ID
  797. "LocalGroup" // LOCALGRP_ID
  798. };
  799. //
  800. // Get the path to the schema
  801. //
  802. dwErr = LDAPReadSchemaPath(pwszOU,
  803. &pszSchemaPath,
  804. &pLDAP);
  805. if(dwErr == ERROR_SUCCESS)
  806. {
  807. //
  808. // Ok, now, we need to query the schema for the information
  809. //
  810. for(i = 0; i < UNKNOWN_ID && dwErr == ERROR_SUCCESS; i++)
  811. {
  812. //
  813. // Get the info from the schema
  814. //
  815. dwErr = LDAPReadSecAndObjIdAsString(pLDAP,
  816. pszSchemaPath,
  817. pszAttribs[i],
  818. &(ppwszObjIdList[i]),
  819. i > MAX_DEF_ACCESS_ID ?
  820. NULL :
  821. &(ppDefObjAccessList[i]));
  822. }
  823. LocalFree(pszSchemaPath);
  824. LDAPUnbind(pLDAP);
  825. }
  826. return(dwErr);
  827. }
  828. VOID
  829. FreeIdAndAccessList (
  830. IN PWSTR *ppwszObjIdList,
  831. IN PACTRL_ACCESS *ppDefObjAccessList
  832. )
  833. /*++
  834. Routine Description:
  835. This routine will process the list of Ids and determine if any of them
  836. have been converted to strings. If so, it deallocates the memory
  837. Arguments:
  838. pObjIdList - The list of object ids to free
  839. Return Value:
  840. VOID
  841. --*/
  842. {
  843. DWORD i;
  844. for(i = 0; i < UNKNOWN_ID; i++)
  845. {
  846. RpcStringFree(&(ppwszObjIdList[i]));
  847. if(i <= MAX_DEF_ACCESS_ID)
  848. {
  849. LocalFree(ppDefObjAccessList[i]);
  850. }
  851. }
  852. }
  853. DWORD
  854. ProcessCmdlineUsers (
  855. IN PACTRL_ACCESSW pAccessList,
  856. IN CHAR *argv[],
  857. IN INT argc,
  858. IN DWORD iStart,
  859. IN DELEGATE_OP Op,
  860. IN ULONG fFlags,
  861. IN PWSTR *ppwszIDs,
  862. IN PACTRL_ACCESS *ppDefObjAccessList,
  863. OUT PULONG pcUsed,
  864. OUT PACTRL_ACCESSW *ppNewAccess
  865. )
  866. /*++
  867. Routine Description:
  868. This routine will process the command line for any users to have
  869. access added/denied. If any entries are found, the access list will be
  870. appropriately updated.
  871. The returned access list must be freed via a call to LocalFree
  872. Arguments:
  873. pAccessList - The current access list
  874. argv - List of command line arguments
  875. argc - count of command line arguments
  876. iStart - Where in the command line does the current argument start
  877. Op - Type of operation (grant, revoke, etc) to perform
  878. fInherit - Whether to do inheritance or not
  879. fProtected - Whether to mark the entries as protected
  880. ppwszIDs - List of supported IDs
  881. pcUsed - Number of items command line items used
  882. ppNewAccess - Where the new access list is returned. Only valid if
  883. returned count of revoked items is non-0
  884. Return Value:
  885. ERROR_SUCCESS - Success
  886. ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
  887. --*/
  888. {
  889. DWORD dwErr = ERROR_SUCCESS;
  890. DWORD i;
  891. PACTRL_ACCESSW pListToFree = NULL;
  892. *pcUsed = 0;
  893. iStart++;
  894. //
  895. // Process all the entries until we find the next seperator or the end of
  896. // the list
  897. //
  898. while(iStart + *pcUsed < (DWORD)argc &&
  899. !IS_ARG_SWITCH(argv[iStart + *pcUsed]) &&
  900. dwErr == ERROR_SUCCESS)
  901. {
  902. PWSTR pwszUser = NULL;
  903. PSTR pszAccess;
  904. PSTR pszAccessStart;
  905. //
  906. // Get the user name and the list of arguments, if it exists
  907. //
  908. dwErr = GetUserInfoFromCmdlineString(argv[iStart + *pcUsed],
  909. &pwszUser,
  910. &pszAccessStart);
  911. if(dwErr == ERROR_SUCCESS)
  912. {
  913. pszAccess = pszAccessStart;
  914. //
  915. // Should we have arguments? All except for the revoke case, we
  916. // should
  917. //
  918. if(pszAccess == NULL && Op != REVOKE)
  919. {
  920. fprintf(stderr,
  921. "Missing permissions for %ws\n",
  922. pwszUser);
  923. dwErr = ERROR_INVALID_PARAMETER;
  924. }
  925. }
  926. //
  927. // Ok, now we'll have to process the list, and actually build the
  928. // access entries
  929. //
  930. if(dwErr == ERROR_SUCCESS)
  931. {
  932. DWORD iIndex = 0;
  933. //
  934. // Reset our list of entries...
  935. //
  936. pszAccess = pszAccessStart;
  937. while(dwErr == ERROR_SUCCESS)
  938. {
  939. PSTR pszNext = NULL;
  940. if(pszAccess != NULL)
  941. {
  942. pszNext = strchr(pszAccess, ',');
  943. if(pszNext != NULL)
  944. {
  945. *pszNext = '\0';
  946. }
  947. }
  948. dwErr = AddAccessEntry(pAccessList,
  949. pszAccess,
  950. pwszUser,
  951. Op,
  952. ppwszIDs,
  953. ppDefObjAccessList,
  954. fFlags,
  955. ppNewAccess);
  956. //
  957. // Restore our string
  958. //
  959. if(pszNext != NULL)
  960. {
  961. *pszNext = ',';
  962. pszNext++;
  963. }
  964. pszAccess = pszNext;
  965. if(dwErr == ERROR_SUCCESS)
  966. {
  967. //
  968. // We don't want to free the original list, since that
  969. // is what we were given to start with...
  970. //
  971. LocalFree(pListToFree);
  972. pAccessList = *ppNewAccess;
  973. pListToFree = pAccessList;
  974. }
  975. else
  976. {
  977. if(dwErr == ERROR_NONE_MAPPED)
  978. {
  979. fprintf(stderr,"Unknown user %ws specified\n",
  980. pwszUser);
  981. }
  982. }
  983. if(Op == REVOKE || pszAccess == NULL)
  984. {
  985. break;
  986. }
  987. }
  988. }
  989. (*pcUsed)++;
  990. }
  991. if(*pcUsed == 0)
  992. {
  993. dwErr = ERROR_INVALID_PARAMETER;
  994. fprintf(stderr,"No user information was supplied!\n");
  995. }
  996. return(dwErr);
  997. }
  998. DWORD
  999. GetUserInfoFromCmdlineString (
  1000. IN PSTR pszUserInfo,
  1001. OUT PWSTR *ppwszUser,
  1002. OUT PSTR *ppszAccessStart
  1003. )
  1004. /*++
  1005. Routine Description:
  1006. This routine will process the command line for any user to convert the
  1007. user name to a wide string, and optionally get the access, if it exists
  1008. The returned user must be freed via a call to LocalFree
  1009. Arguments:
  1010. pszUserInfo - The user info to convert. In the form of username or
  1011. username:access
  1012. ppwszUser - Where to return the user name
  1013. pAccess - Where the access is returned
  1014. Return Value:
  1015. ERROR_SUCCESS - Success
  1016. --*/
  1017. {
  1018. DWORD dwErr = ERROR_SUCCESS;
  1019. //
  1020. // First, find the seperator, if it exists
  1021. //
  1022. PSTR pszSep = strchr(pszUserInfo, ':');
  1023. if(pszSep != NULL)
  1024. {
  1025. *pszSep = '\0';
  1026. }
  1027. //
  1028. // Convert our user name
  1029. //
  1030. dwErr = ConvertStringAToStringW(pszUserInfo,
  1031. ppwszUser);
  1032. if(pszSep != NULL)
  1033. {
  1034. *pszSep = ':';
  1035. pszSep++;
  1036. }
  1037. *ppszAccessStart = pszSep;
  1038. return(dwErr);
  1039. }
  1040. DWORD
  1041. AddAccessEntry (
  1042. IN PACTRL_ACCESSW pAccessList,
  1043. IN PSTR pszAccess,
  1044. IN PWSTR pwszTrustee,
  1045. IN DELEGATE_OP Op,
  1046. IN PWSTR *ppwszIDs,
  1047. IN PACTRL_ACCESS *ppDefObjAccessList,
  1048. IN ULONG fFlags,
  1049. OUT PACTRL_ACCESSW *ppNewAccess
  1050. )
  1051. /*++
  1052. Routine Description:
  1053. This routine will add a new access entry to the list based upon the access
  1054. action string and the operation. The pointer to the index variable will
  1055. indicate where in the list it goes, and will be updated to point to the
  1056. next entry on return.
  1057. Arguments:
  1058. pAccessList - The current access list. Can be NULL.
  1059. pszAccess - User access string to add
  1060. pwszTrustee - The user for which an entry is being created
  1061. Op - Type of operation (grant, revoke, etc) to perform
  1062. ppwszIDs - List of object IDs from the DS Schema
  1063. fFlags - Whether to do inheritance, protection, etc
  1064. ppNewAccess - Where the new access list is returned.
  1065. Return Value:
  1066. ERROR_SUCCESS - Success
  1067. ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
  1068. --*/
  1069. {
  1070. DWORD dwErr = ERROR_SUCCESS;
  1071. DWORD i,j,k,iIndex = 0;
  1072. PWSTR pwszProperty = NULL;
  1073. ULONG cEntries = 0;
  1074. BOOL fInherit;
  1075. ACCESS_MODE Access[] = {REVOKE_ACCESS,
  1076. GRANT_ACCESS,
  1077. GRANT_ACCESS};
  1078. ULONG Flags[] = {0,
  1079. ACTRL_ACCESS_ALLOWED,
  1080. ACTRL_ACCESS_DENIED};
  1081. //
  1082. // The most we add is 3 entries at a time... (2 per items, 1 inheritable)
  1083. //
  1084. ACTRL_ACCESS_ENTRYW AccList[3];
  1085. memset(&AccList, 0, sizeof(AccList));
  1086. fInherit = (BOOL)(fFlags & D_INHERIT);
  1087. if(Op == REVOKE)
  1088. {
  1089. BuildTrusteeWithName(&(AccList[cEntries].Trustee),
  1090. pwszTrustee);
  1091. }
  1092. else
  1093. {
  1094. //
  1095. // GroupMembership
  1096. //
  1097. if(_stricmp(pszAccess, D_MEMBERS) == 0)
  1098. {
  1099. //
  1100. // This gets 1 access entry: WriteProp
  1101. //
  1102. AccList[cEntries].lpInheritProperty = ppwszIDs[GROUP_ID];
  1103. AccList[cEntries].Inheritance = INHERIT_ONLY | fInherit ?
  1104. SUB_CONTAINERS_AND_OBJECTS_INHERIT :
  1105. 0;
  1106. BuildTrusteeWithName(&(AccList[cEntries].Trustee),
  1107. pwszTrustee);
  1108. AccList[cEntries].fAccessFlags = Flags[Op];
  1109. AccList[cEntries].Access = ACTRL_DS_WRITE_PROP;
  1110. pwszProperty = ppwszIDs[MEMBER_ID];
  1111. iIndex = MEMBER_ID;
  1112. fprintf(stderr,
  1113. "Sorry... delegation for changing Group membership is "
  1114. "not supported in this alpha release\n");
  1115. dwErr = ERROR_INVALID_PARAMETER;
  1116. }
  1117. //
  1118. // SetPassword
  1119. //
  1120. else if(_stricmp(pszAccess, D_PASSWD) == 0)
  1121. {
  1122. //
  1123. // This gets 1 access entry: WriteProp
  1124. //
  1125. AccList[cEntries].lpInheritProperty = ppwszIDs[USER_ID];
  1126. AccList[cEntries].Inheritance = INHERIT_ONLY | fInherit ?
  1127. SUB_CONTAINERS_AND_OBJECTS_INHERIT :
  1128. 0;
  1129. BuildTrusteeWithName(&(AccList[cEntries].Trustee),
  1130. pwszTrustee);
  1131. AccList[cEntries].fAccessFlags = Flags[Op];
  1132. AccList[cEntries].Access = ACTRL_DS_WRITE_PROP;
  1133. pwszProperty = ppwszIDs[PASSWD_ID];
  1134. iIndex = PASSWD_ID;
  1135. fprintf(stderr,
  1136. "Sorry... delegation for Set Password is "
  1137. "not supported in this alpha release\n");
  1138. dwErr = ERROR_INVALID_PARAMETER;
  1139. }
  1140. //
  1141. // Enable/Disable accounts
  1142. //
  1143. else if(_stricmp(pszAccess, D_ENABLE) == 0)
  1144. {
  1145. //
  1146. // This gets 1 access entry: WriteProp
  1147. //
  1148. AccList[cEntries].lpInheritProperty = ppwszIDs[USER_ID];
  1149. AccList[cEntries].Inheritance = INHERIT_ONLY | fInherit ?
  1150. SUB_CONTAINERS_AND_OBJECTS_INHERIT :
  1151. 0;
  1152. BuildTrusteeWithName(&(AccList[cEntries].Trustee),
  1153. pwszTrustee);
  1154. AccList[cEntries].fAccessFlags = Flags[Op];
  1155. AccList[cEntries].Access = ACTRL_DS_WRITE_PROP;
  1156. pwszProperty = ppwszIDs[ACCTCTRL_ID];
  1157. iIndex = ACCTCTRL_ID;
  1158. fprintf(stderr,
  1159. "Sorry... delegation for Enabling and Disabling accounts "
  1160. " is not supported in this alpha release\n");
  1161. dwErr = ERROR_INVALID_PARAMETER;
  1162. }
  1163. else
  1164. {
  1165. //
  1166. // Some object type...
  1167. //
  1168. if(_stricmp(pszAccess, D_ALL) == 0) // All
  1169. {
  1170. pwszProperty = NULL;
  1171. }
  1172. else if(_stricmp(pszAccess, D_USER) == 0) // User
  1173. {
  1174. pwszProperty = ppwszIDs[USER_ID];
  1175. iIndex = USER_ID;
  1176. fprintf(stderr,
  1177. "Sorry... delegation for user objects is "
  1178. "not supported in this alpha release\n");
  1179. dwErr = ERROR_INVALID_PARAMETER;
  1180. }
  1181. else if(_stricmp(pszAccess, D_GROUP) == 0) // Group
  1182. {
  1183. pwszProperty = ppwszIDs[USER_ID];
  1184. iIndex = GROUP_ID;
  1185. fprintf(stderr,
  1186. "Sorry... delegation for group objects is "
  1187. "not supported in this alpha release\n");
  1188. dwErr = ERROR_INVALID_PARAMETER;
  1189. }
  1190. else if(_stricmp(pszAccess, D_PRINT) == 0) // Printers
  1191. {
  1192. pwszProperty = ppwszIDs[PRINT_ID];
  1193. iIndex = PRINT_ID;
  1194. }
  1195. else if(_stricmp(pszAccess, D_VOL) == 0) // Volumes
  1196. {
  1197. pwszProperty = ppwszIDs[VOLUME_ID];
  1198. iIndex = VOLUME_ID;
  1199. }
  1200. else if(_stricmp(pszAccess, D_OU) == 0) // OUs
  1201. {
  1202. pwszProperty = ppwszIDs[OU_ID];
  1203. iIndex = OU_ID;
  1204. }
  1205. else
  1206. {
  1207. dwErr = ERROR_INVALID_PARAMETER;
  1208. fprintf(stderr,
  1209. "Unexpected delegation permission %s given for "
  1210. "user %ws\n",
  1211. pszAccess,
  1212. pwszTrustee);
  1213. }
  1214. if(dwErr == ERROR_SUCCESS)
  1215. {
  1216. //
  1217. // Add the create/delete for the user
  1218. //
  1219. BuildTrusteeWithName(&(AccList[cEntries].Trustee),
  1220. pwszTrustee);
  1221. AccList[cEntries].fAccessFlags = Flags[Op];
  1222. AccList[cEntries].Access = ACTRL_DS_CREATE_CHILD |
  1223. ACTRL_DS_DELETE_CHILD;
  1224. AccList[cEntries].Inheritance = fInherit ?
  1225. SUB_CONTAINERS_AND_OBJECTS_INHERIT :
  1226. 0;
  1227. //
  1228. // If we are inheriting, make sure we inherit only to the
  1229. // proper property
  1230. //
  1231. if(fInherit == TRUE)
  1232. {
  1233. AccList[cEntries].lpInheritProperty = pwszProperty;
  1234. }
  1235. //
  1236. // Then the inherit on the child object
  1237. //
  1238. cEntries++;
  1239. AccList[cEntries].Inheritance = INHERIT_ONLY |
  1240. (fInherit ?
  1241. SUB_CONTAINERS_AND_OBJECTS_INHERIT :
  1242. 0);
  1243. BuildTrusteeWithName(&(AccList[cEntries].Trustee),
  1244. pwszTrustee);
  1245. AccList[cEntries].fAccessFlags = Flags[Op];
  1246. AccList[cEntries].Access = ACTRL_DS_WRITE_PROP |
  1247. ACTRL_DS_READ_PROP |
  1248. ACTRL_DS_LIST |
  1249. ACTRL_DS_SELF;
  1250. AccList[cEntries].lpInheritProperty = pwszProperty;
  1251. }
  1252. }
  1253. }
  1254. if(dwErr == ERROR_SUCCESS)
  1255. {
  1256. //
  1257. // SetEntriesInAccessList is a NT5 API
  1258. //
  1259. dwErr = SetEntriesInAccessList(cEntries + 1,
  1260. AccList,
  1261. Access[Op],
  1262. pwszProperty,
  1263. pAccessList,
  1264. ppNewAccess);
  1265. //
  1266. // Mark it as protected if we were so asked
  1267. //
  1268. if(dwErr == ERROR_SUCCESS && (fFlags & D_PROTECT) != 0)
  1269. {
  1270. (*ppNewAccess)->pPropertyAccessList[0].fListFlags =
  1271. ACTRL_ACCESS_PROTECTED;
  1272. }
  1273. }
  1274. //
  1275. // Finally, if this was the first entry we were asked to add for this
  1276. // property, we'll have to go get the default security information
  1277. // from the schema, so we can figure out what inherited entries should
  1278. // be on the object, and apply them as object inherit entries for the
  1279. // property
  1280. //
  1281. if(dwErr == ERROR_SUCCESS && iIndex <= MAX_DEF_ACCESS_ID && Op != REVOKE)
  1282. {
  1283. PACTRL_ACCESS pOldAccess = pAccessList;
  1284. //
  1285. // First, find the property in our list of access entries we
  1286. // created above
  1287. //
  1288. for(i = 0; i <= (*ppNewAccess)->cEntries; i++)
  1289. {
  1290. //
  1291. // We'll do this based on property... In this case, the only
  1292. // entries we'll be adding will have a property, so we don't have
  1293. // to protect against that...
  1294. //
  1295. if(pwszProperty != NULL &&
  1296. (*ppNewAccess)->pPropertyAccessList[i].lpProperty != NULL &&
  1297. _wcsicmp((*ppNewAccess)->pPropertyAccessList[i].lpProperty,
  1298. pwszProperty) == 0)
  1299. {
  1300. //
  1301. // If it has more entries that we added, we won't have to
  1302. // worry about it, since the information will already
  1303. // have been added. Note that in this case, we don't have
  1304. // to worry about pAccessEntryList being null, since we know
  1305. // we have added some valid entries.
  1306. //
  1307. if((*ppNewAccess)->pPropertyAccessList[i].
  1308. pAccessEntryList->cEntries ==
  1309. cEntries + 1)
  1310. {
  1311. PACTRL_ACCESS pAddAccess = ppDefObjAccessList[iIndex];
  1312. pAccessList = *ppNewAccess;
  1313. //
  1314. // Ok, we'll have to add them...
  1315. //
  1316. for(j = 0;
  1317. j < (DWORD)(pAddAccess->cEntries) &&
  1318. dwErr == ERROR_SUCCESS;
  1319. j++)
  1320. {
  1321. PACTRL_PROPERTY_ENTRY pPPE =
  1322. &(pAddAccess->pPropertyAccessList[j]);
  1323. dwErr = SetEntriesInAccessList(
  1324. pPPE->pAccessEntryList->cEntries,
  1325. pPPE->pAccessEntryList->pAccessList,
  1326. GRANT_ACCESS,
  1327. pPPE->lpProperty,
  1328. pAccessList,
  1329. ppNewAccess);
  1330. if(dwErr == ERROR_SUCCESS)
  1331. {
  1332. pAccessList = *ppNewAccess;
  1333. }
  1334. }
  1335. }
  1336. //
  1337. // We don't want to run through the loop anymore
  1338. //
  1339. break;
  1340. }
  1341. }
  1342. }
  1343. return(dwErr);
  1344. }
  1345. DWORD
  1346. IsPathOU (
  1347. IN PWSTR pwszOU,
  1348. OUT PBOOL pfIsOU
  1349. )
  1350. /*++
  1351. Routine Description:
  1352. This routine will determine whether the given path is an OU or not.
  1353. Arguments:
  1354. pwszOU - The path into the DS to check on
  1355. ppwszIDs - List of string representations of known IDs
  1356. pfIsOU - Where the results of the test are returned
  1357. Return Value:
  1358. ERROR_SUCCESS - Success
  1359. --*/
  1360. {
  1361. DWORD dwErr = ERROR_SUCCESS;
  1362. PSTR pszOU = NULL;
  1363. HANDLE hDS = NULL;
  1364. PDS_NAME_RESULTA pNameRes;
  1365. dwErr = ConvertStringWToStringA(pwszOU,
  1366. &pszOU);
  1367. if(dwErr == ERROR_SUCCESS)
  1368. {
  1369. dwErr = DsBindA(NULL,
  1370. NULL,
  1371. &hDS);
  1372. }
  1373. if(dwErr == ERROR_SUCCESS)
  1374. {
  1375. dwErr = DsCrackNamesA(hDS,
  1376. DS_NAME_NO_FLAGS,
  1377. DS_UNKNOWN_NAME,
  1378. DS_FQDN_1779_NAME,
  1379. 1,
  1380. &pszOU,
  1381. &pNameRes);
  1382. if(dwErr == ERROR_SUCCESS)
  1383. {
  1384. if(pNameRes->cItems == 0)
  1385. {
  1386. dwErr = ERROR_PATH_NOT_FOUND;
  1387. }
  1388. else
  1389. {
  1390. PSTR pszName = NULL;
  1391. PLDAP pLDAP;
  1392. //
  1393. // Now, we'll bind to the object, and then do the read
  1394. //
  1395. dwErr = LDAPBind(pNameRes->rItems[0].pDomain,
  1396. &pLDAP);
  1397. if(dwErr == ERROR_SUCCESS)
  1398. {
  1399. PSTR *ppszValues;
  1400. DWORD cValues;
  1401. dwErr = LDAPReadAttribute(pszOU,
  1402. "objectclass",
  1403. pLDAP,
  1404. &cValues,
  1405. &ppszValues);
  1406. LDAPUnbind(pLDAP);
  1407. if(dwErr == ERROR_SUCCESS)
  1408. {
  1409. ULONG i;
  1410. *pfIsOU = FALSE;
  1411. for(i = 0; i <cValues; i++)
  1412. {
  1413. if(_stricmp(ppszValues[i],
  1414. "organizationalUnit") == 0)
  1415. {
  1416. *pfIsOU = TRUE;
  1417. break;
  1418. }
  1419. }
  1420. LDAPFreeValues(ppszValues);
  1421. }
  1422. }
  1423. }
  1424. DsFreeNameResultA(pNameRes);
  1425. }
  1426. }
  1427. if (NULL != pszOU)
  1428. {
  1429. LocalFree(pszOU);
  1430. }
  1431. if (NULL != hDS)
  1432. {
  1433. DsUnBindA(hDS);
  1434. }
  1435. return(dwErr);
  1436. }