master
cpp 881 lines 33 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 LxssInstance.cpp
8
9 Abstract:
10
11 This file contains lxss instance definitions.
12
13 --*/
14
15 #include "precomp.h"
16 #include "LxssInstance.h"
17 #include "LxssSecurity.h"
18 #include "LxssUserSession.h"
19
20 // Number of milliseconds to wait for the init process to respond.
21 #define LXSS_INIT_CONNECTION_TIMEOUT_MS (30 * 1000)
22
23 // Registry keys related to integrity level checks.
24 #define LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK L"DisableMixedIntegrityLaunch"
25 #define LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK_DISABLED 0
26 #define LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK_ENABLED 1
27
28 // Legacy folder mount information.
29 #define LXSS_CACHE_MOUNT_LXSS "/cache"
30 #define LXSS_CACHE_MOUNT_NT L"cache"
31 #define LXSS_CACHE_PERMISSIONS (0770)
32 #define LXSS_DATA_MOUNT_LXSS "/data"
33 #define LXSS_DATA_MOUNT_NT L"data"
34 #define LXSS_DATA_PERMISSIONS (0771)
35 #define LXSS_HOME_MOUNT_LXSS "/home"
36 #define LXSS_HOME_MOUNT_NT L"home"
37 #define LXSS_HOME_PERMISSIONS (0755)
38 #define LXSS_MNT_MOUNT_LXSS "/mnt"
39 #define LXSS_MNT_MOUNT_NT L"mnt"
40 #define LXSS_MNT_PERMISSIONS (0755)
41 #define LXSS_ROOT_HOME_MOUNT_LXSS "/root"
42 #define LXSS_ROOT_HOME_MOUNT_NT L"root"
43 #define LXSS_ROOT_PERMISSIONS (0700)
44 #define LXSS_ROOTFS_PERMISSIONS (0755)
45
46 extern bool g_lxcoreInitialized;
47
48 using namespace std::placeholders;
49 using namespace Microsoft::WRL;
50 using namespace wsl::windows::common::filesystem;
51
52 LxssInstance::LxssInstance(
53 _In_ const GUID& InstanceId,
54 _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,
55 _In_ ULONG DefaultUid,
56 _In_ ULONG64 ClientLifetimeId,
57 _In_ const std::function<void()>& TerminationCallback,
58 _In_ const std::function<void()>& UpdateInitCallback,
59 _In_ ULONG Flags,
60 _In_ int IdleTimeout) :
61 LxssRunningInstance(IdleTimeout),
62 m_instanceId(InstanceId),
63 m_instanceHandle(),
64 m_terminationCallback(TerminationCallback),
65 m_initialized(false),
66 m_running(false),
67 m_defaultUid(DefaultUid),
68 m_configuration(Configuration),
69 m_ntClientLifetimeId(ClientLifetimeId),
70 m_instanceBasicIntegrityLevelCheckEnabled(false),
71 m_redirectorConnectionTargets{m_configuration.Name}
72 {
73 // Running a WSL1 distro is not possible if the lxcore driver is not present.
74 THROW_HR_IF(WSL_E_WSL1_NOT_SUPPORTED, !g_lxcoreInitialized);
75
76 // Copy immutable distribution data into the info structure.
77 m_distributionInfo.Id = m_configuration.DistroId;
78 m_distributionInfo.Name = m_configuration.Name.c_str();
79 m_distributionInfo.PackageFamilyName = m_configuration.PackageFamilyName.c_str();
80 m_distributionInfo.InitPid = 1;
81
82 m_ServerPort = std::make_shared<LxssServerPort>();
83 {
84 // Create a job to hold pico processes from LXSS.
85 auto runAsSessionUser = wil::CoImpersonateClient();
86
87 m_instanceJob.reset(CreateJobObjectW(nullptr, nullptr));
88 THROW_LAST_ERROR_IF(!m_instanceJob);
89
90 Security::InitializeInstanceJob(m_instanceJob.get());
91
92 // Check if VPN detection is enabled.
93 const wil::unique_hkey LxssKey = wsl::windows::common::registry::OpenLxssUserKey();
94 m_enableVpnDetection = (wsl::windows::common::registry::ReadDword(LxssKey.get(), nullptr, L"EnableVpnDetection", 1)) != 0;
95 }
96
97 // Store the user token for access checks.
98 m_userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
99
100 // Create manual reset event that is signaled on instance termination
101 m_instanceTerminatedEvent.reset(CreateEventW(nullptr, TRUE, FALSE, nullptr));
102 THROW_LAST_ERROR_IF(!m_instanceTerminatedEvent);
103
104 // Check if integrity level check is enabled.
105 //
106 // N.B. The registry value is only writable from high-IL and above.
107 if (wsl::windows::common::registry::ReadDword(
108 HKEY_LOCAL_MACHINE, LXSS_SERVICE_REGISTRY_PATH, LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK, LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK_DISABLED) ==
109 LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK_ENABLED)
110 {
111 // Query the integrity level of the caller and store it in the instance.
112 m_instanceBasicIntegrityLevel = wsl::windows::common::security::GetUserBasicIntegrityLevel(m_userToken.get());
113 m_instanceBasicIntegrityLevelCheckEnabled = true;
114 }
115
116 // Initialize mount paths.
117 _ConfigureFilesystem(Flags);
118
119 // Update the init binary if needed.
120 UpdateInitCallback();
121
122 // Create the LXSS instance
123 _StartInstance(Configuration.Flags);
124
125 // Create a threadpool wait object to listen for instance termination
126 m_terminationWait.reset(CreateThreadpoolWait(
127 [](PTP_CALLBACK_INSTANCE, PVOID context, PTP_WAIT, TP_WAIT_RESULT) {
128 try
129 {
130 const auto instance = static_cast<LxssInstance*>(context);
131 instance->OnTerminated();
132 }
133 CATCH_LOG()
134 },
135 this,
136 nullptr));
137
138 THROW_LAST_ERROR_IF(!m_terminationWait);
139
140 SetThreadpoolWait(m_terminationWait.get(), m_instanceTerminatedEvent.get(), nullptr);
141
142 // Mark the instance as started.
143 m_running = true;
144 }
145
146 LxssInstance::~LxssInstance()
147 {
148 Stop();
149 }
150 bool CreateLxProcessIsValidStdHandle(_In_ PLXSS_HANDLE StdHandle)
151 {
152 bool IsValid = false;
153 switch (StdHandle->HandleType)
154 {
155 case LxssHandleConsole:
156 if (StdHandle->Handle == LXSS_HANDLE_USE_CONSOLE)
157 {
158 IsValid = true;
159 }
160
161 break;
162
163 case LxssHandleInput:
164 case LxssHandleOutput:
165 if (StdHandle->Handle != LXSS_HANDLE_USE_CONSOLE)
166 {
167 IsValid = true;
168 }
169
170 break;
171 }
172
173 return IsValid;
174 }
175
176 void LxssInstance::CreateLxProcess(
177 _In_ const CreateLxProcessData& CreateProcessData,
178 _In_ const CreateLxProcessContext& CreateProcessContext,
179 _In_ const CreateLxProcessConsoleData& ConsoleData,
180 _In_ SHORT Columns,
181 _In_ SHORT Rows,
182 _In_ PLXSS_STD_HANDLES StdHandles,
183 _Out_ GUID* InstanceId,
184 _Out_ HANDLE* ProcessHandle,
185 _Out_ HANDLE* ServerHandle,
186 _Out_ HANDLE* StandardIn,
187 _Out_ HANDLE* StandardOut,
188 _Out_ HANDLE* StandardErr,
189 _Out_ HANDLE* CommunicationChannel,
190 _Out_ HANDLE* InteropSocket)
191 {
192 UNREFERENCED_PARAMETER(Columns);
193 UNREFERENCED_PARAMETER(Rows);
194
195 THROW_HR_IF(E_INVALIDARG, !CreateLxProcessIsValidStdHandle(&StdHandles->StdIn));
196 THROW_HR_IF(E_INVALIDARG, !CreateLxProcessIsValidStdHandle(&StdHandles->StdOut));
197 THROW_HR_IF(E_INVALIDARG, !CreateLxProcessIsValidStdHandle(&StdHandles->StdErr));
198
199 // Check that the process is at the same basic integrity level if
200 // needed.
201 if (m_instanceBasicIntegrityLevelCheckEnabled != false)
202 {
203 const DWORD BasicIntegrityLevel =
204 wsl::windows::common::security::GetUserBasicIntegrityLevel(CreateProcessContext.UserToken.get());
205 if (m_instanceBasicIntegrityLevel != BasicIntegrityLevel)
206 {
207 if (m_instanceBasicIntegrityLevel > BasicIntegrityLevel)
208 {
209 THROW_HR(WSL_E_LOWER_INTEGRITY);
210 }
211
212 THROW_HR(WSL_E_HIGHER_INTEGRITY);
213 }
214 }
215
216 if (m_oobeCompleteEvent && !m_oobeCompleteEvent.is_signaled())
217 {
218 EMIT_USER_WARNING(wsl::shared::Localization::MessageWaitingForOobe(m_configuration.Name.c_str()));
219 m_oobeCompleteEvent.wait();
220 }
221
222 // Duplicate the handles from the calling process into the current process.
223 std::vector<wil::unique_handle> StdHandlesLocal(3);
224 if (StdHandles->StdIn.Handle != LXSS_HANDLE_USE_CONSOLE)
225 {
226 StdHandlesLocal[0].reset(wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(StdHandles->StdIn.Handle)));
227 }
228
229 if (StdHandles->StdOut.Handle != LXSS_HANDLE_USE_CONSOLE)
230 {
231 StdHandlesLocal[1].reset(wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(StdHandles->StdOut.Handle)));
232 }
233
234 if (StdHandles->StdErr.Handle != LXSS_HANDLE_USE_CONSOLE)
235 {
236 StdHandlesLocal[2].reset(wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(StdHandles->StdErr.Handle)));
237 }
238
239 // Enable symlink creation privilege on the token, which is needed
240 // to allow DrvFs to create NT symlinks.
241 //
242 // N.B. This privilege is required to create NT symlinks only when
243 // developer mode is disabled. When developer mode is enabled,
244 // acquiring the privilege can fail, but creating the symlink
245 // will succeed even without the privilege. Therefore, it does
246 // not matter if this call fails.
247 const wil::unique_handle Token = wsl::windows::common::security::GetUserToken(TokenPrimary);
248 wsl::windows::common::security::EnableTokenPrivilege(Token.get(), SE_CREATE_SYMBOLIC_LINK_NAME);
249
250 // Create an unnamed server port if the caller provided a buffer and
251 // interop is enabled.
252 wil::unique_handle ServerPort;
253 PHANDLE ServerPortPointer = nullptr;
254 if (LXSS_INTEROP_ENABLED(CreateProcessContext.Flags))
255 {
256 ServerPortPointer = &ServerPort;
257 }
258
259 // Send the create process request to the session leader.
260 bool CreatedSessionLeader;
261 const auto SessionLeader = std::static_pointer_cast<LxssMessagePort>(
262 m_consoleManager->GetSessionLeader(ConsoleData, CreateProcessContext.Elevated, &CreatedSessionLeader));
263
264 // If the session leader was just created, ensure the networking information
265 // is up-to-date.
266 if (CreatedSessionLeader)
267 {
268 _UpdateNetworkConfigurationFiles(true);
269 }
270
271 wil::unique_handle ProcessHandleLocal =
272 _CreateLxProcess(SessionLeader, CreateProcessData, StdHandlesLocal, Token, m_defaultUid, ServerPortPointer);
273
274 *InstanceId = m_instanceId;
275 *ProcessHandle = ProcessHandleLocal.release();
276 *ServerHandle = ServerPort ? ServerPort.release() : nullptr;
277 *StandardIn = nullptr;
278 *StandardOut = nullptr;
279 *StandardErr = nullptr;
280 *CommunicationChannel = nullptr;
281 *InteropSocket = nullptr;
282 }
283
284 ULONG LxssInstance::GetClientId() const
285 {
286 return LXSS_CLIENT_ID_INVALID;
287 }
288
289 GUID LxssInstance::GetDistributionId() const
290 {
291 return m_configuration.DistroId;
292 }
293
294 std::shared_ptr<LxssPort> LxssInstance::GetInitPort()
295 {
296 return m_InitMessagePort;
297 }
298
299 void LxssInstance::UpdateTimezone()
300 {
301 const auto timezone = wsl::windows::common::helpers::GetLinuxTimezone(m_userToken.get());
302 auto message = wsl::windows::common::helpers::GenerateTimezoneUpdateMessage(timezone);
303 auto lock = m_InitMessagePort->Lock();
304 m_InitMessagePort->Send(message.data(), gsl::narrow_cast<ULONG>(message.size()));
305 }
306
307 ULONG64 LxssInstance::GetLifetimeManagerId() const
308 {
309 return m_ntClientLifetimeId;
310 }
311
312 void LxssInstance::Initialize()
313 {
314 std::lock_guard<std::mutex> lock(m_stateLock);
315 if (m_initialized)
316 {
317 return;
318 }
319
320 const auto socketPath = m_configuration.BasePath / LXSS_PLAN9_UNIX_SOCKET;
321
322 // Open the communication channel with init.
323 _InitiateConnectionToInitProcess();
324
325 // Send initial configuration information to the init daemon.
326 _InitializeConfiguration(socketPath);
327
328 // Initialize networking for the instance, this will also register for network
329 // state change notifications and send the initial network information to the
330 // init process.
331 _InitializeNetworking();
332
333 // Initialization successful.
334 m_initialized = true;
335 }
336
337 void LxssInstance::OnTerminated() const
338 {
339 m_terminationCallback();
340 }
341
342 bool LxssInstance::RequestStop(_In_ bool Force)
343 {
344 std::lock_guard<std::mutex> lock(m_stateLock);
345
346 // Send the message to the init daemon to check if the instance can be terminated.
347 bool shutdown = true;
348 if (m_InitMessagePort)
349 {
350 try
351 {
352 auto lock = m_InitMessagePort->Lock();
353 LX_INIT_TERMINATE_INSTANCE terminateMessage{};
354 terminateMessage.Header.MessageType = LxInitMessageTerminateInstance;
355 terminateMessage.Header.MessageSize = sizeof(terminateMessage);
356 terminateMessage.Force = Force;
357 m_InitMessagePort->Send(&terminateMessage, sizeof(terminateMessage));
358 LX_INIT_TERMINATE_INSTANCE::TResponse terminateResponse{};
359 m_InitMessagePort->Receive(&terminateResponse, sizeof(terminateResponse));
360 shutdown = terminateResponse.Result;
361 }
362 CATCH_LOG()
363 }
364
365 return shutdown;
366 }
367
368 void LxssInstance::Stop()
369 {
370 std::lock_guard<std::mutex> lock(m_stateLock);
371
372 // Do nothing if the instance is already terminated.
373 if (!m_running)
374 {
375 return;
376 }
377
378 // Logs when a distro is stopped
379 WSL_LOG_TELEMETRY(
380 "StopInstance",
381 PDT_ProductAndServiceUsage,
382 TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA),
383 TraceLoggingValue(m_configuration.Name.c_str(), "distroName"),
384 TraceLoggingValue(LXSS_WSL_VERSION_1, "version"),
385 TraceLoggingValue(m_instanceId, "instanceId"));
386
387 // Unregister the instance termination TP wait.
388 if (m_terminationWait)
389 {
390 SetThreadpoolWait(m_terminationWait.get(), nullptr, nullptr);
391 }
392
393 // Unregister from network change notifications.
394 m_networkNotificationHandle.reset();
395
396 // Unwind in the reverse order of creation. Stop the LXSS instance, delete it.
397 LOG_IF_NTSTATUS_FAILED(::LxssClientInstanceStop(m_instanceHandle.get()));
398 m_instanceTerminatedEvent.reset();
399
400 LOG_IF_NTSTATUS_FAILED(::LxssClientInstanceDestroy(m_instanceHandle.get()));
401 m_instanceHandle.reset();
402
403 // Wait on the oobe thread.
404 if (m_oobeThread.joinable())
405 {
406 m_oobeThread.join();
407 }
408
409 // Remove the instance's Plan 9 Redirector connection targets.
410 m_redirectorConnectionTargets.RemoveAll();
411
412 // Attempt to clean up the instance's temp folder.
413 if (!m_tempPath.empty())
414 {
415 auto runAsUser = wil::impersonate_token(m_userToken.get());
416 LOG_IF_FAILED(wil::RemoveDirectoryRecursiveNoThrow(m_tempPath.c_str()));
417 }
418
419 m_running = false;
420 m_tempPath.clear();
421 m_rootDirectory.reset();
422 m_tempDirectory.reset();
423 return;
424 }
425
426 void LxssInstance::RegisterPlan9ConnectionTarget(_In_ HANDLE userToken)
427 {
428 const auto path = m_configuration.BasePath / LXSS_PLAN9_UNIX_SOCKET;
429 using unique_unicode_string = wil::unique_struct<UNICODE_STRING, decltype(RtlFreeUnicodeString), RtlFreeUnicodeString>;
430 unique_unicode_string socketPath;
431 THROW_IF_NTSTATUS_FAILED(RtlDosPathNameToNtPathName_U_WithStatus(path.c_str(), &socketPath, nullptr, nullptr));
432
433 m_redirectorConnectionTargets.AddConnectionTarget(
434 userToken, {}, m_defaultUid, std::wstring_view{socketPath.Buffer, socketPath.Length / sizeof(WCHAR)});
435 }
436
437 const WSLDistributionInformation* LxssInstance::DistributionInformation() const noexcept
438 {
439 return &m_distributionInfo;
440 }
441
442 void LxssInstance::_ConfigureFilesystem(_In_ ULONG Flags)
443 {
444 // Part of this process will try to upgrade existing LxFs folders to
445 // enable the per-directory case sensitivity flag. To allow easy detection
446 // of already processed folders, and resumption in case the process is
447 // interrupted, directories are only marked case-sensitive after their
448 // children are processed.
449 //
450 // Paths for LXSS instances look like so:
451 //
452 // <root>
453 // \rootfs <-- Where the file system is located
454 // \temp\{Instance GUID} <-- Where temporary files go
455 auto runAsUser = wil::CoImpersonateClient();
456
457 // Ensure the parent temp folder exists and is empty.
458 const auto tempFolder = m_configuration.BasePath / LXSS_TEMP_DIRECTORY;
459 EnsureDirectory(tempFolder.c_str());
460 LOG_IF_FAILED(wil::RemoveDirectoryRecursiveNoThrow(tempFolder.c_str(), wil::RemoveDirectoryOptions::KeepRootDirectory));
461
462 // Create a subdirectory to be used as the temp folder for this instance.
463 const auto instanceIdString = wsl::shared::string::GuidToString<wchar_t>(m_instanceId);
464 m_tempPath = tempFolder / instanceIdString;
465
466 // Make sure the directories of interest exist. Attributes are added
467 // separately because on upgrade directories without attributes may
468 // already exist.
469 EnsureDirectory(m_configuration.BasePath.c_str());
470
471 auto ensureDirectoryWithAttributes = [&](LPCWSTR directory, ULONG permissions) {
472 EnsureDirectoryWithAttributes(
473 (m_configuration.BasePath / directory).c_str(), permissions, LX_UID_ROOT, LX_GID_ROOT, Flags, m_configuration.Version);
474 };
475
476 ensureDirectoryWithAttributes(LXSS_ROOTFS_DIRECTORY, LXSS_ROOTFS_PERMISSIONS);
477
478 // If this is the legacy distribution, ensure that the additional LxFs
479 // directories exist and have the correct attributes. Otherwise, ensure that
480 // the rootfs/mnt directory exists for DrvFs mounts.
481 switch (m_configuration.Version)
482 {
483 case LXSS_DISTRO_VERSION_LEGACY:
484
485 WI_ASSERT(IsEqualGUID(LXSS_LEGACY_DISTRO_GUID, m_configuration.DistroId));
486
487 ensureDirectoryWithAttributes(LXSS_MNT_MOUNT_NT, LXSS_MNT_PERMISSIONS);
488 ensureDirectoryWithAttributes(LXSS_CACHE_MOUNT_NT, LXSS_CACHE_PERMISSIONS);
489 ensureDirectoryWithAttributes(LXSS_DATA_MOUNT_NT, LXSS_DATA_PERMISSIONS);
490 ensureDirectoryWithAttributes(LXSS_ROOT_HOME_MOUNT_NT, LXSS_ROOTFS_PERMISSIONS);
491 ensureDirectoryWithAttributes(LXSS_HOME_MOUNT_NT, LXSS_HOME_PERMISSIONS);
492 break;
493
494 case LXSS_DISTRO_VERSION_1:
495 case LXSS_DISTRO_VERSION_CURRENT:
496 break;
497
498 DEFAULT_UNREACHABLE;
499 }
500
501 // Wipe out and then recreate the temporary directory.
502 m_tempDirectory = WipeAndOpenDirectory(m_tempPath.c_str());
503
504 // Open handle to rootfs directory for the instance.
505 m_rootDirectory = OpenDirectoryHandle(m_configuration.BasePath.c_str(), true);
506 }
507
508 wil::unique_handle LxssInstance::_CreateLxProcess(
509 _In_ const std::shared_ptr<LxssMessagePort>& MessagePort,
510 _In_ const CreateLxProcessData& CreateProcessData,
511 _In_ const std::vector<wil::unique_handle>& StdHandles,
512 _In_ const wil::unique_handle& Token,
513 _In_ ULONG DefaultUid,
514 _Out_opt_ PHANDLE ServerPortHandle)
515 {
516 auto lock = MessagePort->Lock();
517
518 // Send a create process message for the session leader.
519 auto Message = _CreateLxProcessMarshalMessage(MessagePort, CreateProcessData, StdHandles, Token, DefaultUid);
520
521 WI_ASSERT(Message.size() <= ULONG_MAX);
522
523 const auto MessageLocal = (PLX_INIT_CREATE_PROCESS)Message.data();
524 const bool AllowOOBE = WI_IsFlagSet(MessageLocal->Common.Flags, LxInitCreateProcessFlagAllowOOBE);
525 auto HandleEraser =
526 wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ReleaseHandlesFromLxProcessMarshalMessage(MessagePort, MessageLocal); });
527
528 std::unique_ptr<LxssServerPort> ServerPort;
529 if (ARGUMENT_PRESENT(ServerPortHandle) || AllowOOBE)
530 {
531 ServerPort = std::make_unique<LxssServerPort>(MessagePort->CreateUnnamedServer(&MessageLocal->IpcServerId));
532 }
533
534 MessagePort->Send(Message.data(), gsl::narrow_cast<ULONG>(Message.size()));
535
536 if (AllowOOBE)
537 {
538 auto OobeMessagePort = ServerPort->WaitForConnection();
539
540 {
541 m_oobeCompleteEvent.create(wil::EventOptions::ManualReset);
542
543 auto impersonate = wil::CoImpersonateClient();
544 auto registration = wsl::windows::service::DistributionRegistration::Open(
545 wsl::windows::common::registry::OpenLxssUserKey().get(), m_configuration.DistroId);
546
547 // Wait for a potential previous oobe thread to complete before creating a new one.
548 if (m_oobeThread.joinable())
549 {
550 m_oobeThread.join();
551 }
552
553 m_oobeThread = std::thread([this, OobeMessagePort = std::move(OobeMessagePort), registration = std::move(registration)]() mutable {
554 try
555 {
556 // N.B. The LX_INIT_OOBE_RESULT message is only sent once the OOBE process completes, which might be waiting on user input.
557 // Do no set a timeout here otherwise the OOBE flow will fail if the OOBE process takes longer than expected.
558 auto Message = OobeMessagePort->Receive(INFINITE);
559 auto* OobeResult = gslhelpers::try_get_struct<LX_INIT_OOBE_RESULT>(gsl::make_span(Message));
560 THROW_HR_IF(E_INVALIDARG, !OobeResult || (OobeResult->Header.MessageType != LxInitOobeResult));
561
562 WSL_LOG(
563 "OOBEResult",
564 TraceLoggingValue(OobeResult->Result, "Result"),
565 TraceLoggingValue(OobeResult->DefaultUid, "DefaultUid"),
566 TraceLoggingValue(m_configuration.Name.c_str(), "Name"),
567 TraceLoggingValue(1, "Version"));
568
569 if (OobeResult->Result == 0)
570 {
571 // OOBE was successful, don't run it again.
572 m_configuration.RunOOBE = false;
573 registration.Write(wsl::windows::service::Property::RunOOBE, 0);
574
575 if (OobeResult->DefaultUid != -1)
576 {
577 registration.Write(wsl::windows::service::Property::DefaultUid, static_cast<int>(OobeResult->DefaultUid));
578 m_defaultUid = static_cast<int>(OobeResult->DefaultUid);
579 }
580
581 m_redirectorConnectionTargets.UpdateUid(m_defaultUid);
582 }
583 }
584 CATCH_LOG()
585
586 m_oobeCompleteEvent.SetEvent();
587 });
588 }
589 }
590
591 // Wait for the session leader to send the process identifier and unmarshal
592 // the process handle.
593 LXBUS_IPC_PROCESS_ID ProcessId = {};
594 MessagePort->Receive(&ProcessId, sizeof(ProcessId));
595 HandleEraser.release();
596
597 auto ProcessEraser = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {
598 // Reply to the init process that the process is not unmarshaled.
599 ProcessId = 0;
600 MessagePort->Send(&ProcessId, sizeof(ProcessId));
601 });
602
603 wil::unique_handle ProcessHandle = MessagePort->UnmarshalProcess(ProcessId);
604
605 // Reply to the init process that the process is unmarshaled.
606 ProcessId = 1;
607 MessagePort->Send(&ProcessId, sizeof(ProcessId));
608
609 ProcessEraser.release();
610
611 if (ARGUMENT_PRESENT(ServerPortHandle))
612 {
613 *ServerPortHandle = ServerPort->ReleaseServerPort();
614 }
615
616 return ProcessHandle;
617 }
618
619 std::vector<gsl::byte> LxssInstance::_CreateLxProcessMarshalMessage(
620 _In_ const std::shared_ptr<LxssMessagePort>& MessagePort,
621 _In_ const CreateLxProcessData& CreateProcessData,
622 _In_ const std::vector<wil::unique_handle>& StdHandles,
623 _In_ const wil::unique_handle& Token,
624 _In_ ULONG DefaultUid) const
625 {
626 // Allocate a message and initialize the common parameters.
627 auto Message = LxssCreateProcess::CreateMessage(LxInitMessageCreateProcess, CreateProcessData, DefaultUid);
628
629 const auto MessageLocal = (PLX_INIT_CREATE_PROCESS)Message.data();
630
631 {
632 static_assert(LX_INIT_CREATE_PROCESS_USE_CONSOLE == 0);
633 }
634
635 {
636 static_assert(LX_INIT_CREATE_PROCESS_USE_CONSOLE == LXSS_HANDLE_USE_CONSOLE);
637 }
638
639 // Marshal the standard handles.
640
641 auto Eraser =
642 wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ReleaseHandlesFromLxProcessMarshalMessage(MessagePort, MessageLocal); });
643
644 for (size_t Index = 0; Index < StdHandles.size(); ++Index)
645 {
646 if (StdHandles[Index])
647 {
648 LXBUS_IPC_MESSAGE_MARSHAL_HANDLE_DATA HandleData = {};
649 HandleData.Handle = HandleToUlong(StdHandles[Index].get());
650 if (Index == 0)
651 {
652 HandleData.HandleType = LxBusIpcMarshalHandleTypeInput;
653 }
654 else
655 {
656 HandleData.HandleType = LxBusIpcMarshalHandleTypeOutput;
657 }
658
659 MessageLocal->StdFdIds[Index] = MessagePort->MarshalHandle(&HandleData);
660 }
661 }
662
663 // Marshal the token.
664
665 {
666 // Acquire assign primary token in order to pass the primary token for the new process.
667 auto revertPriv = wsl::windows::common::security::AcquirePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME);
668 MessageLocal->ForkTokenId = MessagePort->MarshalForkToken(Token.get());
669 }
670
671 if (m_configuration.RunOOBE && CreateProcessData.Filename.empty() && CreateProcessData.CommandLine.empty())
672 {
673 WI_SetFlag(MessageLocal->Common.Flags, LxInitCreateProcessFlagAllowOOBE);
674 }
675
676 Eraser.release();
677 return Message;
678 }
679
680 void LxssInstance::_ReleaseHandlesFromLxProcessMarshalMessage(_In_ const std::shared_ptr<LxssMessagePort>& MessagePort, _In_ PLX_INIT_CREATE_PROCESS Message)
681 {
682 if (Message->ForkTokenId != 0)
683 {
684 try
685 {
686 MessagePort->ReleaseForkToken(Message->ForkTokenId);
687 }
688 CATCH_LOG()
689 }
690
691 for (ULONG Index = 0; Index < RTL_NUMBER_OF(Message->StdFdIds); ++Index)
692 {
693 if (Message->StdFdIds[Index] != LXBUS_IPC_CONSOLE_ID_INVALID)
694 {
695 try
696 {
697 MessagePort->ReleaseHandle(Message->StdFdIds[Index]);
698 }
699 CATCH_LOG()
700 }
701 }
702 }
703
704 std::vector<unique_lxss_addmount> LxssInstance::_InitializeMounts() const
705 {
706 // Legacy distributions have more than one LxFs mount. If this is a legacy
707 // distribution add the /home, /root, /data, /cache, and /mnt LxFs mounts.
708 std::vector<unique_lxss_addmount> mounts;
709 auto addMount = [&](PCWSTR directory, PCWSTR source, LPCSTR target, ULONG mode) {
710 mounts.emplace_back(CreateMount((m_configuration.BasePath / directory).c_str(), source, target, LXSS_FS_TYPE_LXFS, mode));
711 };
712
713 switch (m_configuration.Version)
714 {
715 case LXSS_DISTRO_VERSION_LEGACY:
716
717 WI_ASSERT(IsEqualGUID(LXSS_LEGACY_DISTRO_GUID, m_configuration.DistroId));
718
719 addMount(LXSS_ROOT_HOME_MOUNT_NT, LXSS_ROOT_HOME_MOUNT_NT, LXSS_ROOT_HOME_MOUNT_LXSS, LXSS_ROOT_PERMISSIONS);
720 addMount(LXSS_HOME_MOUNT_NT, LXSS_HOME_MOUNT_NT, LXSS_HOME_MOUNT_LXSS, LXSS_HOME_PERMISSIONS);
721 addMount(LXSS_DATA_MOUNT_NT, LXSS_DATA_MOUNT_NT, LXSS_DATA_MOUNT_LXSS, LXSS_DATA_PERMISSIONS);
722 addMount(LXSS_CACHE_MOUNT_NT, LXSS_CACHE_MOUNT_NT, LXSS_CACHE_MOUNT_LXSS, LXSS_CACHE_PERMISSIONS);
723 addMount(LXSS_MNT_MOUNT_NT, LXSS_MNT_MOUNT_NT, LXSS_MNT_MOUNT_LXSS, LXSS_MNT_PERMISSIONS);
724
725 default:
726 break;
727 }
728
729 return mounts;
730 }
731
732 void LxssInstance::_StartInstance(_In_ ULONG DistributionFlags)
733 {
734 std::vector<unique_lxss_addmount> mounts;
735 wil::unique_handle instanceToken;
736 {
737 // Be in the right session for creating the instance parameters.
738 const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
739 auto runAsUser = wil::impersonate_token(userToken.get());
740
741 // Initialize mount points.
742 mounts = _InitializeMounts();
743
744 // Create token for the instance
745 instanceToken = wsl::windows::common::security::CreateRestrictedToken(userToken.get());
746 }
747
748 // Create a new instance.
749 LX_KINSTANCECREATESTART createParameters = {};
750 createParameters.RootFsType = LXSS_DISTRO_USES_WSL_FS(m_configuration.Version) ? LXSS_FS_TYPE_WSLFS : LXSS_FS_TYPE_LXFS;
751 WI_SetFlagIf(createParameters.Flags, LX_KINSTANCECREATESTART_FLAG_DISABLE_DRIVE_MOUNTING, WI_IsFlagClear(DistributionFlags, LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING));
752 createParameters.InstanceId = m_instanceId;
753 createParameters.RootDirectoryHandle = HandleToULong(m_rootDirectory.get());
754 createParameters.TempDirectoryHandle = HandleToULong(m_tempDirectory.get());
755 createParameters.JobHandle = HandleToULong(m_instanceJob.get());
756 createParameters.TokenHandle = HandleToULong(instanceToken.get());
757 createParameters.KernelCommandLine = LXSS_DISTRO_DEFAULT_KERNEL_COMMAND_LINE;
758 createParameters.NumPathsToMap = static_cast<ULONG>(mounts.size());
759 createParameters.PathsToMap = static_cast<PLX_KMAPPATHS_ADDMOUNT>(mounts.data());
760 createParameters.InstanceTerminatedEventHandle = HandleToULong(m_instanceTerminatedEvent.get());
761 const wil::unique_hfile nulDevice{OpenNulDevice(GENERIC_READ | GENERIC_WRITE)};
762 LX_KINIT_FILE_DESCRIPTOR initFileDescriptors[] = {
763 {nulDevice.get(), LX_O_RDONLY, 0}, {nulDevice.get(), LX_O_WRONLY, 0}, {nulDevice.get(), LX_O_WRONLY, 0}};
764
765 createParameters.NumInitFileDescriptors = RTL_NUMBER_OF(initFileDescriptors);
766 createParameters.InitFileDescriptors = initFileDescriptors;
767
768 {
769 // Acquire assign primary token in order to pass the primary token for init process.
770 auto revertPriv = wsl::windows::common::security::AcquirePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME);
771 THROW_IF_NTSTATUS_FAILED(::LxssClientInstanceCreate(&createParameters, &m_instanceHandle));
772 }
773
774 auto deleter = wil::scope_exit([&] { LOG_IF_NTSTATUS_FAILED(::LxssClientInstanceDestroy(m_instanceHandle.get())); });
775
776 // Launch the instance.
777 const wil::unique_handle clientProcess = wsl::windows::common::wslutil::OpenCallingProcess(PROCESS_CREATE_PROCESS | SYNCHRONIZE);
778 THROW_IF_NTSTATUS_FAILED(::LxssClientInstanceStart(m_instanceHandle.get(), clientProcess.get()));
779
780 deleter.release();
781 }
782
783 void LxssInstance::_UpdateNetworkInformation()
784 try
785 {
786 // Impersonate the service.
787 auto runAsSelf = wil::run_as_self();
788
789 // Update the resolv.conf file if it has changed.
790 _UpdateNetworkConfigurationFiles(false);
791 return;
792 }
793 CATCH_LOG()
794
795 void LxssInstance::_UpdateNetworkConfigurationFiles(_In_ bool UpdateAlways)
796 {
797 // Generate contents of /etc/resolv.conf file
798 wsl::core::networking::DnsSettingsFlags flags = wsl::core::networking::DnsSettingsFlags::IncludeIpv6Servers;
799 WI_SetFlagIf(flags, wsl::core::networking::DnsSettingsFlags::IncludeVpn, m_enableVpnDetection);
800
801 const auto dnsSettings = wsl::core::networking::HostDnsInfo::GetDnsSettings(flags);
802 std::string fileContents = GenerateResolvConf(dnsSettings);
803 std::lock_guard<std::mutex> lock(m_resolvConfLock);
804 if (!UpdateAlways && (fileContents == m_lastResolvConfContents))
805 {
806 return;
807 }
808
809 // Construct the network information message.
810 wsl::shared::MessageWriter<LX_INIT_NETWORK_INFORMATION> message(LxInitMessageNetworkInformation);
811 message.WriteString(message->FileHeaderIndex, wsl::shared::string::WideToMultiByte(LX_INIT_RESOLVCONF_FULL_HEADER));
812 message.WriteString(message->FileContentsIndex, fileContents);
813 auto messageSpan = message.Span();
814
815 // Send the message.
816 auto messagePortLock = m_InitMessagePort->Lock();
817 m_InitMessagePort->Send(messageSpan.data(), gsl::narrow_cast<ULONG>(messageSpan.size()));
818 m_lastResolvConfContents = std::move(fileContents);
819 }
820
821 void LxssInstance::_InitializeNetworking()
822 {
823 m_ipTables.EnableIpTablesSupport(m_instanceHandle);
824
825 // Register for network connectivity change notifications to update network information.
826 LOG_IF_WIN32_ERROR(::NotifyNetworkConnectivityHintChange(
827 [](PVOID context, NL_NETWORK_CONNECTIVITY_HINT) { static_cast<LxssInstance*>(context)->_UpdateNetworkInformation(); }, this, TRUE, &m_networkNotificationHandle));
828 }
829
830 void LxssInstance::_InitiateConnectionToInitProcess()
831 {
832 // Register the server, wait for the init process to connect to the server,
833 // and create the message event.
834 m_ServerPort->RegisterLxBusServer(m_instanceHandle, LX_INIT_SERVER_NAME);
835 std::unique_ptr<LxssMessagePort> NewMessagePort = m_ServerPort->WaitForConnection(LXSS_INIT_CONNECTION_TIMEOUT_MS);
836
837 // Associate the server port with the new message port and create a console
838 // manager that will be used to manage session leaders.
839 NewMessagePort->SetServerPort(m_ServerPort);
840 m_InitMessagePort = std::make_shared<LxssMessagePort>(std::move(NewMessagePort));
841 m_consoleManager = ConsoleManager::CreateConsoleManager(m_InitMessagePort);
842 }
843
844 void LxssInstance::_InitializeConfiguration(_In_ const std::filesystem::path& Plan9SocketPath)
845 {
846 // If DrvFs mounting is supported, initialize a bitmap of all fixed drives.
847 ULONG fixedDrives = 0;
848 if (WI_IsFlagSet(m_configuration.Flags, LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING))
849 {
850 fixedDrives = EnumerateFixedDrives().first;
851 }
852
853 const auto timezone = wsl::windows::common::helpers::GetLinuxTimezone(m_userToken.get());
854 ULONG featureFlags{};
855 WI_SetFlagIf(featureFlags, LxInitFeatureRootfsCompressed, WI_IsFlagSet(GetFileAttributesW(m_configuration.BasePath.c_str()), FILE_ATTRIBUTE_COMPRESSED));
856 auto message = wsl::windows::common::helpers::GenerateConfigurationMessage(
857 m_configuration.Name, fixedDrives, m_defaultUid, timezone, Plan9SocketPath.wstring(), featureFlags);
858
859 // Send the message to the init daemon.
860 auto lock = m_InitMessagePort->Lock();
861 m_InitMessagePort->Send(message.data(), static_cast<ULONG>(message.size()));
862
863 // Init replies with information about the distribution.
864 auto buffer = m_InitMessagePort->Receive();
865 const auto span = gsl::make_span(buffer);
866 const auto* response = gslhelpers::try_get_struct<LX_INIT_CONFIGURATION_INFORMATION_RESPONSE>(span);
867 THROW_HR_IF(E_UNEXPECTED, !response || response->Header.MessageType != LxInitMessageInitializeResponse);
868
869 m_defaultUid = response->DefaultUid;
870 if (response->VersionIndex > 0)
871 {
872 m_configuration.OsVersion = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(span, response->VersionIndex));
873 m_distributionInfo.Version = m_configuration.OsVersion.c_str();
874 }
875
876 if (response->FlavorIndex > 0)
877 {
878 m_configuration.Flavor = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(span, response->FlavorIndex));
879 m_distributionInfo.Flavor = m_configuration.Flavor.c_str();
880 }
881 }