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.

536 lines
15 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. srventry.c
  5. Abstract:
  6. This module contains the main entry for the User-mode Plug-and-Play Service.
  7. It also contains the service control handler and service status update
  8. routines.
  9. Author:
  10. Paula Tomlinson (paulat) 6-8-1995
  11. Environment:
  12. User-mode only.
  13. Revision History:
  14. 8-June-1995 paulat
  15. Creation and initial implementation.
  16. --*/
  17. //
  18. // includes
  19. //
  20. #include "precomp.h"
  21. #pragma hdrstop
  22. #include "umpnpi.h"
  23. #include <svcsp.h>
  24. //
  25. // private prototypes
  26. //
  27. DWORD
  28. PnPControlHandlerEx(
  29. IN DWORD dwControl,
  30. IN DWORD dwEventType,
  31. IN LPVOID lpEventData,
  32. IN LPVOID lpContext
  33. );
  34. VOID
  35. PnPServiceStatusUpdate(
  36. SERVICE_STATUS_HANDLE hSvcHandle,
  37. DWORD dwState,
  38. DWORD dwCheckPoint,
  39. DWORD dwExitCode
  40. );
  41. RPC_STATUS
  42. CALLBACK
  43. PnPRpcIfCallback(
  44. RPC_IF_HANDLE* Interface,
  45. void* Context
  46. );
  47. //
  48. // global data
  49. //
  50. PSVCS_GLOBAL_DATA PnPGlobalData = NULL;
  51. HANDLE PnPGlobalSvcRefHandle = NULL;
  52. DWORD CurrentServiceState = SERVICE_START_PENDING;
  53. SERVICE_STATUS_HANDLE hSvcHandle = 0;
  54. VOID
  55. SvcEntry_PlugPlay(
  56. DWORD argc,
  57. LPWSTR argv[],
  58. PSVCS_GLOBAL_DATA SvcsGlobalData,
  59. HANDLE SvcRefHandle
  60. )
  61. /*++
  62. Routine Description:
  63. This is the main routine for the User-mode Plug-and-Play Service. It
  64. registers itself as an RPC server and notifies the Service Controller
  65. of the PNP service control entry point.
  66. Arguments:
  67. argc, argv - Command-line arguments, not used.
  68. SvcsGlobalData - Global data for services running in services.exe that
  69. contains function entry points and pipe name for
  70. establishing an RPC server interface for this service.
  71. SvcRefHandle - Service reference handle, not used.
  72. Return Value:
  73. None.
  74. Note:
  75. None.
  76. --*/
  77. {
  78. RPC_STATUS RpcStatus;
  79. HANDLE hThread;
  80. DWORD ThreadID;
  81. UNREFERENCED_PARAMETER(argc);
  82. UNREFERENCED_PARAMETER(argv);
  83. //
  84. // Save the global data and service reference handle in global variables
  85. //
  86. PnPGlobalSvcRefHandle = SvcRefHandle;
  87. PnPGlobalData = SvcsGlobalData;
  88. //
  89. // Register our service ctrl handler
  90. //
  91. if ((hSvcHandle = RegisterServiceCtrlHandlerEx(L"PlugPlay",
  92. (LPHANDLER_FUNCTION_EX)PnPControlHandlerEx,
  93. NULL)) == 0) {
  94. KdPrintEx((DPFLTR_PNPMGR_ID,
  95. DBGF_ERRORS,
  96. "UMPNPMGR: RegisterServiceCtrlHandlerEx failed, error = %d\n",
  97. GetLastError()));
  98. return;
  99. }
  100. //
  101. // Notify Service Controller that we're alive
  102. //
  103. PnPServiceStatusUpdate(hSvcHandle, SERVICE_START_PENDING, 1, 0);
  104. //
  105. // Create the Plug and Play security object, used to determine client access
  106. // to the PlugPlay server APIs. Note that since the security object is used
  107. // by the PNP RPC interface security callback routine, it must be created
  108. // before the PNP RPC interface can be registered, below.
  109. //
  110. if (!CreatePlugPlaySecurityObject()) {
  111. KdPrintEx((DPFLTR_PNPMGR_ID,
  112. DBGF_ERRORS,
  113. "UMPNPMGR: CreatePlugPlayManagerSecurityDescriptor failed!\n"));
  114. return;
  115. }
  116. //
  117. // Notify Service Controller that we're alive
  118. //
  119. PnPServiceStatusUpdate(hSvcHandle, SERVICE_START_PENDING, 2, 0);
  120. //
  121. // Register the PNP RPC interface, and specify a security callback routine
  122. // for the interface. The callback will be called for all methods in the
  123. // interface, before RPC has marshalled any data to the stubs. This allows
  124. // us to reject calls before RPC has allocated any memory from our process,
  125. // preventing possible DOS attacks by unauthorized clients.
  126. //
  127. // Few things to note about how we do this...
  128. //
  129. // First, NOTE that we previously used the RPC start/stop server routines
  130. // provided by SVCS_GLOBAL_DATA StartRpcServer/StopRpcServer, however those
  131. // did not allow for a security callback routine to be registered for the
  132. // interface (RpcServerRegisterIf). Instead, we now register and unregister
  133. // the PNP RPC interface directly ourself, using RpcServerRegisterIfEx.
  134. //
  135. // Also NOTE that technically, we should also register the named pipe
  136. // endpoint and protocol sequence that our CFGMGR32 client uses to access
  137. // this interface ("ntsvcs", "ncacn_np") with the RPC runtime -- BUT because
  138. // we know that our server resides in the services.exe process along with
  139. // the SCM, and that the same endopint and protocol is also used by the SCM,
  140. // we know that it has already been registered for the process long before
  141. // our service is started, and will exist after our service is stopped.
  142. //
  143. // And also NOTE that technically, we should also make sure that the RPC
  144. // runtime is listening within the process when we register our interface,
  145. // and that it remains listening until we have unregistered out interface --
  146. // BUT because we're in services.exe, RPC should already be listening in the
  147. // process for the SCM before and after our service needs it to be (see
  148. // above). We don't really need to start RPC listening ourselves either,
  149. // but there's no harm in registering our interface as "auto-listen", so
  150. // we'll do that anyways.
  151. //
  152. // EXTRA NOTE -- This is really just a safeguard replacement for the
  153. // refcounting for that would ordinarily have been done by the
  154. // SVCS_GLOBAL_DATA StartRpcServer, StopRpcServer routines that the SCM
  155. // and other servers in this process use to register their interfaces.
  156. // These routines refcount the need to listen in the process by counting
  157. // the number of interfaces in the process that have been registered by
  158. // those routines. Since we are now registering the PNP interface ourself
  159. // (outside these routines), no refcounting is done for our interface. By
  160. // registering our interface as "auto-listen", We can make sure that the
  161. // RPC runtime is listening when we register our interface, and that it
  162. // remains listening until it is unregistered (regardless of the listening
  163. // state that is started and stopped on behalf of the other servers in
  164. // this process).
  165. //
  166. // ... Basically, because we share a process with the SCM, the only work we
  167. // really need to do ourselves is register our own interface. If we ever
  168. // move the PlugPlay service outside of the services.exe process, we will
  169. // need to do everything else mentioned above ourselves, as well.
  170. //
  171. //
  172. // Even though we will register our interface as "auto-listen", verify that
  173. // this process is already listening via a previous call to RpcServerListen
  174. // (note that other "auto-listen" interfaces don't count). This tells us
  175. // that the endpoint has already been registered, and RPC is already
  176. // listening, on behalf of some other server.
  177. //
  178. ASSERT(RpcMgmtIsServerListening(NULL) == RPC_S_OK);
  179. //
  180. // Register the PNP RPC interface.
  181. //
  182. RpcStatus =
  183. RpcServerRegisterIfEx(
  184. pnp_ServerIfHandle,
  185. NULL,
  186. NULL,
  187. RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH,
  188. RPC_C_LISTEN_MAX_CALLS_DEFAULT,
  189. PnPRpcIfCallback);
  190. if (RpcStatus != RPC_S_OK) {
  191. KdPrintEx((DPFLTR_PNPMGR_ID,
  192. DBGF_ERRORS,
  193. "UMPNPMGR: RpcServerRegisterIfEx failed with RpcStatus = %d\n",
  194. RpcStatus));
  195. return;
  196. }
  197. //
  198. // Notify Service Controller that we're alive
  199. //
  200. PnPServiceStatusUpdate(hSvcHandle, SERVICE_START_PENDING, 3, 0);
  201. //
  202. // Initialize pnp manager
  203. //
  204. hThread = CreateThread(NULL,
  205. 0,
  206. (LPTHREAD_START_ROUTINE)InitializePnPManager,
  207. NULL,
  208. 0,
  209. &ThreadID);
  210. if (hThread != NULL) {
  211. CloseHandle(hThread);
  212. }
  213. //
  214. // Notify Service Controller that we're now running
  215. //
  216. PnPServiceStatusUpdate(hSvcHandle, SERVICE_RUNNING, 0, 0);
  217. //
  218. // Service initialization is complete.
  219. //
  220. return;
  221. } // SvcEntry_PlugPlay
  222. DWORD
  223. PnPControlHandlerEx(
  224. IN DWORD dwControl,
  225. IN DWORD dwEventType,
  226. IN LPVOID lpEventData,
  227. IN LPVOID lpContext
  228. )
  229. /*++
  230. Routine Description:
  231. This is the service control handler of the Plug-and-Play service.
  232. Arguments:
  233. dwControl - The requested control code.
  234. dwEventType - The type of event that has occurred.
  235. lpEventData - Additional device information, if required.
  236. lpContext - User-defined data, not used.
  237. Return Value:
  238. Returns NO_ERROR if sucessful, otherwise returns an error code describing
  239. the problem.
  240. --*/
  241. {
  242. RPC_STATUS RpcStatus;
  243. UNREFERENCED_PARAMETER(lpContext);
  244. switch (dwControl) {
  245. case SERVICE_CONTROL_STOP:
  246. case SERVICE_CONTROL_SHUTDOWN:
  247. //
  248. // If we aren't already in the middle of a stop, then
  249. // stop the PNP service now and perform the necessary cleanup.
  250. //
  251. if (CurrentServiceState != SERVICE_STOPPED &&
  252. CurrentServiceState != SERVICE_STOP_PENDING) {
  253. //
  254. // Notify Service Controller that we're stopping
  255. //
  256. PnPServiceStatusUpdate(hSvcHandle, SERVICE_STOP_PENDING, 1, 0);
  257. //
  258. // Unregister the RPC server interface registered by our service
  259. // entry point, do not wait for outstanding calls to complete
  260. // before unregistering the interface.
  261. //
  262. RpcStatus =
  263. RpcServerUnregisterIf(
  264. pnp_ServerIfHandle,
  265. NULL, 0);
  266. if (RpcStatus != RPC_S_OK) {
  267. KdPrintEx((DPFLTR_PNPMGR_ID,
  268. DBGF_ERRORS,
  269. "UMPNPMGR: RpcServerUnregisterIf failed with RpcStatus = %d\n",
  270. RpcStatus));
  271. }
  272. //
  273. // Destroy the Plug and Play security object
  274. //
  275. DestroyPlugPlaySecurityObject();
  276. //
  277. // Notify Service Controller that we've now stopped
  278. //
  279. PnPServiceStatusUpdate(hSvcHandle, SERVICE_STOPPED, 0, 0);
  280. }
  281. break;
  282. case SERVICE_CONTROL_INTERROGATE:
  283. //
  284. // Request to immediately notify Service Controller of
  285. // current status
  286. //
  287. PnPServiceStatusUpdate(hSvcHandle, CurrentServiceState, 0, 0);
  288. break;
  289. case SERVICE_CONTROL_SESSIONCHANGE:
  290. //
  291. // Session change notification.
  292. //
  293. SessionNotificationHandler(dwEventType, (PWTSSESSION_NOTIFICATION)lpEventData);
  294. break;
  295. default:
  296. //
  297. // No special handling for any other service controls.
  298. //
  299. break;
  300. }
  301. return NO_ERROR;
  302. } // PnPControlHandlerEx
  303. VOID
  304. PnPServiceStatusUpdate(
  305. SERVICE_STATUS_HANDLE hSvcHandle,
  306. DWORD dwState,
  307. DWORD dwCheckPoint,
  308. DWORD dwExitCode
  309. )
  310. /*++
  311. Routine Description:
  312. This routine notifies the Service Controller of the current status of the
  313. Plug-and-Play service.
  314. Arguments:
  315. hSvcHandle - Supplies the service status handle for the Plug-and-Play service.
  316. dwState - Specifies the current state of the service to report.
  317. dwCheckPoint - Specifies an intermediate checkpoint for operations during
  318. which the state is pending.
  319. dwExitCode - Specifies a service specific error code.
  320. Return Value:
  321. None.
  322. Note:
  323. This routine also updates the set of controls accepted by the service.
  324. The PlugPlay service currently accepts the following controls when the
  325. service is running:
  326. SERVICE_CONTROL_SHUTDOWN - the system is shutting down.
  327. SERVICE_CONTROL_SESSIONCHANGE - the state of some remote or console session
  328. has changed.
  329. --*/
  330. {
  331. SERVICE_STATUS SvcStatus;
  332. SvcStatus.dwServiceType = SERVICE_WIN32;
  333. SvcStatus.dwCurrentState = CurrentServiceState = dwState;
  334. SvcStatus.dwCheckPoint = dwCheckPoint;
  335. if (dwState == SERVICE_RUNNING) {
  336. SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_SESSIONCHANGE;
  337. } else {
  338. SvcStatus.dwControlsAccepted = 0;
  339. }
  340. if ((dwState == SERVICE_START_PENDING) ||
  341. (dwState == SERVICE_STOP_PENDING)) {
  342. SvcStatus.dwWaitHint = 45000; // 45 seconds
  343. } else {
  344. SvcStatus.dwWaitHint = 0;
  345. }
  346. if (dwExitCode != 0) {
  347. SvcStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  348. SvcStatus.dwServiceSpecificExitCode = dwExitCode;
  349. } else {
  350. SvcStatus.dwWin32ExitCode = NO_ERROR;
  351. SvcStatus.dwServiceSpecificExitCode = 0;
  352. }
  353. SetServiceStatus(hSvcHandle, &SvcStatus);
  354. return;
  355. } // PnPServiceStatusUpdate
  356. RPC_STATUS
  357. CALLBACK
  358. PnPRpcIfCallback(
  359. RPC_IF_HANDLE* Interface,
  360. void* Context
  361. )
  362. /*++
  363. Routine Description:
  364. RPC interface callback function for authenticating clients of the Plug and
  365. Play RPC server.
  366. Arguments:
  367. Interface - Supplies the UUID and version of the interface.
  368. Context - Supplies a server binding handle representing the client
  369. Return Value:
  370. RPC_S_OK if an interface method can be called, RPC_S_ACCESS_DENIED if the
  371. interface method should not be called.
  372. --*/
  373. {
  374. handle_t hBinding;
  375. RPC_STATUS RpcStatus = RPC_S_OK;
  376. UNREFERENCED_PARAMETER(Interface);
  377. //
  378. // The Context supplied to the interface callback routine is an RPC binding
  379. // handle.
  380. //
  381. hBinding = (handle_t)Context;
  382. //
  383. // Make sure that the provided RPC binding handle is not NULL.
  384. //
  385. // The RPC interface routines sometimes get called directly directly by the
  386. // SCM and other internal routines, using a NULL binding handle. This
  387. // security callback routine should only get called in the context of an RPC
  388. // call, so the supplied binding handle should never be NULL.
  389. //
  390. ASSERT(hBinding != NULL);
  391. //
  392. // Verify client basic "read" access for all APIs.
  393. //
  394. if (!VerifyClientAccess(hBinding,
  395. PLUGPLAY_READ)) {
  396. RpcStatus = RPC_S_ACCESS_DENIED;
  397. goto Clean0;
  398. }
  399. Clean0:
  400. return RpcStatus;
  401. } // PnPRpcIfCallback
  402.