diff --git a/vuinputd/Cargo.toml b/vuinputd/Cargo.toml index 4f7485d..95e917c 100644 --- a/vuinputd/Cargo.toml +++ b/vuinputd/Cargo.toml @@ -29,3 +29,4 @@ anyhow = "1.0.100" clap = { version = "4", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +base64 = "0.22" diff --git a/vuinputd/src/jobs/mknod_device_in_container_job.rs b/vuinputd/src/jobs/mknod_device_in_container_job.rs index d6d9fd9..90d3a7d 100644 --- a/vuinputd/src/jobs/mknod_device_in_container_job.rs +++ b/vuinputd/src/jobs/mknod_device_in_container_job.rs @@ -9,7 +9,7 @@ use std::{ }; use crate::{ - actions::{action::Action}, + actions::action::Action, job_engine::job::{Job, JobTarget}, process_tools::{self, await_process, Pid, RequestingProcess}, }; diff --git a/vuinputd/src/main.rs b/vuinputd/src/main.rs index 16f8763..822c501 100644 --- a/vuinputd/src/main.rs +++ b/vuinputd/src/main.rs @@ -19,10 +19,10 @@ // Filter out Ctrl+Alt+Fx. "sysrq" keys or the low-level VT switching combos. use ::cuse_lowlevel::*; +use base64::prelude::BASE64_STANDARD; +use base64::Engine as _; use log::info; use std::ffi::CString; -use std::fs::OpenOptions; -use std::os::fd::AsRawFd; use std::os::raw::c_char; use std::sync::atomic::AtomicU64; use std::sync::Mutex; @@ -69,6 +69,10 @@ struct Args { #[arg(long, value_name = "JSON")] pub action: Option, + /// Action to execute (base64-encoded JSON). Note that this excludes all other options. + #[arg(long = "action-base64", value_name = "BASE64")] + pub action_base64: Option, + /// Path to the target process's /proc//ns directory used as namespace source. #[arg( long = "target-namespace", @@ -88,18 +92,27 @@ struct Args { } fn validate_args(args: &Args) -> Result<(), String> { + let action: &Option = match (&args.action, &args.action_base64) { + (None, None) => &None, + (None, Some(_)) => &args.action_base64, + (Some(_), None) => &args.action, + (Some(_), Some(_)) => { + return Err("--action and --action-base64 may not be used together".into()); + } + }; + // action might only occur with target-namespace match ( &args.major, &args.minor, &args.devname, - &args.action, + action, &args.target_namespace, ) { (None, None, None, Some(_), _) => {} (_, _, _, None, None) => {} _ => { - return Err("--action must not be used in combination with any other argument other than target-namespace".into()); + return Err("--action or --action-base64 must not be used in combination with any other argument other than target-namespace".into()); } } @@ -137,11 +150,26 @@ fn main() -> std::io::Result<()> { std::process::exit(2); } - if args.action.is_some() { + let action = match (&args.action, &args.action_base64) { + (Some(json), None) => Some(json.clone()), + (None, Some(b64)) => { + let decoded = BASE64_STANDARD + .decode(&b64) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?; + + let decoded = String::from_utf8(decoded) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; + Some(decoded) + } + (None, None) => None, + _ => unreachable!("validate_args enforces mutual exclusion"), + }; + + if action.is_some() { if let Some(target_namespace) = args.target_namespace { process_tools::run_in_net_and_mnt_namespace(target_namespace.as_str()).unwrap(); } - let error_code = actions::handle_action::handle_cli_action(args.action.unwrap()); + let error_code = actions::handle_action::handle_cli_action(action.unwrap()); std::process::exit(error_code); } diff --git a/vuinputd/src/process_tools/mod.rs b/vuinputd/src/process_tools/mod.rs index f57e045..e54119e 100644 --- a/vuinputd/src/process_tools/mod.rs +++ b/vuinputd/src/process_tools/mod.rs @@ -3,6 +3,8 @@ // Author: Johannes Leupolz use async_io::Async; +use base64::prelude::BASE64_STANDARD; +use base64::Engine as _; use log::debug; use std::{ fs::{self, File}, @@ -252,10 +254,23 @@ pub fn get_requesting_process(pid: Pid) -> RequestingProcess { } } +fn print_debug_string(action: &str, ns: &RequestingProcess) { + let action_base64 = (BASE64_STANDARD.encode(action)); + let mut debugstring = String::new(); + debugstring.push_str("In case you need to debug the system calls, call `strace vuinputd"); + debugstring.push_str(" --target-namespace "); + debugstring.push_str(ns.nsroot.as_str()); + debugstring.push_str(" --action-base64 "); + debugstring.push_str(action_base64.as_str()); + debugstring.push_str("`"); + debug!("{}", debugstring); +} + /// Runs a function inside the given network and mount namespaces. /// Returns the child PID so the caller can `waitpid` on it. pub fn start_action(action: Action, ns: &RequestingProcess) -> anyhow::Result { let action_json = serde_json::to_string(&action).unwrap(); + print_debug_string(&action_json, &ns); let child = unsafe { Command::new("/proc/self/exe")