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.

1067 lines
31 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. crash.cxx
  5. Abstract:
  6. Contains code concerned with recovery actions that are taken when
  7. a service crashes. This file contains the following functions:
  8. ScQueueRecoveryAction
  9. CCrashRecord::IncrementCount
  10. CRestartContext::Perform
  11. CRebootMessageContext::Perform
  12. CRebootContext::Perform
  13. CRunCommandContext::Perform
  14. Author:
  15. Anirudh Sahni (anirudhs) 02-Dec-1996
  16. Environment:
  17. User Mode -Win32
  18. Revision History:
  19. 22-Oct-1998 jschwart
  20. Convert SCM to use NT thread pool APIs
  21. 02-Dec-1996 AnirudhS
  22. Created
  23. --*/
  24. //
  25. // INCLUDES
  26. //
  27. #include "precomp.hxx"
  28. #include <lmcons.h> // needed for other lm headers
  29. #include <lmerr.h> // NERR_Success
  30. #include <lmshare.h> // NetSessionEnum
  31. #include <lmmsg.h> // NetMessageBufferSend
  32. #include <lmapibuf.h> // NetApiBufferFree
  33. #include <valid.h> // ACTION_TYPE_INVALID
  34. #include <svcslib.h> // CWorkItemContext
  35. #include "smartp.h" // CHeapPtr
  36. #include "scconfig.h" // ScReadFailureActions, etc.
  37. #include "depend.h" // ScStartServiceAndDependencies
  38. #include "account.h" // ScLogonService
  39. #include "scseclib.h" // ScCreateAndSetSD
  40. #include "start.h" // ScAllowInteractiveServices, ScInitStartupInfo
  41. #include "resource.h" // IDS_SC_ACTION_BASE
  42. //
  43. // Defines and Typedefs
  44. //
  45. #define FILETIMES_PER_SEC ((__int64) 10000000) // (1 second)/(100 ns)
  46. #define LENGTH(array) (sizeof(array)/sizeof((array)[0]))
  47. //
  48. // Globals
  49. //
  50. //
  51. // Local Function Prototypes
  52. //
  53. VOID
  54. ScLogRecoveryFailure(
  55. IN SC_ACTION_TYPE ActionType,
  56. IN LPCWSTR ServiceDisplayName,
  57. IN DWORD Error
  58. );
  59. inline LPWSTR
  60. LocalDup(
  61. LPCWSTR String
  62. )
  63. {
  64. LPWSTR Dup = (LPWSTR) LocalAlloc(0, WCSSIZE(String));
  65. if (Dup != NULL)
  66. {
  67. wcscpy(Dup, String);
  68. }
  69. return Dup;
  70. }
  71. //
  72. // Callback context for restarting a service
  73. //
  74. class CRestartContext : public CWorkItemContext
  75. {
  76. DECLARE_CWorkItemContext
  77. public:
  78. CRestartContext(
  79. IN LPSERVICE_RECORD ServiceRecord
  80. ) :
  81. _ServiceRecord(ServiceRecord)
  82. {
  83. ServiceRecord->UseCount++;
  84. SC_LOG2(USECOUNT, "CRestartContext: %ws increment "
  85. "USECOUNT=%lu\n", ServiceRecord->ServiceName,
  86. ServiceRecord->UseCount);
  87. }
  88. ~CRestartContext()
  89. {
  90. CServiceRecordExclusiveLock RLock;
  91. ScDecrementUseCountAndDelete(_ServiceRecord);
  92. }
  93. private:
  94. LPSERVICE_RECORD _ServiceRecord;
  95. };
  96. //
  97. // Callback context for broadcasting a reboot message
  98. //
  99. class CRebootMessageContext : public CWorkItemContext
  100. {
  101. DECLARE_CWorkItemContext
  102. public:
  103. CRebootMessageContext(
  104. IN LPWSTR RebootMessage,
  105. IN DWORD Delay,
  106. IN LPWSTR DisplayName
  107. ) :
  108. _RebootMessage(RebootMessage),
  109. _Delay(Delay),
  110. _DisplayName(LocalDup(DisplayName))
  111. { }
  112. ~CRebootMessageContext()
  113. {
  114. LocalFree(_RebootMessage);
  115. }
  116. private:
  117. LPWSTR _RebootMessage;
  118. DWORD _Delay;
  119. LPWSTR _DisplayName;
  120. };
  121. //
  122. // Callback context for a reboot
  123. // (The service name is used only for logging)
  124. //
  125. class CRebootContext : public CWorkItemContext
  126. {
  127. DECLARE_CWorkItemContext
  128. public:
  129. CRebootContext(
  130. IN DWORD ActionDelay,
  131. IN LPWSTR DisplayName
  132. ) :
  133. _Delay(ActionDelay),
  134. _DisplayName(DisplayName)
  135. { }
  136. ~CRebootContext()
  137. {
  138. LocalFree(_DisplayName);
  139. }
  140. private:
  141. DWORD _Delay;
  142. LPWSTR _DisplayName;
  143. };
  144. //
  145. // Callback context for running a recovery command
  146. //
  147. class CRunCommandContext : public CWorkItemContext
  148. {
  149. DECLARE_CWorkItemContext
  150. public:
  151. CRunCommandContext(
  152. IN LPSERVICE_RECORD ServiceRecord,
  153. IN LPWSTR FailureCommand
  154. ) :
  155. _ServiceRecord(ServiceRecord),
  156. _FailureCommand(FailureCommand)
  157. {
  158. //
  159. // The service record is used to get the
  160. // account name to run the command in.
  161. //
  162. ServiceRecord->UseCount++;
  163. SC_LOG2(USECOUNT, "CRunCommandContext: %ws increment "
  164. "USECOUNT=%lu\n", ServiceRecord->ServiceName,
  165. ServiceRecord->UseCount);
  166. }
  167. ~CRunCommandContext()
  168. {
  169. LocalFree(_FailureCommand);
  170. CServiceRecordExclusiveLock RLock;
  171. ScDecrementUseCountAndDelete(_ServiceRecord);
  172. }
  173. private:
  174. LPSERVICE_RECORD _ServiceRecord;
  175. LPWSTR _FailureCommand;
  176. };
  177. /****************************************************************************/
  178. VOID
  179. ScQueueRecoveryAction(
  180. IN LPSERVICE_RECORD ServiceRecord
  181. )
  182. /*++
  183. Routine Description:
  184. Arguments:
  185. Return Value:
  186. none.
  187. --*/
  188. {
  189. SC_ACTION_TYPE ActionType = SC_ACTION_NONE;
  190. DWORD ActionDelay = 0;
  191. DWORD FailNum = 1;
  192. NTSTATUS ntStatus;
  193. //
  194. // See if there is any recovery action configured for this service.
  195. //
  196. HKEY Key = NULL;
  197. {
  198. DWORD ResetPeriod = INFINITE;
  199. LPSERVICE_FAILURE_ACTIONS_WOW64 psfa = NULL;
  200. DWORD Error = ScOpenServiceConfigKey(
  201. ServiceRecord->ServiceName,
  202. KEY_READ,
  203. FALSE, // don't create if missing
  204. &Key
  205. );
  206. if (Error == ERROR_SUCCESS)
  207. {
  208. Error = ScReadFailureActions(Key, &psfa);
  209. }
  210. if (Error != ERROR_SUCCESS)
  211. {
  212. SC_LOG(ERROR, "Couldn't read service's failure actions, %lu\n", Error);
  213. }
  214. else if (psfa != NULL && psfa->cActions > 0)
  215. {
  216. ResetPeriod = psfa->dwResetPeriod;
  217. }
  218. //
  219. // Allocate a crash record for the service.
  220. // Increment the service's crash count, subject to the reset period
  221. // we just read from the registry (INFINITE if we read none).
  222. //
  223. if (ServiceRecord->CrashRecord == NULL)
  224. {
  225. ServiceRecord->CrashRecord = new CCrashRecord;
  226. }
  227. if (ServiceRecord->CrashRecord == NULL)
  228. {
  229. SC_LOG0(ERROR, "Couldn't allocate service's crash record\n");
  230. //
  231. // NOTE: We still continue, taking the failure count to be 1.
  232. // (The crash record is used only in the "else" clause.)
  233. //
  234. }
  235. else
  236. {
  237. FailNum = ServiceRecord->CrashRecord->IncrementCount(ResetPeriod);
  238. }
  239. //
  240. // Figure out which recovery action we're going to take.
  241. //
  242. if (psfa != NULL && psfa->cActions > 0)
  243. {
  244. SC_ACTION * lpsaActions = (SC_ACTION *) ((LPBYTE) psfa + psfa->dwsaActionsOffset);
  245. DWORD i = min(FailNum, psfa->cActions);
  246. ActionType = lpsaActions[i - 1].Type;
  247. ActionDelay = lpsaActions[i - 1].Delay;
  248. if (ACTION_TYPE_INVALID(ActionType))
  249. {
  250. SC_LOG(ERROR, "Service has invalid action type %lu\n", ActionType);
  251. ActionType = SC_ACTION_NONE;
  252. }
  253. }
  254. LocalFree(psfa);
  255. }
  256. //
  257. // Log an event about this service failing, and about the proposed
  258. // recovery action.
  259. //
  260. if (ActionType != SC_ACTION_NONE)
  261. {
  262. WCHAR wszActionString[50];
  263. if (!LoadString(GetModuleHandle(NULL),
  264. IDS_SC_ACTION_BASE + ActionType,
  265. wszActionString,
  266. LENGTH(wszActionString)))
  267. {
  268. SC_LOG(ERROR, "LoadString failed %lu\n", GetLastError());
  269. wszActionString[0] = L'\0';
  270. }
  271. SC_LOG2(ERROR, "The following recovery action will be taken in %d ms: %ws.\n",
  272. ActionDelay, wszActionString);
  273. ScLogEvent(NEVENT_SERVICE_CRASH,
  274. ServiceRecord->DisplayName,
  275. FailNum,
  276. ActionDelay,
  277. ActionType,
  278. wszActionString);
  279. }
  280. else
  281. {
  282. ScLogEvent(NEVENT_SERVICE_CRASH_NO_ACTION,
  283. ServiceRecord->DisplayName,
  284. FailNum);
  285. }
  286. //
  287. // Queue a work item that will actually carry out the action after the
  288. // delay has elapsed.
  289. //
  290. switch (ActionType)
  291. {
  292. case SC_ACTION_NONE:
  293. break;
  294. case SC_ACTION_RESTART:
  295. {
  296. CRestartContext * pCtx = new CRestartContext(ServiceRecord);
  297. if (pCtx == NULL)
  298. {
  299. SC_LOG0(ERROR, "Couldn't allocate restart context\n");
  300. break;
  301. }
  302. ntStatus = pCtx->AddDelayedWorkItem(ActionDelay,
  303. WT_EXECUTEONLYONCE);
  304. if (!NT_SUCCESS(ntStatus))
  305. {
  306. SC_LOG(ERROR, "Couldn't add restart work item 0x%x\n", ntStatus);
  307. delete pCtx;
  308. }
  309. break;
  310. }
  311. case SC_ACTION_REBOOT:
  312. {
  313. //
  314. // Get the reboot message for the service, if any
  315. //
  316. LPWSTR RebootMessage = NULL;
  317. ScReadRebootMessage(Key, &RebootMessage);
  318. if (RebootMessage != NULL)
  319. {
  320. //
  321. // Broadcast the message to all users. Do this in a separate
  322. // thread so that we can release our exclusive lock on the
  323. // service database quickly.
  324. //
  325. CRebootMessageContext * pCtx = new CRebootMessageContext(
  326. RebootMessage,
  327. ActionDelay,
  328. ServiceRecord->DisplayName
  329. );
  330. if (pCtx == NULL)
  331. {
  332. SC_LOG0(ERROR, "Couldn't allocate restart context\n");
  333. LocalFree(RebootMessage);
  334. break;
  335. }
  336. ntStatus = pCtx->AddWorkItem(WT_EXECUTEONLYONCE);
  337. if (!NT_SUCCESS(ntStatus))
  338. {
  339. SC_LOG(ERROR, "Couldn't add restart work item 0x%x\n", ntStatus);
  340. delete pCtx;
  341. }
  342. }
  343. else
  344. {
  345. //
  346. // Queue a work item to perform the reboot after the delay has
  347. // elapsed.
  348. // (CODEWORK Share this code with CRebootMessageContext::Perform)
  349. //
  350. LPWSTR DisplayNameCopy = LocalDup(ServiceRecord->DisplayName);
  351. CRebootContext * pCtx = new CRebootContext(
  352. ActionDelay,
  353. DisplayNameCopy
  354. );
  355. if (pCtx == NULL)
  356. {
  357. SC_LOG0(ERROR, "Couldn't allocate reboot context\n");
  358. LocalFree(DisplayNameCopy);
  359. }
  360. else
  361. {
  362. ntStatus = pCtx->AddWorkItem(WT_EXECUTEONLYONCE);
  363. if (!NT_SUCCESS(ntStatus))
  364. {
  365. SC_LOG(ERROR, "Couldn't add reboot work item 0x%x\n", ntStatus);
  366. delete pCtx;
  367. }
  368. }
  369. }
  370. }
  371. break;
  372. case SC_ACTION_RUN_COMMAND:
  373. {
  374. //
  375. // Get the failure command for the service, if any
  376. //
  377. CHeapPtr<LPWSTR> FailureCommand;
  378. ScReadFailureCommand(Key, &FailureCommand);
  379. if (FailureCommand == NULL)
  380. {
  381. SC_LOG0(ERROR, "Asked to run a failure command, but found "
  382. "none for this service\n");
  383. ScLogRecoveryFailure(
  384. SC_ACTION_RUN_COMMAND,
  385. ServiceRecord->DisplayName,
  386. ERROR_NO_RECOVERY_PROGRAM
  387. );
  388. break;
  389. }
  390. //
  391. // Replace %1% in the failure command with the failure count.
  392. // (FormatMessage is *useless* for this purpose because it AV's
  393. // if the failure command contains a %2, %3 etc.!)
  394. //
  395. UNICODE_STRING Formatted;
  396. {
  397. UNICODE_STRING Unformatted;
  398. RtlInitUnicodeString(&Unformatted, FailureCommand);
  399. Formatted.Length = 0;
  400. Formatted.MaximumLength = Unformatted.MaximumLength + 200;
  401. Formatted.Buffer =
  402. (LPWSTR) LocalAlloc(0, Formatted.MaximumLength);
  403. if (Formatted.Buffer == NULL)
  404. {
  405. SC_LOG(ERROR, "Couldn't allocate formatted string, %lu\n", GetLastError());
  406. break;
  407. }
  408. WCHAR Environment[30];
  409. wsprintf(Environment, L"1=%lu%c", FailNum, L'\0');
  410. NTSTATUS ntstatus = RtlExpandEnvironmentStrings_U(
  411. Environment,
  412. &Unformatted,
  413. &Formatted,
  414. NULL);
  415. if (!NT_SUCCESS(ntstatus))
  416. {
  417. SC_LOG(ERROR, "RtlExpandEnvironmentStrings_U failed %#lx\n", ntstatus);
  418. wcscpy(Formatted.Buffer, FailureCommand);
  419. }
  420. }
  421. CRunCommandContext * pCtx =
  422. new CRunCommandContext(ServiceRecord, Formatted.Buffer);
  423. if (pCtx == NULL)
  424. {
  425. SC_LOG0(ERROR, "Couldn't allocate RunCommand context\n");
  426. LocalFree(Formatted.Buffer);
  427. break;
  428. }
  429. ntStatus = pCtx->AddDelayedWorkItem(ActionDelay,
  430. WT_EXECUTEONLYONCE);
  431. if (!NT_SUCCESS(ntStatus))
  432. {
  433. SC_LOG(ERROR, "Couldn't add RunCommand work item 0x%x\n", ntStatus);
  434. delete pCtx;
  435. }
  436. }
  437. break;
  438. default:
  439. SC_ASSERT(0);
  440. }
  441. if (Key != NULL)
  442. {
  443. ScRegCloseKey(Key);
  444. }
  445. }
  446. DWORD
  447. CCrashRecord::IncrementCount(
  448. DWORD ResetSeconds
  449. )
  450. /*++
  451. Routine Description:
  452. Increments a service's crash count.
  453. Arguments:
  454. ResetSeconds - Length, in seconds, of a period of no crashes after which
  455. the crash count should be reset to zero.
  456. Return Value:
  457. The service's new crash count.
  458. --*/
  459. {
  460. __int64 SecondLastCrashTime = _LastCrashTime;
  461. GetSystemTimeAsFileTime((FILETIME *) &_LastCrashTime);
  462. if (ResetSeconds == INFINITE ||
  463. SecondLastCrashTime + ResetSeconds * FILETIMES_PER_SEC > _LastCrashTime)
  464. {
  465. _Count++;
  466. }
  467. else
  468. {
  469. SC_LOG(CONFIG_API, "More than %lu seconds have elapsed since last "
  470. "crash, resetting crash count.\n",
  471. ResetSeconds);
  472. _Count = 1;
  473. }
  474. SC_LOG(CONFIG_API, "Service's crash count is now %lu\n", _Count);
  475. return _Count;
  476. }
  477. VOID
  478. CRestartContext::Perform(
  479. IN BOOLEAN fWaitStatus
  480. )
  481. /*++
  482. Routine Description:
  483. --*/
  484. {
  485. //
  486. // Make sure we were called because of a timeout
  487. //
  488. SC_ASSERT(fWaitStatus == TRUE);
  489. SC_LOG(CONFIG_API, "Restarting %ws service...\n", _ServiceRecord->ServiceName);
  490. RemoveDelayedWorkItem();
  491. //
  492. // CODEWORK Allow arguments to the service.
  493. //
  494. DWORD status = ScStartServiceAndDependencies(_ServiceRecord, 0, NULL, FALSE);
  495. if (status == NO_ERROR)
  496. {
  497. status = _ServiceRecord->StartError;
  498. SC_LOG(CONFIG_API, "ScStartServiceAndDependencies succeeded, StartError = %lu\n",
  499. status);
  500. }
  501. else
  502. {
  503. SC_LOG(CONFIG_API, "ScStartServiceAndDependencies failed, %lu\n", status);
  504. //
  505. // Should we treat ERROR_SERVICE_ALREADY_RUNNING as a success?
  506. // No, because it could alert the administrator to a less-than-
  507. // optimal system configuration wherein something else is
  508. // restarting the service.
  509. //
  510. ScLogRecoveryFailure(
  511. SC_ACTION_RESTART,
  512. _ServiceRecord->DisplayName,
  513. status
  514. );
  515. }
  516. delete this;
  517. }
  518. VOID
  519. CRebootMessageContext::Perform(
  520. IN BOOLEAN fWaitStatus
  521. )
  522. /*++
  523. Routine Description:
  524. --*/
  525. {
  526. //
  527. // Broadcast the reboot message to all users
  528. //
  529. SESSION_INFO_0 * Buffer = NULL;
  530. DWORD EntriesRead = 0, TotalEntries = 0;
  531. NTSTATUS ntStatus;
  532. NET_API_STATUS Status = NetSessionEnum(
  533. NULL, // servername
  534. NULL, // UncClientName
  535. NULL, // username
  536. 0, // level
  537. (LPBYTE *) &Buffer,
  538. 0xFFFFFFFF, // prefmaxlen
  539. &EntriesRead,
  540. &TotalEntries,
  541. NULL // resume_handle
  542. );
  543. if (EntriesRead > 0)
  544. {
  545. SC_ASSERT(EntriesRead == TotalEntries);
  546. SC_ASSERT(Status == NERR_Success);
  547. WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
  548. DWORD nSize = LENGTH(ComputerName);
  549. if (!GetComputerName(ComputerName, &nSize))
  550. {
  551. SC_LOG(ERROR, "GetComputerName failed! %lu\n", GetLastError());
  552. }
  553. else
  554. {
  555. DWORD MsgLen = (DWORD) WCSSIZE(_RebootMessage);
  556. for (DWORD i = 0; i < EntriesRead; i++)
  557. {
  558. Status = NetMessageBufferSend(
  559. NULL, // servername
  560. Buffer[i].sesi0_cname, // msgname
  561. ComputerName, // fromname
  562. (LPBYTE) _RebootMessage,// buf
  563. MsgLen // buflen
  564. );
  565. if (Status != NERR_Success)
  566. {
  567. SC_LOG2(ERROR, "NetMessageBufferSend to %ws failed %lu\n",
  568. Buffer[i].sesi0_cname, Status);
  569. }
  570. }
  571. }
  572. }
  573. else if (Status != NERR_Success)
  574. {
  575. SC_LOG(ERROR, "NetSessionEnum failed %lu\n", Status);
  576. }
  577. if (Buffer != NULL)
  578. {
  579. NetApiBufferFree(Buffer);
  580. }
  581. //
  582. // Queue a work item to perform the reboot after the delay has elapsed.
  583. // Note: We're counting the delay from the time that the broadcast finished.
  584. //
  585. CRebootContext * pCtx = new CRebootContext(_Delay, _DisplayName);
  586. if (pCtx == NULL)
  587. {
  588. SC_LOG0(ERROR, "Couldn't allocate reboot context\n");
  589. }
  590. else
  591. {
  592. _DisplayName = NULL; // pCtx will free it
  593. ntStatus = pCtx->AddWorkItem(WT_EXECUTEONLYONCE);
  594. if (!NT_SUCCESS(ntStatus))
  595. {
  596. SC_LOG(ERROR, "Couldn't add reboot work item 0x%x\n", ntStatus);
  597. delete pCtx;
  598. }
  599. }
  600. delete this;
  601. }
  602. VOID
  603. CRebootContext::Perform(
  604. IN BOOLEAN fWaitStatus
  605. )
  606. /*++
  607. Routine Description:
  608. --*/
  609. {
  610. SC_LOG0(CONFIG_API, "Rebooting machine...\n");
  611. // Write an event log entry?
  612. //
  613. // Enable our shutdown privilege. Since we are shutting down, don't
  614. // bother doing it for only the current thread and don't bother
  615. // disabling it afterwards.
  616. //
  617. BOOLEAN WasEnabled;
  618. NTSTATUS Status = RtlAdjustPrivilege(
  619. SE_SHUTDOWN_PRIVILEGE,
  620. TRUE, // enable
  621. FALSE, // this thread only? - No
  622. &WasEnabled);
  623. if (!NT_SUCCESS(Status))
  624. {
  625. SC_LOG(ERROR, "RtlAdjustPrivilege failed! %#lx\n", Status);
  626. SC_ASSERT(0);
  627. }
  628. else
  629. {
  630. WCHAR wszShutdownText[128];
  631. WCHAR wszPrintableText[128 + MAX_SERVICE_NAME_LENGTH];
  632. if (LoadString(GetModuleHandle(NULL),
  633. IDS_SC_REBOOT_MESSAGE,
  634. wszShutdownText,
  635. LENGTH(wszShutdownText)))
  636. {
  637. wsprintf(wszPrintableText, wszShutdownText, _DisplayName);
  638. }
  639. else
  640. {
  641. //
  642. // If LoadString failed, it probably means the buffer
  643. // is too small to hold the localized string
  644. //
  645. SC_LOG(ERROR, "LoadString failed! %lu\n", GetLastError());
  646. SC_ASSERT(FALSE);
  647. wszShutdownText[0] = L'\0';
  648. }
  649. if (!InitiateSystemShutdownEx(NULL, // machine name
  650. wszPrintableText, // reboot message
  651. _Delay / 1000, // timeout in seconds
  652. TRUE, // force apps closed
  653. TRUE, // reboot
  654. SHTDN_REASON_MAJOR_SOFTWARE |
  655. SHTDN_REASON_MINOR_UNSTABLE))
  656. {
  657. DWORD dwError = GetLastError();
  658. //
  659. // If two services fail simultaneously and both are configured
  660. // to reboot the machine, InitiateSystemShutdown will fail all
  661. // calls past the first with ERROR_SHUTDOWN_IN_PROGRESS. We
  662. // don't want to log an event in this case.
  663. //
  664. if (dwError != ERROR_SHUTDOWN_IN_PROGRESS) {
  665. SC_LOG(ERROR, "InitiateSystemShutdown failed! %lu\n", dwError);
  666. ScLogRecoveryFailure(
  667. SC_ACTION_REBOOT,
  668. _DisplayName,
  669. dwError
  670. );
  671. }
  672. }
  673. }
  674. delete this;
  675. }
  676. VOID
  677. CRunCommandContext::Perform(
  678. IN BOOLEAN fWaitStatus
  679. )
  680. /*++
  681. Routine Description:
  682. CODEWORK Share this code with ScLogonAndStartImage
  683. --*/
  684. {
  685. //
  686. // Make sure we were called because of a timeout
  687. //
  688. SC_ASSERT(fWaitStatus == TRUE);
  689. DWORD status = NO_ERROR;
  690. HANDLE Token = NULL;
  691. PSID ServiceSid = NULL; // SID is returned only if not LocalSystem
  692. LPWSTR AccountName = NULL;
  693. SECURITY_ATTRIBUTES SaProcess; // Process security info (used only if not LocalSystem)
  694. STARTUPINFOW StartupInfo;
  695. PROCESS_INFORMATION ProcessInfo;
  696. RemoveDelayedWorkItem();
  697. //
  698. // Get the Account Name for the service. A NULL Account Name means the
  699. // service is configured to run in the LocalSystem account.
  700. //
  701. status = ScLookupServiceAccount(
  702. _ServiceRecord->ServiceName,
  703. &AccountName
  704. );
  705. // We only need to log on if it's not the LocalSystem account
  706. if (AccountName != NULL)
  707. {
  708. //
  709. // CODEWORK: Keep track of recovery EXEs spawned so we can
  710. // load/unload the user profile for the process.
  711. //
  712. status = ScLogonService(
  713. _ServiceRecord->ServiceName,
  714. AccountName,
  715. &Token,
  716. NULL,
  717. &ServiceSid
  718. );
  719. if (status != NO_ERROR)
  720. {
  721. SC_LOG(ERROR, "CRunCommandContext: ScLogonService failed, %lu\n", status);
  722. goto Clean0;
  723. }
  724. SaProcess.nLength = sizeof(SECURITY_ATTRIBUTES);
  725. SaProcess.bInheritHandle = FALSE;
  726. SC_ACE_DATA AceData[] =
  727. {
  728. {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
  729. PROCESS_ALL_ACCESS, &ServiceSid},
  730. {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
  731. PROCESS_SET_INFORMATION |
  732. PROCESS_TERMINATE |
  733. SYNCHRONIZE, &LocalSystemSid}
  734. };
  735. NTSTATUS ntstatus = ScCreateAndSetSD(
  736. AceData, // AceData
  737. LENGTH(AceData), // AceCount
  738. NULL, // OwnerSid (optional)
  739. NULL, // GroupSid (optional)
  740. &SaProcess.lpSecurityDescriptor
  741. // pNewDescriptor
  742. );
  743. LocalFree(ServiceSid);
  744. if (! NT_SUCCESS(ntstatus))
  745. {
  746. SC_LOG(ERROR, "CRunCommandContext: ScCreateAndSetSD failed %#lx\n", ntstatus);
  747. status = RtlNtStatusToDosError(ntstatus);
  748. goto Clean1;
  749. }
  750. SC_LOG2(CONFIG_API,"CRunCommandContext: about to spawn recovery program in account %ws: %ws\n",
  751. AccountName, _FailureCommand);
  752. //
  753. // Impersonate the user so we don't give access to
  754. // EXEs that have been locked down for the account.
  755. //
  756. if (!ImpersonateLoggedOnUser(Token))
  757. {
  758. status = GetLastError();
  759. SC_LOG1(ERROR,
  760. "ScLogonAndStartImage: ImpersonateLoggedOnUser failed %d\n",
  761. status);
  762. goto Clean2;
  763. }
  764. //
  765. // Spawn the Image Process
  766. //
  767. ScInitStartupInfo(&StartupInfo, FALSE);
  768. if (!CreateProcessAsUserW(
  769. Token, // logon token
  770. NULL, // lpApplicationName
  771. _FailureCommand, // lpCommandLine
  772. &SaProcess, // process' security attributes
  773. NULL, // first thread's security attributes
  774. FALSE, // whether new process inherits handles
  775. CREATE_NEW_CONSOLE, // creation flags
  776. NULL, // environment block
  777. NULL, // current directory
  778. &StartupInfo, // startup info
  779. &ProcessInfo // process info
  780. ))
  781. {
  782. status = GetLastError();
  783. SC_LOG(ERROR, "CRunCommandContext: CreateProcessAsUser failed %lu\n", status);
  784. RevertToSelf();
  785. goto Clean2;
  786. }
  787. RevertToSelf();
  788. }
  789. else
  790. {
  791. //
  792. // It's the LocalSystem account
  793. //
  794. //
  795. // If the process is to be interactive, set the appropriate flags.
  796. //
  797. BOOL bInteractive = FALSE;
  798. if (AccountName == NULL &&
  799. _ServiceRecord->ServiceStatus.dwServiceType & SERVICE_INTERACTIVE_PROCESS)
  800. {
  801. bInteractive = ScAllowInteractiveServices();
  802. if (!bInteractive)
  803. {
  804. //
  805. // Write an event to indicate that an interactive service
  806. // was started, but the system is configured to not allow
  807. // services to be interactive.
  808. //
  809. ScLogEvent(NEVENT_SERVICE_NOT_INTERACTIVE,
  810. _ServiceRecord->DisplayName);
  811. }
  812. }
  813. ScInitStartupInfo(&StartupInfo, bInteractive);
  814. SC_LOG1(CONFIG_API,"CRunCommandContext: about to spawn recovery program in "
  815. "the LocalSystem account: %ws\n", _FailureCommand);
  816. //
  817. // Spawn the Image Process
  818. //
  819. if (!CreateProcessW(
  820. NULL, // lpApplicationName
  821. _FailureCommand, // lpCommandLine
  822. NULL, // process' security attributes
  823. NULL, // first thread's security attributes
  824. FALSE, // whether new process inherits handles
  825. CREATE_NEW_CONSOLE, // creation flags
  826. NULL, // environment block
  827. NULL, // current directory
  828. &StartupInfo, // startup info
  829. &ProcessInfo // process info
  830. ))
  831. {
  832. status = GetLastError();
  833. SC_LOG(ERROR, "CRunCommandContext: CreateProcess failed %lu\n", status);
  834. goto Clean2;
  835. }
  836. }
  837. SC_LOG0(CONFIG_API, "Recovery program spawned successfully.\n");
  838. CloseHandle(ProcessInfo.hThread);
  839. CloseHandle(ProcessInfo.hProcess);
  840. Clean2:
  841. if (AccountName != NULL)
  842. {
  843. RtlDeleteSecurityObject(&SaProcess.lpSecurityDescriptor);
  844. }
  845. Clean1:
  846. if (AccountName != NULL)
  847. {
  848. CloseHandle(Token);
  849. }
  850. Clean0:
  851. if (status != NO_ERROR)
  852. {
  853. ScLogRecoveryFailure(
  854. SC_ACTION_RUN_COMMAND,
  855. _ServiceRecord->DisplayName,
  856. status
  857. );
  858. }
  859. delete this;
  860. }
  861. VOID
  862. ScLogRecoveryFailure(
  863. IN SC_ACTION_TYPE ActionType,
  864. IN LPCWSTR ServiceDisplayName,
  865. IN DWORD Error
  866. )
  867. /*++
  868. Routine Description:
  869. --*/
  870. {
  871. WCHAR wszActionString[50];
  872. if (!LoadString(GetModuleHandle(NULL),
  873. IDS_SC_ACTION_BASE + ActionType,
  874. wszActionString,
  875. LENGTH(wszActionString)))
  876. {
  877. SC_LOG(ERROR, "LoadString failed %lu\n", GetLastError());
  878. wszActionString[0] = L'\0';
  879. }
  880. ScLogEvent(
  881. NEVENT_SERVICE_RECOVERY_FAILED,
  882. ActionType,
  883. wszActionString,
  884. (LPWSTR) ServiceDisplayName,
  885. Error
  886. );
  887. }