Leaked source code of windows server 2003
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.

1664 lines
49 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. user.c
  5. Abstract:
  6. This module contains the worker routines for the NetWkstaUser
  7. APIs implemented in the Workstation service.
  8. Author:
  9. Rita Wong (ritaw) 20-Feb-1991
  10. Revision History:
  11. --*/
  12. #include "wsutil.h"
  13. #include "wsdevice.h"
  14. #include "wssec.h"
  15. #include "wsconfig.h"
  16. #include "wslsa.h"
  17. #include "wswksta.h"
  18. #include <strarray.h>
  19. #include <config.h> // NT config file helpers in netlib
  20. #include <configp.h> // USE_WIN32_CONFIG (if defined), etc.
  21. #include <confname.h> // Section and keyword equates.
  22. #include "wsregcfg.h" // Registry helpers
  23. #define WS_OTH_DOMAIN_DELIMITER_STR L" "
  24. #define WS_OTH_DOMAIN_DELIMITER_CHAR L' '
  25. //-------------------------------------------------------------------//
  26. // //
  27. // Local function prototypes //
  28. // //
  29. //-------------------------------------------------------------------//
  30. STATIC
  31. NET_API_STATUS
  32. WsGetUserInfo(
  33. IN PLUID LogonId,
  34. IN DWORD Level,
  35. OUT PMSV1_0_GETUSERINFO_RESPONSE *UserInfoResponse,
  36. OUT PDGRECEIVE_NAMES *DgrNames,
  37. OUT LPDWORD DgrNamesCount,
  38. IN OUT LPDWORD TotalBytesNeeded OPTIONAL
  39. );
  40. STATIC
  41. NET_API_STATUS
  42. WsGetActiveDgrNames(
  43. IN PLUID LogonId,
  44. OUT PDGRECEIVE_NAMES *DgrNames,
  45. OUT LPDWORD DgrNamesCount,
  46. IN OUT LPDWORD TotalBytesNeeded OPTIONAL
  47. );
  48. STATIC
  49. NET_API_STATUS
  50. WsSetOtherDomains(
  51. IN DWORD Level,
  52. IN LPBYTE Buffer
  53. );
  54. STATIC
  55. NET_API_STATUS
  56. WsEnumUserInfo(
  57. IN DWORD Level,
  58. IN DWORD PreferedMaximumLength,
  59. IN PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse,
  60. OUT LPBYTE *OutputBuffer,
  61. OUT LPDWORD EntriesRead,
  62. OUT LPDWORD TotalEntries,
  63. IN OUT LPDWORD ResumeHandle OPTIONAL
  64. );
  65. STATIC
  66. NET_API_STATUS
  67. WsPackageUserInfo(
  68. IN DWORD Level,
  69. IN DWORD UserInfoFixedLength,
  70. IN PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse,
  71. IN PDGRECEIVE_NAMES DgrNames,
  72. IN DWORD DgrNamesCount,
  73. IN OUT LPBYTE *FixedPortion,
  74. IN OUT LPTSTR *EndOfVariableData,
  75. IN OUT LPDWORD EntriesRead OPTIONAL
  76. );
  77. STATIC
  78. BOOL
  79. WsFillUserInfoBuffer(
  80. IN DWORD Level,
  81. IN PMSV1_0_GETUSERINFO_RESPONSE UserInfo,
  82. IN PDGRECEIVE_NAMES DgrNames,
  83. IN DWORD DgrNamesCount,
  84. IN OUT LPBYTE *FixedPortion,
  85. IN OUT LPTSTR *EndOfVariableData,
  86. IN DWORD UserInfoFixedLength
  87. );
  88. STATIC
  89. VOID
  90. WsWriteOtherDomains(
  91. IN PDGRECEIVE_NAMES DgrNames,
  92. IN DWORD DgrNamesCount,
  93. IN OUT LPBYTE *FixedPortion,
  94. IN OUT LPTSTR *EndOfVariableData,
  95. IN DWORD UserInfoFixedLength,
  96. OUT LPWSTR *OtherDomainsPointer
  97. );
  98. //-------------------------------------------------------------------//
  99. // //
  100. // Global variables //
  101. // //
  102. //-------------------------------------------------------------------//
  103. NET_API_STATUS NET_API_FUNCTION
  104. NetrWkstaUserGetInfo(
  105. IN LPTSTR Reserved,
  106. IN DWORD Level,
  107. OUT LPWKSTA_USER_INFO UserInfo
  108. )
  109. /*++
  110. Routine Description:
  111. This function is the NetWkstaUserGetInfo entry point in the Workstation
  112. service. It calls the LSA subsystem and the MSV1_0 authentication
  113. package to get per user information.
  114. Arguments:
  115. Reserved - Must be 0.
  116. Level - Supplies the requested level of information.
  117. UserInfo - Returns, in this structure, a pointer to a buffer which
  118. contains the requested user information.
  119. Return Value:
  120. NET_API_STATUS - NERR_Success or reason for failure.
  121. --*/
  122. {
  123. NET_API_STATUS status;
  124. LUID LogonId;
  125. PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse = NULL;
  126. LPBYTE FixedPortion;
  127. LPTSTR EndOfVariableData;
  128. DWORD UserInfoFixedLength = USER_FIXED_LENGTH(Level);
  129. LPBYTE OutputBuffer;
  130. DWORD TotalBytesNeeded = 0;
  131. PDGRECEIVE_NAMES DgrNames = NULL;
  132. DWORD DgrNamesCount;
  133. if (Reserved != NULL) {
  134. return ERROR_INVALID_PARAMETER;
  135. }
  136. //
  137. // Levels 0, 1, and 1101 are valid
  138. //
  139. if (Level > 1 && Level != 1101) {
  140. return ERROR_INVALID_LEVEL;
  141. }
  142. //
  143. // Impersonate caller and get the logon id
  144. //
  145. if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
  146. return status;
  147. }
  148. if ((status = WsGetUserInfo(
  149. &LogonId,
  150. Level,
  151. &UserInfoResponse,
  152. &DgrNames,
  153. &DgrNamesCount,
  154. &TotalBytesNeeded
  155. )) != NERR_Success) {
  156. return status;
  157. }
  158. if ((OutputBuffer = MIDL_user_allocate(TotalBytesNeeded)) == NULL) {
  159. status = ERROR_NOT_ENOUGH_MEMORY;
  160. goto FreeBuffers;
  161. }
  162. RtlZeroMemory((PVOID) OutputBuffer, TotalBytesNeeded);
  163. SET_USER_INFO_POINTER(UserInfo, OutputBuffer);
  164. //
  165. // Write the user information into output buffer.
  166. //
  167. FixedPortion = OutputBuffer;
  168. EndOfVariableData = (LPTSTR) ((DWORD_PTR) FixedPortion + TotalBytesNeeded);
  169. status = WsPackageUserInfo(
  170. Level,
  171. UserInfoFixedLength,
  172. UserInfoResponse,
  173. DgrNames,
  174. DgrNamesCount,
  175. &FixedPortion,
  176. &EndOfVariableData,
  177. NULL
  178. );
  179. NetpAssert(status == NERR_Success);
  180. FreeBuffers:
  181. if (UserInfoResponse != NULL) {
  182. (void) LsaFreeReturnBuffer((PVOID) UserInfoResponse);
  183. }
  184. if (DgrNames != NULL) {
  185. MIDL_user_free((PVOID) DgrNames);
  186. }
  187. return status;
  188. }
  189. NET_API_STATUS NET_API_FUNCTION
  190. NetrWkstaUserSetInfo(
  191. IN LPTSTR Reserved,
  192. IN DWORD Level,
  193. IN LPWKSTA_USER_INFO UserInfo,
  194. OUT LPDWORD ErrorParameter OPTIONAL
  195. )
  196. /*++
  197. Routine Description:
  198. This function is the NetWkstaUserSetInfo entry point in the Workstation
  199. service. It sets the other domains for the current user.
  200. Arguments:
  201. Reserved - Must be NULL.
  202. Level - Supplies the level of information.
  203. UserInfo - Supplies a pointer to union structure of pointers to
  204. buffer of fields to set. The level denotes the fields supplied in
  205. this buffer.
  206. ErrorParameter - Returns the identifier to the invalid parameter if
  207. this function returns ERROR_INVALID_PARAMETER.
  208. Return Value:
  209. NET_API_STATUS - NERR_Success or reason for failure.
  210. --*/
  211. {
  212. NET_API_STATUS status = NERR_Success;
  213. if (Reserved != NULL) {
  214. return ERROR_INVALID_PARAMETER;
  215. }
  216. //
  217. // Only admins can set redirector configurable fields. Validate access.
  218. //
  219. if (NetpAccessCheckAndAudit(
  220. WORKSTATION_DISPLAY_NAME, // Subsystem name
  221. (LPTSTR) CONFIG_INFO_OBJECT, // Object type name
  222. ConfigurationInfoSd, // Security descriptor
  223. WKSTA_CONFIG_INFO_SET, // Desired access
  224. &WsConfigInfoMapping // Generic mapping
  225. ) != NERR_Success) {
  226. return ERROR_ACCESS_DENIED;
  227. }
  228. //
  229. // Check for NULL input buffer
  230. //
  231. if (UserInfo->UserInfo1 == NULL) {
  232. RETURN_INVALID_PARAMETER(ErrorParameter, PARM_ERROR_UNKNOWN);
  233. }
  234. //
  235. // Serialize write access
  236. //
  237. if (! RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
  238. return NERR_InternalError;
  239. }
  240. switch (Level) {
  241. //
  242. // Other domains is the only settable field in the entire
  243. // system-wide info structure
  244. //
  245. case 1:
  246. case 1101:
  247. if ((status = WsSetOtherDomains(
  248. Level,
  249. (LPBYTE) UserInfo->UserInfo1
  250. )) == ERROR_INVALID_PARAMETER) {
  251. if (ARGUMENT_PRESENT(ErrorParameter)) {
  252. *ErrorParameter = WKSTA_OTH_DOMAINS_PARMNUM;
  253. }
  254. }
  255. break;
  256. default:
  257. status = ERROR_INVALID_LEVEL;
  258. }
  259. RtlReleaseResource(&WsInfo.ConfigResource);
  260. return status;
  261. }
  262. NET_API_STATUS NET_API_FUNCTION
  263. NetrWkstaUserEnum(
  264. IN LPTSTR ServerName OPTIONAL,
  265. IN OUT LPWKSTA_USER_ENUM_STRUCT UserInfo,
  266. IN DWORD PreferedMaximumLength,
  267. OUT LPDWORD TotalEntries,
  268. IN OUT LPDWORD ResumeHandle OPTIONAL
  269. )
  270. /*++
  271. Routine Description:
  272. This function is the NetWkstaUserEnum entry point in the Workstation
  273. service.
  274. Arguments:
  275. ServerName - Supplies the name of server to execute this function
  276. UserInfo - This structure supplies the level of information requested,
  277. returns a pointer to the buffer allocated by the Workstation service
  278. which contains a sequence of information structure of the specified
  279. information level, and returns the number of entries read. The buffer
  280. pointer is set to NULL if return code is not NERR_Success or
  281. ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
  282. value is only valid if the return code is NERR_Success or
  283. ERROR_MORE_DATA.
  284. PreferedMaximumLength - Supplies the number of bytes of information
  285. to return in the buffer. If this value is MAXULONG, all available
  286. information will be returned.
  287. TotalEntries - Returns the total number of entries available. This value
  288. is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
  289. ResumeHandle - Supplies a handle to resume the enumeration from where it
  290. left off the last time through. Returns the resume handle if return
  291. code is ERROR_MORE_DATA.
  292. Return Value:
  293. NET_API_STATUS - NERR_Success or reason for failure.
  294. --*/
  295. {
  296. NET_API_STATUS status;
  297. PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse = NULL;
  298. UNREFERENCED_PARAMETER(ServerName);
  299. //
  300. // Only levels 0 and 1
  301. //
  302. if (UserInfo->Level > 1) {
  303. return ERROR_INVALID_LEVEL;
  304. }
  305. if( UserInfo->WkstaUserInfo.Level1 == NULL ) {
  306. return ERROR_INVALID_PARAMETER;
  307. }
  308. //
  309. // Perform access validation on the caller.
  310. // Do the check always, not just when (WsLsaRestrictAnonymous > 0),
  311. // to prevent information disclosure and make it secure 'out of the box'.
  312. //
  313. if (NetpAccessCheckAndAudit(
  314. WORKSTATION_DISPLAY_NAME, // Subsystem name
  315. (LPTSTR) CONFIG_INFO_OBJECT, // Object type name
  316. ConfigurationInfoSd, // Security descriptor
  317. WKSTA_CONFIG_ADMIN_INFO_GET, // Desired access
  318. &WsConfigInfoMapping // Generic mapping
  319. ) != NERR_Success) {
  320. return ERROR_ACCESS_DENIED;
  321. }
  322. //
  323. // Ask authentication package to enumerate users who are physically
  324. // logged to the local machine.
  325. //
  326. if ((status = WsLsaEnumUsers(
  327. (LPBYTE *) &EnumUsersResponse
  328. )) != NERR_Success) {
  329. return status;
  330. }
  331. if (EnumUsersResponse == NULL) {
  332. return ERROR_GEN_FAILURE;
  333. }
  334. //
  335. // If no users are logged on, set appropriate fields and return success.
  336. //
  337. if (EnumUsersResponse->NumberOfLoggedOnUsers == 0) {
  338. UserInfo->WkstaUserInfo.Level1->Buffer = NULL;
  339. UserInfo->WkstaUserInfo.Level1->EntriesRead = 0;
  340. *TotalEntries = 0;
  341. status = NERR_Success;
  342. } else {
  343. status = WsEnumUserInfo(
  344. UserInfo->Level,
  345. PreferedMaximumLength,
  346. EnumUsersResponse,
  347. (LPBYTE *) &(UserInfo->WkstaUserInfo.Level1->Buffer),
  348. (LPDWORD) &(UserInfo->WkstaUserInfo.Level1->EntriesRead),
  349. TotalEntries,
  350. ResumeHandle
  351. );
  352. }
  353. (void) LsaFreeReturnBuffer((PVOID) EnumUsersResponse);
  354. return status;
  355. }
  356. STATIC
  357. NET_API_STATUS
  358. WsEnumUserInfo(
  359. IN DWORD Level,
  360. IN DWORD PreferedMaximumLength,
  361. IN PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse,
  362. OUT LPBYTE *OutputBuffer,
  363. OUT LPDWORD EntriesRead,
  364. OUT LPDWORD TotalEntries,
  365. IN OUT LPDWORD ResumeHandle OPTIONAL
  366. )
  367. /*++
  368. Routine Description:
  369. This function takes the logon IDs returned by MS V1.0 Authentication
  370. Package to call it again to get information about each user.
  371. Arguments:
  372. Level - Supplies the level of information to be returned.
  373. PreferedMaximumLength - Supplies the number of bytes of information
  374. to return in the buffer. If this value is MAXULONG, all available
  375. information will be returned.
  376. EnumUsersResponse - Supplies the structure returned from calling the MS
  377. V1.0 Authentication Package to enumerate logged on users.
  378. OutputBuffer - Returns a pointer to the enumerated user information.
  379. TotalEntries - Returns the total number of entries available. This value
  380. is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
  381. EntriesRead - Supplies a running total of the number of entries read
  382. into the output buffer. This value is incremented every time a
  383. user entry is successfully written into the output buffer.
  384. ResumeHandle - Returns the handle to continue with the enumeration if
  385. this function returns ERROR_MORE_DATA.
  386. Return Value:
  387. NET_API_STATUS - NERR_Success or reason for failure.
  388. --*/
  389. {
  390. NET_API_STATUS status = NERR_Success;
  391. //
  392. // Array of per user info entries, each entry contains a pointer
  393. // LSA info, and a pointer to active datagram receiver names.
  394. //
  395. PWSPER_USER_INFO UserInfoArray;
  396. DWORD UserInfoFixedLength = USER_FIXED_LENGTH(Level);
  397. DWORD i;
  398. DWORD OutputBufferLength = 0;
  399. DWORD UserEntriesRead = 0;
  400. DWORD OtherDomainsSize = 0;
  401. LPDWORD PointerToOutputBufferLength = &OutputBufferLength;
  402. LPBYTE FixedPortion;
  403. LPTSTR EndOfVariableData;
  404. DWORD StartEnumeration = 0;
  405. if (PreferedMaximumLength != MAXULONG) {
  406. //
  407. // We will return as much as possible that fits into this specified
  408. // buffer size.
  409. //
  410. OutputBufferLength =
  411. ROUND_UP_COUNT(PreferedMaximumLength, ALIGN_WCHAR);
  412. if (OutputBufferLength < UserInfoFixedLength) {
  413. *OutputBuffer = NULL;
  414. *EntriesRead = 0;
  415. *TotalEntries = EnumUsersResponse->NumberOfLoggedOnUsers;
  416. return ERROR_MORE_DATA;
  417. }
  418. //
  419. // This indicates that we should not bother calculating the
  420. // total output buffer size needed.
  421. //
  422. PointerToOutputBufferLength = NULL;
  423. }
  424. //
  425. // Allocate a temporary array to save pointers to user information
  426. // we retrieve from the LSA and datagram receiver. This is because we
  427. // need to go through the list of users twice: the first time to add
  428. // up the number of bytes to allocate for the output buffer; the second
  429. // time to write the user information into the output buffer.
  430. //
  431. if ((UserInfoArray = (PWSPER_USER_INFO) LocalAlloc(
  432. LMEM_ZEROINIT,
  433. EnumUsersResponse->NumberOfLoggedOnUsers *
  434. sizeof(WSPER_USER_INFO)
  435. )) == NULL) {
  436. return GetLastError();
  437. }
  438. //
  439. // Get the info for each user and calculate the amount of memory to
  440. // allocate for the output buffer if PointerToOutputBufferLength is
  441. // not set to NULL. If it was set to NULL, we will allocate the
  442. // output buffer size as specified by the caller.
  443. //
  444. for (i = 0; i < EnumUsersResponse->NumberOfLoggedOnUsers; i++) {
  445. if ((status = WsGetUserInfo(
  446. &(EnumUsersResponse->LogonIds[i]),
  447. Level,
  448. (PMSV1_0_GETUSERINFO_RESPONSE *) &(UserInfoArray[i].LsaUserInfo),
  449. (PDGRECEIVE_NAMES *) &UserInfoArray[i].DgrNames,
  450. (LPDWORD) &UserInfoArray[i].DgrNamesCount,
  451. PointerToOutputBufferLength
  452. )) != NERR_Success) {
  453. goto FreeBuffers;
  454. }
  455. }
  456. IF_DEBUG(INFO) {
  457. NetpKdPrint(("[Wksta] NetrWkstaUserEnum: OutputBufferLength=%lu\n",
  458. OutputBufferLength));
  459. }
  460. //
  461. // Allocate the output buffer
  462. //
  463. if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
  464. status = ERROR_NOT_ENOUGH_MEMORY;
  465. goto FreeBuffers;
  466. }
  467. RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
  468. FixedPortion = *OutputBuffer;
  469. EndOfVariableData = (LPTSTR) ((DWORD_PTR) FixedPortion + OutputBufferLength);
  470. //
  471. // Get the enumeration starting point.
  472. //
  473. if (ARGUMENT_PRESENT(ResumeHandle)) {
  474. StartEnumeration = *ResumeHandle;
  475. }
  476. //
  477. // Enumerate the user information
  478. //
  479. for (i = 0; i < EnumUsersResponse->NumberOfLoggedOnUsers &&
  480. status == NERR_Success; i++) {
  481. IF_DEBUG(INFO) {
  482. NetpKdPrint(("LsaList->ResumeKey=%lu\n",
  483. EnumUsersResponse->EnumHandles[i]));
  484. }
  485. if (StartEnumeration <= EnumUsersResponse->EnumHandles[i]) {
  486. status = WsPackageUserInfo(
  487. Level,
  488. UserInfoFixedLength,
  489. UserInfoArray[i].LsaUserInfo,
  490. UserInfoArray[i].DgrNames,
  491. UserInfoArray[i].DgrNamesCount,
  492. &FixedPortion,
  493. &EndOfVariableData,
  494. &UserEntriesRead
  495. );
  496. if (status == ERROR_MORE_DATA) {
  497. *TotalEntries = (EnumUsersResponse->NumberOfLoggedOnUsers
  498. - i) + UserEntriesRead;
  499. }
  500. }
  501. }
  502. //
  503. // Return entries read and total entries. We can only get NERR_Success
  504. // or ERROR_MORE_DATA from WsPackageUserInfo.
  505. //
  506. *EntriesRead = UserEntriesRead;
  507. if (status == NERR_Success) {
  508. *TotalEntries = UserEntriesRead;
  509. }
  510. if (status == ERROR_MORE_DATA && ARGUMENT_PRESENT(ResumeHandle)) {
  511. *ResumeHandle = EnumUsersResponse->EnumHandles[i - 1];
  512. }
  513. if (*EntriesRead == 0) {
  514. MIDL_user_free(*OutputBuffer);
  515. *OutputBuffer = NULL;
  516. }
  517. FreeBuffers:
  518. for (i = 0; i < EnumUsersResponse->NumberOfLoggedOnUsers; i++) {
  519. if (UserInfoArray[i].DgrNames != NULL) {
  520. MIDL_user_free((PVOID) UserInfoArray[i].DgrNames);
  521. }
  522. if (UserInfoArray[i].LsaUserInfo != NULL) {
  523. (void) LsaFreeReturnBuffer((PVOID) UserInfoArray[i].LsaUserInfo);
  524. }
  525. }
  526. (void) LocalFree((HLOCAL) UserInfoArray);
  527. return status;
  528. }
  529. STATIC
  530. NET_API_STATUS
  531. WsGetUserInfo(
  532. IN PLUID LogonId,
  533. IN DWORD Level,
  534. OUT PMSV1_0_GETUSERINFO_RESPONSE *UserInfoResponse,
  535. OUT PDGRECEIVE_NAMES *DgrNames,
  536. OUT LPDWORD DgrNamesCount,
  537. IN OUT LPDWORD TotalBytesNeeded OPTIONAL
  538. )
  539. /*++
  540. Routine Description:
  541. This function gets the other domains for the current user from
  542. the datagram receiver.
  543. Arguments:
  544. LogonId - Supplies a pointer to the user's Logon Id.
  545. Level - Supplies the level of information to be returned.
  546. UserInfoResponse - Returns a pointer to the user information from the
  547. authentication package.
  548. DgrNames - Returns a pointer an array of active datagram receiver
  549. names.
  550. DgrNamesCount - Returns the number of entries in DgrNames.
  551. TotalBytesNeeded - Returns the number of bytes required to in the
  552. output buffer for writing the other domains to.
  553. Return Value:
  554. NET_API_STATUS - NERR_Success or reason for failure.
  555. --*/
  556. {
  557. NET_API_STATUS status;
  558. ULONG UserInfoResponseLength;
  559. DWORD OtherDomainsSize = 0;
  560. //
  561. // Ask the datagram receiver for the other domains
  562. //
  563. if (Level == 1 || Level == 1101) {
  564. if ((status = WsGetActiveDgrNames(
  565. LogonId,
  566. DgrNames,
  567. DgrNamesCount,
  568. TotalBytesNeeded
  569. )) != NERR_Success) {
  570. return status;
  571. }
  572. }
  573. //
  574. // Don't get user info from authentication package if level
  575. // is 1101 since only other domains are returned in this level.
  576. //
  577. if (Level != 1101) {
  578. //
  579. // Ask authentication package for user information.
  580. //
  581. if ((status = WsLsaGetUserInfo(
  582. LogonId,
  583. (LPBYTE *) UserInfoResponse,
  584. &UserInfoResponseLength
  585. )) != NERR_Success) {
  586. MIDL_user_free((PVOID) *DgrNames);
  587. *DgrNames = NULL;
  588. *DgrNamesCount = 0;
  589. return status;
  590. }
  591. //
  592. // Calculate the amount of memory needed to hold the user information
  593. // and allocate the return buffer of that size.
  594. //
  595. if (ARGUMENT_PRESENT(TotalBytesNeeded)) {
  596. (*TotalBytesNeeded) +=
  597. FIXED_PLUS_LSA_SIZE(
  598. Level,
  599. (*UserInfoResponse)->UserName.Length +
  600. sizeof(TCHAR),
  601. (*UserInfoResponse)->LogonDomainName.Length +
  602. sizeof(TCHAR),
  603. (*UserInfoResponse)->LogonServer.Length +
  604. sizeof(TCHAR)
  605. );
  606. }
  607. }
  608. else {
  609. *TotalBytesNeeded += USER_FIXED_LENGTH(Level);
  610. }
  611. return NERR_Success;
  612. }
  613. STATIC
  614. NET_API_STATUS
  615. WsGetActiveDgrNames(
  616. IN PLUID LogonId,
  617. OUT PDGRECEIVE_NAMES *DgrNames,
  618. OUT LPDWORD DgrNamesCount,
  619. IN OUT LPDWORD TotalBytesNeeded OPTIONAL
  620. )
  621. /*++
  622. Routine Description:
  623. This function gets the other domains for the current user from
  624. the datagram receiver.
  625. Arguments:
  626. LogonId - Supplies a pointer to the user's Logon Id.
  627. DgrNames - Returns a pointer an array of active datagram receiver
  628. names.
  629. DgrNamesCount - Returns the number of entries in DgrNames.
  630. TotalBytesNeeded - Returns the number of bytes required to in the
  631. output buffer for writing the other domains to.
  632. Return Value:
  633. NET_API_STATUS - NERR_Success or reason for failure.
  634. --*/
  635. {
  636. NET_API_STATUS status;
  637. LMDR_REQUEST_PACKET Drp; // Datagram receiver request packet
  638. DWORD EnumDgNamesHintSize = 0;
  639. DWORD i;
  640. Drp.Version = LMDR_REQUEST_PACKET_VERSION;
  641. Drp.Type = EnumerateNames;
  642. RtlCopyLuid(&Drp.LogonId, LogonId);
  643. Drp.Parameters.EnumerateNames.ResumeHandle = 0;
  644. Drp.Parameters.EnumerateNames.TotalBytesNeeded = 0;
  645. //
  646. // Get the other domains from the datagram receiver.
  647. //
  648. if ((status = WsDeviceControlGetInfo(
  649. DatagramReceiver,
  650. WsDgReceiverDeviceHandle,
  651. IOCTL_LMDR_ENUMERATE_NAMES,
  652. (PVOID) &Drp,
  653. sizeof(LMDR_REQUEST_PACKET),
  654. (LPBYTE *) DgrNames,
  655. MAXULONG,
  656. EnumDgNamesHintSize,
  657. NULL
  658. )) != NERR_Success) {
  659. return status;
  660. }
  661. //
  662. // Include room for NULL character, in case there are no
  663. // other domains
  664. //
  665. if (ARGUMENT_PRESENT(TotalBytesNeeded)) {
  666. (*TotalBytesNeeded) += sizeof(TCHAR);
  667. }
  668. *DgrNamesCount = Drp.Parameters.EnumerateNames.EntriesRead;
  669. if (*DgrNamesCount == 0 && *DgrNames != NULL) {
  670. MIDL_user_free((PVOID) *DgrNames);
  671. *DgrNames = NULL;
  672. }
  673. //
  674. // Calculate the amount of memory to allocate for the output buffer
  675. //
  676. if (ARGUMENT_PRESENT(TotalBytesNeeded)) {
  677. for (i = 0; i < *DgrNamesCount; i++) {
  678. //
  679. // Add up the lengths of all the other domain names
  680. //
  681. if ((*DgrNames)[i].Type == OtherDomain) {
  682. (*TotalBytesNeeded) += (*DgrNames)[i].DGReceiverName.Length +
  683. sizeof(TCHAR);
  684. }
  685. }
  686. }
  687. return NERR_Success;
  688. }
  689. STATIC
  690. NET_API_STATUS
  691. WsSetOtherDomains(
  692. IN DWORD Level,
  693. IN LPBYTE Buffer
  694. )
  695. /*++
  696. Routine Description:
  697. This function sets the other domains for the current user in
  698. the datagram receiver.
  699. Arguments:
  700. Level - Supplies the level of information.
  701. Buffer - Supplies a buffer which contains the information structure
  702. if Parameter is WkstaSetAllParm. Otherwise Buffer contains the
  703. individual field to set.
  704. Return Value:
  705. NET_API_STATUS - NERR_Success or reason for failure.
  706. --*/
  707. {
  708. NET_API_STATUS status = NERR_Success;
  709. PCHAR DrpBuffer[sizeof(LMDR_REQUEST_PACKET) +
  710. DNLEN * sizeof(TCHAR)];
  711. PLMDR_REQUEST_PACKET Drp = (PLMDR_REQUEST_PACKET) DrpBuffer;
  712. DWORD DrpSize = sizeof(LMDR_REQUEST_PACKET) +
  713. DNLEN * sizeof(TCHAR);
  714. PDGRECEIVE_NAMES DgrNames;
  715. DWORD DgrNamesCount;
  716. DWORD EnumDgNamesHintSize = 0;
  717. DWORD NumberOfOtherDomains = 0;
  718. DWORD i, j, k;
  719. DWORD IndexToList = 0;
  720. PWSNAME_RECORD OtherDomainsInfo = NULL;
  721. LPTSTR OtherDomainsPointer;
  722. LPTSTR OtherDomains;
  723. LPTSTR CanonBuffer;
  724. DWORD CanonBufferSize;
  725. if (Level == 1101) {
  726. OtherDomains = ((PWKSTA_USER_INFO_1101) Buffer)->wkui1101_oth_domains;
  727. }
  728. else {
  729. OtherDomains = ((PWKSTA_USER_INFO_1) Buffer)->wkui1_oth_domains;
  730. }
  731. //
  732. // NULL pointer means leave the other domains unmodified
  733. //
  734. if (OtherDomains == NULL) {
  735. return NERR_Success;
  736. }
  737. IF_DEBUG(INFO) {
  738. NetpKdPrint(("WsSetOtherDomains: Input other domain is %ws\n", OtherDomains));
  739. }
  740. //
  741. // Before canonicalizing the input buffer, we have to find out how
  742. // many other domain entries are there so that we can supply a
  743. // buffer of the right size to the canonicalize routine.
  744. //
  745. OtherDomainsPointer = OtherDomains;
  746. while (*OtherDomainsPointer != TCHAR_EOS) {
  747. if (*(OtherDomainsPointer + 1) == WS_OTH_DOMAIN_DELIMITER_CHAR ||
  748. *(OtherDomainsPointer + 1) == TCHAR_EOS) {
  749. NumberOfOtherDomains++;
  750. }
  751. OtherDomainsPointer++;
  752. }
  753. //
  754. // Allocate the buffer to put the canonicalized other domain names
  755. //
  756. CanonBufferSize = NumberOfOtherDomains * (DNLEN + 1) * sizeof(TCHAR) +
  757. sizeof(TCHAR); // One more char for the NULL terminator
  758. if ((CanonBuffer = (LPTSTR) LocalAlloc(
  759. LMEM_ZEROINIT,
  760. (UINT) CanonBufferSize
  761. )) == NULL) {
  762. return GetLastError();
  763. }
  764. //
  765. // Canonicalize the input other domains separated by NULLs and put
  766. // into CanonBuffer, each other domain name separated by NULL, and
  767. // the buffer itself terminated by another NULL.
  768. //
  769. status = I_NetListCanonicalize(
  770. NULL,
  771. OtherDomains,
  772. WS_OTH_DOMAIN_DELIMITER_STR,
  773. CanonBuffer,
  774. CanonBufferSize,
  775. &NumberOfOtherDomains,
  776. NULL,
  777. 0,
  778. OUTLIST_TYPE_NULL_NULL |
  779. NAMETYPE_DOMAIN |
  780. INLC_FLAGS_CANONICALIZE
  781. );
  782. if (status != NERR_Success) {
  783. NetpKdPrint(("[Wksta] Error in canonicalizing other domains %lu",
  784. status));
  785. status = ERROR_INVALID_PARAMETER;
  786. goto FreeCanonBuffer;
  787. }
  788. //
  789. // Initialize datagram receiver packet to add or delete
  790. // other domains.
  791. //
  792. Drp->Version = LMDR_REQUEST_PACKET_VERSION;
  793. Drp->Type = EnumerateNames;
  794. Drp->Parameters.AddDelName.Type = OtherDomain;
  795. if ((status = WsImpersonateAndGetLogonId(
  796. &Drp->LogonId
  797. )) != NERR_Success) {
  798. goto FreeCanonBuffer;
  799. }
  800. //
  801. // Get all datagram receiver names from the datagram receiver.
  802. //
  803. if ((status = WsDeviceControlGetInfo(
  804. DatagramReceiver,
  805. WsDgReceiverDeviceHandle,
  806. IOCTL_LMDR_ENUMERATE_NAMES,
  807. (PVOID) Drp,
  808. DrpSize,
  809. (LPBYTE *) &DgrNames,
  810. MAXULONG,
  811. EnumDgNamesHintSize,
  812. NULL
  813. )) != NERR_Success) {
  814. goto FreeCanonBuffer;
  815. }
  816. DgrNamesCount = Drp->Parameters.EnumerateNames.EntriesRead;
  817. //
  818. // The other domains the user wants to set has to be merged with the
  819. // one that is maintained by the datagram receiver. We will attempt
  820. // to add all the other domains first. If it already exists, we ignore
  821. // the error. If it was added successfully, we mark it as such so that
  822. // if we run into an error other than the already exist error, we can
  823. // back out the ones we've already added.
  824. //
  825. // This requires that we allocate a structure to keep track of the ones
  826. // we've added.
  827. //
  828. if (NumberOfOtherDomains != 0) {
  829. if ((OtherDomainsInfo = (PWSNAME_RECORD) LocalAlloc(
  830. LMEM_ZEROINIT,
  831. (UINT) (NumberOfOtherDomains *
  832. sizeof(WSNAME_RECORD))
  833. )) == NULL) {
  834. status = GetLastError();
  835. goto FreeDgrNames;
  836. }
  837. //
  838. // Add all other domains specified. If any already exist, we ignore
  839. // the error from the datagram receiver.
  840. //
  841. OtherDomains = CanonBuffer;
  842. while ((OtherDomainsPointer = I_NetListTraverse(
  843. NULL,
  844. &OtherDomains,
  845. 0
  846. )) != NULL) {
  847. OtherDomainsInfo[IndexToList].Name = OtherDomainsPointer;
  848. OtherDomainsInfo[IndexToList].Size =
  849. STRLEN(OtherDomainsPointer) * sizeof(TCHAR);
  850. for (j = 0; j < DgrNamesCount; j++) {
  851. if (DgrNames[j].Type == OtherDomain) {
  852. if (WsCompareStringU(
  853. DgrNames[j].DGReceiverName.Buffer,
  854. DgrNames[j].DGReceiverName.Length / sizeof(TCHAR),
  855. OtherDomainsPointer,
  856. OtherDomainsInfo[IndexToList].Size / sizeof(TCHAR)
  857. ) == 0) {
  858. break;
  859. }
  860. }
  861. }
  862. //
  863. // User-specified domain does not already exist, so add it.
  864. //
  865. if (j == DgrNamesCount) {
  866. Drp->Parameters.AddDelName.Type = OtherDomain;
  867. status = WsAddDomainName(
  868. Drp,
  869. DrpSize,
  870. OtherDomainsPointer,
  871. OtherDomainsInfo[IndexToList].Size
  872. );
  873. if (status == NERR_Success) {
  874. OtherDomainsInfo[IndexToList].IsAdded = TRUE;
  875. IF_DEBUG(INFO) {
  876. NetpKdPrint((
  877. "[Wksta] Successfully added other domain %ws\n",
  878. OtherDomainsPointer
  879. ));
  880. }
  881. }
  882. else {
  883. //
  884. // We're ran into trouble and have to delete those
  885. // we've just added.
  886. //
  887. IF_DEBUG(INFO) {
  888. NetpKdPrint((
  889. "[Wksta] Trouble with adding other domain %ws %lu\n",
  890. OtherDomainsPointer, status
  891. ));
  892. }
  893. for (i = 0; i < IndexToList; i++) {
  894. if (OtherDomainsInfo[i].IsAdded) {
  895. IF_DEBUG(INFO) {
  896. NetpKdPrint(("[Wksta] About to remove %ws\n",
  897. OtherDomainsInfo[i].Name));
  898. }
  899. Drp->Parameters.AddDelName.Type = OtherDomain;
  900. (void) WsDeleteDomainName(
  901. Drp,
  902. DrpSize,
  903. OtherDomainsInfo[i].Name,
  904. OtherDomainsInfo[i].Size
  905. );
  906. }
  907. }
  908. goto FreeDomainInfo;
  909. } // back out added domains
  910. } // attempted to add a non-existing domain name
  911. IndexToList++;
  912. } // while there is a user-specified domain name
  913. } // if NumberOfOtherDomains != 0
  914. //
  915. // Now we need to delete an active domain name from the Datagram
  916. // Receiver if it is not in the input other domain list.
  917. //
  918. for (i = 0; i < DgrNamesCount; i++) {
  919. if (DgrNames[i].Type == OtherDomain) {
  920. for (j = 0; j < NumberOfOtherDomains; j++) {
  921. if (WsCompareStringU(
  922. DgrNames[i].DGReceiverName.Buffer,
  923. DgrNames[i].DGReceiverName.Length / sizeof(TCHAR),
  924. OtherDomainsInfo[j].Name,
  925. OtherDomainsInfo[j].Size / sizeof(TCHAR)
  926. ) == 0) {
  927. break;
  928. }
  929. }
  930. //
  931. // Did not find the active other domain name in the
  932. // input list. We have to delete it.
  933. //
  934. if (j == NumberOfOtherDomains) {
  935. Drp->Parameters.AddDelName.Type = OtherDomain;
  936. status = WsDeleteDomainName(
  937. Drp,
  938. DrpSize,
  939. DgrNames[i].DGReceiverName.Buffer,
  940. DgrNames[i].DGReceiverName.Length
  941. );
  942. //
  943. // Save the delete status of the other domain name away
  944. // because we might run into a problem and have to back
  945. // out the deletion later. What a mess!
  946. //
  947. if (status == NERR_Success) {
  948. IF_DEBUG(INFO) {
  949. NetpKdPrint((
  950. "[Wksta] Successfully deleted other domain\n"));
  951. //"[Wksta] Successfully deleted other domain %wZ\n",
  952. //DgrNames[i].DGReceiverName);
  953. }
  954. DgrNames[i].Type = DGR_NAME_DELETED;
  955. }
  956. else {
  957. //
  958. // Could not delete the name. Back all successful
  959. // changes so far--this includes adding the names
  960. // that were deleted, and removing the names that
  961. // were added.
  962. //
  963. IF_DEBUG(INFO) {
  964. NetpKdPrint((
  965. "[Wksta] Trouble with deleting other domain %lu\n",
  966. status));
  967. //"[Wksta] Trouble with deleting other domain %wZ %lu\n",
  968. //DgrNames[i].DGReceiverName, status);
  969. }
  970. //
  971. // Add back all deleted names
  972. //
  973. for (k = 0; k < i; k++) {
  974. if (DgrNames[k].Type == DGR_NAME_DELETED) {
  975. IF_DEBUG(INFO) {
  976. NetpKdPrint(("[Wksta] About to add back %wZ\n",
  977. DgrNames[k].DGReceiverName));
  978. }
  979. Drp->Parameters.AddDelName.Type = OtherDomain;
  980. (void) WsAddDomainName(
  981. Drp,
  982. DrpSize,
  983. DgrNames[k].DGReceiverName.Buffer,
  984. DgrNames[k].DGReceiverName.Length
  985. );
  986. }
  987. } // back out deletions
  988. //
  989. // Remove all added names
  990. //
  991. for (k = 0; k < NumberOfOtherDomains; k++) {
  992. if (OtherDomainsInfo[k].IsAdded) {
  993. IF_DEBUG(INFO) {
  994. NetpKdPrint(("[Wksta] About to remove %ws\n",
  995. OtherDomainsInfo[k].Name));
  996. }
  997. Drp->Parameters.AddDelName.Type = OtherDomain;
  998. (void) WsDeleteDomainName(
  999. Drp,
  1000. DrpSize,
  1001. OtherDomainsInfo[k].Name,
  1002. OtherDomainsInfo[k].Size
  1003. );
  1004. }
  1005. } // back out additions
  1006. goto FreeDomainInfo;
  1007. } // back out all changes so far
  1008. } // delete the active other domain
  1009. }
  1010. }
  1011. //
  1012. // Make other domains persistent by writing to the registry
  1013. //
  1014. if (status == NERR_Success) {
  1015. LPNET_CONFIG_HANDLE SectionHandle = NULL;
  1016. if (NetpOpenConfigData(
  1017. &SectionHandle,
  1018. NULL, // no server name
  1019. SECT_NT_WKSTA,
  1020. FALSE // not read-only
  1021. ) != NERR_Success) {
  1022. //
  1023. // Ignore the error if the config section couldn't be found.
  1024. //
  1025. goto FreeDomainInfo;
  1026. }
  1027. //
  1028. // Set value for OtherDomains keyword in the wksta section.
  1029. // This is a "NULL-NULL" array (which corresponds to REG_MULTI_SZ).
  1030. // Ignore error if not set properly in the registry.
  1031. //
  1032. (void) WsSetConfigTStrArray(
  1033. SectionHandle,
  1034. WKSTA_KEYWORD_OTHERDOMAINS,
  1035. CanonBuffer
  1036. );
  1037. (void) NetpCloseConfigData(SectionHandle);
  1038. }
  1039. FreeDomainInfo:
  1040. if (OtherDomainsInfo != NULL) {
  1041. (void) LocalFree((HLOCAL) OtherDomainsInfo);
  1042. }
  1043. FreeDgrNames:
  1044. MIDL_user_free((PVOID) DgrNames);
  1045. FreeCanonBuffer:
  1046. (void) LocalFree((HLOCAL) CanonBuffer);
  1047. IF_DEBUG(INFO) {
  1048. NetpKdPrint(("WsSetOtherDomains: about to return %lu\n", status));
  1049. }
  1050. return status;
  1051. }
  1052. STATIC
  1053. NET_API_STATUS
  1054. WsPackageUserInfo(
  1055. IN DWORD Level,
  1056. IN DWORD UserInfoFixedLength,
  1057. IN PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse,
  1058. IN PDGRECEIVE_NAMES DgrNames,
  1059. IN DWORD DgrNamesCount,
  1060. IN OUT LPBYTE *FixedPortion,
  1061. IN OUT LPTSTR *EndOfVariableData,
  1062. IN OUT LPDWORD EntriesRead OPTIONAL
  1063. )
  1064. /*++
  1065. Routine Description:
  1066. This function writes the user information from LSA and datagram
  1067. receiver into the output buffer. It increments the EntriesRead
  1068. variable when a user entry is written into the output buffer.
  1069. Arguments:
  1070. Level - Supplies the level of information to be returned.
  1071. UserInfoFixedLength - Supplies the length of the fixed portion of the
  1072. information structure.
  1073. UserInfoResponse - Supplies a pointer to the user information from the
  1074. authentication package.
  1075. DgrNames - Supplies an array of active datagram receiver names.
  1076. DgrNamesCount - Supplies the number of entries in DgrNames.
  1077. FixedPortion - Supplies a pointer to the output buffer where the next
  1078. entry of the fixed portion of the use information will be written.
  1079. This pointer is updated to point to the next fixed portion entry
  1080. after a user entry is written.
  1081. EndOfVariableData - Supplies a pointer just off the last available byte
  1082. in the output buffer. This is because the variable portion of the
  1083. user information is written into the output buffer starting from
  1084. the end.
  1085. This pointer is updated after any variable length information is
  1086. written to the output buffer.
  1087. EntriesRead - Supplies a running total of the number of entries read
  1088. into the output buffer. This value is incremented every time a
  1089. user entry is successfully written into the output buffer.
  1090. Return Value:
  1091. NERR_Success - The current entry fits into the output buffer.
  1092. ERROR_MORE_DATA - The current entry does not fit into the output buffer.
  1093. --*/
  1094. {
  1095. if (((DWORD_PTR) *FixedPortion + UserInfoFixedLength) >=
  1096. (DWORD_PTR) *EndOfVariableData) {
  1097. //
  1098. // Fixed length portion does not fit.
  1099. //
  1100. return ERROR_MORE_DATA;
  1101. }
  1102. if (! WsFillUserInfoBuffer(
  1103. Level,
  1104. UserInfoResponse,
  1105. DgrNames,
  1106. DgrNamesCount,
  1107. FixedPortion,
  1108. EndOfVariableData,
  1109. UserInfoFixedLength
  1110. )) {
  1111. //
  1112. // Variable length portion does not fit.
  1113. //
  1114. return ERROR_MORE_DATA;
  1115. }
  1116. if (ARGUMENT_PRESENT(EntriesRead)) {
  1117. (*EntriesRead)++;
  1118. }
  1119. return NERR_Success;
  1120. }
  1121. STATIC
  1122. BOOL
  1123. WsFillUserInfoBuffer(
  1124. IN DWORD Level,
  1125. IN PMSV1_0_GETUSERINFO_RESPONSE UserInfo,
  1126. IN PDGRECEIVE_NAMES DgrNames,
  1127. IN DWORD DgrNamesCount,
  1128. IN OUT LPBYTE *FixedPortion,
  1129. IN OUT LPTSTR *EndOfVariableData,
  1130. IN DWORD UserInfoFixedLength
  1131. )
  1132. /*++
  1133. Routine Description:
  1134. This function fills an entry in the output buffer with the supplied user
  1135. information.
  1136. NOTE: This function assumes that the fixed size portion will fit into
  1137. the output buffer.
  1138. It also assumes that info structure level 1 is a superset of info
  1139. structure level 0, and that the offset to each common field is
  1140. exactly the same. This allows us to take advantage of a switch
  1141. statement without a break between the levels.
  1142. Arguments:
  1143. Level - Supplies the level of information to be returned.
  1144. UserInfo - Supplies a pointer to the user information from the
  1145. authentication package.
  1146. DgrNames - Supplies an array of active datagram receiver names.
  1147. DgrNamesCount - Supplies the number of entries in DgrNames.
  1148. FixedPortion - Supplies a pointer to the output buffer where the next
  1149. entry of the fixed portion of the use information will be written.
  1150. This pointer is updated after a user entry is written to the
  1151. output buffer.
  1152. EndOfVariableData - Supplies a pointer just off the last available byte
  1153. in the output buffer. This is because the variable portion of the use
  1154. information is written into the output buffer starting from the end.
  1155. This pointer is updated after any variable length information is
  1156. written to the output buffer.
  1157. UserInfoFixedLength - Supplies the number of bytes needed to hold the
  1158. fixed size portion.
  1159. Return Value:
  1160. Returns TRUE if entire entry fits into output buffer, FALSE otherwise.
  1161. --*/
  1162. {
  1163. PWKSTA_USER_INFO_1 WkstaUserInfo = (PWKSTA_USER_INFO_1) *FixedPortion;
  1164. PWKSTA_USER_INFO_1101 UserInfo1101 = (PWKSTA_USER_INFO_1101) *FixedPortion;
  1165. *FixedPortion += UserInfoFixedLength;
  1166. switch (Level) {
  1167. case 1:
  1168. //
  1169. // Logon server from authentication package
  1170. //
  1171. if (! WsCopyStringToBuffer(
  1172. &UserInfo->LogonServer,
  1173. *FixedPortion,
  1174. EndOfVariableData,
  1175. (LPWSTR *) &WkstaUserInfo->wkui1_logon_server
  1176. )) {
  1177. return FALSE;
  1178. }
  1179. //
  1180. // Logon Domain from authentication package
  1181. //
  1182. if (! WsCopyStringToBuffer(
  1183. &UserInfo->LogonDomainName,
  1184. *FixedPortion,
  1185. EndOfVariableData,
  1186. (LPWSTR *) &WkstaUserInfo->wkui1_logon_domain
  1187. )) {
  1188. return FALSE;
  1189. }
  1190. WsWriteOtherDomains(
  1191. DgrNames,
  1192. DgrNamesCount,
  1193. FixedPortion,
  1194. EndOfVariableData,
  1195. UserInfoFixedLength,
  1196. (LPWSTR *) &WkstaUserInfo->wkui1_oth_domains
  1197. );
  1198. //
  1199. // Fall through because level 1 is a superset of level 0
  1200. //
  1201. case 0:
  1202. //
  1203. // User name from authentication package
  1204. //
  1205. if (! WsCopyStringToBuffer(
  1206. &UserInfo->UserName,
  1207. *FixedPortion,
  1208. EndOfVariableData,
  1209. (LPWSTR *) &WkstaUserInfo->wkui1_username
  1210. )) {
  1211. return FALSE;
  1212. }
  1213. break;
  1214. case 1101:
  1215. WsWriteOtherDomains(
  1216. DgrNames,
  1217. DgrNamesCount,
  1218. FixedPortion,
  1219. EndOfVariableData,
  1220. UserInfoFixedLength,
  1221. (LPWSTR *) &UserInfo1101->wkui1101_oth_domains
  1222. );
  1223. break;
  1224. default:
  1225. //
  1226. // This should never happen.
  1227. //
  1228. NetpKdPrint(("WsFillUserInfoBuffer: Invalid level %u.\n", Level));
  1229. NetpAssert(FALSE);
  1230. }
  1231. return TRUE;
  1232. }
  1233. STATIC
  1234. VOID
  1235. WsWriteOtherDomains(
  1236. IN PDGRECEIVE_NAMES DgrNames,
  1237. IN DWORD DgrNamesCount,
  1238. IN OUT LPBYTE *FixedPortion,
  1239. IN OUT LPTSTR *EndOfVariableData,
  1240. IN DWORD UserInfoFixedLength,
  1241. OUT LPWSTR *OtherDomainsPointer
  1242. )
  1243. /*++
  1244. Routine Description:
  1245. This function writes to the output buffer the other domains field.
  1246. Arguments:
  1247. DgrNames - Supplies an array of active datagram receiver names.
  1248. DgrNamesCount - Supplies the number of entries in DgrNames.
  1249. FixedPortion - Supplies a pointer to the output buffer where the next
  1250. entry of the fixed portion of the use information will be written.
  1251. This pointer is updated after a user entry is written to the
  1252. output buffer.
  1253. EndOfVariableData - Supplies a pointer just off the last available byte
  1254. in the output buffer. This is because the variable portion of the use
  1255. information is written into the output buffer starting from the end.
  1256. This pointer is updated after any variable length information is
  1257. written to the output buffer.
  1258. UserInfoFixedLength - Supplies the number of bytes needed to hold the
  1259. fixed size portion.
  1260. Return Value:
  1261. Returns TRUE if entire entry fits into output buffer, FALSE otherwise.
  1262. --*/
  1263. {
  1264. DWORD i;
  1265. DWORD OtherDomainsCount = 0;
  1266. //
  1267. // Other domain names form a NULL terminated string each
  1268. // separated by a space.
  1269. //
  1270. for (i = 0; i < DgrNamesCount; i++) {
  1271. if (DgrNames[i].Type == OtherDomain) {
  1272. WsCopyStringToBuffer(
  1273. &DgrNames[i].DGReceiverName,
  1274. *FixedPortion,
  1275. EndOfVariableData,
  1276. OtherDomainsPointer
  1277. );
  1278. OtherDomainsCount++;
  1279. if (OtherDomainsCount > 1) {
  1280. (*OtherDomainsPointer)[
  1281. STRLEN(*OtherDomainsPointer)
  1282. ] = WS_OTH_DOMAIN_DELIMITER_CHAR;
  1283. }
  1284. IF_DEBUG(INFO) {
  1285. NetpKdPrint(("[Wksta] Other domains is %ws\n",
  1286. *OtherDomainsPointer));
  1287. }
  1288. }
  1289. }
  1290. if (OtherDomainsCount == 0) {
  1291. NetpCopyStringToBuffer(
  1292. NULL,
  1293. 0,
  1294. *FixedPortion,
  1295. EndOfVariableData,
  1296. OtherDomainsPointer
  1297. );
  1298. }
  1299. }
  1300.