diff options
author | Tyler Murphy <tylermurphy534@gmail.com> | 2022-11-10 14:17:16 -0500 |
---|---|---|
committer | Tyler Murphy <tylermurphy534@gmail.com> | 2022-11-10 14:17:16 -0500 |
commit | 83d045a58cd67286ae9d9fa2bee9103fa0cc931a (patch) | |
tree | e9d4cfbe4c6231adf4236847b59109aba7bf77a1 /src/main.rs | |
parent | slight refactor (diff) | |
download | crab-83d045a58cd67286ae9d9fa2bee9103fa0cc931a.tar.gz crab-83d045a58cd67286ae9d9fa2bee9103fa0cc931a.tar.bz2 crab-83d045a58cd67286ae9d9fa2bee9103fa0cc931a.zip |
documentation and group support
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 115 |
1 files changed, 103 insertions, 12 deletions
diff --git a/src/main.rs b/src/main.rs index 51ca0f2..c6e8201 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,13 @@ use std::env; use std::process::ExitCode; use pwd::Passwd; -use nix::unistd; +use nix::unistd::{self, Uid, Group}; + + +mod persist; +mod flags; +mod secure; -extern crate time; const ERROR_ARGS: u8 = 1; const ERROR_CONFIG: u8 = 2; @@ -12,35 +16,47 @@ const ERROR_NOT_AUTHORIZED: u8 = 4; const ERROR_AUTH_FAILED: u8 = 5; const ERROR_RUN_ROOT: u8 = 6; -mod persist; -mod flags; -mod secure; fn main() -> ExitCode { + + // gets the arguments from the env let args: Vec<String> = env::args().collect(); + + // pase the arguments into valid flags that are usuable by crab let flags = match flags::parse(&args[1..]) { Some(data) => data, + // if there is an invalid flag, print the help message and exit None => { help(); return ExitCode::from(ERROR_ARGS); } }; + + // If the version arg flag is set, print the crab version if flags.version { println!("crab version 0.0.5"); return ExitCode::SUCCESS; } + + // If the help arg flag is set, print the crab help message if flags.help { help(); return ExitCode::SUCCESS; } + + // If the amount of acutal command arguments is less than two, a.k.a just `crab`, print the command usage if args.len() - flags.arg_count < 2 { println!("usage: crab [-d] command [args]"); return ExitCode::SUCCESS; } + + // Load the command config from /etc let config = match config("/etc/crab.conf") { Some(data) => data, None => return ExitCode::from(ERROR_CONFIG) }; + + // get the current user login let user = match Passwd::current_user() { Some(data) => data, None => { @@ -48,6 +64,8 @@ fn main() -> ExitCode { return ExitCode::from(ERROR_NO_USER); } }; + + // check if the user is authorized let persist = match allowed(&config, &user.name) { Some(data) => data && !flags.dont_persist, None => { @@ -56,23 +74,31 @@ fn main() -> ExitCode { } }; + + // authenticate the user if !validate(&user.name, persist) { eprintln!("Authentication failed."); return ExitCode::from(ERROR_AUTH_FAILED); } + // set the uid and gid of the process to root to run the command as root if !unistd::setuid(unistd::geteuid()).is_ok() || !unistd::setgid(unistd::getegid()).is_ok() { eprintln!("Failed to set root permissions"); return ExitCode::from(ERROR_RUN_ROOT); }; + + // execute the passed command let start = 1 + flags.arg_count; let err = exec::execvp(&args[start], &args[start..]); + // print an error if an error was returned eprintln!("{}", err); ExitCode::SUCCESS } + +/// Prints the help message to the standard output fn help() { let help = "Usage: @@ -84,10 +110,22 @@ Options: println!("{}", help); } + struct Config { - users: Vec<(String, bool)> + identitys: Vec<(String, bool)>, } + +/// Validates the authorized user, and authenticates them for +/// the current session. If the user is already persisted, it +/// will attempt to read the persist file, and then skip +/// authentication if the is still persisted. +/// #### Arguments +/// * `user` - The login name of the linux user +/// * `persist` - If the user's login should persist +/// #### Returns +/// * `true` - If the user authenticated sucessfully, or the user is persisted +/// * `false` - If the user failed to authenticate fn validate(user: &str, persist: bool) -> bool { if persist && persist::get_persist(user) { return true; @@ -110,15 +148,68 @@ fn validate(user: &str, persist: bool) -> bool { return true; } + +/// Returns if the user is authorized given a sepcific config and user name. +/// #### Arguments +/// * `config` - A config struct contaning the authorization settings for crab +/// * `user` - The name of the user to check is authorized +/// #### Returns +/// * `None` - If the user is not authorized +/// * `Some(bool)` - If the user is authorized, returning the boolean if the +/// user is set to persist fn allowed(config: &Config, user: &str) -> Option<bool> { - for (name, persist) in &config.users { - if name == user { + // get user groups + let groups = get_groups(Uid::current()); + // check each config identiy + for (identity, persist) in &config.identitys { + // if it starts with a :, its a group + if identity.starts_with(":") { + let group = &identity[1..]; + if groups.contains(&group.to_string()) { + return Some(persist.clone()); + }; + // else its a user + } else if identity == user { return Some(persist.clone()); } } None } + +/// Returns a vector of group names of the groups a specific linux user is in +/// #### Arguments +/// * 'uid' - The user id od the linux user to get groups from +/// #### Returns +/// A vector of strings of the groups the user is in. If the vector is empty, +/// either the function coudn't retrieve the users groups, or the user is not in +/// any groups. +fn get_groups(uid: Uid) -> Vec<String> { + let no_change = Uid::from_raw(u32::MAX); + if unistd::setresuid(no_change, uid, no_change).is_err() { + return vec![] + }; + let groups = match nix::unistd::getgroups() { + Ok(data) => data, + Err(_) => return vec![] + }; + let names = groups.iter() + .map(|gid| Group::from_gid(*gid)) + .flatten().flatten() + .map(|group| group.name) + .collect(); + names +} + + +/// Returns a option containing the config ad the specific pile path. +/// This function will print out config syntax errors to the standard +/// output if it failes to parse certain lines. +/// #### Arguments +/// * `path` - The path to the config file +/// #### Returns +/// * `None` - If the config failed to load +/// * `Some(Config)` - If the config was sucessfully parsed. fn config(path: &str) -> Option<Config> { let file = match std::fs::read_to_string(path) { Err(e) => { @@ -128,7 +219,7 @@ fn config(path: &str) -> Option<Config> { Ok(data) => data }; - let mut users = vec![]; + let mut identitys = vec![]; for (line_num, line) in file.split("\n").enumerate() { let args: Vec<&str> = line.split(" ").collect(); if line.trim() == "" { @@ -138,7 +229,7 @@ fn config(path: &str) -> Option<Config> { eprintln!("Error in config at line {}: Not enough arguments", line_num); continue; } - let user: String = args[0].to_string(); + let identity: String = args[0].to_string(); let persist: bool = match args[1].parse() { Err(e) => { eprintln!("Error in config at line {}: {}", line_num, e); @@ -146,7 +237,7 @@ fn config(path: &str) -> Option<Config> { }, Ok(data) => data }; - users.push((user, persist)); + identitys.push((identity, persist)); } - Some(Config{users}) + Some(Config{identitys}) } |