diff options
Diffstat (limited to '')
-rw-r--r-- | src/main.rs | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..dd8eeb3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,188 @@ +use std::fs; +use std::{env, os::unix::prelude::PermissionsExt}; +use std::process::ExitCode; +use std::time::SystemTime; +use pwd::Passwd; +use nix::{unistd}; +use serde_json::Value; + +extern crate time; + +fn main() -> ExitCode { + let args: Vec<String> = env::args().collect(); + if args.len() < 2 { + eprintln!("Invalid argument count."); + return ExitCode::from(0); + } + let config = match config("/etc/crab.conf") { + Some(data) => data, + None => return ExitCode::from(1) + }; + let user = match Passwd::current_user() { + Some(data) => data, + None => { + eprintln!("You dont exist."); + return ExitCode::from(2); + } + }; + let persist = match allowed(&config, &user.name) { + Some(data) => data, + None => { + eprintln!("Operation Not Permitted. This incidence will be reported."); + return ExitCode::from(3); + } + }; + + if !validate(&user.name, persist) { + eprintln!("Authentication failed."); + return ExitCode::from(4); + } + + if !unistd::setuid(unistd::geteuid()).is_ok() || !unistd::setgid(unistd::getegid()).is_ok() { + eprintln!("Failed to set root permissions"); + return ExitCode::from(5); + }; + + let err = exec::execvp(&args[1], &args[1..]); + println!("Error: {}", err); + + ExitCode::from(0) +} + +struct Config { + users: Vec<(String, bool)> +} + +fn validate(user: &str, persist: bool) -> bool { + if persist && get_persist(user) { + return true; + } + let input = rpassword::prompt_password(format!("crab ({}) password: ", user)).unwrap(); + let mut auth = pam::Authenticator::with_password("crab").unwrap(); + auth.get_handler().set_credentials(user.to_owned(), input); + if !auth.authenticate().is_ok() || !auth.open_session().is_ok() { + return false; + } + if persist { + set_persist(user); + } + return true; +} + +fn allowed(config: &Config, user: &str) -> Option<bool> { + for (name, persist) in &config.users { + if name == user { + return Some(persist.clone()); + } + } + None +} + +fn config(path: &str) -> Option<Config> { + let file = match std::fs::read_to_string(path) { + Err(e) => { + eprintln!("{}: {}", &path, e); + return None + }, + Ok(data) => data + }; + + let mut users = vec![]; + for (line_num, line) in file.split("\n").enumerate() { + let args: Vec<&str> = line.split(" ").collect(); + if line.trim() == "" { + continue; + } + if args.len() < 2 { + eprintln!("Error in config at line {}: Not enough arguments", line_num); + continue; + } + let user: String = args[0].to_string(); + let persist: bool = match args[1].parse() { + Err(e) => { + eprintln!("Error in config at line {}: {}", line_num, e); + continue; + }, + Ok(data) => data + }; + users.push((user, persist)); + } + Some(Config{users}) +} + +fn get_terminal_process() -> Option<i32> { + let id: i32 = match std::process::id().try_into() { + Ok(data) => data, + Err(_) => return None + }; + let stat = match procinfo::pid::stat(id) { + Ok(data) => data, + Err(_) => return None + }; + Some(stat.tty_nr) +} + +fn get_terminal_config() -> Option<Value> { + let id = match get_terminal_process() { + Some(data) => data, + None => return None + }; + let data = match std::fs::read_to_string(path(&id)) { + Ok(data) => data, + Err(_) => "{}".to_string() + }; + let json: Value = match serde_json::from_str(&data) { + Ok(data) => data, + Err(_) => return None + }; + Some(json) +} + +fn write_terminal_config(id: &i32, data: &str) -> Result<(), Box<dyn std::error::Error>> { + std::fs::write(path(&id), data)?; + unistd::chown(std::path::Path::new(&path(&id)), Some(unistd::Uid::from(0)), Some(unistd::Gid::from(0)))?; + let metadata = std::fs::metadata(path(&id))?; + let mut perms = metadata.permissions(); + perms.set_mode(0o0660); + fs::set_permissions(path(&id), perms)?; + Ok(()) +} + + +fn get_persist(user: &str) -> bool { + let json = match get_terminal_config() { + Some(data) => data, + None => return false + }; + let timestamp = match json[user].as_u64() { + Some(data) => data, + None => return false + }; + return now() - timestamp < 60 * 3; +} + +fn set_persist(user: &str) { + let mut json = match get_terminal_config() { + Some(data) => data, + None => return + }; + json[user] = Value::from(now()); + let id = match get_terminal_process() { + Some(data) => data, + None => return + }; + match write_terminal_config(&id, &json.to_string()) { + Ok(_) => {}, + Err(e) => { + eprintln!("Internal Error: {}", e) + } + }; +} + +fn now() -> u64 { + return SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); +} + +fn path(id: &i32) -> String { + return format!("/tmp/crab-{}", id); +}
\ No newline at end of file |