From 2f9d3db08d3f2ec55f4c2025613f3678f6bacaf7 Mon Sep 17 00:00:00 2001 From: Marc Schoolderman Date: Mon, 2 Sep 2024 17:28:34 +0200 Subject: [PATCH] add more Rustified version of kernel_check --- src/common/error.rs | 2 +- src/sudo/mod.rs | 2 +- src/system/kernel.rs | 62 +++++++++++++++++++++++++------------------- 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/common/error.rs b/src/common/error.rs index 711402814..3f243a988 100644 --- a/src/common/error.rs +++ b/src/common/error.rs @@ -57,7 +57,7 @@ impl fmt::Display for Error { Error::SelfCheck => { f.write_str("sudo must be owned by uid 0 and have the setuid bit set") } - Error::KernelCheck => f.write_str("sudo needs a Kernel >= 5.9"), + Error::KernelCheck => f.write_str("sudo-rs needs a Linux kernel newer than v5.9"), Error::CommandNotFound(p) => write!(f, "'{}': command not found", p.display()), Error::InvalidCommand(p) => write!(f, "'{}': invalid command", p.display()), Error::UserNotFound(u) => write!(f, "user '{u}' not found"), diff --git a/src/sudo/mod.rs b/src/sudo/mod.rs index 07264bf5e..947d784e1 100644 --- a/src/sudo/mod.rs +++ b/src/sudo/mod.rs @@ -111,7 +111,7 @@ fn sudo_process() -> Result<(), Error> { dev_info!("development logs are enabled"); self_check()?; - kernel_check(5, 9)?; + kernel_check()?; let pipeline = Pipeline { policy: SudoersPolicy::default(), diff --git a/src/system/kernel.rs b/src/system/kernel.rs index 070cf6c57..99f0d8cd4 100644 --- a/src/system/kernel.rs +++ b/src/system/kernel.rs @@ -1,41 +1,49 @@ use std::ffi::CStr; -use std::mem::zeroed; +use std::mem::MaybeUninit; -use crate::common::Error; +use crate::{common::Error, cutils::cerr}; -pub fn kernel_check(major: u32, minor: u32) -> Result<(), Error> { - if cfg!(target_os = "freebsd") { - return Ok(()); - } +#[cfg(target_os = "linux")] +pub fn kernel_check() -> Result<(), Error> { + // On Linux, we need kernel version 5.9 to have access to `close_range()` + const TARGET_VERSION: (u32, u32) = (5, 9); - let mut utsname: libc::utsname = unsafe { zeroed() }; + let mut utsname = MaybeUninit::uninit(); - if unsafe { libc::uname(&mut utsname) } != 0 { - // Could not get the kernel version. Try to run anyway - return Ok(()); - } + // SAFETY: uname is passed a correct pointer + cerr(unsafe { libc::uname(utsname.as_mut_ptr()) })?; - let release = unsafe { CStr::from_ptr(utsname.release.as_ptr()) } - .to_string_lossy() - .into_owned(); + // SAFETY: since uname exited normally, the struct is now initialized + let utsname = unsafe { utsname.assume_init() }; - let version_parts: Vec<&str> = release.split('.').collect(); - - if version_parts.len() < 2 { - // Could not get the kernel version. Try to run anyway - return Ok(()); - } + // SAFETY: utsname.release will hold a null-terminated C string + let release = unsafe { CStr::from_ptr(utsname.release.as_ptr()) }.to_string_lossy(); // Parse the major and minor version numbers - if let (Ok(major_version), Ok(minor_version)) = ( - version_parts[0].parse::(), - version_parts[1].parse::(), - ) { - if major_version > major || (major_version == major && minor_version >= minor) { - return Ok(()); + let mut version_parts = release.split('.').map_while(|x| x.parse::().ok()); + + match (version_parts.next(), version_parts.next()) { + (Some(major), Some(minor)) if (major, minor) < TARGET_VERSION => { + // We have determined that this Linux kernel is too old. + Err(Error::KernelCheck) + } + _ => { + // We have not been able to prove that sudo-rs is incompatible with this kernel + // and are giving the benefit of the doubt. + Ok(()) } } +} + +#[cfg(target_os = "freebsd")] +pub fn kernel_check() -> Result<(), Error> { + // the kernel check doesn't make much sense on FreeBSD (we need FreeBSD 8.0 or newer, + // which is comparatively ancient compared to Linux 5.9) + Ok(()) +} - Err(Error::KernelCheck) +#[cfg(not(any(target_os = "freebsd", target_os = "linux")))] +pub fn kernel_check() -> Result<(), Error> { + compile_error!("sudo-rs only works on Linux and FreeBSD"); }