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.

2072 lines
57 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. dsgroups.cpp
  5. Abstract:
  6. Routines to configure/analyze groups in DS
  7. Author:
  8. Jin Huang (jinhuang) 7-Nov-1996
  9. --*/
  10. #include "headers.h"
  11. #include "serverp.h"
  12. #include <io.h>
  13. #include <lm.h>
  14. #include <lmcons.h>
  15. #include <lmapibuf.h>
  16. #pragma hdrstop
  17. //
  18. // LDAP handle
  19. //
  20. PLDAP Thread pGrpLDAP = NULL;
  21. HANDLE Thread hDS = NULL;
  22. HINSTANCE Thread hNtdsApi = NULL;
  23. #define SCEGRP_MEMBERS 1
  24. #define SCEGRP_MEMBERSHIP 2
  25. #if _WIN32_WINNT>=0x0500
  26. typedef DWORD (WINAPI *PFNDSBIND) (TCHAR *, TCHAR *, HANDLE *);
  27. typedef DWORD (WINAPI *PFNDSUNBIND) (HANDLE *);
  28. typedef DWORD (WINAPI *PFNDSCRACKNAMES) ( HANDLE, DS_NAME_FLAGS, DS_NAME_FORMAT, \
  29. DS_NAME_FORMAT, DWORD, LPTSTR *, PDS_NAME_RESULT *);
  30. typedef void (WINAPI *PFNDSFREENAMERESULT) (DS_NAME_RESULT *);
  31. DWORD
  32. ScepDsConfigGroupMembers(
  33. IN PSCE_OBJECT_LIST pRoots,
  34. IN PWSTR GroupName,
  35. IN OUT DWORD *pStatus,
  36. IN PSCE_NAME_LIST pMembers,
  37. IN PSCE_NAME_LIST pMemberOf,
  38. IN OUT DWORD *nGroupCount
  39. );
  40. DWORD
  41. ScepDsGetDsNameList(
  42. IN PSCE_NAME_LIST pNameList,
  43. OUT PSCE_NAME_LIST *pRealNames
  44. );
  45. DWORD
  46. ScepDsCompareNames(
  47. IN PWSTR *Values,
  48. IN OUT PSCE_NAME_LIST *pAddList,
  49. OUT PSCE_NAME_LIST *pDeleteList OPTIONAL
  50. );
  51. DWORD
  52. ScepDsChangeMembers(
  53. IN ULONG Flag,
  54. IN PWSTR RealGroupName,
  55. IN PSCE_NAME_LIST pAddList OPTIONAL,
  56. IN PSCE_NAME_LIST pDeleteList OPTIONAL
  57. );
  58. DWORD
  59. ScepDsAnalyzeGroupMembers(
  60. IN LSA_HANDLE LsaPolicy,
  61. IN PSCE_OBJECT_LIST pRoots,
  62. IN PWSTR GroupName,
  63. IN PWSTR KeyName,
  64. IN DWORD KeyLen,
  65. IN OUT DWORD *pStatus,
  66. IN PSCE_NAME_LIST pMembers,
  67. IN PSCE_NAME_LIST pMemberOf,
  68. IN OUT DWORD *nGroupCount
  69. );
  70. DWORD
  71. ScepDsMembersDifferent(
  72. IN ULONG Flag,
  73. IN PWSTR *Values,
  74. IN OUT PSCE_NAME_LIST *pNameList,
  75. OUT PSCE_NAME_LIST *pCurrentList,
  76. OUT PBOOL pbDifferent
  77. );
  78. PWSTR
  79. ScepGetLocalAdminsName();
  80. DWORD
  81. ScepDsConvertDsNameList(
  82. IN OUT PSCE_NAME_LIST pDsNameList
  83. );
  84. //
  85. // helpers
  86. //
  87. SCESTATUS
  88. ScepCrackOpen(
  89. OUT HANDLE *phDS
  90. )
  91. {
  92. if ( !phDS ) {
  93. return(SCESTATUS_INVALID_PARAMETER);
  94. }
  95. DWORD Win32rc;
  96. *phDS = NULL;
  97. hNtdsApi = LoadLibrary(TEXT("ntdsapi.dll"));
  98. if ( hNtdsApi == NULL ) {
  99. return (SCESTATUS_MOD_NOT_FOUND);
  100. }
  101. PFNDSBIND pfnDsBind;
  102. PFNDSUNBIND pfnDsUnBind;
  103. #if defined(UNICODE)
  104. pfnDsBind = (PFNDSBIND)GetProcAddress(hNtdsApi, "DsBindW");
  105. pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindW");
  106. #else
  107. pfnDsBind = (PFNDSBIND)GetProcAddress(hNtdsApi, "DsBindA");
  108. pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindA");
  109. #endif
  110. if ( pfnDsBind == NULL || pfnDsUnBind == NULL ) {
  111. return(SCESTATUS_MOD_NOT_FOUND);
  112. }
  113. Win32rc = (*pfnDsBind) (
  114. NULL,
  115. NULL,
  116. phDS);
  117. if ( Win32rc != ERROR_SUCCESS ) {
  118. ScepLogOutput3(3, Win32rc, IDS_ERROR_BIND, L"the GC");
  119. }
  120. return(ScepDosErrorToSceStatus(Win32rc));
  121. }
  122. SCESTATUS
  123. ScepCrackClose(
  124. IN HANDLE *phDS
  125. )
  126. {
  127. if ( hNtdsApi ) {
  128. if ( phDS ) {
  129. PFNDSUNBIND pfnDsUnBind;
  130. #if defined(UNICODE)
  131. pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindW");
  132. #else
  133. pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindA");
  134. #endif
  135. if ( pfnDsUnBind ) {
  136. (*pfnDsUnBind) (phDS);
  137. }
  138. }
  139. FreeLibrary(hNtdsApi);
  140. hNtdsApi = NULL;
  141. }
  142. return(SCESTATUS_SUCCESS);
  143. }
  144. #endif
  145. //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  146. //
  147. // Functions to configure group membership in DS
  148. //
  149. //
  150. //
  151. //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  152. SCESTATUS
  153. ScepConfigDsGroups(
  154. IN OUT PSCE_GROUP_MEMBERSHIP pGroupMembership,
  155. IN DWORD ConfigOptions
  156. )
  157. /* ++
  158. Routine Description:
  159. Configure the ds group membership. The main difference of ds groups
  160. from NT4 groups is that now group can be a member of another group.
  161. Members in the group are configured exactly as the pMembers list in
  162. the restricted group. The group is only validated (added) as a member
  163. of the MemberOf group list. Other existing members in those groups
  164. won't be removed.
  165. The restricted groups are specified in the SCP profile by group name.
  166. It could be a global group, or a alias (no difference in NT5 DS),
  167. but must be defined in the local domain.
  168. Arguments:
  169. pGroupMembership - The restricted group list with members/memberof info to configure
  170. ConfigOptions - options passed in for the configuration
  171. Return value:
  172. SCESTATUS error codes
  173. ++ */
  174. {
  175. #if _WIN32_WINNT<0x0500
  176. return(SCESTATUS_SUCCESS);
  177. #else
  178. if ( pGroupMembership == NULL ) {
  179. ScepPostProgress(TICKS_GROUPS,
  180. AREA_GROUP_MEMBERSHIP,
  181. NULL);
  182. return(SCESTATUS_SUCCESS);
  183. }
  184. SCESTATUS rc;
  185. //
  186. // open the Ldap server, should open two ldap server, one for the local domain
  187. // the other is for the global search (for members, membership)
  188. //
  189. rc = ScepLdapOpen(&pGrpLDAP);
  190. if ( rc == SCESTATUS_SUCCESS ) {
  191. rc = ScepCrackOpen(&hDS);
  192. }
  193. if ( rc == SCESTATUS_SUCCESS ) {
  194. //
  195. // get the root of the domain
  196. //
  197. PSCE_OBJECT_LIST pRoots=NULL;
  198. rc = ScepEnumerateDsObjectRoots(
  199. pGrpLDAP,
  200. &pRoots
  201. );
  202. if ( rc == SCESTATUS_SUCCESS ) {
  203. PSCE_GROUP_MEMBERSHIP pGroup;
  204. DWORD Win32rc;
  205. DWORD rc32=NO_ERROR; // the saved status
  206. BOOL bAdminFound=FALSE;
  207. DWORD nGroupCount=0;
  208. //
  209. // configure each group
  210. //
  211. for ( pGroup=pGroupMembership; pGroup != NULL; pGroup=pGroup->Next ) {
  212. // If members is empty but memberof is not, it is likely
  213. // the admin wanted to configure only memberof. It's too late
  214. // to change the UI to support "unconfigured" members so we'll
  215. // hardcode a special rule here.
  216. if ((NULL == pGroup->pMembers) &&
  217. (NULL != pGroup->pMemberOf))
  218. {
  219. // flag members part as not configured
  220. pGroup->Status |= SCE_GROUP_STATUS_NC_MEMBERS;
  221. }
  222. //
  223. // if both members and memberof are not defined for the group
  224. // we don't need to do anything for the group
  225. //
  226. if ( ( pGroup->Status & SCE_GROUP_STATUS_NC_MEMBERS ) &&
  227. ( pGroup->Status & SCE_GROUP_STATUS_NC_MEMBEROF ) ) {
  228. continue;
  229. }
  230. //
  231. // if within policy propagation and a system shutdown
  232. // is requested, we need to quit as soon as possible
  233. //
  234. if ( (ConfigOptions & SCE_POLICY_TEMPLATE) &&
  235. ScepIsSystemShutDown() ) {
  236. rc = SCESTATUS_SERVICE_NOT_SUPPORT;
  237. break;
  238. }
  239. LPTSTR pTemp = wcschr(pGroup->GroupName, L'\\');
  240. if ( pTemp ) {
  241. //
  242. // there is a domain name, check it with computer name
  243. // to determine if the account is local
  244. //
  245. UNICODE_STRING uName;
  246. uName.Buffer = pGroup->GroupName;
  247. uName.Length = ((USHORT)(pTemp-pGroup->GroupName))*sizeof(TCHAR);
  248. if ( !ScepIsDomainLocal(&uName) ) {
  249. //
  250. // non local groups are not supported for the configuration
  251. //
  252. ScepLogOutput3(1, 0, SCEDLL_NO_MAPPINGS, pGroup->GroupName);
  253. rc = SCESTATUS_INVALID_DATA;
  254. pGroup->Status |= SCE_GROUP_STATUS_DONE_IN_DS;
  255. continue;
  256. }
  257. pTemp++;
  258. } else {
  259. pTemp = pGroup->GroupName;
  260. }
  261. //
  262. // local groups will be handled outside (in SAM)
  263. // find the group (validate) in this domain
  264. //
  265. Win32rc = ScepDsConfigGroupMembers(
  266. pRoots,
  267. pTemp, // pGroup->GroupName,
  268. &(pGroup->Status),
  269. pGroup->pMembers,
  270. pGroup->pMemberOf,
  271. &nGroupCount
  272. );
  273. if ( Win32rc != ERROR_SUCCESS &&
  274. (pGroup->Status & SCE_GROUP_STATUS_DONE_IN_DS) ) {
  275. //
  276. // the group should be handled by the DS function
  277. // but it failed.
  278. //
  279. ScepLogOutput3(1,Win32rc, SCEDLL_SCP_ERROR_CONFIGURE, pGroup->GroupName);
  280. rc32 = Win32rc;
  281. if ( Win32rc == ERROR_FILE_NOT_FOUND ||
  282. Win32rc == ERROR_SHARING_VIOLATION ||
  283. Win32rc == ERROR_ACCESS_DENIED ) {
  284. Win32rc = ERROR_SUCCESS;
  285. } else
  286. break;
  287. }
  288. }
  289. if ( rc32 != NO_ERROR ) {
  290. rc = ScepDosErrorToSceStatus(rc32);
  291. }
  292. //
  293. // free the root DN buffer
  294. //
  295. ScepFreeObjectList(pRoots);
  296. }
  297. }
  298. if ( pGrpLDAP ) {
  299. ScepLdapClose(&pGrpLDAP);
  300. pGrpLDAP = NULL;
  301. }
  302. ScepCrackClose(&hDS);
  303. hDS = NULL;
  304. //
  305. // ticks will be called within ConfigureGroupMembership, so ignore it here
  306. //
  307. return(rc);
  308. #endif
  309. }
  310. #if _WIN32_WINNT>=0x0500
  311. DWORD
  312. ScepDsConfigGroupMembers(
  313. IN PSCE_OBJECT_LIST pRoots,
  314. IN PWSTR GroupName,
  315. IN OUT DWORD *pStatus,
  316. IN PSCE_NAME_LIST pMembers,
  317. IN PSCE_NAME_LIST pMemberOf,
  318. IN OUT DWORD *nGroupCount
  319. )
  320. /*
  321. Description:
  322. Configure group membership (members and Memberof) of a group, specified by
  323. GroupName.
  324. The group membership is configured using ldap based on info stored in DS.
  325. Since foreign wellknown principals may not be present in the Active
  326. Directory, this function cannot configure membership with well known
  327. principals.
  328. Global groups and Universal groups cannot have wellknown pricipals as
  329. members (or memberof) but local groups (such as builtin groups) can. In
  330. order to solve this problem, local groups are configured using the old
  331. SAM apis outside of this function. This function only configures global
  332. and Universal group defined in the local domain. If the group is a
  333. global or universal group, the pStatus parameter will be marked to
  334. indicate the group is processed by this function (SCE_GROUP_STATUS_DONE_IN_DS)
  335. so the old SAM function can skip it.
  336. Arguments:
  337. pRoots - contains the local domain's base DN
  338. GroupName - the group name to configure
  339. pStatus - the status of the group (such as member defined, memberof defined, etc)
  340. pMembers - the list of members to configure
  341. pMemberOf - the list of memberOf to configure
  342. nGroupCount - the count maintained for progress indication only. If the group is
  343. processed in this function, the count will be incremented.
  344. Return:
  345. WIN32 error code.
  346. */
  347. {
  348. if ( GroupName == NULL ) {
  349. return(ERROR_SUCCESS);
  350. }
  351. if ( pRoots == NULL ) {
  352. return(ERROR_INVALID_PARAMETER);
  353. }
  354. DWORD retErr = ERROR_SUCCESS;
  355. DWORD retSave = ERROR_SUCCESS;
  356. //
  357. // search for the group name, if find it, get members and memberof attributes
  358. //
  359. LDAPMessage *Message = NULL;
  360. PWSTR Attribs[4];
  361. Attribs[0] = L"distinguishedName";
  362. Attribs[1] = L"member";
  363. Attribs[2] = L"memberOf";
  364. Attribs[3] = NULL;
  365. WCHAR tmpBuf[128];
  366. //
  367. // define a filter for global or universal group only
  368. //
  369. wcscpy(tmpBuf, L"( &(&(|");
  370. swprintf(tmpBuf+wcslen(L"( &(&(|"), L"(groupType=%d)(groupType=%d))(objectClass=group))(samAccountName=\0",
  371. GROUP_TYPE_ACCOUNT_GROUP | GROUP_TYPE_SECURITY_ENABLED, GROUP_TYPE_UNIVERSAL_GROUP | GROUP_TYPE_SECURITY_ENABLED);
  372. PWSTR Filter;
  373. Filter = (PWSTR)LocalAlloc(LMEM_ZEROINIT,
  374. (wcslen(tmpBuf)+wcslen(GroupName)+4)*sizeof(WCHAR));
  375. if ( Filter == NULL ) {
  376. return(SCESTATUS_NOT_ENOUGH_RESOURCE);
  377. }
  378. swprintf(Filter, L"%s%s) )", tmpBuf, GroupName);
  379. //
  380. // no chased referrel search because the group must be defined locally
  381. // on the domain
  382. //
  383. pGrpLDAP->ld_options = 0;
  384. retErr = ldap_search_s(
  385. pGrpLDAP,
  386. pRoots->Name,
  387. LDAP_SCOPE_SUBTREE,
  388. Filter,
  389. Attribs,
  390. 0,
  391. &Message);
  392. retErr = LdapMapErrorToWin32(retErr);
  393. if(retErr == ERROR_SUCCESS) {
  394. LDAPMessage *Entry = NULL;
  395. //
  396. // find the group, should have only one entry, unless there are duplicate
  397. // groups within the domain, in which case, we only care the first entry anyway
  398. //
  399. Entry = ldap_first_entry(pGrpLDAP, Message);
  400. if(Entry != NULL) {
  401. //
  402. // get the values of requested attributes
  403. // Note, Value pointer returned must be freed
  404. //
  405. PWSTR *Values;
  406. PWSTR RealGroupName;
  407. //
  408. // the DN name
  409. //
  410. Values = ldap_get_values(pGrpLDAP, Entry, Attribs[0]);
  411. if(Values != NULL) {
  412. ScepLogOutput3(1,0, SCEDLL_SCP_CONFIGURE, GroupName);
  413. if ( *nGroupCount < TICKS_GROUPS ) {
  414. ScepPostProgress(1,
  415. AREA_GROUP_MEMBERSHIP,
  416. GroupName);
  417. (*nGroupCount)++;
  418. }
  419. //
  420. // Save the real group name for add/remove members later.
  421. //
  422. RealGroupName = (PWSTR)LocalAlloc(0,(wcslen(Values[0]) + 1)*sizeof(WCHAR));
  423. if ( RealGroupName != NULL ) {
  424. wcscpy(RealGroupName, Values[0]);
  425. ldap_value_free(Values);
  426. ScepLogOutput3(3, 0, SCEDLL_SCP_CONFIGURE, RealGroupName);
  427. PSCE_NAME_LIST pRealNames=NULL;
  428. PSCE_NAME_LIST pDeleteNames=NULL;
  429. //
  430. // translate each name in the pMembers list to real ds names (search)
  431. //
  432. if ( !( *pStatus & SCE_GROUP_STATUS_NC_MEMBERS) ) {
  433. retErr = ScepDsGetDsNameList(pMembers, &pRealNames);
  434. retSave = retErr;
  435. //
  436. // continue to configure group membership even if
  437. // there are some members not resolved
  438. //
  439. // BUT if no member is resolved, do not proceed to remove
  440. // all members
  441. //
  442. if ( retErr == ERROR_SUCCESS ||
  443. (retErr == ERROR_FILE_NOT_FOUND && pRealNames) ) {
  444. //
  445. // get members attribute
  446. //
  447. Values = ldap_get_values(pGrpLDAP, Entry, Attribs[1]);
  448. if ( Values != NULL ) {
  449. //
  450. // process each member
  451. //
  452. retErr = ScepDsCompareNames(Values, &pRealNames, &pDeleteNames);
  453. ldap_value_free(Values);
  454. } else {
  455. //
  456. // it is OK if no members are found
  457. //
  458. ScepLogOutput3(3, 0, SCEDLL_EMPTY_MEMBERSHIP);
  459. retErr = ERROR_SUCCESS;
  460. }
  461. if ( NO_ERROR == retErr ) {
  462. //
  463. // add/remove members of the group
  464. //
  465. retErr = ScepDsChangeMembers(SCEGRP_MEMBERS,
  466. RealGroupName,
  467. pRealNames,
  468. pDeleteNames);
  469. }
  470. if ( ERROR_SUCCESS == retSave ) {
  471. retSave = retErr;
  472. }
  473. }
  474. //
  475. // free buffers
  476. //
  477. ScepFreeNameList(pRealNames);
  478. ScepFreeNameList(pDeleteNames);
  479. pRealNames = NULL;
  480. pDeleteNames = NULL;
  481. }
  482. /*
  483. if ( !( *pStatus & SCE_GROUP_STATUS_NC_MEMBEROF) ) {
  484. //
  485. // memberof is also defined for the group
  486. // crack the memberof list first
  487. //
  488. retErr = ScepDsGetDsNameList(pMemberOf, &pRealNames);
  489. if ( ERROR_SUCCESS == retSave ) {
  490. retSave = retErr;
  491. }
  492. if ( ( ERROR_SUCCESS == retErr ||
  493. ERROR_FILE_NOT_FOUND == retErr ) && pRealNames ) {
  494. //
  495. // get memberof attribute of the group
  496. //
  497. Values = ldap_get_values(pGrpLDAP, Entry, Attribs[2]);
  498. if ( Values != NULL ) {
  499. //
  500. // process each membership
  501. //
  502. retErr = ScepDsCompareNames(Values, &pRealNames, NULL);
  503. ldap_value_free(Values);
  504. } else {
  505. //
  506. // it is OK if no membership is defined
  507. //
  508. ScepLogOutput3(3, 0, SCEDLL_EMPTY_MEMBERSHIP);
  509. retErr = NO_ERROR;
  510. }
  511. if ( retErr == NO_ERROR ) {
  512. //
  513. // add the group to the defined membership
  514. // Note, other existing membership is not removed
  515. //
  516. retErr = ScepDsChangeMembers(SCEGRP_MEMBERSHIP,
  517. RealGroupName,
  518. pRealNames,
  519. NULL);
  520. }
  521. ScepFreeNameList(pRealNames);
  522. pRealNames = NULL;
  523. //
  524. // remember the error
  525. //
  526. if ( ERROR_SUCCESS == retSave ) {
  527. retSave = retErr;
  528. }
  529. }
  530. }
  531. */
  532. LocalFree(RealGroupName);
  533. } else {
  534. ldap_value_free(Values);
  535. retErr = ERROR_NOT_ENOUGH_MEMORY;
  536. }
  537. //
  538. // regardless success or failure, this group has been
  539. // processed by this function. Mark it so that it will
  540. // be skipped by the old SAM API
  541. //
  542. //
  543. // "members" configuration is done in the DS.
  544. // "memberof" configuration is done in the SAM.
  545. // so, mark the status appropriately
  546. //
  547. *pStatus |= SCE_GROUP_STATUS_NC_MEMBERS;
  548. if ( *pStatus & SCE_GROUP_STATUS_NC_MEMBEROF )
  549. *pStatus |= SCE_GROUP_STATUS_DONE_IN_DS;
  550. } else {
  551. //
  552. // Value[0] (group name) can not be empty
  553. //
  554. retErr = LdapMapErrorToWin32(pGrpLDAP->ld_errno);
  555. ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
  556. }
  557. } else {
  558. //
  559. // the group is not found
  560. //
  561. retErr = ERROR_FILE_NOT_FOUND;
  562. ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
  563. }
  564. } else {
  565. //
  566. // error finding the group (with the filter defined)
  567. //
  568. ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, Filter);
  569. }
  570. //
  571. // free Filter
  572. //
  573. if ( Message )
  574. ldap_msgfree(Message);
  575. LocalFree(Filter);
  576. //
  577. // return the error
  578. //
  579. if ( ERROR_SUCCESS == retSave ) {
  580. retSave = retErr;
  581. }
  582. return(retSave);
  583. }
  584. DWORD
  585. ScepDsGetDsNameList(
  586. IN PSCE_NAME_LIST pNameList,
  587. OUT PSCE_NAME_LIST *pRealNames
  588. )
  589. /*
  590. Description:
  591. Translate account names in the list to FQDN format (CN=<account>,DC=<domain>,...).
  592. The output list pRealNames can be filled up even if the function returns
  593. error, to handle valid accounts while there are invalid accounts defined in
  594. the list.
  595. Arguments:
  596. pNameList - the link list for accounts in name format to convert
  597. pRealNames - the output link list for converted FQDN format accounts
  598. Return:
  599. WIN32 error code.
  600. If ERROR_FILE_NOT_FOUND is returned, it means that some accounts in the
  601. input list cannot be cracked.
  602. */
  603. {
  604. if ( pNameList == NULL ) {
  605. return(ERROR_SUCCESS);
  606. }
  607. if ( pRealNames == NULL ) {
  608. return(ERROR_INVALID_PARAMETER);
  609. }
  610. //
  611. // find the procedure address of DsCrackNames and DsFreeNameResult
  612. // ntdsapi.dll is dynamically loaded in ScepCrackOpen
  613. //
  614. PFNDSCRACKNAMES pfnDsCrackNames=NULL;
  615. PFNDSFREENAMERESULT pfnDsFreeNameResult=NULL;
  616. if ( hNtdsApi ) {
  617. #if defined(UNICODE)
  618. pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesW");
  619. pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultW");
  620. #else
  621. pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesA");
  622. pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultA");
  623. #endif
  624. }
  625. //
  626. // the two entry points must exist before continue
  627. //
  628. if ( pfnDsCrackNames == NULL || pfnDsFreeNameResult == NULL ) {
  629. return(ERROR_PROC_NOT_FOUND);
  630. }
  631. DWORD retErr=ERROR_SUCCESS;
  632. DWORD retSave=ERROR_SUCCESS;
  633. PWSTR pTemp;
  634. DS_NAME_RESULT *pDsResult=NULL;
  635. //
  636. // loop through each name in the list to crack
  637. //
  638. for ( PSCE_NAME_LIST pName = pNameList; pName != NULL; pName = pName->Next ) {
  639. //
  640. // Crack the name from NT4 account name to FQDN. Note, hDS is bound to
  641. // the GC in order to crack foreign domain accounts
  642. //
  643. retErr = (*pfnDsCrackNames) (
  644. hDS, // in
  645. DS_NAME_FLAG_TRUST_REFERRAL, // in
  646. DS_NT4_ACCOUNT_NAME,// in
  647. DS_FQDN_1779_NAME, // in
  648. 1, // in
  649. &(pName->Name), // in
  650. &pDsResult); // out
  651. if(retErr == ERROR_SUCCESS && pDsResult &&
  652. pDsResult->cItems > 0 && pDsResult->rItems ) {
  653. if ( pDsResult->rItems[0].pName ) {
  654. //
  655. // find the member
  656. // Save the real group name for add/remove members later.
  657. //
  658. ScepLogOutput3(3,0, SCEDLL_PROCESS, pDsResult->rItems[0].pName);
  659. retErr = ScepAddToNameList(pRealNames, pDsResult->rItems[0].pName, 0);
  660. } else {
  661. //
  662. // this name cannot be cracked.
  663. //
  664. retErr = pDsResult->rItems[0].status;
  665. ScepLogOutput3(1,retErr, SCEDLL_CANNOT_FIND_INDS, pName->Name);
  666. }
  667. } else {
  668. //
  669. // no match is found
  670. //
  671. retErr = ERROR_FILE_NOT_FOUND;
  672. ScepLogOutput3(1,retErr, SCEDLL_CANNOT_FIND_INDS, pName->Name);
  673. }
  674. if ( pDsResult ) {
  675. (*pfnDsFreeNameResult) (pDsResult);
  676. pDsResult = NULL;
  677. }
  678. //
  679. // remember the error to return
  680. //
  681. if ( ERROR_SUCCESS != retErr )
  682. retSave = retErr;
  683. }
  684. return(retSave);
  685. }
  686. DWORD
  687. ScepDsCompareNames(
  688. IN PWSTR *Values,
  689. IN OUT PSCE_NAME_LIST *pAddList,
  690. OUT PSCE_NAME_LIST *pDeleteList OPTIONAL
  691. )
  692. /*
  693. Description:
  694. Arguments:
  695. Values
  696. pAddList
  697. pDeleteList
  698. Return Value:
  699. WIN32 error
  700. */
  701. {
  702. if ( Values == NULL || pAddList == NULL ) {
  703. return(ERROR_INVALID_PARAMETER);
  704. }
  705. //
  706. // count how many existing members (memberof)
  707. //
  708. ULONG ValCount = ldap_count_values(Values);
  709. DWORD rc=NO_ERROR;
  710. PSCE_NAME_LIST pTemp;
  711. //
  712. // loop through each existing value to compare with the ones defined
  713. // for configuration to determine which one should be added and which
  714. // one should be removed from the membership
  715. //
  716. for(ULONG index = 0; index < ValCount; index++) {
  717. if ( Values[index] == NULL ) {
  718. continue;
  719. }
  720. pTemp = *pAddList;
  721. PSCE_NAME_LIST pParent = NULL, pTemp2;
  722. BOOL bFound=FALSE;
  723. while (pTemp != NULL ) {
  724. if ( _wcsicmp(Values[index], pTemp->Name) == 0 ) {
  725. //
  726. // find this member in both place, no need to add or remove
  727. // from the membership so take this one out of the list
  728. //
  729. if ( pParent == NULL ) {
  730. *pAddList = pTemp->Next;
  731. } else
  732. pParent->Next = pTemp->Next;
  733. pTemp2 = pTemp;
  734. pTemp = pTemp->Next;
  735. pTemp2->Next = NULL;
  736. ScepFreeNameList(pTemp2);
  737. bFound=TRUE;
  738. break;
  739. } else {
  740. //
  741. // move to the next one
  742. //
  743. pParent = pTemp;
  744. pTemp = pTemp->Next;
  745. }
  746. }
  747. if ( !bFound && pDeleteList != NULL ) {
  748. //
  749. // did not find in the real name list, should be deleted
  750. // if the remove buffer is passed in
  751. //
  752. rc = ScepAddToNameList(pDeleteList, Values[index], 0);
  753. if ( rc != ERROR_SUCCESS ) {
  754. ScepLogOutput3(1,rc, SCEDLL_SCP_ERROR_ADD, Values[index]);
  755. }
  756. }
  757. if ( rc != NO_ERROR ) {
  758. //
  759. // pDeleteList will be freed outside
  760. //
  761. break;
  762. }
  763. }
  764. return(rc);
  765. }
  766. DWORD
  767. ScepDsChangeMembers(
  768. IN ULONG Flag,
  769. IN PWSTR RealGroupName,
  770. IN PSCE_NAME_LIST pAddList OPTIONAL,
  771. IN PSCE_NAME_LIST pDeleteList OPTIONAL
  772. )
  773. {
  774. if ( RealGroupName == NULL ) {
  775. return(ERROR_INVALID_PARAMETER);
  776. }
  777. if ( pAddList == NULL && pDeleteList == NULL ) {
  778. //
  779. // nothing to do
  780. //
  781. return(ERROR_SUCCESS);
  782. }
  783. PLDAP pSrhLDAP = NULL;
  784. SCESTATUS rc = ScepLdapOpen(&pSrhLDAP);
  785. if ( rc != SCESTATUS_SUCCESS ) {
  786. return(ScepSceStatusToDosError(rc));
  787. }
  788. PLDAPMod rgMods[2];
  789. LDAPMod Mod;
  790. DWORD retErr=NO_ERROR;
  791. DWORD retSave=NO_ERROR;
  792. PWSTR rgpszVals[2];
  793. PSCE_NAME_LIST pName;
  794. rgMods[0] = &Mod;
  795. rgMods[1] = NULL;
  796. rgpszVals[1] = NULL;
  797. //
  798. // need to do one at a time because individual members/memberof may fail
  799. //
  800. Mod.mod_op = LDAP_MOD_ADD;
  801. Mod.mod_values = rgpszVals;
  802. if ( Flag == SCEGRP_MEMBERS )
  803. Mod.mod_type = L"member";
  804. else
  805. Mod.mod_type = L"memberOf";
  806. for ( pName=pAddList; pName != NULL; pName = pName->Next ) {
  807. ScepLogOutput3(2,0, SCEDLL_SCP_ADD, pName->Name);
  808. rgpszVals[0] = pName->Name;
  809. //
  810. // Now, we'll do the write for members...
  811. //
  812. retErr = ldap_modify_s(pSrhLDAP,
  813. RealGroupName,
  814. rgMods
  815. );
  816. retErr = LdapMapErrorToWin32(retErr);
  817. //
  818. // if the same member already exist, do not consider it as an error
  819. //
  820. if ( retErr == ERROR_ALREADY_EXISTS )
  821. retErr = ERROR_SUCCESS;
  822. if ( retErr != ERROR_SUCCESS ) {
  823. ScepLogOutput3(1,retErr, SCEDLL_SCP_ERROR_ADDTO, RealGroupName);
  824. retSave = retErr;
  825. }
  826. }
  827. if ( Flag == SCEGRP_MEMBERS && pDeleteList ) {
  828. //
  829. // remove existing members. Note, memberof won't be removed
  830. //
  831. if ( NO_ERROR == retSave ) {
  832. //
  833. // only remove existing members if all members are added successfully
  834. //
  835. Mod.mod_op = LDAP_MOD_DELETE;
  836. Mod.mod_type = L"member";
  837. Mod.mod_values = rgpszVals;
  838. for ( pName=pDeleteList; pName != NULL; pName = pName->Next ) {
  839. ScepLogOutput3(2,0, SCEDLL_SCP_REMOVE, pName->Name);
  840. rgpszVals[0] = pName->Name;
  841. //
  842. // Now, we'll do the write for members...
  843. //
  844. retErr = ldap_modify_s(pSrhLDAP,
  845. RealGroupName,
  846. rgMods
  847. );
  848. retErr = LdapMapErrorToWin32(retErr);
  849. //
  850. // if the member doesn't exist in the group, ignore
  851. //
  852. if ( retErr == ERROR_FILE_NOT_FOUND ) {
  853. retErr = ERROR_SUCCESS;
  854. }
  855. if ( retErr != ERROR_SUCCESS) {
  856. ScepLogOutput3(1,retErr, SCEDLL_SCP_ERROR_REMOVE, RealGroupName);
  857. retSave = retErr;
  858. }
  859. }
  860. } else {
  861. //
  862. // something is wrong when adding new members
  863. // so existing members won't be removed
  864. //
  865. ScepLogOutput3(1,retSave, SCEDLL_SCP_ERROR_NOREMOVE);
  866. }
  867. }
  868. if ( pSrhLDAP ) {
  869. ScepLdapClose(&pSrhLDAP);
  870. pSrhLDAP = NULL;
  871. }
  872. return(retSave);
  873. }
  874. #endif
  875. //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  876. //
  877. // Functions to analyze group membership in DS
  878. //
  879. //
  880. //
  881. //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  882. SCESTATUS
  883. ScepAnalyzeDsGroups(
  884. IN PSCE_GROUP_MEMBERSHIP pGroupMembership
  885. )
  886. /* ++
  887. Routine Description:
  888. Analyze the ds group membership. The main difference of ds groups
  889. from NT4 groups is that now group can be a member of another group.
  890. Members in the group are configured exactly as the pMembers list in
  891. the restricted group. The group is only validated (added) as a member
  892. of the MemberOf group list. Other existing members in those groups
  893. won't be removed.
  894. The restricted groups are specified in the SCP profile by group name.
  895. It could be a global group, or a alias (no difference in NT5 DS),
  896. but must be defined in the local domain.
  897. Arguments:
  898. pGroupMembership - The restricted group list with members/memberof info to configure
  899. Return value:
  900. SCESTATUS error codes
  901. ++ */
  902. {
  903. #if _WIN32_WINNT<0x0500
  904. return(SCESTATUS_SUCCESS);
  905. #else
  906. if ( pGroupMembership == NULL ) {
  907. ScepPostProgress(TICKS_GROUPS,
  908. AREA_GROUP_MEMBERSHIP,
  909. NULL);
  910. return(SCESTATUS_SUCCESS);
  911. }
  912. SCESTATUS rc;
  913. DWORD nGroupCount=0;
  914. PSCE_GROUP_MEMBERSHIP pGroup=pGroupMembership;
  915. PWSTR KeyName=NULL;
  916. DWORD GroupLen;
  917. //
  918. // open local policy
  919. //
  920. LSA_HANDLE PolicyHandle=NULL;
  921. rc = RtlNtStatusToDosError(
  922. ScepOpenLsaPolicy(
  923. POLICY_LOOKUP_NAMES,
  924. &PolicyHandle,
  925. TRUE
  926. ));
  927. if (ERROR_SUCCESS != rc ) {
  928. ScepLogOutput3(1, rc, SCEDLL_LSA_POLICY);
  929. return(ScepDosErrorToSceStatus(rc));
  930. }
  931. //
  932. // open the Ldap server, should open two ldap server, one for the local domain only
  933. // the other is for the global search (for members, membership)
  934. //
  935. rc = ScepLdapOpen(&pGrpLDAP);
  936. if ( rc == SCESTATUS_SUCCESS ) {
  937. ScepCrackOpen(&hDS);
  938. }
  939. if ( rc == SCESTATUS_SUCCESS ) {
  940. //
  941. // get the root of the domain
  942. //
  943. PSCE_OBJECT_LIST pRoots=NULL;
  944. rc = ScepEnumerateDsObjectRoots(
  945. pGrpLDAP,
  946. &pRoots
  947. );
  948. if ( rc == SCESTATUS_SUCCESS ) {
  949. //
  950. // configure each group
  951. //
  952. DWORD Win32rc;
  953. DWORD rc32=NO_ERROR; // saved status
  954. BOOL bAdminFound=FALSE;
  955. //
  956. // get the local administratos group name
  957. //
  958. for ( pGroup=pGroupMembership; pGroup != NULL; pGroup=pGroup->Next ) {
  959. if ( KeyName ) {
  960. LocalFree(KeyName);
  961. KeyName = NULL;
  962. }
  963. LPTSTR pTemp = wcschr(pGroup->GroupName, L'\\');
  964. if ( pTemp ) {
  965. //
  966. // there is a domain name, check it with computer name
  967. //
  968. UNICODE_STRING uName;
  969. uName.Buffer = pGroup->GroupName;
  970. uName.Length = ((USHORT)(pTemp-pGroup->GroupName))*sizeof(TCHAR);
  971. if ( !ScepIsDomainLocal(&uName) ) {
  972. ScepLogOutput3(1, 0, SCEDLL_NO_MAPPINGS, pGroup->GroupName);
  973. rc = SCESTATUS_INVALID_DATA;
  974. ScepRaiseErrorString(
  975. NULL,
  976. KeyName ? KeyName : pGroup->GroupName,
  977. szMembers
  978. );
  979. pGroup->Status |= SCE_GROUP_STATUS_DONE_IN_DS;
  980. continue;
  981. }
  982. ScepConvertNameToSidString(
  983. PolicyHandle,
  984. pGroup->GroupName,
  985. FALSE,
  986. &KeyName,
  987. &GroupLen
  988. );
  989. if(NULL == KeyName) // failed to map to SID, we'll use GroupName
  990. {
  991. GroupLen = wcslen(pGroup->GroupName);
  992. }
  993. pTemp++;
  994. } else {
  995. pTemp = pGroup->GroupName;
  996. GroupLen = wcslen(pTemp);
  997. }
  998. //
  999. // find the group (validate) in this domain
  1000. //
  1001. Win32rc = ScepDsAnalyzeGroupMembers(
  1002. PolicyHandle,
  1003. pRoots,
  1004. pTemp, // pGroup->GroupName,
  1005. KeyName ? KeyName : pGroup->GroupName,
  1006. GroupLen,
  1007. &(pGroup->Status),
  1008. pGroup->pMembers,
  1009. pGroup->pMemberOf,
  1010. &nGroupCount
  1011. );
  1012. if ( (Win32rc != ERROR_SUCCESS) &&
  1013. (pGroup->Status & SCE_GROUP_STATUS_DONE_IN_DS) ) {
  1014. ScepLogOutput3(1, Win32rc, SCEDLL_SAP_ERROR_ANALYZE, pGroup->GroupName);
  1015. rc32 = Win32rc;
  1016. if ( Win32rc == ERROR_FILE_NOT_FOUND ||
  1017. Win32rc == ERROR_SHARING_VIOLATION ||
  1018. Win32rc == ERROR_ACCESS_DENIED ) {
  1019. ScepRaiseErrorString(
  1020. NULL,
  1021. KeyName ? KeyName : pGroup->GroupName,
  1022. szMembers
  1023. );
  1024. Win32rc = ERROR_SUCCESS;
  1025. } else
  1026. break;
  1027. }
  1028. }
  1029. if ( rc32 != NO_ERROR ) {
  1030. rc = ScepDosErrorToSceStatus(rc32);
  1031. }
  1032. //
  1033. // free pRoots
  1034. //
  1035. ScepFreeObjectList(pRoots);
  1036. }
  1037. }
  1038. if ( KeyName ) {
  1039. LocalFree(KeyName);
  1040. }
  1041. if ( pGrpLDAP ) {
  1042. ScepLdapClose(&pGrpLDAP);
  1043. pGrpLDAP = NULL;
  1044. }
  1045. ScepCrackClose(&hDS);
  1046. hDS = NULL;
  1047. /*
  1048. // this will be handled in the analysis into SAM
  1049. //
  1050. // raise groups that are errored
  1051. //
  1052. for ( PSCE_GROUP_MEMBERSHIP pTmpGrp=pGroup;
  1053. pTmpGrp != NULL; pTmpGrp = pTmpGrp->Next ) {
  1054. if ( pTmpGrp->GroupName == NULL )
  1055. continue;
  1056. if ( wcschr(pGroup->GroupName, L'\\') ) {
  1057. ScepConvertNameToSidString(
  1058. PolicyHandle,
  1059. pGroup->GroupName,
  1060. FALSE,
  1061. &KeyName,
  1062. &GroupLen
  1063. );
  1064. }
  1065. ScepRaiseErrorString(
  1066. NULL,
  1067. KeyName ? KeyName : pTmpGrp->GroupName,
  1068. szMembers
  1069. );
  1070. if ( KeyName ) {
  1071. LocalFree(KeyName);
  1072. KeyName = NULL;
  1073. }
  1074. }
  1075. if ( rc != SCESTATUS_SERVICE_NOT_SUPPORT &&
  1076. nGroupCount < TICKS_GROUPS ) {
  1077. ScepPostProgress(TICKS_GROUPS-nGroupCount,
  1078. AREA_GROUP_MEMBERSHIP,
  1079. NULL);
  1080. }
  1081. */
  1082. if ( PolicyHandle ) {
  1083. LsaClose(PolicyHandle);
  1084. }
  1085. return(rc);
  1086. #endif
  1087. }
  1088. #if _WIN32_WINNT>=0x0500
  1089. DWORD
  1090. ScepDsAnalyzeGroupMembers(
  1091. IN LSA_HANDLE LsaPolicy,
  1092. IN PSCE_OBJECT_LIST pRoots,
  1093. IN PWSTR GroupName,
  1094. IN PWSTR KeyName,
  1095. IN DWORD KeyLen,
  1096. IN OUT DWORD *pStatus,
  1097. IN PSCE_NAME_LIST pMembers,
  1098. IN PSCE_NAME_LIST pMemberOf,
  1099. IN DWORD *nGroupCount
  1100. )
  1101. {
  1102. if ( GroupName == NULL ) {
  1103. return(ERROR_SUCCESS);
  1104. }
  1105. if ( pRoots == NULL ) {
  1106. return(ERROR_INVALID_PARAMETER);
  1107. }
  1108. DWORD retErr=ERROR_SUCCESS;
  1109. //
  1110. // search for the name, if find it, get members and memberof
  1111. //
  1112. LDAPMessage *Message = NULL;
  1113. PWSTR Attribs[4];
  1114. Attribs[0] = L"distinguishedName";
  1115. Attribs[1] = L"member";
  1116. Attribs[2] = L"memberOf";
  1117. Attribs[3] = NULL;
  1118. WCHAR tmpBuf[128];
  1119. // wcscpy(tmpBuf, L"( &(|(objectClass=localGroup)(objectClass=group))(cn=");
  1120. // wcscpy(tmpBuf, L"( &(|(objectClass=localGroup)(objectClass=group))(samAccountName=");
  1121. wcscpy(tmpBuf, L"( &(&(|");
  1122. swprintf(tmpBuf+wcslen(L"( &(&(|"), L"(groupType=%d)(groupType=%d))(objectClass=group))(samAccountName=\0",
  1123. GROUP_TYPE_ACCOUNT_GROUP | GROUP_TYPE_SECURITY_ENABLED, GROUP_TYPE_UNIVERSAL_GROUP | GROUP_TYPE_SECURITY_ENABLED);
  1124. PWSTR Filter;
  1125. DWORD Len=wcslen(GroupName);
  1126. Filter = (PWSTR)LocalAlloc(LMEM_ZEROINIT, (wcslen(tmpBuf)+Len+4)*sizeof(WCHAR));
  1127. if ( Filter == NULL ) {
  1128. return(SCESTATUS_NOT_ENOUGH_RESOURCE);
  1129. }
  1130. swprintf(Filter, L"%s%s) )", tmpBuf, GroupName);
  1131. pGrpLDAP->ld_options = 0; // no chased referrel
  1132. retErr = ldap_search_s(
  1133. pGrpLDAP,
  1134. pRoots->Name,
  1135. LDAP_SCOPE_SUBTREE,
  1136. Filter,
  1137. Attribs,
  1138. 0,
  1139. &Message);
  1140. retErr = LdapMapErrorToWin32(retErr);
  1141. if(retErr == ERROR_SUCCESS) {
  1142. //
  1143. // find the group
  1144. //
  1145. LDAPMessage *Entry = NULL;
  1146. //
  1147. // should only have one entry, unless there are duplicate groups
  1148. // within the domain, in which case, we only care the first entry anyway
  1149. //
  1150. //
  1151. // get the first one.
  1152. //
  1153. Entry = ldap_first_entry(pGrpLDAP, Message);
  1154. if(Entry != NULL) {
  1155. PWSTR *Values;
  1156. Values = ldap_get_values(pGrpLDAP, Entry, Attribs[0]);
  1157. if(Values != NULL) {
  1158. ScepLogOutput3(1,0, SCEDLL_SAP_ANALYZE, GroupName);
  1159. if ( *nGroupCount < TICKS_GROUPS ) {
  1160. ScepPostProgress(1,
  1161. AREA_GROUP_MEMBERSHIP,
  1162. GroupName);
  1163. (*nGroupCount)++;
  1164. }
  1165. ScepLogOutput2(3,0, L"\t\t%s", Values[0]);
  1166. ldap_value_free(Values);
  1167. PSCE_NAME_LIST pRealNames=NULL;
  1168. PSCE_NAME_LIST pCurrentList=NULL;
  1169. BOOL bDifferent;
  1170. DWORD retErr2, rc;
  1171. //
  1172. // translate each name in the pMembers list to real ds names (search)
  1173. //
  1174. retErr = ScepDsGetDsNameList(pMembers, &pRealNames);
  1175. if ( ERROR_SUCCESS == retErr ||
  1176. ERROR_FILE_NOT_FOUND == retErr ) {
  1177. //
  1178. // analyze members
  1179. //
  1180. Values = ldap_get_values(pGrpLDAP, Entry, Attribs[1]);
  1181. rc = ScepDsMembersDifferent(SCEGRP_MEMBERS,
  1182. Values,
  1183. &pRealNames,
  1184. &pCurrentList,
  1185. &bDifferent);
  1186. if ( Values != NULL )
  1187. ldap_value_free(Values);
  1188. //
  1189. // if there are some names unresolvable, this should be
  1190. // treated as mismatch
  1191. //
  1192. if ( ERROR_FILE_NOT_FOUND == retErr )
  1193. bDifferent = TRUE;
  1194. retErr = rc;
  1195. if ( ( ERROR_SUCCESS == retErr ) &&
  1196. ( bDifferent ||
  1197. (*pStatus & SCE_GROUP_STATUS_NC_MEMBERS) ) ) {
  1198. //
  1199. // save to the database
  1200. //
  1201. retErr = ScepDsConvertDsNameList(pCurrentList);
  1202. if ( retErr == NO_ERROR ) {
  1203. retErr = ScepSaveMemberMembershipList(
  1204. LsaPolicy,
  1205. szMembers,
  1206. KeyName,
  1207. KeyLen,
  1208. pCurrentList,
  1209. (*pStatus & SCE_GROUP_STATUS_NC_MEMBERS) ? 2: 1);
  1210. }
  1211. if ( retErr != ERROR_SUCCESS ) {
  1212. ScepLogOutput3(1,retErr, SCEDLL_SAP_ERROR_SAVE, GroupName);
  1213. }
  1214. }
  1215. ScepFreeNameList(pCurrentList);
  1216. pCurrentList = NULL;
  1217. ScepFreeNameList(pRealNames);
  1218. pRealNames = NULL;
  1219. }
  1220. retErr2 = ScepDsGetDsNameList(pMemberOf, &pRealNames);
  1221. if ( ( ERROR_SUCCESS == retErr2 ||
  1222. ERROR_FILE_NOT_FOUND == retErr2 ) && pRealNames ) {
  1223. //
  1224. // analyze membership
  1225. //
  1226. Values = ldap_get_values(pGrpLDAP, Entry, Attribs[2]);
  1227. rc = ScepDsMembersDifferent(SCEGRP_MEMBERSHIP,
  1228. Values,
  1229. &pRealNames,
  1230. &pCurrentList,
  1231. &bDifferent);
  1232. if ( Values != NULL )
  1233. ldap_value_free(Values);
  1234. //
  1235. // if there are some names unresolvable, this should be
  1236. // treated as mismatch
  1237. //
  1238. if ( ERROR_FILE_NOT_FOUND == retErr )
  1239. bDifferent = TRUE;
  1240. retErr2 = rc;
  1241. if ( (retErr2 == NO_ERROR) &&
  1242. ( bDifferent ||
  1243. (*pStatus & SCE_GROUP_STATUS_NC_MEMBEROF) ) ) {
  1244. //
  1245. // save to the database
  1246. //
  1247. retErr2 = ScepDsConvertDsNameList(pCurrentList);
  1248. if ( retErr2 == NO_ERROR ) {
  1249. retErr2 = ScepSaveMemberMembershipList(
  1250. LsaPolicy,
  1251. szMemberof,
  1252. KeyName,
  1253. KeyLen,
  1254. pCurrentList,
  1255. (*pStatus & SCE_GROUP_STATUS_NC_MEMBEROF) ? 2 : 1);
  1256. }
  1257. if ( retErr2 != ERROR_SUCCESS ) {
  1258. ScepLogOutput3(1,retErr2, SCEDLL_SAP_ERROR_SAVE, GroupName);
  1259. }
  1260. }
  1261. ScepFreeNameList(pCurrentList);
  1262. pCurrentList = NULL;
  1263. ScepFreeNameList(pRealNames);
  1264. pRealNames = NULL;
  1265. }
  1266. *pStatus |= SCE_GROUP_STATUS_DONE_IN_DS;
  1267. //
  1268. // remember the error
  1269. //
  1270. if ( retErr == NO_ERROR ) {
  1271. retErr = retErr2;
  1272. }
  1273. } else {
  1274. //
  1275. // Value[0] (group name) may not be empty
  1276. //
  1277. retErr = LdapMapErrorToWin32(pGrpLDAP->ld_errno);
  1278. ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
  1279. }
  1280. } else {
  1281. retErr = ERROR_FILE_NOT_FOUND; // the group is not found
  1282. ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
  1283. }
  1284. } else {
  1285. ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, Filter);
  1286. }
  1287. if ( Message )
  1288. ldap_msgfree(Message);
  1289. //
  1290. // free Filter
  1291. //
  1292. LocalFree(Filter);
  1293. return(retErr);
  1294. }
  1295. DWORD
  1296. ScepDsMembersDifferent(
  1297. IN ULONG Flag,
  1298. IN PWSTR *Values,
  1299. IN OUT PSCE_NAME_LIST *pNameList,
  1300. OUT PSCE_NAME_LIST *pCurrentList,
  1301. OUT PBOOL pbDifferent
  1302. )
  1303. {
  1304. if ( pCurrentList == NULL || pbDifferent == NULL ) {
  1305. return(ERROR_INVALID_PARAMETER);
  1306. }
  1307. if ( Values == NULL ) {
  1308. if ( pNameList == NULL || *pNameList == NULL )
  1309. *pbDifferent = FALSE;
  1310. else
  1311. *pbDifferent = TRUE;
  1312. return(ERROR_SUCCESS);
  1313. }
  1314. ULONG ValCount = ldap_count_values(Values);
  1315. DWORD rc=NO_ERROR;
  1316. *pbDifferent = FALSE;
  1317. for(ULONG index = 0; index < ValCount; index++) {
  1318. if ( Values[index] == NULL ) {
  1319. continue;
  1320. }
  1321. if ( !(*pbDifferent) ) {
  1322. PSCE_NAME_LIST pTemp = *pNameList, pTemp2;
  1323. PSCE_NAME_LIST pParent = NULL;
  1324. INT i;
  1325. while ( pTemp != NULL ) {
  1326. if ( (i = _wcsicmp(Values[index], pTemp->Name)) == 0 ) {
  1327. //
  1328. // find this member
  1329. //
  1330. if ( pParent == NULL ) {
  1331. *pNameList = pTemp->Next;
  1332. } else
  1333. pParent->Next = pTemp->Next;
  1334. pTemp2 = pTemp;
  1335. pTemp = pTemp->Next;
  1336. pTemp2->Next = NULL;
  1337. ScepFreeNameList(pTemp2);
  1338. break;
  1339. } else {
  1340. pParent = pTemp;
  1341. pTemp = pTemp->Next;
  1342. }
  1343. }
  1344. if ( pTemp == NULL && i != 0 )
  1345. *pbDifferent = TRUE;
  1346. }
  1347. //
  1348. // build the current list
  1349. //
  1350. rc = ScepAddToNameList(pCurrentList, Values[index], 0);
  1351. if ( rc != NO_ERROR ) {
  1352. ScepLogOutput3(1,rc, SCEDLL_SCP_ERROR_ADD, Values[index]);
  1353. break;
  1354. }
  1355. }
  1356. if ( rc == NO_ERROR && Flag == SCEGRP_MEMBERS &&
  1357. *pbDifferent == FALSE ) {
  1358. //
  1359. // still same so far, only continue to compare for members
  1360. // because membership is not one to one configuring
  1361. //
  1362. if ( *pNameList != NULL )
  1363. *pbDifferent = TRUE;
  1364. } // pCurrentList will be freed outside
  1365. return(rc);
  1366. }
  1367. PWSTR
  1368. ScepGetLocalAdminsName()
  1369. {
  1370. NTSTATUS NtStatus;
  1371. SAM_HANDLE AccountDomain=NULL;
  1372. SAM_HANDLE AliasHandle=NULL;
  1373. SAM_HANDLE ServerHandle=NULL;
  1374. PSID DomainSid=NULL;
  1375. SAM_HANDLE theBuiltinHandle=NULL;
  1376. PSID theBuiltinSid=NULL;
  1377. ALIAS_NAME_INFORMATION *BufName=NULL;
  1378. PWSTR pAdminsName=NULL;
  1379. //
  1380. // open the sam account domain
  1381. //
  1382. NtStatus = ScepOpenSamDomain(
  1383. SAM_SERVER_ALL_ACCESS,
  1384. MAXIMUM_ALLOWED,
  1385. &ServerHandle,
  1386. &AccountDomain,
  1387. &DomainSid,
  1388. &theBuiltinHandle,
  1389. &theBuiltinSid
  1390. );
  1391. if ( !NT_SUCCESS(NtStatus) ) {
  1392. ScepLogOutput3(1, RtlNtStatusToDosError(NtStatus),
  1393. SCEDLL_ERROR_OPEN, L"SAM");
  1394. return(NULL);
  1395. }
  1396. NtStatus = SamOpenAlias(
  1397. theBuiltinHandle,
  1398. MAXIMUM_ALLOWED,
  1399. DOMAIN_ALIAS_RID_ADMINS,
  1400. &AliasHandle
  1401. );
  1402. if ( NT_SUCCESS( NtStatus ) ) {
  1403. NtStatus = SamQueryInformationAlias(
  1404. AliasHandle,
  1405. AliasNameInformation,
  1406. (PVOID *)&BufName
  1407. );
  1408. if ( NT_SUCCESS( NtStatus ) && BufName &&
  1409. BufName->Name.Length > 0 && BufName->Name.Buffer ) {
  1410. //
  1411. // allocate buffer to return
  1412. //
  1413. pAdminsName = (PWSTR)ScepAlloc(0, BufName->Name.Length+2);
  1414. if ( pAdminsName ) {
  1415. wcsncpy(pAdminsName, BufName->Name.Buffer,
  1416. BufName->Name.Length/2);
  1417. pAdminsName[BufName->Name.Length/2] = L'\0';
  1418. } else {
  1419. NtStatus = STATUS_NO_MEMORY;
  1420. }
  1421. }
  1422. if ( BufName ) {
  1423. SamFreeMemory(BufName);
  1424. BufName = NULL;
  1425. }
  1426. //
  1427. // close the user handle
  1428. //
  1429. SamCloseHandle(AliasHandle);
  1430. }
  1431. SamCloseHandle(AccountDomain);
  1432. SamCloseHandle( ServerHandle );
  1433. if ( DomainSid != NULL )
  1434. SamFreeMemory(DomainSid);
  1435. SamCloseHandle( theBuiltinHandle );
  1436. if ( theBuiltinSid != NULL )
  1437. SamFreeMemory(theBuiltinSid);
  1438. return pAdminsName;
  1439. }
  1440. DWORD
  1441. ScepDsConvertDsNameList(
  1442. IN OUT PSCE_NAME_LIST pDsNameList
  1443. )
  1444. /*
  1445. Routine:
  1446. The input list is in the LDAP format (CN=<>,...DC=<>, ...). When the routine
  1447. returns, the list will be in NT4 account name format (domain\account)
  1448. */
  1449. {
  1450. if ( pDsNameList == NULL ) {
  1451. return(ERROR_SUCCESS);
  1452. }
  1453. PFNDSCRACKNAMES pfnDsCrackNames=NULL;
  1454. PFNDSFREENAMERESULT pfnDsFreeNameResult=NULL;
  1455. if ( hNtdsApi ) {
  1456. #if defined(UNICODE)
  1457. pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesW");
  1458. pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultW");
  1459. #else
  1460. pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesA");
  1461. pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultA");
  1462. #endif
  1463. }
  1464. if ( pfnDsCrackNames == NULL || pfnDsFreeNameResult == NULL ) {
  1465. return(ERROR_PROC_NOT_FOUND);
  1466. }
  1467. DWORD retErr=ERROR_SUCCESS;
  1468. PWSTR pTemp;
  1469. DS_NAME_RESULT *pDsResult=NULL;
  1470. DWORD DomLen;
  1471. DWORD SidLen;
  1472. CHAR SidBuf[MAX_PATH];
  1473. PWSTR RefDom[MAX_PATH];
  1474. SID_NAME_USE SidUse;
  1475. for ( PSCE_NAME_LIST pName = pDsNameList; pName != NULL; pName = pName->Next ) {
  1476. if ( pName->Name == NULL ) {
  1477. continue;
  1478. }
  1479. retErr = (*pfnDsCrackNames) (
  1480. hDS, // in
  1481. DS_NAME_NO_FLAGS, // in
  1482. DS_FQDN_1779_NAME, // in
  1483. DS_NT4_ACCOUNT_NAME,// in
  1484. 1, // in
  1485. &(pName->Name), // in
  1486. &pDsResult); // out
  1487. if(retErr == ERROR_SUCCESS && pDsResult && pDsResult->rItems &&
  1488. pDsResult->rItems[0].pName ) {
  1489. //
  1490. // NT4 account name format is returned, should check if the
  1491. // domain is not a acccount domain
  1492. //
  1493. pTemp = wcschr(pDsResult->rItems[0].pName, L'\\');
  1494. if ( pTemp ) {
  1495. DomLen=MAX_PATH;
  1496. SidLen=MAX_PATH;
  1497. if ( LookupAccountName(
  1498. NULL,
  1499. pDsResult->rItems[0].pName,
  1500. (PSID)SidBuf,
  1501. &SidLen,
  1502. (PWSTR)RefDom,
  1503. &DomLen,
  1504. &SidUse
  1505. ) ) {
  1506. if ( !ScepIsSidFromAccountDomain( (PSID)SidBuf) ) {
  1507. //
  1508. // add name only
  1509. //
  1510. pTemp++;
  1511. } else {
  1512. pTemp = pDsResult->rItems[0].pName;
  1513. }
  1514. } else {
  1515. pTemp = pDsResult->rItems[0].pName;
  1516. }
  1517. } else {
  1518. pTemp = pDsResult->rItems[0].pName;
  1519. }
  1520. PWSTR pNewName = (PWSTR)ScepAlloc(0, (wcslen(pTemp)+1)*sizeof(WCHAR));
  1521. if ( pNewName ) {
  1522. wcscpy(pNewName, pTemp);
  1523. ScepFree(pName->Name);
  1524. pName->Name = pNewName;
  1525. } else {
  1526. retErr = ERROR_NOT_ENOUGH_MEMORY;
  1527. }
  1528. } else {
  1529. // no match is found
  1530. retErr = ERROR_FILE_NOT_FOUND;
  1531. ScepLogOutput3(1,retErr, SCEDLL_CANNOT_FIND, pName->Name);
  1532. }
  1533. if ( pDsResult ) {
  1534. (*pfnDsFreeNameResult) (pDsResult);
  1535. pDsResult = NULL;
  1536. }
  1537. if ( retErr != ERROR_SUCCESS ) {
  1538. break;
  1539. }
  1540. }
  1541. return(retErr);
  1542. }
  1543. #endif