master
cpp 236 lines 7.14 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 LxssUserSessionFactory.cpp
8
9 Abstract:
10
11 This file contains user session factory function definitions.
12
13 --*/
14
15 #include "precomp.h"
16 #include "LxssSecurity.h"
17 #include "LxssUserSessionFactory.h"
18 #include "PluginManager.h"
19
20 using namespace Microsoft::WRL;
21 using namespace Security;
22 using namespace wil;
23
24 bool g_disabledByPolicy{false};
25
26 // Note: g_sessionTerminationLock must always be acquired before g_sessionLock
27 std::recursive_mutex g_sessionTerminationLock;
28 srwlock g_sessionLock;
29
30 std::optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>> g_sessions =
31 std::make_optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>>();
32
33 extern wsl::windows::service::PluginManager g_pluginManager;
34
35 extern unique_event g_networkingReady;
36 extern bool g_lxcoreInitialized;
37
38 void ClearSessionsAndBlockNewInstancesLockHeld(std::optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>>& sessions)
39 {
40 std::lock_guard lock(g_sessionTerminationLock);
41
42 if (sessions)
43 {
44 // Shutdown the session and prevent new session creation.
45 for (const auto& session : sessions.value())
46 {
47 // Because Shutdown() acquires the session inner lock, it shouldn't called while g_sessionLock is held,
48 // since that could lead to a deadlock if FindSessionByCookie is called since that would try to lock g_sessionLock
49 // while holding the session inner lock
50
51 session->Shutdown(true, ShutdownBehavior::ForceAfter30Seconds);
52 }
53
54 sessions.reset();
55 }
56 }
57
58 void ClearSessionsAndBlockNewInstances()
59 {
60 std::optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>> sessions;
61
62 {
63 auto sessionsLock = g_sessionLock.lock_exclusive();
64 sessions = std::move(g_sessions);
65
66 // This is required because the moved-from std::optional<T> isn't made empty, so this needs to be done explicitly.
67 g_sessions.reset();
68 }
69
70 ClearSessionsAndBlockNewInstancesLockHeld(sessions);
71 }
72
73 void SetSessionPolicy(_In_ bool enabled)
74 {
75 std::lock_guard lock(g_sessionTerminationLock);
76
77 if (enabled)
78 {
79 auto sessionsLock = g_sessionLock.lock_exclusive();
80 if (!g_sessions)
81 {
82 g_sessions = std::make_optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>>();
83 }
84 }
85 else
86 {
87 ClearSessionsAndBlockNewInstances();
88 }
89
90 g_disabledByPolicy = !enabled;
91 }
92
93 std::shared_ptr<LxssUserSessionImpl> FindSessionByCookie(_In_ DWORD Cookie)
94 {
95 // Find a session with a matching session ID and terminate it.
96 //
97 // N.B. Sessions launched from session zero will only be terminated when the
98 // service is stopped.
99 auto lock = g_sessionLock.lock_exclusive();
100 if (!g_sessions.has_value())
101 {
102 return {};
103 }
104
105 const auto found = std::find_if(std::begin(g_sessions.value()), std::end(g_sessions.value()), [Cookie](const auto& session) {
106 return (session->GetSessionCookie() == Cookie);
107 });
108
109 return found == g_sessions->end() ? std::shared_ptr<LxssUserSessionImpl>() : *found;
110 }
111
112 void TerminateSession(_In_ DWORD sessionId)
113 {
114 // Find a session with a matching session ID and terminate it.
115 //
116 // N.B. Sessions launched from session zero will only be terminated when the
117 // service is stopped.
118
119 std::lock_guard lock(g_sessionTerminationLock);
120
121 std::shared_ptr<LxssUserSessionImpl> session;
122
123 {
124 auto lock = g_sessionLock.lock_exclusive();
125 if (!g_sessions.has_value())
126 {
127 return;
128 }
129
130 const auto found = std::find_if(std::begin(g_sessions.value()), std::end(g_sessions.value()), [&sessionId](const auto& session) {
131 return (session->GetSessionId() == sessionId);
132 });
133
134 if (found != g_sessions->end())
135 {
136 // Shutdown the session and prevent new instance creation.
137 session = std::move(*found);
138 g_sessions->erase(found);
139 }
140 }
141
142 if (session)
143 {
144 session->Shutdown(true);
145 }
146 }
147
148 CoCreatableClassWithFactory(LxssUserSession, LxssUserSessionFactory);
149
150 HRESULT LxssUserSessionFactory::CreateInstance(_In_ IUnknown* pUnkOuter, _In_ REFIID riid, _Out_ void** ppCreated)
151 {
152 RETURN_HR_IF_NULL(E_POINTER, ppCreated);
153 *ppCreated = nullptr;
154
155 RETURN_HR_IF(CLASS_E_NOAGGREGATION, pUnkOuter != nullptr);
156
157 WSL_LOG("LxssUserSessionCreateInstanceBegin", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
158
159 // Wait for the network cleanup to be done before continuing.
160 g_networkingReady.wait();
161
162 try
163 {
164 auto instance = CreateInstanceForCurrentUser();
165 const auto userSession = wil::MakeOrThrow<LxssUserSession>(instance);
166 THROW_IF_FAILED(userSession.CopyTo(riid, ppCreated));
167 }
168 catch (...)
169 {
170 const auto result = wil::ResultFromCaughtException();
171
172 // Note: S_FALSE will cause COM to retry if the service is stopping.
173 return result == CO_E_SERVER_STOPPING ? S_FALSE : result;
174 }
175
176 WSL_LOG("LxssUserSessionCreateInstanceEnd", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
177
178 return S_OK;
179 }
180
181 _Requires_lock_held_(g_sessionLock)
182 std::shared_ptr<LxssUserSessionImpl> FindSessionLockHeld(PSID User)
183 {
184 // Fail if the service is stopping (see ClearSessionsAndBlockNewInstances()).
185 THROW_HR_IF(CO_E_SERVER_STOPPING, !g_sessions.has_value());
186
187 const auto found = std::find_if(std::begin(g_sessions.value()), std::end(g_sessions.value()), [&](const auto& session) {
188 return (::EqualSid(User, session->GetUserSid()));
189 });
190
191 if (found != g_sessions->end())
192 {
193 return *found;
194 }
195 else
196 {
197 return {};
198 }
199 }
200
201 std::weak_ptr<LxssUserSessionImpl> CreateInstanceForCurrentUser()
202 {
203 // Do not create sessions for localsystem.
204 const unique_handle userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
205 THROW_HR_IF(WSL_E_LOCAL_SYSTEM_NOT_SUPPORTED, wsl::windows::common::security::IsTokenLocalSystem(userToken.get()));
206
207 // Get the session ID and SID of the client process.
208 DWORD sessionId{};
209 DWORD length = 0;
210 THROW_IF_WIN32_BOOL_FALSE(::GetTokenInformation(userToken.get(), TokenSessionId, &sessionId, sizeof(sessionId), &length));
211
212 const auto tokenInfo = wil::get_token_information<TOKEN_USER>(userToken.get());
213
214 // Find an existing session or create a new one.
215 std::shared_ptr<LxssUserSessionImpl> userSession;
216 {
217 std::lock_guard sessionLock(g_sessionTerminationLock);
218 auto lock = g_sessionLock.lock_exclusive();
219
220 // Do not allow session creation if WSL is disabled via policy.
221 THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY), g_disabledByPolicy);
222
223 // Builds prior to Windows 10 require the WSL optional component.
224 THROW_HR_IF(WSL_E_WSL_OPTIONAL_COMPONENT_REQUIRED, !g_lxcoreInitialized && !wsl::windows::common::helpers::IsWindows11OrAbove());
225
226 userSession = FindSessionLockHeld(tokenInfo->User.Sid);
227
228 if (!userSession)
229 {
230 userSession.reset(new LxssUserSessionImpl(tokenInfo->User.Sid, sessionId, g_pluginManager));
231 g_sessions->emplace_back(userSession);
232 }
233 }
234
235 return userSession;
236 }