From 838f47df1d6221f88281aa4b76d1b2b1bd375048 Mon Sep 17 00:00:00 2001 From: cyber-sushi Date: Tue, 7 May 2024 19:52:01 +0200 Subject: [PATCH] Run shell commands with least privileges even when running as root --- src/event_reader.rs | 36 +++++++++++++++++++++++++++++------- src/udev_monitor.rs | 34 ++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/event_reader.rs b/src/event_reader.rs index 28daf46..dfb4595 100644 --- a/src/event_reader.rs +++ b/src/event_reader.rs @@ -5,7 +5,7 @@ use evdev::{EventStream, Key, RelativeAxisType, AbsoluteAxisType, EventType, Inp use crate::virtual_devices::VirtualDevices; use crate::Config; use crate::active_client::*; - +use crate::udev_monitor::Environment; struct Stick { function: String, @@ -29,6 +29,7 @@ pub struct EventReader { modifier_was_activated: Arc>, device_is_connected: Arc>, current_desktop: Option, + environment: Environment, settings: Settings, } @@ -38,6 +39,7 @@ impl EventReader { stream: Arc>, modifiers: Arc>>, modifier_was_activated: Arc>, + environment: Environment, current_desktop: Option, ) -> Self { let mut position_vector: Vec = Vec::new(); @@ -88,6 +90,7 @@ impl EventReader { modifier_was_activated, device_is_connected, current_desktop, + environment, settings, } } @@ -420,12 +423,31 @@ impl EventReader { async fn spawn_subprocess(&self, command_list: &Vec) { let mut modifier_was_activated = self.modifier_was_activated.lock().await; *modifier_was_activated = true; - for command in command_list { - Command::new("sh") - .arg("-c") - .arg(command) - .spawn() - .expect("Failed to run command."); + match &self.environment.user { + Ok(user) if user == &"root".to_string() => { + match &self.environment.sudo_user { + Ok(sudo_user) => { + for command in command_list { + Command::new("sh") + .arg("-c") + .arg(format!("env runuser {} -c {}", sudo_user.as_str(), command)) + .spawn() + .expect("Failed to run command."); + } + }, + _ => {} + } + }, + Ok(_) => { + for command in command_list { + Command::new("sh") + .arg("-c") + .arg(command) + .spawn() + .expect("Failed to run command."); + } + } + Err(_) => {}, } } diff --git a/src/udev_monitor.rs b/src/udev_monitor.rs index e9f415b..670dc1a 100644 --- a/src/udev_monitor.rs +++ b/src/udev_monitor.rs @@ -7,6 +7,12 @@ use crate::Config; use crate::event_reader::EventReader; +#[derive(Clone)] +pub struct Environment { + pub user: Result, + pub sudo_user: Result, +} + pub async fn start_monitoring_udev(config_files: Vec, mut tasks: Vec>) { launch_tasks(&config_files, &mut tasks); let mut monitor = tokio_udev::AsyncMonitorSocket::new ( @@ -29,27 +35,39 @@ pub async fn start_monitoring_udev(config_files: Vec, mut tasks: Vec, tasks: &mut Vec>) { let modifiers: Arc>> = Arc::new(Mutex::new(Default::default())); let modifier_was_activated: Arc> = Arc::new(Mutex::new(true)); + match env::var("DBUS_SESSION_BUS_ADDRESS") { + Ok(_) => true, + Err(_) => { + println!("Warning: unable to inherit user environment.\n\ + To launch Makima as root, use 'sudo -E makima', otherwise some shell commands won't work.\n"); + false + }, + }; + let environment = Environment { + user: env::var("USER"), + sudo_user: env::var("SUDO_USER"), + }; let current_desktop: Option = match (env::var("XDG_SESSION_TYPE"), env::var("XDG_CURRENT_DESKTOP")) { (Ok(session), Ok(desktop)) if session == "wayland".to_string() && vec!["Hyprland".to_string(), "sway".to_string()].contains(&desktop) => { - println!(">> Running on {}, active window detection enabled.\n", desktop); + println!("Running on {}, active window detection enabled.\n", desktop); Option::Some(desktop) }, (Ok(session), Ok(desktop)) if session == "wayland".to_string() => { - println!(">> Unsupported compositor: {}, won't be able to change bindings according to active window.\n\ + println!("Warning: unsupported compositor: {}, won't be able to change bindings according to active window.\n\ Currently supported desktops: Hyprland, Sway, X11.\n", desktop); Option::None }, (Ok(session), _) if session == "x11".to_string() => { - println!(">> Running on X11, active window detection enabled."); + println!("Running on X11, active window detection enabled."); Option::Some("x11".to_string()) }, (Ok(session), Err(_)) if session == "wayland".to_string() => { - println!(">> Unable to retrieve the current desktop based on XDG_CURRENT_DESKTOP env var.\n\ + println!("Warning: unable to retrieve the current desktop based on XDG_CURRENT_DESKTOP env var.\n\ Won't be able to change bindings according to the active window.\n"); Option::None }, (Err(_), _) => { - println!(">> Unable to retrieve the session type based on XDG_SESSION_TYPE env var.\n\ + println!("Warning: unable to retrieve the session type based on XDG_SESSION_TYPE env var.\n\ Won't be able to change bindings according to the active window.\n"); Option::None }, @@ -97,7 +115,7 @@ pub fn launch_tasks(config_files: &Vec, tasks: &mut Vec>) let event_device = device.0.as_path().to_str().unwrap().to_string(); if !config_map.is_empty() { let stream = Arc::new(Mutex::new(get_event_stream(Path::new(&event_device), config_map.clone()))); - let reader = EventReader::new(config_map.clone(), stream, modifiers.clone(), modifier_was_activated.clone(), current_desktop.clone()); + let reader = EventReader::new(config_map.clone(), stream, modifiers.clone(), modifier_was_activated.clone(), environment.clone(), current_desktop.clone()); tasks.push(tokio::spawn(start_reader(reader))); devices_found += 1 } @@ -118,10 +136,10 @@ pub fn get_event_stream(path: &Path, config: HashMap) -> EventSt match config.get("default").unwrap().settings.get("GRAB_DEVICE") { Some(value) => { if value == &true.to_string() { - device.grab().unwrap() + device.grab().expect("Unable to grab device. Is another instance of Makima running?") } } - None => device.grab().unwrap() + None => device.grab().expect("Unable to grab device. Is another instance of Makima running?") } let stream: EventStream = device.into_event_stream().unwrap(); return stream