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.

813 lines
22 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. display.c
  5. Abstract:
  6. NetQueryDisplay Information and NetGetDisplayInformationIndex API functions
  7. Author:
  8. Cliff Van Dyke (cliffv) 14-Dec-1994
  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. --*/
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #undef DOMAIN_ALL_ACCESS // defined in both ntsam.h and ntwinapi.h
  19. #include <ntsam.h>
  20. #include <ntlsa.h>
  21. #include <windef.h>
  22. #include <winbase.h>
  23. #include <lmcons.h>
  24. #include <accessp.h>
  25. #include <align.h>
  26. // #include <lmapibuf.h>
  27. #include <lmaccess.h>
  28. #include <lmerr.h>
  29. // #include <limits.h>
  30. #include <netdebug.h>
  31. #include <netlib.h>
  32. #include <netlibnt.h>
  33. #include <rpcutil.h>
  34. // #include <rxuser.h>
  35. // #include <secobj.h>
  36. // #include <stddef.h>
  37. #include <uasp.h>
  38. VOID
  39. DisplayRelocationRoutine(
  40. IN DWORD Level,
  41. IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
  42. IN PTRDIFF_T Offset
  43. )
  44. /*++
  45. Routine Description:
  46. Routine to relocate the pointers from the fixed portion of a NetGroupEnum
  47. enumeration
  48. buffer to the string portion of an enumeration buffer. It is called
  49. as a callback routine from NetpAllocateEnumBuffer when it re-allocates
  50. such a buffer. NetpAllocateEnumBuffer copied the fixed portion and
  51. string portion into the new buffer before calling this routine.
  52. Arguments:
  53. Level - Level of information in the buffer.
  54. BufferDescriptor - Description of the new buffer.
  55. Offset - Offset to add to each pointer in the fixed portion.
  56. Return Value:
  57. Returns the error code for the operation.
  58. --*/
  59. {
  60. DWORD EntryCount;
  61. DWORD EntryNumber;
  62. DWORD FixedSize;
  63. IF_DEBUG( UAS_DEBUG_USER ) {
  64. NetpKdPrint(( "DisplayRelocationRoutine: entering\n" ));
  65. }
  66. //
  67. // Compute the number of fixed size entries
  68. //
  69. switch (Level) {
  70. case 1:
  71. FixedSize = sizeof(NET_DISPLAY_USER);
  72. break;
  73. case 2:
  74. FixedSize = sizeof(NET_DISPLAY_MACHINE);
  75. break;
  76. case 3:
  77. FixedSize = sizeof(NET_DISPLAY_GROUP);
  78. break;
  79. default:
  80. NetpAssert( FALSE );
  81. return;
  82. }
  83. EntryCount =
  84. ((DWORD)(BufferDescriptor->FixedDataEnd - BufferDescriptor->Buffer)) /
  85. FixedSize;
  86. //
  87. // Loop relocating each field in each fixed size structure
  88. //
  89. for ( EntryNumber=0; EntryNumber<EntryCount; EntryNumber++ ) {
  90. LPBYTE TheStruct = BufferDescriptor->Buffer + FixedSize * EntryNumber;
  91. switch (Level) {
  92. case 1:
  93. RELOCATE_ONE( ((PNET_DISPLAY_USER)TheStruct)->usri1_name, Offset );
  94. RELOCATE_ONE( ((PNET_DISPLAY_USER)TheStruct)->usri1_comment, Offset );
  95. RELOCATE_ONE( ((PNET_DISPLAY_USER)TheStruct)->usri1_full_name, Offset );
  96. break;
  97. case 2:
  98. RELOCATE_ONE( ((PNET_DISPLAY_MACHINE)TheStruct)->usri2_name, Offset );
  99. RELOCATE_ONE( ((PNET_DISPLAY_MACHINE)TheStruct)->usri2_comment, Offset );
  100. break;
  101. case 3:
  102. RELOCATE_ONE( ((PNET_DISPLAY_GROUP)TheStruct)->grpi3_name, Offset );
  103. RELOCATE_ONE( ((PNET_DISPLAY_GROUP)TheStruct)->grpi3_comment, Offset );
  104. break;
  105. default:
  106. return;
  107. }
  108. }
  109. return;
  110. } // DisplayRelocationRoutine
  111. NET_API_STATUS NET_API_FUNCTION
  112. NetQueryDisplayInformation(
  113. IN LPCWSTR ServerName OPTIONAL,
  114. IN DWORD Level,
  115. IN DWORD Index,
  116. IN DWORD EntriesRequested,
  117. IN DWORD PreferredMaximumLength,
  118. OUT LPDWORD ReturnedEntryCount,
  119. OUT PVOID *SortedBuffer )
  120. /*++
  121. Routine Description:
  122. This routine provides fast return of information commonly
  123. needed to be displayed in user interfaces.
  124. NT User Interface has a requirement for quick enumeration of
  125. accounts for display in list boxes.
  126. This API is remotable. The Server can be any NT workstation or server.
  127. The server cannot be a Lanman or WFW machine.
  128. NT 3.1 workstations and servers do not support Level 3.
  129. NT 3.1 workstations and servers do not support an infinitely large
  130. EntriesRequested.
  131. Parameters:
  132. ServerName - A pointer to a string containing the name of the remote
  133. server on which the function is to execute. A NULL pointer
  134. or string specifies the local machine.
  135. Level - Level of information provided. Must be
  136. 1 --> Return all Local and Global (normal) user accounts
  137. 2 --> Return all Workstation and Server (BDC) user accounts
  138. 3 --> Return all Global Groups.
  139. Index - The index of the first entry to be retrieved. Pass zero on
  140. the first call. Pass the 'next_index' field of the last entry
  141. returned on the previous call.
  142. EntriesRequested - Specifies an upper limit on the number of entries
  143. to be returned .
  144. PreferedMaximumLength - A recommended upper limit to the number of
  145. bytes to be returned. The returned information is allocated by
  146. this routine.
  147. ReturnedEntryCount - Number of entries returned by this call. Zero
  148. indicates there are no entries with an index as large as that
  149. specified.
  150. Entries may be returned for a return status of either NERR_Success
  151. or ERROR_MORE_DATA.
  152. SortedBuffer - Receives a pointer to a buffer containing a sorted
  153. list of the requested information. This buffer is allocated
  154. by this routine and contains the following structure:
  155. 1 --> An array of ReturnedEntryCount elements of type
  156. NET_DISPLAY_USER. This is followed by the bodies of the
  157. various strings pointed to from within the
  158. NET_DISPLAY_USER structures.
  159. 2 --> An array of ReturnedEntryCount elements of type
  160. NET_DISPLAY_MACHINE. This is followed by the bodies of the
  161. various strings pointed to from within the
  162. NET_DISPLAY_MACHINE structures.
  163. 3 --> An array of ReturnedEntryCount elements of type
  164. NET_DISPLAY_GROUP. This is followed by the bodies of the
  165. various strings pointed to from within the
  166. NET_DISPLAY_GROUP structures.
  167. Return Values:
  168. NERR_Success - normal, successful completion. There are no more entries
  169. to be returned.
  170. ERROR_ACCESS_DENIED - The caller doesn't have access to the requested
  171. information.
  172. ERROR_INVALID_LEVEL - The requested level of information
  173. is not legitimate for this service.
  174. ERROR_MORE_DATA - More entries are available. That is, the last entry
  175. returned in SortedBuffer is not the last entry available. More
  176. entries will be returned by calling again with the Index parameter
  177. set to the 'next_index' field of the last entry in SortedBuffer.
  178. --*/
  179. {
  180. NET_API_STATUS NetStatus;
  181. NTSTATUS Status;
  182. NET_API_STATUS SavedStatus;
  183. BUFFER_DESCRIPTOR BufferDescriptor;
  184. DWORD i;
  185. SAM_HANDLE SamServerHandle = NULL;
  186. SAM_HANDLE DomainHandle = NULL;
  187. DOMAIN_DISPLAY_INFORMATION DisplayInformation;
  188. DWORD FixedSize;
  189. LPBYTE FixedData;
  190. DWORD SamTotalBytesAvailable;
  191. DWORD SamTotalBytesReturned;
  192. DWORD SamReturnedEntryCount;
  193. PVOID SamSortedBuffer = NULL;
  194. DWORD Mode = SAM_SID_COMPATIBILITY_ALL;
  195. //
  196. // Validate Level parameter
  197. //
  198. *ReturnedEntryCount = 0;
  199. *SortedBuffer = NULL;
  200. BufferDescriptor.Buffer = NULL;
  201. switch (Level) {
  202. case 1:
  203. DisplayInformation = DomainDisplayUser;
  204. FixedSize = sizeof(NET_DISPLAY_USER);
  205. break;
  206. case 2:
  207. DisplayInformation = DomainDisplayMachine;
  208. FixedSize = sizeof(NET_DISPLAY_MACHINE);
  209. break;
  210. case 3:
  211. DisplayInformation = DomainDisplayGroup;
  212. FixedSize = sizeof(NET_DISPLAY_GROUP);
  213. break;
  214. default:
  215. return ERROR_INVALID_LEVEL;
  216. }
  217. //
  218. // Connect to the SAM server
  219. //
  220. NetStatus = UaspOpenSam( ServerName,
  221. FALSE, // Don't try null session
  222. &SamServerHandle );
  223. if ( NetStatus != NERR_Success ) {
  224. IF_DEBUG( UAS_DEBUG_USER ) {
  225. NetpKdPrint(( "NetQueryDisplayInformation: Cannot UaspOpenSam %ld\n", NetStatus ));
  226. }
  227. goto Cleanup;
  228. }
  229. //
  230. // Open the Account Domain.
  231. //
  232. NetStatus = UaspOpenDomain( SamServerHandle,
  233. DOMAIN_LIST_ACCOUNTS,
  234. TRUE, // Account Domain
  235. &DomainHandle,
  236. NULL );
  237. if ( NetStatus != NERR_Success ) {
  238. goto Cleanup;
  239. }
  240. Status = SamGetCompatibilityMode(DomainHandle,
  241. &Mode);
  242. if (NT_SUCCESS(Status)) {
  243. if ( (Mode == SAM_SID_COMPATIBILITY_STRICT)) {
  244. //
  245. // All these info levels return RID's
  246. //
  247. Status = STATUS_NOT_SUPPORTED;
  248. }
  249. }
  250. if (!NT_SUCCESS(Status)) {
  251. NetStatus = NetpNtStatusToApiStatus( Status );
  252. goto Cleanup;
  253. }
  254. //
  255. // Pass the call to SAM
  256. //
  257. Status = SamQueryDisplayInformation (
  258. DomainHandle,
  259. DisplayInformation,
  260. Index,
  261. EntriesRequested,
  262. PreferredMaximumLength,
  263. &SamTotalBytesAvailable,
  264. &SamTotalBytesReturned,
  265. &SamReturnedEntryCount,
  266. &SamSortedBuffer );
  267. if ( !NT_SUCCESS( Status ) ) {
  268. SamSortedBuffer = NULL;
  269. IF_DEBUG( UAS_DEBUG_USER ) {
  270. NetpKdPrint((
  271. "NetQueryDisplayInformation: SamQueryDisplayInformation returned %lX\n",
  272. Status ));
  273. }
  274. //
  275. // NT 3.1 systems returned STATUS_NO_MORE_ENTRIES if Index is too large.
  276. //
  277. if ( Status == STATUS_NO_MORE_ENTRIES ) {
  278. NetStatus = NERR_Success;
  279. goto Cleanup;
  280. }
  281. NetStatus = NetpNtStatusToApiStatus( Status );
  282. goto Cleanup;
  283. }
  284. //
  285. // Remember what status to return on success.
  286. //
  287. if ( Status == STATUS_MORE_ENTRIES ) {
  288. SavedStatus = ERROR_MORE_DATA;
  289. } else {
  290. SavedStatus = NERR_Success;
  291. }
  292. //
  293. // Loop for each entry
  294. //
  295. for ( i = 0; i < SamReturnedEntryCount; i++ ) {
  296. PDOMAIN_DISPLAY_USER DomainDisplayUser;
  297. PDOMAIN_DISPLAY_MACHINE DomainDisplayMachine;
  298. PDOMAIN_DISPLAY_GROUP DomainDisplayGroup;
  299. DWORD Size;
  300. //
  301. // Determine the total size of the return information.
  302. //
  303. Size = FixedSize;
  304. switch (Level) {
  305. case 1:
  306. DomainDisplayUser = &((PDOMAIN_DISPLAY_USER)(SamSortedBuffer))[i];
  307. Size += DomainDisplayUser->LogonName.Length + sizeof(WCHAR) +
  308. DomainDisplayUser->AdminComment.Length + sizeof(WCHAR) +
  309. DomainDisplayUser->FullName.Length + sizeof(WCHAR);
  310. break;
  311. case 2:
  312. DomainDisplayMachine = &((PDOMAIN_DISPLAY_MACHINE)(SamSortedBuffer))[i];
  313. Size += DomainDisplayMachine->Machine.Length + sizeof(WCHAR) +
  314. DomainDisplayMachine->Comment.Length + sizeof(WCHAR);
  315. break;
  316. case 3:
  317. DomainDisplayGroup = &((PDOMAIN_DISPLAY_GROUP)(SamSortedBuffer))[i];
  318. Size += DomainDisplayGroup->Group.Length + sizeof(WCHAR) +
  319. DomainDisplayGroup->Comment.Length + sizeof(WCHAR);
  320. break;
  321. default:
  322. NetStatus = ERROR_INVALID_LEVEL;
  323. goto Cleanup;
  324. }
  325. //
  326. // Ensure there is buffer space for this information.
  327. //
  328. Size = ROUND_UP_COUNT( Size, ALIGN_WCHAR );
  329. NetStatus = NetpAllocateEnumBuffer(
  330. &BufferDescriptor,
  331. FALSE, // Enumeration
  332. 0xFFFFFFFF, // PrefMaxLen (already limited by SAM)
  333. Size,
  334. DisplayRelocationRoutine,
  335. Level );
  336. if (NetStatus != NERR_Success) {
  337. //
  338. // NetpAllocateEnumBuffer returns ERROR_MORE_DATA if this
  339. // entry doesn't fit into the buffer.
  340. //
  341. if ( NetStatus == ERROR_MORE_DATA ) {
  342. NetStatus = NERR_InternalError;
  343. }
  344. IF_DEBUG( UAS_DEBUG_USER ) {
  345. NetpKdPrint(( "NetQueryDisplayInformation: NetpAllocateEnumBuffer returns %ld\n",
  346. NetStatus ));
  347. }
  348. goto Cleanup;
  349. }
  350. //
  351. // Define macros to make copying zero terminated strings less repetitive.
  352. //
  353. #define COPY_STRING( _dest, _string ) \
  354. if ( !NetpCopyStringToBuffer( \
  355. (_string).Buffer, \
  356. (_string).Length/sizeof(WCHAR), \
  357. BufferDescriptor.FixedDataEnd, \
  358. (LPWSTR *)&BufferDescriptor.EndOfVariableData, \
  359. (_dest) )) { \
  360. \
  361. NetStatus = NERR_InternalError; \
  362. IF_DEBUG( UAS_DEBUG_USER ) { \
  363. NetpKdPrint(( "NetQueryDisplayInformation: NetpCopyString returns %ld\n", \
  364. NetStatus )); \
  365. } \
  366. goto Cleanup; \
  367. }
  368. //
  369. // Place this entry into the return buffer.
  370. //
  371. // Fill in the information. The array of fixed entries are
  372. // placed at the beginning of the allocated buffer. The strings
  373. // pointed to by these fixed entries are allocated starting at
  374. // the end of the allocated buffer.
  375. //
  376. FixedData = BufferDescriptor.FixedDataEnd;
  377. BufferDescriptor.FixedDataEnd += FixedSize;
  378. switch (Level) {
  379. case 1: {
  380. PNET_DISPLAY_USER NetDisplayUser = (PNET_DISPLAY_USER)FixedData;
  381. COPY_STRING( &NetDisplayUser->usri1_name,
  382. DomainDisplayUser->LogonName );
  383. COPY_STRING( &NetDisplayUser->usri1_comment,
  384. DomainDisplayUser->AdminComment );
  385. NetDisplayUser->usri1_flags = NetpAccountControlToFlags(
  386. DomainDisplayUser->AccountControl,
  387. NULL );
  388. COPY_STRING( &NetDisplayUser->usri1_full_name,
  389. DomainDisplayUser->FullName );
  390. if (Mode == SAM_SID_COMPATIBILITY_ALL) {
  391. NetDisplayUser->usri1_user_id = DomainDisplayUser->Rid;
  392. } else {
  393. NetDisplayUser->usri1_user_id = 0;
  394. }
  395. NetDisplayUser->usri1_next_index = DomainDisplayUser->Index;
  396. break;
  397. }
  398. case 2: {
  399. PNET_DISPLAY_MACHINE NetDisplayMachine = (PNET_DISPLAY_MACHINE)FixedData;
  400. COPY_STRING( &NetDisplayMachine->usri2_name,
  401. DomainDisplayMachine->Machine );
  402. COPY_STRING( &NetDisplayMachine->usri2_comment,
  403. DomainDisplayMachine->Comment );
  404. NetDisplayMachine->usri2_flags = NetpAccountControlToFlags(
  405. DomainDisplayMachine->AccountControl,
  406. NULL );
  407. if (Mode == SAM_SID_COMPATIBILITY_ALL) {
  408. NetDisplayMachine->usri2_user_id = DomainDisplayMachine->Rid;
  409. } else {
  410. NetDisplayMachine->usri2_user_id = 0;
  411. }
  412. NetDisplayMachine->usri2_next_index = DomainDisplayMachine->Index;
  413. break;
  414. }
  415. case 3: {
  416. PNET_DISPLAY_GROUP NetDisplayGroup = (PNET_DISPLAY_GROUP)FixedData;
  417. COPY_STRING( &NetDisplayGroup->grpi3_name,
  418. DomainDisplayGroup->Group );
  419. COPY_STRING( &NetDisplayGroup->grpi3_comment,
  420. DomainDisplayGroup->Comment );
  421. if (Mode == SAM_SID_COMPATIBILITY_ALL) {
  422. NetDisplayGroup->grpi3_group_id = DomainDisplayGroup->Rid;
  423. } else {
  424. NetDisplayGroup->grpi3_group_id = 0;
  425. }
  426. NetDisplayGroup->grpi3_attributes = DomainDisplayGroup->Attributes;
  427. NetDisplayGroup->grpi3_next_index = DomainDisplayGroup->Index;
  428. break;
  429. }
  430. default:
  431. NetStatus = ERROR_INVALID_LEVEL;
  432. goto Cleanup;
  433. }
  434. //
  435. // Indicate that more information was returned.
  436. //
  437. (*ReturnedEntryCount) ++;
  438. }
  439. NetStatus = SavedStatus;
  440. //
  441. // Clean up.
  442. //
  443. Cleanup:
  444. //
  445. // Free up all resources, we reopen them if the caller calls again.
  446. //
  447. if ( DomainHandle != NULL ) {
  448. UaspCloseDomain( DomainHandle );
  449. }
  450. if ( SamServerHandle != NULL ) {
  451. (VOID) SamCloseHandle( SamServerHandle );
  452. }
  453. //
  454. // If we're not returning data to the caller,
  455. // free the return buffer.
  456. //
  457. if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
  458. if ( BufferDescriptor.Buffer != NULL ) {
  459. MIDL_user_free( BufferDescriptor.Buffer );
  460. BufferDescriptor.Buffer = NULL;
  461. }
  462. }
  463. //
  464. // Free buffer returned from SAM.
  465. //
  466. if ( SamSortedBuffer != NULL ) {
  467. (VOID) SamFreeMemory( SamSortedBuffer );
  468. }
  469. //
  470. // Set the output parameters
  471. //
  472. *SortedBuffer = BufferDescriptor.Buffer;
  473. IF_DEBUG( UAS_DEBUG_USER ) {
  474. NetpKdPrint(( "NetQueryDisplayInformation: returning %ld\n", NetStatus ));
  475. }
  476. return NetStatus;
  477. }
  478. NET_API_STATUS NET_API_FUNCTION
  479. NetGetDisplayInformationIndex(
  480. IN LPCWSTR ServerName OPTIONAL,
  481. IN DWORD Level,
  482. IN LPCWSTR Prefix,
  483. OUT LPDWORD Index )
  484. /*++
  485. Routine Description:
  486. This routine returns the index of the first display information entry
  487. alphabetically equal to or following Prefix.
  488. Parameters:
  489. ServerName - A pointer to a string containing the name of the remote
  490. server on which the function is to execute. A NULL pointer
  491. or string specifies the local machine.
  492. Level - Level of information queried. Must be
  493. 1 --> all Local and Global (normal) user accounts
  494. 2 --> all Workstation and Server (BDC) user accounts
  495. 3 --> all Global Groups.
  496. Prefix - Prefix to be searched for
  497. Index - The index of the entry found.
  498. Return Values:
  499. NERR_Success - normal, successful completion. The specified index
  500. was returned.
  501. ERROR_ACCESS_DENIED - The caller doesn't have access to the requested
  502. information.
  503. ERROR_INVALID_LEVEL - The requested level of information
  504. is not legitimate for this service.
  505. --*/
  506. {
  507. NET_API_STATUS NetStatus;
  508. NTSTATUS Status;
  509. SAM_HANDLE SamServerHandle = NULL;
  510. SAM_HANDLE DomainHandle = NULL;
  511. DOMAIN_DISPLAY_INFORMATION DisplayInformation;
  512. UNICODE_STRING PrefixString;
  513. DWORD Mode = SAM_SID_COMPATIBILITY_ALL;
  514. //
  515. // Validate Level parameter
  516. //
  517. switch (Level) {
  518. case 1:
  519. DisplayInformation = DomainDisplayUser;
  520. break;
  521. case 2:
  522. DisplayInformation = DomainDisplayMachine;
  523. break;
  524. case 3:
  525. DisplayInformation = DomainDisplayGroup;
  526. break;
  527. default:
  528. return ERROR_INVALID_LEVEL;
  529. }
  530. //
  531. // Connect to the SAM server
  532. //
  533. NetStatus = UaspOpenSam( ServerName,
  534. FALSE, // Don't try null session
  535. &SamServerHandle );
  536. if ( NetStatus != NERR_Success ) {
  537. IF_DEBUG( UAS_DEBUG_USER ) {
  538. NetpKdPrint(( "NetGetDisplayInformationIndex: Cannot UaspOpenSam %ld\n", NetStatus ));
  539. }
  540. goto Cleanup;
  541. }
  542. //
  543. // Open the Account Domain.
  544. //
  545. NetStatus = UaspOpenDomain( SamServerHandle,
  546. DOMAIN_LIST_ACCOUNTS,
  547. TRUE, // Account Domain
  548. &DomainHandle,
  549. NULL );
  550. if ( NetStatus != NERR_Success ) {
  551. goto Cleanup;
  552. }
  553. Status = SamGetCompatibilityMode(DomainHandle,
  554. &Mode);
  555. if (NT_SUCCESS(Status)) {
  556. if ( (Mode == SAM_SID_COMPATIBILITY_STRICT)) {
  557. //
  558. // All these info levels return RID's
  559. //
  560. Status = STATUS_NOT_SUPPORTED;
  561. }
  562. }
  563. if (!NT_SUCCESS(Status)) {
  564. NetStatus = NetpNtStatusToApiStatus( Status );
  565. goto Cleanup;
  566. }
  567. //
  568. // Pass the call to SAM
  569. //
  570. RtlInitUnicodeString( &PrefixString, Prefix );
  571. Status = SamGetDisplayEnumerationIndex (
  572. DomainHandle,
  573. DisplayInformation,
  574. &PrefixString,
  575. Index );
  576. if ( !NT_SUCCESS( Status ) ) {
  577. IF_DEBUG( UAS_DEBUG_USER ) {
  578. NetpKdPrint((
  579. "NetGetDisplayInformationIndex: SamGetDisplayEnumerationIndex returned %lX\n",
  580. Status ));
  581. }
  582. NetStatus = NetpNtStatusToApiStatus( Status );
  583. goto Cleanup;
  584. }
  585. Status = NERR_Success;
  586. //
  587. // Clean up.
  588. //
  589. Cleanup:
  590. //
  591. // Free up all resources, we reopen them if the caller calls again.
  592. //
  593. if ( DomainHandle != NULL ) {
  594. UaspCloseDomain( DomainHandle );
  595. }
  596. if ( SamServerHandle != NULL ) {
  597. (VOID) SamCloseHandle( SamServerHandle );
  598. }
  599. //
  600. // Set the output parameters
  601. //
  602. IF_DEBUG( UAS_DEBUG_USER ) {
  603. NetpKdPrint(( "NetGetDisplayInformationIndex: returning %ld\n", NetStatus ));
  604. }
  605. return NetStatus;
  606. }