Harden session terminal release authority

This commit is contained in:
ms2k 2026-07-17 05:59:35 +09:00
commit 4dc5daa57b

View file

@ -57,15 +57,69 @@ fn run_session_exec(arguments: &[OsString]) -> io::Result<()> {
}
fn report_session_identity(arguments: &[OsString]) -> io::Result<()> {
let pid = match arguments {
[pid] => pid
match arguments {
[pid] => report_process_identity(parse_positive_pid(pid)?),
[mode, group] if mode == "--group" => report_group_presence(parse_positive_pid(group)?),
_ => Err(io::Error::other("session identity query is invalid")),
}
}
fn parse_positive_pid(value: &OsStr) -> io::Result<u32> {
value
.to_str()
.and_then(|value| value.parse::<u32>().ok())
.filter(|value| *value > 0)
.ok_or_else(|| io::Error::other("session identity number is invalid"))
}
fn report_process_identity(pid: u32) -> io::Result<()> {
let Some((observed_pid, start_ticks, group)) = read_process_identity(pid)? else {
println!("absent");
return Ok(());
};
println!("{observed_pid} {start_ticks} {group}");
Ok(())
}
fn report_group_presence(group: u32) -> io::Result<()> {
println!(
"{}",
if group_is_present_except(group, std::process::id())? {
"present"
} else {
"absent"
}
);
Ok(())
}
fn group_is_present_except(group: u32, observer: u32) -> io::Result<bool> {
for entry in std::fs::read_dir("/proc")? {
let entry = entry?;
let Some(pid) = entry
.file_name()
.to_str()
.and_then(|value| value.parse::<u32>().ok())
.filter(|pid| *pid > 0)
.ok_or_else(|| io::Error::other("session identity PID is invalid"))?,
_ => return Err(io::Error::other("session identity requires one PID")),
else {
continue;
};
if pid == observer {
continue;
}
if read_process_identity(pid)?.is_some_and(|(_, _, observed_group)| observed_group == group)
{
return Ok(true);
}
}
Ok(false)
}
fn read_process_identity(pid: u32) -> io::Result<Option<(u32, u64, u32)>> {
let stat = match std::fs::read_to_string(format!("/proc/{pid}/stat")) {
Ok(stat) => stat,
Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(None),
Err(error) => return Err(error),
};
let stat = std::fs::read_to_string(format!("/proc/{pid}/stat"))?;
let close = stat
.rfind(')')
.ok_or_else(|| io::Error::other("guest process stat is malformed"))?;
@ -82,8 +136,7 @@ fn report_session_identity(arguments: &[OsString]) -> io::Result<()> {
.get(19)
.and_then(|value| value.parse::<u64>().ok())
.ok_or_else(|| io::Error::other("guest process stat start is malformed"))?;
println!("{observed_pid} {start_ticks} {group}");
Ok(())
Ok(Some((observed_pid, start_ticks, group)))
}
#[cfg(test)]
@ -125,4 +178,11 @@ mod tests {
.is_err()
);
}
#[test]
fn group_presence_is_independent_of_a_designated_leader() {
let (_, _, group) = read_process_identity(std::process::id()).unwrap().unwrap();
assert!(group_is_present_except(group, u32::MAX).unwrap());
assert!(!group_is_present_except(u32::MAX, u32::MAX).unwrap());
}
}