diff --git a/compartments/kryptikd/src/spawn.rs b/compartments/kryptikd/src/spawn.rs index 3347e553..a6ce17be 100644 --- a/compartments/kryptikd/src/spawn.rs +++ b/compartments/kryptikd/src/spawn.rs @@ -1251,6 +1251,21 @@ fn intermediate_main( Some(format!("/proc/self/fd/{raw}")) } }; + /* The images' /usr too: its set is stacked under /run/kryptik (images.rs), which the zone's + * mapped root may not walk. Like the broker's, the descriptor dies in pid 1's sweep. */ + let usr_in_zone = if usr == "/usr" { + usr.to_string() + } else { + let c = match std::ffi::CString::new(usr) { + Ok(c) => c, + Err(_) => bail!("the images' /usr path contains a NUL"), + }; + let fd = unsafe { libc::open(c.as_ptr(), libc::O_PATH | libc::O_DIRECTORY | libc::O_NOFOLLOW) }; + if fd < 0 { + bail!("the images' /usr at {}: {}", usr, io::Error::last_os_error()); + } + format!("/proc/self/fd/{fd}") + }; // Until the parent writes our id maps we are nobody (65534) and cannot become root. ready.signal(); @@ -1312,7 +1327,7 @@ fn intermediate_main( if inner == 0 { alive.close_write(); let service = maps_service(id.privileged, zone_policy); - let rc = zone_init(zone, rootfs, argv, flags, zone_policy, fs_rules, plumbed, &broker_in_zone, wayland_in_zone.as_deref(), wifi_conf, sound_dir, camera_dir, usr, service, &alive); + let rc = zone_init(zone, rootfs, argv, flags, zone_policy, fs_rules, plumbed, &broker_in_zone, wayland_in_zone.as_deref(), wifi_conf, sound_dir, camera_dir, &usr_in_zone, service, &alive); unsafe { libc::_exit(rc) }; } alive.close_read(); diff --git a/docs/design/software-delivery.md b/docs/design/software-delivery.md index 3296efd8..07eefbf3 100644 --- a/docs/design/software-delivery.md +++ b/docs/design/software-delivery.md @@ -295,7 +295,9 @@ up ADR-001 in the zones where the applications run. a zone names, kryptikd mounts a read-only overlay (no upper directory; the images' `usr` over the root's `/usr`; redirects in a layer not followed) at `/run/kryptik/usr/` in the initial namespace, and the zone's bind - of `/usr` takes that instead. A set already mounted there is used only if + of `/usr` takes that instead, through a descriptor kryptikd opens before + it takes the zone's ids: `/run/kryptik` is root's alone, and the zone's + mapped root may not walk it. A set already mounted there is used only if it stacks exactly those mounts, as its name holds 64 bits of each root hash. Since zone 0 mounts it and the zone only receives a bind, the hardened kernel's `OVERLAY_FS_UNPRIVILEGED`, which is off, does not come