@@ -5,7 +5,7 @@ use std::ffi::{OsStr, OsString};
5
5
use std:: fs:: { self , File } ;
6
6
use std:: io:: { BufReader , BufWriter , Write } ;
7
7
use std:: os:: linux:: fs:: MetadataExt ;
8
- use std:: os:: unix:: prelude:: CommandExt ;
8
+ use std:: os:: unix:: prelude:: { CommandExt , OsStrExt } ;
9
9
use std:: path:: { Path , PathBuf } ;
10
10
use std:: process:: Command ;
11
11
@@ -44,6 +44,8 @@ impl DistroLauncher {
44
44
} ;
45
45
set_wsl_interop_envs_in_system_envs ( & mut distro_launcher)
46
46
. with_context ( || "failed to set up WSL interop env vars" ) ?;
47
+ mount_kernelcmdline_with_wsl_interop_envs_for_systemd ( & mut distro_launcher)
48
+ . with_context ( || "Failed to mount the custom /proc/cmdline" ) ?;
47
49
set_per_user_wsl_envs ( & mut distro_launcher)
48
50
. with_context ( || "failed to mount WSL environment variables init script." ) ?;
49
51
mount_slash_run_static_files ( & mut distro_launcher)
@@ -211,39 +213,92 @@ impl DistroLauncher {
211
213
}
212
214
213
215
fn set_wsl_interop_envs_in_system_envs ( distro_launcher : & mut DistroLauncher ) -> Result < ( ) > {
214
- // Be careful to collect only harmless environment variables.
216
+ for ( key, value) in collect_safe_wsl_interop_envs ( )
217
+ . with_context ( || "Failed to collect safe WSL interop envs" ) ?
218
+ {
219
+ distro_launcher. with_system_env (
220
+ key. to_string_lossy ( ) . to_string ( ) ,
221
+ value. to_string_lossy ( ) . to_string ( ) ,
222
+ ) ;
223
+ distro_launcher
224
+ . container_launcher
225
+ . with_init_arg ( & env_to_systemd_setenv_arg ( key, value) ) ;
226
+ }
227
+ Ok ( ( ) )
228
+ }
229
+
230
+ fn mount_kernelcmdline_with_wsl_interop_envs_for_systemd (
231
+ distro_launcher : & mut DistroLauncher ,
232
+ ) -> Result < ( ) > {
233
+ let cmdline_overwrite_path = get_cmdline_overwrite_path ( )
234
+ . with_context ( || "Failed to get the /proc/cmdline overwrite path." ) ?;
235
+ std:: fs:: write (
236
+ cmdline_overwrite_path. as_path ( ) ,
237
+ get_cmdline_with_wsl_interop_envs_for_systemd ( "/proc/cmdline" )
238
+ . with_context ( || "Failed to generate the contents of new /proc/cmdline" ) ?,
239
+ )
240
+ . with_context ( || format ! ( "Failed to write to {:?}" , & cmdline_overwrite_path) ) ?;
241
+
242
+ distro_launcher. with_mount (
243
+ Some ( HostPath :: new ( cmdline_overwrite_path) ?) , // /run is bind-mounted
244
+ ContainerPath :: new ( "/proc/cmdline" ) ?,
245
+ None ,
246
+ nix:: mount:: MsFlags :: MS_BIND ,
247
+ None ,
248
+ true ,
249
+ ) ;
250
+ Ok ( ( ) )
251
+ }
252
+
253
+ fn get_cmdline_with_wsl_interop_envs_for_systemd < P : AsRef < Path > > (
254
+ cmdline_path : P ,
255
+ ) -> Result < Vec < u8 > > {
256
+ let mut cmdline = std:: fs:: read ( cmdline_path. as_ref ( ) )
257
+ . with_context ( || format ! ( "Failed to read {:?}." , cmdline_path. as_ref( ) ) ) ?;
258
+ if cmdline. ends_with ( "\n " . as_bytes ( ) ) {
259
+ cmdline. truncate ( cmdline. len ( ) - 1 ) ;
260
+ }
261
+
262
+ // Set default environment vairables for the systemd services.
263
+ for ( key, value) in
264
+ collect_safe_wsl_interop_envs ( ) . with_context ( || "Failed to collect WSL envs." ) ?
265
+ {
266
+ cmdline. extend ( " " . as_bytes ( ) ) ;
267
+ cmdline. extend ( env_to_systemd_setenv_arg ( & key, & value) . as_bytes ( ) ) ;
268
+ }
269
+ cmdline. extend ( "\n " . as_bytes ( ) ) ;
270
+
271
+ Ok ( cmdline)
272
+ }
273
+
274
+ fn collect_safe_wsl_interop_envs ( ) -> Result < Vec < ( OsString , OsString ) > > {
275
+ // Collect only harmless environment variables.
215
276
// Distrod can be running as setuid program. So, non-root user can set arbitrary values.
216
- // mount_per_user_wsl_envs_script and its family should source those arbitrary values instead .
277
+ // Thus, mount_per_user_wsl_envs_script or similar functions should collect only restricted value .
217
278
// Actually, this doesn't matter for now because WSL allows a non-root user to be root by `wsl.exe -u root`,
218
279
// but be prepared for this WSL spec to be improved in a safer direction someday.
219
- let wsl_envs = collect_wsl_env_vars ( ) . with_context ( || "Failed to collect WSL envs." ) ? ;
280
+ let mut envs = vec ! [ ] ;
220
281
let envs_to_set = [
221
282
OsStr :: new ( "WSL_INTEROP" ) ,
222
283
OsStr :: new ( "WSLENV" ) ,
223
284
OsStr :: new ( "WSL_DISTRO_NAME" ) ,
224
285
] ;
225
- for ( key, value) in & wsl_envs {
286
+ for ( key, value) in collect_wsl_env_vars ( ) . with_context ( || "Failed to collect WSL envs." ) ? {
226
287
if !envs_to_set. contains ( & key. as_os_str ( ) ) {
227
288
continue ;
228
289
}
229
- if !sanity_check_wsl_env ( key, value) {
290
+ if !sanity_check_wsl_env ( & key, & value) {
230
291
log:: warn!( "sanity check of {:?} failed." , & key) ;
231
292
// stop handling this and further envs
232
- break ;
293
+ continue ;
233
294
}
234
- distro_launcher. with_system_env (
235
- key. to_string_lossy ( ) . to_string ( ) ,
236
- value. to_string_lossy ( ) . to_string ( ) ,
237
- ) ;
238
- distro_launcher
239
- . container_launcher
240
- . with_init_arg ( & env_to_systemd_setenv_arg ( key, value) ) ;
295
+ envs. push ( ( key, value) ) ;
241
296
}
242
- Ok ( ( ) )
297
+ Ok ( envs )
243
298
}
244
299
245
300
/// Make sure that the values of WSL_INTEROP, WSLENV, and WSL_DISTRO_NAME are harmless values that can be
246
- /// written to /etc/environment and passed to Systemd service processes . These values may be polluted
301
+ /// written to /etc/environment and passed to Systemd via /proc/cmdline . These values may be polluted
247
302
/// because distrod-exec can be launched by any user.
248
303
fn sanity_check_wsl_env ( key : & OsStr , value : & OsStr ) -> bool {
249
304
if key == OsStr :: new ( "WSL_INTEROP" ) {
@@ -267,13 +322,20 @@ fn sanity_check_wsl_interop(value: &OsStr) -> bool {
267
322
fn sanity_check_general_wsl_envs ( value : & OsStr ) -> bool {
268
323
// sanity check for WSLENV and WSL_INTEROP
269
324
let inner = || -> Result < bool > {
270
- let harmless_pattern = regex:: Regex :: new ( "^ [a-zA-Z0-9_\\ - ./:]*$") ?;
325
+ let harmless_pattern = regex:: Regex :: new ( r#"^( [a-zA-Z0-9_./:]|-) *$"# ) ?;
271
326
let str = value. to_str ( ) . ok_or_else ( || anyhow ! ( "non-UTF8 value." ) ) ?;
272
327
Ok ( harmless_pattern. is_match ( str) )
273
328
} ;
274
329
inner ( ) . unwrap_or ( false )
275
330
}
276
331
332
+ fn get_cmdline_overwrite_path ( ) -> Result < HostPath > {
333
+ get_distrod_runtime_files_dir_path ( ) . map ( |mut path| {
334
+ path. push ( "cmdline" ) ;
335
+ path
336
+ } )
337
+ }
338
+
277
339
fn env_to_systemd_setenv_arg < K , V > ( key : K , value : V ) -> OsString
278
340
where
279
341
K : AsRef < OsStr > ,
0 commit comments