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.

1602 lines
37 KiB

  1. /*++
  2. Copyright (c) 1991, 1992 Microsoft Corporation
  3. Module Name:
  4. aliasp.c
  5. Abstract:
  6. Private functions for supporting NetLocalGroup API
  7. Author:
  8. Cliff Van Dyke (cliffv) 06-Mar-1991 Original groupp.c
  9. Rita Wong (ritaw) 27-Nov-1992 Adapted for aliasp.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. Note:
  16. This comment is temporary...
  17. Worker routines completed and called by entrypoints in alias.c:
  18. AliaspOpenAliasInDomain
  19. AliaspOpenAlias
  20. AliaspChangeMember
  21. AliaspSetMembers
  22. AliaspGetInfo
  23. --*/
  24. #include <nt.h>
  25. #include <ntrtl.h>
  26. #include <nturtl.h>
  27. #undef DOMAIN_ALL_ACCESS // defined in both ntsam.h and ntwinapi.h
  28. #include <ntsam.h>
  29. #include <ntlsa.h>
  30. #define NOMINMAX // Avoid redefinition of min and max in stdlib.h
  31. #include <windef.h>
  32. #include <winbase.h>
  33. #include <lmcons.h>
  34. #include <access.h>
  35. #include <align.h>
  36. #include <icanon.h>
  37. #include <lmaccess.h>
  38. #include <lmerr.h>
  39. #include <netdebug.h>
  40. #include <netlib.h>
  41. #include <netlibnt.h>
  42. #include <rpcutil.h>
  43. #include <secobj.h>
  44. #include <stddef.h>
  45. #include <prefix.h>
  46. #include <uasp.h>
  47. #include <stdlib.h>
  48. NET_API_STATUS
  49. AliaspChangeMember(
  50. IN LPCWSTR ServerName OPTIONAL,
  51. IN LPCWSTR AliasName,
  52. IN PSID MemberSid,
  53. IN BOOL AddMember
  54. )
  55. /*++
  56. Routine Description:
  57. Common routine to add or remove a member from an alias.
  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. AliasName - Name of the alias to change membership of.
  63. MemberSid - SID of the user or global group to change membership of.
  64. AddMember - TRUE to add the user or global group to the alias. FALSE
  65. to delete.
  66. Return Value:
  67. Error code for the operation.
  68. --*/
  69. {
  70. NET_API_STATUS NetStatus;
  71. NTSTATUS Status;
  72. SAM_HANDLE SamServerHandle = NULL;
  73. SAM_HANDLE AliasHandle = NULL;
  74. //
  75. // Connect to the SAM server
  76. //
  77. NetStatus = UaspOpenSam( ServerName,
  78. FALSE, // Don't try null session
  79. &SamServerHandle );
  80. if ( NetStatus != NERR_Success ) {
  81. IF_DEBUG( UAS_DEBUG_ALIAS ) {
  82. NetpKdPrint(( "AliaspChangeMember: Cannot UaspOpenSam %ld\n", NetStatus ));
  83. }
  84. goto Cleanup;
  85. }
  86. //
  87. // Open the alias. Look for alias in the builtin domain first,
  88. // and if not found look in the account domain.
  89. //
  90. NetStatus = AliaspOpenAliasInDomain(
  91. SamServerHandle,
  92. AliaspBuiltinOrAccountDomain,
  93. AddMember ?
  94. ALIAS_ADD_MEMBER : ALIAS_REMOVE_MEMBER,
  95. AliasName,
  96. &AliasHandle );
  97. if (NetStatus != NERR_Success) {
  98. goto Cleanup;
  99. }
  100. if (AddMember) {
  101. //
  102. // Add the user or global group as a member of the local group.
  103. //
  104. Status = SamAddMemberToAlias(
  105. AliasHandle,
  106. MemberSid
  107. );
  108. }
  109. else {
  110. //
  111. // Delete the user as a member of the group
  112. //
  113. Status = SamRemoveMemberFromAlias(
  114. AliasHandle,
  115. MemberSid
  116. );
  117. }
  118. if (! NT_SUCCESS(Status)) {
  119. NetpKdPrint((
  120. PREFIX_NETAPI
  121. "AliaspChangeMember: SamAdd(orRemove)MemberFromAlias returned %lX\n",
  122. Status));
  123. NetStatus = NetpNtStatusToApiStatus(Status);
  124. goto Cleanup;
  125. }
  126. NetStatus = NERR_Success;
  127. Cleanup:
  128. //
  129. // Clean up.
  130. //
  131. if (AliasHandle != NULL) {
  132. (VOID) SamCloseHandle(AliasHandle);
  133. }
  134. if ( SamServerHandle != NULL ) {
  135. (VOID) SamCloseHandle( SamServerHandle );
  136. }
  137. return NetStatus;
  138. } // AliaspChangeMember
  139. NET_API_STATUS
  140. AliaspGetInfo(
  141. IN SAM_HANDLE AliasHandle,
  142. IN DWORD Level,
  143. OUT PVOID *Buffer
  144. )
  145. /*++
  146. Routine Description:
  147. Internal routine to get alias information
  148. Arguments:
  149. AliasHandle - Supplies the handle of the alias.
  150. Level - Level of information required. 0 and 1 are valid.
  151. Buffer - Returns a pointer to the return information structure.
  152. Caller must deallocate buffer using NetApiBufferFree.
  153. Return Value:
  154. Error code for the operation.
  155. --*/
  156. {
  157. NET_API_STATUS NetStatus;
  158. NTSTATUS Status;
  159. ALIAS_GENERAL_INFORMATION *AliasGeneral = NULL;
  160. LPWSTR LastString;
  161. DWORD BufferSize;
  162. DWORD FixedSize;
  163. PLOCALGROUP_INFO_1 Info;
  164. //
  165. // Get the information about the alias.
  166. //
  167. Status = SamQueryInformationAlias( AliasHandle,
  168. AliasGeneralInformation,
  169. (PVOID *)&AliasGeneral);
  170. if ( ! NT_SUCCESS( Status ) ) {
  171. NetStatus = NetpNtStatusToApiStatus( Status );
  172. goto Cleanup;
  173. }
  174. //
  175. // Figure out how big the return buffer needs to be
  176. //
  177. switch ( Level ) {
  178. case 0:
  179. FixedSize = sizeof( LOCALGROUP_INFO_0 );
  180. BufferSize = FixedSize +
  181. AliasGeneral->Name.Length + sizeof(WCHAR);
  182. break;
  183. case 1:
  184. FixedSize = sizeof( LOCALGROUP_INFO_1 );
  185. BufferSize = FixedSize +
  186. AliasGeneral->Name.Length + sizeof(WCHAR) +
  187. AliasGeneral->AdminComment.Length + sizeof(WCHAR);
  188. break;
  189. default:
  190. NetStatus = ERROR_INVALID_LEVEL;
  191. goto Cleanup;
  192. }
  193. //
  194. // Allocate the return buffer.
  195. //
  196. BufferSize = ROUND_UP_COUNT( BufferSize, ALIGN_WCHAR );
  197. *Buffer = MIDL_user_allocate( BufferSize );
  198. if ( *Buffer == NULL ) {
  199. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  200. goto Cleanup;
  201. }
  202. LastString = (LPWSTR) (((LPBYTE)*Buffer) + BufferSize);
  203. //
  204. // Fill the name into the return buffer.
  205. //
  206. NetpAssert( offsetof( LOCALGROUP_INFO_0, lgrpi0_name ) ==
  207. offsetof( LOCALGROUP_INFO_1, lgrpi1_name ) );
  208. Info = (PLOCALGROUP_INFO_1) *Buffer;
  209. //
  210. // Fill in the return buffer.
  211. //
  212. switch ( Level ) {
  213. case 1:
  214. //
  215. // copy fields common to info level 1 and 0.
  216. //
  217. if ( !NetpCopyStringToBuffer(
  218. AliasGeneral->AdminComment.Buffer,
  219. AliasGeneral->AdminComment.Length/sizeof(WCHAR),
  220. ((LPBYTE)(*Buffer)) + FixedSize,
  221. &LastString,
  222. &Info->lgrpi1_comment ) ) {
  223. NetStatus = NERR_InternalError;
  224. goto Cleanup;
  225. }
  226. //
  227. // Fall through for name field
  228. //
  229. case 0:
  230. //
  231. // copy common field (name field) in the buffer.
  232. //
  233. if ( !NetpCopyStringToBuffer(
  234. AliasGeneral->Name.Buffer,
  235. AliasGeneral->Name.Length/sizeof(WCHAR),
  236. ((LPBYTE)(*Buffer)) + FixedSize,
  237. &LastString,
  238. &Info->lgrpi1_name ) ) {
  239. NetStatus = NERR_InternalError;
  240. goto Cleanup;
  241. }
  242. break;
  243. default:
  244. NetStatus = ERROR_INVALID_LEVEL;
  245. goto Cleanup;
  246. }
  247. NetStatus = NERR_Success;
  248. //
  249. // Cleanup and return.
  250. //
  251. Cleanup:
  252. if ( AliasGeneral ) {
  253. Status = SamFreeMemory( AliasGeneral );
  254. NetpAssert( NT_SUCCESS(Status) );
  255. }
  256. IF_DEBUG( UAS_DEBUG_ALIAS ) {
  257. NetpKdPrint(( "AliaspGetInfo: returns %lu\n", NetStatus ));
  258. }
  259. return NetStatus;
  260. } // AliaspGetInfo
  261. NET_API_STATUS
  262. AliaspOpenAliasInDomain(
  263. IN SAM_HANDLE SamServerHandle,
  264. IN ALIASP_DOMAIN_TYPE DomainType,
  265. IN ACCESS_MASK DesiredAccess,
  266. IN LPCWSTR AliasName,
  267. OUT PSAM_HANDLE AliasHandle
  268. )
  269. /*++
  270. Routine Description:
  271. Open a Sam Alias by Name
  272. Arguments:
  273. SamServerHandle - A handle to the SAM server to open the alias on.
  274. DomainType - Supplies the type of domain to look for an alias. This
  275. may specify to look for the alias in either the BuiltIn or Account
  276. domain (searching in the BuiltIn first), or specifically one of them.
  277. DesiredAccess - Supplies access mask indicating desired access to alias.
  278. AliasName - Name of the alias.
  279. AliasHandle - Returns a handle to the alias.
  280. Return Value:
  281. Error code for the operation.
  282. --*/
  283. {
  284. NET_API_STATUS NetStatus;
  285. SAM_HANDLE DomainHandleLocal ;
  286. switch (DomainType) {
  287. case AliaspBuiltinOrAccountDomain:
  288. //
  289. // Try looking for alias in the builtin domain first
  290. //
  291. NetStatus = UaspOpenDomain( SamServerHandle,
  292. DOMAIN_LOOKUP,
  293. FALSE, // Builtin Domain
  294. &DomainHandleLocal,
  295. NULL ); // DomainId
  296. if (NetStatus != NERR_Success) {
  297. return NetStatus;
  298. }
  299. NetStatus = AliaspOpenAlias( DomainHandleLocal,
  300. DesiredAccess,
  301. AliasName,
  302. AliasHandle );
  303. if (NetStatus != ERROR_NO_SUCH_ALIAS &&
  304. NetStatus != NERR_GroupNotFound) {
  305. goto Cleanup;
  306. }
  307. //
  308. // Close the builtin domain handle.
  309. //
  310. UaspCloseDomain( DomainHandleLocal );
  311. //
  312. // Fall through. Try looking for alias in the account
  313. // domain.
  314. //
  315. case AliaspAccountDomain:
  316. NetStatus = UaspOpenDomain( SamServerHandle,
  317. DOMAIN_LOOKUP,
  318. TRUE, // Account Domain
  319. &DomainHandleLocal,
  320. NULL ); // DomainId
  321. if (NetStatus != NERR_Success) {
  322. return NetStatus;
  323. }
  324. NetStatus = AliaspOpenAlias( DomainHandleLocal,
  325. DesiredAccess,
  326. AliasName,
  327. AliasHandle );
  328. break;
  329. case AliaspBuiltinDomain:
  330. NetStatus = UaspOpenDomain( SamServerHandle,
  331. DOMAIN_LOOKUP,
  332. FALSE, // Builtin Domain
  333. &DomainHandleLocal,
  334. NULL ); // DomainId
  335. if (NetStatus != NERR_Success) {
  336. return NetStatus;
  337. }
  338. NetStatus = AliaspOpenAlias( DomainHandleLocal,
  339. DesiredAccess,
  340. AliasName,
  341. AliasHandle );
  342. break;
  343. default:
  344. NetpAssert(FALSE);
  345. return NERR_InternalError;
  346. }
  347. Cleanup:
  348. UaspCloseDomain( DomainHandleLocal );
  349. if (NetStatus != NERR_Success) {
  350. *AliasHandle = NULL;
  351. IF_DEBUG( UAS_DEBUG_ALIAS ) {
  352. NetpKdPrint((PREFIX_NETAPI "AliaspOpenAliasInDomain of type %lu returns %lu\n",
  353. DomainType, NetStatus));
  354. }
  355. }
  356. return NetStatus;
  357. } // AliaspOpenAliasInDomain
  358. NET_API_STATUS
  359. AliaspOpenAlias(
  360. IN SAM_HANDLE DomainHandle,
  361. IN ACCESS_MASK DesiredAccess,
  362. IN LPCWSTR AliasName,
  363. OUT PSAM_HANDLE AliasHandle
  364. )
  365. /*++
  366. Routine Description:
  367. Open a Sam Alias by Name
  368. Arguments:
  369. DomainHandle - Supplies the handle of the domain the alias is in.
  370. DesiredAccess - Supplies access mask indicating desired access to alias.
  371. AliasName - Name of the alias.
  372. AliasHandle - Returns a handle to the alias.
  373. Return Value:
  374. Error code for the operation.
  375. --*/
  376. {
  377. NTSTATUS Status;
  378. NET_API_STATUS NetStatus;
  379. //
  380. // Variables for converting names to relative IDs
  381. //
  382. UNICODE_STRING NameString;
  383. PSID_NAME_USE NameUse;
  384. PULONG LocalRelativeId;
  385. RtlInitUnicodeString( &NameString, AliasName );
  386. //
  387. // Convert group name to relative ID.
  388. //
  389. Status = SamLookupNamesInDomain( DomainHandle,
  390. 1,
  391. &NameString,
  392. &LocalRelativeId,
  393. &NameUse );
  394. if ( !NT_SUCCESS(Status) ) {
  395. IF_DEBUG( UAS_DEBUG_ALIAS ) {
  396. NetpKdPrint(( "AliaspOpenAlias: %wZ: SamLookupNamesInDomain %lX\n",
  397. &NameString,
  398. Status ));
  399. }
  400. return NetpNtStatusToApiStatus( Status );
  401. }
  402. if ( *NameUse != SidTypeAlias ) {
  403. IF_DEBUG( UAS_DEBUG_ALIAS ) {
  404. NetpKdPrint(( "AliaspOpenAlias: %wZ: Name is not an alias %ld\n",
  405. &NameString,
  406. *NameUse ));
  407. }
  408. NetStatus = ERROR_NO_SUCH_ALIAS;
  409. goto Cleanup;
  410. }
  411. //
  412. // Open the alias
  413. //
  414. Status = SamOpenAlias( DomainHandle,
  415. DesiredAccess,
  416. *LocalRelativeId,
  417. AliasHandle);
  418. if ( !NT_SUCCESS(Status) ) {
  419. IF_DEBUG( UAS_DEBUG_ALIAS ) {
  420. NetpKdPrint(( "AliaspOpenAlias: %wZ: SamOpenGroup %lX\n",
  421. &NameString,
  422. Status ));
  423. }
  424. NetStatus = NetpNtStatusToApiStatus( Status );
  425. goto Cleanup;
  426. }
  427. NetStatus = NERR_Success;
  428. //
  429. // Cleanup
  430. //
  431. Cleanup:
  432. if ( LocalRelativeId != NULL ) {
  433. Status = SamFreeMemory( LocalRelativeId );
  434. NetpAssert( NT_SUCCESS(Status) );
  435. }
  436. if ( NameUse != NULL ) {
  437. Status = SamFreeMemory( NameUse );
  438. NetpAssert( NT_SUCCESS(Status) );
  439. }
  440. return NetStatus;
  441. } // AliaspOpenAlias
  442. NET_API_STATUS
  443. AliaspOpenAlias2(
  444. IN SAM_HANDLE DomainHandle,
  445. IN ACCESS_MASK DesiredAccess,
  446. IN ULONG RelativeID,
  447. OUT PSAM_HANDLE AliasHandle
  448. )
  449. /*++
  450. Routine Description:
  451. Open a Sam Alias by its RID
  452. Arguments:
  453. DomainHandle - Supplies the handle of the domain the alias is in.
  454. DesiredAccess - Supplies access mask indicating desired access to alias.
  455. RelativeID - RID of the alias to open
  456. AliasHandle - Returns a handle to the alias
  457. Return Value:
  458. Error code for the operation.
  459. --*/
  460. {
  461. NTSTATUS Status;
  462. NET_API_STATUS NetStatus = NERR_Success ;
  463. if ( AliasHandle == NULL )
  464. return ERROR_INVALID_PARAMETER ;
  465. //
  466. // Open the alias
  467. //
  468. Status = SamOpenAlias( DomainHandle,
  469. DesiredAccess,
  470. RelativeID,
  471. AliasHandle);
  472. if ( !NT_SUCCESS(Status) ) {
  473. IF_DEBUG( UAS_DEBUG_ALIAS ) {
  474. NetpKdPrint(( "AliaspOpenAlias2: SamOpenAlias %lX\n",
  475. Status ));
  476. }
  477. NetStatus = NetpNtStatusToApiStatus( Status );
  478. }
  479. return NetStatus;
  480. } // AliaspOpenAlias2
  481. VOID
  482. AliaspRelocationRoutine(
  483. IN DWORD Level,
  484. IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
  485. IN PTRDIFF_T Offset
  486. )
  487. /*++
  488. Routine Description:
  489. Routine to relocate the pointers from the fixed portion of a NetGroupEnum
  490. enumeration
  491. buffer to the string portion of an enumeration buffer. It is called
  492. as a callback routine from NetpAllocateEnumBuffer when it re-allocates
  493. such a buffer. NetpAllocateEnumBuffer copied the fixed portion and
  494. string portion into the new buffer before calling this routine.
  495. Arguments:
  496. Level - Level of information in the buffer.
  497. BufferDescriptor - Description of the new buffer.
  498. Offset - Offset to add to each pointer in the fixed portion.
  499. Return Value:
  500. Returns the error code for the operation.
  501. --*/
  502. {
  503. DWORD EntryCount;
  504. DWORD EntryNumber;
  505. DWORD FixedSize;
  506. IF_DEBUG( UAS_DEBUG_ALIAS ) {
  507. NetpKdPrint(( "AliaspRelocationRoutine: entering\n" ));
  508. }
  509. //
  510. // Compute the number of fixed size entries
  511. //
  512. switch (Level) {
  513. case 0:
  514. FixedSize = sizeof(LOCALGROUP_INFO_0);
  515. break;
  516. case 1:
  517. FixedSize = sizeof(LOCALGROUP_INFO_1);
  518. break;
  519. default:
  520. NetpAssert( FALSE );
  521. return;
  522. }
  523. EntryCount =
  524. ((DWORD)(BufferDescriptor->FixedDataEnd - BufferDescriptor->Buffer)) /
  525. FixedSize;
  526. //
  527. // Loop relocating each field in each fixed size structure
  528. //
  529. for ( EntryNumber=0; EntryNumber<EntryCount; EntryNumber++ ) {
  530. LPBYTE TheStruct = BufferDescriptor->Buffer + FixedSize * EntryNumber;
  531. switch ( Level ) {
  532. case 1:
  533. RELOCATE_ONE( ((PLOCALGROUP_INFO_1)TheStruct)->lgrpi1_comment, Offset );
  534. //
  535. // Drop through to case 0
  536. //
  537. case 0:
  538. RELOCATE_ONE( ((PLOCALGROUP_INFO_0)TheStruct)->lgrpi0_name, Offset );
  539. break;
  540. default:
  541. return;
  542. }
  543. }
  544. return;
  545. } // AliaspRelocationRoutine
  546. VOID
  547. AliaspMemberRelocationRoutine(
  548. IN DWORD Level,
  549. IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
  550. IN PTRDIFF_T Offset
  551. )
  552. /*++
  553. Routine Description:
  554. Routine to relocate the pointers from the fixed portion of a
  555. NetGroupGetUsers enumeration
  556. buffer to the string portion of an enumeration buffer. It is called
  557. as a callback routine from NetpAllocateEnumBuffer when it re-allocates
  558. such a buffer. NetpAllocateEnumBuffer copied the fixed portion and
  559. string portion into the new buffer before calling this routine.
  560. Arguments:
  561. Level - Level of information in the buffer.
  562. BufferDescriptor - Description of the new buffer.
  563. Offset - Offset to add to each pointer in the fixed portion.
  564. Return Value:
  565. Returns the error code for the operation.
  566. --*/
  567. {
  568. DWORD EntryCount;
  569. DWORD EntryNumber;
  570. DWORD FixedSize;
  571. IF_DEBUG( UAS_DEBUG_ALIAS ) {
  572. NetpKdPrint(( "AliaspMemberRelocationRoutine: entering\n" ));
  573. }
  574. //
  575. // Compute the number of fixed size entries
  576. //
  577. NetpAssert( sizeof(LOCALGROUP_MEMBERS_INFO_1) ==
  578. sizeof(LOCALGROUP_MEMBERS_INFO_2));
  579. NetpAssert( offsetof( LOCALGROUP_MEMBERS_INFO_1, lgrmi1_sid ) ==
  580. offsetof( LOCALGROUP_MEMBERS_INFO_2, lgrmi2_sid ) );
  581. NetpAssert( offsetof( LOCALGROUP_MEMBERS_INFO_1, lgrmi1_sidusage ) ==
  582. offsetof( LOCALGROUP_MEMBERS_INFO_2, lgrmi2_sidusage ) );
  583. NetpAssert( offsetof( LOCALGROUP_MEMBERS_INFO_1, lgrmi1_name ) ==
  584. offsetof( LOCALGROUP_MEMBERS_INFO_2, lgrmi2_domainandname ) );
  585. switch (Level) {
  586. case 0:
  587. FixedSize = sizeof(LOCALGROUP_MEMBERS_INFO_0);
  588. break;
  589. case 1:
  590. case 2:
  591. FixedSize = sizeof(LOCALGROUP_MEMBERS_INFO_1);
  592. break;
  593. case 3:
  594. FixedSize = sizeof(LOCALGROUP_MEMBERS_INFO_3);
  595. break;
  596. default:
  597. NetpAssert( FALSE );
  598. return;
  599. }
  600. EntryCount =
  601. ((DWORD)(BufferDescriptor->FixedDataEnd - BufferDescriptor->Buffer)) /
  602. FixedSize;
  603. //
  604. // Loop relocating each field in each fixed size structure
  605. //
  606. for ( EntryNumber=0; EntryNumber<EntryCount; EntryNumber++ ) {
  607. LPBYTE TheStruct = BufferDescriptor->Buffer + FixedSize * EntryNumber;
  608. switch ( Level ) {
  609. case 3:
  610. RELOCATE_ONE( ((PLOCALGROUP_MEMBERS_INFO_3)TheStruct)->lgrmi3_domainandname, Offset );
  611. break;
  612. case 1:
  613. case 2:
  614. //
  615. // Sid usage gets relocated automatically
  616. //
  617. RELOCATE_ONE( ((PLOCALGROUP_MEMBERS_INFO_1)TheStruct)->lgrmi1_name, Offset );
  618. //
  619. // Drop through to case 0
  620. //
  621. case 0:
  622. RELOCATE_ONE( ((PLOCALGROUP_MEMBERS_INFO_0)TheStruct)->lgrmi0_sid, Offset );
  623. break;
  624. default:
  625. return;
  626. }
  627. }
  628. return;
  629. } // AliaspMemberRelocationRoutine
  630. NET_API_STATUS
  631. AliaspSetMembers (
  632. IN LPCWSTR ServerName OPTIONAL,
  633. IN LPCWSTR AliasName,
  634. IN DWORD Level,
  635. IN LPBYTE Buffer,
  636. IN DWORD NewMemberCount,
  637. IN ALIAS_MEMBER_CHANGE_TYPE ChangeType
  638. )
  639. /*++
  640. Routine Description:
  641. Set the list of members of an alias.
  642. The members specified by "Buffer" are called new members. The current
  643. members of the alias are called old members.
  644. The SAM API allows only one member to be added or deleted at a time.
  645. This API allows all of the members of an alias to be specified en-masse.
  646. This API is careful to always leave the alias membership in the SAM
  647. database in a reasonable state. It does by mergeing the list of
  648. old and new members, then only changing those memberships which absolutely
  649. need changing.
  650. Alias membership is restored to its previous state (if possible) if
  651. an error occurs during changing the alias membership.
  652. Arguments:
  653. ServerName - A pointer to a string containing the name of the remote
  654. server on which the function is to execute. A NULL pointer
  655. or string specifies the local machine.
  656. AliasName - Name of the alias to modify.
  657. Level - Level of information provided. Must be 0 (so Buffer contains
  658. array of member SIDs) or 3 (so Buffer contains array of pointers to
  659. names)
  660. Buffer - A pointer to the buffer containing an array of NewMemberCount
  661. the alias membership information structures.
  662. NewMemberCount - Number of entries in Buffer.
  663. ChangeType - Indicates whether the specified members are to be set, added,
  664. or deleted.
  665. Return Value:
  666. Error code for the operation.
  667. --*/
  668. {
  669. NET_API_STATUS NetStatus;
  670. NTSTATUS Status;
  671. SAM_HANDLE SamServerHandle = NULL;
  672. SAM_HANDLE AliasHandle = NULL;
  673. //
  674. // Define an internal member list structure.
  675. //
  676. // This structure is to hold information about a member which
  677. // requires some operation in SAM: either it is a new member to
  678. // be added, or an old member to be deleted.
  679. //
  680. typedef enum { // Action taken for this member
  681. NoAction,
  682. AddMember, // Add Member to group
  683. RemoveMember // Remove Member from group
  684. } MEMBER_ACTION;
  685. typedef struct {
  686. LIST_ENTRY Next; // Next entry in linked list;
  687. MEMBER_ACTION Action; // Action to taken for this member
  688. PSID MemberSid; // SID of member
  689. BOOL Done; // True if this action has been taken
  690. } MEMBER_DESCRIPTION, *PMEMBER_DESCRIPTION;
  691. MEMBER_DESCRIPTION *ActionEntry;
  692. PLIST_ENTRY ListEntry;
  693. LIST_ENTRY ActionList;
  694. //
  695. // Array of existing (old) members, and count
  696. //
  697. PSID *OldMemberList = NULL;
  698. PSID *OldMember;
  699. ULONG OldMemberCount, i;
  700. //
  701. // Array of new members
  702. //
  703. PLOCALGROUP_MEMBERS_INFO_0 NewMemberList;
  704. PLOCALGROUP_MEMBERS_INFO_0 NewMember;
  705. BOOLEAN FreeNewMemberList = FALSE;
  706. DWORD j;
  707. //
  708. // Validate the level
  709. //
  710. InitializeListHead( &ActionList );
  711. switch (Level) {
  712. case 0:
  713. NewMemberList = (PLOCALGROUP_MEMBERS_INFO_0) Buffer;
  714. break;
  715. //
  716. // If this is level 3,
  717. // compute the SID of each of the added members
  718. //
  719. case 3:
  720. NetpAssert( sizeof( LOCALGROUP_MEMBERS_INFO_3) ==
  721. sizeof( LPWSTR ) );
  722. NetpAssert( sizeof( LOCALGROUP_MEMBERS_INFO_0) ==
  723. sizeof( PSID ) );
  724. NetStatus = AliaspNamesToSids (
  725. ServerName,
  726. FALSE,
  727. NewMemberCount,
  728. (LPWSTR *)Buffer,
  729. (PSID **) &NewMemberList );
  730. if ( NetStatus != NERR_Success ) {
  731. goto Cleanup;
  732. }
  733. FreeNewMemberList = TRUE;
  734. break;
  735. default:
  736. return ERROR_INVALID_LEVEL;
  737. }
  738. //
  739. // Connect to the SAM server
  740. //
  741. NetStatus = UaspOpenSam( ServerName,
  742. FALSE, // Don't try null session
  743. &SamServerHandle );
  744. if ( NetStatus != NERR_Success ) {
  745. IF_DEBUG( UAS_DEBUG_ALIAS ) {
  746. NetpKdPrint(( "AliaspChangeMember: Cannot UaspOpenSam %ld\n", NetStatus ));
  747. }
  748. goto Cleanup;
  749. }
  750. //
  751. // Look for the specified alias in either the builtin or account
  752. // domain.
  753. //
  754. NetStatus = AliaspOpenAliasInDomain(
  755. SamServerHandle,
  756. AliaspBuiltinOrAccountDomain,
  757. ALIAS_READ_INFORMATION | ALIAS_LIST_MEMBERS |
  758. ALIAS_ADD_MEMBER | ALIAS_REMOVE_MEMBER,
  759. AliasName,
  760. &AliasHandle );
  761. if (NetStatus != NERR_Success) {
  762. goto Cleanup;
  763. }
  764. //
  765. // Get the existing membership list.
  766. //
  767. if ( ChangeType == SetMembers ) {
  768. Status = SamGetMembersInAlias(
  769. AliasHandle,
  770. &OldMemberList,
  771. &OldMemberCount
  772. );
  773. if (! NT_SUCCESS(Status)) {
  774. NetpKdPrint((PREFIX_NETAPI
  775. "AliaspSetMembers: SamGetMembersInAlias returns %lX\n",
  776. Status));
  777. NetStatus = NetpNtStatusToApiStatus(Status);
  778. goto Cleanup;
  779. }
  780. }
  781. //
  782. // Loop through each new member deciding what to do with it.
  783. //
  784. for (i = 0, NewMember = NewMemberList;
  785. i < NewMemberCount;
  786. i++, NewMember++) {
  787. MEMBER_ACTION ProposedAction;
  788. PSID ActionSid;
  789. //
  790. // If we're setting the complete membership to the new member list,
  791. // See if New member is also in Old member list.
  792. // if not, add the new member.
  793. // if so, mark the old member as being already found.
  794. //
  795. switch ( ChangeType ) {
  796. case SetMembers:
  797. ProposedAction = AddMember;
  798. ActionSid = NewMember->lgrmi0_sid;
  799. for (j = 0, OldMember = OldMemberList;
  800. j < OldMemberCount;
  801. j++, OldMember++) {
  802. if ( *OldMember != NULL &&
  803. EqualSid(*OldMember, NewMember->lgrmi0_sid)) {
  804. ProposedAction = NoAction;
  805. *OldMember = NULL; // Mark this old member as already found
  806. break; // leave OldMemberList loop
  807. }
  808. }
  809. break;
  810. case AddMembers:
  811. ProposedAction = AddMember;
  812. ActionSid = NewMember->lgrmi0_sid;
  813. break;
  814. case DelMembers:
  815. ProposedAction = RemoveMember;
  816. ActionSid = NewMember->lgrmi0_sid;
  817. break;
  818. }
  819. if ( ProposedAction != NoAction ) {
  820. //
  821. // If action needs to be taken, create an action list entry
  822. // and chain it on the tail of the ActionList.
  823. //
  824. ActionEntry = (PMEMBER_DESCRIPTION)
  825. LocalAlloc(
  826. LMEM_ZEROINIT,
  827. (UINT) sizeof(MEMBER_DESCRIPTION)
  828. );
  829. if (ActionEntry == NULL) {
  830. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  831. goto RestoreMembership;
  832. }
  833. ActionEntry->MemberSid = ActionSid;
  834. ActionEntry->Action = ProposedAction;
  835. InsertTailList( &ActionList, &ActionEntry->Next );
  836. }
  837. }
  838. //
  839. // For each old member,
  840. // if it doesn't have a corresponding entry in the new member list,
  841. // remember to delete the old membership.
  842. //
  843. if ( ChangeType == SetMembers ) {
  844. for (j = 0, OldMember = OldMemberList;
  845. j < OldMemberCount;
  846. j++, OldMember++) {
  847. if ( *OldMember != NULL ) {
  848. //
  849. // Create an add action entry for this new member and
  850. // chain it up on the tail of the ActionList.
  851. //
  852. ActionEntry = (PMEMBER_DESCRIPTION)
  853. LocalAlloc(
  854. LMEM_ZEROINIT,
  855. (UINT) sizeof(MEMBER_DESCRIPTION)
  856. );
  857. if (ActionEntry == NULL) {
  858. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  859. goto RestoreMembership;
  860. }
  861. ActionEntry->MemberSid = *OldMember;
  862. ActionEntry->Action = RemoveMember;
  863. InsertTailList( &ActionList, &ActionEntry->Next );
  864. }
  865. }
  866. }
  867. //
  868. // Now we can call SAM to do the work. Add first so that we
  869. // leave less damage should we fail to restore on an error.
  870. //
  871. for ( ListEntry = ActionList.Flink ;
  872. ListEntry != &ActionList ;
  873. ListEntry = ListEntry->Flink) {
  874. ActionEntry = CONTAINING_RECORD( ListEntry,
  875. MEMBER_DESCRIPTION,
  876. Next );
  877. if (ActionEntry->Action == AddMember) {
  878. Status = SamAddMemberToAlias(
  879. AliasHandle,
  880. ActionEntry->MemberSid
  881. );
  882. if (! NT_SUCCESS(Status)) {
  883. NetpKdPrint((PREFIX_NETAPI
  884. "AliaspSetMembers: SamAddMemberToAlias returns %lX\n",
  885. Status));
  886. NetStatus = NetpNtStatusToApiStatus(Status);
  887. goto RestoreMembership;
  888. }
  889. ActionEntry->Done = TRUE;
  890. }
  891. }
  892. //
  893. // Delete old members.
  894. //
  895. for ( ListEntry = ActionList.Flink ;
  896. ListEntry != &ActionList ;
  897. ListEntry = ListEntry->Flink) {
  898. ActionEntry = CONTAINING_RECORD( ListEntry,
  899. MEMBER_DESCRIPTION,
  900. Next );
  901. if (ActionEntry->Action == RemoveMember) {
  902. Status = SamRemoveMemberFromAlias(
  903. AliasHandle,
  904. ActionEntry->MemberSid
  905. );
  906. if (! NT_SUCCESS(Status)) {
  907. NetpKdPrint((PREFIX_NETAPI
  908. "AliaspSetMembers: SamRemoveMemberFromAlias returns %lX\n",
  909. Status));
  910. NetStatus = NetpNtStatusToApiStatus(Status);
  911. goto RestoreMembership;
  912. }
  913. ActionEntry->Done = TRUE;
  914. }
  915. }
  916. NetStatus = NERR_Success;
  917. //
  918. // Delete the action list
  919. // On error, undo any action already done.
  920. //
  921. RestoreMembership:
  922. while ( !IsListEmpty( &ActionList ) ) {
  923. ListEntry = RemoveHeadList( &ActionList );
  924. ActionEntry = CONTAINING_RECORD( ListEntry,
  925. MEMBER_DESCRIPTION,
  926. Next );
  927. if (NetStatus != NERR_Success && ActionEntry->Done) {
  928. switch (ActionEntry->Action) {
  929. case AddMember:
  930. Status = SamRemoveMemberFromAlias(
  931. AliasHandle,
  932. ActionEntry->MemberSid
  933. );
  934. NetpAssert(NT_SUCCESS(Status));
  935. break;
  936. case RemoveMember:
  937. Status = SamAddMemberToAlias(
  938. AliasHandle,
  939. ActionEntry->MemberSid
  940. );
  941. NetpAssert(NT_SUCCESS(Status));
  942. break;
  943. default:
  944. break;
  945. }
  946. }
  947. //
  948. // Delete the entry
  949. //
  950. (void) LocalFree( ActionEntry );
  951. }
  952. Cleanup:
  953. //
  954. // If we allocated the new member list,
  955. // delete it and any SIDs it points to.
  956. //
  957. if ( FreeNewMemberList ) {
  958. AliaspFreeSidList( NewMemberCount, (PSID *)NewMemberList );
  959. }
  960. if (OldMemberList != NULL) {
  961. SamFreeMemory(OldMemberList);
  962. }
  963. if (AliasHandle != NULL) {
  964. (VOID) SamCloseHandle(AliasHandle);
  965. }
  966. if ( SamServerHandle != NULL ) {
  967. (VOID) SamCloseHandle( SamServerHandle );
  968. }
  969. IF_DEBUG(UAS_DEBUG_ALIAS) {
  970. NetpKdPrint((PREFIX_NETAPI "AliaspSetMembers: returns %lu\n", NetStatus));
  971. }
  972. return NetStatus;
  973. } // AliaspSetMembers
  974. NET_API_STATUS
  975. AliaspNamesToSids (
  976. IN LPCWSTR ServerName,
  977. IN BOOL OnlyAllowUsers,
  978. IN DWORD NameCount,
  979. IN LPWSTR *Names,
  980. OUT PSID **Sids
  981. )
  982. /*++
  983. Routine Description:
  984. Convert a list of Domain\Member strings to SIDs.
  985. Arguments:
  986. ServerName - Name of the server to do the translation on.
  987. OnlyAllowUsers - True if all names must be user accounts.
  988. NameCount - Number of names to convert.
  989. Names - Array of pointers to Domain\Member strings
  990. Sids - Returns a pointer to an array of pointers to SIDs. The array should
  991. be freed via AliaspFreeSidList.
  992. Return Value:
  993. NERR_Success - The translation was successful
  994. ERROR_NO_SUCH_MEMBER - One or more of the names could not be converted
  995. to a SID.
  996. ...
  997. --*/
  998. {
  999. NET_API_STATUS NetStatus;
  1000. NTSTATUS Status;
  1001. DWORD i;
  1002. LSA_HANDLE LsaHandle = NULL;
  1003. OBJECT_ATTRIBUTES ObjectAttributes ;
  1004. UNICODE_STRING ServerNameString ;
  1005. PUNICODE_STRING NameStrings = NULL;
  1006. PSID *SidList = NULL;
  1007. PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains = NULL;
  1008. PLSA_TRANSLATED_SID2 LsaSids = NULL;
  1009. //
  1010. // Open the LSA database
  1011. //
  1012. RtlInitUnicodeString( &ServerNameString, ServerName ) ;
  1013. InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL ) ;
  1014. Status = LsaOpenPolicy( &ServerNameString,
  1015. &ObjectAttributes,
  1016. POLICY_EXECUTE,
  1017. &LsaHandle ) ;
  1018. if ( !NT_SUCCESS( Status ) ) {
  1019. NetStatus = NetpNtStatusToApiStatus( Status );
  1020. goto Cleanup;
  1021. }
  1022. //
  1023. // Convert the names to unicode strings
  1024. //
  1025. NameStrings = (PUNICODE_STRING) LocalAlloc(
  1026. 0,
  1027. sizeof(UNICODE_STRING) * NameCount );
  1028. if ( NameStrings == NULL ) {
  1029. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1030. goto Cleanup;
  1031. }
  1032. for ( i=0; i<NameCount; i++ ) {
  1033. RtlInitUnicodeString( &NameStrings[i], Names[i] );
  1034. }
  1035. //
  1036. // Convert the names to sids
  1037. //
  1038. Status = LsaLookupNames2(
  1039. LsaHandle,
  1040. 0, // Flags
  1041. NameCount,
  1042. NameStrings,
  1043. &ReferencedDomains,
  1044. &LsaSids );
  1045. if ( !NT_SUCCESS( Status ) ) {
  1046. ReferencedDomains = NULL;
  1047. LsaSids = NULL;
  1048. if ( Status == STATUS_NONE_MAPPED ) {
  1049. NetStatus = ERROR_NO_SUCH_MEMBER;
  1050. } else {
  1051. NetStatus = NetpNtStatusToApiStatus( Status );
  1052. }
  1053. goto Cleanup;
  1054. }
  1055. if ( Status == STATUS_SOME_NOT_MAPPED ) {
  1056. NetStatus = ERROR_NO_SUCH_MEMBER;
  1057. goto Cleanup;
  1058. }
  1059. //
  1060. // Allocate the SID list to return
  1061. //
  1062. SidList = (PSID *) LocalAlloc(
  1063. LMEM_ZEROINIT, // Initially all to NULL
  1064. sizeof(PSID) * NameCount );
  1065. if ( SidList == NULL ) {
  1066. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1067. goto Cleanup;
  1068. }
  1069. //
  1070. // Construct a SID for each name
  1071. //
  1072. for ( i=0; i<NameCount; i++ ) {
  1073. ULONG Length;
  1074. //
  1075. // If the caller only want user accounts,
  1076. // ensure this is one.
  1077. //
  1078. if ( LsaSids[i].Use != SidTypeUser ) {
  1079. if ( OnlyAllowUsers ||
  1080. (LsaSids[i].Use != SidTypeGroup &&
  1081. LsaSids[i].Use != SidTypeAlias &&
  1082. LsaSids[i].Use != SidTypeWellKnownGroup )) {
  1083. NetStatus = ERROR_NO_SUCH_MEMBER;
  1084. goto Cleanup;
  1085. }
  1086. }
  1087. //
  1088. // Construct a SID for the name.
  1089. //
  1090. Length = RtlLengthSid( LsaSids[i].Sid );
  1091. SidList[i] = NetpMemoryAllocate(Length);
  1092. if ( NULL == SidList[i] ) {
  1093. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1094. goto Cleanup;
  1095. }
  1096. RtlCopySid( Length, SidList[i], LsaSids[i].Sid );
  1097. }
  1098. NetStatus = NERR_Success;
  1099. //
  1100. // Free locally used resources.
  1101. //
  1102. Cleanup:
  1103. if ( LsaHandle != NULL ) {
  1104. (void) LsaClose( LsaHandle );
  1105. }
  1106. if ( NameStrings != NULL ) {
  1107. (void) LocalFree( NameStrings );
  1108. }
  1109. if ( ReferencedDomains != NULL ) {
  1110. (void) LsaFreeMemory( ReferencedDomains );
  1111. }
  1112. if ( LsaSids != NULL ) {
  1113. (void) LsaFreeMemory( LsaSids );
  1114. }
  1115. //
  1116. // If the translation wasn't successful,
  1117. // free any partial translation.
  1118. //
  1119. if ( NetStatus != NERR_Success ) {
  1120. if ( SidList != NULL ) {
  1121. AliaspFreeSidList( NameCount, SidList );
  1122. }
  1123. SidList = NULL;
  1124. }
  1125. //
  1126. // Return
  1127. //
  1128. *Sids = SidList;
  1129. return NetStatus;
  1130. }
  1131. VOID
  1132. AliaspFreeSidList (
  1133. IN DWORD SidCount,
  1134. IN PSID *Sids
  1135. )
  1136. /*++
  1137. Routine Description:
  1138. Free the SID list returned by AliaspNamesToSids
  1139. Arguments:
  1140. SidCount - Number of entries in the sid list
  1141. Sids - Aan array of pointers to SIDs.
  1142. Return Value:
  1143. None;
  1144. --*/
  1145. {
  1146. DWORD i;
  1147. if ( Sids != NULL ) {
  1148. for ( i=0; i<SidCount; i++ ) {
  1149. if ( Sids[i] != NULL ) {
  1150. NetpMemoryFree( Sids[i] );
  1151. }
  1152. }
  1153. (void) LocalFree( Sids );
  1154. }
  1155. }