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.

1380 lines
35 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. //-------------------------------------------------------------------//
  38. // //
  39. // Structures
  40. // //
  41. //-------------------------------------------------------------------//
  42. typedef struct _REG_NOTIFY_INFO {
  43. HANDLE NotifyEventHandle;
  44. DWORD Timeout;
  45. HANDLE WorkItemHandle;
  46. HANDLE RegistryHandle;
  47. } REG_NOTIFY_INFO, *PREG_NOTIFY_INFO, *LPREG_NOTIFY_INFO;
  48. //-------------------------------------------------------------------//
  49. // //
  50. // Global variables //
  51. // //
  52. //-------------------------------------------------------------------//
  53. WS_GLOBAL_DATA WsGlobalData;
  54. PSVCHOST_GLOBAL_DATA WsLmsvcsGlobalData;
  55. REG_NOTIFY_INFO RegNotifyInfo = {0};
  56. HANDLE TerminateWorkItem = NULL;
  57. CRITICAL_SECTION WsWorkerCriticalSection;
  58. BOOL WsIsTerminating=FALSE;
  59. BOOL WsLUIDDeviceMapsEnabled=FALSE;
  60. DWORD WsNumWorkerThreads=0;
  61. // Used by the termination routine:
  62. BOOL ConfigHandleOpened = FALSE;
  63. BOOL WsWorkerCriticalSectionInitialized = FALSE;
  64. HKEY ConfigHandle;
  65. HANDLE RegistryChangeEvent = NULL;
  66. LPTSTR RegPathToWatch = NULL;
  67. DWORD WsInitState = 0;
  68. // Stores the status with which WsInitializeWorkstation failed, so that it can later
  69. // be passed to WsShutdownWorkstation
  70. NET_API_STATUS WsInitializeStatusError = NERR_Success;
  71. //-------------------------------------------------------------------//
  72. // //
  73. // Function prototypes //
  74. // //
  75. //-------------------------------------------------------------------//
  76. STATIC
  77. NET_API_STATUS
  78. WsInitializeWorkstation(
  79. OUT LPDWORD WsInitState
  80. );
  81. STATIC
  82. VOID
  83. WsShutdownWorkstation(
  84. IN NET_API_STATUS ErrorCode,
  85. IN DWORD WsInitState
  86. );
  87. STATIC
  88. NET_API_STATUS
  89. WsCreateApiStructures(
  90. IN OUT LPDWORD WsInitState
  91. );
  92. STATIC
  93. VOID
  94. WsDestroyApiStructures(
  95. IN DWORD WsInitState
  96. );
  97. VOID
  98. WkstaControlHandler(
  99. IN DWORD Opcode
  100. );
  101. BOOL
  102. WsReInitChangeNotify(
  103. PREG_NOTIFY_INFO pNotifyInfo
  104. );
  105. DWORD
  106. WsRegistryNotify(
  107. LPVOID pParms,
  108. BOOLEAN fWaitStatus
  109. );
  110. VOID
  111. WsTerminationNotify(
  112. LPVOID pParms,
  113. BOOLEAN fWaitStatus
  114. );
  115. STATIC
  116. BOOL
  117. WsGetLUIDDeviceMapsEnabled(
  118. VOID
  119. );
  120. RPC_STATUS WsRpcSecurityCallback(
  121. IN RPC_IF_HANDLE *Interface,
  122. IN void *Context
  123. );
  124. VOID
  125. SvchostPushServiceGlobals(
  126. PSVCHOST_GLOBAL_DATA pGlobals
  127. )
  128. {
  129. WsLmsvcsGlobalData = pGlobals;
  130. }
  131. VOID
  132. ServiceMain(
  133. DWORD NumArgs,
  134. LPTSTR *ArgsArray
  135. )
  136. /*++
  137. Routine Description:
  138. This is the main routine of the Workstation Service which registers
  139. itself as an RPC server and notifies the Service Controller of the
  140. Workstation service control entry point.
  141. After the workstation is started, this thread is used (since it's
  142. otherwise unused) to watch for changes to the registry.
  143. Arguments:
  144. NumArgs - Supplies the number of strings specified in ArgsArray.
  145. ArgsArray - Supplies string arguments that are specified in the
  146. StartService API call. This parameter is ignored by the
  147. Workstation service.
  148. Return Value:
  149. None.
  150. --*/
  151. {
  152. NET_API_STATUS ApiStatus;
  153. UNREFERENCED_PARAMETER(NumArgs);
  154. UNREFERENCED_PARAMETER(ArgsArray);
  155. //
  156. // Make sure svchost.exe gave us the global data
  157. //
  158. ASSERT(WsLmsvcsGlobalData != NULL);
  159. WsInitState = 0;
  160. WsIsTerminating=FALSE;
  161. WsLUIDDeviceMapsEnabled=FALSE;
  162. WsWorkerCriticalSectionInitialized = FALSE;
  163. RegNotifyInfo.NotifyEventHandle = NULL;
  164. RegNotifyInfo.Timeout = 0;
  165. RegNotifyInfo.WorkItemHandle = NULL;
  166. RegNotifyInfo.RegistryHandle = NULL;
  167. //
  168. // Initialize the workstation.
  169. //
  170. if ((ApiStatus = WsInitializeWorkstation(&WsInitState)) != NERR_Success) {
  171. DbgPrint("WKSSVC failed to initialize workstation %x\n",WsInitState);
  172. goto Cleanup;
  173. }
  174. //
  175. // Set up to wait for registry change or terminate event.
  176. //
  177. ApiStatus = NetpAllocConfigName(
  178. SERVICES_ACTIVE_DATABASE,
  179. SERVICE_WORKSTATION,
  180. NULL, // default area ("Parameters")
  181. &RegPathToWatch
  182. );
  183. if (ApiStatus != NERR_Success) {
  184. goto Cleanup;
  185. }
  186. NetpAssert(RegPathToWatch != NULL && *RegPathToWatch != TCHAR_EOS);
  187. ApiStatus = (NET_API_STATUS) RegOpenKeyEx(
  188. HKEY_LOCAL_MACHINE, // hKey
  189. RegPathToWatch, // lpSubKey
  190. 0L, // ulOptions (reserved)
  191. KEY_READ | KEY_NOTIFY, // desired access
  192. &ConfigHandle // Newly Opened Key Handle
  193. );
  194. if (ApiStatus != NO_ERROR) {
  195. goto Cleanup;
  196. }
  197. ConfigHandleOpened = TRUE;
  198. RegistryChangeEvent = CreateEvent(
  199. NULL, // no security descriptor
  200. FALSE, // use automatic reset
  201. FALSE, // initial state: not signalled
  202. NULL // no name
  203. );
  204. if (RegistryChangeEvent == NULL) {
  205. ApiStatus = (NET_API_STATUS) GetLastError();
  206. goto Cleanup;
  207. }
  208. ApiStatus = RtlRegisterWait(
  209. &TerminateWorkItem, // work item handle
  210. WsGlobalData.TerminateNowEvent, // wait handle
  211. WsTerminationNotify, // callback fcn
  212. NULL, // parameter
  213. INFINITE, // timeout
  214. WT_EXECUTEONLYONCE | // flags
  215. WT_EXECUTELONGFUNCTION);
  216. if (!NT_SUCCESS(ApiStatus)) {
  217. ApiStatus = RtlNtStatusToDosError(ApiStatus);
  218. goto Cleanup;
  219. }
  220. //
  221. // Setup to monitor registry changes.
  222. //
  223. RegNotifyInfo.NotifyEventHandle = RegistryChangeEvent;
  224. RegNotifyInfo.Timeout = INFINITE;
  225. RegNotifyInfo.WorkItemHandle = NULL;
  226. RegNotifyInfo.RegistryHandle = ConfigHandle;
  227. EnterCriticalSection(&WsWorkerCriticalSection);
  228. if (!WsReInitChangeNotify(&RegNotifyInfo)) {
  229. ApiStatus = GetLastError();
  230. RtlDeregisterWait(TerminateWorkItem);
  231. TerminateWorkItem = NULL;
  232. LeaveCriticalSection(&WsWorkerCriticalSection);
  233. goto Cleanup;
  234. }
  235. LeaveCriticalSection(&WsWorkerCriticalSection);
  236. //
  237. // We are done with starting the Workstation service. Tell Service
  238. // Controller our new status.
  239. //
  240. WsGlobalData.Status.dwCurrentState = SERVICE_RUNNING;
  241. WsGlobalData.Status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
  242. SERVICE_ACCEPT_PAUSE_CONTINUE |
  243. SERVICE_ACCEPT_SHUTDOWN;
  244. WsGlobalData.Status.dwCheckPoint = 0;
  245. WsGlobalData.Status.dwWaitHint = 0;
  246. if ((ApiStatus = WsUpdateStatus()) != NERR_Success) {
  247. WsInitializeStatusError = ApiStatus;
  248. DbgPrint("WKSSVC failed with WsUpdateStatus %x\n",ApiStatus);
  249. goto Cleanup;
  250. }
  251. //
  252. // This thread has done all that it can do. So we can return it
  253. // to the service controller.
  254. //
  255. return;
  256. Cleanup:
  257. DbgPrint("WKSSVC ServiceMain returned with %x\n",ApiStatus);
  258. WsTerminationNotify(NULL, NO_ERROR);
  259. return;
  260. }
  261. BOOL
  262. WsReInitChangeNotify(
  263. PREG_NOTIFY_INFO pNotifyInfo
  264. )
  265. /*++
  266. Routine Description:
  267. NOTE: This function should only be called when in the
  268. WsWorkerCriticalSection.
  269. Arguments:
  270. Return Value:
  271. --*/
  272. {
  273. BOOL bStat = TRUE;
  274. NTSTATUS Status;
  275. Status = RegNotifyChangeKeyValue (ConfigHandle,
  276. TRUE, // watch a subtree
  277. REG_NOTIFY_CHANGE_LAST_SET,
  278. RegistryChangeEvent,
  279. TRUE // async call
  280. );
  281. if (!NT_SUCCESS(Status)) {
  282. NetpKdPrint((PREFIX_WKSTA "Couldn't Initialize Registry Notify %d\n",
  283. RtlNtStatusToDosError(Status)));
  284. bStat = FALSE;
  285. goto CleanExit;
  286. }
  287. //
  288. // Add the work item that is to be called when the
  289. // RegistryChangeEvent is signalled.
  290. //
  291. Status = RtlRegisterWait(
  292. &pNotifyInfo->WorkItemHandle,
  293. pNotifyInfo->NotifyEventHandle,
  294. WsRegistryNotify,
  295. (PVOID)pNotifyInfo,
  296. pNotifyInfo->Timeout,
  297. WT_EXECUTEONLYONCE | WT_EXECUTEINPERSISTENTIOTHREAD);
  298. if (!NT_SUCCESS(Status)) {
  299. NetpKdPrint((PREFIX_WKSTA "Couldn't add Reg Notify work item\n"));
  300. bStat = FALSE;
  301. }
  302. CleanExit:
  303. if (bStat) {
  304. if (WsNumWorkerThreads == 0) {
  305. WsNumWorkerThreads++;
  306. }
  307. }
  308. else {
  309. if (WsNumWorkerThreads == 1) {
  310. WsNumWorkerThreads--;
  311. }
  312. }
  313. return(bStat);
  314. }
  315. DWORD
  316. WsRegistryNotify(
  317. LPVOID pParms,
  318. BOOLEAN fWaitStatus
  319. )
  320. /*++
  321. Routine Description:
  322. Handles Workstation Registry Notification. This function is called by a
  323. thread pool Worker thread when the event used for registry notification is
  324. signaled.
  325. Arguments:
  326. Return Value:
  327. --*/
  328. {
  329. NET_API_STATUS ApiStatus;
  330. PREG_NOTIFY_INFO pNotifyinfo=(PREG_NOTIFY_INFO)pParms;
  331. NET_CONFIG_HANDLE NetConfigHandle;
  332. UNREFERENCED_PARAMETER(fWaitStatus);
  333. //
  334. // The NT thread pool requires explicit work item deregistration,
  335. // even if we specified the WT_EXECUTEONLYONCE flag
  336. //
  337. RtlDeregisterWait(pNotifyinfo->WorkItemHandle);
  338. EnterCriticalSection(&WsWorkerCriticalSection);
  339. if (WsIsTerminating) {
  340. WsNumWorkerThreads--;
  341. SetEvent(WsGlobalData.TerminateNowEvent);
  342. LeaveCriticalSection(&WsWorkerCriticalSection);
  343. return(NO_ERROR);
  344. }
  345. //
  346. // Serialize write access to config information
  347. //
  348. if (RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
  349. //
  350. // Update the redir fields based on change notify.
  351. // WsUpdateWkstaToMatchRegistry expects a NET_CONFIG_HANDLE
  352. // handle, so we conjure up one from the HKEY handle.
  353. //
  354. NetConfigHandle.WinRegKey = ConfigHandle;
  355. WsUpdateWkstaToMatchRegistry(&NetConfigHandle, FALSE);
  356. ApiStatus = WsUpdateRedirToMatchWksta(
  357. PARMNUM_ALL,
  358. NULL
  359. );
  360. // NetpAssert( ApiStatus == NO_ERROR );
  361. RtlReleaseResource(&WsInfo.ConfigResource);
  362. }
  363. if (!WsReInitChangeNotify(&RegNotifyInfo)) {
  364. //
  365. // If we can't add the work item, then we just won't
  366. // listen for registry changes. There's not a whole
  367. // lot we can do here.
  368. //
  369. ApiStatus = GetLastError();
  370. }
  371. LeaveCriticalSection(&WsWorkerCriticalSection);
  372. return(NO_ERROR);
  373. }
  374. VOID
  375. WsTerminationNotify(
  376. LPVOID pParms,
  377. BOOLEAN fWaitStatus
  378. )
  379. /*++
  380. Routine Description:
  381. This function gets called by a services worker thread when the
  382. termination event gets signaled.
  383. Arguments:
  384. Return Value:
  385. --*/
  386. {
  387. UNREFERENCED_PARAMETER(pParms);
  388. UNREFERENCED_PARAMETER(fWaitStatus);
  389. IF_DEBUG(MAIN) {
  390. NetpKdPrint((PREFIX_WKSTA "WORKSTATION_main: cleaning up, "
  391. "api status.\n"));
  392. }
  393. //
  394. // The NT thread pool requires explicit work item deregistration,
  395. // even if we specified the WT_EXECUTEONLYONCE flag
  396. //
  397. if (TerminateWorkItem != NULL) {
  398. RtlDeregisterWait(TerminateWorkItem);
  399. TerminateWorkItem = NULL;
  400. }
  401. if ( !WsWorkerCriticalSectionInitialized ) {
  402. // We try to initialize this at the very beginning.
  403. // So, if that failed, then nothing else has been done.
  404. // So, just exit.
  405. return;
  406. }
  407. EnterCriticalSection(&WsWorkerCriticalSection);
  408. WsIsTerminating = TRUE;
  409. //
  410. // Must close winreg handle (which turns off notify) before event handle.
  411. // Closing the regkey handle generates a change notify event!
  412. //
  413. if (ConfigHandleOpened) {
  414. (VOID) RegCloseKey(ConfigHandle);
  415. ConfigHandleOpened = FALSE;
  416. #if DBG
  417. //
  418. // Workaround for a benign winreg assertion caused by us
  419. // closing the RegistryChangeEvent handle which it wants
  420. // to signal.
  421. //
  422. Sleep(2000);
  423. #endif
  424. }
  425. if (RegPathToWatch != NULL) {
  426. (VOID) NetApiBufferFree(RegPathToWatch);
  427. RegPathToWatch = NULL;
  428. }
  429. if ((RegistryChangeEvent != NULL) && (WsNumWorkerThreads != 0)) {
  430. //
  431. // There is still a RegistryNotify Work Item in the system, we
  432. // will attempt to remove it by setting the event to wake it up.
  433. //
  434. ResetEvent(WsGlobalData.TerminateNowEvent);
  435. LeaveCriticalSection(&WsWorkerCriticalSection);
  436. SetEvent(RegistryChangeEvent);
  437. //
  438. // Wait until the WsRegistryNotify Thread is finished.
  439. // We will give it 60 seconds. If the thread isn't
  440. // finished in that time frame, we will go on anyway.
  441. //
  442. WaitForSingleObject(
  443. WsGlobalData.TerminateNowEvent,
  444. 60000);
  445. if (WsNumWorkerThreads != 0) {
  446. NetpKdPrint((PREFIX_WKSTA "WsTerminationNotify: "
  447. "Registry Notification thread didn't terminate\n"));
  448. }
  449. EnterCriticalSection(&WsWorkerCriticalSection);
  450. }
  451. if ( RegistryChangeEvent != NULL ) {
  452. (VOID) CloseHandle(RegistryChangeEvent);
  453. RegistryChangeEvent = NULL;
  454. }
  455. //
  456. // Shutting down
  457. //
  458. // NOTE: We must synchronize with the RegistryNotification Thread.
  459. //
  460. WsShutdownWorkstation(
  461. WsInitializeStatusError,
  462. WsInitState
  463. );
  464. WsIsTerminating = FALSE;
  465. LeaveCriticalSection(&WsWorkerCriticalSection);
  466. DeleteCriticalSection(&WsWorkerCriticalSection);
  467. WsWorkerCriticalSectionInitialized = FALSE;
  468. return;
  469. }
  470. STATIC
  471. NET_API_STATUS
  472. WsInitializeWorkstation(
  473. OUT LPDWORD WsInitState
  474. )
  475. /*++
  476. Routine Description:
  477. This function initializes the Workstation service.
  478. Arguments:
  479. WsInitState - Returns a flag to indicate how far we got with initializing
  480. the Workstation service before an error occured.
  481. Return Value:
  482. NET_API_STATUS - NERR_Success or reason for failure.
  483. --*/
  484. {
  485. NET_API_STATUS status;
  486. RPC_STATUS rpcStatus;
  487. int i;
  488. //
  489. // Initialize all the status fields so that subsequent calls to
  490. // SetServiceStatus need to only update fields that changed.
  491. //
  492. WsGlobalData.Status.dwServiceType = SERVICE_WIN32;
  493. WsGlobalData.Status.dwCurrentState = SERVICE_START_PENDING;
  494. WsGlobalData.Status.dwControlsAccepted = 0;
  495. WsGlobalData.Status.dwCheckPoint = 1;
  496. WsGlobalData.Status.dwWaitHint = WS_WAIT_HINT_TIME;
  497. SET_SERVICE_EXITCODE(
  498. NO_ERROR,
  499. WsGlobalData.Status.dwWin32ExitCode,
  500. WsGlobalData.Status.dwServiceSpecificExitCode
  501. );
  502. //
  503. // Initialize workstation to receive service requests by registering the
  504. // control handler.
  505. //
  506. if ((WsGlobalData.StatusHandle = RegisterServiceCtrlHandler(
  507. SERVICE_WORKSTATION,
  508. WkstaControlHandler
  509. )) == (SERVICE_STATUS_HANDLE) 0) {
  510. status = GetLastError();
  511. WsInitializeStatusError = status;
  512. DbgPrint("WKSSVC failed with RegisterServiceCtrlHandler %x\n",status);
  513. return status;
  514. }
  515. //
  516. // Notify the Service Controller for the first time that we are alive
  517. // and we are start pending
  518. //
  519. if ((status = WsUpdateStatus()) != NERR_Success) {
  520. WsInitializeStatusError = status;
  521. DbgPrint("WKSSVC failed with WsUpdateStatus %x\n",status);
  522. return status;
  523. }
  524. try {
  525. InitializeCriticalSection(&WsWorkerCriticalSection);
  526. } except( EXCEPTION_EXECUTE_HANDLER ) {
  527. status = ERROR_NOT_ENOUGH_MEMORY;
  528. return status;
  529. }
  530. WsWorkerCriticalSectionInitialized = TRUE;
  531. //
  532. // Initialize NetJoin logging
  533. //
  534. NetpInitializeLogFile();
  535. //
  536. // Initialize the resource for serializing access to configuration
  537. // information.
  538. //
  539. // This must be done before the redir is initialized
  540. try {
  541. RtlInitializeResource(&WsInfo.ConfigResource);
  542. } except(EXCEPTION_EXECUTE_HANDLER) {
  543. return RtlNtStatusToDosError(GetExceptionCode());
  544. }
  545. (*WsInitState) |= WS_CONFIG_RESOURCE_INITIALIZED;
  546. //
  547. // Create an event which is used by the service control handler to notify
  548. // the Workstation service that it is time to terminate.
  549. //
  550. if ((WsGlobalData.TerminateNowEvent =
  551. CreateEvent(
  552. NULL, // Event attributes
  553. TRUE, // Event must be manually reset
  554. FALSE,
  555. NULL // Initial state not signalled
  556. )) == NULL) {
  557. status = GetLastError();
  558. WsInitializeStatusError = status;
  559. return status;
  560. }
  561. (*WsInitState) |= WS_TERMINATE_EVENT_CREATED;
  562. //
  563. // Initialize the workstation as a logon process with LSA, and
  564. // get the MS V 1.0 authentication package ID.
  565. //
  566. if ((status = WsInitializeLsa()) != NERR_Success) {
  567. WsInitializeStatusError = status;
  568. DbgPrint("WKSSVC failed with WsInitializeLsa %x\n",status);
  569. return status;
  570. }
  571. (*WsInitState) |= WS_LSA_INITIALIZED;
  572. //
  573. // Read the configuration information to initialize the redirector and
  574. // datagram receiver
  575. //
  576. if ((status = WsInitializeRedirector()) != NERR_Success) {
  577. WsInitializeStatusError = status;
  578. DbgPrint("WKSSVC failed with WsInitializeRedirector %x\n",status);
  579. return status;
  580. }
  581. (*WsInitState) |= WS_DEVICES_INITIALIZED;
  582. //
  583. // Service install still pending. Update checkpoint counter and the
  584. // status with the Service Controller.
  585. //
  586. (WsGlobalData.Status.dwCheckPoint)++;
  587. (void) WsUpdateStatus();
  588. //
  589. // Bind to transports
  590. //
  591. if ((status = WsBindToTransports()) != NERR_Success) {
  592. WsInitializeStatusError = status;
  593. DbgPrint("WKSSVC failed with WsBindToTransports %x\n",status);
  594. return status;
  595. }
  596. //
  597. // Service install still pending. Update checkpoint counter and the
  598. // status with the Service Controller.
  599. //
  600. (WsGlobalData.Status.dwCheckPoint)++;
  601. (void) WsUpdateStatus();
  602. //
  603. // Add domain names.
  604. //
  605. if ((status = WsAddDomains()) != NERR_Success) {
  606. WsInitializeStatusError = status;
  607. DbgPrint("WKSSVC failed with WsAddDomains %x\n",status);
  608. return status;
  609. }
  610. //
  611. // Service start still pending. Update checkpoint counter and the
  612. // status with the Service Controller.
  613. //
  614. (WsGlobalData.Status.dwCheckPoint)++;
  615. (void) WsUpdateStatus();
  616. //
  617. // Create Workstation service API data structures
  618. //
  619. if ((status = WsCreateApiStructures(WsInitState)) != NERR_Success) {
  620. WsInitializeStatusError = status;
  621. DbgPrint("WKSSVC failed with WsCreateApiStructures %x\n",status);
  622. return status;
  623. }
  624. //
  625. // Initialize the workstation service to receive RPC requests
  626. //
  627. // NOTE: Now all RPC servers in services.exe share the same pipe name.
  628. // However, in order to support communication with version 1.0 of WinNt,
  629. // it is necessary for the Client Pipe name to remain the same as
  630. // it was in version 1.0. Mapping to the new name is performed in
  631. // the Named Pipe File System code.
  632. //
  633. //
  634. // Register with our protseq and the endpoint we expect to get called on
  635. //
  636. rpcStatus = RpcServerUseProtseqEpW(
  637. L"ncacn_np",
  638. RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
  639. L"\\PIPE\\wkssvc",
  640. NULL
  641. );
  642. // duplicate endpoint is ok
  643. if ( rpcStatus == RPC_S_DUPLICATE_ENDPOINT ) {
  644. rpcStatus = RPC_S_OK;
  645. }
  646. //
  647. // Register our interface as an autolisten interface, and allow clients to call us,
  648. // even unauthenticated ones. We do the access check separately on a per function
  649. // basis when that function is called.
  650. // Some functions can be called by guest / anonymous
  651. //
  652. if (rpcStatus == RPC_S_OK) {
  653. rpcStatus = RpcServerRegisterIfEx(wkssvc_ServerIfHandle,
  654. NULL,
  655. NULL,
  656. RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH ,
  657. RPC_C_LISTEN_MAX_CALLS_DEFAULT,
  658. WsRpcSecurityCallback
  659. );
  660. }
  661. status = (rpcStatus == RPC_S_OK) ? NERR_Success : rpcStatus;
  662. if (status != NERR_Success ) {
  663. WsInitializeStatusError = status;
  664. DbgPrint("WKSSVC failed with StartRpcServer %x\n",status);
  665. return status;
  666. }
  667. (*WsInitState) |= WS_RPC_SERVER_STARTED;
  668. //
  669. // Lastly, we create a thread to communicate with the
  670. // Dfs-enabled MUP driver.
  671. //
  672. if ((status = WsInitializeDfs()) != NERR_Success) {
  673. WsInitializeStatusError = status;
  674. DbgPrint("WKSSVC failed with WsInitializeDfs %x\n",status);
  675. return status;
  676. }
  677. (*WsInitState) |= WS_DFS_THREAD_STARTED;
  678. WsLUIDDeviceMapsEnabled = WsGetLUIDDeviceMapsEnabled();
  679. (void) I_ScSetServiceBits(
  680. WsGlobalData.StatusHandle,
  681. SV_TYPE_WORKSTATION,
  682. TRUE,
  683. TRUE,
  684. NULL
  685. );
  686. IF_DEBUG(MAIN) {
  687. NetpKdPrint(("[Wksta] Successful Initialization\n"));
  688. }
  689. return NERR_Success;
  690. }
  691. VOID
  692. WsShutdownWorkstation(
  693. IN NET_API_STATUS ErrorCode,
  694. IN DWORD WsInitState
  695. )
  696. /*++
  697. Routine Description:
  698. This function shuts down the Workstation service.
  699. Arguments:
  700. ErrorCode - Supplies the error code of the failure
  701. WsInitState - Supplies a flag to indicate how far we got with initializing
  702. the Workstation service before an error occured, thus the amount of
  703. clean up needed.
  704. Return Value:
  705. None.
  706. --*/
  707. {
  708. NET_API_STATUS status = NERR_Success;
  709. //
  710. // Service stop still pending. Update checkpoint counter and the
  711. // status with the Service Controller.
  712. //
  713. (WsGlobalData.Status.dwCheckPoint)++;
  714. (void) WsUpdateStatus();
  715. if (WsInitState & WS_DFS_THREAD_STARTED) {
  716. //
  717. // Stop the Dfs thread
  718. //
  719. WsShutdownDfs();
  720. }
  721. if (WsInitState & WS_RPC_SERVER_STARTED) {
  722. //
  723. // Stop the RPC server
  724. //
  725. status = RpcServerUnregisterIf( wkssvc_ServerIfHandle,
  726. NULL,
  727. 1
  728. );
  729. if ( status != NERR_Success ) {
  730. DbgPrint("WKSSVC failed to unregister rpc interface with status %x\n",status);
  731. }
  732. }
  733. if (WsInitState & WS_API_STRUCTURES_CREATED) {
  734. //
  735. // Destroy data structures created for Workstation APIs
  736. //
  737. WsDestroyApiStructures(WsInitState);
  738. }
  739. WsShutdownMessageSend();
  740. //
  741. // Don't need to ask redirector to unbind from its transports when
  742. // cleaning up because the redirector will tear down the bindings when
  743. // it stops.
  744. //
  745. if (WsInitState & WS_DEVICES_INITIALIZED) {
  746. //
  747. // Shut down the redirector and datagram receiver
  748. //
  749. status = WsShutdownRedirector();
  750. }
  751. //
  752. // Delete resource for serializing access to config information
  753. // This must be done only after the redir is shutdown
  754. // We do this here, (the Init is done in WsInitializeWorkstation routine above)
  755. // to avoid putting additional synchronization on delete
  756. // Otherwise we get into the situation where, the redir is shutting down and
  757. // it deletes the resource while someone has acquired it, causing bad things.
  758. //
  759. if ( WsInitState & WS_CONFIG_RESOURCE_INITIALIZED ) {
  760. RtlDeleteResource(&WsInfo.ConfigResource);
  761. }
  762. if (WsInitState & WS_LSA_INITIALIZED) {
  763. //
  764. // Deregister workstation as logon process
  765. //
  766. WsShutdownLsa();
  767. }
  768. if (WsInitState & WS_TERMINATE_EVENT_CREATED) {
  769. //
  770. // Close handle to termination event
  771. //
  772. CloseHandle(WsGlobalData.TerminateNowEvent);
  773. }
  774. //
  775. // Shut down NetJoin logging
  776. //
  777. NetpShutdownLogFile();
  778. I_ScSetServiceBits(
  779. WsGlobalData.StatusHandle,
  780. SV_TYPE_WORKSTATION,
  781. FALSE,
  782. TRUE,
  783. NULL
  784. );
  785. //
  786. // We are done with cleaning up. Tell Service Controller that we are
  787. // stopped.
  788. //
  789. WsGlobalData.Status.dwCurrentState = SERVICE_STOPPED;
  790. WsGlobalData.Status.dwControlsAccepted = 0;
  791. if ((ErrorCode == NERR_Success) &&
  792. (status == ERROR_REDIRECTOR_HAS_OPEN_HANDLES)) {
  793. ErrorCode = status;
  794. }
  795. SET_SERVICE_EXITCODE(
  796. ErrorCode,
  797. WsGlobalData.Status.dwWin32ExitCode,
  798. WsGlobalData.Status.dwServiceSpecificExitCode
  799. );
  800. WsGlobalData.Status.dwCheckPoint = 0;
  801. WsGlobalData.Status.dwWaitHint = 0;
  802. (void) WsUpdateStatus();
  803. }
  804. NET_API_STATUS
  805. WsUpdateStatus(
  806. VOID
  807. )
  808. /*++
  809. Routine Description:
  810. This function updates the Workstation service status with the Service
  811. Controller.
  812. Arguments:
  813. None.
  814. Return Value:
  815. NET_API_STATUS - NERR_Success or reason for failure.
  816. --*/
  817. {
  818. NET_API_STATUS status = NERR_Success;
  819. if (WsGlobalData.StatusHandle == (SERVICE_STATUS_HANDLE) 0) {
  820. NetpKdPrint((
  821. "[Wksta] Cannot call SetServiceStatus, no status handle.\n"
  822. ));
  823. return ERROR_INVALID_HANDLE;
  824. }
  825. if (! SetServiceStatus(WsGlobalData.StatusHandle, &WsGlobalData.Status)) {
  826. status = GetLastError();
  827. IF_DEBUG(MAIN) {
  828. NetpKdPrint(("[Wksta] SetServiceStatus error %lu\n", status));
  829. }
  830. }
  831. return status;
  832. }
  833. STATIC
  834. NET_API_STATUS
  835. WsCreateApiStructures(
  836. IN OUT LPDWORD WsInitState
  837. )
  838. /*++
  839. Routine Description:
  840. This function creates and initializes all the data structures required
  841. for the Workstation APIs.
  842. Arguments:
  843. WsInitState - Returns the supplied flag of how far we got in the
  844. Workstation service initialization process.
  845. Return Value:
  846. NET_API_STATUS - NERR_Success or reason for failure.
  847. --*/
  848. {
  849. NET_API_STATUS status;
  850. //
  851. // Create workstation security objects
  852. //
  853. if ((status = WsCreateWkstaObjects()) != NERR_Success) {
  854. return status;
  855. }
  856. (*WsInitState) |= WS_SECURITY_OBJECTS_CREATED;
  857. //
  858. // Create Use Table
  859. //
  860. if ((status = WsInitUseStructures()) != NERR_Success) {
  861. return status;
  862. }
  863. (*WsInitState) |= WS_USE_TABLE_CREATED;
  864. return NERR_Success;
  865. }
  866. STATIC
  867. VOID
  868. WsDestroyApiStructures(
  869. IN DWORD WsInitState
  870. )
  871. /*++
  872. Routine Description:
  873. This function destroys the data structures created for the Workstation
  874. APIs.
  875. Arguments:
  876. WsInitState - Supplies a flag which tells us what API structures
  877. were created in the initialization process and now have to be
  878. cleaned up.
  879. Return Value:
  880. None.
  881. --*/
  882. {
  883. if (WsInitState & WS_USE_TABLE_CREATED) {
  884. //
  885. // Destroy Use Table
  886. //
  887. WsDestroyUseStructures();
  888. }
  889. if (WsInitState & WS_SECURITY_OBJECTS_CREATED) {
  890. //
  891. // Destroy workstation security objects
  892. //
  893. WsDestroyWkstaObjects();
  894. }
  895. }
  896. VOID
  897. WkstaControlHandler(
  898. IN DWORD Opcode
  899. )
  900. /*++
  901. Routine Description:
  902. This is the service control handler of the Workstation service.
  903. Arguments:
  904. Opcode - Supplies a value which specifies the action for the Workstation
  905. service to perform.
  906. Arg - Supplies a value which tells a service specifically what to do
  907. for an operation specified by Opcode.
  908. Return Value:
  909. None.
  910. --*/
  911. {
  912. IF_DEBUG(MAIN) {
  913. NetpKdPrint(("[Wksta] In Control Handler\n"));
  914. }
  915. switch (Opcode) {
  916. case SERVICE_CONTROL_PAUSE:
  917. //
  918. // Pause redirection of print and comm devices
  919. //
  920. WsPauseOrContinueRedirection(
  921. PauseRedirection
  922. );
  923. break;
  924. case SERVICE_CONTROL_CONTINUE:
  925. //
  926. // Resume redirection of print and comm devices
  927. //
  928. WsPauseOrContinueRedirection(
  929. ContinueRedirection
  930. );
  931. break;
  932. case SERVICE_CONTROL_SHUTDOWN:
  933. //
  934. // Lack of break is intentional!
  935. //
  936. case SERVICE_CONTROL_STOP:
  937. if (WsGlobalData.Status.dwCurrentState != SERVICE_STOP_PENDING) {
  938. IF_DEBUG(MAIN) {
  939. NetpKdPrint(("[Wksta] Stopping workstation...\n"));
  940. }
  941. WsGlobalData.Status.dwCurrentState = SERVICE_STOP_PENDING;
  942. WsGlobalData.Status.dwCheckPoint = 1;
  943. WsGlobalData.Status.dwWaitHint = WS_WAIT_HINT_TIME;
  944. //
  945. // Send the status response.
  946. //
  947. (void) WsUpdateStatus();
  948. if (! SetEvent(WsGlobalData.TerminateNowEvent)) {
  949. //
  950. // Problem with setting event to terminate Workstation
  951. // service.
  952. //
  953. NetpKdPrint(("[Wksta] Error setting TerminateNowEvent "
  954. FORMAT_API_STATUS "\n", GetLastError()));
  955. NetpAssert(FALSE);
  956. }
  957. return;
  958. }
  959. break;
  960. case SERVICE_CONTROL_INTERROGATE:
  961. break;
  962. default:
  963. IF_DEBUG(MAIN) {
  964. NetpKdPrint(("Unknown workstation opcode " FORMAT_HEX_DWORD
  965. "\n", Opcode));
  966. }
  967. }
  968. //
  969. // Send the status response.
  970. //
  971. (void) WsUpdateStatus();
  972. }
  973. STATIC
  974. BOOL
  975. WsGetLUIDDeviceMapsEnabled(
  976. VOID
  977. )
  978. /*++
  979. Routine Description:
  980. This function calls NtQueryInformationProcess() to determine if
  981. LUID device maps are enabled
  982. Arguments:
  983. none
  984. Return Value:
  985. TRUE - LUID device maps are enabled
  986. FALSE - LUID device maps are disabled
  987. --*/
  988. {
  989. NTSTATUS Status;
  990. ULONG LUIDDeviceMapsEnabled;
  991. BOOL Result;
  992. Status = NtQueryInformationProcess( NtCurrentProcess(),
  993. ProcessLUIDDeviceMapsEnabled,
  994. &LUIDDeviceMapsEnabled,
  995. sizeof(LUIDDeviceMapsEnabled),
  996. NULL
  997. );
  998. if (!NT_SUCCESS( Status )) {
  999. IF_DEBUG(MAIN) {
  1000. NetpKdPrint(("[Wksta] NtQueryInformationProcess(WsLUIDDeviceMapsEnabled) failed "
  1001. "FORMAT_API_STATUS" "\n",Status));
  1002. }
  1003. Result = FALSE;
  1004. }
  1005. else {
  1006. Result = (LUIDDeviceMapsEnabled != 0);
  1007. }
  1008. return( Result );
  1009. }
  1010. RPC_STATUS WsRpcSecurityCallback(
  1011. IN RPC_IF_HANDLE *Interface,
  1012. IN void *Context
  1013. )
  1014. /*++
  1015. Routine description:
  1016. RPC security callback - called before any call is passed on to a function.
  1017. Check the protseq to see whether the client is using the protseq we expect - named pipes.
  1018. Lifted from NwRpcSecurityCallback
  1019. Return value:
  1020. RPC_S_ACCESS_DENIED if the protseq doesnt match named pipes,
  1021. RPC_S_OK otherwise.
  1022. --*/
  1023. {
  1024. RPC_STATUS Status;
  1025. RPC_BINDING_HANDLE ServerIfHandle;
  1026. LPWSTR binding = NULL;
  1027. LPWSTR protseq = NULL;
  1028. Status = RpcBindingServerFromClient((RPC_IF_HANDLE)Context, &ServerIfHandle);
  1029. if (Status != RPC_S_OK)
  1030. {
  1031. return (RPC_S_ACCESS_DENIED);
  1032. }
  1033. Status = RpcBindingToStringBinding(ServerIfHandle, &binding);
  1034. if (Status != RPC_S_OK)
  1035. {
  1036. Status = RPC_S_ACCESS_DENIED;
  1037. goto CleanUp;
  1038. }
  1039. Status = RpcStringBindingParse(binding, NULL, &protseq, NULL, NULL, NULL);
  1040. if (Status != RPC_S_OK)
  1041. {
  1042. Status = RPC_S_ACCESS_DENIED;
  1043. }
  1044. else
  1045. {
  1046. if (lstrcmp(protseq, L"ncacn_np") != 0)
  1047. Status = RPC_S_ACCESS_DENIED;
  1048. }
  1049. CleanUp:
  1050. RpcBindingFree(&ServerIfHandle);
  1051. if ( binding )
  1052. {
  1053. RpcStringFreeW( &binding );
  1054. }
  1055. if ( protseq )
  1056. {
  1057. RpcStringFreeW( &protseq );
  1058. }
  1059. return Status;
  1060. }