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.

2135 lines
56 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. group.c
  5. Abstract:
  6. NetGroup API functions
  7. Author:
  8. Cliff Van Dyke (cliffv) 05-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. 17-Jan-1992 (madana)
  17. Added support to change group account name.
  18. 20-Jan-1992 (madana)
  19. Sundry API changes
  20. --*/
  21. #include <nt.h>
  22. #include <ntrtl.h>
  23. #include <nturtl.h>
  24. #undef DOMAIN_ALL_ACCESS // defined in both ntsam.h and ntwinapi.h
  25. #include <ntsam.h>
  26. #include <ntlsa.h>
  27. #include <windef.h>
  28. #include <winbase.h>
  29. #include <lmcons.h>
  30. #include <access.h>
  31. #include <align.h>
  32. #include <icanon.h>
  33. #include <lmapibuf.h>
  34. #include <lmaccess.h>
  35. #include <lmerr.h>
  36. #include <netdebug.h>
  37. #include <netlib.h>
  38. #include <netlibnt.h>
  39. #include <rpcutil.h>
  40. #include <rxgroup.h>
  41. #include <stddef.h>
  42. #include <uasp.h>
  43. #include <stdlib.h>
  44. /*lint -e614 */ /* Auto aggregate initializers need not be constant */
  45. // Lint complains about casts of one structure type to another.
  46. // That is done frequently in the code below.
  47. /*lint -e740 */ /* don't complain about unusual cast */ \
  48. NET_API_STATUS NET_API_FUNCTION
  49. NetGroupAdd(
  50. IN LPCWSTR ServerName OPTIONAL,
  51. IN DWORD Level,
  52. IN LPBYTE Buffer,
  53. OUT LPDWORD ParmError OPTIONAL // Name required by NetpSetParmError
  54. )
  55. /*++
  56. Routine Description:
  57. Create a group account in the user accounts database.
  58. Arguments:
  59. ServerName - A pointer to a string containing the name of the remote
  60. server on which the function is to execute. A NULL pointer
  61. or string specifies the local machine.
  62. Level - Level of information provided. Must be 0, 1 or 2.
  63. Buffer - A pointer to the buffer containing the group information
  64. structure.
  65. ParmError - Optional pointer to a DWORD to return the index of the
  66. first parameter in error when ERROR_INVALID_PARAMETER is returned.
  67. If NULL, the parameter is not returned on error.
  68. Return Value:
  69. Error code for the operation.
  70. --*/
  71. {
  72. LPWSTR GroupName;
  73. UNICODE_STRING GroupNameString;
  74. LPWSTR GroupComment;
  75. DWORD GroupAttributes;
  76. NET_API_STATUS NetStatus;
  77. NTSTATUS Status;
  78. SAM_HANDLE SamServerHandle = NULL;
  79. SAM_HANDLE DomainHandle = NULL;
  80. SAM_HANDLE GroupHandle;
  81. ULONG RelativeId;
  82. //
  83. // Initialize
  84. //
  85. NetpSetParmError( PARM_ERROR_NONE );
  86. //
  87. // Validate Level parameter and fields of structures.
  88. //
  89. switch (Level) {
  90. case 0:
  91. GroupName = ((PGROUP_INFO_0)Buffer)->grpi0_name;
  92. GroupComment = NULL;
  93. break;
  94. case 1:
  95. GroupName = ((PGROUP_INFO_1)Buffer)->grpi1_name;
  96. GroupComment = ((PGROUP_INFO_1)Buffer)->grpi1_comment;
  97. break;
  98. case 2:
  99. GroupName = ((PGROUP_INFO_2)Buffer)->grpi2_name;
  100. GroupComment = ((PGROUP_INFO_2)Buffer)->grpi2_comment;
  101. GroupAttributes = ((PGROUP_INFO_2)Buffer)->grpi2_attributes;
  102. break;
  103. case 3:
  104. GroupName = ((PGROUP_INFO_3)Buffer)->grpi3_name;
  105. GroupComment = ((PGROUP_INFO_3)Buffer)->grpi3_comment;
  106. GroupAttributes = ((PGROUP_INFO_3)Buffer)->grpi3_attributes;
  107. break;
  108. default:
  109. return ERROR_INVALID_LEVEL;
  110. }
  111. //
  112. // Don't allow creation of the group names that will confuse LM 2.x BDCs.
  113. //
  114. if ( UaspNameCompare( GroupName, L"users", NAMETYPE_GROUP ) == 0 ||
  115. UaspNameCompare( GroupName, L"guests", NAMETYPE_GROUP ) == 0 ||
  116. UaspNameCompare( GroupName, L"admins", NAMETYPE_GROUP ) == 0 ||
  117. UaspNameCompare( GroupName, L"local", NAMETYPE_GROUP ) == 0 ) {
  118. NetStatus = NERR_SpeGroupOp;
  119. goto Cleanup;
  120. }
  121. //
  122. // Connect to the SAM server
  123. //
  124. NetStatus = UaspOpenSam( ServerName,
  125. FALSE, // Don't try null session
  126. &SamServerHandle );
  127. if ( NetStatus != NERR_Success ) {
  128. IF_DEBUG( UAS_DEBUG_GROUP ) {
  129. NetpKdPrint(( "NetGroupAdd: Cannot UaspOpenSam %ld\n", NetStatus ));
  130. }
  131. goto Cleanup;
  132. }
  133. //
  134. // Open the Domain asking for DOMAIN_CREATE_GROUP access.
  135. //
  136. NetStatus = UaspOpenDomain( SamServerHandle,
  137. DOMAIN_CREATE_GROUP | DOMAIN_LOOKUP,
  138. TRUE, // Account Domain
  139. &DomainHandle,
  140. NULL); // DomainId
  141. if ( NetStatus != NERR_Success ) {
  142. IF_DEBUG( UAS_DEBUG_GROUP ) {
  143. NetpKdPrint(( "NetGroupAdd: Cannot UaspOpenDomain %ld\n", NetStatus ));
  144. }
  145. goto Cleanup;
  146. }
  147. //
  148. // Create the Group with the specified group name
  149. // (and a default security descriptor).
  150. //
  151. RtlInitUnicodeString( &GroupNameString, GroupName );
  152. Status = SamCreateGroupInDomain( DomainHandle,
  153. &GroupNameString,
  154. DELETE | GROUP_WRITE_ACCOUNT,
  155. &GroupHandle,
  156. &RelativeId );
  157. if ( !NT_SUCCESS(Status) ) {
  158. NetStatus = NetpNtStatusToApiStatus( Status );
  159. goto Cleanup;
  160. }
  161. //
  162. // Set the Admin Comment on the group.
  163. //
  164. if ( (Level == 1) || (Level == 2) || (Level == 3) ) {
  165. GROUP_ADM_COMMENT_INFORMATION AdminComment;
  166. RtlInitUnicodeString( &AdminComment.AdminComment, GroupComment );
  167. Status = SamSetInformationGroup( GroupHandle,
  168. GroupAdminCommentInformation,
  169. &AdminComment );
  170. if ( !NT_SUCCESS(Status) ) {
  171. NetStatus = NetpNtStatusToApiStatus( Status );
  172. Status = SamDeleteGroup( GroupHandle );
  173. NetpAssert( NT_SUCCESS(Status) );
  174. goto Cleanup;
  175. }
  176. }
  177. // Set the attributes on the group.
  178. //
  179. if ( (Level == 2) || (Level == 3) ) {
  180. GROUP_ATTRIBUTE_INFORMATION GroupAttributeInfo;
  181. GroupAttributeInfo.Attributes = GroupAttributes;
  182. Status = SamSetInformationGroup( GroupHandle,
  183. GroupAttributeInformation,
  184. &GroupAttributeInfo );
  185. if ( !NT_SUCCESS(Status) ) {
  186. NetStatus = NetpNtStatusToApiStatus( Status );
  187. Status = SamDeleteGroup( GroupHandle );
  188. NetpAssert( NT_SUCCESS(Status) );
  189. goto Cleanup;
  190. }
  191. }
  192. //
  193. // Close the created group.
  194. //
  195. (VOID) SamCloseHandle( GroupHandle );
  196. NetStatus = NERR_Success;
  197. //
  198. // Clean up
  199. //
  200. Cleanup:
  201. if ( DomainHandle != NULL ) {
  202. UaspCloseDomain( DomainHandle );
  203. }
  204. if ( SamServerHandle != NULL ) {
  205. (VOID) SamCloseHandle( SamServerHandle );
  206. }
  207. //
  208. // Handle downlevel.
  209. //
  210. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  211. NetStatus = RxNetGroupAdd( (LPWSTR) ServerName, Level, Buffer, ParmError );
  212. UASP_DOWNLEVEL_END;
  213. IF_DEBUG( UAS_DEBUG_GROUP ) {
  214. NetpKdPrint(( "NetGroupAdd: returns %ld\n", NetStatus ));
  215. }
  216. return NetStatus;
  217. } // NetGroupAdd
  218. NET_API_STATUS NET_API_FUNCTION
  219. NetGroupAddUser(
  220. IN LPCWSTR ServerName OPTIONAL,
  221. IN LPCWSTR GroupName,
  222. IN LPCWSTR UserName
  223. )
  224. /*++
  225. Routine Description:
  226. Give an existing user account membership in an existing group.
  227. Arguments:
  228. ServerName - A pointer to a string containing the name of the remote
  229. server on which the function is to execute. A NULL pointer
  230. or string specifies the local machine.
  231. GroupName - Name of the group to which the user is to be given membership.
  232. UserName - Name of the user to be given group membership.
  233. Return Value:
  234. Error code for the operation.
  235. --*/
  236. {
  237. NET_API_STATUS NetStatus;
  238. //
  239. // Call the routine shared by NetGroupAddUser and NetGroupDelUser
  240. //
  241. NetStatus = GrouppChangeMember( ServerName, GroupName, UserName, TRUE);
  242. //
  243. // Handle downlevel.
  244. //
  245. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  246. NetStatus = RxNetGroupAddUser( (LPWSTR) ServerName, (LPWSTR) GroupName, (LPWSTR) UserName );
  247. UASP_DOWNLEVEL_END( NetStatus );
  248. IF_DEBUG( UAS_DEBUG_GROUP ) {
  249. NetpKdPrint(( "NetGroupAddUser: returns %ld\n", NetStatus ));
  250. }
  251. return NetStatus;
  252. } // NetGroupAddUser
  253. NET_API_STATUS NET_API_FUNCTION
  254. NetGroupDel(
  255. IN LPCWSTR ServerName OPTIONAL,
  256. IN LPCWSTR GroupName
  257. )
  258. /*++
  259. Routine Description:
  260. Delete a Group
  261. Arguments:
  262. ServerName - A pointer to a string containing the name of the remote
  263. server on which the function is to execute. A NULL pointer
  264. or string specifies the local machine.
  265. GroupName - Name of the group to delete.
  266. Return Value:
  267. Error code for the operation.
  268. --*/
  269. {
  270. NET_API_STATUS NetStatus;
  271. //
  272. // Call a common routine to delete all of the memberships in this group
  273. // and then delete the group itself.
  274. //
  275. NetStatus = GrouppSetUsers( ServerName,
  276. GroupName,
  277. 0, // Level
  278. NULL, // No new members
  279. 0, // Number of members desired
  280. TRUE ); // Delete the group when done
  281. //
  282. // Handle downlevel.
  283. //
  284. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  285. NetStatus = RxNetGroupDel( (LPWSTR) ServerName, (LPWSTR) GroupName );
  286. UASP_DOWNLEVEL_END;
  287. IF_DEBUG( UAS_DEBUG_GROUP ) {
  288. NetpKdPrint(( "NetGroupDel: returns %ld\n", NetStatus ));
  289. }
  290. return NetStatus;
  291. } // NetGroupDel
  292. NET_API_STATUS NET_API_FUNCTION
  293. NetGroupDelUser(
  294. IN LPCWSTR ServerName OPTIONAL,
  295. IN LPCWSTR GroupName,
  296. IN LPCWSTR UserName
  297. )
  298. /*++
  299. Routine Description:
  300. Remove a user from a particular group.
  301. Arguments:
  302. ServerName - A pointer to a string containing the name of the remote
  303. server on which the function is to execute. A NULL pointer
  304. or string specifies the local machine.
  305. GroupName - Name of the group from which the user is to be removed.
  306. UserName - Name of the user to be removed from the group.
  307. Return Value:
  308. Error code for the operation.
  309. --*/
  310. {
  311. NET_API_STATUS NetStatus;
  312. //
  313. // Call the routine shared by NetGroupAddUser and NetGroupDelUser
  314. //
  315. NetStatus = GrouppChangeMember( ServerName, GroupName, UserName, FALSE );
  316. //
  317. // Handle downlevel.
  318. //
  319. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  320. NetStatus = RxNetGroupDelUser( (LPWSTR) ServerName, (LPWSTR) GroupName, (LPWSTR) UserName );
  321. UASP_DOWNLEVEL_END;
  322. return NetStatus;
  323. } // NetGroupDelUser
  324. NET_API_STATUS NET_API_FUNCTION
  325. NetGroupEnum(
  326. IN LPCWSTR ServerName OPTIONAL,
  327. IN DWORD Level,
  328. OUT LPBYTE *Buffer,
  329. IN DWORD PrefMaxLen,
  330. OUT LPDWORD EntriesRead,
  331. OUT LPDWORD EntriesLeft,
  332. IN OUT PDWORD_PTR ResumeHandle OPTIONAL
  333. )
  334. /*++
  335. Routine Description:
  336. Retrieve information about each group on a server.
  337. Arguments:
  338. ServerName - A pointer to a string containing the name of the remote
  339. server on which the function is to execute. A NULL pointer
  340. or string specifies the local machine.
  341. Level - Level of information required. 0, 1 and 2 are valid.
  342. Buffer - Returns a pointer to the return information structure.
  343. Caller must deallocate buffer using NetApiBufferFree.
  344. PrefMaxLen - Prefered maximum length of returned data.
  345. EntriesRead - Returns the actual enumerated element count.
  346. EntriesLeft - Returns the total entries available to be enumerated.
  347. ResumeHandle - Used to continue an existing search. The handle should
  348. be zero on the first call and left unchanged for subsequent calls.
  349. Return Value:
  350. Error code for the operation.
  351. --*/
  352. {
  353. NET_API_STATUS NetStatus;
  354. NTSTATUS Status;
  355. PDOMAIN_DISPLAY_GROUP SamQDIEnum; // Sam returned buffer
  356. PSAM_RID_ENUMERATION SamEnum;
  357. PGROUP_INFO_0 grpi0;
  358. PGROUP_INFO_0 grpi0_temp = NULL;
  359. SAM_HANDLE SamServerHandle = NULL;
  360. BUFFER_DESCRIPTOR BufferDescriptor;
  361. PDOMAIN_GENERAL_INFORMATION DomainGeneral;
  362. DWORD Mode = SAM_SID_COMPATIBILITY_ALL;
  363. //
  364. // Declare Opaque group enumeration handle.
  365. //
  366. struct _UAS_ENUM_HANDLE {
  367. SAM_HANDLE DomainHandle;
  368. ULONG SamEnumHandle; // Current Sam Enum Handle
  369. PDOMAIN_DISPLAY_GROUP SamQDIEnum; // Sam returned buffer
  370. PSAM_RID_ENUMERATION SamEnum;
  371. ULONG Index; // Index to current entry
  372. ULONG Count; // Total Number of entries
  373. ULONG TotalRemaining;
  374. BOOL SamAllDone; // True, if Sam has completed
  375. BOOL fUseSamQDI;
  376. } *UasEnumHandle = NULL;
  377. //
  378. // If this is a resume, get the resume handle that the caller passed in.
  379. //
  380. BufferDescriptor.Buffer = NULL;
  381. *EntriesRead = 0;
  382. *EntriesLeft = 0;
  383. *Buffer = NULL;
  384. if ( ARGUMENT_PRESENT( ResumeHandle ) && *ResumeHandle != 0 ) {
  385. /*lint -e511 */ /* Size incompatibility */
  386. UasEnumHandle = (struct _UAS_ENUM_HANDLE *) *ResumeHandle;
  387. /*lint +e511 */ /* Size incompatibility */
  388. //
  389. // If this is not a resume, allocate and initialize a resume handle.
  390. //
  391. } else {
  392. //
  393. // Allocate a resume handle.
  394. //
  395. UasEnumHandle = NetpMemoryAllocate( sizeof(struct _UAS_ENUM_HANDLE) );
  396. if ( UasEnumHandle == NULL ) {
  397. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  398. goto Cleanup;
  399. }
  400. //
  401. // Initialize all the fields in the newly allocated resume handle
  402. // to indicate that SAM has never yet been called.
  403. //
  404. UasEnumHandle->DomainHandle = NULL;
  405. UasEnumHandle->SamEnumHandle = 0;
  406. UasEnumHandle->SamQDIEnum = NULL;
  407. UasEnumHandle->SamEnum = NULL;
  408. UasEnumHandle->Index = 0;
  409. UasEnumHandle->Count = 0;
  410. UasEnumHandle->TotalRemaining = 0;
  411. UasEnumHandle->SamAllDone = FALSE;
  412. UasEnumHandle->fUseSamQDI = TRUE;
  413. //
  414. // Connect to the SAM server
  415. //
  416. NetStatus = UaspOpenSam( ServerName,
  417. FALSE, // Don't try null session
  418. &SamServerHandle );
  419. if ( NetStatus != NERR_Success ) {
  420. IF_DEBUG( UAS_DEBUG_GROUP ) {
  421. NetpKdPrint(( "NetGroupEnum: Cannot UaspOpenSam %ld\n", NetStatus ));
  422. }
  423. goto Cleanup;
  424. }
  425. //
  426. // Open the Domain.
  427. //
  428. NetStatus = UaspOpenDomain( SamServerHandle,
  429. DOMAIN_LOOKUP |
  430. DOMAIN_LIST_ACCOUNTS |
  431. DOMAIN_READ_OTHER_PARAMETERS,
  432. TRUE, // Account Domain
  433. &UasEnumHandle->DomainHandle,
  434. NULL );
  435. if ( NetStatus != NERR_Success ) {
  436. goto Cleanup;
  437. }
  438. //
  439. // Get the total number of groups from SAM
  440. //
  441. Status = SamQueryInformationDomain( UasEnumHandle->DomainHandle,
  442. DomainGeneralInformation,
  443. (PVOID *)&DomainGeneral );
  444. if ( !NT_SUCCESS(Status) ) {
  445. NetStatus = NetpNtStatusToApiStatus( Status );
  446. goto Cleanup;
  447. }
  448. UasEnumHandle->TotalRemaining = DomainGeneral->GroupCount;
  449. Status = SamFreeMemory( DomainGeneral );
  450. NetpAssert( NT_SUCCESS(Status) );
  451. }
  452. Status = SamGetCompatibilityMode(UasEnumHandle->DomainHandle,
  453. &Mode);
  454. if (NT_SUCCESS(Status)) {
  455. if ( (Mode == SAM_SID_COMPATIBILITY_STRICT)
  456. && ( Level == 2 ) ) {
  457. //
  458. // This info level returns a RID
  459. //
  460. Status = STATUS_NOT_SUPPORTED;
  461. }
  462. }
  463. if (!NT_SUCCESS(Status)) {
  464. NetStatus = NetpNtStatusToApiStatus( Status );
  465. goto Cleanup;
  466. }
  467. //
  468. // Loop for each group
  469. //
  470. // Each iteration of the loop below puts one more entry into the array
  471. // returned to the caller. The algorithm is split into 3 parts. The
  472. // first part checks to see if we need to retrieve more information from
  473. // SAM. We then get the description of several group from SAM in a single
  474. // call. The second part sees if there is room for this entry in the
  475. // buffer we'll return to the caller. If not, a larger buffer is allocated
  476. // for return to the caller. The third part puts the entry in the
  477. // buffer.
  478. //
  479. for ( ;; ) {
  480. DWORD FixedSize;
  481. DWORD Size;
  482. ULONG TotalAvail;
  483. ULONG TotalReturned;
  484. //
  485. // Get more group information from SAM
  486. //
  487. // Handle when we've already consumed all of the information
  488. // returned on a previous call to SAM. This is a 'while' rather
  489. // than an if to handle the case where SAM returns zero entries.
  490. //
  491. while ( UasEnumHandle->Index >= UasEnumHandle->Count ) {
  492. //
  493. // If we've already gotten everything from SAM,
  494. // return all done status to our caller.
  495. //
  496. if ( UasEnumHandle->SamAllDone ) {
  497. NetStatus = NERR_Success;
  498. goto Cleanup;
  499. }
  500. //
  501. // Free any previous buffer returned from SAM.
  502. //
  503. if ( UasEnumHandle->SamQDIEnum != NULL ) {
  504. Status = SamFreeMemory( UasEnumHandle->SamQDIEnum );
  505. NetpAssert( NT_SUCCESS(Status) );
  506. UasEnumHandle->SamQDIEnum = NULL;
  507. }
  508. //
  509. // Do the actual enumeration
  510. //
  511. if ( UasEnumHandle->fUseSamQDI )
  512. {
  513. Status = SamQueryDisplayInformation(
  514. UasEnumHandle->DomainHandle,
  515. DomainDisplayGroup,
  516. UasEnumHandle->SamEnumHandle,
  517. 0xffffffff, //query as many as PrefMaxLen can handle
  518. PrefMaxLen,
  519. &TotalAvail,
  520. &TotalReturned,
  521. &UasEnumHandle->Count,
  522. (PVOID *)&UasEnumHandle->SamQDIEnum );
  523. if ( !NT_SUCCESS( Status ) ) {
  524. if ( Status == STATUS_INVALID_INFO_CLASS ) {
  525. UasEnumHandle->fUseSamQDI = FALSE;
  526. } else {
  527. NetStatus = NetpNtStatusToApiStatus( Status );
  528. goto Cleanup;
  529. }
  530. }
  531. UasEnumHandle->SamEnumHandle += UasEnumHandle->Count;
  532. }
  533. if ( !UasEnumHandle->fUseSamQDI ) {
  534. Status = SamEnumerateGroupsInDomain(
  535. UasEnumHandle->DomainHandle,
  536. &UasEnumHandle->SamEnumHandle,
  537. (PVOID *)&UasEnumHandle->SamEnum,
  538. PrefMaxLen,
  539. &UasEnumHandle->Count );
  540. if ( !NT_SUCCESS( Status ) ) {
  541. NetStatus = NetpNtStatusToApiStatus( Status );
  542. goto Cleanup;
  543. }
  544. }
  545. //
  546. // Adjust TotalRemaining as we get better information
  547. //
  548. if (UasEnumHandle->TotalRemaining < UasEnumHandle->Count) {
  549. UasEnumHandle->TotalRemaining = UasEnumHandle->Count;
  550. }
  551. //
  552. // If SAM says there is more information, just ensure he returned
  553. // something to us on this call.
  554. //
  555. if ( Status == STATUS_MORE_ENTRIES ) {
  556. if ( UasEnumHandle->Count == 0 ) {
  557. NetStatus = NERR_BufTooSmall;
  558. goto Cleanup;
  559. }
  560. //
  561. // If SAM says he's returned all of the information,
  562. // remember not to ask SAM for more.
  563. //
  564. } else {
  565. UasEnumHandle->SamAllDone = TRUE;
  566. }
  567. UasEnumHandle->Index = 0;
  568. }
  569. //
  570. // ASSERT: UasEnumHandle identifies the next entry to return
  571. // from SAM.
  572. //
  573. if ( UasEnumHandle->fUseSamQDI ) {
  574. SamQDIEnum = &UasEnumHandle->SamQDIEnum[UasEnumHandle->Index];
  575. } else {
  576. SamEnum = &UasEnumHandle->SamEnum[UasEnumHandle->Index];
  577. }
  578. //
  579. // Place this entry into the return buffer.
  580. //
  581. // Determine the size of the data passed back to the caller
  582. //
  583. switch (Level) {
  584. case 0:
  585. FixedSize = sizeof(GROUP_INFO_0);
  586. Size = sizeof(GROUP_INFO_0) +
  587. (UasEnumHandle->fUseSamQDI ? SamQDIEnum->Group.Length : SamEnum->Name.Length) +
  588. sizeof(WCHAR);
  589. break;
  590. case 1:
  591. FixedSize = sizeof(GROUP_INFO_1);
  592. if ( UasEnumHandle->fUseSamQDI ) {
  593. Size = sizeof(GROUP_INFO_1) +
  594. SamQDIEnum->Group.Length + sizeof(WCHAR) +
  595. SamQDIEnum->Comment.Length + sizeof(WCHAR);
  596. } else {
  597. NetStatus = GrouppGetInfo( UasEnumHandle->DomainHandle,
  598. SamEnum->RelativeId,
  599. Level,
  600. (PVOID *)&grpi0_temp);
  601. if ( NetStatus != NERR_Success ) {
  602. goto Cleanup;
  603. }
  604. Size = sizeof(GROUP_INFO_1) +
  605. SamEnum->Name.Length + sizeof(WCHAR) +
  606. (wcslen(((PGROUP_INFO_1)grpi0_temp)->grpi1_comment) +
  607. 1) * sizeof(WCHAR);
  608. }
  609. break;
  610. case 2:
  611. FixedSize = sizeof(GROUP_INFO_2);
  612. if ( UasEnumHandle->fUseSamQDI ) {
  613. Size = sizeof(GROUP_INFO_2) +
  614. SamQDIEnum->Group.Length + sizeof(WCHAR) +
  615. SamQDIEnum->Comment.Length + sizeof(WCHAR);
  616. } else {
  617. NetStatus = GrouppGetInfo( UasEnumHandle->DomainHandle,
  618. SamEnum->RelativeId,
  619. Level,
  620. (PVOID *)&grpi0_temp);
  621. if ( NetStatus != NERR_Success ) {
  622. goto Cleanup;
  623. }
  624. Size = sizeof(GROUP_INFO_2) +
  625. SamEnum->Name.Length + sizeof(WCHAR) +
  626. (wcslen(((PGROUP_INFO_2)grpi0_temp)->grpi2_comment) +
  627. 1) * sizeof(WCHAR);
  628. }
  629. break;
  630. default:
  631. NetStatus = ERROR_INVALID_LEVEL;
  632. goto Cleanup;
  633. }
  634. //
  635. // Ensure there is buffer space for this information.
  636. //
  637. Size = ROUND_UP_COUNT( Size, ALIGN_WCHAR );
  638. NetStatus = NetpAllocateEnumBuffer(
  639. &BufferDescriptor,
  640. FALSE, // Not a 'get' operation
  641. PrefMaxLen,
  642. Size,
  643. GrouppRelocationRoutine,
  644. Level );
  645. if (NetStatus != NERR_Success) {
  646. goto Cleanup;
  647. }
  648. //
  649. // Fill in the information. The array of fixed entries is
  650. // placed at the beginning of the allocated buffer. The strings
  651. // pointed to by these fixed entries are allocated starting at
  652. // the end of the allocate buffer.
  653. //
  654. //
  655. // Copy the common group name
  656. //
  657. NetpAssert( offsetof( GROUP_INFO_0, grpi0_name ) ==
  658. offsetof( GROUP_INFO_1, grpi1_name ) );
  659. NetpAssert( offsetof( GROUP_INFO_1, grpi1_name ) ==
  660. offsetof( GROUP_INFO_2, grpi2_name ) );
  661. NetpAssert( offsetof( GROUP_INFO_1, grpi1_comment ) ==
  662. offsetof( GROUP_INFO_2, grpi2_comment ) );
  663. grpi0 = (PGROUP_INFO_0)(BufferDescriptor.FixedDataEnd);
  664. BufferDescriptor.FixedDataEnd += FixedSize;
  665. //
  666. // Fill in the Level dependent fields
  667. //
  668. switch ( Level ) {
  669. case 2:
  670. if (Mode == SAM_SID_COMPATIBILITY_ALL) {
  671. ((PGROUP_INFO_2)grpi0)->grpi2_group_id =
  672. UasEnumHandle->fUseSamQDI
  673. ? SamQDIEnum->Rid
  674. : ((PGROUP_INFO_2)grpi0_temp)->grpi2_group_id;
  675. } else {
  676. ((PGROUP_INFO_2)grpi0)->grpi2_group_id = 0;
  677. }
  678. ((PGROUP_INFO_2)grpi0)->grpi2_attributes =
  679. UasEnumHandle->fUseSamQDI
  680. ? SamQDIEnum->Attributes
  681. : ((PGROUP_INFO_2)grpi0_temp)->grpi2_attributes;
  682. /* FALL THROUGH FOR THE OTHER FIELDS */
  683. case 1:
  684. if ( !NetpCopyStringToBuffer(
  685. UasEnumHandle->fUseSamQDI
  686. ? SamQDIEnum->Comment.Buffer
  687. : ((PGROUP_INFO_1)grpi0_temp)->grpi1_comment,
  688. UasEnumHandle->fUseSamQDI
  689. ? SamQDIEnum->Comment.Length/sizeof(WCHAR)
  690. : wcslen(((PGROUP_INFO_1)grpi0_temp)->grpi1_comment),
  691. BufferDescriptor.FixedDataEnd,
  692. (LPWSTR *)&BufferDescriptor.EndOfVariableData,
  693. &((PGROUP_INFO_1)grpi0)->grpi1_comment) ) {
  694. NetStatus = NERR_InternalError;
  695. goto Cleanup;
  696. }
  697. if ( !UasEnumHandle->fUseSamQDI ) {
  698. MIDL_user_free( grpi0_temp );
  699. grpi0_temp = NULL;
  700. }
  701. /* FALL THROUGH FOR THE NAME FIELD */
  702. case 0:
  703. if ( !NetpCopyStringToBuffer(
  704. UasEnumHandle->fUseSamQDI
  705. ? SamQDIEnum->Group.Buffer
  706. : SamEnum->Name.Buffer,
  707. UasEnumHandle->fUseSamQDI
  708. ? SamQDIEnum->Group.Length/sizeof(WCHAR)
  709. : SamEnum->Name.Length/sizeof(WCHAR),
  710. BufferDescriptor.FixedDataEnd,
  711. (LPWSTR *)&BufferDescriptor.EndOfVariableData,
  712. &(grpi0->grpi0_name))){
  713. NetStatus = NERR_InternalError;
  714. goto Cleanup;
  715. }
  716. break;
  717. default:
  718. NetStatus = ERROR_INVALID_LEVEL;
  719. goto Cleanup;
  720. }
  721. //
  722. // ASSERT: The current entry has been completely copied to the
  723. // return buffer.
  724. //
  725. (*EntriesRead)++;
  726. UasEnumHandle->Index ++;
  727. UasEnumHandle->TotalRemaining --;
  728. }
  729. //
  730. // Clean up.
  731. //
  732. Cleanup:
  733. //
  734. // Free any locally used resources.
  735. //
  736. if ( grpi0_temp != NULL ) {
  737. MIDL_user_free( grpi0_temp );
  738. }
  739. //
  740. // Set EntriesLeft to the number left to return plus those that
  741. // we returned on this call.
  742. //
  743. if ( UasEnumHandle != NULL ) {
  744. *EntriesLeft = UasEnumHandle->TotalRemaining + *EntriesRead;
  745. }
  746. //
  747. // If we're done or the caller doesn't want an enumeration handle,
  748. // free the enumeration handle.
  749. //
  750. if ( NetStatus != ERROR_MORE_DATA || !ARGUMENT_PRESENT( ResumeHandle ) ) {
  751. if ( UasEnumHandle != NULL ) {
  752. if ( UasEnumHandle->DomainHandle != NULL ) {
  753. UaspCloseDomain( UasEnumHandle->DomainHandle );
  754. }
  755. if ( UasEnumHandle->SamQDIEnum != NULL ) {
  756. Status = SamFreeMemory( UasEnumHandle->SamQDIEnum );
  757. NetpAssert( NT_SUCCESS(Status) );
  758. }
  759. if ( UasEnumHandle->SamEnum != NULL ) {
  760. Status = SamFreeMemory( UasEnumHandle->SamEnum );
  761. NetpAssert( NT_SUCCESS(Status) );
  762. }
  763. NetpMemoryFree( UasEnumHandle );
  764. UasEnumHandle = NULL;
  765. }
  766. }
  767. //
  768. // If we're not returning data to the caller,
  769. // free the return buffer.
  770. //
  771. if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
  772. if ( BufferDescriptor.Buffer != NULL ) {
  773. MIDL_user_free( BufferDescriptor.Buffer );
  774. BufferDescriptor.Buffer = NULL;
  775. }
  776. *EntriesRead = 0;
  777. *EntriesLeft = 0;
  778. }
  779. //
  780. // Set the output parameters
  781. //
  782. *Buffer = BufferDescriptor.Buffer;
  783. if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
  784. *ResumeHandle = (DWORD_PTR) UasEnumHandle;
  785. }
  786. if ( SamServerHandle != NULL ) {
  787. (VOID) SamCloseHandle( SamServerHandle );
  788. }
  789. //
  790. // Handle downlevel.
  791. //
  792. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  793. NetStatus = RxNetGroupEnum( (LPWSTR) ServerName,
  794. Level,
  795. Buffer,
  796. PrefMaxLen,
  797. EntriesRead,
  798. EntriesLeft,
  799. ResumeHandle );
  800. UASP_DOWNLEVEL_END;
  801. IF_DEBUG( UAS_DEBUG_GROUP ) {
  802. NetpKdPrint(( "NetGroupEnum: returns %ld\n", NetStatus ));
  803. }
  804. return NetStatus;
  805. } // NetGroupEnum
  806. NET_API_STATUS NET_API_FUNCTION
  807. NetGroupGetInfo(
  808. IN LPCWSTR ServerName OPTIONAL,
  809. IN LPCWSTR GroupName,
  810. IN DWORD Level,
  811. OUT LPBYTE *Buffer
  812. )
  813. /*++
  814. Routine Description:
  815. Retrieve information about a particular group.
  816. Arguments:
  817. ServerName - A pointer to a string containing the name of the remote
  818. server on which the function is to execute. A NULL pointer
  819. or string specifies the local machine.
  820. GroupName - Name of the group to get information about.
  821. Level - Level of information required. 0, 1 and 2 are valid.
  822. Buffer - Returns a pointer to the return information structure.
  823. Caller must deallocate buffer using NetApiBufferFree.
  824. Return Value:
  825. Error code for the operation.
  826. --*/
  827. {
  828. NET_API_STATUS NetStatus;
  829. SAM_HANDLE SamServerHandle = NULL;
  830. SAM_HANDLE DomainHandle = NULL;
  831. ULONG RelativeId; // Relative Id of the group
  832. //
  833. // Connect to the SAM server
  834. //
  835. NetStatus = UaspOpenSam( ServerName,
  836. FALSE, // Don't try null session
  837. &SamServerHandle );
  838. if ( NetStatus != NERR_Success ) {
  839. IF_DEBUG( UAS_DEBUG_GROUP ) {
  840. NetpKdPrint(( "NetGroupGetInfo: Cannot UaspOpenSam %ld\n", NetStatus ));
  841. }
  842. goto Cleanup;
  843. }
  844. //
  845. // Open the Domain
  846. //
  847. NetStatus = UaspOpenDomain( SamServerHandle,
  848. DOMAIN_LOOKUP,
  849. TRUE, // Account Domain
  850. &DomainHandle,
  851. NULL); // DomainId
  852. if ( NetStatus != NERR_Success ) {
  853. goto Cleanup;
  854. }
  855. //
  856. // Validate the group name and get the relative ID.
  857. //
  858. NetStatus = GrouppOpenGroup( DomainHandle,
  859. 0, // DesiredAccess
  860. GroupName,
  861. NULL, // GroupHandle
  862. &RelativeId );
  863. if (NetStatus != NERR_Success ) {
  864. goto Cleanup;
  865. }
  866. //
  867. // Get the Information about the group.
  868. //
  869. NetStatus = GrouppGetInfo( DomainHandle,
  870. RelativeId,
  871. Level,
  872. (PVOID *)Buffer);
  873. //
  874. // Clean up.
  875. //
  876. Cleanup:
  877. UaspCloseDomain( DomainHandle );
  878. if ( SamServerHandle != NULL ) {
  879. (VOID) SamCloseHandle( SamServerHandle );
  880. }
  881. //
  882. // Handle downlevel.
  883. //
  884. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  885. NetStatus = RxNetGroupGetInfo( (LPWSTR)ServerName, (LPWSTR)GroupName, Level, Buffer );
  886. UASP_DOWNLEVEL_END;
  887. IF_DEBUG( UAS_DEBUG_GROUP ) {
  888. NetpKdPrint(( "NetGroupGetInfo: returns %ld\n", NetStatus ));
  889. }
  890. return NetStatus;
  891. } // NetGroupGetInfo
  892. NET_API_STATUS NET_API_FUNCTION
  893. NetGroupGetUsers(
  894. IN LPCWSTR ServerName OPTIONAL,
  895. IN LPCWSTR GroupName,
  896. IN DWORD Level,
  897. OUT LPBYTE *Buffer,
  898. IN DWORD PrefMaxLen,
  899. OUT LPDWORD EntriesRead,
  900. OUT LPDWORD EntriesLeft,
  901. IN OUT PDWORD_PTR ResumeHandle
  902. )
  903. /*++
  904. Routine Description:
  905. Enumerate the users which are members of a particular group.
  906. Arguments:
  907. ServerName - A pointer to a string containing the name of the remote
  908. server on which the function is to execute. A NULL pointer
  909. or string specifies the local machine.
  910. GroupName - The name of the group whose members are to be listed.
  911. Level - Level of information required. 0 and 1 are valid.
  912. Buffer - Returns a pointer to the return information structure.
  913. Caller must deallocate buffer using NetApiBufferFree.
  914. PrefMaxLen - Prefered maximum length of returned data.
  915. EntriesRead - Returns the actual enumerated element count.
  916. EntriesLeft - Returns the total entries available to be enumerated.
  917. ResumeHandle - Used to continue an existing search. The handle should
  918. be zero on the first call and left unchanged for subsequent calls.
  919. Return Value:
  920. Error code for the operation.
  921. --*/
  922. {
  923. NET_API_STATUS NetStatus;
  924. NTSTATUS Status;
  925. NTSTATUS Status2;
  926. DWORD FixedSize; // The fixed size of each new entry.
  927. DWORD Size;
  928. BUFFER_DESCRIPTOR BufferDescriptor;
  929. PGROUP_USERS_INFO_0 grui0;
  930. SAM_HANDLE SamServerHandle = NULL;
  931. //
  932. // Declare Opaque group member enumeration handle.
  933. //
  934. struct _UAS_ENUM_HANDLE {
  935. SAM_HANDLE DomainHandle;
  936. SAM_HANDLE GroupHandle;
  937. PUNICODE_STRING Names; // Names of each member
  938. PSID_NAME_USE NameUse; // Usage of each member
  939. PULONG Attributes; // Attributes of each member
  940. ULONG Index; // Index to current entry
  941. ULONG Count; // Total Number of entries
  942. } *UasEnumHandle = NULL;
  943. //
  944. // Validate Parameters
  945. //
  946. BufferDescriptor.Buffer = NULL;
  947. *Buffer = NULL;
  948. *EntriesRead = 0;
  949. *EntriesLeft = 0;
  950. switch (Level) {
  951. case 0:
  952. FixedSize = sizeof(GROUP_USERS_INFO_0);
  953. break;
  954. case 1:
  955. FixedSize = sizeof(GROUP_USERS_INFO_1);
  956. break;
  957. default:
  958. NetStatus = ERROR_INVALID_LEVEL;
  959. goto Cleanup;
  960. }
  961. //
  962. // If this is a resume, get the resume handle that the caller passed in.
  963. //
  964. if ( ARGUMENT_PRESENT( ResumeHandle ) && *ResumeHandle != 0 ) {
  965. /*lint -e511 */ /* Size incompatibility */
  966. UasEnumHandle = (struct _UAS_ENUM_HANDLE *) *ResumeHandle;
  967. /*lint +e511 */ /* Size incompatibility */
  968. //
  969. // If this is not a resume, allocate and initialize a resume handle.
  970. //
  971. } else {
  972. PULONG MemberIds; // Member IDs returned from SAM
  973. //
  974. // Allocate a resume handle.
  975. //
  976. UasEnumHandle = NetpMemoryAllocate( sizeof(struct _UAS_ENUM_HANDLE) );
  977. if ( UasEnumHandle == NULL ) {
  978. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  979. goto Cleanup;
  980. }
  981. //
  982. // Initialize all the fields in the newly allocated resume handle
  983. // to indicate that SAM has never yet been called.
  984. //
  985. UasEnumHandle->DomainHandle = NULL;
  986. UasEnumHandle->GroupHandle = NULL;
  987. UasEnumHandle->Names = NULL;
  988. UasEnumHandle->NameUse = NULL;
  989. UasEnumHandle->Attributes = NULL;
  990. UasEnumHandle->Index = 0;
  991. UasEnumHandle->Count = 0;
  992. //
  993. // Connect to the SAM server
  994. //
  995. NetStatus = UaspOpenSam( ServerName,
  996. FALSE, // Don't try null session
  997. &SamServerHandle );
  998. if ( NetStatus != NERR_Success ) {
  999. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1000. NetpKdPrint(( "NetGroupGetUsers: Cannot UaspOpenSam %ld\n", NetStatus ));
  1001. }
  1002. goto Cleanup;
  1003. }
  1004. //
  1005. // Open the Domain
  1006. //
  1007. NetStatus = UaspOpenDomain( SamServerHandle,
  1008. DOMAIN_LOOKUP,
  1009. TRUE, // Account Domain
  1010. &UasEnumHandle->DomainHandle,
  1011. NULL );
  1012. if ( NetStatus != NERR_Success ) {
  1013. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1014. NetpKdPrint((
  1015. "NetGroupGetUsers: UaspOpenDomain returns %ld\n",
  1016. NetStatus ));
  1017. }
  1018. goto Cleanup;
  1019. }
  1020. //
  1021. // Open the group asking for GROUP_LIST_MEMBER access.
  1022. //
  1023. NetStatus = GrouppOpenGroup( UasEnumHandle->DomainHandle,
  1024. GROUP_LIST_MEMBERS,
  1025. GroupName,
  1026. &UasEnumHandle->GroupHandle,
  1027. NULL ); // Relative ID
  1028. if ( NetStatus != NERR_Success ) {
  1029. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1030. NetpKdPrint((
  1031. "NetGroupGetUsers: GrouppOpenGroup returns %ld\n",
  1032. NetStatus ));
  1033. }
  1034. goto Cleanup;
  1035. }
  1036. //
  1037. // Get the group membership information from SAM
  1038. //
  1039. Status = SamGetMembersInGroup(
  1040. UasEnumHandle->GroupHandle,
  1041. &MemberIds,
  1042. &UasEnumHandle->Attributes,
  1043. &UasEnumHandle->Count );
  1044. if ( !NT_SUCCESS( Status ) ) {
  1045. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1046. NetpKdPrint((
  1047. "NetGroupGetUsers: SamGetMembersInGroup returned %lX\n",
  1048. Status ));
  1049. }
  1050. NetStatus = NetpNtStatusToApiStatus( Status );
  1051. goto Cleanup;
  1052. }
  1053. if ( UasEnumHandle->Count == 0 ) {
  1054. NetStatus = NERR_Success;
  1055. goto Cleanup;
  1056. }
  1057. //
  1058. // Determine the names and name usage for all the returned
  1059. // relative Ids.
  1060. //
  1061. Status = SamLookupIdsInDomain( UasEnumHandle->DomainHandle,
  1062. UasEnumHandle->Count,
  1063. MemberIds,
  1064. &UasEnumHandle->Names,
  1065. &UasEnumHandle->NameUse );
  1066. Status2 = SamFreeMemory( MemberIds );
  1067. NetpAssert( NT_SUCCESS(Status2) );
  1068. if ( !NT_SUCCESS( Status ) ) {
  1069. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1070. NetpKdPrint((
  1071. "NetGroupGetUsers: SamLookupIdsInDomain returned %lX\n",
  1072. Status ));
  1073. }
  1074. if ( Status == STATUS_NONE_MAPPED ) {
  1075. NetStatus = NERR_GroupNotFound;
  1076. goto Cleanup;
  1077. }
  1078. NetStatus = NetpNtStatusToApiStatus( Status );
  1079. goto Cleanup;
  1080. }
  1081. }
  1082. //
  1083. // Loop for each member
  1084. //
  1085. while ( UasEnumHandle->Index < UasEnumHandle->Count ) {
  1086. //
  1087. // ASSERT: UasEnumHandle identifies the next entry to return
  1088. // from SAM.
  1089. //
  1090. //
  1091. // Ignore members which aren't a user.
  1092. //
  1093. if ( UasEnumHandle->NameUse[UasEnumHandle->Index] != SidTypeUser ) {
  1094. UasEnumHandle->Index ++;
  1095. continue;
  1096. }
  1097. //
  1098. // Place this entry into the return buffer.
  1099. // Compute the total size of this entry.
  1100. //
  1101. Size = FixedSize +
  1102. UasEnumHandle->Names[UasEnumHandle->Index].Length + sizeof(WCHAR);
  1103. //
  1104. // Ensure there is buffer space for this information.
  1105. //
  1106. Size = ROUND_UP_COUNT( Size, ALIGN_WCHAR );
  1107. NetStatus = NetpAllocateEnumBuffer(
  1108. &BufferDescriptor,
  1109. FALSE, // Not a 'get' operation
  1110. PrefMaxLen,
  1111. Size,
  1112. GrouppMemberRelocationRoutine,
  1113. Level );
  1114. if (NetStatus != NERR_Success) {
  1115. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1116. NetpKdPrint((
  1117. "NetGroupGetUsers: NetpAllocateEnumBuffer returns %ld\n",
  1118. NetStatus ));
  1119. }
  1120. goto Cleanup;
  1121. }
  1122. //
  1123. // Copy the common member name
  1124. //
  1125. NetpAssert( offsetof( GROUP_USERS_INFO_0, grui0_name ) ==
  1126. offsetof( GROUP_USERS_INFO_1, grui1_name ) );
  1127. grui0 = (PGROUP_USERS_INFO_0)BufferDescriptor.FixedDataEnd;
  1128. BufferDescriptor.FixedDataEnd += FixedSize;
  1129. if ( ! NetpCopyStringToBuffer(
  1130. UasEnumHandle->Names[UasEnumHandle->Index].Buffer,
  1131. UasEnumHandle->Names[UasEnumHandle->Index].Length
  1132. /sizeof(WCHAR),
  1133. BufferDescriptor.FixedDataEnd,
  1134. (LPWSTR *)&BufferDescriptor.EndOfVariableData,
  1135. &grui0->grui0_name) ) {
  1136. NetStatus = NERR_InternalError;
  1137. goto Cleanup;
  1138. }
  1139. //
  1140. // Fill in the Level dependent fields
  1141. //
  1142. switch ( Level ) {
  1143. case 0:
  1144. break;
  1145. case 1:
  1146. //
  1147. // Return the attributes for this particular membership
  1148. //
  1149. ((PGROUP_USERS_INFO_1)grui0)->grui1_attributes =
  1150. UasEnumHandle->Attributes[UasEnumHandle->Index];
  1151. break;
  1152. default:
  1153. NetStatus = ERROR_INVALID_LEVEL;
  1154. goto Cleanup;
  1155. }
  1156. //
  1157. // ASSERT: The current entry has been completely copied to the
  1158. // return buffer.
  1159. //
  1160. UasEnumHandle->Index ++;
  1161. (*EntriesRead)++;
  1162. }
  1163. //
  1164. // All entries have be returned to the caller.
  1165. //
  1166. NetStatus = NERR_Success;
  1167. //
  1168. // Clean up.
  1169. //
  1170. Cleanup:
  1171. //
  1172. // Set EntriesLeft to the number left to return plus those that
  1173. // we returned on this call.
  1174. //
  1175. if ( UasEnumHandle != NULL ) {
  1176. *EntriesLeft = (UasEnumHandle->Count - UasEnumHandle->Index)
  1177. + *EntriesRead;
  1178. }
  1179. //
  1180. // If we're done or the caller doesn't want an enumeration handle,
  1181. // free the enumeration handle.
  1182. //
  1183. if ( NetStatus != ERROR_MORE_DATA || !ARGUMENT_PRESENT( ResumeHandle ) ) {
  1184. if ( UasEnumHandle != NULL ) {
  1185. if ( UasEnumHandle->GroupHandle != NULL ) {
  1186. (VOID) SamCloseHandle( UasEnumHandle->GroupHandle );
  1187. }
  1188. if ( UasEnumHandle->DomainHandle != NULL ) {
  1189. UaspCloseDomain( UasEnumHandle->DomainHandle );
  1190. }
  1191. if ( UasEnumHandle->NameUse != NULL ) {
  1192. Status = SamFreeMemory( UasEnumHandle->NameUse );
  1193. NetpAssert( NT_SUCCESS(Status) );
  1194. }
  1195. if ( UasEnumHandle->Names != NULL ) {
  1196. Status = SamFreeMemory( UasEnumHandle->Names );
  1197. NetpAssert( NT_SUCCESS(Status) );
  1198. }
  1199. if ( UasEnumHandle->Attributes != NULL ) {
  1200. Status = SamFreeMemory( UasEnumHandle->Attributes );
  1201. NetpAssert( NT_SUCCESS(Status) );
  1202. }
  1203. NetpMemoryFree( UasEnumHandle );
  1204. UasEnumHandle = NULL;
  1205. }
  1206. }
  1207. //
  1208. // If we're not returning data to the caller,
  1209. // free the return buffer.
  1210. //
  1211. if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
  1212. if ( BufferDescriptor.Buffer != NULL ) {
  1213. MIDL_user_free( BufferDescriptor.Buffer );
  1214. }
  1215. BufferDescriptor.Buffer = NULL;
  1216. }
  1217. //
  1218. // Set the output parameters
  1219. //
  1220. *Buffer = BufferDescriptor.Buffer;
  1221. if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
  1222. NetpAssert( sizeof(UasEnumHandle) <= sizeof(DWORD_PTR) );
  1223. *ResumeHandle = (DWORD_PTR) UasEnumHandle;
  1224. }
  1225. if ( SamServerHandle != NULL ) {
  1226. (VOID) SamCloseHandle( SamServerHandle );
  1227. }
  1228. //
  1229. // Handle downlevel.
  1230. //
  1231. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  1232. NetStatus = RxNetGroupGetUsers( (LPWSTR) ServerName,
  1233. (LPWSTR) GroupName,
  1234. Level,
  1235. Buffer,
  1236. PrefMaxLen,
  1237. EntriesRead,
  1238. EntriesLeft,
  1239. ResumeHandle );
  1240. UASP_DOWNLEVEL_END;
  1241. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1242. NetpKdPrint(( "NetGroupGetUsers: returns %ld\n", NetStatus ));
  1243. }
  1244. return NetStatus;
  1245. } // NetGroupGetUsers
  1246. NET_API_STATUS NET_API_FUNCTION
  1247. NetGroupSetInfo(
  1248. IN LPCWSTR ServerName OPTIONAL,
  1249. IN LPCWSTR GroupName,
  1250. IN DWORD Level,
  1251. IN LPBYTE Buffer,
  1252. OUT LPDWORD ParmError OPTIONAL // Name required by NetpSetParmError
  1253. )
  1254. /*++
  1255. Routine Description:
  1256. Set the parameters on a group account in the user accounts database.
  1257. Arguments:
  1258. ServerName - A pointer to a string containing the name of the remote
  1259. server on which the function is to execute. A NULL pointer
  1260. or string specifies the local machine.
  1261. GroupName - Name of the group to modify.
  1262. Level - Level of information provided. Must be 0, 1, 2, 1002 or 1005.
  1263. Buffer - A pointer to the buffer containing the group information
  1264. structure.
  1265. ParmError - Optional pointer to a DWORD to return the index of the
  1266. first parameter in error when ERROR_INVALID_PARAMETER is returned.
  1267. If NULL, the parameter is not returned on error.
  1268. Return Value:
  1269. Error code for the operation.
  1270. --*/
  1271. {
  1272. NET_API_STATUS NetStatus;
  1273. NTSTATUS Status;
  1274. SAM_HANDLE SamServerHandle = NULL;
  1275. SAM_HANDLE DomainHandle = NULL;
  1276. SAM_HANDLE GroupHandle = NULL;
  1277. //
  1278. // Initialize
  1279. //
  1280. NetpSetParmError( PARM_ERROR_NONE );
  1281. //
  1282. // Connect to the SAM server
  1283. //
  1284. NetStatus = UaspOpenSam( ServerName,
  1285. FALSE, // Don't try null session
  1286. &SamServerHandle );
  1287. if ( NetStatus != NERR_Success ) {
  1288. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1289. NetpKdPrint(( "NetGroupSetInfo: Cannot UaspOpenSam %ld\n", NetStatus ));
  1290. }
  1291. goto Cleanup;
  1292. }
  1293. //
  1294. // Open the Domain
  1295. //
  1296. NetStatus = UaspOpenDomain( SamServerHandle,
  1297. DOMAIN_LOOKUP,
  1298. TRUE, // Account Domain
  1299. &DomainHandle,
  1300. NULL); // DomainId
  1301. if ( NetStatus != NERR_Success ) {
  1302. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1303. NetpKdPrint(( "NetGroupSetInfo: UaspOpenDomain returns %ld\n",
  1304. NetStatus ));
  1305. }
  1306. goto Cleanup;
  1307. }
  1308. //
  1309. // Open the group
  1310. //
  1311. NetStatus = GrouppOpenGroup( DomainHandle,
  1312. GROUP_WRITE_ACCOUNT|GROUP_READ_INFORMATION,
  1313. GroupName,
  1314. &GroupHandle,
  1315. NULL ); // Relative ID
  1316. if ( NetStatus != NERR_Success ) {
  1317. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1318. NetpKdPrint(( "NetGroupSetInfo: GrouppOpenGroup returns %ld\n",
  1319. NetStatus ));
  1320. }
  1321. goto Cleanup;
  1322. }
  1323. //
  1324. // Change the group
  1325. //
  1326. switch (Level) {
  1327. //
  1328. // changing group name
  1329. //
  1330. case 0:
  1331. {
  1332. LPWSTR NewGroupName;
  1333. GROUP_NAME_INFORMATION NewSamGroupName;
  1334. NewGroupName = ((PGROUP_INFO_0)Buffer)->grpi0_name;
  1335. if (NewGroupName == NULL) {
  1336. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1337. NetpKdPrint(( "NetGroupSetInfo: Group Name is NULL\n" ));
  1338. }
  1339. NetStatus = NERR_Success;
  1340. goto Cleanup;
  1341. }
  1342. //
  1343. // Validate the new group name
  1344. //
  1345. RtlInitUnicodeString( &NewSamGroupName.Name, NewGroupName );
  1346. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1347. NetpKdPrint(( "NetGroupSetInfo: Renaming Group Account to %wZ\n",
  1348. &NewSamGroupName.Name));
  1349. }
  1350. Status = SamSetInformationGroup( GroupHandle,
  1351. GroupNameInformation,
  1352. &NewSamGroupName );
  1353. if ( !NT_SUCCESS(Status) ) {
  1354. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1355. NetpKdPrint(( "NetGroupSetInfo: SamSetInformationGroup %lX\n",
  1356. Status ));
  1357. }
  1358. NetStatus = NetpNtStatusToApiStatus( Status );
  1359. goto Cleanup;
  1360. }
  1361. break;
  1362. }
  1363. //
  1364. // Set the group attributes
  1365. //
  1366. case 2:
  1367. case 3:
  1368. case 1005: {
  1369. GROUP_ATTRIBUTE_INFORMATION Attributes;
  1370. PGROUP_ATTRIBUTE_INFORMATION OldAttributes;
  1371. //
  1372. // Get the information out of the passed in structures.
  1373. //
  1374. if( Level == 1005 ) {
  1375. Attributes.Attributes =
  1376. ((PGROUP_INFO_1005)Buffer)->grpi1005_attributes;
  1377. } else if ( Level == 2 ) {
  1378. Attributes.Attributes =
  1379. ((PGROUP_INFO_2)Buffer)->grpi2_attributes;
  1380. } else {
  1381. Attributes.Attributes =
  1382. ((PGROUP_INFO_3)Buffer)->grpi3_attributes;
  1383. }
  1384. //
  1385. // Get the current attributes so we can restore them in case of
  1386. // error.
  1387. //
  1388. //
  1389. // ?? OldAttributes gotten here is never used below.
  1390. //
  1391. Status = SamQueryInformationGroup( GroupHandle,
  1392. GroupAttributeInformation,
  1393. (PVOID*)&OldAttributes );
  1394. if ( !NT_SUCCESS(Status) ) {
  1395. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1396. NetpKdPrint((
  1397. "NetGroupSetInfo: SamQueryInformationGroup %lX\n",
  1398. Status ));
  1399. }
  1400. NetStatus = NetpNtStatusToApiStatus( Status );
  1401. goto Cleanup;
  1402. }
  1403. //
  1404. // Set the current attributes.
  1405. //
  1406. Status = SamSetInformationGroup( GroupHandle,
  1407. GroupAttributeInformation,
  1408. &Attributes );
  1409. if ( !NT_SUCCESS(Status) ) {
  1410. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1411. NetpKdPrint((
  1412. "NetGroupSetInfo: SamSetInformationGroup Attribute %lX\n",
  1413. Status ));
  1414. }
  1415. NetpSetParmError( GROUP_ATTRIBUTES_PARMNUM );
  1416. NetStatus = NetpNtStatusToApiStatus( Status );
  1417. Status = SamFreeMemory( OldAttributes );
  1418. NetpAssert( NT_SUCCESS(Status) );
  1419. goto Cleanup;
  1420. }
  1421. Status = SamFreeMemory( OldAttributes );
  1422. NetpAssert( NT_SUCCESS(Status) );
  1423. if( Level == 1005 ) {
  1424. break;
  1425. }
  1426. //
  1427. // for level 2 and 3, FALL THROUGH TO SET THE COMMENT FIELD
  1428. //
  1429. }
  1430. //
  1431. // Set the group comment
  1432. //
  1433. case 1:
  1434. case 1002:
  1435. {
  1436. LPWSTR GroupComment;
  1437. GROUP_ADM_COMMENT_INFORMATION AdminComment;
  1438. //
  1439. // Get the new group comment
  1440. //
  1441. if ( Level == 1002 ) {
  1442. GroupComment = ((PGROUP_INFO_1002)Buffer)->grpi1002_comment;
  1443. } else if ( Level == 2 ) {
  1444. GroupComment = ((PGROUP_INFO_2)Buffer)->grpi2_comment;
  1445. } else if ( Level == 3 ) {
  1446. GroupComment = ((PGROUP_INFO_3)Buffer)->grpi3_comment;
  1447. } else {
  1448. GroupComment = ((PGROUP_INFO_1)Buffer)->grpi1_comment;
  1449. }
  1450. if (GroupComment == NULL) {
  1451. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1452. NetpKdPrint(( "NetGroupSetInfo: Group comment is NULL\n" ));
  1453. }
  1454. NetStatus = NERR_Success;
  1455. goto Cleanup;
  1456. }
  1457. //
  1458. // Validate the group comment
  1459. //
  1460. RtlInitUnicodeString( &AdminComment.AdminComment, GroupComment );
  1461. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1462. NetpKdPrint(( "NetGroupSetInfo: Setting AdminComment to %wZ\n",
  1463. &AdminComment.AdminComment ));
  1464. }
  1465. Status = SamSetInformationGroup( GroupHandle,
  1466. GroupAdminCommentInformation,
  1467. &AdminComment );
  1468. if ( !NT_SUCCESS(Status) ) {
  1469. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1470. NetpKdPrint(( "NetGroupSetInfo: SamSetInformationGroup %lX\n",
  1471. Status ));
  1472. }
  1473. NetStatus = NetpNtStatusToApiStatus( Status );
  1474. goto Cleanup;
  1475. }
  1476. break;
  1477. }
  1478. default:
  1479. NetStatus = ERROR_INVALID_LEVEL;
  1480. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1481. NetpKdPrint(( "NetGroupSetInfo: Invalid Level %ld\n", Level ));
  1482. }
  1483. goto Cleanup;
  1484. }
  1485. NetStatus = NERR_Success;
  1486. //
  1487. // Clean up.
  1488. //
  1489. Cleanup:
  1490. if (GroupHandle != NULL) {
  1491. (VOID) SamCloseHandle( GroupHandle );
  1492. }
  1493. UaspCloseDomain( DomainHandle );
  1494. if ( SamServerHandle != NULL ) {
  1495. (VOID) SamCloseHandle( SamServerHandle );
  1496. }
  1497. //
  1498. // Handle downlevel.
  1499. //
  1500. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  1501. NetStatus = RxNetGroupSetInfo( (LPWSTR) ServerName,
  1502. (LPWSTR) GroupName,
  1503. Level,
  1504. Buffer,
  1505. ParmError );
  1506. UASP_DOWNLEVEL_END;
  1507. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1508. NetpKdPrint(( "NetGroupSetInfo: returns %ld\n", NetStatus ));
  1509. }
  1510. return NetStatus;
  1511. } // NetGroupSetInfo
  1512. NET_API_STATUS NET_API_FUNCTION
  1513. NetGroupSetUsers (
  1514. IN LPCWSTR ServerName OPTIONAL,
  1515. IN LPCWSTR GroupName,
  1516. IN DWORD Level,
  1517. IN LPBYTE Buffer,
  1518. IN DWORD NewMemberCount
  1519. )
  1520. /*++
  1521. Routine Description:
  1522. Set the list of members of a group.
  1523. The SAM API allows only one member to be added or deleted at a time.
  1524. This API allows all of the members of a group to be specified en-masse.
  1525. This API is careful to always leave the group membership in the SAM
  1526. database in a reasonable state. It does by mergeing the list of
  1527. old and new members, then only changing those memberships which absolutely
  1528. need changing.
  1529. Group membership is restored to its previous state (if possible) if
  1530. an error occurs during changing the group membership.
  1531. Arguments:
  1532. ServerName - A pointer to a string containing the name of the remote
  1533. server on which the function is to execute. A NULL pointer
  1534. or string specifies the local machine.
  1535. GroupName - Name of the group to modify.
  1536. Level - Level of information provided. Must be 0 or 1.
  1537. Buffer - A pointer to the buffer containing an array of NewMemberCount
  1538. the group membership information structures.
  1539. NewMemberCount - Number of entries in Buffer.
  1540. Return Value:
  1541. Error code for the operation.
  1542. --*/
  1543. {
  1544. NET_API_STATUS NetStatus;
  1545. //
  1546. // Call a routine shared by NetGroupDel to do all the actual work of
  1547. // changing the membership.
  1548. //
  1549. NetStatus = GrouppSetUsers( ServerName,
  1550. GroupName,
  1551. Level,
  1552. Buffer,
  1553. NewMemberCount,
  1554. FALSE ); // Don't delete the group when done
  1555. //
  1556. // Handle downlevel.
  1557. //
  1558. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  1559. NetStatus = RxNetGroupSetUsers( (LPWSTR) ServerName,
  1560. (LPWSTR) GroupName,
  1561. Level,
  1562. Buffer,
  1563. NewMemberCount );
  1564. UASP_DOWNLEVEL_END;
  1565. IF_DEBUG( UAS_DEBUG_GROUP ) {
  1566. NetpKdPrint(( "NetGroupSetUsers: returns %ld\n", NetStatus ));
  1567. }
  1568. return NetStatus;
  1569. } // NetGroupSetUsers
  1570. /*lint +e614 */
  1571. /*lint +e740 */