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.

2166 lines
59 KiB

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