diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..d0cb3a4 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,51 @@ +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.43" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "nix" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "qsni" +version = "0.1.0" +dependencies = [ + "nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" +"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..5743355 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "qsni" +version = "0.1.0" +authors = ["Albert S. "] + +[dependencies] +nix = "0.11.0" diff --git a/rust/src/main.rs b/rust/src/main.rs new file mode 100644 index 0000000..caae5a3 --- /dev/null +++ b/rust/src/main.rs @@ -0,0 +1,105 @@ +use std::env; +use std::io::BufReader; +use std::io::BufRead; +use std::io::Error; +use std::io::ErrorKind; +use std::io::Write; +use std::ffi::CString; + +static PROFILEPATH : &'static str ="/etc/qsni.d/"; +static NET_CLS_DIR : &'static str="/sys/fs/cgroup/net_cls/"; +extern crate nix; + + +fn ensure_outside_profile() +{ + let fp = std::fs::File::open("/proc/self/cgroup").expect("Error opening cgroups file of process"); + let bf = BufReader::new(fp); + for line in bf.lines() { + let currentline = line.expect("Error while reading line"); + let splitted : Vec<&str> = currentline.split(':').collect(); + if splitted.len() < 3 { + panic!("Misformated line in cgroups file!"); + } + if splitted[1] == "net_cls" && splitted[2] != "/" { + panic!("already assigned to a net class, thus you can't use this binary to change that"); + } + } +} + +fn init_profile(profilepath : &str) +{ + use nix::unistd::*; + use nix::sys::wait::*; + match fork() + { + Ok(ForkResult::Parent { child, .. }) => { + let waitresult = waitpid(child, Some(WaitPidFlag::empty())).expect("waitpid failed"); + match waitresult + { + WaitStatus::Exited(pid, code) => + { + if code != 0 + { + panic!("profile setup script failed"); + } + }, + _ => { }, + } + } + Ok(ForkResult::Child) => { + unsafe { nix::libc::clearenv(); } + nix::unistd::execv(&CString::new(profilepath).unwrap(), &[CString::new(profilepath).unwrap()]).expect("Faileed execv");}, + Err(_) => println!("Fork failed"), + } +} + +fn assign_to_profile(profilename : &str) +{ + let filename = NET_CLS_DIR.to_owned() + "/" + profilename + "/tasks"; + let mut file = std::fs::OpenOptions::new().write(true).append(true).open(filename).expect("Failed to open net class file for writing"); + let mypid = nix::unistd::getpid().to_string(); + write!(file, "{}", mypid).expect("An error occured while writing the pid"); + +} + +fn main() +{ + std::panic::set_hook(Box::new(|pi| { + if let Some(s) = pi.payload().downcast_ref::() { + eprintln!("{}", s); + } + eprintln!("Details:"); + eprintln!("{}", pi); + })); + let args : Vec = std::env::args().collect(); + if args.len() < 3 { + println!("usage: qsni profile command [arguments...]"); + std::process::exit(1); + } + ensure_outside_profile(); + + let profilename : &str = &args[1]; + + let profilefilepath = PROFILEPATH.to_owned() + profilename; + if ! std::path::Path::new(&profilefilepath).exists(){ + eprintln!("The specified profile {} does not exist", profilename); + std::process::exit(1); + } + + let currentuid = nix::unistd::getuid(); + let currentgid = nix::unistd::getgid(); + + nix::unistd::setuid(nix::unistd::Uid::from_raw(0)).expect("Failed to become root"); + + init_profile(&profilefilepath); + assign_to_profile(&profilename); + + nix::unistd::setgid(currentgid).expect("setgid failed during drop"); + nix::unistd::setuid(currentuid).expect("setuid failed during drop"); + + let cmd = &args[2]; + let mut execargs : Vec = args.iter().skip(3).map(|s| { CString::new(s.as_str()).unwrap() }).collect(); + execargs.insert(0, CString::new(cmd.as_str()).unwrap()); + nix::unistd::execvp(&CString::new(cmd.as_str()).unwrap(), &execargs).expect("execv failed launching your program"); +}