master
cpp 331 lines 11.4 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 ServiceMain.cpp
8
9 Abstract:
10
11 This file contains the entrypoint for the Lxss Manager service.
12
13 --*/
14
15 #include "precomp.h"
16 #include "comservicehelper.h"
17 #include "LxssSecurity.h"
18 #include "WslCoreFilesystem.h"
19 #include "LxssIpTables.h"
20 #include "LxssUserSessionFactory.h"
21 #include "WSLCSessionManagerFactory.h"
22 #include <ctime>
23
24 using namespace wsl::windows::common::registry;
25 using namespace wsl::windows::common::string;
26 using namespace wsl::windows::common::timestamp;
27 using namespace wsl::windows::common::wslutil;
28 using namespace wsl::windows::policies;
29
30 bool g_lxcoreInitialized{false};
31 wil::unique_event g_networkingReady{wil::EventOptions::ManualReset};
32
33 wsl::windows::service::PluginManager g_pluginManager;
34
35 // Declare the LxssUserSession COM class.
36 CoCreatableClassWrlCreatorMapInclude(LxssUserSession);
37
38 // Declare the WSLCSessionManager COM class.
39 CoCreatableClassWrlCreatorMapInclude(WSLCSessionManager);
40
41 struct WslServiceSecurityPolicy
42 {
43 static LPCWSTR GetSDDLText()
44 {
45 // COM Access and Launch permissions allowed for authenticated user, principal self, and system.
46 // 0xB = (COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL | COM_RIGHTS_ACTIVATE_LOCAL)
47 // N.B. This should be kept in sync with the security descriptors in the appxmanifest and package.wix.
48 return L"O:BAG:BAD:(A;;0xB;;;AU)(A;;0xB;;;PS)(A;;0xB;;;SY)";
49 }
50 };
51
52 class WslService : public Windows::Internal::Service<WslService, Windows::Internal::ContinueRunningWithNoObjects, WslServiceSecurityPolicy>
53 {
54 public:
55 static wchar_t* GetName()
56 {
57 return const_cast<LPWSTR>(L"WslService");
58 }
59
60 static void OnSessionChanged(DWORD eventType, DWORD sessionId);
61 HRESULT OnServiceStarting();
62 HRESULT ServiceStarted();
63 void ServiceStopped();
64
65 private:
66 static void __stdcall CheckForUpdates(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_ PVOID Context, _Inout_ PTP_TIMER);
67 static void ApplyProcessPolicies();
68
69 void CreateExplorerExtensions() noexcept;
70 void EvaluateWslPolicy();
71 void Initialize();
72 static void InitializePlan9Redirector();
73 void RegisterEventSource();
74 void StartCheckingForUpdates();
75
76 wil::unique_couninitialize_call m_coInit{false};
77 wil::unique_registry_watcher m_watcher;
78 wil::unique_threadpool_timer m_updateCheckTimer;
79 wil::unique_any_handle_null<decltype(&::DeregisterEventSource), ::DeregisterEventSource> m_eventLog;
80 };
81
82 void WslService::EvaluateWslPolicy()
83 {
84 // If WSL is disabled, terminate any sessions and block future sessions from being created.
85 //
86 // N.B. This is done instead of failing service start so a proper error can be returned to the user.
87 const auto policiesKey = OpenPoliciesKey();
88 const auto enabled = IsFeatureAllowed(policiesKey.get(), c_allowWSL);
89 if (enabled)
90 {
91 Initialize();
92 }
93
94 SetSessionPolicy(enabled);
95 }
96
97 void WslService::Initialize()
98 {
99 static std::once_flag flag{};
100 std::call_once(flag, [&]() {
101 // Initialize the connection to the LxCore driver.
102 //
103 // N.B. The WSL optional component is required on Windows 10. On Windows 11 and later,
104 // the lifted WSL service can run but will only support WSL2 distros.
105 g_lxcoreInitialized = NT_SUCCESS(::LxssClientInitialize());
106
107 try
108 {
109 // Initialize the Plan 9 redirector (can fail iff the OC is not enabled on Win10).
110 // Failures here are silently ignored because we don't want the service to fail to start in that case
111 // so it can return WSL_E_WSL_OPTIONAL_COMPONENT_REQUIRED in LxssUserSession
112 InitializePlan9Redirector();
113 }
114 CATCH_LOG()
115
116 RegisterEventSource();
117 });
118 }
119
120 void WslService::InitializePlan9Redirector()
121 {
122 // Make sure that the Plan 9 redirector trigger start prefix is correct.
123 try
124 {
125 // Acquire backup and restore privileges to modify the P9NP trigger start registry key.
126 auto restore = wsl::windows::common::security::AcquirePrivileges({SE_BACKUP_NAME, SE_RESTORE_NAME});
127
128 // Read the P9NP registry key and ensure it contains the correct value.
129 constexpr auto* keyName = L"SYSTEM\\CurrentControlSet\\Services\\P9NP\\NetworkProvider";
130 const auto key = CreateKey(HKEY_LOCAL_MACHINE, keyName, (KEY_READ | KEY_SET_VALUE), nullptr, REG_OPTION_BACKUP_RESTORE);
131 constexpr auto* valueName = L"TriggerStartPrefix";
132 DWORD valueType{};
133 THROW_IF_WIN32_ERROR(RegGetValueW(key.get(), nullptr, valueName, (RRF_RT_ANY | RRF_NOEXPAND), &valueType, nullptr, nullptr));
134 if (valueType != REG_MULTI_SZ)
135 {
136 // Because older Windows 10 builds won't have the p9rdr changes to support TriggerStartPrefix being a REG_MULTI_SZ,
137 // make sure that this build has the updated AppIdFlags value (added to support vp9fs being called from packaged context),
138 // which was added in the same commit.
139 // This theoretically shouldn't happen since the package shouldn't install on Windows 10 builds that are too old to
140 // support lifted, but if this block ran on such a build it would completely break p9rdr, so better safe than sorry.
141 if (!wsl::windows::common::helpers::IsWindows11OrAbove())
142 {
143 auto appIdFlags = ReadDword(HKEY_CLASSES_ROOT, L"AppID\\{DFB65C4C-B34F-435D-AFE9-A86218684AA8}", L"AppIdFlags", 0);
144 THROW_HR_IF_MSG(
145 E_UNEXPECTED,
146 WI_IsFlagClear(appIdFlags, APPIDREGFLAGS_AAA_NO_IMPLICIT_ACTIVATE_AS_IU),
147 "TriggerStartPrefix needs update, but AppIdFlags isn't up to date");
148 }
149
150 WSL_LOG("Updating TriggerStartPrefix", TraceLoggingLevel(WINEVENT_LEVEL_INFO));
151
152 constexpr wchar_t newValue[] = L"wsl.localhost\0wsl$\0";
153 THROW_IF_WIN32_ERROR(RegSetValueEx(key.get(), valueName, 0, REG_MULTI_SZ, (BYTE*)newValue, sizeof(newValue)));
154 }
155 }
156 CATCH_LOG()
157
158 // Make sure the Plan 9 redirector driver is loaded.
159 wsl::windows::common::redirector::EnsureRedirectorStarted();
160 }
161
162 HRESULT WslService::OnServiceStarting()
163 try
164 {
165 ConfigureCrt();
166
167 // Enable contextualized errors
168 wsl::windows::common::EnableContextualizedErrors(true, false, true);
169
170 // Initialize telemetry.
171 WslTraceLoggingInitialize(WslServiceTelemetryProvider, !wsl::shared::OfficialBuild);
172
173 WSL_LOG("Service starting", TraceLoggingLevel(WINEVENT_LEVEL_INFO));
174
175 // Don't kill the process on unknown C++ exceptions.
176 wil::g_fResultFailFastUnknownExceptions = false;
177
178 wsl::windows::common::security::ApplyProcessMitigationPolicies();
179
180 // Initialize Winsock.
181 WSADATA Data;
182 THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &Data));
183
184 // Load plugins.
185 g_pluginManager.LoadPlugins();
186
187 // Check if WSL is disabled via policy and set up a registry watcher to watch for changes.
188 //
189 // N.B. The registry watcher must be created before checking the policy to avoid missing notifications.
190 m_watcher = wil::make_registry_watcher(HKEY_LOCAL_MACHINE, ROOT_POLICIES_KEY, true, [this](wil::RegistryChangeKind) {
191 try
192 {
193 EvaluateWslPolicy();
194 }
195 CATCH_LOG()
196 });
197
198 EvaluateWslPolicy();
199
200 wsl::windows::common::helpers::RegisterWithDcat();
201
202 return S_OK;
203 }
204 CATCH_RETURN()
205
206 void WslService::RegisterEventSource()
207 try
208 {
209 m_eventLog.reset(::RegisterEventSource(nullptr, L"WSL"));
210 THROW_LAST_ERROR_IF(!m_eventLog);
211
212 wsl::windows::common::SetEventLog(m_eventLog.get());
213 }
214 CATCH_LOG();
215
216 HRESULT WslService::ServiceStarted()
217 {
218 m_coInit = wil::CoInitializeEx(COINIT_MULTITHREADED);
219
220 // Cleanup any data from a previously aborted session (crash, power loss, etc).
221 LxssIpTables::CleanupRemnants();
222 g_networkingReady.SetEvent();
223
224 if constexpr (wsl::shared::OfficialBuild)
225 {
226 StartCheckingForUpdates();
227 }
228
229 return S_OK;
230 }
231
232 void WslService::OnSessionChanged(DWORD eventType, DWORD sessionId)
233 {
234 if (eventType == WTS_SESSION_LOGOFF)
235 {
236 TerminateSession(sessionId);
237 }
238 }
239
240 void WslService::ServiceStopped()
241 {
242 WSL_LOG("Service stopping", TraceLoggingLevel(WINEVENT_LEVEL_INFO));
243
244 // Stop checking for updates.
245 m_updateCheckTimer.reset();
246
247 // Stop watching the WSL policy registry keys.
248 m_watcher.reset();
249
250 // Terminate all user sessions.
251 ClearSessionsAndBlockNewInstances();
252
253 // Also tear down WSLC sessions.
254 wsl::windows::service::wslc::ClearWslcSessionsAndBlockNewInstances();
255
256 // Disconnect from the LxCore driver.
257 if (g_lxcoreInitialized)
258 {
259 LxssClientUninitialize();
260 }
261
262 // There is a potential deadlock if CoUninitialize() is called before the LanguageChangeNotifyThread
263 // isn't done initializing. Clearing the COM objects before calling CoUninitialize() works around the issue.
264 winrt::clear_factory_cache();
265
266 // Tear down telemetry.
267 WslTraceLoggingUninitialize();
268
269 // uninitialize COM. This must be done here because this call can cause cleanups that will be fail
270 // if the CRT is shutting down.
271 m_coInit.reset();
272 }
273
274 void WslService::StartCheckingForUpdates()
275 try
276 {
277 const auto lxssKey = OpenLxssMachineKey(KEY_QUERY_VALUE);
278 constexpr std::uint64_t c_updateCheckPeriodDefaultMs = 24 * 60 * 60 * 1000; // 24h
279 const auto period =
280 wsl::windows::common::registry::ReadDword(lxssKey.get(), nullptr, L"UpdateCheckPeriodMs", c_updateCheckPeriodDefaultMs);
281
282 if (period <= 0)
283 {
284 WSL_LOG("Update check is disabled via the registry", TraceLoggingLevel(WINEVENT_LEVEL_INFO));
285
286 return;
287 }
288
289 m_updateCheckTimer.reset(CreateThreadpoolTimer(WslService::CheckForUpdates, this, nullptr));
290 THROW_LAST_ERROR_IF_NULL(m_updateCheckTimer);
291
292 // Check for updates at the configured period, starting one minute after the service starts.
293 auto dueTime = wil::filetime::from_int64(static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_minute));
294 SetThreadpoolTimer(m_updateCheckTimer.get(), &dueTime, period, 60 * 1000);
295 }
296 CATCH_LOG()
297
298 void WslService::CheckForUpdates(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_ PVOID Context, _Inout_ PTP_TIMER)
299 try
300 {
301 auto [version, _] = GetLatestGitHubRelease(false);
302 if (ParseWslPackageVersion(version) > ParseWslPackageVersion(TEXT(WSL_PACKAGE_VERSION)))
303 {
304 WSL_LOG("WSL Package update is available", TraceLoggingLevel(WINEVENT_LEVEL_INFO));
305
306 // Reset the timer since there's no reason to check for updates anymore.
307 SetThreadpoolTimer(static_cast<WslService*>(Context)->m_updateCheckTimer.get(), nullptr, 0, 0);
308
309 // Get current release date
310 const std::wstring currentReleaseCreatedAtDate = GetGitHubReleaseByTag(TEXT(WSL_PACKAGE_VERSION)).created_at;
311
312 const auto tp = std::chrono::system_clock::from_time_t(
313 static_cast<std::time_t>(Rfc3339ToEpoch(WideToMultiByte(currentReleaseCreatedAtDate))));
314
315 // If their release of WSL is older than 30 days, then show a notification to update
316 if (std::chrono::system_clock::now() - std::chrono::days(30) > tp)
317 {
318 // Create a notification to inform the user that an update is available
319 THROW_IF_FAILED(wsl::windows::common::notifications::DisplayUpdateNotification(version));
320
321 WSL_LOG("WSL Package update notification displayed", TraceLoggingLevel(WINEVENT_LEVEL_INFO));
322 }
323 }
324 }
325 CATCH_LOG()
326
327 int __cdecl wmain()
328 {
329 WslService::ProcessMain();
330 return 0;
331 }