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.

1581 lines
40 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. groupp.c
  5. Abstract:
  6. Private functions for supporting NetGroup API
  7. Author:
  8. Cliff Van Dyke (cliffv) 06-Mar-1991
  9. Environment:
  10. User mode only.
  11. Contains NT-specific code.
  12. Requires ANSI C extensions: slash-slash comments, long external names.
  13. Revision History:
  14. 17-Apr-1991 (cliffv)
  15. Incorporated review comments.
  16. 20-Jan-1992 (madana)
  17. Sundry API changes
  18. --*/
  19. #include <nt.h>
  20. #include <ntrtl.h>
  21. #include <nturtl.h>
  22. #undef DOMAIN_ALL_ACCESS // defined in both ntsam.h and ntwinapi.h
  23. #include <ntsam.h>
  24. #include <ntlsa.h>
  25. #define NOMINMAX // Avoid redefinition of min and max in stdlib.h
  26. #include <windef.h>
  27. #include <winbase.h>
  28. #include <lmcons.h>
  29. #include <access.h>
  30. #include <align.h>
  31. #include <icanon.h>
  32. #include <lmaccess.h>
  33. #include <lmerr.h>
  34. #include <netdebug.h>
  35. #include <netlib.h>
  36. #include <netlibnt.h>
  37. #include <rpcutil.h>
  38. #include <stddef.h>
  39. #include <uasp.h>
  40. #include <stdlib.h>
  41. #include <accessp.h>
  42. NET_API_STATUS
  43. GrouppChangeMember(
  44. IN LPCWSTR ServerName OPTIONAL,
  45. IN LPCWSTR GroupName,
  46. IN LPCWSTR UserName,
  47. IN BOOL AddMember
  48. )
  49. /*++
  50. Routine Description:
  51. Common routine to add or remove a member from a group
  52. Arguments:
  53. ServerName - A pointer to a string containing the name of the remote
  54. server on which the function is to execute. A NULL pointer
  55. or string specifies the local machine.
  56. GroupName - Name of the group to change membership of.
  57. UserName - Name of the user to change membership of.
  58. AddMember - True to ADD the user to the group.
  59. Return Value:
  60. Error code for the operation.
  61. --*/
  62. {
  63. NET_API_STATUS NetStatus;
  64. NTSTATUS Status;
  65. SAM_HANDLE SamServerHandle = NULL;
  66. SAM_HANDLE DomainHandle = NULL;
  67. SAM_HANDLE GroupHandle = NULL;
  68. //
  69. // Variables for converting names to relative IDs
  70. //
  71. UNICODE_STRING NameString;
  72. PULONG RelativeId = NULL;
  73. PSID_NAME_USE NameUse = NULL;
  74. //
  75. // Connect to the SAM server
  76. //
  77. NetStatus = UaspOpenSam( ServerName,
  78. FALSE, // Don't try null session
  79. &SamServerHandle );
  80. if ( NetStatus != NERR_Success ) {
  81. IF_DEBUG( UAS_DEBUG_GROUP ) {
  82. NetpKdPrint(( "GrouppChangeMember: Cannot UaspOpenSam %ld\n", NetStatus ));
  83. }
  84. goto Cleanup;
  85. }
  86. //
  87. // Open the Domain
  88. //
  89. NetStatus = UaspOpenDomain( SamServerHandle,
  90. DOMAIN_LOOKUP,
  91. TRUE, // Account Domain
  92. &DomainHandle,
  93. NULL); // DomainId
  94. if ( NetStatus != NERR_Success ) {
  95. IF_DEBUG( UAS_DEBUG_GROUP ) {
  96. NetpKdPrint((
  97. "GrouppChangeMember: UaspOpenDomain returned %ld\n",
  98. NetStatus ));
  99. }
  100. goto Cleanup;
  101. }
  102. //
  103. // Open the group
  104. //
  105. NetStatus = GrouppOpenGroup( DomainHandle,
  106. AddMember ?
  107. GROUP_ADD_MEMBER : GROUP_REMOVE_MEMBER,
  108. GroupName,
  109. &GroupHandle,
  110. NULL ); // Relative Id
  111. if ( NetStatus != NERR_Success ) {
  112. IF_DEBUG( UAS_DEBUG_GROUP ) {
  113. NetpKdPrint((
  114. "GrouppChangeMember: GrouppOpenGroup returned %ld\n",
  115. NetStatus ));
  116. }
  117. goto Cleanup;
  118. }
  119. //
  120. // Convert User name to relative ID.
  121. //
  122. RtlInitUnicodeString( &NameString, UserName );
  123. Status = SamLookupNamesInDomain( DomainHandle,
  124. 1,
  125. &NameString,
  126. &RelativeId,
  127. &NameUse );
  128. if ( !NT_SUCCESS(Status) ) {
  129. IF_DEBUG( UAS_DEBUG_GROUP ) {
  130. NetpKdPrint((
  131. "GrouppChangeMember: SamLookupNamesInDomain returned %lX\n",
  132. Status ));
  133. }
  134. if ( Status == STATUS_NONE_MAPPED ) {
  135. NetStatus = NERR_UserNotFound;
  136. } else {
  137. NetStatus = NetpNtStatusToApiStatus( Status );
  138. }
  139. goto Cleanup;
  140. }
  141. if ( *NameUse != SidTypeUser ) {
  142. NetStatus = NERR_UserNotFound;
  143. goto Cleanup;
  144. }
  145. //
  146. // Add the user as a member of the group.
  147. //
  148. // SE_GROUP_MANDATORY might be conflict with the attributes of the group, so
  149. // try that attribute both ways.
  150. //
  151. if ( AddMember ) {
  152. Status = SamAddMemberToGroup(
  153. GroupHandle,
  154. *RelativeId,
  155. SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
  156. SE_GROUP_ENABLED );
  157. if ( Status == STATUS_INVALID_GROUP_ATTRIBUTES ) {
  158. Status = SamAddMemberToGroup( GroupHandle,
  159. *RelativeId,
  160. SE_GROUP_ENABLED_BY_DEFAULT |
  161. SE_GROUP_ENABLED );
  162. }
  163. //
  164. // Delete the user as a member of the group
  165. //
  166. } else {
  167. Status = SamRemoveMemberFromGroup( GroupHandle,
  168. *RelativeId);
  169. }
  170. IF_DEBUG( UAS_DEBUG_GROUP ) {
  171. NetpKdPrint((
  172. "GrouppChangeMember: SamAdd(orRemove)MemberFromGroup returned %lX\n",
  173. Status ));
  174. }
  175. if ( !NT_SUCCESS(Status) ) {
  176. NetStatus = NetpNtStatusToApiStatus( Status );
  177. goto Cleanup;
  178. }
  179. NetStatus = NERR_Success;
  180. //
  181. // Clean up.
  182. //
  183. Cleanup:
  184. if ( RelativeId != NULL ) {
  185. Status = SamFreeMemory( RelativeId );
  186. NetpAssert( NT_SUCCESS(Status) );
  187. }
  188. if ( NameUse != NULL ) {
  189. Status = SamFreeMemory( NameUse );
  190. NetpAssert( NT_SUCCESS(Status) );
  191. }
  192. if ( GroupHandle != NULL ) {
  193. (VOID) SamCloseHandle( GroupHandle );
  194. }
  195. UaspCloseDomain( DomainHandle );
  196. if ( SamServerHandle != NULL ) {
  197. (VOID) SamCloseHandle( SamServerHandle );
  198. }
  199. return NetStatus;
  200. } // GrouppChangeMember
  201. NET_API_STATUS
  202. GrouppGetInfo(
  203. IN SAM_HANDLE DomainHandle,
  204. IN ULONG RelativeId,
  205. IN DWORD Level,
  206. OUT PVOID *Buffer
  207. )
  208. /*++
  209. Routine Description:
  210. Internal routine to get group information
  211. Arguments:
  212. DomainHandle - Supplies the Handle of the domain the group is in.
  213. RelativeId - Supplies the relative ID of the group to open.
  214. Level - Level of information required. 0, 1 and 2 are valid.
  215. Buffer - Returns a pointer to the return information structure.
  216. Caller must deallocate buffer using NetApiBufferFree.
  217. Return Value:
  218. Error code for the operation.
  219. --*/
  220. {
  221. NET_API_STATUS NetStatus;
  222. NTSTATUS Status;
  223. SAM_HANDLE GroupHandle = NULL;
  224. GROUP_GENERAL_INFORMATION *GroupGeneral = NULL;
  225. PVOID lastVarData;
  226. DWORD BufferSize;
  227. DWORD FixedSize;
  228. PSID GroupSid = NULL;
  229. ULONG RidToReturn = RelativeId;
  230. PGROUP_INFO_0 grpi0;
  231. //
  232. // Validate the level
  233. //
  234. if ( Level == 2 ) {
  235. ULONG Mode;
  236. Status = SamGetCompatibilityMode(DomainHandle, &Mode);
  237. if (!NT_SUCCESS(Status)) {
  238. NetStatus = NetpNtStatusToApiStatus( Status );
  239. goto Cleanup;
  240. }
  241. switch (Mode) {
  242. case SAM_SID_COMPATIBILITY_STRICT:
  243. NetStatus = ERROR_NOT_SUPPORTED;
  244. goto Cleanup;
  245. case SAM_SID_COMPATIBILITY_LAX:
  246. RidToReturn = 0;
  247. break;
  248. }
  249. }
  250. //
  251. // Open the group
  252. //
  253. Status = SamOpenGroup( DomainHandle,
  254. GROUP_READ_INFORMATION,
  255. RelativeId,
  256. &GroupHandle);
  257. if ( !NT_SUCCESS(Status) ) {
  258. NetStatus = NetpNtStatusToApiStatus( Status );
  259. goto Cleanup;
  260. }
  261. //
  262. // Get the information about the group.
  263. //
  264. Status = SamQueryInformationGroup( GroupHandle,
  265. GroupReplicationInformation,
  266. (PVOID *)&GroupGeneral);
  267. if ( ! NT_SUCCESS( Status ) ) {
  268. NetStatus = NetpNtStatusToApiStatus( Status );
  269. goto Cleanup;
  270. }
  271. //
  272. // Obtain the group's sid
  273. //
  274. if ( Level == 3 ) {
  275. NetStatus = NetpSamRidToSid(DomainHandle,
  276. RelativeId,
  277. &GroupSid);
  278. if ( NetStatus != NERR_Success ) {
  279. goto Cleanup;
  280. }
  281. }
  282. //
  283. // Figure out how big the return buffer needs to be
  284. //
  285. switch ( Level ) {
  286. case 0:
  287. FixedSize = sizeof( GROUP_INFO_0 );
  288. BufferSize = FixedSize +
  289. GroupGeneral->Name.Length + sizeof(WCHAR);
  290. break;
  291. case 1:
  292. FixedSize = sizeof( GROUP_INFO_1 );
  293. BufferSize = FixedSize +
  294. GroupGeneral->Name.Length + sizeof(WCHAR) +
  295. GroupGeneral->AdminComment.Length + sizeof(WCHAR);
  296. break;
  297. case 2:
  298. FixedSize = sizeof( GROUP_INFO_2 );
  299. BufferSize = FixedSize +
  300. GroupGeneral->Name.Length + sizeof(WCHAR) +
  301. GroupGeneral->AdminComment.Length + sizeof(WCHAR);
  302. break;
  303. case 3:
  304. FixedSize = sizeof( GROUP_INFO_3 );
  305. BufferSize = FixedSize +
  306. GroupGeneral->Name.Length + sizeof(WCHAR) +
  307. GroupGeneral->AdminComment.Length + sizeof(WCHAR) +
  308. RtlLengthSid(GroupSid);
  309. break;
  310. default:
  311. NetStatus = ERROR_INVALID_LEVEL;
  312. goto Cleanup;
  313. }
  314. //
  315. // Allocate the return buffer.
  316. //
  317. BufferSize = ROUND_UP_COUNT( BufferSize, ALIGN_DWORD );
  318. *Buffer = MIDL_user_allocate( BufferSize );
  319. if (*Buffer == NULL ) {
  320. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  321. goto Cleanup;
  322. }
  323. lastVarData = (PBYTE) ((LPBYTE)(*Buffer)) + BufferSize;
  324. //
  325. // Fill the name into the return buffer.
  326. //
  327. NetpAssert( offsetof( GROUP_INFO_0, grpi0_name ) ==
  328. offsetof( GROUP_INFO_1, grpi1_name ) );
  329. NetpAssert( offsetof( GROUP_INFO_1, grpi1_name ) ==
  330. offsetof( GROUP_INFO_2, grpi2_name ) );
  331. NetpAssert( offsetof( GROUP_INFO_2, grpi2_name ) ==
  332. offsetof( GROUP_INFO_3, grpi3_name ) );
  333. NetpAssert( offsetof( GROUP_INFO_1, grpi1_comment ) ==
  334. offsetof( GROUP_INFO_2, grpi2_comment ) );
  335. NetpAssert( offsetof( GROUP_INFO_2, grpi2_comment ) ==
  336. offsetof( GROUP_INFO_3, grpi3_comment ) );
  337. grpi0 = ((PGROUP_INFO_0)*Buffer);
  338. //
  339. // Fill in the return buffer.
  340. //
  341. switch ( Level ) {
  342. case 3:
  343. {
  344. PGROUP_INFO_3 grpi3 = ((PGROUP_INFO_3)grpi0);
  345. NetpAssert( NULL != GroupSid );
  346. if ( !NetpCopyDataToBuffer(
  347. (LPBYTE) GroupSid,
  348. RtlLengthSid(GroupSid),
  349. ((LPBYTE)(*Buffer)) + FixedSize,
  350. (PBYTE*) &lastVarData,
  351. (LPBYTE *)&grpi3->grpi3_group_sid,
  352. ALIGN_DWORD ) ) {
  353. NetStatus = NERR_InternalError;
  354. goto Cleanup;
  355. }
  356. ((PGROUP_INFO_3)grpi3)->grpi3_attributes = GroupGeneral->Attributes;
  357. //
  358. // Fall through to the next level
  359. //
  360. }
  361. case 2:
  362. //
  363. // copy info level 2 only fields
  364. //
  365. if ( Level == 2 ) {
  366. ((PGROUP_INFO_2)grpi0)->grpi2_group_id = RidToReturn;
  367. ((PGROUP_INFO_2)grpi0)->grpi2_attributes = GroupGeneral->Attributes;
  368. }
  369. /* FALL THROUGH FOR OTHER FIELDS */
  370. case 1:
  371. //
  372. // copy fields common to info level 1 and 2.
  373. //
  374. if ( !NetpCopyStringToBuffer(
  375. GroupGeneral->AdminComment.Buffer,
  376. GroupGeneral->AdminComment.Length/sizeof(WCHAR),
  377. ((LPBYTE)(*Buffer)) + FixedSize,
  378. (LPWSTR*)&lastVarData,
  379. &((PGROUP_INFO_1)grpi0)->grpi1_comment ) ) {
  380. NetStatus = NERR_InternalError;
  381. goto Cleanup;
  382. }
  383. /* FALL THROUGH FOR NAME FIELD */
  384. case 0:
  385. //
  386. // copy common field (name field) in the buffer.
  387. //
  388. if ( !NetpCopyStringToBuffer(
  389. GroupGeneral->Name.Buffer,
  390. GroupGeneral->Name.Length/sizeof(WCHAR),
  391. ((LPBYTE)(*Buffer)) + FixedSize,
  392. (LPWSTR*)&lastVarData,
  393. &grpi0->grpi0_name ) ) {
  394. NetStatus = NERR_InternalError;
  395. goto Cleanup;
  396. }
  397. break;
  398. default:
  399. NetStatus = ERROR_INVALID_LEVEL;
  400. goto Cleanup;
  401. }
  402. NetStatus = NERR_Success;
  403. //
  404. // Cleanup and return.
  405. //
  406. Cleanup:
  407. if ( GroupGeneral ) {
  408. Status = SamFreeMemory( GroupGeneral );
  409. NetpAssert( NT_SUCCESS(Status) );
  410. }
  411. if ( GroupHandle ) {
  412. (VOID) SamCloseHandle( GroupHandle );
  413. }
  414. if ( GroupSid ) {
  415. NetpMemoryFree( GroupSid );
  416. }
  417. IF_DEBUG( UAS_DEBUG_GROUP ) {
  418. NetpKdPrint(( "GrouppGetInfo: returns %ld\n", NetStatus ));
  419. }
  420. return NetStatus;
  421. } // GrouppGetInfo
  422. NET_API_STATUS
  423. GrouppOpenGroup(
  424. IN SAM_HANDLE DomainHandle,
  425. IN ACCESS_MASK DesiredAccess,
  426. IN LPCWSTR GroupName,
  427. OUT PSAM_HANDLE GroupHandle OPTIONAL,
  428. OUT PULONG RelativeId OPTIONAL
  429. )
  430. /*++
  431. Routine Description:
  432. Open a Sam Group by Name
  433. Arguments:
  434. DomainHandle - Supplies the Domain Handle.
  435. DesiredAccess - Supplies access mask indicating desired access to group.
  436. GroupName - Group name of the group.
  437. GroupHandle - Returns a handle to the group. If NULL, group is not
  438. actually opened (merely the relative ID is returned).
  439. RelativeId - Returns the relative ID of the group. If NULL the relative
  440. Id is not returned.
  441. Return Value:
  442. Error code for the operation.
  443. --*/
  444. {
  445. NTSTATUS Status;
  446. NET_API_STATUS NetStatus;
  447. //
  448. // Variables for converting names to relative IDs
  449. //
  450. UNICODE_STRING NameString;
  451. PSID_NAME_USE NameUse;
  452. PULONG LocalRelativeId;
  453. RtlInitUnicodeString( &NameString, GroupName );
  454. //
  455. // Convert group name to relative ID.
  456. //
  457. Status = SamLookupNamesInDomain( DomainHandle,
  458. 1,
  459. &NameString,
  460. &LocalRelativeId,
  461. &NameUse );
  462. if ( !NT_SUCCESS(Status) ) {
  463. IF_DEBUG( UAS_DEBUG_GROUP ) {
  464. NetpKdPrint(( "GrouppOpenGroup: %wZ: SamLookupNamesInDomain %lX\n",
  465. &NameString,
  466. Status ));
  467. }
  468. return NetpNtStatusToApiStatus( Status );
  469. }
  470. if ( *NameUse != SidTypeGroup ) {
  471. IF_DEBUG( UAS_DEBUG_GROUP ) {
  472. NetpKdPrint(( "GrouppOpenGroup: %wZ: Name is not a group %ld\n",
  473. &NameString,
  474. *NameUse ));
  475. }
  476. NetStatus = NERR_GroupNotFound;
  477. goto Cleanup;
  478. }
  479. //
  480. // Open the group
  481. //
  482. if ( GroupHandle != NULL ) {
  483. Status = SamOpenGroup( DomainHandle,
  484. DesiredAccess,
  485. *LocalRelativeId,
  486. GroupHandle);
  487. if ( !NT_SUCCESS(Status) ) {
  488. IF_DEBUG( UAS_DEBUG_GROUP ) {
  489. NetpKdPrint(( "GrouppOpenGroup: %wZ: SamOpenGroup %lX\n",
  490. &NameString,
  491. Status ));
  492. }
  493. NetStatus = NetpNtStatusToApiStatus( Status );
  494. goto Cleanup;
  495. }
  496. }
  497. //
  498. // Return the relative Id if it's wanted.
  499. //
  500. if ( RelativeId != NULL ) {
  501. *RelativeId = *LocalRelativeId;
  502. }
  503. NetStatus = NERR_Success;
  504. //
  505. // Cleanup
  506. //
  507. Cleanup:
  508. if ( LocalRelativeId != NULL ) {
  509. Status = SamFreeMemory( LocalRelativeId );
  510. NetpAssert( NT_SUCCESS(Status) );
  511. }
  512. if ( NameUse != NULL ) {
  513. Status = SamFreeMemory( NameUse );
  514. NetpAssert( NT_SUCCESS(Status) );
  515. }
  516. return NetStatus;
  517. } // GrouppOpenGroup
  518. VOID
  519. GrouppRelocationRoutine(
  520. IN DWORD Level,
  521. IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
  522. IN PTRDIFF_T Offset
  523. )
  524. /*++
  525. Routine Description:
  526. Routine to relocate the pointers from the fixed portion of a NetGroupEnum
  527. enumeration
  528. buffer to the string portion of an enumeration buffer. It is called
  529. as a callback routine from NetpAllocateEnumBuffer when it re-allocates
  530. such a buffer. NetpAllocateEnumBuffer copied the fixed portion and
  531. string portion into the new buffer before calling this routine.
  532. Arguments:
  533. Level - Level of information in the buffer.
  534. BufferDescriptor - Description of the new buffer.
  535. Offset - Offset to add to each pointer in the fixed portion.
  536. Return Value:
  537. Returns the error code for the operation.
  538. --*/
  539. {
  540. DWORD EntryCount;
  541. DWORD EntryNumber;
  542. DWORD FixedSize;
  543. IF_DEBUG( UAS_DEBUG_GROUP ) {
  544. NetpKdPrint(( "GrouppRelocationRoutine: entering\n" ));
  545. }
  546. //
  547. // Compute the number of fixed size entries
  548. //
  549. switch (Level) {
  550. case 0:
  551. FixedSize = sizeof(GROUP_INFO_0);
  552. break;
  553. case 1:
  554. FixedSize = sizeof(GROUP_INFO_1);
  555. break;
  556. case 2:
  557. FixedSize = sizeof(GROUP_INFO_2);
  558. break;
  559. default:
  560. NetpAssert( FALSE );
  561. return;
  562. }
  563. EntryCount =
  564. ((DWORD)(BufferDescriptor->FixedDataEnd - BufferDescriptor->Buffer)) /
  565. FixedSize;
  566. //
  567. // Loop relocating each field in each fixed size structure
  568. //
  569. for ( EntryNumber=0; EntryNumber<EntryCount; EntryNumber++ ) {
  570. LPBYTE TheStruct = BufferDescriptor->Buffer + FixedSize * EntryNumber;
  571. switch ( Level ) {
  572. case 2:
  573. case 1:
  574. RELOCATE_ONE( ((PGROUP_INFO_1)TheStruct)->grpi1_comment, Offset );
  575. //
  576. // Drop through to case 0
  577. //
  578. case 0:
  579. RELOCATE_ONE( ((PGROUP_INFO_0)TheStruct)->grpi0_name, Offset );
  580. break;
  581. default:
  582. return;
  583. }
  584. }
  585. return;
  586. } // GrouppRelocationRoutine
  587. VOID
  588. GrouppMemberRelocationRoutine(
  589. IN DWORD Level,
  590. IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
  591. IN PTRDIFF_T Offset
  592. )
  593. /*++
  594. Routine Description:
  595. Routine to relocate the pointers from the fixed portion of a
  596. NetGroupGetUsers enumeration
  597. buffer to the string portion of an enumeration buffer. It is called
  598. as a callback routine from NetpAllocateEnumBuffer when it re-allocates
  599. such a buffer. NetpAllocateEnumBuffer copied the fixed portion and
  600. string portion into the new buffer before calling this routine.
  601. Arguments:
  602. Level - Level of information in the buffer.
  603. BufferDescriptor - Description of the new buffer.
  604. Offset - Offset to add to each pointer in the fixed portion.
  605. Return Value:
  606. Returns the error code for the operation.
  607. --*/
  608. {
  609. DWORD EntryCount;
  610. DWORD EntryNumber;
  611. DWORD FixedSize;
  612. IF_DEBUG( UAS_DEBUG_GROUP ) {
  613. NetpKdPrint(( "GrouppMemberRelocationRoutine: entering\n" ));
  614. }
  615. //
  616. // Compute the number of fixed size entries
  617. //
  618. switch (Level) {
  619. case 0:
  620. FixedSize = sizeof(GROUP_USERS_INFO_0);
  621. break;
  622. case 1:
  623. FixedSize = sizeof(GROUP_USERS_INFO_1);
  624. break;
  625. default:
  626. NetpAssert( FALSE );
  627. return;
  628. }
  629. EntryCount =
  630. ((DWORD)(BufferDescriptor->FixedDataEnd - BufferDescriptor->Buffer)) /
  631. FixedSize;
  632. //
  633. // Loop relocating each field in each fixed size structure
  634. //
  635. for ( EntryNumber=0; EntryNumber<EntryCount; EntryNumber++ ) {
  636. LPBYTE TheStruct = BufferDescriptor->Buffer + FixedSize * EntryNumber;
  637. //
  638. // Both info levels only have one field to relocate
  639. //
  640. RELOCATE_ONE( ((PGROUP_USERS_INFO_0)TheStruct)->grui0_name, Offset );
  641. }
  642. return;
  643. } // GrouppMemberRelocationRoutine
  644. NET_API_STATUS
  645. GrouppSetUsers (
  646. IN LPCWSTR ServerName OPTIONAL,
  647. IN LPCWSTR GroupName,
  648. IN DWORD Level,
  649. IN LPBYTE Buffer,
  650. IN DWORD NewMemberCount,
  651. IN BOOL DeleteGroup
  652. )
  653. /*++
  654. Routine Description:
  655. Set the list of members of a group and optionally delete the group
  656. when finished.
  657. The members specified by "Buffer" are called new members. The current
  658. members of the group are called old members. Members which are
  659. on both the old and new list are common members.
  660. The SAM API allows only one member to be added or deleted at a time.
  661. This API allows all of the members of a group to be specified en-masse.
  662. This API is careful to always leave the group membership in the SAM
  663. database in a reasonable state. It does by mergeing the list of
  664. old and new members, then only changing those memberships which absolutely
  665. need changing.
  666. Group membership is restored to its previous state (if possible) if
  667. an error occurs during changing the group membership.
  668. Arguments:
  669. ServerName - A pointer to a string containing the name of the remote
  670. server on which the function is to execute. A NULL pointer
  671. or string specifies the local machine.
  672. GroupName - Name of the group to modify.
  673. Level - Level of information provided. Must be 0 or 1.
  674. Buffer - A pointer to the buffer containing an array of NewMemberCount
  675. the group membership information structures.
  676. NewMemberCount - Number of entries in Buffer.
  677. DeleteGroup - TRUE if the group is to be deleted after changing the
  678. membership.
  679. Return Value:
  680. Error code for the operation.
  681. --*/
  682. {
  683. NET_API_STATUS NetStatus;
  684. NTSTATUS Status;
  685. SAM_HANDLE SamServerHandle = NULL;
  686. SAM_HANDLE DomainHandle = NULL;
  687. SAM_HANDLE GroupHandle = NULL;
  688. ACCESS_MASK DesiredAccess;
  689. //
  690. // Variables for dealing with old or new lists of members
  691. //
  692. PULONG NewRelativeIds = NULL; // Relative Ids of the new members
  693. PULONG OldRelativeIds = NULL; // Relative Ids of the old members
  694. PULONG OldAttributes = NULL; // Attributes of the old members
  695. PSID_NAME_USE NewNameUse = NULL;// Name usage of the new members
  696. PSID_NAME_USE OldNameUse = NULL;// Name usage of the old members
  697. PUNICODE_STRING NameStrings = NULL; // Names of a list of members
  698. ULONG OldMemberCount; // Number of current members in the group
  699. ULONG DefaultMemberAttributes; // Default attributes for new members
  700. DWORD FixedSize;
  701. //
  702. // Define an internal member list structure.
  703. //
  704. // The structure defines a list of new members to be added, members whose
  705. // attributes merely need to be changed, and members which
  706. // need to be deleted. The list is maintained in relative ID sorted
  707. // order.
  708. //
  709. struct _MEMBER_DESCRIPTION {
  710. struct _MEMBER_DESCRIPTION * Next; // Next entry in linked list;
  711. ULONG RelativeId; // Relative ID of this member
  712. enum _Action { // Action taken for this member
  713. AddMember, // Add Member to group
  714. RemoveMember, // Remove Member from group
  715. SetAttributesMember, // Change the Members attributes
  716. IgnoreMember // Ignore this member
  717. } Action;
  718. ULONG NewAttributes; // Attributes to set for the member
  719. BOOL Done; // True if this action has been taken
  720. ULONG OldAttributes; // Attributes to restore on a recovery
  721. } *MemberList = NULL , *CurEntry, **Entry;
  722. //
  723. // Connect to the SAM server
  724. //
  725. NetStatus = UaspOpenSam( ServerName,
  726. FALSE, // Don't try null session
  727. &SamServerHandle );
  728. if ( NetStatus != NERR_Success ) {
  729. IF_DEBUG( UAS_DEBUG_GROUP ) {
  730. NetpKdPrint(( "GrouppSetUsers: Cannot UaspOpenSam %ld\n", NetStatus ));
  731. }
  732. goto Cleanup;
  733. }
  734. //
  735. // Open the Domain
  736. //
  737. NetStatus = UaspOpenDomain( SamServerHandle,
  738. DOMAIN_LOOKUP,
  739. TRUE, // Account Domain
  740. &DomainHandle,
  741. NULL); // DomainId
  742. if ( NetStatus != NERR_Success ) {
  743. IF_DEBUG( UAS_DEBUG_GROUP ) {
  744. NetpKdPrint(( "GrouppSetUsers: UaspOpenDomain returns %ld\n",
  745. NetStatus ));
  746. }
  747. goto Cleanup;
  748. }
  749. //
  750. // Open the group
  751. //
  752. DesiredAccess = GROUP_READ_INFORMATION | GROUP_LIST_MEMBERS |
  753. GROUP_ADD_MEMBER | GROUP_REMOVE_MEMBER;
  754. if ( DeleteGroup ) {
  755. NetpAssert( NewMemberCount == 0 );
  756. DesiredAccess |= DELETE;
  757. }
  758. NetStatus = GrouppOpenGroup( DomainHandle,
  759. DesiredAccess,
  760. GroupName,
  761. &GroupHandle,
  762. NULL ); // Relative Id
  763. if ( NetStatus != NERR_Success ) {
  764. IF_DEBUG( UAS_DEBUG_GROUP ) {
  765. NetpKdPrint(( "GrouppSetUsers: GrouppOpenGroup returns %ld\n",
  766. NetStatus ));
  767. }
  768. goto Cleanup;
  769. }
  770. //
  771. // Validate the level
  772. //
  773. switch (Level) {
  774. case 0: {
  775. //
  776. // Determine the attributes of the group as a whole. Use that
  777. // for deciding on the default attributes for new members.
  778. //
  779. PGROUP_ATTRIBUTE_INFORMATION Attributes;
  780. Status = SamQueryInformationGroup( GroupHandle,
  781. GroupAttributeInformation,
  782. (PVOID*)&Attributes );
  783. if ( !NT_SUCCESS(Status) ) {
  784. IF_DEBUG( UAS_DEBUG_GROUP ) {
  785. NetpKdPrint((
  786. "GrouppSetUsers: SamQueryInformationGroup returns %lX\n",
  787. Status ));
  788. }
  789. NetStatus = NetpNtStatusToApiStatus( Status );
  790. goto Cleanup;
  791. }
  792. DefaultMemberAttributes =
  793. (Attributes->Attributes & SE_GROUP_MANDATORY) |
  794. SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED ;
  795. FixedSize = sizeof( GROUP_USERS_INFO_0 );
  796. Status = SamFreeMemory( Attributes );
  797. NetpAssert( NT_SUCCESS(Status) );
  798. break;
  799. }
  800. case 1:
  801. FixedSize = sizeof( GROUP_USERS_INFO_1 );
  802. break;
  803. default:
  804. NetStatus = ERROR_INVALID_LEVEL;
  805. goto Cleanup;
  806. }
  807. //
  808. // Determine the Relative Id and usage of each of the new members.
  809. //
  810. if ( NewMemberCount > 0 ) {
  811. DWORD NewIndex; // Index to a new member
  812. PGROUP_USERS_INFO_0 grui0;
  813. //
  814. // Allocate a buffer big enough to contain all the string variables
  815. // for the new member names.
  816. //
  817. NameStrings = NetpMemoryAllocate( NewMemberCount *
  818. sizeof(UNICODE_STRING) );
  819. if ( NameStrings == NULL ) {
  820. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  821. goto Cleanup;
  822. }
  823. //
  824. // Fill in the list of member name strings for each new member.
  825. //
  826. NetpAssert( offsetof( GROUP_USERS_INFO_0, grui0_name ) ==
  827. offsetof( GROUP_USERS_INFO_1, grui1_name ) );
  828. for ( NewIndex=0, grui0 = (PGROUP_USERS_INFO_0)Buffer;
  829. NewIndex<NewMemberCount;
  830. NewIndex++,
  831. grui0 = (PGROUP_USERS_INFO_0)
  832. ((LPBYTE)grui0 + FixedSize) ) {
  833. RtlInitUnicodeString(&NameStrings[NewIndex], grui0->grui0_name);
  834. }
  835. //
  836. // Convert the member names to relative Ids.
  837. //
  838. Status = SamLookupNamesInDomain( DomainHandle,
  839. NewMemberCount,
  840. NameStrings,
  841. &NewRelativeIds,
  842. &NewNameUse );
  843. if ( !NT_SUCCESS( Status )) {
  844. IF_DEBUG( UAS_DEBUG_GROUP ) {
  845. NetpKdPrint((
  846. "GrouppSetUsers: SamLookupNamesInDomain returns %lX\n",
  847. Status ));
  848. }
  849. if ( Status == STATUS_NONE_MAPPED ) {
  850. NetStatus = NERR_UserNotFound;
  851. goto Cleanup;
  852. }
  853. NetStatus = NetpNtStatusToApiStatus( Status );
  854. goto Cleanup;
  855. }
  856. //
  857. // Build a member entry for each of the new members.
  858. // The list is maintained in RelativeId sorted order.
  859. //
  860. for ( NewIndex=0; NewIndex<NewMemberCount; NewIndex++ ) {
  861. //
  862. // Ensure this new member name is an existing user name.
  863. // Group names are not allowed to be added via this API.
  864. //
  865. if (NewNameUse[NewIndex] != SidTypeUser) {
  866. NetStatus = NERR_UserNotFound;
  867. goto Cleanup;
  868. }
  869. //
  870. // Find the place to put the new entry
  871. //
  872. Entry = &MemberList ;
  873. while ( *Entry != NULL &&
  874. (*Entry)->RelativeId < NewRelativeIds[NewIndex] ) {
  875. Entry = &( (*Entry)->Next );
  876. }
  877. //
  878. // If this is not a duplicate entry, allocate a new member structure
  879. // and fill it in.
  880. //
  881. // Just ignore duplicate relative Ids.
  882. //
  883. if ( *Entry == NULL ||
  884. (*Entry)->RelativeId > NewRelativeIds[NewIndex] ) {
  885. CurEntry = NetpMemoryAllocate(
  886. sizeof(struct _MEMBER_DESCRIPTION));
  887. if ( CurEntry == NULL ) {
  888. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  889. goto Cleanup;
  890. }
  891. CurEntry->Next = *Entry;
  892. CurEntry->RelativeId = NewRelativeIds[NewIndex];
  893. CurEntry->Action = AddMember;
  894. CurEntry->Done = FALSE;
  895. CurEntry->NewAttributes = ( Level == 1 ) ?
  896. ((PGROUP_USERS_INFO_1)Buffer)[NewIndex].grui1_attributes :
  897. DefaultMemberAttributes;
  898. *Entry = CurEntry;
  899. }
  900. }
  901. NetpMemoryFree( NameStrings );
  902. NameStrings = NULL;
  903. Status = SamFreeMemory( NewRelativeIds );
  904. NewRelativeIds = NULL;
  905. NetpAssert( NT_SUCCESS(Status) );
  906. Status = SamFreeMemory( NewNameUse );
  907. NewNameUse = NULL;
  908. NetpAssert( NT_SUCCESS(Status) );
  909. }
  910. //
  911. // Determine the number of old members for this group and the
  912. // relative ID's of the old members.
  913. //
  914. Status = SamGetMembersInGroup(
  915. GroupHandle,
  916. &OldRelativeIds,
  917. &OldAttributes,
  918. &OldMemberCount );
  919. if ( !NT_SUCCESS( Status ) ) {
  920. IF_DEBUG( UAS_DEBUG_GROUP ) {
  921. NetpKdPrint((
  922. "GrouppSetUsers: SamGetMembersInGroup returns %lX\n",
  923. Status ));
  924. }
  925. NetStatus = NetpNtStatusToApiStatus( Status );
  926. goto Cleanup;
  927. }
  928. //
  929. // If there are any old members,
  930. // Merge them into the list.
  931. //
  932. if ( OldMemberCount > 0 ) {
  933. ULONG OldIndex; // Index to current entry
  934. PUNICODE_STRING Names;
  935. //
  936. // Determine the usage for all the returned relative Ids.
  937. //
  938. Status = SamLookupIdsInDomain(
  939. DomainHandle,
  940. OldMemberCount,
  941. OldRelativeIds,
  942. &Names,
  943. &OldNameUse );
  944. if ( !NT_SUCCESS( Status ) ) {
  945. IF_DEBUG( UAS_DEBUG_GROUP ) {
  946. NetpKdPrint((
  947. "GrouppSetUsers: SamLookupIdsInDomain returns %lX\n",
  948. Status ));
  949. }
  950. if ( Status == STATUS_NONE_MAPPED ) {
  951. NetStatus = NERR_InternalError ;
  952. goto Cleanup;
  953. }
  954. NetStatus = NetpNtStatusToApiStatus( Status );
  955. goto Cleanup;
  956. }
  957. Status = SamFreeMemory( Names ); // Don't need names at all
  958. NetpAssert( NT_SUCCESS(Status) );
  959. //
  960. // Loop for each current member
  961. //
  962. for ( OldIndex=0; OldIndex<OldMemberCount; OldIndex++ ) {
  963. //
  964. // Ignore old members which aren't a user.
  965. //
  966. if ( OldNameUse[OldIndex] != SidTypeUser ) {
  967. //
  968. // ?? Why? is't it internal error ?
  969. //
  970. continue;
  971. }
  972. //
  973. // Find the place to put the new entry
  974. //
  975. Entry = &MemberList ;
  976. while ( *Entry != NULL &&
  977. (*Entry)->RelativeId < OldRelativeIds[OldIndex] ) {
  978. Entry = &( (*Entry)->Next );
  979. }
  980. //
  981. // If this entry is not already in the list,
  982. // this is a member which exists now but should be deleted.
  983. //
  984. if( *Entry == NULL || (*Entry)->RelativeId > OldRelativeIds[OldIndex]){
  985. CurEntry =
  986. NetpMemoryAllocate(sizeof(struct _MEMBER_DESCRIPTION));
  987. if ( CurEntry == NULL ) {
  988. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  989. goto Cleanup;
  990. }
  991. CurEntry->Next = *Entry;
  992. CurEntry->RelativeId = OldRelativeIds[OldIndex];
  993. CurEntry->Action = RemoveMember;
  994. CurEntry->Done = FALSE;
  995. CurEntry->OldAttributes = OldAttributes[OldIndex];
  996. *Entry = CurEntry;
  997. //
  998. // Handle the case where this member is already in the list
  999. //
  1000. } else {
  1001. //
  1002. // Watch out for SAM returning the same member twice.
  1003. //
  1004. if ( (*Entry)->Action != AddMember ) {
  1005. Status = NERR_InternalError;
  1006. goto Cleanup;
  1007. }
  1008. //
  1009. // If this is info level 1 and the requested attributes are
  1010. // different than the current attributes,
  1011. // Remember to change the attributes.
  1012. //
  1013. if ( Level == 1 &&
  1014. (*Entry)->NewAttributes != OldAttributes[OldIndex] ) {
  1015. (*Entry)->OldAttributes = OldAttributes[OldIndex];
  1016. (*Entry)->Action = SetAttributesMember;
  1017. //
  1018. // This is either info level 0 or the level 1 attributes
  1019. // are the same as the existing attributes.
  1020. //
  1021. } else {
  1022. (*Entry)->Action = IgnoreMember;
  1023. }
  1024. }
  1025. }
  1026. }
  1027. //
  1028. // Loop through the list adding all new members.
  1029. // We do this in a separate loop to minimize the damage that happens
  1030. // should we get an error and not be able to recover.
  1031. //
  1032. for ( CurEntry = MemberList; CurEntry != NULL ; CurEntry=CurEntry->Next ) {
  1033. if ( CurEntry->Action == AddMember ) {
  1034. Status = SamAddMemberToGroup( GroupHandle,
  1035. CurEntry->RelativeId,
  1036. CurEntry->NewAttributes );
  1037. if ( !NT_SUCCESS( Status ) ) {
  1038. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1039. NetpKdPrint((
  1040. "GrouppSetUsers: SamAddMemberToGroup returns %lX\n",
  1041. Status ));
  1042. }
  1043. NetStatus = NetpNtStatusToApiStatus( Status );
  1044. goto Cleanup;
  1045. }
  1046. CurEntry->Done = TRUE;
  1047. }
  1048. }
  1049. //
  1050. // Loop through the list deleting all old members and changing the
  1051. // attributes of all common members.
  1052. //
  1053. for ( CurEntry = MemberList; CurEntry != NULL ; CurEntry=CurEntry->Next ) {
  1054. if ( CurEntry->Action == RemoveMember ) {
  1055. Status = SamRemoveMemberFromGroup( GroupHandle,
  1056. CurEntry->RelativeId);
  1057. if ( !NT_SUCCESS( Status ) ) {
  1058. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1059. NetpKdPrint((
  1060. "GrouppSetUsers: SamRemoveMemberFromGroup returns %lX\n",
  1061. Status ));
  1062. }
  1063. NetStatus = NetpNtStatusToApiStatus( Status );
  1064. goto Cleanup;
  1065. }
  1066. } else if ( CurEntry->Action == SetAttributesMember ) {
  1067. Status = SamSetMemberAttributesOfGroup( GroupHandle,
  1068. CurEntry->RelativeId,
  1069. CurEntry->NewAttributes);
  1070. if ( !NT_SUCCESS( Status ) ) {
  1071. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1072. NetpKdPrint((
  1073. "GrouppSetUsers: SamSetMemberAttributesOfGroup returns %lX\n",
  1074. Status ));
  1075. }
  1076. NetStatus = NetpNtStatusToApiStatus( Status );
  1077. goto Cleanup;
  1078. }
  1079. }
  1080. CurEntry->Done = TRUE;
  1081. }
  1082. //
  1083. // Delete the group if requested to do so.
  1084. //
  1085. if ( DeleteGroup ) {
  1086. Status = SamDeleteGroup( GroupHandle );
  1087. if ( !NT_SUCCESS( Status ) ) {
  1088. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1089. NetpKdPrint(( "GrouppSetUsers: SamDeleteGroup returns %lX\n",
  1090. Status ));
  1091. }
  1092. NetStatus = NetpNtStatusToApiStatus( Status );
  1093. //
  1094. // Put the group memberships back the way they were.
  1095. //
  1096. goto Cleanup;
  1097. }
  1098. GroupHandle = NULL;
  1099. }
  1100. NetStatus = NERR_Success;
  1101. //
  1102. // Clean up.
  1103. //
  1104. Cleanup:
  1105. //
  1106. // Walk the member list cleaning up any damage we've done
  1107. //
  1108. for ( CurEntry = MemberList; CurEntry != NULL ; ) {
  1109. struct _MEMBER_DESCRIPTION *DelEntry;
  1110. if ( NetStatus != NERR_Success && CurEntry->Done ) {
  1111. switch (CurEntry->Action) {
  1112. case AddMember:
  1113. Status = SamRemoveMemberFromGroup( GroupHandle,
  1114. CurEntry->RelativeId );
  1115. NetpAssert( NT_SUCCESS(Status) );
  1116. break;
  1117. case RemoveMember:
  1118. Status = SamAddMemberToGroup( GroupHandle,
  1119. CurEntry->RelativeId,
  1120. CurEntry->OldAttributes );
  1121. NetpAssert( NT_SUCCESS(Status) );
  1122. break;
  1123. case SetAttributesMember:
  1124. Status = SamSetMemberAttributesOfGroup(
  1125. GroupHandle,
  1126. CurEntry->RelativeId,
  1127. CurEntry->OldAttributes );
  1128. NetpAssert( NT_SUCCESS(Status) );
  1129. break;
  1130. default:
  1131. break;
  1132. }
  1133. }
  1134. DelEntry = CurEntry;
  1135. CurEntry = CurEntry->Next;
  1136. NetpMemoryFree( DelEntry );
  1137. }
  1138. if ( NameStrings != NULL ) {
  1139. NetpMemoryFree( NameStrings );
  1140. }
  1141. if (NewRelativeIds != NULL) {
  1142. Status = SamFreeMemory( NewRelativeIds );
  1143. NetpAssert( NT_SUCCESS(Status) );
  1144. }
  1145. if (NewNameUse != NULL) {
  1146. Status = SamFreeMemory( NewNameUse );
  1147. NetpAssert( NT_SUCCESS(Status) );
  1148. }
  1149. if (OldNameUse != NULL) {
  1150. Status = SamFreeMemory( OldNameUse );
  1151. NetpAssert( NT_SUCCESS(Status) );
  1152. }
  1153. if (OldRelativeIds != NULL) {
  1154. Status = SamFreeMemory( OldRelativeIds );
  1155. NetpAssert( NT_SUCCESS(Status) );
  1156. }
  1157. if (OldAttributes != NULL) {
  1158. Status = SamFreeMemory( OldAttributes );
  1159. NetpAssert( NT_SUCCESS(Status) );
  1160. }
  1161. if (GroupHandle != NULL) {
  1162. (VOID) SamCloseHandle( GroupHandle );
  1163. }
  1164. UaspCloseDomain( DomainHandle );
  1165. if ( SamServerHandle != NULL ) {
  1166. (VOID) SamCloseHandle( SamServerHandle );
  1167. }
  1168. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1169. NetpKdPrint(( "GrouppSetUsers: returns %ld\n", NetStatus ));
  1170. }
  1171. return NetStatus;
  1172. } // GrouppSetUsers