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.

4928 lines
130 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. user.c
  5. Abstract:
  6. NetUser API functions
  7. Author:
  8. Cliff Van Dyke (cliffv) 26-Mar-1991
  9. Environment:
  10. User mode only.
  11. Contains NT-specific code.
  12. Requires ANSI C extensions: slash-slash comments, long external names.
  13. Revision History:
  14. 17-Apr-1991 (cliffv)
  15. Incorporated review comments.
  16. 20-Jan-1992 (madana)
  17. Sundry API changes
  18. 28-Nov-1992 (chuckc)
  19. Added stub for NetUserGetLocalGroups
  20. 1-Dec-1992 (chuckc)
  21. Added real code for NetUserGetLocalGroups
  22. --*/
  23. #include <nt.h>
  24. #include <ntrtl.h>
  25. #include <nturtl.h>
  26. #undef DOMAIN_ALL_ACCESS // defined in both ntsam.h and ntwinapi.h
  27. #include <ntsam.h>
  28. #include <ntlsa.h>
  29. #include <windef.h>
  30. #include <winbase.h>
  31. #include <lmcons.h>
  32. #include <access.h>
  33. #include <align.h>
  34. #include <lmapibuf.h>
  35. #include <lmaccess.h>
  36. #include <lmerr.h>
  37. #include <crypt.h>
  38. #include <ntmsv1_0.h>
  39. #include <limits.h>
  40. #include <netdebug.h>
  41. #include <netlib.h>
  42. #include <netlibnt.h>
  43. #include <rpcutil.h>
  44. #include <rxuser.h>
  45. #include <secobj.h>
  46. #include <stddef.h>
  47. #include <uasp.h>
  48. #include <accessp.h>
  49. /*lint -e614 */ /* Auto aggregate initializers need not be constant */
  50. // Lint complains about casts of one structure type to another.
  51. // That is done frequently in the code below.
  52. /*lint -e740 */ /* don't complain about unusual cast */
  53. //
  54. // Define the SAM info classes and pseudo-classes used in NetUserModalsSet.
  55. //
  56. // The values of these definitions must match the order of the
  57. // SamInfoClass array in NetUserModalsSet.
  58. //
  59. #define SAM_LogoffClass 0
  60. #define SAM_NameClass 1
  61. #define SAM_PasswordClass 2
  62. #define SAM_ReplicationClass 3
  63. #define SAM_ServerRoleClass 4
  64. #define SAM_LockoutClass 5
  65. //
  66. // Relate the NetUser API fields to the SAM API fields.
  67. //
  68. // This table contains as much information as possible to describe the
  69. // relationship between fields in the NetUser API and the SAM API.
  70. //
  71. struct _USER_UAS_SAM_TABLE {
  72. //
  73. // Describe the field types for UAS and SAM.
  74. //
  75. enum {
  76. UMT_STRING, // UAS is LPWSTR. SAM is UNICODE_STRING.
  77. UMT_USHORT, // UAS is DWORD. SAM is USHORT.
  78. UMT_ULONG, // UAS is DWORD. SAM is ULONG.
  79. UMT_ROLE, // UAS is role. SAM is enum.
  80. UMT_DELTA // UAS is delta seconds. SAM is LARGE_INTEGER.
  81. } ModalsFieldType;
  82. //
  83. // Define the UAS level and UAS parmnum for this field
  84. //
  85. DWORD UasLevel;
  86. DWORD UasParmNum;
  87. //
  88. // Describe the byte offset of the field in the appropriate UAS
  89. // and SAM structures.
  90. //
  91. DWORD UasOffset;
  92. DWORD SamOffset;
  93. //
  94. // Index to the structure describing the Sam Information class.
  95. //
  96. // If multiple fields use the same Sam information class, then
  97. // this field should have the same index for each such field.
  98. //
  99. DWORD Class;
  100. } UserUasSamTable[] = {
  101. { UMT_USHORT, 0, MODALS_MIN_PASSWD_LEN_PARMNUM,
  102. offsetof( USER_MODALS_INFO_0, usrmod0_min_passwd_len ),
  103. offsetof( DOMAIN_PASSWORD_INFORMATION, MinPasswordLength ),
  104. SAM_PasswordClass },
  105. { UMT_USHORT, 1001, MODALS_MIN_PASSWD_LEN_PARMNUM,
  106. offsetof( USER_MODALS_INFO_1001, usrmod1001_min_passwd_len ),
  107. offsetof( DOMAIN_PASSWORD_INFORMATION, MinPasswordLength ),
  108. SAM_PasswordClass },
  109. { UMT_DELTA, 0, MODALS_MAX_PASSWD_AGE_PARMNUM,
  110. offsetof( USER_MODALS_INFO_0, usrmod0_max_passwd_age ),
  111. offsetof( DOMAIN_PASSWORD_INFORMATION, MaxPasswordAge ),
  112. SAM_PasswordClass },
  113. { UMT_DELTA, 1002, MODALS_MAX_PASSWD_AGE_PARMNUM,
  114. offsetof( USER_MODALS_INFO_1002, usrmod1002_max_passwd_age ),
  115. offsetof( DOMAIN_PASSWORD_INFORMATION, MaxPasswordAge ),
  116. SAM_PasswordClass },
  117. { UMT_DELTA, 0, MODALS_MIN_PASSWD_AGE_PARMNUM,
  118. offsetof( USER_MODALS_INFO_0, usrmod0_min_passwd_age ),
  119. offsetof( DOMAIN_PASSWORD_INFORMATION, MinPasswordAge ),
  120. SAM_PasswordClass },
  121. { UMT_DELTA, 1003, MODALS_MIN_PASSWD_AGE_PARMNUM,
  122. offsetof( USER_MODALS_INFO_1003, usrmod1003_min_passwd_age ),
  123. offsetof( DOMAIN_PASSWORD_INFORMATION, MinPasswordAge ),
  124. SAM_PasswordClass },
  125. { UMT_DELTA, 0, MODALS_FORCE_LOGOFF_PARMNUM,
  126. offsetof( USER_MODALS_INFO_0, usrmod0_force_logoff ),
  127. offsetof( DOMAIN_LOGOFF_INFORMATION, ForceLogoff ),
  128. SAM_LogoffClass },
  129. { UMT_DELTA, 1004, MODALS_FORCE_LOGOFF_PARMNUM,
  130. offsetof( USER_MODALS_INFO_1004, usrmod1004_force_logoff ),
  131. offsetof( DOMAIN_LOGOFF_INFORMATION, ForceLogoff ),
  132. SAM_LogoffClass },
  133. { UMT_USHORT, 0, MODALS_PASSWD_HIST_LEN_PARMNUM,
  134. offsetof( USER_MODALS_INFO_0, usrmod0_password_hist_len ),
  135. offsetof( DOMAIN_PASSWORD_INFORMATION, PasswordHistoryLength ),
  136. SAM_PasswordClass },
  137. { UMT_USHORT, 1005, MODALS_PASSWD_HIST_LEN_PARMNUM,
  138. offsetof( USER_MODALS_INFO_1005, usrmod1005_password_hist_len ),
  139. offsetof( DOMAIN_PASSWORD_INFORMATION, PasswordHistoryLength ),
  140. SAM_PasswordClass },
  141. { UMT_ROLE, 1, MODALS_ROLE_PARMNUM,
  142. offsetof( USER_MODALS_INFO_1, usrmod1_role ),
  143. offsetof( DOMAIN_SERVER_ROLE_INFORMATION, DomainServerRole ),
  144. SAM_ServerRoleClass },
  145. { UMT_ROLE, 1006, MODALS_ROLE_PARMNUM,
  146. offsetof( USER_MODALS_INFO_1006, usrmod1006_role ),
  147. offsetof( DOMAIN_SERVER_ROLE_INFORMATION, DomainServerRole ),
  148. SAM_ServerRoleClass },
  149. { UMT_STRING, 1, MODALS_PRIMARY_PARMNUM,
  150. offsetof( USER_MODALS_INFO_1, usrmod1_primary ),
  151. offsetof( DOMAIN_REPLICATION_INFORMATION, ReplicaSourceNodeName ),
  152. SAM_ReplicationClass },
  153. { UMT_STRING, 1007, MODALS_PRIMARY_PARMNUM,
  154. offsetof( USER_MODALS_INFO_1007, usrmod1007_primary ),
  155. offsetof( DOMAIN_REPLICATION_INFORMATION, ReplicaSourceNodeName ),
  156. SAM_ReplicationClass },
  157. { UMT_STRING, 2, MODALS_DOMAIN_NAME_PARMNUM,
  158. offsetof( USER_MODALS_INFO_2, usrmod2_domain_name ),
  159. offsetof( DOMAIN_NAME_INFORMATION, DomainName ),
  160. SAM_NameClass },
  161. { UMT_DELTA, 3, MODALS_LOCKOUT_DURATION_PARMNUM,
  162. offsetof( USER_MODALS_INFO_3, usrmod3_lockout_duration ),
  163. offsetof( DOMAIN_LOCKOUT_INFORMATION, LockoutDuration ),
  164. SAM_LockoutClass },
  165. { UMT_DELTA, 3, MODALS_LOCKOUT_OBSERVATION_WINDOW_PARMNUM,
  166. offsetof( USER_MODALS_INFO_3, usrmod3_lockout_observation_window ),
  167. offsetof( DOMAIN_LOCKOUT_INFORMATION, LockoutObservationWindow ),
  168. SAM_LockoutClass },
  169. { UMT_USHORT, 3, MODALS_LOCKOUT_THRESHOLD_PARMNUM,
  170. offsetof( USER_MODALS_INFO_3, usrmod3_lockout_threshold ),
  171. offsetof( DOMAIN_LOCKOUT_INFORMATION, LockoutThreshold ),
  172. SAM_LockoutClass },
  173. };
  174. NET_API_STATUS NET_API_FUNCTION
  175. NetUserAdd(
  176. IN LPCWSTR ServerName OPTIONAL,
  177. IN DWORD Level,
  178. IN LPBYTE Buffer,
  179. OUT LPDWORD ParmError OPTIONAL // Name required by NetpSetParmError
  180. )
  181. /*++
  182. Routine Description:
  183. Create a user account in the user accounts database.
  184. Arguments:
  185. ServerName - A pointer to a string containing the name of the remote
  186. server on which the function is to execute. A NULL pointer
  187. or string specifies the local machine.
  188. Level - Level of information provided. Must be 1, 2, 3 or 22.
  189. Buffer - A pointer to the buffer containing the user information
  190. structure.
  191. ParmError - Optional pointer to a DWORD to return the index of the
  192. first parameter in error when ERROR_INVALID_PARAMETER is returned.
  193. If NULL, the parameter is not returned on error.
  194. Return Value:
  195. Error code for the operation.
  196. --*/
  197. {
  198. UNICODE_STRING UserNameString;
  199. NET_API_STATUS NetStatus;
  200. NTSTATUS Status;
  201. SAM_HANDLE SamServerHandle = NULL;
  202. SAM_HANDLE UserHandle = NULL;
  203. SAM_HANDLE DomainHandle = NULL;
  204. ULONG RelativeId;
  205. ULONG GrantedAccess;
  206. ULONG NewSamAccountType;
  207. DWORD UasUserFlags;
  208. ULONG WhichFieldsMask = 0xFFFFFFFF;
  209. //
  210. // Variables for building the new user's Sid
  211. //
  212. PSID DomainId = NULL; // Domain Id of the primary domain
  213. IF_DEBUG( UAS_DEBUG_USER ) {
  214. NetpKdPrint(( "NetUserAdd: entered \n"));
  215. }
  216. //
  217. // Initialize
  218. //
  219. NetpSetParmError( PARM_ERROR_NONE );
  220. //
  221. // Validate Level parameter.
  222. //
  223. switch (Level) {
  224. case 1:
  225. case 2:
  226. case 3:
  227. case 4:
  228. NetpAssert ( offsetof( USER_INFO_1, usri1_flags ) ==
  229. offsetof( USER_INFO_2, usri2_flags ) );
  230. NetpAssert ( offsetof( USER_INFO_1, usri1_flags ) ==
  231. offsetof( USER_INFO_3, usri3_flags ) );
  232. NetpAssert ( offsetof( USER_INFO_1, usri1_flags ) ==
  233. offsetof( USER_INFO_4, usri4_flags ) );
  234. UasUserFlags = ((PUSER_INFO_1)Buffer)->usri1_flags;
  235. break;
  236. case 22:
  237. UasUserFlags = ((PUSER_INFO_22)Buffer)->usri22_flags;
  238. break;
  239. default:
  240. return ERROR_INVALID_LEVEL; // Nothing to cleanup yet
  241. }
  242. //
  243. // Determine the account type we're creating.
  244. //
  245. if( UasUserFlags & UF_ACCOUNT_TYPE_MASK ) {
  246. //
  247. // Account Types bits are exclusive, so make sure that
  248. // precisely one Account Type bit is set.
  249. //
  250. if ( !JUST_ONE_BIT( UasUserFlags & UF_ACCOUNT_TYPE_MASK )) {
  251. NetpSetParmError( USER_FLAGS_PARMNUM );
  252. NetStatus = ERROR_INVALID_PARAMETER;
  253. IF_DEBUG( UAS_DEBUG_USER ) {
  254. NetpKdPrint((
  255. "NetUserAdd: Invalid account control bits (2) \n" ));
  256. }
  257. goto Cleanup;
  258. }
  259. //
  260. // Determine what the new account type should be.
  261. //
  262. if ( UasUserFlags & UF_TEMP_DUPLICATE_ACCOUNT ) {
  263. NewSamAccountType = USER_TEMP_DUPLICATE_ACCOUNT;
  264. } else if ( UasUserFlags & UF_NORMAL_ACCOUNT ) {
  265. NewSamAccountType = USER_NORMAL_ACCOUNT;
  266. } else if (UasUserFlags & UF_WORKSTATION_TRUST_ACCOUNT){
  267. NewSamAccountType = USER_WORKSTATION_TRUST_ACCOUNT;
  268. // Because of a bug in NT 3.5x, we have to initially create SERVER
  269. // and interdomain trust accounts as normal accounts and change them
  270. // later. Specifically, SAM didn't call I_NetNotifyMachineAccount
  271. // in SamCreateUser2InDomain. Therefore, netlogon didn't get notified
  272. // of the change. That bug is fixed in NT 4.0.
  273. //
  274. // In NT 5.0, we relaxed that restriction for BDC accounts. An NT 5.0
  275. // client creating a BDC account on an NT 3.5x DC will have the problem
  276. // above. However, by making the change, an NT 5.0 BDC creating a BDC
  277. // account on an NT 5.0 DC will properly create the BDC account as a
  278. // Computer object.
  279. //
  280. } else if ( UasUserFlags & UF_SERVER_TRUST_ACCOUNT ) {
  281. NewSamAccountType = USER_SERVER_TRUST_ACCOUNT;
  282. } else if (UasUserFlags & UF_INTERDOMAIN_TRUST_ACCOUNT){
  283. NewSamAccountType = USER_NORMAL_ACCOUNT;
  284. } else {
  285. IF_DEBUG( UAS_DEBUG_USER ) {
  286. NetpKdPrint((
  287. "NetUserAdd: Invalid account type (3)\n"));
  288. }
  289. NetStatus = NERR_InternalError;
  290. goto Cleanup;
  291. }
  292. //
  293. // If SAM has none of its bits set,
  294. // set USER_NORMAL_ACCOUNT.
  295. //
  296. } else {
  297. NewSamAccountType = USER_NORMAL_ACCOUNT;
  298. }
  299. //
  300. // Connect to the SAM server
  301. //
  302. NetStatus = UaspOpenSam( ServerName,
  303. FALSE, // Don't try null session
  304. &SamServerHandle );
  305. if ( NetStatus != NERR_Success ) {
  306. IF_DEBUG( UAS_DEBUG_USER ) {
  307. NetpKdPrint(( "NetUserAdd: Cannot UaspOpenSam %ld\n", NetStatus ));
  308. }
  309. goto Cleanup;
  310. }
  311. //
  312. // Open the Domain asking for DOMAIN_CREATE_USER access.
  313. //
  314. // DOMAIN_LOOKUP is needed to lookup group memberships later.
  315. //
  316. // DOMAIN_READ_PASSWORD_PARAMETERS is needed in those cases that we'll
  317. // set the password on the account.
  318. //
  319. NetStatus = UaspOpenDomain(
  320. SamServerHandle,
  321. DOMAIN_CREATE_USER | DOMAIN_LOOKUP |
  322. DOMAIN_READ_PASSWORD_PARAMETERS,
  323. TRUE, // Account Domain
  324. &DomainHandle,
  325. &DomainId );
  326. if ( NetStatus == ERROR_ACCESS_DENIED &&
  327. NewSamAccountType == USER_WORKSTATION_TRUST_ACCOUNT ) {
  328. // Workstation accounts can be created with either DOMAIN_CREATE_USER access
  329. // or SE_CREATE_MACHINE_ACCOUNT_PRIVILEGE. So we'll try both.
  330. // In the later case, we probably will only have access to the account
  331. // to set the password, so we'll avoid setting any other parameters on the
  332. // account.
  333. //
  334. NetStatus = UaspOpenDomain(
  335. SamServerHandle,
  336. DOMAIN_LOOKUP | DOMAIN_READ_PASSWORD_PARAMETERS,
  337. TRUE, // Account Domain
  338. &DomainHandle,
  339. &DomainId );
  340. WhichFieldsMask = USER_ALL_NTPASSWORDPRESENT;
  341. }
  342. if ( NetStatus != NERR_Success ) {
  343. IF_DEBUG( UAS_DEBUG_USER ) {
  344. NetpKdPrint(( "NetUserAdd: UaspOpenDomain returns %ld\n",
  345. NetStatus ));
  346. }
  347. goto Cleanup;
  348. }
  349. //
  350. // Create the User with the specified name
  351. // Create workstation trust accounts(and default security descriptor).
  352. //
  353. RtlInitUnicodeString( &UserNameString, ((PUSER_INFO_1)Buffer)->usri1_name );
  354. Status = SamCreateUser2InDomain(
  355. DomainHandle,
  356. &UserNameString,
  357. NewSamAccountType,
  358. GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE |
  359. WRITE_DAC | DELETE | USER_FORCE_PASSWORD_CHANGE |
  360. USER_READ_ACCOUNT | USER_WRITE_ACCOUNT,
  361. &UserHandle,
  362. &GrantedAccess,
  363. &RelativeId );
  364. if ( !NT_SUCCESS(Status) ) {
  365. IF_DEBUG( UAS_DEBUG_USER ) {
  366. NetpKdPrint(( "NetUserAdd: SamCreateUserInDomain rets %lX\n",
  367. Status ));
  368. }
  369. NetStatus = NetpNtStatusToApiStatus( Status );
  370. goto Cleanup;
  371. }
  372. //
  373. // Set all the other attributes for this user
  374. //
  375. NetStatus = UserpSetInfo(
  376. DomainHandle,
  377. DomainId,
  378. UserHandle,
  379. NULL, // BuiltinDomainHandle not needed for create case
  380. RelativeId,
  381. ((PUSER_INFO_1)Buffer)->usri1_name,
  382. Level,
  383. Buffer,
  384. WhichFieldsMask,
  385. ParmError );
  386. if ( NetStatus != NERR_Success ) {
  387. IF_DEBUG( UAS_DEBUG_USER ) {
  388. NetpKdPrint(( "NetUserAdd: UserpSetInfo returns %ld\n",
  389. NetStatus ));
  390. }
  391. goto Cleanup;
  392. }
  393. //
  394. // Done
  395. //
  396. NetStatus = NERR_Success;
  397. //
  398. // Clean up
  399. //
  400. Cleanup:
  401. //
  402. // Delete the user or close the handle depending on success or failure.
  403. //
  404. if ( UserHandle != NULL ) {
  405. if ( NetStatus != NERR_Success ) {
  406. (VOID) SamDeleteUser( UserHandle );
  407. } else {
  408. (VOID) SamCloseHandle( UserHandle );
  409. }
  410. }
  411. //
  412. // Free locally used resources.
  413. //
  414. if ( DomainHandle != NULL ) {
  415. UaspCloseDomain( DomainHandle );
  416. }
  417. if ( SamServerHandle != NULL ) {
  418. (VOID) SamCloseHandle( SamServerHandle );
  419. }
  420. if ( DomainId != NULL ) {
  421. NetpMemoryFree( DomainId );
  422. }
  423. //
  424. // Handle downlevel.
  425. //
  426. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  427. NetStatus = RxNetUserAdd( (LPWSTR) ServerName, Level, Buffer, ParmError );
  428. UASP_DOWNLEVEL_END;
  429. IF_DEBUG( UAS_DEBUG_USER ) {
  430. NetpKdPrint(( "NetUserAdd: returning %ld\n", NetStatus ));
  431. }
  432. return NetStatus;
  433. } // NetUserAdd
  434. NET_API_STATUS NET_API_FUNCTION
  435. NetUserDel(
  436. IN LPCWSTR ServerName OPTIONAL,
  437. IN LPCWSTR UserName
  438. )
  439. /*++
  440. Routine Description:
  441. Delete a User
  442. Arguments:
  443. ServerName - A pointer to a string containing the name of the remote
  444. server on which the function is to execute. A NULL pointer
  445. or string specifies the local machine.
  446. UserName - Name of the user to delete.
  447. Return Value:
  448. Error code for the operation.
  449. --*/
  450. {
  451. NET_API_STATUS NetStatus;
  452. NTSTATUS Status;
  453. SAM_HANDLE SamServerHandle = NULL;
  454. SAM_HANDLE DomainHandle = NULL;
  455. SAM_HANDLE BuiltinDomainHandle = NULL;
  456. SAM_HANDLE UserHandle = NULL;
  457. PSID DomainId = NULL; // Domain Id of the primary domain
  458. ULONG UserRelativeId; // RelativeId of the user being deleted
  459. PSID UserSid = NULL;
  460. //
  461. // Connect to the SAM server
  462. //
  463. NetStatus = UaspOpenSam( ServerName,
  464. FALSE, // Don't try null session
  465. &SamServerHandle );
  466. if ( NetStatus != NERR_Success ) {
  467. IF_DEBUG( UAS_DEBUG_USER ) {
  468. NetpKdPrint(( "NetUserDel: Cannot UaspOpenSam %ld\n", NetStatus ));
  469. }
  470. goto Cleanup;
  471. }
  472. //
  473. // Open the Domain
  474. //
  475. NetStatus = UaspOpenDomain( SamServerHandle,
  476. DOMAIN_LOOKUP,
  477. TRUE, // Account Domain
  478. &DomainHandle,
  479. &DomainId );
  480. if ( NetStatus != NERR_Success ) {
  481. goto Cleanup;
  482. }
  483. //
  484. // Open the Builtin Domain.
  485. //
  486. NetStatus = UaspOpenDomain( SamServerHandle,
  487. DOMAIN_LOOKUP,
  488. FALSE, // Builtin Domain
  489. &BuiltinDomainHandle,
  490. NULL ); // DomainId
  491. if ( NetStatus != NERR_Success ) {
  492. goto Cleanup;
  493. }
  494. //
  495. // Open the user asking for delete access.
  496. //
  497. NetStatus = UserpOpenUser( DomainHandle,
  498. DELETE,
  499. UserName,
  500. &UserHandle,
  501. &UserRelativeId );
  502. if ( NetStatus != NERR_Success ) {
  503. IF_DEBUG( UAS_DEBUG_USER ) {
  504. NetpKdPrint(( "NetUserDel: UserpOpenUser returns %ld\n",
  505. NetStatus ));
  506. }
  507. goto Cleanup;
  508. }
  509. //
  510. // Determine the SID of the User being deleted.
  511. //
  512. NetStatus = NetpSamRidToSid( UserHandle,
  513. UserRelativeId,
  514. &UserSid );
  515. if ( NetStatus != NERR_Success ) {
  516. goto Cleanup;
  517. }
  518. //
  519. // Delete any aliases to this user from the Builtin domain
  520. //
  521. Status = SamRemoveMemberFromForeignDomain( BuiltinDomainHandle,
  522. UserSid );
  523. if ( !NT_SUCCESS(Status) ) {
  524. IF_DEBUG( UAS_DEBUG_USER ) {
  525. NetpKdPrint((
  526. "NetUserDel: SamRemoveMembershipFromForeignDomain returns %lX\n",
  527. Status ));
  528. }
  529. NetStatus = NetpNtStatusToApiStatus( Status );
  530. goto Cleanup;
  531. }
  532. //
  533. // Delete the user.
  534. //
  535. Status = SamDeleteUser( UserHandle );
  536. if ( !NT_SUCCESS(Status) ) {
  537. IF_DEBUG( UAS_DEBUG_USER ) {
  538. NetpKdPrint(( "NetUserDel: SamDeleteUser returns %lX\n", Status ));
  539. }
  540. NetStatus = NetpNtStatusToApiStatus( Status );
  541. goto Cleanup;
  542. }
  543. NetStatus = NERR_Success;
  544. UserHandle = NULL; // Don't touch the handle to a deleted user
  545. //
  546. // Clean up.
  547. //
  548. Cleanup:
  549. if ( UserHandle != NULL ) {
  550. (VOID) SamCloseHandle( UserHandle );
  551. }
  552. if ( DomainHandle != NULL ) {
  553. UaspCloseDomain( DomainHandle );
  554. }
  555. if ( BuiltinDomainHandle != NULL ) {
  556. UaspCloseDomain( BuiltinDomainHandle );
  557. }
  558. if ( SamServerHandle != NULL ) {
  559. (VOID) SamCloseHandle( SamServerHandle );
  560. }
  561. if ( DomainId != NULL ) {
  562. NetpMemoryFree( DomainId );
  563. }
  564. if ( UserSid != NULL ) {
  565. NetpMemoryFree( UserSid );
  566. }
  567. //
  568. // Handle downlevel.
  569. //
  570. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  571. NetStatus = RxNetUserDel( (LPWSTR)ServerName, (LPWSTR)UserName );
  572. UASP_DOWNLEVEL_END;
  573. IF_DEBUG( UAS_DEBUG_USER ) {
  574. NetpKdPrint(( "NetUserDel: returning %ld\n", NetStatus ));
  575. }
  576. return NetStatus;
  577. } // NetUserDel
  578. ULONG
  579. UserpComputeSamPrefMaxLen(
  580. IN DWORD Level,
  581. IN DWORD NetUserPrefMaxLen,
  582. IN DWORD NetUserBytesAlreadyReturned,
  583. IN DWORD SamBytesAlreadyReturned
  584. )
  585. /*++
  586. Routine Description:
  587. This routine is a helper function for NetUserEnum. NetUserEnum enumerates
  588. the appropriate users by calling SamEnumerateUsersInDomain. NetUserEnum builds
  589. the appropriate return structure for each such enumerated user.
  590. SamEnumerateUsersInDomain returns a resume handle as does NetUserEnum. If
  591. NetUserEnum were to return to its caller without having processed all of the
  592. entries returned from SAM, NetUserEnum would have to "compute" a resume handle
  593. corresponding to an intermediate entry returned from SAM. That's impossible
  594. (except in the special cases where no "filter" parameter is passed to SAM).
  595. Instead, we choose to pass SamEnumerateUsersInDomain a PrefMaxLen which will
  596. attempt to enumerate exactly the right number of users that NetUserEnum can
  597. pack into its PrefMaxLen buffer.
  598. Since the size of the structure returned from SAM is different than the size of
  599. the structure returned from it is difficult to determine an optimal PrefMaxLen
  600. to pass to SamEnumerateUsersInDomain. This routine attempts to do that.
  601. We realise that this algorithm may cause NetUserEnum to exceed its PrefMaxLen by
  602. a significant amount.
  603. Arguments:
  604. Level - The NetUserEnum info level.
  605. NetUserPrefMaxLen - The NetUserEnum prefered maximum length of returned data.
  606. NetUserBytesAlreadyReturned - The number of bytes already packed by
  607. NetUserEnum
  608. SamBytesAlreadyReturned - The number of bytes already returned by
  609. SamEnumerateUserInDomain.
  610. Return Value:
  611. Value to use as PrefMaxLen on next call to SamEnumerateUsersInDomain.
  612. --*/
  613. {
  614. ULONG RemainingPrefMaxLen;
  615. ULARGE_INTEGER LargeTemp;
  616. ULONG SamPrefMaxLen;
  617. //
  618. // If caller simply wants ALL the data,
  619. // ask SAM for the same thing.
  620. //
  621. if ( NetUserPrefMaxLen == 0xFFFFFFFF ) {
  622. IF_DEBUG( UAS_DEBUG_USER ) {
  623. NetpKdPrint(("SamPrefMaxLen: Net Pref: %ld Net bytes: %ld Sam Bytes: %ld Sam Pref: %ld\n",
  624. NetUserPrefMaxLen, NetUserBytesAlreadyReturned, SamBytesAlreadyReturned, NetUserPrefMaxLen ));
  625. }
  626. return NetUserPrefMaxLen;
  627. }
  628. //
  629. // If no bytes have been returned yet,
  630. // use sample data based on a sample domain (REDMOND).
  631. // Since the information returned by SAM and NetUserEnum is variable
  632. // length, there is no way to compute a value.
  633. //
  634. if ( NetUserBytesAlreadyReturned == 0 ) {
  635. //
  636. // Use a different constant for each info level.
  637. //
  638. switch ( Level ) {
  639. case 0:
  640. SamBytesAlreadyReturned = 1;
  641. NetUserBytesAlreadyReturned = 1;
  642. break;
  643. case 2:
  644. case 3:
  645. case 11:
  646. SamBytesAlreadyReturned = 1;
  647. NetUserBytesAlreadyReturned = 10;
  648. break;
  649. case 1:
  650. case 10:
  651. case 20:
  652. SamBytesAlreadyReturned = 1;
  653. NetUserBytesAlreadyReturned = 4;
  654. break;
  655. default:
  656. SamBytesAlreadyReturned = 1;
  657. NetUserBytesAlreadyReturned = 1;
  658. break;
  659. }
  660. }
  661. //
  662. // Use the above computed divisor to compute the desired number of bytes to
  663. // enumerate from SAM.
  664. //
  665. if ( NetUserBytesAlreadyReturned >= NetUserPrefMaxLen ) {
  666. RemainingPrefMaxLen = 0;
  667. } else {
  668. RemainingPrefMaxLen = NetUserPrefMaxLen - NetUserBytesAlreadyReturned;
  669. }
  670. LargeTemp.QuadPart = UInt32x32To64 ( RemainingPrefMaxLen, SamBytesAlreadyReturned );
  671. SamPrefMaxLen = (ULONG)(LargeTemp.QuadPart / (ULONGLONG) NetUserBytesAlreadyReturned);
  672. //
  673. // Ensure we always make reasonable progress by returning at least 5
  674. // entries from SAM (unless the caller is really conservative).
  675. //
  676. #define MIN_SAM_ENUMERATION \
  677. ((sizeof(SAM_RID_ENUMERATION) + LM20_UNLEN * sizeof(WCHAR) + sizeof(WCHAR)))
  678. #define TYPICAL_SAM_ENUMERATION \
  679. (MIN_SAM_ENUMERATION * 5)
  680. if ( SamPrefMaxLen < TYPICAL_SAM_ENUMERATION && NetUserPrefMaxLen > 1 ) {
  681. SamPrefMaxLen = TYPICAL_SAM_ENUMERATION;
  682. } else if ( SamPrefMaxLen < MIN_SAM_ENUMERATION ) {
  683. SamPrefMaxLen = MIN_SAM_ENUMERATION;
  684. }
  685. IF_DEBUG( UAS_DEBUG_USER ) {
  686. NetpKdPrint(("SamPrefMaxLen: Net Pref: %ld Net bytes: %ld Sam Bytes: %ld Sam Pref: %ld\n",
  687. NetUserPrefMaxLen, NetUserBytesAlreadyReturned, SamBytesAlreadyReturned, SamPrefMaxLen ));
  688. }
  689. return SamPrefMaxLen;
  690. }
  691. NET_API_STATUS NET_API_FUNCTION
  692. NetUserEnum(
  693. IN LPCWSTR ServerName OPTIONAL,
  694. IN DWORD Level,
  695. IN DWORD Filter,
  696. OUT LPBYTE *Buffer,
  697. IN DWORD PrefMaxLen,
  698. OUT LPDWORD EntriesRead,
  699. OUT LPDWORD EntriesLeft,
  700. IN OUT LPDWORD ResumeHandle OPTIONAL
  701. )
  702. /*++
  703. Routine Description:
  704. Retrieve information about each user on a server.
  705. Arguments:
  706. ServerName - A pointer to a string containing the name of the remote
  707. server on which the function is to execute. A NULL pointer
  708. or string specifies the local machine.
  709. Level - Level of information required. level 0, 1, 2, 3, 10,
  710. and 20 are valid
  711. Filter - Returns the user accounts of the type specified here. Combination
  712. of the following types may be specified as filter parameter.
  713. #define FILTER_TEMP_DUPLICATE_ACCOUNT (0x0001)
  714. #define FILTER_NORMAL_ACCOUNT (0x0002)
  715. #define FILTER_INTERDOMAIN_TRUST_ACCOUNT (0x0008)
  716. #define FILTER_WORKSTATION_TRUST_ACCOUNT (0x0010)
  717. #define FILTER_SERVER_TRUST_ACCOUNT (0x0020)
  718. Buffer - Returns a pointer to the return information structure.
  719. Caller must deallocate buffer using NetApiBufferFree.
  720. PrefMaxLen - Prefered maximum length of returned data.
  721. EntriesRead - Returns the actual enumerated element count.
  722. EntriesLeft - Returns the total entries available to be enumerated.
  723. ResumeHandle - Used to continue an existing search. The handle should
  724. be zero on the first call and left unchanged for subsequent calls.
  725. Return Value:
  726. Error code for the operation.
  727. --*/
  728. {
  729. NET_API_STATUS NetStatus;
  730. NTSTATUS Status;
  731. NTSTATUS CachedStatus;
  732. BUFFER_DESCRIPTOR BufferDescriptor;
  733. SAM_HANDLE SamServerHandle = NULL;
  734. SAM_HANDLE DomainHandle = NULL;
  735. SAM_HANDLE BuiltinDomainHandle = NULL;
  736. PSID DomainId = NULL;
  737. ULONG TotalRemaining = 0;
  738. SAM_ENUMERATE_HANDLE EnumHandle;
  739. PSAM_RID_ENUMERATION EnumBuffer = NULL;
  740. DWORD CountReturned = 0;
  741. BOOL AllDone = FALSE;
  742. SAM_ENUMERATE_HANDLE LocalEnumHandle;
  743. DWORD LocalResumeHandle;
  744. DWORD SamFilter;
  745. DWORD SamPrefMaxLen;
  746. DWORD NetUserBytesAlreadyReturned;
  747. DWORD SamBytesAlreadyReturned;
  748. DWORD Mode = SAM_SID_COMPATIBILITY_ALL;
  749. #define USERACCOUNTCONTROL( _f ) ( \
  750. ( ( (_f) & FILTER_TEMP_DUPLICATE_ACCOUNT ) ? \
  751. USER_TEMP_DUPLICATE_ACCOUNT : 0 ) | \
  752. ( ( (_f) & FILTER_NORMAL_ACCOUNT ) ? \
  753. USER_NORMAL_ACCOUNT : 0 ) | \
  754. ( ( (_f) & FILTER_INTERDOMAIN_TRUST_ACCOUNT ) ? \
  755. USER_INTERDOMAIN_TRUST_ACCOUNT : 0 ) | \
  756. ( ( (_f) & FILTER_WORKSTATION_TRUST_ACCOUNT ) ? \
  757. USER_WORKSTATION_TRUST_ACCOUNT : 0 ) | \
  758. ( ( (_f) & FILTER_SERVER_TRUST_ACCOUNT ) ? \
  759. USER_SERVER_TRUST_ACCOUNT : 0 ) \
  760. )
  761. //
  762. // Pick up the resume handle.
  763. //
  764. // Do this early to ensure we don't scrog the ResumeHandle in
  765. // case we go downlevel.
  766. //
  767. if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
  768. LocalResumeHandle = *ResumeHandle;
  769. } else {
  770. LocalResumeHandle = 0;
  771. }
  772. EnumHandle = (SAM_ENUMERATE_HANDLE) LocalResumeHandle;
  773. //
  774. // Initialization
  775. //
  776. *Buffer = NULL;
  777. *EntriesRead = 0;
  778. *EntriesLeft = 0;
  779. RtlZeroMemory(
  780. &BufferDescriptor,
  781. sizeof(BUFFER_DESCRIPTOR)
  782. );
  783. SamFilter = USERACCOUNTCONTROL( Filter );
  784. //
  785. // Connect to the SAM server
  786. //
  787. NetStatus = UaspOpenSam( ServerName,
  788. FALSE, // Don't try null session
  789. &SamServerHandle );
  790. if ( NetStatus != NERR_Success ) {
  791. IF_DEBUG( UAS_DEBUG_USER ) {
  792. NetpKdPrint(( "NetUserEnum: Cannot UaspOpenSam %ld\n", NetStatus ));
  793. }
  794. goto Cleanup;
  795. }
  796. Status = SamGetCompatibilityMode(SamServerHandle,
  797. &Mode);
  798. if (NT_SUCCESS(Status)) {
  799. if ( (Mode == SAM_SID_COMPATIBILITY_STRICT)
  800. && ( Level == 3 || Level == 20 ) ) {
  801. //
  802. // These info levels return RID's
  803. //
  804. Status = STATUS_NOT_SUPPORTED;
  805. }
  806. }
  807. if (!NT_SUCCESS(Status)) {
  808. NetStatus = NetpNtStatusToApiStatus( Status );
  809. goto Cleanup;
  810. }
  811. //
  812. // Validate Level parameter
  813. //
  814. switch (Level) {
  815. case 1:
  816. case 2:
  817. case 3:
  818. case 11:
  819. //
  820. // Open the Builtin Domain.
  821. //
  822. NetStatus = UaspOpenDomain( SamServerHandle,
  823. DOMAIN_GET_ALIAS_MEMBERSHIP,
  824. FALSE, // Builtin Domain
  825. &BuiltinDomainHandle,
  826. NULL ); // DomainId
  827. if ( NetStatus != NERR_Success ) {
  828. goto Cleanup;
  829. }
  830. case 0:
  831. case 10:
  832. case 20:
  833. break;
  834. default:
  835. NetStatus = ERROR_INVALID_LEVEL;
  836. goto Cleanup;
  837. }
  838. //
  839. // Open the Account Domain.
  840. //
  841. NetStatus = UaspOpenDomain( SamServerHandle,
  842. DOMAIN_LIST_ACCOUNTS |
  843. DOMAIN_READ_OTHER_PARAMETERS,
  844. TRUE, // Account Domain
  845. &DomainHandle,
  846. &DomainId );
  847. if ( NetStatus != NERR_Success ) {
  848. goto Cleanup;
  849. }
  850. //
  851. // Get the total number of users from SAM
  852. //
  853. //
  854. // the only way to get the total number of specified accounts is
  855. // enumerate the specified accounts till there is no more accounts
  856. // and add all CountReturned.
  857. //
  858. TotalRemaining = 0;
  859. LocalEnumHandle = EnumHandle;
  860. SamPrefMaxLen = UserpComputeSamPrefMaxLen(
  861. Level,
  862. PrefMaxLen,
  863. 0, // NetUserBytesAlreadyReturned,
  864. 0 );// SamBytesAlreadyReturned
  865. SamBytesAlreadyReturned = SamPrefMaxLen;
  866. do {
  867. NTSTATUS LocalStatus;
  868. PSAM_RID_ENUMERATION LocalEnumBuffer = NULL;
  869. DWORD LocalCountReturned;
  870. IF_DEBUG( UAS_DEBUG_USER ) {
  871. NetpKdPrint(("Calling Enumerate phase 1: PrefLen %ld\n", SamPrefMaxLen ));
  872. }
  873. Status = SamEnumerateUsersInDomain(
  874. DomainHandle,
  875. &LocalEnumHandle,
  876. SamFilter,
  877. (PVOID *) &LocalEnumBuffer,
  878. SamPrefMaxLen,
  879. &LocalCountReturned
  880. );
  881. if ( !NT_SUCCESS(Status) ) {
  882. NetStatus = NetpNtStatusToApiStatus( Status );
  883. if(LocalEnumBuffer != NULL ) {
  884. Status = SamFreeMemory( LocalEnumBuffer );
  885. NetpAssert( NT_SUCCESS( Status ) );
  886. }
  887. goto Cleanup;
  888. }
  889. //
  890. // aggrigate total count.
  891. //
  892. IF_DEBUG( UAS_DEBUG_USER ) {
  893. NetpKdPrint(("Enumerate phase 1: Returned %ld entries\n", LocalCountReturned ));
  894. }
  895. TotalRemaining += LocalCountReturned;
  896. //
  897. // cache first enum buffer to use it in the loop below.
  898. //
  899. if( EnumBuffer == NULL ) {
  900. EnumBuffer = LocalEnumBuffer;
  901. EnumHandle = LocalEnumHandle;
  902. CountReturned = LocalCountReturned;
  903. CachedStatus = Status;
  904. // Subsequent calls can use a reasonably large buffer size.
  905. if ( SamPrefMaxLen < NETP_ENUM_GUESS ) {
  906. SamPrefMaxLen = NETP_ENUM_GUESS;
  907. }
  908. } else {
  909. LocalStatus = SamFreeMemory( LocalEnumBuffer );
  910. NetpAssert( NT_SUCCESS( LocalStatus ) );
  911. }
  912. } while ( Status == STATUS_MORE_ENTRIES );
  913. //
  914. // Loop for each user
  915. //
  916. //
  917. NetUserBytesAlreadyReturned = 0;
  918. for ( ;; ) {
  919. DWORD i;
  920. //
  921. // use cached enum buffer if one available
  922. //
  923. if( EnumBuffer != NULL ) {
  924. Status = CachedStatus;
  925. } else {
  926. SamPrefMaxLen = UserpComputeSamPrefMaxLen(
  927. Level,
  928. PrefMaxLen,
  929. NetUserBytesAlreadyReturned,
  930. SamBytesAlreadyReturned );
  931. IF_DEBUG( UAS_DEBUG_USER ) {
  932. NetpKdPrint(("Calling Enumerate phase 2: PrefLen %ld\n", SamPrefMaxLen ));
  933. }
  934. Status = SamEnumerateUsersInDomain(
  935. DomainHandle,
  936. &EnumHandle,
  937. SamFilter,
  938. (PVOID *) &EnumBuffer,
  939. SamPrefMaxLen,
  940. &CountReturned );
  941. IF_DEBUG( UAS_DEBUG_USER ) {
  942. NetpKdPrint(("Enumerate phase 2: Returned %ld entries\n", CountReturned ));
  943. }
  944. SamBytesAlreadyReturned += SamPrefMaxLen;
  945. }
  946. if ( !NT_SUCCESS( Status ) ) {
  947. NetStatus = NetpNtStatusToApiStatus( Status );
  948. goto Cleanup;
  949. }
  950. if( Status != STATUS_MORE_ENTRIES ) {
  951. AllDone = TRUE;
  952. }
  953. for( i = 0; i < CountReturned; i++ ) {
  954. LPBYTE EndOfVariableData;
  955. LPBYTE FixedDataEnd;
  956. //
  957. // save return buffer end points.
  958. //
  959. EndOfVariableData = BufferDescriptor.EndOfVariableData;
  960. FixedDataEnd = BufferDescriptor.FixedDataEnd;
  961. //
  962. // Place another entry into the return buffer.
  963. //
  964. // Use 0xFFFFFFFF as PrefMaxLen to prevent this routine from
  965. // prematurely returning ERROR_MORE_DATA. We'll calculate that
  966. // ourselves below.
  967. //
  968. NetStatus = UserpGetInfo(
  969. DomainHandle,
  970. DomainId,
  971. BuiltinDomainHandle,
  972. EnumBuffer[i].Name,
  973. EnumBuffer[i].RelativeId,
  974. Level,
  975. 0xFFFFFFFF,
  976. &BufferDescriptor,
  977. FALSE, // Not a 'get' operation
  978. 0 );
  979. if (NetStatus != NERR_Success) {
  980. //
  981. // We may have access to enumerate objects we don't have access
  982. // to touch. So, simply ignore those accounts we can't get
  983. // information for.
  984. //
  985. if ( NetStatus == ERROR_ACCESS_DENIED ) {
  986. continue;
  987. }
  988. goto Cleanup;
  989. }
  990. //
  991. // Only count this entry if it was added to the return buffer.
  992. //
  993. if ( (EndOfVariableData != BufferDescriptor.EndOfVariableData ) ||
  994. (FixedDataEnd != BufferDescriptor.FixedDataEnd ) ) {
  995. (*EntriesRead)++;
  996. }
  997. }
  998. //
  999. // free up current EnumBuffer and get another EnumBuffer.
  1000. //
  1001. Status = SamFreeMemory( EnumBuffer );
  1002. NetpAssert( NT_SUCCESS(Status) );
  1003. EnumBuffer = NULL;
  1004. if( AllDone == TRUE ) {
  1005. NetStatus = NERR_Success;
  1006. break;
  1007. }
  1008. //
  1009. // Check here if we've exceeded PrefMaxLen since here we know
  1010. // a valid resume handle.
  1011. //
  1012. NetUserBytesAlreadyReturned =
  1013. ( BufferDescriptor.AllocSize -
  1014. ((DWORD)(BufferDescriptor.EndOfVariableData -
  1015. BufferDescriptor.FixedDataEnd)) );
  1016. if ( NetUserBytesAlreadyReturned >= PrefMaxLen ) {
  1017. LocalResumeHandle = EnumHandle;
  1018. NetStatus = ERROR_MORE_DATA;
  1019. goto Cleanup;
  1020. }
  1021. }
  1022. //
  1023. // Clean up.
  1024. //
  1025. Cleanup:
  1026. //
  1027. // Set EntriesLeft to the number left to return plus those that
  1028. // we returned on this call.
  1029. //
  1030. if( TotalRemaining >= *EntriesRead ) {
  1031. *EntriesLeft = TotalRemaining;
  1032. }
  1033. else {
  1034. *EntriesLeft = *EntriesRead;
  1035. }
  1036. //
  1037. // Free up all resources, we reopen them if the caller calls again.
  1038. //
  1039. if ( DomainHandle != NULL ) {
  1040. UaspCloseDomain( DomainHandle );
  1041. }
  1042. if ( BuiltinDomainHandle != NULL ) {
  1043. UaspCloseDomain( BuiltinDomainHandle );
  1044. }
  1045. if ( SamServerHandle != NULL ) {
  1046. (VOID) SamCloseHandle( SamServerHandle );
  1047. }
  1048. if ( DomainId != NULL ) {
  1049. NetpMemoryFree( DomainId );
  1050. }
  1051. if ( EnumBuffer != NULL ) {
  1052. Status = SamFreeMemory( EnumBuffer );
  1053. NetpAssert( NT_SUCCESS(Status) );
  1054. }
  1055. //
  1056. // If we're not returning data to the caller,
  1057. // free the return buffer.
  1058. //
  1059. if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
  1060. if( NetStatus != NERR_BufTooSmall ) {
  1061. if ( BufferDescriptor.Buffer != NULL ) {
  1062. MIDL_user_free( BufferDescriptor.Buffer );
  1063. BufferDescriptor.Buffer = NULL;
  1064. }
  1065. *EntriesRead = 0;
  1066. *EntriesLeft = 0;
  1067. }
  1068. else {
  1069. NetpAssert( BufferDescriptor.Buffer == NULL );
  1070. NetpAssert( *EntriesRead == 0 );
  1071. }
  1072. }
  1073. //
  1074. // Set the output parameters
  1075. //
  1076. *Buffer = BufferDescriptor.Buffer;
  1077. if ( ARGUMENT_PRESENT( ResumeHandle ) ) {
  1078. *ResumeHandle = LocalResumeHandle;
  1079. }
  1080. IF_DEBUG( UAS_DEBUG_USER ) {
  1081. NetpKdPrint(("NetUserEnum: PrefLen %ld Returned %ld\n", PrefMaxLen,
  1082. ( BufferDescriptor.AllocSize -
  1083. ((DWORD)(BufferDescriptor.EndOfVariableData -
  1084. BufferDescriptor.FixedDataEnd)) ) ));
  1085. }
  1086. //
  1087. // Handle downlevel.
  1088. //
  1089. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  1090. NetStatus = RxNetUserEnum( (LPWSTR)ServerName,
  1091. Level,
  1092. Buffer,
  1093. PrefMaxLen,
  1094. EntriesRead,
  1095. EntriesLeft,
  1096. ResumeHandle );
  1097. UASP_DOWNLEVEL_END;
  1098. IF_DEBUG( UAS_DEBUG_USER ) {
  1099. NetpKdPrint(( "NetUserEnum: returning %ld\n", NetStatus ));
  1100. }
  1101. return NetStatus;
  1102. } // NetUserEnum
  1103. NET_API_STATUS NET_API_FUNCTION
  1104. NetUserGetInfo(
  1105. IN LPCWSTR ServerName OPTIONAL,
  1106. IN LPCWSTR UserName,
  1107. IN DWORD Level,
  1108. OUT LPBYTE *Buffer
  1109. )
  1110. /*++
  1111. Routine Description:
  1112. Retrieve information about a particular user.
  1113. Arguments:
  1114. ServerName - A pointer to a string containing the name of the remote
  1115. server on which the function is to execute. A NULL pointer
  1116. or string specifies the local machine.
  1117. UserName - Name of the user to get information about.
  1118. Level - Level of information required.
  1119. Buffer - Returns a pointer to the return information structure.
  1120. Caller must deallocate buffer using NetApiBufferFree.
  1121. Return Value:
  1122. Error code for the operation.
  1123. --*/
  1124. {
  1125. NET_API_STATUS NetStatus;
  1126. SAM_HANDLE SamServerHandle = NULL;
  1127. SAM_HANDLE DomainHandle = NULL;
  1128. PSID DomainId = NULL;
  1129. SAM_HANDLE BuiltinDomainHandle = NULL;
  1130. BUFFER_DESCRIPTOR BufferDescriptor;
  1131. ULONG RelativeId; // Relative Id of the user
  1132. UNICODE_STRING UserNameString;
  1133. BufferDescriptor.Buffer = NULL;
  1134. //
  1135. // Connect to the SAM server
  1136. //
  1137. NetStatus = UaspOpenSam( ServerName,
  1138. FALSE, // Don't try null session
  1139. &SamServerHandle );
  1140. if ( NetStatus != NERR_Success ) {
  1141. IF_DEBUG( UAS_DEBUG_USER ) {
  1142. NetpKdPrint(( "NetUserGetInfo: Cannot UaspOpenSam %ld\n", NetStatus ));
  1143. }
  1144. goto Cleanup;
  1145. }
  1146. //
  1147. // Open the Domain
  1148. //
  1149. NetStatus = UaspOpenDomain( SamServerHandle,
  1150. DOMAIN_LOOKUP,
  1151. TRUE, // Account Domain
  1152. &DomainHandle,
  1153. &DomainId );
  1154. if ( NetStatus != NERR_Success ) {
  1155. goto Cleanup;
  1156. }
  1157. //
  1158. // Open the Builtin Domain.
  1159. //
  1160. NetStatus = UaspOpenDomain( SamServerHandle,
  1161. DOMAIN_GET_ALIAS_MEMBERSHIP,
  1162. FALSE, // Builtin Domain
  1163. &BuiltinDomainHandle,
  1164. NULL ); // DomainId
  1165. if ( NetStatus != NERR_Success ) {
  1166. goto Cleanup;
  1167. }
  1168. //
  1169. // Validate the user name and get the relative ID.
  1170. //
  1171. NetStatus = UserpOpenUser( DomainHandle,
  1172. 0, // DesiredAccess
  1173. UserName,
  1174. NULL, // UserHandle
  1175. &RelativeId );
  1176. if (NetStatus != NERR_Success ) {
  1177. goto Cleanup;
  1178. }
  1179. //
  1180. // Get the Information about the user.
  1181. //
  1182. RtlInitUnicodeString( &UserNameString, UserName );
  1183. NetStatus = UserpGetInfo(
  1184. DomainHandle,
  1185. DomainId,
  1186. BuiltinDomainHandle,
  1187. UserNameString,
  1188. RelativeId,
  1189. Level,
  1190. 0, // PrefMaxLen
  1191. &BufferDescriptor,
  1192. TRUE, // Is a 'get' operation
  1193. 0 ); // don't filter account
  1194. //
  1195. // Clean up.
  1196. //
  1197. Cleanup:
  1198. //
  1199. // If we're returning data to the caller,
  1200. // Don't free the return buffer.
  1201. //
  1202. if ( NetStatus == NERR_Success ) {
  1203. *Buffer = BufferDescriptor.Buffer;
  1204. } else {
  1205. if ( BufferDescriptor.Buffer != NULL ) {
  1206. MIDL_user_free( BufferDescriptor.Buffer );
  1207. }
  1208. }
  1209. if ( DomainHandle != NULL ) {
  1210. UaspCloseDomain( DomainHandle );
  1211. }
  1212. if ( BuiltinDomainHandle != NULL ) {
  1213. UaspCloseDomain( BuiltinDomainHandle );
  1214. }
  1215. if ( SamServerHandle != NULL ) {
  1216. (VOID) SamCloseHandle( SamServerHandle );
  1217. }
  1218. if ( DomainId != NULL ) {
  1219. NetpMemoryFree( DomainId );
  1220. }
  1221. //
  1222. // Handle downlevel.
  1223. //
  1224. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  1225. NetStatus = RxNetUserGetInfo( (LPWSTR)ServerName, (LPWSTR)UserName, Level, Buffer );
  1226. UASP_DOWNLEVEL_END;
  1227. IF_DEBUG( UAS_DEBUG_USER ) {
  1228. NetpKdPrint(( "NetUserGetInfo: returning %ld\n", NetStatus ));
  1229. }
  1230. return NetStatus;
  1231. } // NetUserGetInfo
  1232. NET_API_STATUS NET_API_FUNCTION
  1233. NetUserSetInfo(
  1234. IN LPCWSTR ServerName OPTIONAL,
  1235. IN LPCWSTR UserName,
  1236. IN DWORD Level,
  1237. IN LPBYTE Buffer,
  1238. OUT LPDWORD ParmError OPTIONAL // Name required by NetpSetParmError
  1239. )
  1240. /*++
  1241. Routine Description:
  1242. Set the parameters on a user account in the user accounts database.
  1243. Arguments:
  1244. ServerName - A pointer to a string containing the name of the remote
  1245. server on which the function is to execute. A NULL pointer
  1246. or string specifies the local machine.
  1247. UserName - Name of the user to modify.
  1248. Level - Level of information provided.
  1249. Buffer - A pointer to the buffer containing the user information
  1250. structure.
  1251. ParmError - Optional pointer to a DWORD to return the index of the
  1252. first parameter in error when ERROR_INVALID_PARAMETER is returned.
  1253. If NULL, the parameter is not returned on error.
  1254. Return Value:
  1255. Error code for the operation.
  1256. --*/
  1257. {
  1258. NET_API_STATUS NetStatus;
  1259. SAM_HANDLE SamServerHandle = NULL;
  1260. SAM_HANDLE DomainHandle = NULL;
  1261. PSID DomainId = NULL;
  1262. SAM_HANDLE BuiltinDomainHandle = NULL;
  1263. ULONG UserRelativeId;
  1264. //
  1265. // Initialize
  1266. //
  1267. NetpSetParmError( PARM_ERROR_NONE );
  1268. //
  1269. // Connect to the SAM server
  1270. //
  1271. NetStatus = UaspOpenSam( ServerName,
  1272. FALSE, // Don't try null session
  1273. &SamServerHandle );
  1274. if ( NetStatus != NERR_Success ) {
  1275. IF_DEBUG( UAS_DEBUG_USER ) {
  1276. NetpKdPrint(( "NetUserSetInfo: Cannot UaspOpenSam %ld\n", NetStatus ));
  1277. }
  1278. goto Cleanup;
  1279. }
  1280. //
  1281. // Open the Account Domain
  1282. // DOMAIN_READ_PASSWORD_PARAMETERS is needed in those cases that we'll
  1283. // set the password on the account.
  1284. //
  1285. NetStatus = UaspOpenDomain( SamServerHandle,
  1286. DOMAIN_LOOKUP | DOMAIN_READ_PASSWORD_PARAMETERS,
  1287. TRUE, // Account Domain
  1288. &DomainHandle,
  1289. &DomainId );
  1290. if ( NetStatus != NERR_Success ) {
  1291. goto Cleanup;
  1292. }
  1293. //
  1294. // Open the Builtin Domain.
  1295. //
  1296. NetStatus = UaspOpenDomain( SamServerHandle,
  1297. DOMAIN_GET_ALIAS_MEMBERSHIP,
  1298. FALSE, // Builtin Domain
  1299. &BuiltinDomainHandle,
  1300. NULL ); // DomainId
  1301. if ( NetStatus != NERR_Success ) {
  1302. goto Cleanup;
  1303. }
  1304. //
  1305. // Get the relative ID of the user. Don't open the user yet
  1306. // since we don't know the desired access.
  1307. //
  1308. NetStatus = UserpOpenUser( DomainHandle,
  1309. 0, // DesiredAccess
  1310. UserName,
  1311. NULL, // UserHandle
  1312. &UserRelativeId );
  1313. if ( NetStatus != NERR_Success ) {
  1314. goto Cleanup;
  1315. }
  1316. //
  1317. // Change the user
  1318. //
  1319. NetStatus = UserpSetInfo(
  1320. DomainHandle,
  1321. DomainId,
  1322. NULL, // UserHandle (let UserpSetInfo open the user)
  1323. BuiltinDomainHandle,
  1324. UserRelativeId,
  1325. UserName,
  1326. Level,
  1327. Buffer,
  1328. 0xFFFFFFFF, // set all requested fields
  1329. ParmError );
  1330. //
  1331. // Clean up.
  1332. //
  1333. Cleanup:
  1334. if ( DomainHandle != NULL ) {
  1335. UaspCloseDomain( DomainHandle );
  1336. }
  1337. if ( BuiltinDomainHandle != NULL ) {
  1338. UaspCloseDomain( BuiltinDomainHandle );
  1339. }
  1340. if ( SamServerHandle != NULL ) {
  1341. (VOID) SamCloseHandle( SamServerHandle );
  1342. }
  1343. if ( DomainId != NULL ) {
  1344. NetpMemoryFree( DomainId );
  1345. }
  1346. //
  1347. // Handle downlevel.
  1348. //
  1349. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  1350. NetStatus = RxNetUserSetInfo( (LPWSTR) ServerName,
  1351. (LPWSTR) UserName,
  1352. Level,
  1353. Buffer,
  1354. ParmError );
  1355. UASP_DOWNLEVEL_END;
  1356. IF_DEBUG( UAS_DEBUG_USER ) {
  1357. NetpKdPrint(( "NetUserSetInfo: returning %ld\n", NetStatus ));
  1358. }
  1359. return NetStatus;
  1360. } // NetUserSetInfo
  1361. NET_API_STATUS NET_API_FUNCTION
  1362. NetUserGetGroups(
  1363. IN LPCWSTR ServerName OPTIONAL,
  1364. IN LPCWSTR UserName,
  1365. IN DWORD Level,
  1366. OUT LPBYTE *Buffer,
  1367. IN DWORD PrefMaxLen,
  1368. OUT LPDWORD EntriesRead,
  1369. OUT LPDWORD EntriesLeft
  1370. )
  1371. /*++
  1372. Routine Description:
  1373. Enumerate the groups that this user is a member of.
  1374. Arguments:
  1375. ServerName - A pointer to a string containing the name of the remote
  1376. server on which the function is to execute. A NULL pointer
  1377. or string specifies the local machine.
  1378. UserName - The name of the user whose members are to be listed.
  1379. Level - Level of information required (must be 0 or 1)
  1380. Buffer - Returns a pointer to the return information structure.
  1381. Caller must deallocate buffer using NetApiBufferFree.
  1382. PrefMaxLen - Prefered maximum length of returned data.
  1383. EntriesRead - Returns the actual enumerated element count.
  1384. EntriesLeft - Returns the total entries available to be enumerated.
  1385. Return Value:
  1386. Error code for the operation.
  1387. --*/
  1388. {
  1389. NET_API_STATUS NetStatus;
  1390. NTSTATUS Status;
  1391. BUFFER_DESCRIPTOR BufferDescriptor;
  1392. DWORD FixedSize; // The fixed size of each new entry.
  1393. DWORD i;
  1394. SAM_HANDLE SamServerHandle = NULL;
  1395. SAM_HANDLE DomainHandle = NULL;
  1396. SAM_HANDLE UserHandle = NULL;
  1397. PUNICODE_STRING Names = NULL; // Names corresponding to Ids
  1398. ULONG GroupCount;
  1399. PGROUP_MEMBERSHIP GroupAttributes = NULL;
  1400. PULONG MemberIds = NULL; // Sam returned MemberIds
  1401. PULONG MemberGroupAttributes = NULL; // Sam returned MemberAttributes;
  1402. //
  1403. // Validate Parameters
  1404. //
  1405. *EntriesRead = 0;
  1406. *EntriesLeft = 0;
  1407. BufferDescriptor.Buffer = NULL;
  1408. switch (Level) {
  1409. case 0:
  1410. FixedSize = sizeof(GROUP_USERS_INFO_0);
  1411. break;
  1412. case 1:
  1413. FixedSize = sizeof(GROUP_USERS_INFO_1);
  1414. break;
  1415. default:
  1416. NetStatus = ERROR_INVALID_LEVEL;
  1417. goto Cleanup;
  1418. }
  1419. //
  1420. // Connect to the SAM server
  1421. //
  1422. NetStatus = UaspOpenSam( ServerName,
  1423. FALSE, // Don't try null session
  1424. &SamServerHandle );
  1425. if ( NetStatus != NERR_Success ) {
  1426. IF_DEBUG( UAS_DEBUG_USER ) {
  1427. NetpKdPrint(( "NetUserGetGroups: Cannot UaspOpenSam %ld\n", NetStatus ));
  1428. }
  1429. goto Cleanup;
  1430. }
  1431. //
  1432. // Open the Domain
  1433. //
  1434. NetStatus = UaspOpenDomain( SamServerHandle,
  1435. DOMAIN_LOOKUP,
  1436. TRUE, // Account Domain
  1437. &DomainHandle,
  1438. NULL); // DomainId
  1439. if ( NetStatus != NERR_Success ) {
  1440. goto Cleanup;
  1441. }
  1442. //
  1443. // Open the user asking for USER_LIST_GROUPS access.
  1444. //
  1445. NetStatus = UserpOpenUser( DomainHandle,
  1446. USER_LIST_GROUPS,
  1447. UserName,
  1448. &UserHandle,
  1449. NULL); // Relative Id
  1450. if ( NetStatus != NERR_Success ) {
  1451. goto Cleanup;
  1452. }
  1453. //
  1454. // Get the membership from SAM
  1455. //
  1456. // This API is an odd one for SAM. It returns all of the membership
  1457. // information in a single call.
  1458. //
  1459. Status = SamGetGroupsForUser( UserHandle, &GroupAttributes, &GroupCount );
  1460. if ( !NT_SUCCESS( Status ) ) {
  1461. IF_DEBUG( UAS_DEBUG_USER ) {
  1462. NetpKdPrint((
  1463. "NetUserGetGroups: SamGetGroupsForUser returned %lX\n",
  1464. Status ));
  1465. }
  1466. NetStatus = NetpNtStatusToApiStatus( Status );
  1467. goto Cleanup;
  1468. }
  1469. //
  1470. // Handle the case where there is nothing to return.
  1471. //
  1472. if ( GroupCount == 0 ) {
  1473. NetStatus = NERR_Success;
  1474. goto Cleanup;
  1475. }
  1476. //
  1477. // Convert the returned relative IDs to user names.
  1478. //
  1479. //
  1480. // Allocate a buffer for converting relative ids to user names
  1481. //
  1482. MemberIds = NetpMemoryAllocate( GroupCount * sizeof(ULONG) );
  1483. if ( MemberIds == NULL ) {
  1484. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1485. goto Cleanup;
  1486. }
  1487. MemberGroupAttributes = NetpMemoryAllocate( GroupCount * sizeof(ULONG) );
  1488. if ( MemberGroupAttributes == NULL ) {
  1489. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1490. goto Cleanup;
  1491. }
  1492. //
  1493. // Allocate another buffer for store attributes of the groups
  1494. // we returning.
  1495. //
  1496. //
  1497. // Copy the relative IDs returned from SAM to the allocated buffer.
  1498. //
  1499. for ( i=0; i < GroupCount; i++ ) {
  1500. MemberIds[*EntriesLeft] = GroupAttributes[i].RelativeId;
  1501. MemberGroupAttributes[*EntriesLeft] = GroupAttributes[i].Attributes;
  1502. (*EntriesLeft)++;
  1503. }
  1504. //
  1505. // Convert the relative IDs to names
  1506. //
  1507. Status = SamLookupIdsInDomain( DomainHandle,
  1508. *EntriesLeft,
  1509. MemberIds,
  1510. &Names,
  1511. NULL ); // NameUse
  1512. if ( !NT_SUCCESS( Status ) ) {
  1513. IF_DEBUG( UAS_DEBUG_USER ) {
  1514. NetpKdPrint((
  1515. "NetUserGetGroups: SamLookupIdsInDomain returned %lX\n",
  1516. Status ));
  1517. }
  1518. NetStatus = NetpNtStatusToApiStatus( Status );
  1519. goto Cleanup;
  1520. }
  1521. //
  1522. // Determine the number of entries that will fit in the caller's
  1523. // buffer.
  1524. //
  1525. for ( i=0; i < *EntriesLeft; i++ ) {
  1526. DWORD Size;
  1527. PGROUP_USERS_INFO_0 grui0;
  1528. //
  1529. // Compute the size of the next entry
  1530. //
  1531. Size = FixedSize + Names[i].Length + sizeof(WCHAR);
  1532. //
  1533. // Ensure the return buffer is big enough.
  1534. //
  1535. Size = ROUND_UP_COUNT( Size, ALIGN_WCHAR );
  1536. NetStatus = NetpAllocateEnumBuffer(
  1537. &BufferDescriptor,
  1538. FALSE, // Is an enumeration routine.
  1539. PrefMaxLen,
  1540. Size,
  1541. GrouppMemberRelocationRoutine,
  1542. Level );
  1543. if ( NetStatus != NERR_Success ) {
  1544. goto Cleanup;
  1545. }
  1546. //
  1547. // Copy the data into the buffer
  1548. //
  1549. grui0 = (PGROUP_USERS_INFO_0) BufferDescriptor.FixedDataEnd;
  1550. BufferDescriptor.FixedDataEnd += FixedSize ;
  1551. NetpAssert( offsetof( GROUP_USERS_INFO_0, grui0_name ) ==
  1552. offsetof( GROUP_USERS_INFO_1, grui1_name ) );
  1553. if ( !NetpCopyStringToBuffer(
  1554. Names[i].Buffer,
  1555. Names[i].Length/sizeof(WCHAR),
  1556. BufferDescriptor.FixedDataEnd,
  1557. (LPWSTR *)&BufferDescriptor.EndOfVariableData,
  1558. &grui0->grui0_name) ) {
  1559. NetStatus = NERR_InternalError;
  1560. goto Cleanup;
  1561. }
  1562. if ( Level == 1 ) {
  1563. ((PGROUP_USERS_INFO_1)grui0)->grui1_attributes =
  1564. MemberGroupAttributes[i];
  1565. }
  1566. (*EntriesRead)++;
  1567. }
  1568. NetStatus = NERR_Success ;
  1569. //
  1570. // Clean up.
  1571. //
  1572. Cleanup:
  1573. //
  1574. // Free any resources used locally
  1575. //
  1576. if( MemberIds != NULL ) {
  1577. NetpMemoryFree( MemberIds );
  1578. }
  1579. if( MemberGroupAttributes != NULL ) {
  1580. NetpMemoryFree( MemberGroupAttributes );
  1581. }
  1582. if ( Names != NULL ) {
  1583. Status = SamFreeMemory( Names );
  1584. NetpAssert( NT_SUCCESS(Status) );
  1585. }
  1586. if ( UserHandle != NULL ) {
  1587. (VOID) SamCloseHandle( UserHandle );
  1588. }
  1589. if ( GroupAttributes != NULL ) {
  1590. Status = SamFreeMemory( GroupAttributes );
  1591. NetpAssert( NT_SUCCESS(Status) );
  1592. }
  1593. UaspCloseDomain( DomainHandle );
  1594. if ( SamServerHandle != NULL ) {
  1595. (VOID) SamCloseHandle( SamServerHandle );
  1596. }
  1597. //
  1598. // If we're not returning data to the caller,
  1599. // free the return buffer.
  1600. //
  1601. if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
  1602. if ( BufferDescriptor.Buffer != NULL ) {
  1603. MIDL_user_free( BufferDescriptor.Buffer );
  1604. BufferDescriptor.Buffer = NULL;
  1605. }
  1606. *EntriesLeft = 0;
  1607. *EntriesRead = 0;
  1608. }
  1609. *Buffer = BufferDescriptor.Buffer;
  1610. //
  1611. // Handle downlevel.
  1612. //
  1613. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  1614. NetStatus = RxNetUserGetGroups( (LPWSTR)ServerName,
  1615. (LPWSTR)UserName,
  1616. Level,
  1617. Buffer,
  1618. PrefMaxLen,
  1619. EntriesRead,
  1620. EntriesLeft );
  1621. UASP_DOWNLEVEL_END;
  1622. IF_DEBUG( UAS_DEBUG_USER ) {
  1623. NetpKdPrint(( "NetUserGetGroups: returning %ld\n", NetStatus ));
  1624. }
  1625. return NetStatus;
  1626. } // NetUserGetGroups
  1627. NET_API_STATUS NET_API_FUNCTION
  1628. NetUserSetGroups (
  1629. IN LPCWSTR ServerName OPTIONAL,
  1630. IN LPCWSTR UserName,
  1631. IN DWORD Level,
  1632. IN LPBYTE Buffer,
  1633. IN DWORD NewGroupCount
  1634. )
  1635. /*++
  1636. Routine Description:
  1637. Set the list of groups that is user is a member of.
  1638. The groups specified by "Buffer" are called new groups. The groups
  1639. that the user is currently a member of are called old groups.
  1640. Groups which are on both the old and new list are called common groups.
  1641. The SAM API allows only one member to be added or deleted at a time.
  1642. This API allows all of the groups this user is a member of to be
  1643. specified en-masse. This API is careful to always leave the group
  1644. membership in the SAM database in a reasonable state.
  1645. It does by merging the list of
  1646. old and new groups, then only changing those memberships which absolutely
  1647. need changing.
  1648. Group membership is restored to its previous state (if possible) if
  1649. an error occurs during changing the group membership.
  1650. Arguments:
  1651. ServerName - A pointer to a string containing the name of the remote
  1652. server on which the function is to execute. A NULL pointer
  1653. or string specifies the local machine.
  1654. UserName - Name of the user to modify.
  1655. Level - Level of information provided. Must be 0 or 1.
  1656. Buffer - A pointer to the buffer containing an array of NewGroupCount
  1657. group membership information structures.
  1658. NewGroupCount - Number of entries in Buffer.
  1659. Return Value:
  1660. Error code for the operation.
  1661. --*/
  1662. {
  1663. NET_API_STATUS NetStatus;
  1664. NTSTATUS Status;
  1665. SAM_HANDLE SamServerHandle = NULL;
  1666. SAM_HANDLE DomainHandle = NULL;
  1667. SAM_HANDLE UserHandle = NULL;
  1668. ULONG UserRelativeId;
  1669. DWORD FixedSize;
  1670. PULONG NewRelativeIds = NULL; // Relative Ids of a list of new groups
  1671. PSID_NAME_USE NewNameUse = NULL; // Name usage of a list of new groups
  1672. PUNICODE_STRING NewNameStrings = NULL;// Names of a list of new groups
  1673. //
  1674. // Define an internal group membership list structure.
  1675. //
  1676. // This structure defines a list of new group memberships to be added,
  1677. // group memberships whose attributes merely need to be changed,
  1678. // and group memberships which need to be deleted.
  1679. //
  1680. // The list is maintained in relative ID sorted order.
  1681. //
  1682. struct _GROUP_DESCRIPTION {
  1683. struct _GROUP_DESCRIPTION * Next; // Next entry in linked list;
  1684. ULONG RelativeId; // Relative ID of this group
  1685. SAM_HANDLE GroupHandle; // Group Handle of this group
  1686. enum _Action { // Action taken for this group membership
  1687. AddMember, // Add membership to group
  1688. RemoveMember, // Remove membership from group
  1689. SetAttributesMember, // Change the membership's attributes
  1690. IgnoreMember // Ignore this membership
  1691. } Action;
  1692. BOOL Done; // True if this action has been taken
  1693. ULONG NewAttributes; // Attributes to set for the membership
  1694. ULONG OldAttributes; // Attributes to restore on a recovery
  1695. } *GroupList = NULL, *CurEntry, **Entry, *TempEntry;
  1696. //
  1697. // Connect to the SAM server
  1698. //
  1699. NetStatus = UaspOpenSam( ServerName,
  1700. FALSE, // Don't try null session
  1701. &SamServerHandle );
  1702. if ( NetStatus != NERR_Success ) {
  1703. IF_DEBUG( UAS_DEBUG_USER ) {
  1704. NetpKdPrint(( "NetUserSetGroups: Cannot UaspOpenSam %ld\n", NetStatus ));
  1705. }
  1706. goto Cleanup;
  1707. }
  1708. //
  1709. // Open the Domain
  1710. //
  1711. NetStatus = UaspOpenDomain( SamServerHandle,
  1712. DOMAIN_LOOKUP,
  1713. TRUE, // Account Domain
  1714. &DomainHandle,
  1715. NULL); // DomainId
  1716. if ( NetStatus != NERR_Success ) {
  1717. goto Cleanup;
  1718. }
  1719. //
  1720. // Open the user
  1721. //
  1722. NetStatus = UserpOpenUser( DomainHandle,
  1723. USER_LIST_GROUPS,
  1724. UserName,
  1725. &UserHandle,
  1726. &UserRelativeId );
  1727. if ( NetStatus != NERR_Success ) {
  1728. goto Cleanup;
  1729. }
  1730. //
  1731. // Validate the level
  1732. //
  1733. switch (Level) {
  1734. case 0:
  1735. FixedSize = sizeof( GROUP_USERS_INFO_0 );
  1736. break;
  1737. case 1:
  1738. FixedSize = sizeof( GROUP_USERS_INFO_1 );
  1739. break;
  1740. default:
  1741. NetStatus = ERROR_INVALID_LEVEL;
  1742. goto Cleanup;
  1743. }
  1744. //
  1745. // Build the list of new groups
  1746. //
  1747. if ( NewGroupCount > 0 ) {
  1748. DWORD NewIndex; // Index to the current New group
  1749. //
  1750. // Allocate a buffer big enough to contain all the string variables
  1751. // for the new group names.
  1752. //
  1753. NewNameStrings = NetpMemoryAllocate( NewGroupCount *
  1754. sizeof(UNICODE_STRING));
  1755. if ( NewNameStrings == NULL ) {
  1756. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1757. goto Cleanup;
  1758. }
  1759. //
  1760. // Fill in the list of group name strings for each new group.
  1761. //
  1762. NetpAssert( offsetof( GROUP_USERS_INFO_0, grui0_name ) ==
  1763. offsetof( GROUP_USERS_INFO_1, grui1_name ) );
  1764. for ( NewIndex=0; NewIndex<NewGroupCount; NewIndex++ ) {
  1765. LPWSTR GroupName;
  1766. GroupName =
  1767. ((PGROUP_USERS_INFO_0)(Buffer+FixedSize*NewIndex))->grui0_name;
  1768. RtlInitUnicodeString( &NewNameStrings[NewIndex], GroupName );
  1769. }
  1770. //
  1771. // Convert the group names to relative Ids.
  1772. //
  1773. Status = SamLookupNamesInDomain( DomainHandle,
  1774. NewGroupCount,
  1775. NewNameStrings,
  1776. &NewRelativeIds,
  1777. &NewNameUse );
  1778. if ( !NT_SUCCESS( Status )) {
  1779. IF_DEBUG( UAS_DEBUG_USER ) {
  1780. NetpKdPrint((
  1781. "NetUserSetGroups: SamLookupNamesInDomain returned %lX\n",
  1782. Status ));
  1783. }
  1784. if ( Status == STATUS_NONE_MAPPED ) {
  1785. NetStatus = NERR_GroupNotFound;
  1786. goto Cleanup;
  1787. }
  1788. NetStatus = NetpNtStatusToApiStatus( Status );
  1789. goto Cleanup;
  1790. }
  1791. //
  1792. // Build a group entry for each of the new groups.
  1793. // The list is maintained in RelativeId sorted order.
  1794. //
  1795. for ( NewIndex=0; NewIndex<NewGroupCount; NewIndex++ ) {
  1796. //
  1797. // Ensure the new group name is really a group
  1798. // One cannot become the member of a user!!!
  1799. //
  1800. if (NewNameUse[NewIndex] != SidTypeGroup) {
  1801. NetStatus = NERR_GroupNotFound;
  1802. goto Cleanup;
  1803. }
  1804. //
  1805. // Find the place to put the new entry
  1806. //
  1807. Entry = &GroupList;
  1808. while ( *Entry != NULL &&
  1809. (*Entry)->RelativeId < NewRelativeIds[NewIndex] ) {
  1810. Entry = &( (*Entry)->Next );
  1811. }
  1812. //
  1813. // If this is not a duplicate entry, allocate a new group structure
  1814. // and fill it in.
  1815. //
  1816. // Just ignore duplicate relative Ids.
  1817. //
  1818. if ( *Entry == NULL ||
  1819. (*Entry)->RelativeId > NewRelativeIds[NewIndex] ) {
  1820. CurEntry =
  1821. NetpMemoryAllocate( sizeof(struct _GROUP_DESCRIPTION) );
  1822. if ( CurEntry == NULL ) {
  1823. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1824. goto Cleanup;
  1825. }
  1826. CurEntry->Next = *Entry;
  1827. CurEntry->RelativeId = NewRelativeIds[NewIndex];
  1828. CurEntry->Action = AddMember;
  1829. CurEntry->Done = FALSE;
  1830. CurEntry->GroupHandle = NULL;
  1831. CurEntry->NewAttributes = ( Level == 1 ) ?
  1832. ((PGROUP_USERS_INFO_1)Buffer)[NewIndex].grui1_attributes :
  1833. SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
  1834. SE_GROUP_ENABLED;
  1835. *Entry = CurEntry;
  1836. }
  1837. }
  1838. }
  1839. //
  1840. // Merge the old groups into the list.
  1841. //
  1842. {
  1843. ULONG OldIndex; // Index to current entry
  1844. ULONG OldCount; // Total Number of entries
  1845. PGROUP_MEMBERSHIP GroupAttributes = NULL;
  1846. //
  1847. // Determine the old group membership
  1848. //
  1849. Status = SamGetGroupsForUser(
  1850. UserHandle,
  1851. &GroupAttributes,
  1852. &OldCount );
  1853. if ( !NT_SUCCESS( Status ) ) {
  1854. IF_DEBUG( UAS_DEBUG_USER ) {
  1855. NetpKdPrint((
  1856. "NetUserSetGroups: SamGetGroupsForUser returned %lX\n",
  1857. Status ));
  1858. }
  1859. NetStatus = NetpNtStatusToApiStatus( Status );
  1860. goto Cleanup;
  1861. }
  1862. //
  1863. // Merge each old group into the list
  1864. //
  1865. for ( OldIndex=0; OldIndex < OldCount; OldIndex++) {
  1866. //
  1867. // Find the place to put the new entry
  1868. //
  1869. Entry = &GroupList ;
  1870. while ( *Entry != NULL &&
  1871. (*Entry)->RelativeId < GroupAttributes[OldIndex].RelativeId ) {
  1872. Entry = &( (*Entry)->Next );
  1873. }
  1874. //
  1875. // If this entry is not already in the list,
  1876. // this is a group membership which exists now but should
  1877. // be deleted.
  1878. //
  1879. if( *Entry == NULL ||
  1880. (*Entry)->RelativeId > GroupAttributes[OldIndex].RelativeId){
  1881. CurEntry =
  1882. NetpMemoryAllocate(sizeof(struct _GROUP_DESCRIPTION));
  1883. if ( CurEntry == NULL ) {
  1884. Status = SamFreeMemory( GroupAttributes );
  1885. NetpAssert( NT_SUCCESS(Status) );
  1886. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1887. goto Cleanup;
  1888. }
  1889. CurEntry->Next = *Entry;
  1890. CurEntry->RelativeId = GroupAttributes[OldIndex].RelativeId;
  1891. CurEntry->Action = RemoveMember;
  1892. CurEntry->Done = FALSE;
  1893. CurEntry->OldAttributes = GroupAttributes[OldIndex].Attributes;
  1894. CurEntry->GroupHandle = NULL;
  1895. *Entry = CurEntry;
  1896. //
  1897. // Handle the case where this group is already in the list
  1898. //
  1899. } else {
  1900. //
  1901. // Watch out for SAM returning the same group twice.
  1902. //
  1903. if ( (*Entry)->Action != AddMember ) {
  1904. Status = SamFreeMemory( GroupAttributes );
  1905. NetpAssert( NT_SUCCESS(Status) );
  1906. NetStatus = NERR_InternalError;
  1907. goto Cleanup;
  1908. }
  1909. //
  1910. // If this is info level 1 and the requested attributes are
  1911. // different than the current attributes,
  1912. // Remember to change the attributes.
  1913. //
  1914. if ( Level == 1 && (*Entry)->NewAttributes !=
  1915. GroupAttributes[OldIndex].Attributes ) {
  1916. (*Entry)->OldAttributes =
  1917. GroupAttributes[OldIndex].Attributes;
  1918. (*Entry)->Action = SetAttributesMember;
  1919. //
  1920. // This is either info level 0 or the level 1 attributes
  1921. // are the same as the existing attributes.
  1922. //
  1923. // In either case, this group membership is already set
  1924. // up properly and we should ignore this entry for the
  1925. // rest of this routine.
  1926. //
  1927. } else {
  1928. (*Entry)->Action = IgnoreMember;
  1929. }
  1930. }
  1931. }
  1932. }
  1933. //
  1934. // Loop through the list opening all of the groups
  1935. //
  1936. // Ask for add and remove access for BOTH added and removed memberships.
  1937. // One access is required to do the operation initially. The other access
  1938. // is required to undo the operation during recovery.
  1939. //
  1940. for ( CurEntry = GroupList; CurEntry != NULL ; CurEntry=CurEntry->Next ) {
  1941. if ( CurEntry->Action == AddMember || CurEntry->Action == RemoveMember){
  1942. Status = SamOpenGroup(
  1943. DomainHandle,
  1944. GROUP_ADD_MEMBER | GROUP_REMOVE_MEMBER,
  1945. CurEntry->RelativeId,
  1946. &CurEntry->GroupHandle );
  1947. if ( !NT_SUCCESS( Status ) ) {
  1948. IF_DEBUG( UAS_DEBUG_USER ) {
  1949. NetpKdPrint((
  1950. "NetUserSetGroups: SamOpenGroup returned %lX\n",
  1951. Status ));
  1952. }
  1953. NetStatus = NetpNtStatusToApiStatus( Status );
  1954. goto Cleanup;
  1955. }
  1956. }
  1957. }
  1958. //
  1959. // Loop through the list adding membership to all new groups.
  1960. // We do this in a separate loop to minimize the damage that happens
  1961. // should we get an error and not be able to recover.
  1962. //
  1963. for ( CurEntry = GroupList; CurEntry != NULL ; CurEntry=CurEntry->Next ) {
  1964. if ( CurEntry->Action == AddMember ) {
  1965. Status = SamAddMemberToGroup( CurEntry->GroupHandle,
  1966. UserRelativeId,
  1967. CurEntry->NewAttributes );
  1968. //
  1969. // For level 0, if the default attributes were incompatible,
  1970. // try these attributes.
  1971. //
  1972. if ( Level == 0 && Status == STATUS_INVALID_GROUP_ATTRIBUTES ) {
  1973. Status = SamAddMemberToGroup( CurEntry->GroupHandle,
  1974. UserRelativeId,
  1975. SE_GROUP_ENABLED_BY_DEFAULT |
  1976. SE_GROUP_ENABLED );
  1977. }
  1978. if ( !NT_SUCCESS( Status ) ) {
  1979. IF_DEBUG( UAS_DEBUG_USER ) {
  1980. NetpKdPrint((
  1981. "NetUserSetGroups: SamAddMemberToGroup returned %lX\n",
  1982. Status ));
  1983. }
  1984. NetStatus = NetpNtStatusToApiStatus( Status );
  1985. goto Cleanup;
  1986. }
  1987. CurEntry->Done = TRUE;
  1988. }
  1989. }
  1990. //
  1991. // Loop through the list deleting membership from all old groups
  1992. // and changing the membership attributes of all common groups.
  1993. //
  1994. for ( CurEntry = GroupList; CurEntry != NULL ; CurEntry=CurEntry->Next ) {
  1995. if ( CurEntry->Action == RemoveMember ) {
  1996. Status = SamRemoveMemberFromGroup( CurEntry->GroupHandle,
  1997. UserRelativeId);
  1998. } else if ( CurEntry->Action == SetAttributesMember ) {
  1999. Status = SamSetMemberAttributesOfGroup( CurEntry->GroupHandle,
  2000. UserRelativeId,
  2001. CurEntry->NewAttributes);
  2002. }
  2003. if ( !NT_SUCCESS( Status ) ) {
  2004. IF_DEBUG( UAS_DEBUG_USER ) {
  2005. NetpKdPrint((
  2006. "NetUserSetGroups: SamRemoveMemberFromGroup (or SetMemberAttributes) returned %lX\n",
  2007. Status ));
  2008. }
  2009. NetStatus = NetpNtStatusToApiStatus( Status );
  2010. goto Cleanup;
  2011. }
  2012. CurEntry->Done = TRUE;
  2013. }
  2014. NetStatus = NERR_Success;
  2015. //
  2016. // Clean up.
  2017. //
  2018. Cleanup:
  2019. //
  2020. // Walk the group list cleaning up any damage we've done
  2021. //
  2022. for ( CurEntry = GroupList; CurEntry != NULL ; ) {
  2023. if ( NetStatus != NERR_Success && CurEntry->Done ) {
  2024. switch (CurEntry->Action) {
  2025. case AddMember:
  2026. Status = SamRemoveMemberFromGroup( CurEntry->GroupHandle,
  2027. UserRelativeId );
  2028. NetpAssert( NT_SUCCESS(Status) );
  2029. break;
  2030. case RemoveMember:
  2031. Status = SamAddMemberToGroup( CurEntry->GroupHandle,
  2032. UserRelativeId,
  2033. CurEntry->OldAttributes );
  2034. NetpAssert( NT_SUCCESS(Status) );
  2035. break;
  2036. case SetAttributesMember:
  2037. Status = SamSetMemberAttributesOfGroup(CurEntry->GroupHandle,
  2038. UserRelativeId,
  2039. CurEntry->OldAttributes);
  2040. NetpAssert( NT_SUCCESS(Status) );
  2041. break;
  2042. default:
  2043. break;
  2044. }
  2045. }
  2046. if (CurEntry->GroupHandle != NULL) {
  2047. (VOID) SamCloseHandle( CurEntry->GroupHandle );
  2048. }
  2049. TempEntry = CurEntry;
  2050. CurEntry = CurEntry->Next;
  2051. NetpMemoryFree( TempEntry );
  2052. }
  2053. //
  2054. // Free up any locally used resources.
  2055. //
  2056. if ( NewNameStrings != NULL ) {
  2057. NetpMemoryFree( NewNameStrings );
  2058. }
  2059. if ( NewRelativeIds != NULL ) {
  2060. Status = SamFreeMemory( NewRelativeIds );
  2061. NetpAssert( NT_SUCCESS(Status) );
  2062. }
  2063. if ( NewNameUse != NULL ) {
  2064. Status = SamFreeMemory( NewNameUse );
  2065. NetpAssert( NT_SUCCESS(Status) );
  2066. }
  2067. if (UserHandle != NULL) {
  2068. (VOID) SamCloseHandle( UserHandle );
  2069. }
  2070. UaspCloseDomain( DomainHandle );
  2071. if ( SamServerHandle != NULL ) {
  2072. (VOID) SamCloseHandle( SamServerHandle );
  2073. }
  2074. //
  2075. // Handle downlevel.
  2076. //
  2077. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  2078. NetStatus = RxNetUserSetGroups( (LPWSTR)ServerName,
  2079. (LPWSTR)UserName,
  2080. Level,
  2081. Buffer,
  2082. NewGroupCount );
  2083. UASP_DOWNLEVEL_END;
  2084. IF_DEBUG( UAS_DEBUG_USER ) {
  2085. NetpKdPrint(( "NetUserSetGroups: returning %ld\n", NetStatus ));
  2086. }
  2087. return NetStatus;
  2088. } // NetUserSetGroups
  2089. NET_API_STATUS NET_API_FUNCTION
  2090. NetUserGetLocalGroups(
  2091. IN LPCWSTR ServerName OPTIONAL,
  2092. IN LPCWSTR UserName,
  2093. IN DWORD Level,
  2094. IN DWORD Flags,
  2095. OUT LPBYTE *Buffer,
  2096. IN DWORD PrefMaxLen,
  2097. OUT LPDWORD EntriesRead,
  2098. OUT LPDWORD EntriesLeft
  2099. )
  2100. /*++
  2101. Routine Description:
  2102. Enumerate the local groups that this user is a member of.
  2103. Arguments:
  2104. ServerName - A pointer to a string containing the name of the remote
  2105. server on which the function is to execute. A NULL pointer
  2106. or string specifies the local machine.
  2107. UserName - The name of the user whose members are to be listed.
  2108. The UserName can be of the form <UserName> in which case the
  2109. UserName is expected to be found on ServerName. The UserName can also
  2110. be of the form <DomainName>\<UserName> in which case <DomainName> is
  2111. expected to be trusted by ServerName and <UserName> is expected to be to
  2112. be found on that domain.
  2113. Level - Level of information required (must be 0)
  2114. Flags - Indicates if indirect local group membership is to be
  2115. included.
  2116. Buffer - Returns a pointer to the return information structure.
  2117. Caller must deallocate buffer using NetApiBufferFree.
  2118. PrefMaxLen - Prefered maximum length of returned data.
  2119. EntriesRead - Returns the actual enumerated element count.
  2120. EntriesLeft - Returns the total entries available to be enumerated.
  2121. Return Value:
  2122. Error code for the operation.
  2123. --*/
  2124. {
  2125. NET_API_STATUS NetStatus;
  2126. NTSTATUS Status;
  2127. BUFFER_DESCRIPTOR BufferDescriptor;
  2128. DWORD FixedSize; // The fixed size of each new entry.
  2129. SAM_HANDLE SamServerHandle = NULL;
  2130. SAM_HANDLE DomainHandle = NULL;
  2131. SAM_HANDLE UsersDomainHandle = NULL;
  2132. SAM_HANDLE BuiltinDomainHandle = NULL;
  2133. SAM_HANDLE UserHandle = NULL;
  2134. PSID DomainId = NULL ;
  2135. PSID DomainIdToUse;
  2136. PSID UsersDomainId = NULL ;
  2137. PSID *UserSidList = NULL;
  2138. ULONG PartialCount = 0;
  2139. LPCWSTR OrigUserName = UserName;
  2140. PWCHAR BackSlash;
  2141. PUNICODE_STRING Names = NULL; // Names corresponding to Ids
  2142. PULONG Aliases = NULL;
  2143. ULONG GroupCount = 0 ;
  2144. ULONG GroupIndex;
  2145. PGROUP_MEMBERSHIP GroupMembership = NULL;
  2146. PSID *UserSids = NULL;
  2147. ULONG UserSidCount = 0;
  2148. ULONG UserRelativeId = 0;
  2149. //
  2150. // Validate Parameters
  2151. //
  2152. *EntriesRead = 0;
  2153. *EntriesLeft = 0;
  2154. BufferDescriptor.Buffer = NULL;
  2155. if (Flags & ~LG_INCLUDE_INDIRECT) {
  2156. NetStatus = ERROR_INVALID_PARAMETER; // unknown flag
  2157. goto Cleanup;
  2158. }
  2159. switch (Level) {
  2160. case 0:
  2161. FixedSize = sizeof(LOCALGROUP_USERS_INFO_0);
  2162. break;
  2163. default:
  2164. NetStatus = ERROR_INVALID_LEVEL;
  2165. goto Cleanup;
  2166. }
  2167. //
  2168. // Connect to the SAM server
  2169. //
  2170. NetStatus = UaspOpenSam( ServerName,
  2171. FALSE, // Don't try null session
  2172. &SamServerHandle );
  2173. if ( NetStatus != NERR_Success ) {
  2174. IF_DEBUG( UAS_DEBUG_USER ) {
  2175. NetpKdPrint(( "NetUserGetLocalGroups: Cannot UaspOpenSam %ld\n", NetStatus ));
  2176. }
  2177. goto Cleanup;
  2178. }
  2179. //
  2180. // Open the Domains (Account & Builtin)
  2181. //
  2182. NetStatus = UaspOpenDomain( SamServerHandle,
  2183. DOMAIN_LOOKUP | DOMAIN_GET_ALIAS_MEMBERSHIP,
  2184. TRUE, // Account Domain
  2185. &DomainHandle,
  2186. &DomainId);
  2187. if ( NetStatus != NERR_Success ) {
  2188. goto Cleanup;
  2189. }
  2190. NetStatus = UaspOpenDomain( SamServerHandle,
  2191. DOMAIN_GET_ALIAS_MEMBERSHIP,
  2192. FALSE, // Builtin Domain
  2193. &BuiltinDomainHandle,
  2194. NULL ); // DomainId
  2195. if ( NetStatus != NERR_Success ) {
  2196. goto Cleanup;
  2197. }
  2198. //
  2199. // Parse the <DomainName>\<UserName>
  2200. //
  2201. BackSlash = wcschr( UserName, L'\\' );
  2202. //
  2203. // If the global group of the user are to be taken into consideration,
  2204. // get the global groups now.
  2205. //
  2206. if ( Flags & LG_INCLUDE_INDIRECT ) {
  2207. SAM_HANDLE DomainHandleToUse;
  2208. //
  2209. // Handle the case where no domain is specified
  2210. //
  2211. if ( BackSlash == NULL ) {
  2212. DomainHandleToUse = DomainHandle;
  2213. DomainIdToUse = DomainId;
  2214. //
  2215. // Handle the case where a domain name was specified
  2216. //
  2217. } else {
  2218. DWORD UsersDomainNameLength;
  2219. WCHAR UsersDomainName[DNLEN+1];
  2220. //
  2221. // Grab the domain name
  2222. //
  2223. UsersDomainNameLength = (DWORD)(BackSlash - UserName);
  2224. if ( UsersDomainNameLength == 0 ||
  2225. UsersDomainNameLength > DNLEN ) {
  2226. NetStatus = NERR_DCNotFound;
  2227. goto Cleanup;
  2228. }
  2229. RtlCopyMemory( UsersDomainName, UserName, UsersDomainNameLength*sizeof(WCHAR) );
  2230. UsersDomainName[UsersDomainNameLength] = L'\0';
  2231. UserName = BackSlash+1;
  2232. //
  2233. // Open a handle to the specified domain's SAM.
  2234. //
  2235. NetStatus = UaspOpenDomainWithDomainName(
  2236. UsersDomainName,
  2237. DOMAIN_LOOKUP,
  2238. TRUE, // Account Domain
  2239. &UsersDomainHandle,
  2240. &UsersDomainId );
  2241. if ( NetStatus != NERR_Success ) {
  2242. goto Cleanup;
  2243. }
  2244. DomainHandleToUse = UsersDomainHandle;
  2245. DomainIdToUse = UsersDomainId;
  2246. }
  2247. //
  2248. // Open the user asking for USER_LIST_GROUPS access.
  2249. //
  2250. NetStatus = UserpOpenUser( DomainHandleToUse,
  2251. USER_LIST_GROUPS,
  2252. UserName,
  2253. &UserHandle,
  2254. &UserRelativeId); // Relative Id
  2255. if ( NetStatus != NERR_Success ) {
  2256. goto Cleanup;
  2257. }
  2258. //
  2259. // Get the group membership from SAM, since we are
  2260. // interested in indirect alias membership via group membership.
  2261. //
  2262. // This API is an odd one for SAM. It returns all of the membership
  2263. // information in a single call.
  2264. //
  2265. Status = SamGetGroupsForUser( UserHandle, &GroupMembership, &GroupCount );
  2266. if ( !NT_SUCCESS( Status ) ) {
  2267. IF_DEBUG( UAS_DEBUG_USER ) {
  2268. NetpKdPrint((
  2269. "NetUserGetGroups: SamGetGroupsForUser returned %lX\n",
  2270. Status ));
  2271. }
  2272. NetStatus = NetpNtStatusToApiStatus( Status );
  2273. goto Cleanup;
  2274. }
  2275. }
  2276. //
  2277. // Allocate a buffer to point to the SIDs we're interested in
  2278. // alias membership for.
  2279. //
  2280. UserSids = (PSID *) NetpMemoryAllocate( (GroupCount+1) * sizeof(PSID) );
  2281. if ( UserSids == NULL ) {
  2282. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2283. goto Cleanup;
  2284. }
  2285. RtlZeroMemory( UserSids, (GroupCount+1) * sizeof(PSID) );
  2286. //
  2287. // If no domain is specified,
  2288. // just grab the SID of the user account from SAM.
  2289. //
  2290. if ( BackSlash == NULL ) {
  2291. //
  2292. // Get the rid of the account
  2293. //
  2294. if ( UserRelativeId == 0 ) {
  2295. NetStatus = UserpOpenUser( DomainHandle,
  2296. 0,
  2297. UserName,
  2298. NULL,
  2299. &UserRelativeId); // Relative Id
  2300. if ( NetStatus != NERR_Success ) {
  2301. goto Cleanup;
  2302. }
  2303. }
  2304. //
  2305. // Add the User's Sid to the Array of Sids.
  2306. //
  2307. NetStatus = NetpSamRidToSid( DomainHandle,
  2308. UserRelativeId,
  2309. &UserSids[UserSidCount] );
  2310. if ( NetStatus != NERR_Success ) {
  2311. goto Cleanup;
  2312. }
  2313. UserSidCount ++;
  2314. //
  2315. // If a domain name is specified,
  2316. // use LookupAccountName to translate the name to a SID.
  2317. //
  2318. // Don't open the user account. We typically don't have access to do that.
  2319. // Newer version of NT don't allow anything over the NULL session.
  2320. //
  2321. } else {
  2322. //
  2323. // Translate the name to a SID.
  2324. //
  2325. NetStatus = AliaspNamesToSids ( ServerName,
  2326. TRUE, // Only allow users
  2327. 1,
  2328. (LPWSTR *)&OrigUserName,
  2329. &UserSidList );
  2330. if ( NetStatus != NERR_Success ) {
  2331. if ( NetStatus == ERROR_NO_SUCH_MEMBER ) {
  2332. NetStatus = NERR_UserNotFound;
  2333. }
  2334. goto Cleanup;
  2335. }
  2336. //
  2337. // Add the User's Sid to the Array of Sids.
  2338. //
  2339. UserSids[UserSidCount] = UserSidList[0];
  2340. UserSidList[0] = NULL;
  2341. UserSidCount ++;
  2342. }
  2343. //
  2344. // Add each group the user is a member of to the array of Sids.
  2345. // Note that GroupCount would still be zero if LG_INCLUDE_INDIRECT isn't
  2346. // specified.
  2347. //
  2348. for ( GroupIndex = 0; GroupIndex < GroupCount; GroupIndex ++ ) {
  2349. NetStatus = NetpSamRidToSid( UserHandle,
  2350. GroupMembership[GroupIndex].RelativeId,
  2351. &UserSids[UserSidCount] );
  2352. if ( NetStatus != NERR_Success ) {
  2353. goto Cleanup;
  2354. }
  2355. UserSidCount ++;
  2356. }
  2357. //
  2358. // Find out which aliases in the ACCOUNT domain this user is a member of.
  2359. //
  2360. Status = SamGetAliasMembership( DomainHandle,
  2361. UserSidCount,
  2362. UserSids,
  2363. &PartialCount,
  2364. &Aliases );
  2365. if ( !NT_SUCCESS(Status) ) {
  2366. IF_DEBUG( UAS_DEBUG_USER ) {
  2367. NetpKdPrint((
  2368. "UserpGetUserPriv: SamGetAliasMembership returns %lX\n",
  2369. Status ));
  2370. }
  2371. NetStatus = NetpNtStatusToApiStatus( Status );
  2372. goto Cleanup;
  2373. }
  2374. if (PartialCount > 0)
  2375. {
  2376. //
  2377. // Convert the RIDs to names
  2378. //
  2379. Status = SamLookupIdsInDomain( DomainHandle,
  2380. PartialCount,
  2381. Aliases,
  2382. &Names,
  2383. NULL ); // NameUse
  2384. if ( !NT_SUCCESS( Status ) ) {
  2385. IF_DEBUG( UAS_DEBUG_USER ) {
  2386. NetpKdPrint((
  2387. "NetUserGetGroups: SamLookupIdsInDomain returned %lX\n",
  2388. Status ));
  2389. }
  2390. NetStatus = NetpNtStatusToApiStatus( Status );
  2391. goto Cleanup;
  2392. }
  2393. NetStatus = AliaspPackBuf( Level,
  2394. PrefMaxLen,
  2395. PartialCount,
  2396. EntriesRead,
  2397. &BufferDescriptor,
  2398. FixedSize,
  2399. Names) ;
  2400. if (NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA)
  2401. goto Cleanup;
  2402. //
  2403. // free up and reset pointers that need to be reused
  2404. //
  2405. Status = SamFreeMemory( Names );
  2406. NetpAssert( NT_SUCCESS(Status) );
  2407. Names = NULL ;
  2408. Status = SamFreeMemory( Aliases );
  2409. NetpAssert( NT_SUCCESS(Status) );
  2410. Aliases = NULL ;
  2411. *EntriesLeft = PartialCount ;
  2412. }
  2413. //
  2414. // Find out which aliases in the BUILTIN domain this user is a member of.
  2415. //
  2416. Status = SamGetAliasMembership( BuiltinDomainHandle,
  2417. UserSidCount,
  2418. UserSids,
  2419. &PartialCount,
  2420. &Aliases );
  2421. if ( !NT_SUCCESS(Status) ) {
  2422. IF_DEBUG( UAS_DEBUG_USER ) {
  2423. NetpKdPrint((
  2424. "UserpGetUserPriv: SamGetAliasMembership returns %lX\n",
  2425. Status ));
  2426. }
  2427. NetStatus = NetpNtStatusToApiStatus( Status );
  2428. goto Cleanup;
  2429. }
  2430. //
  2431. // Convert the RIDs to names
  2432. //
  2433. Status = SamLookupIdsInDomain( BuiltinDomainHandle,
  2434. PartialCount,
  2435. Aliases,
  2436. &Names,
  2437. NULL ); // NameUse
  2438. if ( !NT_SUCCESS( Status ) ) {
  2439. IF_DEBUG( UAS_DEBUG_USER ) {
  2440. NetpKdPrint((
  2441. "NetUserGetGroups: SamLookupIdsInDomain returned %lX\n",
  2442. Status ));
  2443. }
  2444. NetStatus = NetpNtStatusToApiStatus( Status );
  2445. goto Cleanup;
  2446. }
  2447. NetStatus = AliaspPackBuf( Level,
  2448. PrefMaxLen,
  2449. PartialCount,
  2450. EntriesRead,
  2451. &BufferDescriptor,
  2452. FixedSize,
  2453. Names) ;
  2454. *EntriesLeft += PartialCount ;
  2455. //
  2456. // Clean up.
  2457. //
  2458. Cleanup:
  2459. //
  2460. // Free any resources used locally
  2461. //
  2462. if ( DomainId != NULL ) {
  2463. NetpMemoryFree( DomainId );
  2464. }
  2465. if ( UsersDomainId != NULL ) {
  2466. NetpMemoryFree( UsersDomainId );
  2467. }
  2468. if ( Names != NULL ) {
  2469. Status = SamFreeMemory( Names );
  2470. NetpAssert( NT_SUCCESS(Status) );
  2471. }
  2472. if ( UserHandle != NULL ) {
  2473. (VOID) SamCloseHandle( UserHandle );
  2474. }
  2475. if ( GroupMembership != NULL ) {
  2476. Status = SamFreeMemory( GroupMembership );
  2477. NetpAssert( NT_SUCCESS(Status) );
  2478. }
  2479. if ( UserSids != NULL ) {
  2480. for ( GroupIndex = 0; GroupIndex < UserSidCount; GroupIndex ++ ) {
  2481. NetpMemoryFree( UserSids[GroupIndex] );
  2482. }
  2483. NetpMemoryFree( UserSids );
  2484. }
  2485. if ( UserSidList != NULL ) {
  2486. AliaspFreeSidList ( 1, UserSidList );
  2487. }
  2488. if ( Aliases != NULL ) {
  2489. Status = SamFreeMemory( Aliases );
  2490. NetpAssert( NT_SUCCESS(Status) );
  2491. }
  2492. if ( BuiltinDomainHandle != NULL ) {
  2493. UaspCloseDomain( BuiltinDomainHandle );
  2494. }
  2495. if ( DomainHandle != NULL ) {
  2496. UaspCloseDomain( DomainHandle );
  2497. }
  2498. if ( UsersDomainHandle != NULL ) {
  2499. UaspCloseDomain( UsersDomainHandle );
  2500. }
  2501. if ( SamServerHandle != NULL ) {
  2502. (VOID) SamCloseHandle( SamServerHandle );
  2503. }
  2504. //
  2505. // If we're not returning data to the caller,
  2506. // free the return buffer.
  2507. //
  2508. if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
  2509. if ( BufferDescriptor.Buffer != NULL ) {
  2510. MIDL_user_free( BufferDescriptor.Buffer );
  2511. BufferDescriptor.Buffer = NULL;
  2512. }
  2513. *EntriesLeft = 0;
  2514. *EntriesRead = 0;
  2515. }
  2516. *Buffer = BufferDescriptor.Buffer;
  2517. IF_DEBUG( UAS_DEBUG_USER ) {
  2518. NetpKdPrint(( "NetUserGetGroups: returning %ld\n", NetStatus ));
  2519. }
  2520. return NetStatus;
  2521. } // NetUserGetLocalGroups
  2522. NET_API_STATUS NET_API_FUNCTION
  2523. AliaspPackBuf(
  2524. IN DWORD Level,
  2525. IN DWORD PrefMaxLen,
  2526. IN DWORD EntriesCount,
  2527. OUT LPDWORD EntriesRead,
  2528. BUFFER_DESCRIPTOR *BufferDescriptor,
  2529. DWORD FixedSize,
  2530. PUNICODE_STRING Names)
  2531. {
  2532. NET_API_STATUS NetStatus = NERR_Success ;
  2533. ULONG i ;
  2534. //
  2535. // Determine the number of entries that will fit in the caller's
  2536. // buffer.
  2537. //
  2538. for ( i=0; i < EntriesCount; i++ ) {
  2539. DWORD Size;
  2540. PLOCALGROUP_USERS_INFO_0 lgrui0;
  2541. //
  2542. // Compute the size of the next entry
  2543. //
  2544. Size = FixedSize + Names[i].Length + sizeof(WCHAR);
  2545. //
  2546. // Ensure the return buffer is big enough.
  2547. //
  2548. Size = ROUND_UP_COUNT( Size, ALIGN_WCHAR );
  2549. NetStatus = NetpAllocateEnumBuffer(
  2550. BufferDescriptor,
  2551. FALSE, // Is an enumeration routine.
  2552. PrefMaxLen,
  2553. Size,
  2554. AliaspMemberRelocationRoutine,
  2555. Level );
  2556. if ( NetStatus != NERR_Success ) {
  2557. break ;
  2558. }
  2559. //
  2560. // Copy the data into the buffer
  2561. //
  2562. lgrui0 = (PLOCALGROUP_USERS_INFO_0) BufferDescriptor->FixedDataEnd;
  2563. BufferDescriptor->FixedDataEnd += FixedSize ;
  2564. if ( !NetpCopyStringToBuffer(
  2565. Names[i].Buffer,
  2566. Names[i].Length/sizeof(WCHAR),
  2567. BufferDescriptor->FixedDataEnd,
  2568. (LPWSTR *)&(BufferDescriptor->EndOfVariableData),
  2569. &lgrui0->lgrui0_name) ) {
  2570. NetStatus = NERR_InternalError;
  2571. break ;
  2572. }
  2573. (*EntriesRead)++;
  2574. }
  2575. return NetStatus ;
  2576. }
  2577. NET_API_STATUS NET_API_FUNCTION
  2578. NetUserModalsGet(
  2579. IN LPCWSTR ServerName OPTIONAL,
  2580. IN DWORD Level,
  2581. OUT LPBYTE *Buffer
  2582. )
  2583. /*++
  2584. Routine Description:
  2585. Retrieve global information for all users and groups in the user
  2586. account database.
  2587. Arguments:
  2588. ServerName - A pointer to a string containing the name of the remote
  2589. server on which the function is to execute. A NULL pointer
  2590. or string specifies the local machine.
  2591. Level - Level of information required. 0, 1, and 2 are valid.
  2592. Buffer - Returns a pointer to the return information structure.
  2593. Caller must deallocate buffer using NetApiBufferFree.
  2594. Return Value:
  2595. Error code for the operation.
  2596. --*/
  2597. {
  2598. NTSTATUS Status;
  2599. NET_API_STATUS NetStatus;
  2600. SAM_HANDLE SamServerHandle = NULL;
  2601. SAM_HANDLE DomainHandle = NULL;
  2602. PSID DomainId = NULL;
  2603. ACCESS_MASK DesiredAccess;
  2604. DWORD Size; // Size of returned information
  2605. PDOMAIN_PASSWORD_INFORMATION DomainPassword = NULL;
  2606. PDOMAIN_LOGOFF_INFORMATION DomainLogoff = NULL;
  2607. PDOMAIN_SERVER_ROLE_INFORMATION DomainServerRole = NULL;
  2608. PDOMAIN_REPLICATION_INFORMATION DomainReplication = NULL;
  2609. PDOMAIN_NAME_INFORMATION DomainName = NULL;
  2610. PDOMAIN_LOCKOUT_INFORMATION DomainLockout = NULL;
  2611. //
  2612. // Validate Level
  2613. //
  2614. *Buffer = NULL;
  2615. switch (Level) {
  2616. case 0:
  2617. DesiredAccess =
  2618. DOMAIN_READ_OTHER_PARAMETERS | DOMAIN_READ_PASSWORD_PARAMETERS ;
  2619. break;
  2620. case 1:
  2621. DesiredAccess = DOMAIN_READ_OTHER_PARAMETERS;
  2622. break;
  2623. case 2:
  2624. DesiredAccess = DOMAIN_READ_OTHER_PARAMETERS;
  2625. break;
  2626. case 3:
  2627. DesiredAccess = DOMAIN_READ_PASSWORD_PARAMETERS;
  2628. break;
  2629. default:
  2630. NetStatus = ERROR_INVALID_LEVEL;
  2631. goto Cleanup;
  2632. }
  2633. //
  2634. // Connect to the SAM server
  2635. //
  2636. NetStatus = UaspOpenSam( ServerName,
  2637. FALSE, // Don't try null session
  2638. &SamServerHandle );
  2639. if ( NetStatus != NERR_Success ) {
  2640. IF_DEBUG( UAS_DEBUG_USER ) {
  2641. NetpKdPrint(( "NetUserModalsGet: Cannot UaspOpenSam %ld\n", NetStatus ));
  2642. }
  2643. goto Cleanup;
  2644. }
  2645. //
  2646. // Open the Domain
  2647. //
  2648. NetStatus = UaspOpenDomain( SamServerHandle,
  2649. DesiredAccess,
  2650. TRUE, // Account Domain
  2651. &DomainHandle,
  2652. &DomainId );
  2653. if ( NetStatus != NERR_Success ) {
  2654. goto Cleanup;
  2655. }
  2656. //
  2657. // Get the desired information from SAM and determine the size of
  2658. // our return information.
  2659. //
  2660. switch (Level) {
  2661. case 0:
  2662. Status = SamQueryInformationDomain(
  2663. DomainHandle,
  2664. DomainPasswordInformation,
  2665. (PVOID *)&DomainPassword );
  2666. if ( !NT_SUCCESS( Status ) ) {
  2667. NetStatus = NetpNtStatusToApiStatus( Status );
  2668. goto Cleanup;
  2669. }
  2670. Status = SamQueryInformationDomain(
  2671. DomainHandle,
  2672. DomainLogoffInformation,
  2673. (PVOID *)&DomainLogoff );
  2674. if ( !NT_SUCCESS( Status ) ) {
  2675. NetStatus = NetpNtStatusToApiStatus( Status );
  2676. goto Cleanup;
  2677. }
  2678. Size = sizeof( USER_MODALS_INFO_0 );
  2679. break;
  2680. case 1:
  2681. Status = SamQueryInformationDomain(
  2682. DomainHandle,
  2683. DomainServerRoleInformation,
  2684. (PVOID *)&DomainServerRole );
  2685. if ( !NT_SUCCESS( Status ) ) {
  2686. NetStatus = NetpNtStatusToApiStatus( Status );
  2687. goto Cleanup;
  2688. }
  2689. Status = SamQueryInformationDomain(
  2690. DomainHandle,
  2691. DomainReplicationInformation,
  2692. (PVOID *)&DomainReplication );
  2693. if ( !NT_SUCCESS( Status ) ) {
  2694. NetStatus = NetpNtStatusToApiStatus( Status );
  2695. goto Cleanup;
  2696. }
  2697. Size = sizeof( USER_MODALS_INFO_1 ) +
  2698. DomainReplication->ReplicaSourceNodeName.Length + sizeof(WCHAR);
  2699. break;
  2700. case 2:
  2701. Status = SamQueryInformationDomain(
  2702. DomainHandle,
  2703. DomainNameInformation,
  2704. (PVOID *)&DomainName );
  2705. if ( !NT_SUCCESS( Status ) ) {
  2706. NetStatus = NetpNtStatusToApiStatus( Status );
  2707. goto Cleanup;
  2708. }
  2709. Size = sizeof( USER_MODALS_INFO_2 ) +
  2710. DomainName->DomainName.Length + sizeof(WCHAR) +
  2711. RtlLengthSid( DomainId );
  2712. break;
  2713. case 3:
  2714. Status = SamQueryInformationDomain(
  2715. DomainHandle,
  2716. DomainLockoutInformation,
  2717. (PVOID *)&DomainLockout );
  2718. if ( !NT_SUCCESS( Status ) ) {
  2719. NetStatus = NetpNtStatusToApiStatus( Status );
  2720. goto Cleanup;
  2721. }
  2722. Size = sizeof( USER_MODALS_INFO_3 );
  2723. break;
  2724. default:
  2725. NetStatus = ERROR_INVALID_LEVEL;
  2726. goto Cleanup;
  2727. }
  2728. //
  2729. // Allocate the return buffer
  2730. //
  2731. Size = ROUND_UP_COUNT( Size, ALIGN_WCHAR );
  2732. *Buffer = MIDL_user_allocate( Size );
  2733. if ( *Buffer == NULL ) {
  2734. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2735. goto Cleanup;
  2736. }
  2737. //
  2738. // Fill in the return buffer
  2739. //
  2740. switch (Level) {
  2741. case 0: {
  2742. PUSER_MODALS_INFO_0 usrmod0 = (PUSER_MODALS_INFO_0) *Buffer;
  2743. usrmod0->usrmod0_min_passwd_len = DomainPassword->MinPasswordLength;
  2744. usrmod0->usrmod0_max_passwd_age =
  2745. NetpDeltaTimeToSeconds( DomainPassword->MaxPasswordAge );
  2746. usrmod0->usrmod0_min_passwd_age =
  2747. NetpDeltaTimeToSeconds( DomainPassword->MinPasswordAge );
  2748. usrmod0->usrmod0_force_logoff =
  2749. NetpDeltaTimeToSeconds( DomainLogoff->ForceLogoff );
  2750. usrmod0->usrmod0_password_hist_len =
  2751. DomainPassword->PasswordHistoryLength;
  2752. break;
  2753. }
  2754. case 1: {
  2755. PUSER_MODALS_INFO_1 usrmod1 = (PUSER_MODALS_INFO_1) *Buffer;
  2756. LPWSTR EndOfVariableData = (LPWSTR) (*Buffer + Size);
  2757. switch (DomainServerRole->DomainServerRole) {
  2758. case DomainServerRolePrimary:
  2759. usrmod1->usrmod1_role = UAS_ROLE_PRIMARY;
  2760. break;
  2761. case DomainServerRoleBackup:
  2762. usrmod1->usrmod1_role = UAS_ROLE_BACKUP;
  2763. break;
  2764. default:
  2765. NetStatus = NERR_InternalError;
  2766. goto Cleanup;
  2767. }
  2768. if ( !NetpCopyStringToBuffer(
  2769. DomainReplication->ReplicaSourceNodeName.Buffer,
  2770. DomainReplication->ReplicaSourceNodeName.Length/sizeof(WCHAR),
  2771. *Buffer + sizeof(USER_MODALS_INFO_1),
  2772. &EndOfVariableData,
  2773. &usrmod1->usrmod1_primary) ) {
  2774. NetStatus = NERR_InternalError;
  2775. goto Cleanup;
  2776. }
  2777. break;
  2778. }
  2779. case 2: {
  2780. PUSER_MODALS_INFO_2 usrmod2 = (PUSER_MODALS_INFO_2) *Buffer;
  2781. LPWSTR EndOfVariableData = (LPWSTR) (*Buffer + Size);
  2782. //
  2783. // Copy text first size it has more stringent alignment requirements
  2784. //
  2785. if ( !NetpCopyStringToBuffer(
  2786. DomainName->DomainName.Buffer,
  2787. DomainName->DomainName.Length/sizeof(WCHAR),
  2788. *Buffer + sizeof(USER_MODALS_INFO_2),
  2789. &EndOfVariableData,
  2790. &usrmod2->usrmod2_domain_name) ) {
  2791. NetStatus = NERR_InternalError;
  2792. goto Cleanup;
  2793. }
  2794. if ( !NetpCopyDataToBuffer(
  2795. DomainId,
  2796. RtlLengthSid( DomainId ),
  2797. *Buffer + sizeof(USER_MODALS_INFO_2),
  2798. (LPBYTE *)&EndOfVariableData,
  2799. (LPBYTE *)&usrmod2->usrmod2_domain_id,
  2800. sizeof(BYTE) ) ) {
  2801. NetStatus = NERR_InternalError;
  2802. goto Cleanup;
  2803. }
  2804. break;
  2805. }
  2806. case 3: {
  2807. PUSER_MODALS_INFO_3 usrmod3 = (PUSER_MODALS_INFO_3) *Buffer;
  2808. usrmod3->usrmod3_lockout_duration =
  2809. NetpDeltaTimeToSeconds( DomainLockout->LockoutDuration );
  2810. usrmod3->usrmod3_lockout_observation_window =
  2811. NetpDeltaTimeToSeconds( DomainLockout->LockoutObservationWindow );
  2812. usrmod3->usrmod3_lockout_threshold = DomainLockout->LockoutThreshold;
  2813. break;
  2814. }
  2815. default:
  2816. NetStatus = ERROR_INVALID_LEVEL;
  2817. goto Cleanup;
  2818. }
  2819. NetStatus = NERR_Success;
  2820. //
  2821. // Clean up.
  2822. //
  2823. Cleanup:
  2824. if (DomainPassword != NULL) {
  2825. Status = SamFreeMemory( DomainPassword );
  2826. NetpAssert( NT_SUCCESS(Status) );
  2827. }
  2828. if (DomainLogoff != NULL) {
  2829. Status = SamFreeMemory( DomainLogoff );
  2830. NetpAssert( NT_SUCCESS(Status) );
  2831. }
  2832. if (DomainServerRole != NULL) {
  2833. Status = SamFreeMemory( DomainServerRole );
  2834. NetpAssert( NT_SUCCESS(Status) );
  2835. }
  2836. if (DomainReplication != NULL) {
  2837. Status = SamFreeMemory( DomainReplication );
  2838. NetpAssert( NT_SUCCESS(Status) );
  2839. }
  2840. if (DomainName != NULL) {
  2841. Status = SamFreeMemory( DomainName );
  2842. NetpAssert( NT_SUCCESS(Status) );
  2843. }
  2844. if ( DomainLockout != NULL ) {
  2845. Status = SamFreeMemory( DomainLockout );
  2846. NetpAssert( NT_SUCCESS(Status) );
  2847. }
  2848. if ( DomainId != NULL ) {
  2849. NetpMemoryFree( DomainId );
  2850. }
  2851. if ( DomainHandle != NULL ) {
  2852. UaspCloseDomain( DomainHandle );
  2853. }
  2854. if ( SamServerHandle != NULL ) {
  2855. (VOID) SamCloseHandle( SamServerHandle );
  2856. }
  2857. //
  2858. // Handle downlevel.
  2859. //
  2860. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  2861. NetStatus = RxNetUserModalsGet( (LPWSTR)ServerName, Level, Buffer );
  2862. UASP_DOWNLEVEL_END;
  2863. IF_DEBUG( UAS_DEBUG_USER ) {
  2864. NetpKdPrint(( "NetUserModalsGet: returning %ld\n", NetStatus ));
  2865. }
  2866. return NetStatus;
  2867. } // NetUserModalsGet
  2868. NET_API_STATUS NET_API_FUNCTION
  2869. NetUserModalsSet(
  2870. IN LPCWSTR ServerName OPTIONAL,
  2871. IN DWORD Level,
  2872. IN LPBYTE Buffer,
  2873. OUT LPDWORD ParmError OPTIONAL // Name required by NetpSetParmError
  2874. )
  2875. /*++
  2876. Routine Description:
  2877. Sets global information for all users and group in the user account.
  2878. Arguments:
  2879. ServerName - A pointer to a string containing the name of the remote
  2880. server on which the function is to execute. A NULL pointer
  2881. or string specifies the local machine.
  2882. Level - Level of information provided.
  2883. Buffer - A pointer to the buffer containing the user information
  2884. structure.
  2885. ParmError - Optional pointer to a DWORD to return the index of the
  2886. first parameter in error when ERROR_INVALID_PARAMETER is returned.
  2887. If NULL, the parameter is not returned on error.
  2888. Return Value:
  2889. Error code for the operation.
  2890. --*/
  2891. {
  2892. NET_API_STATUS NetStatus;
  2893. NTSTATUS Status;
  2894. SAM_HANDLE SamServerHandle = NULL;
  2895. SAM_HANDLE DomainHandle = NULL;
  2896. ACCESS_MASK DesiredAccess;
  2897. DWORD UasSamIndex;
  2898. BOOL LSAServerRoleSet = FALSE;
  2899. BOOL BuiltinDomainServerRoleSet = FALSE;
  2900. //
  2901. // Each SAM Information Class is described here. If multiple fields
  2902. // can be set in the same information class, each field is set in a
  2903. // common copy of the information class strcuture.
  2904. //
  2905. struct _SAM_INFORMATION_CLASS {
  2906. //
  2907. // Sam's DomainInformation class for this class.
  2908. //
  2909. DOMAIN_INFORMATION_CLASS DomainInformationClass;
  2910. //
  2911. // The size of this information class structure.
  2912. //
  2913. DWORD SamSize;
  2914. //
  2915. // The state of this information class. As we decide to use this
  2916. // information class, actually change it, and possibly restore
  2917. // its old value, we change the state so later stages of this routine
  2918. // can handle each entry.
  2919. //
  2920. enum {
  2921. UTS_NOT_USED, // No fields are being used
  2922. UTS_USED, // At least one field is to be changed
  2923. UTS_READ, // This info class has been read from SAM
  2924. UTS_DONE, // This info class has been changed in SAM
  2925. UTS_RECOVERED // This info class has be reverted to old values.
  2926. } State;
  2927. //
  2928. // Before this routine changes anything, it gets the old value for
  2929. // each of the information classes. This old value is used for
  2930. // recovery in the event that an error occurs. That is, we will
  2931. // attempt to put the old information back if we aren't successful
  2932. // in changing all the information to the new values.
  2933. //
  2934. // The old information is also used in the case where a single
  2935. // information class contains multiple fields and we're only changing
  2936. // a subset of those fields.
  2937. //
  2938. PVOID OldInformation;
  2939. //
  2940. // The new field values are stored in this instance of the information
  2941. // class.
  2942. //
  2943. PVOID NewInformation;
  2944. //
  2945. // The DesiredAccess mask includes both the access to read and the
  2946. // access to write the appropriate DomainInformationClass.
  2947. //
  2948. ACCESS_MASK DesiredAccess;
  2949. } SamInfoClass[] = {
  2950. //
  2951. // Define a SAM_INFORMATION_CLASS for each information class possibly
  2952. // used.
  2953. //
  2954. // The order of the entries in this array must match the order of
  2955. // the SAM_* defines above.
  2956. //
  2957. /* SAM_LogoffClass */ {
  2958. DomainLogoffInformation, sizeof( DOMAIN_LOGOFF_INFORMATION ),
  2959. UTS_NOT_USED, NULL, NULL,
  2960. DOMAIN_READ_OTHER_PARAMETERS | DOMAIN_WRITE_OTHER_PARAMETERS
  2961. },
  2962. /* SAM_NameClass */ {
  2963. DomainNameInformation, sizeof( DOMAIN_NAME_INFORMATION ),
  2964. UTS_NOT_USED, NULL, NULL,
  2965. DOMAIN_READ_OTHER_PARAMETERS | DOMAIN_WRITE_OTHER_PARAMETERS
  2966. },
  2967. /* SAM_PasswordClass */ {
  2968. DomainPasswordInformation, sizeof( DOMAIN_PASSWORD_INFORMATION),
  2969. UTS_NOT_USED, NULL, NULL,
  2970. DOMAIN_READ_PASSWORD_PARAMETERS | DOMAIN_WRITE_PASSWORD_PARAMS
  2971. },
  2972. /* SAM_ReplicationClass */ {
  2973. DomainReplicationInformation, sizeof( DOMAIN_REPLICATION_INFORMATION ),
  2974. UTS_NOT_USED, NULL, NULL,
  2975. DOMAIN_READ_OTHER_PARAMETERS | DOMAIN_ADMINISTER_SERVER
  2976. },
  2977. /* SAM_ServerRoleClass */ {
  2978. DomainServerRoleInformation, sizeof( DOMAIN_SERVER_ROLE_INFORMATION ),
  2979. UTS_NOT_USED, NULL, NULL,
  2980. DOMAIN_READ_OTHER_PARAMETERS | DOMAIN_ADMINISTER_SERVER
  2981. },
  2982. /* Sam_LockoutClass */ {
  2983. DomainLockoutInformation, sizeof( DOMAIN_LOCKOUT_INFORMATION ),
  2984. UTS_NOT_USED, NULL, NULL,
  2985. DOMAIN_READ_PASSWORD_PARAMETERS | DOMAIN_WRITE_PASSWORD_PARAMS
  2986. }
  2987. };
  2988. //
  2989. // Define several macros for accessing the various fields of the UAS
  2990. // structure. Each macro takes an index into the UserUasSamTable
  2991. // array and returns the value.
  2992. //
  2993. #define GET_UAS_MODAL_STRING_POINTER( _i ) \
  2994. (*((LPWSTR *)(Buffer + UserUasSamTable[_i].UasOffset)))
  2995. #define GET_UAS_MODAL_DWORD( _i ) \
  2996. (*((DWORD *)(Buffer + UserUasSamTable[_i].UasOffset)))
  2997. //
  2998. // Define a macro which returns a pointer the appropriate SamInfoClass
  2999. // structure given an index into the UserUasSamTable.
  3000. //
  3001. #define SAM_MODAL_CLASS( _i ) \
  3002. SamInfoClass[ UserUasSamTable[_i].Class ]
  3003. //
  3004. // Define a macro to return a pointer to the appropriate field in the
  3005. // new sam structure.
  3006. //
  3007. // The caller should coerce the pointer as appropriate.
  3008. //
  3009. #define GET_SAM_MODAL_FIELD_POINTER( _i ) \
  3010. (((LPBYTE)(SAM_MODAL_CLASS(_i).NewInformation)) + \
  3011. UserUasSamTable[_i].SamOffset)
  3012. //
  3013. // Initialize
  3014. //
  3015. NetpSetParmError( PARM_ERROR_NONE );
  3016. //
  3017. // Go through the list of valid fields determining if the info level
  3018. // is valid and computing the desired access to the domain.
  3019. //
  3020. DesiredAccess = 0;
  3021. for ( UasSamIndex=0 ;
  3022. UasSamIndex<sizeof(UserUasSamTable)/sizeof(UserUasSamTable[0]);
  3023. UasSamIndex++ ){
  3024. //
  3025. // If this field isn't one we're changing, just skip to the next one
  3026. //
  3027. if ( Level != UserUasSamTable[UasSamIndex].UasLevel ) {
  3028. continue;
  3029. }
  3030. //
  3031. // Validate the UAS field based on the field type.
  3032. //
  3033. switch (UserUasSamTable[UasSamIndex].ModalsFieldType ) {
  3034. //
  3035. // If this is a PARMNUM_ALL and the caller passed in a
  3036. // NULL pointer to a string, he doesn't want to change the string.
  3037. //
  3038. // Testing for this now allows us to completely ignore a
  3039. // particular SAM information level if absolutely no fields
  3040. // change in that information level.
  3041. //
  3042. case UMT_STRING:
  3043. if ( GET_UAS_MODAL_STRING_POINTER( UasSamIndex ) == NULL ) {
  3044. continue;
  3045. }
  3046. break;
  3047. //
  3048. // Ensure unsigned shorts are really in range.
  3049. //
  3050. case UMT_USHORT:
  3051. if ( GET_UAS_MODAL_DWORD(UasSamIndex) > USHRT_MAX ) {
  3052. IF_DEBUG( UAS_DEBUG_USER ) {
  3053. NetpKdPrint((
  3054. "NetUserModalsSet: ushort too big %lx Index:%ld\n",
  3055. GET_UAS_MODAL_DWORD(UasSamIndex),
  3056. UasSamIndex ));
  3057. }
  3058. NetpSetParmError( UserUasSamTable[UasSamIndex].UasParmNum );
  3059. NetStatus = ERROR_INVALID_PARAMETER;
  3060. goto Cleanup;
  3061. }
  3062. //
  3063. // Some values are always valid
  3064. //
  3065. case UMT_ULONG:
  3066. case UMT_DELTA:
  3067. break;
  3068. //
  3069. // Ensure the role is a recognized one.
  3070. //
  3071. case UMT_ROLE:
  3072. switch ( GET_UAS_MODAL_DWORD(UasSamIndex) ) {
  3073. case UAS_ROLE_PRIMARY:
  3074. case UAS_ROLE_BACKUP:
  3075. case UAS_ROLE_MEMBER:
  3076. break;
  3077. default:
  3078. IF_DEBUG( UAS_DEBUG_USER ) {
  3079. NetpKdPrint((
  3080. "NetUserModalsSet: invalid role %lx Index:%ld\n",
  3081. GET_UAS_MODAL_DWORD(UasSamIndex),
  3082. UasSamIndex ));
  3083. }
  3084. NetpSetParmError( UserUasSamTable[UasSamIndex].UasParmNum );
  3085. NetStatus = ERROR_INVALID_PARAMETER;
  3086. goto Cleanup;
  3087. }
  3088. break;
  3089. //
  3090. // All cases are explicitly handled.
  3091. //
  3092. default:
  3093. NetStatus = NERR_InternalError;
  3094. goto Cleanup;
  3095. }
  3096. //
  3097. // Flag that this information class is to be set and
  3098. // accumulate the desired access to do all this functionality.
  3099. //
  3100. SAM_MODAL_CLASS(UasSamIndex).State = UTS_USED;
  3101. DesiredAccess |= SAM_MODAL_CLASS(UasSamIndex).DesiredAccess;
  3102. }
  3103. //
  3104. // Check to be sure the user specified a valid Level.
  3105. //
  3106. // The search of the UserUasSamTable should have resulted in
  3107. // at least one match if the arguments are valid.
  3108. //
  3109. if ( DesiredAccess == 0 ) {
  3110. NetStatus = ERROR_INVALID_LEVEL;
  3111. goto Cleanup;
  3112. }
  3113. //
  3114. // Connect to the SAM server
  3115. //
  3116. NetStatus = UaspOpenSam( ServerName,
  3117. FALSE, // Don't try null session
  3118. &SamServerHandle );
  3119. if ( NetStatus != NERR_Success ) {
  3120. IF_DEBUG( UAS_DEBUG_USER ) {
  3121. NetpKdPrint(( "NetUserModalsSet: Cannot UaspOpenSam %ld\n", NetStatus ));
  3122. }
  3123. goto Cleanup;
  3124. }
  3125. //
  3126. // Open the domain asking for accumulated desired access
  3127. //
  3128. NetStatus = UaspOpenDomain( SamServerHandle,
  3129. DesiredAccess,
  3130. TRUE, // Account Domain
  3131. &DomainHandle,
  3132. NULL ); // DomainId
  3133. if ( NetStatus != NERR_Success ) {
  3134. goto Cleanup;
  3135. }
  3136. //
  3137. // For each field we're going to change,
  3138. // Get the current value of the field
  3139. // Determine what the new value will be.
  3140. //
  3141. // The old values will be used later in error recovery and when multiple
  3142. // fields are changed in SAM with one information level.
  3143. //
  3144. for ( UasSamIndex=0 ;
  3145. UasSamIndex<sizeof(UserUasSamTable)/sizeof(UserUasSamTable[0]);
  3146. UasSamIndex++ ) {
  3147. //
  3148. // If this field isn't one we're changing, just skip to the next one
  3149. //
  3150. if ( Level != UserUasSamTable[UasSamIndex].UasLevel ) {
  3151. continue;
  3152. }
  3153. //
  3154. // Handle field types that have some special attributes.
  3155. //
  3156. switch (UserUasSamTable[UasSamIndex].ModalsFieldType ) {
  3157. //
  3158. // If the caller passed in a
  3159. // NULL pointer to a string, he doesn't want to change the string.
  3160. //
  3161. case UMT_STRING:
  3162. if ( GET_UAS_MODAL_STRING_POINTER( UasSamIndex ) == NULL ) {
  3163. continue;
  3164. }
  3165. break;
  3166. //
  3167. // Other field types don't have any special case handling.
  3168. //
  3169. default:
  3170. break;
  3171. }
  3172. //
  3173. // ASSERT: This field type is set via a SAM information class
  3174. //
  3175. //
  3176. // If we've not previously gotten this information class
  3177. // from SAM, do so now.
  3178. //
  3179. // If an information class has multiple fields, then multiple
  3180. // entries in the UserUasSamTable will share the same old
  3181. // information class.
  3182. //
  3183. if ( SAM_MODAL_CLASS(UasSamIndex).State == UTS_USED ) {
  3184. //
  3185. // Allocate space for the New information
  3186. //
  3187. SAM_MODAL_CLASS(UasSamIndex).State = UTS_READ;
  3188. SAM_MODAL_CLASS(UasSamIndex).NewInformation = NetpMemoryAllocate(
  3189. SAM_MODAL_CLASS(UasSamIndex).SamSize );
  3190. if ( SAM_MODAL_CLASS(UasSamIndex).NewInformation == NULL ) {
  3191. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3192. goto Cleanup;
  3193. }
  3194. //
  3195. // Get this information class from SAM.
  3196. //
  3197. Status = SamQueryInformationDomain(
  3198. DomainHandle,
  3199. SAM_MODAL_CLASS(UasSamIndex).DomainInformationClass,
  3200. &SAM_MODAL_CLASS(UasSamIndex).OldInformation );
  3201. if ( !NT_SUCCESS(Status) ) {
  3202. IF_DEBUG( UAS_DEBUG_USER ) {
  3203. NetpKdPrint((
  3204. "NetUserModalsSet: Error from"
  3205. " SamQueryInformationDomain %lx Index:%ld\n",
  3206. Status,
  3207. UasSamIndex ));
  3208. }
  3209. NetStatus = NetpNtStatusToApiStatus( Status );
  3210. goto Cleanup;
  3211. }
  3212. //
  3213. // Initialize the new SAM info class structure with the old
  3214. // values.
  3215. //
  3216. NetpMoveMemory( SAM_MODAL_CLASS(UasSamIndex).NewInformation,
  3217. SAM_MODAL_CLASS(UasSamIndex).OldInformation,
  3218. SAM_MODAL_CLASS(UasSamIndex).SamSize );
  3219. }
  3220. //
  3221. // Set the SAM field in the new information class structure to
  3222. // the UAS requested value.
  3223. //
  3224. switch ( UserUasSamTable[UasSamIndex].ModalsFieldType ) {
  3225. //
  3226. // Handle values of type string
  3227. //
  3228. case UMT_STRING:
  3229. RtlInitUnicodeString(
  3230. (PUNICODE_STRING) GET_SAM_MODAL_FIELD_POINTER(UasSamIndex),
  3231. GET_UAS_MODAL_STRING_POINTER(UasSamIndex) );
  3232. break;
  3233. //
  3234. // Convert delta time to its SAM counterpart
  3235. //
  3236. case UMT_DELTA:
  3237. *((PLARGE_INTEGER) GET_SAM_MODAL_FIELD_POINTER(UasSamIndex)) =
  3238. NetpSecondsToDeltaTime( GET_UAS_MODAL_DWORD(UasSamIndex) );
  3239. IF_DEBUG( UAS_DEBUG_USER ) {
  3240. NetpKdPrint((
  3241. "UserpsetInfo: Index: %ld Setting DeltaTime %lx %lx %lx\n",
  3242. UasSamIndex,
  3243. ((PLARGE_INTEGER) GET_SAM_MODAL_FIELD_POINTER(UasSamIndex))
  3244. ->HighPart,
  3245. ((PLARGE_INTEGER) GET_SAM_MODAL_FIELD_POINTER(UasSamIndex))
  3246. ->LowPart,
  3247. GET_UAS_MODAL_DWORD(UasSamIndex) ));
  3248. }
  3249. break;
  3250. //
  3251. // Copy the unsigned short to the SAM structure
  3252. //
  3253. case UMT_USHORT:
  3254. *((PUSHORT)GET_SAM_MODAL_FIELD_POINTER(UasSamIndex)) =
  3255. (USHORT)GET_UAS_MODAL_DWORD(UasSamIndex);
  3256. break;
  3257. //
  3258. // Copy the unsigned long to the SAM structure
  3259. //
  3260. case UMT_ULONG:
  3261. *((PULONG)GET_SAM_MODAL_FIELD_POINTER(UasSamIndex)) =
  3262. (ULONG)GET_UAS_MODAL_DWORD(UasSamIndex);
  3263. break;
  3264. //
  3265. // Ensure the role is a recognized one.
  3266. //
  3267. case UMT_ROLE:
  3268. switch ( GET_UAS_MODAL_DWORD(UasSamIndex) ) {
  3269. case UAS_ROLE_PRIMARY:
  3270. *((PDOMAIN_SERVER_ROLE)GET_SAM_MODAL_FIELD_POINTER(UasSamIndex)) =
  3271. DomainServerRolePrimary;
  3272. break;
  3273. case UAS_ROLE_BACKUP:
  3274. *((PDOMAIN_SERVER_ROLE)GET_SAM_MODAL_FIELD_POINTER(UasSamIndex)) =
  3275. DomainServerRoleBackup;
  3276. break;
  3277. default:
  3278. IF_DEBUG( UAS_DEBUG_USER ) {
  3279. NetpKdPrint((
  3280. "NetUserModalsSet: invalid role %lx Index:%ld\n",
  3281. GET_UAS_MODAL_DWORD(UasSamIndex),
  3282. UasSamIndex ));
  3283. }
  3284. NetpSetParmError( UserUasSamTable[UasSamIndex].UasParmNum );
  3285. NetStatus = ERROR_INVALID_PARAMETER;
  3286. goto Cleanup;
  3287. }
  3288. break;
  3289. //
  3290. // All types should have been handled above.
  3291. //
  3292. default:
  3293. NetStatus = NERR_InternalError;
  3294. goto Cleanup;
  3295. }
  3296. }
  3297. //
  3298. // Set the new values of the fields
  3299. //
  3300. // For role change, I should stop/start the SAM server as appropriate.
  3301. // The UI will stop/start the NetLogon service. ??
  3302. //
  3303. for ( UasSamIndex=0 ;
  3304. UasSamIndex<sizeof(UserUasSamTable)/sizeof(UserUasSamTable[0]);
  3305. UasSamIndex++ ){
  3306. //
  3307. // Make the changes to the Sam Database now.
  3308. //
  3309. if ( SAM_MODAL_CLASS(UasSamIndex).State == UTS_READ ) {
  3310. // if the information class is DomainServerRoleInformation
  3311. // we need to update the ServerRole in LSA first then in SAM.
  3312. if( SAM_MODAL_CLASS(UasSamIndex).DomainInformationClass ==
  3313. DomainServerRoleInformation ) {
  3314. NetStatus = UaspLSASetServerRole(
  3315. ServerName,
  3316. SAM_MODAL_CLASS(UasSamIndex).NewInformation );
  3317. if( NetStatus != NERR_Success ) {
  3318. IF_DEBUG( UAS_DEBUG_USER ) {
  3319. NetpKdPrint((
  3320. "NetUserModalsSet: Error from"
  3321. " UaspLSASetServerRole %lx Index:%ld\n",
  3322. NetStatus,
  3323. UasSamIndex ));
  3324. }
  3325. NetpSetParmError( UserUasSamTable[UasSamIndex].UasParmNum );
  3326. goto Cleanup;
  3327. }
  3328. LSAServerRoleSet = TRUE;
  3329. NetStatus = UaspBuiltinDomainSetServerRole(
  3330. SamServerHandle,
  3331. SAM_MODAL_CLASS(UasSamIndex).NewInformation );
  3332. if( NetStatus != NERR_Success ) {
  3333. IF_DEBUG( UAS_DEBUG_USER ) {
  3334. NetpKdPrint((
  3335. "NetUserModalsSet: Error from"
  3336. " UaspBuiltinDomainSetServerRole %lx Index:%ld\n",
  3337. NetStatus,
  3338. UasSamIndex ));
  3339. }
  3340. NetpSetParmError( UserUasSamTable[UasSamIndex].UasParmNum );
  3341. goto Cleanup;
  3342. }
  3343. BuiltinDomainServerRoleSet = TRUE;
  3344. }
  3345. Status = SamSetInformationDomain(
  3346. DomainHandle,
  3347. SAM_MODAL_CLASS(UasSamIndex).DomainInformationClass,
  3348. SAM_MODAL_CLASS(UasSamIndex).NewInformation );
  3349. if ( !NT_SUCCESS(Status) ) {
  3350. IF_DEBUG( UAS_DEBUG_USER ) {
  3351. NetpKdPrint((
  3352. "NetUserModalsSet: Error from"
  3353. " SamSetInformationDomain %lx Index:%ld\n",
  3354. Status,
  3355. UasSamIndex ));
  3356. }
  3357. NetpSetParmError( UserUasSamTable[UasSamIndex].UasParmNum );
  3358. NetStatus = NetpNtStatusToApiStatus( Status );
  3359. goto Cleanup;
  3360. }
  3361. //
  3362. // Mark this Entry as having been done
  3363. //
  3364. SAM_MODAL_CLASS(UasSamIndex).State = UTS_DONE ;
  3365. }
  3366. }
  3367. NetStatus = NERR_Success;
  3368. //
  3369. // Clean up.
  3370. //
  3371. Cleanup:
  3372. //
  3373. // need to revert the LSA server role when we are unsuccessful to
  3374. // set the info completely.
  3375. //
  3376. if( NetStatus != NERR_Success && LSAServerRoleSet ) {
  3377. #ifdef notdef
  3378. NetpAssert( !UaspLSASetServerRole(
  3379. ServerName,
  3380. SamInfoClass[SAM_ServerRoleClass].OldInformation ) );
  3381. #endif
  3382. }
  3383. //
  3384. // revert server role in builtin domain
  3385. //
  3386. if( NetStatus != NERR_Success && BuiltinDomainServerRoleSet ) {
  3387. #ifdef notdef
  3388. NetpAssert( !UaspBuiltinDomainSetServerRole(
  3389. SamServerHandle,
  3390. SamInfoClass[SAM_ServerRoleClass].OldInformation ) );
  3391. #endif
  3392. }
  3393. //
  3394. // Loop through the UserUasSamTable cleaning up anything that
  3395. // needs cleaning.
  3396. //
  3397. for ( UasSamIndex=0 ;
  3398. UasSamIndex<sizeof(UserUasSamTable)/sizeof(UserUasSamTable[0]);
  3399. UasSamIndex++ ) {
  3400. //
  3401. // If we've not been able to change everything and
  3402. // this information class has been changed above, change it
  3403. // back to the old value here. Ignore any error codes. We
  3404. // will report the original error to the caller.
  3405. //
  3406. if ( NetStatus != NERR_Success &&
  3407. SAM_MODAL_CLASS(UasSamIndex).State == UTS_DONE ) {
  3408. Status = SamSetInformationDomain(
  3409. DomainHandle,
  3410. SAM_MODAL_CLASS(UasSamIndex).DomainInformationClass,
  3411. SAM_MODAL_CLASS(UasSamIndex).OldInformation );
  3412. NetpAssert( NT_SUCCESS(Status) );
  3413. SAM_MODAL_CLASS(UasSamIndex).State = UTS_RECOVERED ;
  3414. }
  3415. //
  3416. // Free any allocated Old information class.
  3417. //
  3418. if ( SAM_MODAL_CLASS(UasSamIndex).OldInformation != NULL ) {
  3419. Status =
  3420. SamFreeMemory( SAM_MODAL_CLASS(UasSamIndex).OldInformation );
  3421. NetpAssert( NT_SUCCESS(Status) );
  3422. SAM_MODAL_CLASS(UasSamIndex).OldInformation = NULL;
  3423. }
  3424. //
  3425. // Free any allocated New information class.
  3426. //
  3427. if ( SAM_MODAL_CLASS(UasSamIndex).NewInformation != NULL ) {
  3428. NetpMemoryFree( SAM_MODAL_CLASS(UasSamIndex).NewInformation );
  3429. SAM_MODAL_CLASS(UasSamIndex).NewInformation = NULL;
  3430. }
  3431. }
  3432. if ( DomainHandle != NULL ) {
  3433. UaspCloseDomain( DomainHandle );
  3434. }
  3435. if ( SamServerHandle != NULL ) {
  3436. (VOID) SamCloseHandle( SamServerHandle );
  3437. }
  3438. //
  3439. // Handle downlevel.
  3440. //
  3441. UASP_DOWNLEVEL_BEGIN( ServerName, NetStatus )
  3442. NetStatus = RxNetUserModalsSet( (LPWSTR) ServerName, Level, Buffer, ParmError );
  3443. UASP_DOWNLEVEL_END;
  3444. IF_DEBUG( UAS_DEBUG_USER ) {
  3445. NetpKdPrint(( "NetUserModalsSet: returning %ld\n", NetStatus ));
  3446. }
  3447. return NetStatus;
  3448. } // NetUserModalsSet
  3449. NET_API_STATUS NET_API_FUNCTION
  3450. NetUserChangePassword(
  3451. IN LPCWSTR DomainName,
  3452. IN LPCWSTR UserName,
  3453. IN LPCWSTR OldPassword,
  3454. IN LPCWSTR NewPassword
  3455. )
  3456. /*++
  3457. Routine Description:
  3458. Changes a users password.
  3459. Arguments:
  3460. DomainName - A pointer to a string containing the name of the domain or
  3461. remote server on which to change the password. The name is assuemd
  3462. to be a domain name unless it begins with "\\". If no domain can be
  3463. located by that name, it is prepended with "\\" and tried as a server
  3464. name.
  3465. If this parameter is not present the domain of the logged on
  3466. user is used.
  3467. UserName - Name of the user who's password is to be changed.
  3468. If this parameter is not present, the logged on user is used.
  3469. OldPassword - NULL terminated string containing the user's old password
  3470. NewPassword - NULL terminated string containing the user's new password.
  3471. Return Value:
  3472. Error code for the operation.
  3473. --*/
  3474. {
  3475. NTSTATUS Status;
  3476. HANDLE LsaHandle = NULL;
  3477. NET_API_STATUS NetStatus = 0;
  3478. PMSV1_0_CHANGEPASSWORD_REQUEST ChangeRequest = NULL;
  3479. PMSV1_0_CHANGEPASSWORD_RESPONSE ChangeResponse = NULL;
  3480. STRING PackageName;
  3481. ULONG PackageId;
  3482. ULONG RequestSize;
  3483. ULONG ResponseSize = 0;
  3484. PBYTE Where;
  3485. NTSTATUS ProtocolStatus;
  3486. PSECURITY_SEED_AND_LENGTH SeedAndLength;
  3487. UCHAR Seed;
  3488. PUNICODE_STRING LsaUserName = NULL;
  3489. PUNICODE_STRING LsaDomainName = NULL;
  3490. UNICODE_STRING UserNameU;
  3491. UNICODE_STRING DomainNameU;
  3492. //
  3493. // If a user name and domain were not supplied, generate them now.
  3494. //
  3495. if ((DomainName == NULL) || (UserName == NULL)) {
  3496. Status = LsaGetUserName(
  3497. &LsaUserName,
  3498. &LsaDomainName
  3499. );
  3500. if (!NT_SUCCESS(Status)) {
  3501. NetStatus = NetpNtStatusToApiStatus( Status );
  3502. goto Cleanup;
  3503. }
  3504. }
  3505. if (UserName == NULL) {
  3506. UserNameU = *LsaUserName;
  3507. } else {
  3508. RtlInitUnicodeString(
  3509. &UserNameU,
  3510. UserName
  3511. );
  3512. }
  3513. if (DomainName == NULL) {
  3514. DomainNameU = *LsaDomainName;
  3515. } else {
  3516. RtlInitUnicodeString(
  3517. &DomainNameU,
  3518. DomainName
  3519. );
  3520. }
  3521. //
  3522. // Calculate the request size
  3523. //
  3524. RequestSize = sizeof(MSV1_0_CHANGEPASSWORD_REQUEST) +
  3525. UserNameU.Length + sizeof(WCHAR) +
  3526. DomainNameU.Length + sizeof(WCHAR);
  3527. if (ARGUMENT_PRESENT(OldPassword)) {
  3528. RequestSize += (wcslen(OldPassword)+1) * sizeof(WCHAR);
  3529. } else {
  3530. NetStatus = ERROR_INVALID_PARAMETER;
  3531. goto Cleanup;
  3532. }
  3533. if (ARGUMENT_PRESENT(NewPassword)) {
  3534. RequestSize += (wcslen(NewPassword)+1) * sizeof(WCHAR);
  3535. } else {
  3536. NetStatus = ERROR_INVALID_PARAMETER;
  3537. goto Cleanup;
  3538. }
  3539. //
  3540. // Connect to the LSA
  3541. //
  3542. Status = LsaConnectUntrusted(
  3543. &LsaHandle
  3544. );
  3545. if (!NT_SUCCESS(Status)) {
  3546. NetStatus = NetpNtStatusToApiStatus( Status );
  3547. goto Cleanup;
  3548. }
  3549. RtlInitString(
  3550. &PackageName,
  3551. MSV1_0_PACKAGE_NAME
  3552. );
  3553. Status = LsaLookupAuthenticationPackage(
  3554. LsaHandle,
  3555. &PackageName,
  3556. &PackageId
  3557. );
  3558. if (!NT_SUCCESS(Status)) {
  3559. NetStatus = NetpNtStatusToApiStatus( Status );
  3560. goto Cleanup;
  3561. }
  3562. //
  3563. // Allocate the request buffer
  3564. //
  3565. ChangeRequest = NetpMemoryAllocate( RequestSize );
  3566. if (ChangeRequest == NULL) {
  3567. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3568. goto Cleanup;
  3569. }
  3570. //
  3571. // Build up the request message
  3572. //
  3573. ChangeRequest->MessageType = MsV1_0ChangePassword;
  3574. ChangeRequest->DomainName = DomainNameU;
  3575. ChangeRequest->AccountName = UserNameU;
  3576. RtlInitUnicodeString(
  3577. &ChangeRequest->OldPassword,
  3578. OldPassword
  3579. );
  3580. //
  3581. // Limit passwords to 127 bytes so we can run-encode them.
  3582. //
  3583. if (ChangeRequest->OldPassword.Length > 127) {
  3584. NetStatus = ERROR_PASSWORD_RESTRICTION;
  3585. goto Cleanup;
  3586. }
  3587. RtlInitUnicodeString(
  3588. &ChangeRequest->NewPassword,
  3589. NewPassword
  3590. );
  3591. if (ChangeRequest->NewPassword.Length > 127) {
  3592. NetStatus = ERROR_PASSWORD_RESTRICTION;
  3593. goto Cleanup;
  3594. }
  3595. //
  3596. // Marshall the buffer pointers. We run-encode the passwords so
  3597. // we don't have cleartext password lying around the pagefile.
  3598. //
  3599. Where = (PBYTE) (ChangeRequest+1);
  3600. ChangeRequest->DomainName.Buffer = (LPWSTR) Where;
  3601. RtlCopyMemory(
  3602. Where,
  3603. DomainNameU.Buffer,
  3604. ChangeRequest->DomainName.MaximumLength
  3605. );
  3606. Where += ChangeRequest->DomainName.MaximumLength;
  3607. ChangeRequest->AccountName.Buffer = (LPWSTR) Where;
  3608. RtlCopyMemory(
  3609. Where,
  3610. UserNameU.Buffer,
  3611. ChangeRequest->AccountName.MaximumLength
  3612. );
  3613. Where += ChangeRequest->AccountName.MaximumLength;
  3614. ChangeRequest->OldPassword.Buffer = (LPWSTR) Where;
  3615. RtlCopyMemory(
  3616. Where,
  3617. OldPassword,
  3618. ChangeRequest->OldPassword.MaximumLength
  3619. );
  3620. Where += ChangeRequest->OldPassword.MaximumLength;
  3621. //
  3622. // Run encode the passwords so they don't lie around the page file.
  3623. //
  3624. Seed = 0;
  3625. RtlRunEncodeUnicodeString(
  3626. &Seed,
  3627. &ChangeRequest->OldPassword
  3628. );
  3629. SeedAndLength = (PSECURITY_SEED_AND_LENGTH) &ChangeRequest->OldPassword.Length;
  3630. SeedAndLength->Seed = Seed;
  3631. ChangeRequest->NewPassword.Buffer = (LPWSTR) Where;
  3632. RtlCopyMemory(
  3633. Where,
  3634. NewPassword,
  3635. ChangeRequest->NewPassword.MaximumLength
  3636. );
  3637. Where += ChangeRequest->NewPassword.MaximumLength;
  3638. Seed = 0;
  3639. RtlRunEncodeUnicodeString(
  3640. &Seed,
  3641. &ChangeRequest->NewPassword
  3642. );
  3643. SeedAndLength = (PSECURITY_SEED_AND_LENGTH) &ChangeRequest->NewPassword.Length;
  3644. SeedAndLength->Seed = Seed;
  3645. //
  3646. // Since we are running in the caller's process, we most certainly are
  3647. // impersonating him/her.
  3648. //
  3649. ChangeRequest->Impersonating = TRUE;
  3650. //
  3651. // Call the MSV1_0 package to change the password.
  3652. //
  3653. Status = LsaCallAuthenticationPackage(
  3654. LsaHandle,
  3655. PackageId,
  3656. ChangeRequest,
  3657. RequestSize,
  3658. (PVOID *) &ChangeResponse,
  3659. &ResponseSize,
  3660. &ProtocolStatus
  3661. );
  3662. if (!NT_SUCCESS(Status)) {
  3663. NetStatus = NetpNtStatusToApiStatus( Status );
  3664. goto Cleanup;
  3665. }
  3666. if (!NT_SUCCESS(ProtocolStatus)) {
  3667. NetStatus = NetpNtStatusToApiStatus( ProtocolStatus );
  3668. goto Cleanup;
  3669. }
  3670. NetStatus = ERROR_SUCCESS;
  3671. Cleanup:
  3672. if (LsaHandle != NULL) {
  3673. NtClose(LsaHandle);
  3674. }
  3675. if (ChangeRequest != NULL) {
  3676. RtlZeroMemory( ChangeRequest, RequestSize );
  3677. NetpMemoryFree( ChangeRequest );
  3678. }
  3679. if (ChangeResponse != NULL) {
  3680. LsaFreeReturnBuffer( ChangeResponse );
  3681. }
  3682. if (LsaUserName != NULL) {
  3683. LsaFreeMemory(LsaUserName->Buffer);
  3684. LsaFreeMemory(LsaUserName);
  3685. }
  3686. if (LsaDomainName != NULL) {
  3687. LsaFreeMemory(LsaDomainName->Buffer);
  3688. LsaFreeMemory(LsaDomainName);
  3689. }
  3690. return(NetStatus);
  3691. }
  3692. /*lint +e614 */
  3693. /*lint +e740 */