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.

1372 lines
37 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000.
  5. //
  6. // File: svchost.c
  7. //
  8. // Contents: Generic Host Process for Win32 Services
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 3-30-98 RichardW Created
  15. // 3-31-98 ShaunCo Took ownership.
  16. // Finished off basic implementation.
  17. // 1-24-00 JSchwart Took ownership.
  18. // Adapted to run NT intrinsic services.
  19. // April 2002 JayKrell added simple support for ServiceManifest registry setting
  20. // did minimal cleanup..much room for improvement..
  21. //----------------------------------------------------------------------------
  22. #include "pch.h"
  23. #pragma hdrstop
  24. #include "globals.h"
  25. #include "registry.h"
  26. #include "security.h"
  27. //
  28. // Generic Service Process:
  29. //
  30. // This process will grovel the service portion of the registry, looking
  31. // for instances of itself (details below), and constructing a list of services
  32. // to submit to the service controller. As an individual service is started,
  33. // the DLL is loaded and the entry point called. Services in these DLLs are
  34. // expected to play nicely with others, that is, use the common thread pool,
  35. // not stomp memory, etc.
  36. //
  37. //
  38. // Loading.
  39. //
  40. // Each service that will be resident in this process must have svchost.exe as
  41. // the ImagePath, with the same parameters. Additionally, the service must
  42. // have under its Parameters key, these values:
  43. //
  44. // ServiceDll = REG_EXPAND_SZ <path to DLL>
  45. // ServiceMain = REG_SZ <pszFunctionName> OPTIONAL
  46. //
  47. // If ServiceMain is not present, then it defaults to "ServiceMain".
  48. //
  49. //
  50. // Multiple Service Groups
  51. //
  52. // Multiple service groups can be accomplished by supplying parameters to the
  53. // svchost.exe on the ImagePath.
  54. //
  55. // svchost.exe -k "Key"
  56. //
  57. // will grovel the services and only load those with matching ImagePath.
  58. //
  59. #define REGSTR_PATH_SVCHOST TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost")
  60. typedef struct _COMMAND_OPTIONS
  61. {
  62. PTSTR CommandLineBuffer;
  63. PTSTR ImageName;
  64. BOOL fServiceGroup;
  65. PTSTR ServiceGroupName;
  66. //
  67. // dwCoInitializeSecurityParam is a DWORD read from the registry for the
  68. // service group we were instantiated for. If non-zero, we will
  69. // call CoInitializeSecurity in a way based on the value.
  70. //
  71. DWORD dwCoInitializeSecurityParam;
  72. DWORD dwAuthLevel;
  73. DWORD dwImpersonationLevel;
  74. DWORD dwAuthCapabilities;
  75. //
  76. // Default stack size for RPC threads (to prevent stack overflow)
  77. //
  78. DWORD dwDefaultRpcStackSize;
  79. //
  80. // Should this svchost instance mark itself as system-critical?
  81. //
  82. BOOL fSystemCritical;
  83. }
  84. COMMAND_OPTIONS, * PCOMMAND_OPTIONS;
  85. typedef struct _SERVICE_DLL
  86. {
  87. LIST_ENTRY List;
  88. HMODULE hmod;
  89. PTSTR pszDllPath;
  90. PTSTR pszManifestPath;
  91. HANDLE hActCtx;
  92. } SERVICE_DLL, * PSERVICE_DLL;
  93. typedef struct _SERVICE
  94. {
  95. PTSTR pszName;
  96. PSERVICE_DLL pDll;
  97. PSTR pszEntryPoint;
  98. } SERVICE, * PSERVICE;
  99. //+---------------------------------------------------------------------------
  100. //
  101. // Global variables.
  102. //
  103. // ListLock protects access to the Dll list and Service array.
  104. //
  105. CRITICAL_SECTION ListLock;
  106. // DllList is a list of SERVICE_DLL structures representing the DLL's
  107. // which host entry points for the services hosted by this process.
  108. //
  109. LIST_ENTRY DllList;
  110. // ServiceArray is an array of SERVICE structures representing the services
  111. // hosted by this process.
  112. //
  113. PSERVICE ServiceArray;
  114. // ServiceCount is the count of SERVICE entries in ServiceList.
  115. //
  116. UINT ServiceCount;
  117. // ServiceNames is the multi-sz read from the registry for the
  118. // service group we were instantiated for.
  119. //
  120. PTSTR ServiceNames;
  121. //+---------------------------------------------------------------------------
  122. //
  123. // Local function prototypes
  124. //
  125. VOID
  126. SvchostCharLowerW(
  127. LPWSTR pszString
  128. );
  129. //+---------------------------------------------------------------------------
  130. //
  131. VOID
  132. DummySvchostCtrlHandler(
  133. DWORD Opcode
  134. )
  135. {
  136. return;
  137. }
  138. VOID
  139. AbortSvchostService( // used if cant find Service DLL or entrypoint
  140. LPWSTR ServiceName,
  141. DWORD Error
  142. )
  143. {
  144. SERVICE_STATUS_HANDLE GenericServiceStatusHandle;
  145. SERVICE_STATUS GenericServiceStatus;
  146. GenericServiceStatus.dwServiceType = SERVICE_WIN32;
  147. GenericServiceStatus.dwCurrentState = SERVICE_STOPPED;
  148. GenericServiceStatus.dwControlsAccepted = SERVICE_CONTROL_STOP;
  149. GenericServiceStatus.dwCheckPoint = 0;
  150. GenericServiceStatus.dwWaitHint = 0;
  151. GenericServiceStatus.dwWin32ExitCode = Error;
  152. GenericServiceStatus.dwServiceSpecificExitCode = 0;
  153. GenericServiceStatusHandle = RegisterServiceCtrlHandler(ServiceName,
  154. DummySvchostCtrlHandler);
  155. if (GenericServiceStatusHandle == NULL)
  156. {
  157. SVCHOST_LOG1(ERROR,
  158. "AbortSvchostService: RegisterServiceCtrlHandler failed %d\n",
  159. GetLastError());
  160. }
  161. else if (!SetServiceStatus (GenericServiceStatusHandle,
  162. &GenericServiceStatus))
  163. {
  164. SVCHOST_LOG1(ERROR,
  165. "AbortSvchostService: SetServiceStatus error %ld\n",
  166. GetLastError());
  167. }
  168. return;
  169. }
  170. FARPROC
  171. GetServiceDllFunction (
  172. PSERVICE_DLL pDll,
  173. PCSTR pszFunctionName,
  174. LPDWORD lpdwError OPTIONAL
  175. )
  176. {
  177. FARPROC pfn = NULL;
  178. HMODULE hmod;
  179. ULONG_PTR ulpActCtxStackCookie = 0;
  180. BOOL fActivateSuccess = FALSE;
  181. //
  182. // GetProcAddress can lead to .dlls being loaded, at least
  183. // in the presence of forwarders. So we are sure to activate the activationcontext
  184. // even if we are not doing a LoadLibrary here.
  185. //
  186. fActivateSuccess = ActivateActCtx(pDll->hActCtx, &ulpActCtxStackCookie);
  187. if (!fActivateSuccess)
  188. {
  189. if (lpdwError)
  190. {
  191. *lpdwError = GetLastError();
  192. }
  193. SVCHOST_LOG2(ERROR,
  194. "ActivateActCtx for %ws failed. Error %d.\n",
  195. pDll->pszDllPath,
  196. GetLastError());
  197. goto Exit;
  198. }
  199. // Load the module if neccessary.
  200. //
  201. hmod = pDll->hmod;
  202. if (!hmod)
  203. {
  204. hmod = LoadLibraryEx (
  205. pDll->pszDllPath,
  206. NULL,
  207. LOAD_WITH_ALTERED_SEARCH_PATH);
  208. if (hmod)
  209. {
  210. pDll->hmod = hmod;
  211. }
  212. else
  213. {
  214. if (lpdwError)
  215. {
  216. *lpdwError = GetLastError();
  217. }
  218. SVCHOST_LOG2(ERROR,
  219. "LoadLibrary (%ws) failed. Error %d.\n",
  220. pDll->pszDllPath,
  221. GetLastError());
  222. goto Exit;
  223. }
  224. }
  225. ASSERT (hmod);
  226. pfn = GetProcAddress(hmod, pszFunctionName);
  227. if (!pfn)
  228. {
  229. if (lpdwError)
  230. {
  231. *lpdwError = GetLastError();
  232. }
  233. SVCHOST_LOG3(TRACE,
  234. "GetProcAddress (%s) failed on DLL %ws. Error = %d.\n",
  235. pszFunctionName,
  236. pDll->pszDllPath,
  237. GetLastError());
  238. }
  239. Exit:
  240. if (fActivateSuccess)
  241. DeactivateActCtx(0, ulpActCtxStackCookie);
  242. return pfn;
  243. }
  244. PSERVICE_DLL
  245. FindDll(
  246. IN LPCTSTR pszManifestPath,
  247. IN LPCTSTR pszDllPath
  248. )
  249. {
  250. PLIST_ENTRY pNode;
  251. PSERVICE_DLL pDll = NULL;
  252. ASSERT (pszDllPath);
  253. EnterCriticalSection (&ListLock);
  254. pNode = DllList.Flink;
  255. while (pNode != &DllList)
  256. {
  257. pDll = CONTAINING_RECORD (pNode, SERVICE_DLL, List);
  258. if (0 == lstrcmp (pDll->pszDllPath, pszDllPath)
  259. && 0 == lstrcmp (pDll->pszManifestPath, pszManifestPath)
  260. )
  261. {
  262. break;
  263. }
  264. pDll = NULL;
  265. pNode = pNode->Flink;
  266. }
  267. LeaveCriticalSection (&ListLock);
  268. return pDll;
  269. }
  270. PSERVICE_DLL
  271. AddDll(
  272. IN LPCTSTR pszManifestPath,
  273. IN LPCTSTR pszDllPath,
  274. OUT LPDWORD lpdwError
  275. )
  276. {
  277. PSERVICE_DLL pDll;
  278. SIZE_T nDllPathLength;
  279. SIZE_T nManifestPathLength;
  280. ASSERT (pszDllPath);
  281. ASSERT (*pszDllPath);
  282. nDllPathLength = lstrlenW (pszDllPath);
  283. nManifestPathLength = lstrlenW (pszManifestPath);
  284. pDll = (PSERVICE_DLL)MemAlloc (HEAP_ZERO_MEMORY,
  285. sizeof (SERVICE_DLL)
  286. + ((nDllPathLength + 1) * sizeof(WCHAR))
  287. + ((nManifestPathLength + 1) * sizeof(WCHAR))
  288. );
  289. if (pDll)
  290. {
  291. // Set the structure members.
  292. //
  293. pDll->pszDllPath = (PTSTR) (pDll + 1);
  294. pDll->pszManifestPath = pDll->pszDllPath + nDllPathLength + 1;
  295. CopyMemory(pDll->pszDllPath, pszDllPath, nDllPathLength * sizeof(WCHAR));
  296. CopyMemory(pDll->pszManifestPath, pszManifestPath, nManifestPathLength * sizeof(WCHAR));
  297. ASSERT(pDll->hActCtx == NULL);
  298. ASSERT(pDll->pszDllPath[nDllPathLength] == 0);
  299. ASSERT(pDll->pszManifestPath[nManifestPathLength] == 0);
  300. // Add the entry to the list.
  301. //
  302. EnterCriticalSection (&ListLock);
  303. InsertTailList (&DllList, &pDll->List);
  304. LeaveCriticalSection (&ListLock);
  305. }
  306. else
  307. {
  308. *lpdwError = ERROR_NOT_ENOUGH_MEMORY;
  309. }
  310. return pDll;
  311. }
  312. LONG
  313. OpenServiceParametersKey (
  314. LPCTSTR pszServiceName,
  315. HKEY* phkey
  316. )
  317. {
  318. LONG lr;
  319. HKEY hkeyServices = NULL;
  320. HKEY hkeySvc = NULL;
  321. ASSERT (phkey);
  322. // Open the Services key.
  323. //
  324. lr = RegOpenKeyEx (
  325. HKEY_LOCAL_MACHINE,
  326. REGSTR_PATH_SERVICES,
  327. 0,
  328. KEY_READ,
  329. &hkeyServices);
  330. if (lr != ERROR_SUCCESS)
  331. goto Exit;
  332. // Open the service key.
  333. //
  334. lr = RegOpenKeyEx (
  335. hkeyServices,
  336. pszServiceName,
  337. 0,
  338. KEY_READ,
  339. &hkeySvc);
  340. if (lr != ERROR_SUCCESS)
  341. goto Exit;
  342. // Open the Parameters key.
  343. //
  344. lr = RegOpenKeyEx (
  345. hkeySvc,
  346. TEXT("Parameters"),
  347. 0,
  348. KEY_READ,
  349. phkey);
  350. Exit:
  351. if (hkeyServices != NULL)
  352. RegCloseKey(hkeyServices);
  353. if (hkeySvc != NULL)
  354. RegCloseKey(hkeySvc);
  355. return lr;
  356. }
  357. #if DBG
  358. BOOL
  359. FDebugBreakForService (
  360. LPCWSTR pszwService
  361. )
  362. {
  363. BOOL fAttach = FALSE;
  364. LONG lr;
  365. HKEY hkeySvchost;
  366. // Open the Svchost key.
  367. //
  368. lr = RegOpenKeyEx (
  369. HKEY_LOCAL_MACHINE,
  370. REGSTR_PATH_SVCHOST,
  371. 0,
  372. KEY_READ,
  373. &hkeySvchost);
  374. if (!lr)
  375. {
  376. HKEY hkeyServiceOptions;
  377. // Look for the key with the same name as the service.
  378. //
  379. lr = RegOpenKeyExW (
  380. hkeySvchost,
  381. pszwService,
  382. 0,
  383. KEY_READ,
  384. &hkeyServiceOptions);
  385. if (!lr)
  386. {
  387. DWORD dwValue;
  388. lr = RegQueryDword (
  389. hkeyServiceOptions,
  390. TEXT("DebugBreak"),
  391. &dwValue);
  392. if (!lr)
  393. {
  394. fAttach = !!dwValue;
  395. }
  396. RegCloseKey (hkeyServiceOptions);
  397. }
  398. RegCloseKey (hkeySvchost);
  399. }
  400. return fAttach;
  401. }
  402. #endif
  403. VOID
  404. GetServiceMainFunctions (
  405. PSERVICE pService,
  406. LPSERVICE_MAIN_FUNCTION *ppfnServiceMain,
  407. LPSVCHOST_PUSH_GLOBAL_FUNCTION *ppfnPushGlobals,
  408. LPDWORD lpdwError
  409. )
  410. {
  411. LPCSTR pszEntryPoint;
  412. ACTCTXW ActCtxW = { sizeof(ActCtxW) };
  413. HANDLE hActCtx = NULL;
  414. HKEY hkeyParams = NULL;
  415. PSERVICE_DLL pDll = NULL;
  416. WCHAR pszExpandedDllName [MAX_PATH + 1];
  417. PWSTR pszDllName = pszExpandedDllName; // This sometimes get bumped forward to be the leaf name.
  418. WCHAR pszExpandedManifestName [MAX_PATH + 1];
  419. WCHAR * Temp = NULL;
  420. const DWORD TempSize = sizeof(pszExpandedDllName);
  421. *lpdwError = NO_ERROR;
  422. pszExpandedDllName[0] = 0;
  423. pszExpandedManifestName[0] = 0;
  424. // Get the dll and entrypoint for this service if we don't have it yet.
  425. //
  426. if (!pService->pDll)
  427. {
  428. LONG lr;
  429. lr = OpenServiceParametersKey (pService->pszName, &hkeyParams);
  430. if (!lr)
  431. {
  432. DWORD dwType;
  433. DWORD dwSize;
  434. Temp = (WCHAR*)MemAlloc(0, TempSize);
  435. if (Temp == NULL)
  436. {
  437. *lpdwError = ERROR_NOT_ENOUGH_MEMORY;
  438. goto Exit;
  439. }
  440. Temp[0] = 0;
  441. // Look for the service dll path and expand it.
  442. //
  443. dwSize = TempSize;
  444. lr = RegQueryValueEx (
  445. hkeyParams,
  446. TEXT("ServiceDll"),
  447. NULL,
  448. &dwType,
  449. (LPBYTE)Temp,
  450. &dwSize);
  451. if (lr != ERROR_SUCCESS)
  452. {
  453. *lpdwError = lr;
  454. SVCHOST_LOG2(ERROR,
  455. "RegQueryValueEx for the ServiceDll parameter of the "
  456. "%ws service returned %u\n",
  457. pService->pszName,
  458. lr);
  459. goto Exit;
  460. }
  461. if (dwType != REG_EXPAND_SZ)
  462. {
  463. *lpdwError = ERROR_FILE_NOT_FOUND;
  464. SVCHOST_LOG1(ERROR,
  465. "The ServiceDll parameter for the %ws service is not "
  466. "of type REG_EXPAND_SZ\n",
  467. pService->pszName);
  468. goto Exit;
  469. }
  470. if (Temp[0] == 0)
  471. {
  472. *lpdwError = ERROR_FILE_NOT_FOUND;
  473. goto Exit;
  474. }
  475. // Expand the dll name and lower case it for comparison
  476. // when we try to find an existing dll record.
  477. //
  478. ExpandEnvironmentStrings (
  479. Temp,
  480. pszDllName,
  481. MAX_PATH);
  482. SvchostCharLowerW (pszDllName);
  483. dwSize = TempSize;
  484. lr = RegQueryValueExW (
  485. hkeyParams,
  486. L"ServiceManifest",
  487. NULL,
  488. &dwType,
  489. (LPBYTE)Temp,
  490. &dwSize);
  491. switch (lr)
  492. {
  493. case ERROR_FILE_NOT_FOUND:
  494. case ERROR_PATH_NOT_FOUND:
  495. // ok
  496. pszExpandedManifestName[0] = 0;
  497. MemFree(Temp);
  498. Temp = NULL;
  499. goto NoManifest;
  500. default:
  501. *lpdwError = lr;
  502. SVCHOST_LOG2(ERROR,
  503. "RegQueryValueEx for the ServiceManifest parameter of the "
  504. "%ws service returned %u\n",
  505. pService->pszName,
  506. lr);
  507. goto Exit;
  508. case ERROR_SUCCESS:
  509. if (REG_EXPAND_SZ != dwType)
  510. {
  511. // invalid parameter is probably better here, but just do
  512. // as it does for ServiceDll
  513. *lpdwError = ERROR_FILE_NOT_FOUND;
  514. SVCHOST_LOG1(ERROR,
  515. "The ServiceManifest parameter for the %ws service is not "
  516. "of type REG_EXPAND_SZ\n",
  517. pService->pszName);
  518. goto Exit;
  519. }
  520. if (Temp[0] == 0)
  521. {
  522. // invalid parameter is probably better here, but just do
  523. // as it does for ServiceDll
  524. *lpdwError = ERROR_FILE_NOT_FOUND;
  525. SVCHOST_LOG1(ERROR,
  526. "The ServiceManifest parameter for the %ws service is not "
  527. "of type REG_EXPAND_SZ\n",
  528. pService->pszName);
  529. goto Exit;
  530. }
  531. }
  532. // Expand the manifest name and lower case it for comparison
  533. // when we try to find an existing dll record.
  534. //
  535. ExpandEnvironmentStringsW (
  536. Temp,
  537. pszExpandedManifestName,
  538. MAX_PATH);
  539. MemFree(Temp);
  540. Temp = NULL;
  541. SvchostCharLowerW (pszExpandedManifestName);
  542. //
  543. // Now only use the leaf dll name.
  544. // This way people can setup/register the same for downlevel and sidebyside.
  545. //
  546. {
  547. SIZE_T i = lstrlenW(pszDllName);
  548. while (i != 0)
  549. {
  550. i -= 1;
  551. if (pszDllName[i] == L'\\' || pszDllName[i] == L'/')
  552. {
  553. pszDllName = pszDllName + i + 1;
  554. break;
  555. }
  556. }
  557. }
  558. NoManifest:
  559. // Try to find an existing dll record that we might have and
  560. // if we don't, add this as a new record.
  561. //
  562. pDll = FindDll (pszExpandedManifestName, pszDllName);
  563. if (!pDll)
  564. {
  565. if (pszExpandedManifestName[0] != 0)
  566. {
  567. ActCtxW.lpSource = pszExpandedManifestName;
  568. hActCtx = CreateActCtxW(&ActCtxW);
  569. if (hActCtx == INVALID_HANDLE_VALUE)
  570. {
  571. *lpdwError = GetLastError();
  572. SVCHOST_LOG3(ERROR,
  573. "CreateActCtxW(%ws) for the "
  574. "%ws service returned %u\n",
  575. pszExpandedManifestName,
  576. pService->pszName,
  577. *lpdwError);
  578. goto Exit;
  579. }
  580. }
  581. // Remember this dll for this service for next time.
  582. //
  583. pDll = AddDll (pszExpandedManifestName, pszDllName, lpdwError);
  584. if (pDll == NULL)
  585. {
  586. //
  587. // Don't set *lpdwError here as AddDll already set it
  588. //
  589. goto Exit;
  590. }
  591. pDll->hActCtx = hActCtx;
  592. hActCtx = NULL;
  593. }
  594. ASSERT (!pService->pDll);
  595. pService->pDll = pDll;
  596. pDll = NULL;
  597. // Look for an explicit entrypoint name for this service.
  598. // (Optional)
  599. //
  600. RegQueryStringA (
  601. hkeyParams,
  602. TEXT("ServiceMain"),
  603. REG_SZ,
  604. &pService->pszEntryPoint);
  605. }
  606. else
  607. {
  608. *lpdwError = lr;
  609. }
  610. // If we don't have the service dll record by now, we're through.
  611. //
  612. if (!pService->pDll)
  613. {
  614. ASSERT(*lpdwError != NO_ERROR);
  615. goto Exit;
  616. }
  617. }
  618. // We should have it the dll by now, so proceed to load the entry point.
  619. //
  620. ASSERT (pService->pDll);
  621. // Default the entry point if we don't have one specified.
  622. //
  623. if (pService->pszEntryPoint)
  624. {
  625. pszEntryPoint = pService->pszEntryPoint;
  626. }
  627. else
  628. {
  629. pszEntryPoint = "ServiceMain";
  630. }
  631. // Get the address for the service's ServiceMain
  632. //
  633. *ppfnServiceMain = (LPSERVICE_MAIN_FUNCTION) GetServiceDllFunction(
  634. pService->pDll,
  635. pszEntryPoint,
  636. lpdwError);
  637. // Get the address for the "push the globals" function (optional)
  638. //
  639. *ppfnPushGlobals = (LPSVCHOST_PUSH_GLOBAL_FUNCTION) GetServiceDllFunction(
  640. pService->pDll,
  641. "SvchostPushServiceGlobals",
  642. NULL);
  643. Exit:
  644. if (hkeyParams != NULL)
  645. RegCloseKey (hkeyParams);
  646. if (Temp != NULL)
  647. MemFree(Temp);
  648. if (hActCtx != NULL && hActCtx != INVALID_HANDLE_VALUE)
  649. ReleaseActCtx(hActCtx);
  650. }
  651. LONG
  652. ReadPerInstanceRegistryParameters(
  653. IN HKEY hkeySvchost,
  654. IN OUT PCOMMAND_OPTIONS pOptions
  655. )
  656. {
  657. HKEY hkeySvchostGroup;
  658. LONG lr;
  659. // Read the value corresponding to this service group.
  660. //
  661. ASSERT (pOptions->ServiceGroupName);
  662. lr = RegQueryString (
  663. hkeySvchost,
  664. pOptions->ServiceGroupName,
  665. REG_MULTI_SZ,
  666. &ServiceNames);
  667. if (!lr && (!ServiceNames || !*ServiceNames))
  668. {
  669. lr = ERROR_INVALID_DATA;
  670. }
  671. // Read any per-instance parameters from the service group subkey
  672. // if it exists.
  673. //
  674. if (!RegOpenKeyEx (
  675. hkeySvchost,
  676. pOptions->ServiceGroupName,
  677. 0, KEY_READ,
  678. &hkeySvchostGroup))
  679. {
  680. DWORD dwValue;
  681. if (!RegQueryDword (
  682. hkeySvchostGroup,
  683. TEXT("CoInitializeSecurityParam"),
  684. &dwValue))
  685. {
  686. pOptions->dwCoInitializeSecurityParam = dwValue;
  687. }
  688. if (pOptions->dwCoInitializeSecurityParam)
  689. {
  690. if (!RegQueryDword (
  691. hkeySvchostGroup,
  692. TEXT("AuthenticationLevel"),
  693. &dwValue))
  694. {
  695. pOptions->dwAuthLevel = dwValue;
  696. }
  697. else
  698. {
  699. pOptions->dwAuthLevel = RPC_C_AUTHN_LEVEL_PKT;
  700. }
  701. if (!RegQueryDword (
  702. hkeySvchostGroup,
  703. TEXT("ImpersonationLevel"),
  704. &dwValue))
  705. {
  706. pOptions->dwImpersonationLevel = dwValue;
  707. }
  708. else
  709. {
  710. pOptions->dwImpersonationLevel = RPC_C_IMP_LEVEL_IDENTIFY;
  711. }
  712. if (!RegQueryDword (
  713. hkeySvchostGroup,
  714. TEXT("AuthenticationCapabilities"),
  715. &dwValue))
  716. {
  717. pOptions->dwAuthCapabilities = dwValue;
  718. }
  719. else
  720. {
  721. pOptions->dwAuthCapabilities = EOAC_NO_CUSTOM_MARSHAL |
  722. EOAC_DISABLE_AAA;
  723. }
  724. }
  725. if (!RegQueryDword (
  726. hkeySvchostGroup,
  727. TEXT("DefaultRpcStackSize"),
  728. &dwValue))
  729. {
  730. pOptions->dwDefaultRpcStackSize = dwValue;
  731. }
  732. if (!RegQueryDword (
  733. hkeySvchostGroup,
  734. TEXT("SystemCritical"),
  735. &dwValue))
  736. {
  737. pOptions->fSystemCritical = dwValue;
  738. }
  739. RegCloseKey (hkeySvchostGroup);
  740. }
  741. return lr;
  742. }
  743. BOOL
  744. CallPerInstanceInitFunctions(
  745. IN OUT PCOMMAND_OPTIONS pOptions
  746. )
  747. {
  748. if (pOptions->dwCoInitializeSecurityParam)
  749. {
  750. if (!InitializeSecurity(pOptions->dwCoInitializeSecurityParam,
  751. pOptions->dwAuthLevel,
  752. pOptions->dwImpersonationLevel,
  753. pOptions->dwAuthCapabilities))
  754. {
  755. return FALSE;
  756. }
  757. }
  758. if (pOptions->dwDefaultRpcStackSize)
  759. {
  760. RpcMgmtSetServerStackSize(pOptions->dwDefaultRpcStackSize * 1024);
  761. }
  762. else
  763. {
  764. //
  765. // Make sure the default RPC stack size will be at least as
  766. // large as the default thread stack size for the process so
  767. // a random service calling RpcMgmtSetServerStackSize can't
  768. // set it to a value that's too low, causing overflows.
  769. //
  770. PIMAGE_NT_HEADERS NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
  771. if (NtHeaders != NULL)
  772. {
  773. RpcMgmtSetServerStackSize((ULONG) NtHeaders->OptionalHeader.SizeOfStackCommit);
  774. }
  775. }
  776. if (pOptions->fSystemCritical)
  777. {
  778. //
  779. // Ignore the return value
  780. //
  781. RtlSetProcessIsCritical(TRUE, NULL, TRUE);
  782. }
  783. return TRUE;
  784. }
  785. VOID
  786. BuildServiceArray (
  787. IN OUT PCOMMAND_OPTIONS pOptions
  788. )
  789. {
  790. LONG lr;
  791. HKEY hkeySvchost;
  792. // Open the Svchost key.
  793. //
  794. lr = RegOpenKeyEx (
  795. HKEY_LOCAL_MACHINE,
  796. REGSTR_PATH_SVCHOST,
  797. 0, KEY_READ,
  798. &hkeySvchost);
  799. if (!lr)
  800. {
  801. lr = ReadPerInstanceRegistryParameters(hkeySvchost, pOptions);
  802. RegCloseKey (hkeySvchost);
  803. }
  804. if (!lr)
  805. {
  806. PTSTR pszServiceName;
  807. EnterCriticalSection (&ListLock);
  808. // Count the number of service names read.
  809. //
  810. ServiceCount = 0;
  811. for (pszServiceName = ServiceNames;
  812. *pszServiceName;
  813. pszServiceName += lstrlen(pszServiceName) + 1)
  814. {
  815. ServiceCount++;
  816. }
  817. ASSERT (ServiceCount);
  818. // Allocate memory for the service array.
  819. //
  820. ServiceArray = MemAlloc (HEAP_ZERO_MEMORY,
  821. sizeof (SERVICE) * ServiceCount);
  822. if (ServiceArray)
  823. {
  824. PSERVICE pService;
  825. // Initialize the service array.
  826. //
  827. pService = ServiceArray;
  828. for (pszServiceName = ServiceNames;
  829. *pszServiceName;
  830. pszServiceName += lstrlen(pszServiceName) + 1)
  831. {
  832. pService->pszName = pszServiceName;
  833. pService++;
  834. }
  835. ASSERT (pService == ServiceArray + ServiceCount);
  836. }
  837. LeaveCriticalSection (&ListLock);
  838. }
  839. }
  840. // type of LPSERVICE_MAIN_FUNCTIONW
  841. //
  842. VOID
  843. WINAPI
  844. ServiceStarter(
  845. DWORD argc,
  846. PWSTR argv[]
  847. )
  848. {
  849. LPSERVICE_MAIN_FUNCTION pfnServiceMain = NULL;
  850. LPSVCHOST_PUSH_GLOBAL_FUNCTION pfnPushGlobals = NULL;
  851. LPCWSTR pszwService = argv[0];
  852. LPWSTR pszwAbort = NULL;
  853. DWORD dwError = ERROR_FILE_NOT_FOUND;
  854. EnterCriticalSection (&ListLock);
  855. {
  856. UINT i;
  857. for (i = 0; i < ServiceCount; i++)
  858. {
  859. if (0 == lstrcmpi (pszwService, ServiceArray[i].pszName))
  860. {
  861. #if DBG
  862. if (FDebugBreakForService (pszwService))
  863. {
  864. SVCHOST_LOG1(TRACE,
  865. "Attaching debugger before getting ServiceMain for %ws...",
  866. pszwService);
  867. DebugBreak ();
  868. }
  869. #endif
  870. GetServiceMainFunctions(&ServiceArray[i],
  871. &pfnServiceMain,
  872. &pfnPushGlobals,
  873. &dwError);
  874. if (pfnServiceMain && pfnPushGlobals && !g_pSvchostSharedGlobals)
  875. {
  876. SvchostBuildSharedGlobals();
  877. }
  878. pszwAbort = argv[0];
  879. break;
  880. }
  881. }
  882. }
  883. LeaveCriticalSection (&ListLock);
  884. if (pfnPushGlobals && g_pSvchostSharedGlobals)
  885. {
  886. pfnPushGlobals (g_pSvchostSharedGlobals);
  887. if (pfnServiceMain)
  888. {
  889. SVCHOST_LOG1(TRACE,
  890. "Calling ServiceMain for %ws...\n",
  891. pszwService);
  892. pfnServiceMain (argc, argv);
  893. }
  894. else if (pszwAbort)
  895. {
  896. AbortSvchostService(pszwAbort,
  897. dwError);
  898. }
  899. }
  900. else if (pfnServiceMain && !pfnPushGlobals)
  901. {
  902. SVCHOST_LOG1(TRACE,
  903. "Calling ServiceMain for %ws...\n",
  904. pszwService);
  905. pfnServiceMain (argc, argv);
  906. }
  907. else if (pszwAbort)
  908. {
  909. AbortSvchostService(pszwAbort,
  910. dwError);
  911. }
  912. }
  913. LPSERVICE_TABLE_ENTRY
  914. BuildServiceTable(
  915. VOID
  916. )
  917. {
  918. LPSERVICE_TABLE_ENTRY pServiceTable;
  919. EnterCriticalSection (&ListLock);
  920. // Allocate one extra entry and zero the entire range. The extra entry
  921. // is the table terminator required by StartServiceCtrlDispatcher.
  922. //
  923. pServiceTable = MemAlloc (HEAP_ZERO_MEMORY,
  924. sizeof (SERVICE_TABLE_ENTRY) * (ServiceCount + 1));
  925. if (pServiceTable)
  926. {
  927. UINT i;
  928. for (i = 0; i < ServiceCount; i++)
  929. {
  930. pServiceTable[i].lpServiceName = ServiceArray[i].pszName;
  931. pServiceTable[i].lpServiceProc = ServiceStarter;
  932. SVCHOST_LOG1(TRACE,
  933. "Added service table entry for %ws\n",
  934. pServiceTable[i].lpServiceName);
  935. }
  936. }
  937. LeaveCriticalSection (&ListLock);
  938. return pServiceTable;
  939. }
  940. PCOMMAND_OPTIONS
  941. BuildCommandOptions (
  942. LPCTSTR pszCommandLine
  943. )
  944. {
  945. PCOMMAND_OPTIONS pOptions;
  946. ULONG cbCommandLine;
  947. if (pszCommandLine == NULL)
  948. {
  949. return NULL;
  950. }
  951. cbCommandLine = (lstrlen(pszCommandLine) + 1) * sizeof (TCHAR);
  952. pOptions = MemAlloc (HEAP_ZERO_MEMORY,
  953. sizeof (COMMAND_OPTIONS) + cbCommandLine);
  954. if (pOptions)
  955. {
  956. TCHAR* pch;
  957. TCHAR* pArgumentStart;
  958. PTSTR* ppNextArgument = NULL;
  959. pOptions->CommandLineBuffer = (PTSTR) (pOptions + 1);
  960. RtlCopyMemory (
  961. pOptions->CommandLineBuffer,
  962. pszCommandLine,
  963. cbCommandLine);
  964. pch = pOptions->CommandLineBuffer;
  965. ASSERT (pch);
  966. // Skip the name of the executable.
  967. //
  968. pOptions->ImageName = pch;
  969. while (*pch && (L' ' != *pch) && (L'\t' != *pch))
  970. {
  971. pch++;
  972. }
  973. if (*pch)
  974. {
  975. *pch++ = 0;
  976. }
  977. SvchostCharLowerW (pOptions->ImageName);
  978. while (1)
  979. {
  980. // Skip whitespace.
  981. //
  982. while (*pch && ((L' ' == *pch) || (L'\t' == *pch)))
  983. {
  984. pch++;
  985. }
  986. // End of string?
  987. //
  988. if (!*pch)
  989. {
  990. break;
  991. }
  992. // Is it a '-' or '/' argument?
  993. //
  994. if (((L'-' == *pch) || (L'/' == *pch)) && *(++pch))
  995. {
  996. if ((L'k' == *pch) || (L'K' == *pch))
  997. {
  998. pOptions->fServiceGroup = TRUE;
  999. ppNextArgument = &pOptions->ServiceGroupName;
  1000. }
  1001. pch++;
  1002. continue;
  1003. }
  1004. // This is the start of an argument.
  1005. //
  1006. pArgumentStart = pch;
  1007. // If the argument starts with a quote, skip it and scan to the
  1008. // next quote to terminate it.
  1009. //
  1010. if ((L'\"' == *pch) && *(++pch))
  1011. {
  1012. pArgumentStart = pch;
  1013. while (*pch && (L'\"' != *pch))
  1014. {
  1015. pch++;
  1016. }
  1017. }
  1018. // otherwise, skip to the next whitespace and this will be
  1019. // our argument.
  1020. //
  1021. else
  1022. {
  1023. while (*pch && (L' ' != *pch) && (L'\t' != *pch))
  1024. {
  1025. pch++;
  1026. }
  1027. }
  1028. if (*pch)
  1029. {
  1030. // terminate the newly found argument string.
  1031. //
  1032. *pch++ = 0;
  1033. }
  1034. if (ppNextArgument)
  1035. {
  1036. *ppNextArgument = pArgumentStart;
  1037. ppNextArgument = NULL;
  1038. }
  1039. }
  1040. pOptions->fServiceGroup = !!pOptions->ServiceGroupName;
  1041. SVCHOST_LOG1(TRACE,
  1042. "Command line : %ws\n",
  1043. pszCommandLine);
  1044. SVCHOST_LOG1(TRACE,
  1045. "Service Group : %ws\n",
  1046. (pOptions->fServiceGroup) ? pOptions->ServiceGroupName : L"No");
  1047. // Validate the options.
  1048. //
  1049. if (!pOptions->fServiceGroup)
  1050. {
  1051. SVCHOST_LOG2(TRACE,
  1052. "Generic Service Host\n\n"
  1053. "%ws [-k <key>] | [-r] | <service>\n\n"
  1054. " -k <key> Host all services whose ImagePath matches\n"
  1055. " %ws -k <key>.\n\n",
  1056. pOptions->CommandLineBuffer,
  1057. pOptions->CommandLineBuffer);
  1058. MemFree (pOptions);
  1059. pOptions = NULL;
  1060. }
  1061. }
  1062. return pOptions;
  1063. }
  1064. VOID
  1065. SvchostCharLowerW(
  1066. LPWSTR pszString
  1067. )
  1068. {
  1069. //
  1070. // LocalVersion of CharLower to avoid pulling in user32.dll
  1071. //
  1072. int cwchT;
  1073. DWORD cwch;
  1074. if (pszString == NULL)
  1075. {
  1076. return;
  1077. }
  1078. cwch = (DWORD) wcslen(pszString) + 1;
  1079. cwchT = LCMapStringW(LOCALE_USER_DEFAULT,
  1080. LCMAP_LOWERCASE,
  1081. pszString,
  1082. cwch,
  1083. pszString,
  1084. cwch);
  1085. if (cwchT == 0)
  1086. {
  1087. SVCHOST_LOG1(ERROR,
  1088. "SvchostCharLowerW failed for %ws\n",
  1089. pszString);
  1090. }
  1091. return;
  1092. }
  1093. LONG
  1094. WINAPI
  1095. SvchostUnhandledExceptionFilter(
  1096. struct _EXCEPTION_POINTERS *ExceptionInfo
  1097. )
  1098. {
  1099. return RtlUnhandledExceptionFilter(ExceptionInfo);
  1100. }
  1101. VOID
  1102. wmainCRTStartup (
  1103. VOID
  1104. )
  1105. {
  1106. LPSERVICE_TABLE_ENTRY pServiceTable = NULL;
  1107. PCOMMAND_OPTIONS pOptions;
  1108. PCWSTR pszwCommandLine;
  1109. SetUnhandledExceptionFilter(&SvchostUnhandledExceptionFilter);
  1110. // Prevent critical errors from raising hard error popups and
  1111. // halting svchost.exe. The flag below will have the system send
  1112. // the errors to the process instead.
  1113. //
  1114. SetErrorMode(SEM_FAILCRITICALERRORS);
  1115. // Initialize our HeapAlloc wrapper to use the process heap.
  1116. //
  1117. MemInit (GetProcessHeap());
  1118. // Initialize our global DLL list, Service array, and the critical
  1119. // section that protects them. InitializeCriticalSection can throw a
  1120. // STATUS_NO_MEMORY exception. We want the process to exit if that
  1121. // happens, so the default exception handler is fine.
  1122. //
  1123. InitializeListHead (&DllList);
  1124. InitializeCriticalSection (&ListLock);
  1125. // Build a COMMAND_OPTIONS structure and use it to grovel the registry
  1126. // and create the service entry table.
  1127. //
  1128. pszwCommandLine = GetCommandLine ();
  1129. pOptions = BuildCommandOptions (pszwCommandLine);
  1130. if (pOptions)
  1131. {
  1132. BuildServiceArray (pOptions);
  1133. pServiceTable = BuildServiceTable ();
  1134. if (pServiceTable)
  1135. {
  1136. if (!CallPerInstanceInitFunctions(pOptions))
  1137. {
  1138. SVCHOST_LOG0(ERROR,
  1139. "CallPerInstanceInitFunctions failed -- exiting!\n");
  1140. ExitProcess(1);
  1141. }
  1142. }
  1143. MemFree (pOptions);
  1144. }
  1145. // If we have a valid service entry table, use it to transfer control
  1146. // to the service controller. StartServiceCtrlDispatcher won't return
  1147. // until all services are stopped.
  1148. //
  1149. if (pServiceTable)
  1150. {
  1151. StartServiceCtrlDispatcher (pServiceTable);
  1152. }
  1153. SVCHOST_LOG1(TRACE,
  1154. "Calling ExitProcess for %ws\n",
  1155. pszwCommandLine);
  1156. ExitProcess (0);
  1157. }