mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-09-15 03:40:22 +00:00
switch-to-configuration-ng: derive user GID/runtime from filesystem
Avoid TOCTOU race against logind async GC: replace D-Bus Properties::get for GID/RuntimePath with stat(/run/user/<uid>), which is atomic. Relates: #429884 Assisted-by: opencode (sst) with glm-5.2
This commit is contained in:
@@ -6,7 +6,8 @@ use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
io::{BufRead, Read, Write},
|
||||
os::unix::{fs::PermissionsExt, process::CommandExt},
|
||||
os::unix::fs::{MetadataExt, PermissionsExt},
|
||||
os::unix::process::CommandExt,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
str::FromStr,
|
||||
@@ -2384,19 +2385,33 @@ won't take effect until you reboot the system.
|
||||
exit_code = 4;
|
||||
}
|
||||
Ok(users) => {
|
||||
for (uid, name, user_dbus_path) in users {
|
||||
let proxy = dbus_conn.with_proxy(
|
||||
"org.freedesktop.login1",
|
||||
&user_dbus_path,
|
||||
Duration::from_millis(5000),
|
||||
);
|
||||
let gid: u32 = proxy
|
||||
.get("org.freedesktop.login1.User", "GID")
|
||||
.with_context(|| format!("Failed to get GID for {name}"))?;
|
||||
|
||||
let runtime_path: String = proxy
|
||||
.get("org.freedesktop.login1.User", "RuntimePath")
|
||||
.with_context(|| format!("Failed to get runtime directory for {name}"))?;
|
||||
for (uid, name, _user_dbus_path) in users {
|
||||
// Derive GID and runtime path from the filesystem instead of querying
|
||||
// logind via D-Bus, which races against logind's async GC of user
|
||||
// objects (list_users snapshot → Properties::get hits UnknownObject).
|
||||
// /run/user/<uid> exists iff the user manager is active (BindsTo= on
|
||||
// user@.service), so stat() is an atomic, race-free liveness check.
|
||||
let runtime_path = PathBuf::from(format!("/run/user/{uid}"));
|
||||
let metadata = match std::fs::metadata(&runtime_path) {
|
||||
Ok(m) => m,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
eprintln!(
|
||||
"skipping user {name}: {} not found \
|
||||
(user manager not running)",
|
||||
runtime_path.display()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"warning: failed to stat {} for user {name}; \
|
||||
skipping: {err}",
|
||||
runtime_path.display()
|
||||
);
|
||||
exit_code = 4;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!("reloading user units for {name}...");
|
||||
let myself = Path::new("/proc/self/exe")
|
||||
@@ -2406,7 +2421,7 @@ won't take effect until you reboot the system.
|
||||
log::debug!("Performing user switch for {name}");
|
||||
let status = std::process::Command::new(&myself)
|
||||
.uid(uid)
|
||||
.gid(gid)
|
||||
.gid(metadata.gid())
|
||||
.env_clear()
|
||||
.env("XDG_RUNTIME_DIR", runtime_path)
|
||||
.env("__NIXOS_SWITCH_TO_CONFIGURATION_PARENT_EXE", &myself)
|
||||
|
||||
Reference in New Issue
Block a user