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.

1773 lines
42 KiB

  1. /*++
  2. Copyright (c) 1991-1992 Microsoft Corporation
  3. Module Name:
  4. wsmain.c
  5. Abstract:
  6. This is the main routine for the NT LAN Manager Workstation service.
  7. Author:
  8. Rita Wong (ritaw) 06-May-1991
  9. Environment:
  10. User Mode - Win32
  11. Revision History:
  12. 15-May-1992 JohnRo
  13. Implement registry watch.
  14. 11-Jun-1992 JohnRo
  15. Ifdef-out winreg notify stuff until we can fix logoff problem.
  16. Added assertion checks on registry watch stuff.
  17. 18-Oct-1993 terryk
  18. Removed WsInitializeLogon stuff
  19. 20-Oct-1993 terryk
  20. Remove WsInitializeMessage stuff
  21. --*/
  22. #include "wsutil.h" // Common routines and data
  23. #include "wssec.h" // WkstaObjects create & destroy
  24. #include "wsdevice.h" // Device init & shutdown
  25. #include "wsuse.h" // UseStructures create & destroy
  26. #include "wsconfig.h" // Configuration loading
  27. #include "wslsa.h" // Lsa initialization
  28. #include "wsmsg.h" // Message send initialization
  29. #include "wswksta.h" // WsUpdateRedirToMatchWksta
  30. #include "wsmain.h" // Service related global definitions
  31. #include "wsdfs.h" // Dfs related routines
  32. #include <lmserver.h> // SV_TYPE_WORKSTATION
  33. #include <srvann.h> // I_ScSetServiceBits
  34. #include <configp.h> // Need NET_CONFIG_HANDLE typedef
  35. #include <confname.h> // NetpAllocConfigName().
  36. #include <prefix.h> // PREFIX_ equates.
  37. #ifdef WS_SET_TIME
  38. #include <lmremutl.h> // NetRemoteTOD
  39. #endif
  40. //-------------------------------------------------------------------//
  41. // //
  42. // String Definitions //
  43. // //
  44. //-------------------------------------------------------------------//
  45. #ifdef WS_SET_TIME
  46. #define CURRENT_CTRL_SET TEXT("system\\CurrentControlSet")
  47. #define WKSTA_KEY TEXT("Services\\LanmanWorkstation\\Parameters")
  48. #define SET_TIME_VALUE_NAME TEXT("SetTime")
  49. #endif
  50. //-------------------------------------------------------------------//
  51. // //
  52. // Structures
  53. // //
  54. //-------------------------------------------------------------------//
  55. typedef struct _REG_NOTIFY_INFO {
  56. HANDLE NotifyEventHandle;
  57. DWORD Timeout;
  58. HANDLE WorkItemHandle;
  59. HANDLE RegistryHandle;
  60. } REG_NOTIFY_INFO, *PREG_NOTIFY_INFO, *LPREG_NOTIFY_INFO;
  61. //-------------------------------------------------------------------//
  62. // //
  63. // Global variables //
  64. // //
  65. //-------------------------------------------------------------------//
  66. WS_GLOBAL_DATA WsGlobalData;
  67. PSVCHOST_GLOBAL_DATA WsLmsvcsGlobalData;
  68. REG_NOTIFY_INFO RegNotifyInfo = {0};
  69. HANDLE TerminateWorkItem = NULL;
  70. CRITICAL_SECTION WsWorkerCriticalSection;
  71. BOOL WsIsTerminating=FALSE;
  72. BOOL WsLUIDDeviceMapsEnabled=FALSE;
  73. DWORD WsNumWorkerThreads=0;
  74. // Used by the termination routine:
  75. BOOL ConfigHandleOpened = FALSE;
  76. HKEY ConfigHandle;
  77. HANDLE RegistryChangeEvent = NULL;
  78. LPTSTR RegPathToWatch = NULL;
  79. DWORD WsInitState = 0;
  80. //-------------------------------------------------------------------//
  81. // //
  82. // Function prototypes //
  83. // //
  84. //-------------------------------------------------------------------//
  85. STATIC
  86. NET_API_STATUS
  87. WsInitializeWorkstation(
  88. OUT LPDWORD WsInitState
  89. );
  90. STATIC
  91. VOID
  92. WsShutdownWorkstation(
  93. IN NET_API_STATUS ErrorCode,
  94. IN DWORD WsInitState
  95. );
  96. STATIC
  97. VOID
  98. WsHandleError(
  99. IN WS_ERROR_CONDITION FailingCondition,
  100. IN NET_API_STATUS Status,
  101. IN DWORD WsInitState
  102. );
  103. STATIC
  104. NET_API_STATUS
  105. WsCreateApiStructures(
  106. IN OUT LPDWORD WsInitState
  107. );
  108. STATIC
  109. VOID
  110. WsDestroyApiStructures(
  111. IN DWORD WsInitState
  112. );
  113. VOID
  114. WkstaControlHandler(
  115. IN DWORD Opcode
  116. );
  117. VOID
  118. WsInitChangeNotify(
  119. PVOID pData);
  120. BOOL
  121. WsReInitChangeNotify(
  122. PREG_NOTIFY_INFO pNotifyInfo
  123. );
  124. DWORD
  125. WsRegistryNotify(
  126. LPVOID pParms,
  127. BOOLEAN fWaitStatus
  128. );
  129. VOID
  130. WsTerminationNotify(
  131. LPVOID pParms,
  132. BOOLEAN fWaitStatus
  133. );
  134. #ifdef WS_SET_TIME
  135. STATIC
  136. VOID
  137. WsSetTime(
  138. VOID
  139. );
  140. STATIC
  141. DWORD
  142. WsFindTimeServer(
  143. LPTSTR *pServerName
  144. );
  145. STATIC
  146. BOOL
  147. WsShouldSetTime(
  148. VOID
  149. );
  150. #endif
  151. STATIC
  152. BOOL
  153. WsGetLUIDDeviceMapsEnabled(
  154. VOID
  155. );
  156. VOID
  157. SvchostPushServiceGlobals(
  158. PSVCHOST_GLOBAL_DATA pGlobals
  159. )
  160. {
  161. WsLmsvcsGlobalData = pGlobals;
  162. }
  163. VOID
  164. ServiceMain(
  165. DWORD NumArgs,
  166. LPTSTR *ArgsArray
  167. )
  168. /*++
  169. Routine Description:
  170. This is the main routine of the Workstation Service which registers
  171. itself as an RPC server and notifies the Service Controller of the
  172. Workstation service control entry point.
  173. After the workstation is started, this thread is used (since it's
  174. otherwise unused) to watch for changes to the registry.
  175. Arguments:
  176. NumArgs - Supplies the number of strings specified in ArgsArray.
  177. ArgsArray - Supplies string arguments that are specified in the
  178. StartService API call. This parameter is ignored by the
  179. Workstation service.
  180. Return Value:
  181. None.
  182. --*/
  183. {
  184. NET_API_STATUS ApiStatus;
  185. UNREFERENCED_PARAMETER(NumArgs);
  186. UNREFERENCED_PARAMETER(ArgsArray);
  187. //
  188. // Make sure svchost.exe gave us the global data
  189. //
  190. ASSERT(WsLmsvcsGlobalData != NULL);
  191. WsInitState = 0;
  192. try {
  193. InitializeCriticalSection(&WsWorkerCriticalSection);
  194. } except( EXCEPTION_EXECUTE_HANDLER ) {
  195. ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
  196. goto Cleanup;
  197. }
  198. //
  199. // Initialize the workstation.
  200. //
  201. if (WsInitializeWorkstation(&WsInitState) != NERR_Success) {
  202. DbgPrint("WKSSVC failed to initialize workstation %x\n",WsInitState);
  203. return;
  204. }
  205. //
  206. // Set up to wait for registry change or terminate event.
  207. //
  208. ApiStatus = NetpAllocConfigName(
  209. SERVICES_ACTIVE_DATABASE,
  210. SERVICE_WORKSTATION,
  211. NULL, // default area ("Parameters")
  212. &RegPathToWatch
  213. );
  214. if (ApiStatus != NERR_Success) {
  215. goto Cleanup;
  216. }
  217. NetpAssert(RegPathToWatch != NULL && *RegPathToWatch != TCHAR_EOS);
  218. ApiStatus = (NET_API_STATUS) RegOpenKeyEx(
  219. HKEY_LOCAL_MACHINE, // hKey
  220. RegPathToWatch, // lpSubKey
  221. 0L, // ulOptions (reserved)
  222. KEY_READ | KEY_NOTIFY, // desired access
  223. &ConfigHandle // Newly Opened Key Handle
  224. );
  225. if (ApiStatus != NO_ERROR) {
  226. goto Cleanup;
  227. }
  228. ConfigHandleOpened = TRUE;
  229. RegistryChangeEvent = CreateEvent(
  230. NULL, // no security descriptor
  231. FALSE, // use automatic reset
  232. FALSE, // initial state: not signalled
  233. NULL // no name
  234. );
  235. if (RegistryChangeEvent == NULL) {
  236. ApiStatus = (NET_API_STATUS) GetLastError();
  237. goto Cleanup;
  238. }
  239. ApiStatus = RtlRegisterWait(
  240. &TerminateWorkItem, // work item handle
  241. WsGlobalData.TerminateNowEvent, // wait handle
  242. WsTerminationNotify, // callback fcn
  243. NULL, // parameter
  244. INFINITE, // timeout
  245. WT_EXECUTEONLYONCE | // flags
  246. WT_EXECUTELONGFUNCTION);
  247. if (!NT_SUCCESS(ApiStatus)) {
  248. ApiStatus = RtlNtStatusToDosError(ApiStatus);
  249. goto Cleanup;
  250. }
  251. //
  252. // Setup to monitor registry changes.
  253. //
  254. RegNotifyInfo.NotifyEventHandle = RegistryChangeEvent;
  255. RegNotifyInfo.Timeout = INFINITE;
  256. RegNotifyInfo.WorkItemHandle = NULL;
  257. RegNotifyInfo.RegistryHandle = ConfigHandle;
  258. EnterCriticalSection(&WsWorkerCriticalSection);
  259. if (!WsReInitChangeNotify(&RegNotifyInfo)) {
  260. ApiStatus = GetLastError();
  261. RtlDeregisterWait(TerminateWorkItem);
  262. LeaveCriticalSection(&WsWorkerCriticalSection);
  263. goto Cleanup;
  264. }
  265. LeaveCriticalSection(&WsWorkerCriticalSection);
  266. //
  267. // This thread has done all that it can do. So we can return it
  268. // to the service controller.
  269. //
  270. return;
  271. Cleanup:
  272. DbgPrint("WKSSVC ServiceMain returned with %x\n",ApiStatus);
  273. WsTerminationNotify(NULL, NO_ERROR);
  274. return;
  275. }
  276. VOID
  277. WsInitChangeNotify(
  278. PVOID pData
  279. )
  280. /*++
  281. Routine Description:
  282. Arguments:
  283. Return Value:
  284. --*/
  285. {
  286. UNREFERENCED_PARAMETER(pData);
  287. RegNotifyChangeKeyValue (
  288. ConfigHandle,
  289. TRUE, // watch a subtree
  290. REG_NOTIFY_CHANGE_LAST_SET,
  291. RegistryChangeEvent,
  292. TRUE // async call
  293. );
  294. return;
  295. }
  296. BOOL
  297. WsReInitChangeNotify(
  298. PREG_NOTIFY_INFO pNotifyInfo
  299. )
  300. /*++
  301. Routine Description:
  302. NOTE: This function should only be called when in the
  303. WsWorkerCriticalSection.
  304. Arguments:
  305. Return Value:
  306. --*/
  307. {
  308. BOOL bStat = TRUE;
  309. NTSTATUS Status;
  310. Status = RtlQueueWorkItem(
  311. WsInitChangeNotify,
  312. (PVOID)pNotifyInfo,
  313. WT_EXECUTEONLYONCE);
  314. if (!NT_SUCCESS(Status)) {
  315. NetpKdPrint((PREFIX_WKSTA "Couldn't Initialize Registry Notify %d\n",
  316. RtlNtStatusToDosError(Status)));
  317. bStat = FALSE;
  318. goto CleanExit;
  319. }
  320. //
  321. // Add the work item that is to be called when the
  322. // RegistryChangeEvent is signalled.
  323. //
  324. Status = RtlRegisterWait(
  325. &pNotifyInfo->WorkItemHandle,
  326. pNotifyInfo->NotifyEventHandle,
  327. WsRegistryNotify,
  328. (PVOID)pNotifyInfo,
  329. pNotifyInfo->Timeout,
  330. WT_EXECUTEONLYONCE | WT_EXECUTEINPERSISTENTIOTHREAD);
  331. if (!NT_SUCCESS(Status)) {
  332. NetpKdPrint((PREFIX_WKSTA "Couldn't add Reg Notify work item\n"));
  333. bStat = FALSE;
  334. }
  335. CleanExit:
  336. if (bStat) {
  337. if (WsNumWorkerThreads == 0) {
  338. WsNumWorkerThreads++;
  339. }
  340. }
  341. else {
  342. if (WsNumWorkerThreads == 1) {
  343. WsNumWorkerThreads--;
  344. }
  345. }
  346. return(bStat);
  347. }
  348. DWORD
  349. WsRegistryNotify(
  350. LPVOID pParms,
  351. BOOLEAN fWaitStatus
  352. )
  353. /*++
  354. Routine Description:
  355. Handles Workstation Registry Notification. This function is called by a
  356. thread pool Worker thread when the event used for registry notification is
  357. signaled.
  358. Arguments:
  359. Return Value:
  360. --*/
  361. {
  362. NET_API_STATUS ApiStatus;
  363. PREG_NOTIFY_INFO pNotifyinfo=(PREG_NOTIFY_INFO)pParms;
  364. NET_CONFIG_HANDLE NetConfigHandle;
  365. UNREFERENCED_PARAMETER(fWaitStatus);
  366. //
  367. // The NT thread pool requires explicit work item deregistration,
  368. // even if we specified the WT_EXECUTEONLYONCE flag
  369. //
  370. RtlDeregisterWait(pNotifyinfo->WorkItemHandle);
  371. EnterCriticalSection(&WsWorkerCriticalSection);
  372. if (WsIsTerminating) {
  373. WsNumWorkerThreads--;
  374. SetEvent(WsGlobalData.TerminateNowEvent);
  375. LeaveCriticalSection(&WsWorkerCriticalSection);
  376. return(NO_ERROR);
  377. }
  378. //
  379. // Serialize write access to config information
  380. //
  381. if (RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
  382. //
  383. // Update the redir fields based on change notify.
  384. // WsUpdateWkstaToMatchRegistry expects a NET_CONFIG_HANDLE
  385. // handle, so we conjure up one from the HKEY handle.
  386. //
  387. NetConfigHandle.WinRegKey = ConfigHandle;
  388. WsUpdateWkstaToMatchRegistry(&NetConfigHandle, FALSE);
  389. ApiStatus = WsUpdateRedirToMatchWksta(
  390. PARMNUM_ALL,
  391. NULL
  392. );
  393. // NetpAssert( ApiStatus == NO_ERROR );
  394. RtlReleaseResource(&WsInfo.ConfigResource);
  395. }
  396. if (!WsReInitChangeNotify(&RegNotifyInfo)) {
  397. //
  398. // If we can't add the work item, then we just won't
  399. // listen for registry changes. There's not a whole
  400. // lot we can do here.
  401. //
  402. ApiStatus = GetLastError();
  403. }
  404. LeaveCriticalSection(&WsWorkerCriticalSection);
  405. return(NO_ERROR);
  406. }
  407. VOID
  408. WsTerminationNotify(
  409. LPVOID pParms,
  410. BOOLEAN fWaitStatus
  411. )
  412. /*++
  413. Routine Description:
  414. This function gets called by a services worker thread when the
  415. termination event gets signaled.
  416. Arguments:
  417. Return Value:
  418. --*/
  419. {
  420. UNREFERENCED_PARAMETER(pParms);
  421. UNREFERENCED_PARAMETER(fWaitStatus);
  422. IF_DEBUG(MAIN) {
  423. NetpKdPrint((PREFIX_WKSTA "WORKSTATION_main: cleaning up, "
  424. "api status.\n"));
  425. }
  426. //
  427. // The NT thread pool requires explicit work item deregistration,
  428. // even if we specified the WT_EXECUTEONLYONCE flag
  429. //
  430. if (TerminateWorkItem != NULL) {
  431. RtlDeregisterWait(TerminateWorkItem);
  432. }
  433. EnterCriticalSection(&WsWorkerCriticalSection);
  434. WsIsTerminating = TRUE;
  435. //
  436. // Must close winreg handle (which turns off notify) before event handle.
  437. // Closing the regkey handle generates a change notify event!
  438. //
  439. if (ConfigHandleOpened) {
  440. (VOID) RegCloseKey(ConfigHandle);
  441. #if DBG
  442. //
  443. // Workaround for a benign winreg assertion caused by us
  444. // closing the RegistryChangeEvent handle which it wants
  445. // to signal.
  446. //
  447. Sleep(2000);
  448. #endif
  449. }
  450. if (RegPathToWatch != NULL) {
  451. (VOID) NetApiBufferFree(RegPathToWatch);
  452. }
  453. if ((RegistryChangeEvent != NULL) && (WsNumWorkerThreads != 0)) {
  454. //
  455. // There is still a RegistryNotify Work Item in the system, we
  456. // will attempt to remove it by setting the event to wake it up.
  457. //
  458. ResetEvent(WsGlobalData.TerminateNowEvent);
  459. LeaveCriticalSection(&WsWorkerCriticalSection);
  460. SetEvent(RegistryChangeEvent);
  461. //
  462. // Wait until the WsRegistryNotify Thread is finished.
  463. // We will give it 60 seconds. If the thread isn't
  464. // finished in that time frame, we will go on anyway.
  465. //
  466. WaitForSingleObject(
  467. WsGlobalData.TerminateNowEvent,
  468. 60000);
  469. if (WsNumWorkerThreads != 0) {
  470. NetpKdPrint((PREFIX_WKSTA "WsTerminationNotify: "
  471. "Registry Notification thread didn't terminate\n"));
  472. }
  473. EnterCriticalSection(&WsWorkerCriticalSection);
  474. }
  475. (VOID) CloseHandle(RegistryChangeEvent);
  476. //
  477. // Shutting down
  478. //
  479. // NOTE: We must synchronize with the RegistryNotification Thread.
  480. //
  481. WsShutdownWorkstation(
  482. NERR_Success,
  483. WsInitState
  484. );
  485. WsIsTerminating = FALSE;
  486. LeaveCriticalSection(&WsWorkerCriticalSection);
  487. DeleteCriticalSection(&WsWorkerCriticalSection);
  488. return;
  489. }
  490. STATIC
  491. NET_API_STATUS
  492. WsInitializeWorkstation(
  493. OUT LPDWORD WsInitState
  494. )
  495. /*++
  496. Routine Description:
  497. This function initializes the Workstation service.
  498. Arguments:
  499. WsInitState - Returns a flag to indicate how far we got with initializing
  500. the Workstation service before an error occured.
  501. Return Value:
  502. NET_API_STATUS - NERR_Success or reason for failure.
  503. --*/
  504. {
  505. NET_API_STATUS status;
  506. //
  507. // Initialize all the status fields so that subsequent calls to
  508. // SetServiceStatus need to only update fields that changed.
  509. //
  510. WsGlobalData.Status.dwServiceType = SERVICE_WIN32;
  511. WsGlobalData.Status.dwCurrentState = SERVICE_START_PENDING;
  512. WsGlobalData.Status.dwControlsAccepted = 0;
  513. WsGlobalData.Status.dwCheckPoint = 1;
  514. WsGlobalData.Status.dwWaitHint = WS_WAIT_HINT_TIME;
  515. SET_SERVICE_EXITCODE(
  516. NO_ERROR,
  517. WsGlobalData.Status.dwWin32ExitCode,
  518. WsGlobalData.Status.dwServiceSpecificExitCode
  519. );
  520. //
  521. // Initialize the resource for serializing access to configuration
  522. // information.
  523. //
  524. // This must be done before the redir is initialized
  525. try {
  526. RtlInitializeResource(&WsInfo.ConfigResource);
  527. } except(EXCEPTION_EXECUTE_HANDLER) {
  528. return RtlNtStatusToDosError(GetExceptionCode());
  529. }
  530. //
  531. // Initialize workstation to receive service requests by registering the
  532. // control handler.
  533. //
  534. if ((WsGlobalData.StatusHandle = RegisterServiceCtrlHandler(
  535. SERVICE_WORKSTATION,
  536. WkstaControlHandler
  537. )) == (SERVICE_STATUS_HANDLE) 0) {
  538. status = GetLastError();
  539. WS_HANDLE_ERROR(WsErrorRegisterControlHandler);
  540. DbgPrint("WKSSVC failed with RegisterServiceCtrlHandler %x\n",status);
  541. return status;
  542. }
  543. //
  544. // Create an event which is used by the service control handler to notify
  545. // the Workstation service that it is time to terminate.
  546. //
  547. if ((WsGlobalData.TerminateNowEvent =
  548. CreateEvent(
  549. NULL, // Event attributes
  550. TRUE, // Event must be manually reset
  551. FALSE,
  552. NULL // Initial state not signalled
  553. )) == NULL) {
  554. status = GetLastError();
  555. WS_HANDLE_ERROR(WsErrorCreateTerminateEvent);
  556. return status;
  557. }
  558. (*WsInitState) |= WS_TERMINATE_EVENT_CREATED;
  559. //
  560. // Notify the Service Controller for the first time that we are alive
  561. // and we are start pending
  562. //
  563. if ((status = WsUpdateStatus()) != NERR_Success) {
  564. WS_HANDLE_ERROR(WsErrorNotifyServiceController);
  565. DbgPrint("WKSSVC failed with WsUpdateStatus %x\n",status);
  566. return status;
  567. }
  568. //
  569. // Initialize the workstation as a logon process with LSA, and
  570. // get the MS V 1.0 authentication package ID.
  571. //
  572. if ((status = WsInitializeLsa()) != NERR_Success) {
  573. WS_HANDLE_ERROR(WsErrorInitLsa);
  574. DbgPrint("WKSSVC failed with WsInitializeLsa %x\n",status);
  575. return status;
  576. }
  577. (*WsInitState) |= WS_LSA_INITIALIZED;
  578. //
  579. // Read the configuration information to initialize the redirector and
  580. // datagram receiver
  581. //
  582. if ((status = WsInitializeRedirector()) != NERR_Success) {
  583. WS_HANDLE_ERROR(WsErrorStartRedirector);
  584. DbgPrint("WKSSVC failed with WsInitializeRedirector %x\n",status);
  585. return status;
  586. }
  587. (*WsInitState) |= WS_DEVICES_INITIALIZED;
  588. //
  589. // Service install still pending. Update checkpoint counter and the
  590. // status with the Service Controller.
  591. //
  592. (WsGlobalData.Status.dwCheckPoint)++;
  593. (void) WsUpdateStatus();
  594. //
  595. // Bind to transports
  596. //
  597. if ((status = WsBindToTransports()) != NERR_Success) {
  598. WS_HANDLE_ERROR(WsErrorBindTransport);
  599. DbgPrint("WKSSVC failed with WsBindToTransports %x\n",status);
  600. return status;
  601. }
  602. //
  603. // Service install still pending. Update checkpoint counter and the
  604. // status with the Service Controller.
  605. //
  606. (WsGlobalData.Status.dwCheckPoint)++;
  607. (void) WsUpdateStatus();
  608. //
  609. // Add domain names.
  610. //
  611. if ((status = WsAddDomains()) != NERR_Success) {
  612. WS_HANDLE_ERROR(WsErrorAddDomains);
  613. DbgPrint("WKSSVC failed with WsAddDomains %x\n",status);
  614. return status;
  615. }
  616. //
  617. // Service start still pending. Update checkpoint counter and the
  618. // status with the Service Controller.
  619. //
  620. (WsGlobalData.Status.dwCheckPoint)++;
  621. (void) WsUpdateStatus();
  622. //
  623. // Create Workstation service API data structures
  624. //
  625. if ((status = WsCreateApiStructures(WsInitState)) != NERR_Success) {
  626. WS_HANDLE_ERROR(WsErrorCreateApiStructures);
  627. DbgPrint("WKSSVC failed with WsCreateApiStructures %x\n",status);
  628. return status;
  629. }
  630. //
  631. // Initialize the workstation service to receive RPC requests
  632. //
  633. // NOTE: Now all RPC servers in services.exe share the same pipe name.
  634. // However, in order to support communication with version 1.0 of WinNt,
  635. // it is necessary for the Client Pipe name to remain the same as
  636. // it was in version 1.0. Mapping to the new name is performed in
  637. // the Named Pipe File System code.
  638. //
  639. if ((status = WsLmsvcsGlobalData->StartRpcServer(
  640. WORKSTATION_INTERFACE_NAME,
  641. wkssvc_ServerIfHandle
  642. )) != NERR_Success) {
  643. WS_HANDLE_ERROR(WsErrorStartRpcServer);
  644. DbgPrint("WKSSVC failed with StartRpcServer %x\n",status);
  645. return status;
  646. }
  647. (*WsInitState) |= WS_RPC_SERVER_STARTED;
  648. //
  649. // Lastly, we create a thread to communicate with the
  650. // Dfs-enabled MUP driver.
  651. //
  652. if ((status = WsInitializeDfs()) != NERR_Success) {
  653. WS_HANDLE_ERROR(WsErrorStartRedirector);
  654. DbgPrint("WKSSVC failed with WsInitializeDfs %x\n",status);
  655. return status;
  656. }
  657. (*WsInitState) |= WS_DFS_THREAD_STARTED;
  658. (void) I_ScSetServiceBits(
  659. WsGlobalData.StatusHandle,
  660. SV_TYPE_WORKSTATION,
  661. TRUE,
  662. TRUE,
  663. NULL
  664. );
  665. //
  666. // We are done with starting the Workstation service. Tell Service
  667. // Controller our new status.
  668. //
  669. WsGlobalData.Status.dwCurrentState = SERVICE_RUNNING;
  670. WsGlobalData.Status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
  671. SERVICE_ACCEPT_PAUSE_CONTINUE |
  672. SERVICE_ACCEPT_SHUTDOWN;
  673. WsGlobalData.Status.dwCheckPoint = 0;
  674. WsGlobalData.Status.dwWaitHint = 0;
  675. if ((status = WsUpdateStatus()) != NERR_Success) {
  676. WS_HANDLE_ERROR(WsErrorNotifyServiceController);
  677. DbgPrint("WKSSVC failed with WsUpdateStatus %x\n",status);
  678. return status;
  679. }
  680. #ifdef WS_SET_TIME
  681. //
  682. // Set the Time
  683. //
  684. WsSetTime();
  685. #endif
  686. IF_DEBUG(MAIN) {
  687. NetpKdPrint(("[Wksta] Successful Initialization\n"));
  688. }
  689. WsLUIDDeviceMapsEnabled = WsGetLUIDDeviceMapsEnabled();
  690. return NERR_Success;
  691. }
  692. VOID
  693. WsShutdownWorkstation(
  694. IN NET_API_STATUS ErrorCode,
  695. IN DWORD WsInitState
  696. )
  697. /*++
  698. Routine Description:
  699. This function shuts down the Workstation service.
  700. Arguments:
  701. ErrorCode - Supplies the error code of the failure
  702. WsInitState - Supplies a flag to indicate how far we got with initializing
  703. the Workstation service before an error occured, thus the amount of
  704. clean up needed.
  705. Return Value:
  706. None.
  707. --*/
  708. {
  709. NET_API_STATUS status = NERR_Success;
  710. //
  711. // Service stop still pending. Update checkpoint counter and the
  712. // status with the Service Controller.
  713. //
  714. (WsGlobalData.Status.dwCheckPoint)++;
  715. (void) WsUpdateStatus();
  716. if (WsInitState & WS_DFS_THREAD_STARTED) {
  717. //
  718. // Stop the Dfs thread
  719. //
  720. WsShutdownDfs();
  721. }
  722. if (WsInitState & WS_RPC_SERVER_STARTED) {
  723. //
  724. // Stop the RPC server
  725. //
  726. WsLmsvcsGlobalData->StopRpcServer(wkssvc_ServerIfHandle);
  727. }
  728. if (WsInitState & WS_API_STRUCTURES_CREATED) {
  729. //
  730. // Destroy data structures created for Workstation APIs
  731. //
  732. WsDestroyApiStructures(WsInitState);
  733. }
  734. WsShutdownMessageSend();
  735. //
  736. // Don't need to ask redirector to unbind from its transports when
  737. // cleaning up because the redirector will tear down the bindings when
  738. // it stops.
  739. //
  740. if (WsInitState & WS_DEVICES_INITIALIZED) {
  741. //
  742. // Shut down the redirector and datagram receiver
  743. //
  744. status = WsShutdownRedirector();
  745. }
  746. //
  747. // Delete resource for serializing access to config information
  748. // This must be done only after the redir is shutdown
  749. // We do this here, (the Init is done in WsInitializeWorkstation routine above)
  750. // to avoid putting additional synchronization on delete
  751. // Otherwise we get into the situation where, the redir is shutting down and
  752. // it deletes the resource while someone has acquired it, causing bad things.
  753. RtlDeleteResource(&WsInfo.ConfigResource);
  754. if (WsInitState & WS_LSA_INITIALIZED) {
  755. //
  756. // Deregister workstation as logon process
  757. //
  758. WsShutdownLsa();
  759. }
  760. if (WsInitState & WS_TERMINATE_EVENT_CREATED) {
  761. //
  762. // Close handle to termination event
  763. //
  764. CloseHandle(WsGlobalData.TerminateNowEvent);
  765. }
  766. I_ScSetServiceBits(
  767. WsGlobalData.StatusHandle,
  768. SV_TYPE_WORKSTATION,
  769. FALSE,
  770. TRUE,
  771. NULL
  772. );
  773. //
  774. // We are done with cleaning up. Tell Service Controller that we are
  775. // stopped.
  776. //
  777. WsGlobalData.Status.dwCurrentState = SERVICE_STOPPED;
  778. WsGlobalData.Status.dwControlsAccepted = 0;
  779. if ((ErrorCode == NERR_Success) &&
  780. (status == ERROR_REDIRECTOR_HAS_OPEN_HANDLES)) {
  781. ErrorCode = status;
  782. }
  783. SET_SERVICE_EXITCODE(
  784. ErrorCode,
  785. WsGlobalData.Status.dwWin32ExitCode,
  786. WsGlobalData.Status.dwServiceSpecificExitCode
  787. );
  788. WsGlobalData.Status.dwCheckPoint = 0;
  789. WsGlobalData.Status.dwWaitHint = 0;
  790. (void) WsUpdateStatus();
  791. }
  792. STATIC
  793. VOID
  794. WsHandleError(
  795. IN WS_ERROR_CONDITION FailingCondition,
  796. IN NET_API_STATUS Status,
  797. IN DWORD WsInitState
  798. )
  799. /*++
  800. Routine Description:
  801. This function handles a Workstation service error condition. If the error
  802. condition is fatal, it shuts down the Workstation service.
  803. Arguments:
  804. FailingCondition - Supplies a value which indicates what the failure is.
  805. Status - Supplies the status code for the failure.
  806. WsInitState - Supplies a flag to indicate how far we got with initializing
  807. the Workstation service before an error occured, thus the amount of
  808. clean up needed.
  809. Return Value:
  810. None.
  811. --*/
  812. {
  813. switch (FailingCondition) {
  814. case WsErrorRegisterControlHandler:
  815. NetpKdPrint(("Workstation cannot register control handler "
  816. FORMAT_API_STATUS "\n", Status));
  817. WS_SHUTDOWN_WORKSTATION(Status);
  818. break;
  819. case WsErrorCreateTerminateEvent:
  820. NetpKdPrint(("[Wksta] Cannot create done event "
  821. FORMAT_API_STATUS "\n", Status));
  822. WS_SHUTDOWN_WORKSTATION(Status);
  823. break;
  824. case WsErrorNotifyServiceController:
  825. NetpKdPrint(("[Wksta] SetServiceStatus error "
  826. FORMAT_API_STATUS "\n", Status));
  827. WS_SHUTDOWN_WORKSTATION(Status);
  828. break;
  829. case WsErrorInitLsa:
  830. NetpKdPrint(("[Wksta] LSA initialization error "
  831. FORMAT_API_STATUS "\n", Status));
  832. WS_SHUTDOWN_WORKSTATION(Status);
  833. break;
  834. case WsErrorStartRedirector:
  835. NetpKdPrint(("[Wksta] Cannot start redirector "
  836. FORMAT_API_STATUS "\n", Status));
  837. WS_SHUTDOWN_WORKSTATION(Status);
  838. break;
  839. case WsErrorBindTransport:
  840. if (Status == NERR_ItemNotFound) {
  841. NetpKdPrint(("[Wksta] Did not bind to any transport driver\n"));
  842. }
  843. WS_SHUTDOWN_WORKSTATION(Status);
  844. break;
  845. case WsErrorAddDomains:
  846. NetpKdPrint(("[Wksta] Could not add domain names "
  847. FORMAT_API_STATUS "\n", Status));
  848. WS_SHUTDOWN_WORKSTATION(Status);
  849. break;
  850. case WsErrorStartRpcServer:
  851. NetpKdPrint(("[Wksta] Cannot start RPC server "
  852. FORMAT_API_STATUS "\n", Status));
  853. WS_SHUTDOWN_WORKSTATION(Status);
  854. break;
  855. case WsErrorCreateApiStructures:
  856. NetpKdPrint(("[Wksta] Error in creating API structures "
  857. FORMAT_API_STATUS "\n", Status));
  858. WS_SHUTDOWN_WORKSTATION(Status);
  859. break;
  860. default:
  861. NetpKdPrint(("[Wksta] WsHandleError: unknown error condition %lu\n",
  862. FailingCondition));
  863. NetpAssert(FALSE);
  864. }
  865. }
  866. NET_API_STATUS
  867. WsUpdateStatus(
  868. VOID
  869. )
  870. /*++
  871. Routine Description:
  872. This function updates the Workstation service status with the Service
  873. Controller.
  874. Arguments:
  875. None.
  876. Return Value:
  877. NET_API_STATUS - NERR_Success or reason for failure.
  878. --*/
  879. {
  880. NET_API_STATUS status = NERR_Success;
  881. if (WsGlobalData.StatusHandle == (SERVICE_STATUS_HANDLE) 0) {
  882. NetpKdPrint((
  883. "[Wksta] Cannot call SetServiceStatus, no status handle.\n"
  884. ));
  885. return ERROR_INVALID_HANDLE;
  886. }
  887. if (! SetServiceStatus(WsGlobalData.StatusHandle, &WsGlobalData.Status)) {
  888. status = GetLastError();
  889. IF_DEBUG(MAIN) {
  890. NetpKdPrint(("[Wksta] SetServiceStatus error %lu\n", status));
  891. }
  892. }
  893. return status;
  894. }
  895. STATIC
  896. NET_API_STATUS
  897. WsCreateApiStructures(
  898. IN OUT LPDWORD WsInitState
  899. )
  900. /*++
  901. Routine Description:
  902. This function creates and initializes all the data structures required
  903. for the Workstation APIs.
  904. Arguments:
  905. WsInitState - Returns the supplied flag of how far we got in the
  906. Workstation service initialization process.
  907. Return Value:
  908. NET_API_STATUS - NERR_Success or reason for failure.
  909. --*/
  910. {
  911. NET_API_STATUS status;
  912. //
  913. // Create workstation security objects
  914. //
  915. if ((status = WsCreateWkstaObjects()) != NERR_Success) {
  916. return status;
  917. }
  918. (*WsInitState) |= WS_SECURITY_OBJECTS_CREATED;
  919. //
  920. // Create Use Table
  921. //
  922. if ((status = WsInitUseStructures()) != NERR_Success) {
  923. return status;
  924. }
  925. (*WsInitState) |= WS_USE_TABLE_CREATED;
  926. return NERR_Success;
  927. }
  928. STATIC
  929. VOID
  930. WsDestroyApiStructures(
  931. IN DWORD WsInitState
  932. )
  933. /*++
  934. Routine Description:
  935. This function destroys the data structures created for the Workstation
  936. APIs.
  937. Arguments:
  938. WsInitState - Supplies a flag which tells us what API structures
  939. were created in the initialization process and now have to be
  940. cleaned up.
  941. Return Value:
  942. None.
  943. --*/
  944. {
  945. if (WsInitState & WS_USE_TABLE_CREATED) {
  946. //
  947. // Destroy Use Table
  948. //
  949. WsDestroyUseStructures();
  950. }
  951. if (WsInitState & WS_SECURITY_OBJECTS_CREATED) {
  952. //
  953. // Destroy workstation security objects
  954. //
  955. WsDestroyWkstaObjects();
  956. }
  957. }
  958. VOID
  959. WkstaControlHandler(
  960. IN DWORD Opcode
  961. )
  962. /*++
  963. Routine Description:
  964. This is the service control handler of the Workstation service.
  965. Arguments:
  966. Opcode - Supplies a value which specifies the action for the Workstation
  967. service to perform.
  968. Arg - Supplies a value which tells a service specifically what to do
  969. for an operation specified by Opcode.
  970. Return Value:
  971. None.
  972. --*/
  973. {
  974. IF_DEBUG(MAIN) {
  975. NetpKdPrint(("[Wksta] In Control Handler\n"));
  976. }
  977. switch (Opcode) {
  978. case SERVICE_CONTROL_PAUSE:
  979. //
  980. // Pause redirection of print and comm devices
  981. //
  982. WsPauseOrContinueRedirection(
  983. PauseRedirection
  984. );
  985. break;
  986. case SERVICE_CONTROL_CONTINUE:
  987. //
  988. // Resume redirection of print and comm devices
  989. //
  990. WsPauseOrContinueRedirection(
  991. ContinueRedirection
  992. );
  993. break;
  994. case SERVICE_CONTROL_SHUTDOWN:
  995. //
  996. // Lack of break is intentional!
  997. //
  998. case SERVICE_CONTROL_STOP:
  999. if (WsGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) {
  1000. IF_DEBUG(MAIN) {
  1001. NetpKdPrint(("[Wksta] Stopping workstation...\n"));
  1002. }
  1003. WsGlobalData.Status.dwCurrentState = SERVICE_STOP_PENDING;
  1004. WsGlobalData.Status.dwCheckPoint = 1;
  1005. WsGlobalData.Status.dwWaitHint = WS_WAIT_HINT_TIME;
  1006. //
  1007. // Send the status response.
  1008. //
  1009. (void) WsUpdateStatus();
  1010. if (! SetEvent(WsGlobalData.TerminateNowEvent)) {
  1011. //
  1012. // Problem with setting event to terminate Workstation
  1013. // service.
  1014. //
  1015. NetpKdPrint(("[Wksta] Error setting TerminateNowEvent "
  1016. FORMAT_API_STATUS "\n", GetLastError()));
  1017. NetpAssert(FALSE);
  1018. }
  1019. return;
  1020. }
  1021. break;
  1022. case SERVICE_CONTROL_INTERROGATE:
  1023. break;
  1024. default:
  1025. IF_DEBUG(MAIN) {
  1026. NetpKdPrint(("Unknown workstation opcode " FORMAT_HEX_DWORD
  1027. "\n", Opcode));
  1028. }
  1029. }
  1030. //
  1031. // Send the status response.
  1032. //
  1033. (void) WsUpdateStatus();
  1034. }
  1035. #ifdef WS_SET_TIME
  1036. STATIC
  1037. VOID
  1038. WsSetTime(
  1039. VOID
  1040. )
  1041. /*++
  1042. Routine Description:
  1043. This function sets the time on the local machine.
  1044. Arguments:
  1045. None.
  1046. Return Value:
  1047. None.
  1048. --*/
  1049. {
  1050. NTSTATUS ntStatus;
  1051. NET_API_STATUS status;
  1052. PTIME_OF_DAY_INFO pTod;
  1053. LPTSTR pServerName;
  1054. LARGE_INTEGER systemTime;
  1055. LARGE_INTEGER previousTime;
  1056. ULONG privileges[1];
  1057. //
  1058. // Look in registry to see if we are to set the time.
  1059. //
  1060. if (!WsShouldSetTime()) {
  1061. IF_DEBUG(MAIN) {
  1062. NetpKdPrint(("[Wksta] Time update is NOT requested\n",
  1063. status));
  1064. }
  1065. return;
  1066. }
  1067. //
  1068. // Find a server to get the time from.
  1069. //
  1070. status = WsFindTimeServer(&pServerName);
  1071. if (status != NERR_Success) {
  1072. IF_DEBUG(MAIN) {
  1073. NetpKdPrint(("[Wksta] WsFindTimeServer Failed "FORMAT_API_STATUS" \n",
  1074. status));
  1075. }
  1076. return;
  1077. }
  1078. //
  1079. // Get the time
  1080. //
  1081. status = NetRemoteTOD(
  1082. pServerName,
  1083. (LPBYTE *)pTod
  1084. );
  1085. if (status != NERR_Success) {
  1086. IF_DEBUG(MAIN) {
  1087. NetpKdPrint(("[Wksta] NetRemoteTOD Failed "FORMAT_API_STATUS" \n",
  1088. status));
  1089. }
  1090. NetApiBufferFree(pServerName);
  1091. return;
  1092. }
  1093. NetApiBufferFree(pServerName);
  1094. //
  1095. // Convert the time to NT time.
  1096. //
  1097. RtlSecondsSince1970ToTime(
  1098. pTod->tod_elapsedt, // ULONG
  1099. &systemTime // PLARGE_INTEGER
  1100. );
  1101. //
  1102. // Set the NT system time. (first get privilege)
  1103. //
  1104. privileges[0] = SE_SYSTEMTIME_PRIVILEGE;
  1105. status = NetpGetPrivilege(1,privileges);
  1106. if (status != NO_ERROR) {
  1107. IF_DEBUG(MAIN) {
  1108. NetpKdPrint(("[Wksta] NetpGetPrivilege Failed "FORMAT_DWORD" \n",
  1109. status));
  1110. }
  1111. NetApiBufferFree(pServerName);
  1112. return;
  1113. }
  1114. ntStatus = NtSetSystemTime(
  1115. &systemTime, // IN PLARGE_INTEGER
  1116. &previousTime // OUT PLARGE_INTEGER
  1117. );
  1118. (VOID)NetpReleasePrivilege();
  1119. if (!NT_SUCCESS(ntStatus)) {
  1120. IF_DEBUG(MAIN) {
  1121. NetpKdPrint(("[Wksta] NtSetSystemTime Failed "FORMAT_NTSTATUS" \n",
  1122. ntStatus));
  1123. }
  1124. }
  1125. return;
  1126. }
  1127. STATIC
  1128. DWORD
  1129. WsFindTimeServer(
  1130. LPTSTR *pServerName
  1131. )
  1132. /*++
  1133. Routine Description:
  1134. This function finds the server name for a TimeSource server.
  1135. This function allocates storage for the name whose pointer is stored in
  1136. pServerName.
  1137. Arguments:
  1138. pServerName - This is a pointer to a location where the pointer to
  1139. the name of the TimeSource Server is to placed.
  1140. Return Value:
  1141. NERR_Success - If the operation was completely successful.
  1142. assorted errors - if the operation failed in any way.
  1143. --*/
  1144. #define SERVER_INFO_BUF_SIZE 512
  1145. {
  1146. NET_API_STATUS status;
  1147. DWORD entriesRead;
  1148. DWORD totalEntries;
  1149. DWORD resumeHandle;
  1150. LPSERVER_INFO_100 pServerInfo = NULL;
  1151. LPTSTR pDomainName;
  1152. //
  1153. // Get the name of our domain.
  1154. //
  1155. status = NetpGetDomainName(&pDomainName);
  1156. if (status != NERR_Success) {
  1157. return(status);
  1158. }
  1159. //
  1160. // Get a short enumerated list of the timesource servers out there.
  1161. //
  1162. status = NetServerEnum (
  1163. NULL, // Local Server
  1164. 100,
  1165. (LPBYTE *)&pServerInfo,
  1166. SERVER_INFO_BUF_SIZE,
  1167. &entriesRead,
  1168. &totalEntries,
  1169. SV_TYPE_TIME_SOURCE,
  1170. pDomainName, // PrimaryDomain
  1171. &resumeHandle
  1172. );
  1173. NetApiBufferFree(pDomainName);
  1174. if (status != NERR_Success) {
  1175. return(status);
  1176. }
  1177. if (entriesRead == 0) {
  1178. IF_DEBUG(MAIN) {
  1179. NetpKdPrint(("[Wksta]FindTimeServer: No TimeSource Servers "
  1180. "in Domain\n"));
  1181. }
  1182. if (pServerInfo != NULL) {
  1183. NetApiBufferFree(pServerInfo);
  1184. }
  1185. return(ERROR_GEN_FAILURE);
  1186. }
  1187. //
  1188. // Allocate storage and copy the Time Source Server name there
  1189. //
  1190. *pServerName = (LPTSTR) LocalAlloc(
  1191. LMEM_ZEROINIT,
  1192. (UINT) STRSIZE(pServerInfo->sv100_name)
  1193. );
  1194. if (*pServerName == NULL) {
  1195. NetApiBufferFree(pServerInfo);
  1196. return(GetLastError());
  1197. }
  1198. STRCPY(*pServerName, pServerInfo->sv100_name);
  1199. NetApiBufferFree(pServerInfo);
  1200. return(NERR_Success);
  1201. }
  1202. STATIC
  1203. BOOL
  1204. WsShouldSetTime(
  1205. VOID
  1206. )
  1207. /*++
  1208. Routine Description:
  1209. This function looks in the registry to determine if the workstation
  1210. service is to go out and find the time so it can set the time on
  1211. this machine.
  1212. Arguments:
  1213. none
  1214. Return Value:
  1215. TRUE - The Workstation should set the time.
  1216. FALSE - The Workstation should not set the time.
  1217. --*/
  1218. {
  1219. HKEY systemKey;
  1220. HKEY wkstaKey;
  1221. DWORD status;
  1222. DWORD setTimeFlag;
  1223. DWORD bufferSize;
  1224. //
  1225. // Get the key for the current system control set.
  1226. //
  1227. status = RegOpenKeyEx(
  1228. HKEY_LOCAL_MACHINE, // hKey
  1229. CURRENT_CTRL_SET, // lpSubKey
  1230. 0L, // ulOptions (reserved)
  1231. KEY_READ, // desired access
  1232. &systemKey); // Newly Opened Key Handle
  1233. if (status != NO_ERROR) {
  1234. IF_DEBUG(MAIN) {
  1235. NetpKdPrint(("[Wksta] RegOpenKeyEx (system key) failed "
  1236. "FORMAT_API_STATUS" "\n",status));
  1237. }
  1238. return (FALSE);
  1239. }
  1240. //
  1241. // Get the Workstation Service Key
  1242. //
  1243. status = RegOpenKeyEx(
  1244. systemKey, // hKey
  1245. WKSTA_KEY, // lpSubKey
  1246. 0L, // ulOptions (reserved)
  1247. KEY_READ, // desired access
  1248. &wkstaKey // Newly Opened Key Handle
  1249. );
  1250. if (status != NO_ERROR) {
  1251. IF_DEBUG(MAIN) {
  1252. NetpKdPrint(("[Wksta] RegOpenKeyEx (wksta key) failed "
  1253. "FORMAT_API_STATUS" "\n",status));
  1254. }
  1255. RegCloseKey(systemKey);
  1256. return (FALSE);
  1257. }
  1258. //
  1259. // Read the SetTime Value. This is a DWORD sized object that is
  1260. // expected to be non-zero if we are to read the time, or zero
  1261. // if we are not.
  1262. //
  1263. bufferSize = sizeof(DWORD);
  1264. status = RegQueryValueEx (
  1265. wkstaKey, // hKey
  1266. SET_TIME_VALUE_NAME, // lpValueName
  1267. NULL, // lpTitleIndex
  1268. NULL, // lpType
  1269. (LPBYTE)&setTimeFlag, // lpData
  1270. &bufferSize // lpcbData
  1271. );
  1272. RegCloseKey(systemKey);
  1273. RegCloseKey(wkstaKey);
  1274. if (status != NO_ERROR) {
  1275. IF_DEBUG(MAIN) {
  1276. NetpKdPrint(("[Wksta] RegQueryValueEx(SetTimeValue) failed "
  1277. FORMAT_API_STATUS "\n",status));
  1278. }
  1279. return(FALSE);
  1280. }
  1281. //
  1282. // NB The Set Time feature is currently disabled
  1283. //
  1284. return(FALSE);
  1285. // return(setTimeFlag);
  1286. }
  1287. #endif
  1288. STATIC
  1289. BOOL
  1290. WsGetLUIDDeviceMapsEnabled(
  1291. VOID
  1292. )
  1293. /*++
  1294. Routine Description:
  1295. This function calls NtQueryInformationProcess() to determine if
  1296. LUID device maps are enabled
  1297. Arguments:
  1298. none
  1299. Return Value:
  1300. TRUE - LUID device maps are enabled
  1301. FALSE - LUID device maps are disabled
  1302. --*/
  1303. {
  1304. NTSTATUS Status;
  1305. ULONG LUIDDeviceMapsEnabled;
  1306. BOOL Result;
  1307. Status = NtQueryInformationProcess( NtCurrentProcess(),
  1308. ProcessLUIDDeviceMapsEnabled,
  1309. &LUIDDeviceMapsEnabled,
  1310. sizeof(LUIDDeviceMapsEnabled),
  1311. NULL
  1312. );
  1313. if (!NT_SUCCESS( Status )) {
  1314. IF_DEBUG(MAIN) {
  1315. NetpKdPrint(("[Wksta] NtQueryInformationProcess(WsLUIDDeviceMapsEnabled) failed "
  1316. "FORMAT_API_STATUS" "\n",Status));
  1317. }
  1318. Result = FALSE;
  1319. }
  1320. else {
  1321. Result = (LUIDDeviceMapsEnabled != 0);
  1322. }
  1323. return( Result );
  1324. }