use nix::unistd::Group; use crate::{persist, secure}; pub struct Config { pub identitys: Vec<(String, bool)>, } /// 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. pub fn load_config(path: &str) -> Option { let file = match std::fs::read_to_string(path) { Err(e) => { eprintln!("{}: {}", &path, e); return None }, Ok(data) => data }; let mut identitys = vec![]; for (line_num, line) in file.split("\n").enumerate() { let args: Vec<&str> = line.split(" ").collect(); if line.starts_with("#") || line.trim() == "" { continue; } if args.len() < 2 { eprintln!("Error in config at line {}: Not enough arguments", line_num); continue; } 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); continue; }, Ok(data) => data }; identitys.push((identity, persist)); } Some(Config{identitys}) } /// Returns a vector of group names of the groups the current effective uid is in /// #### 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() -> 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 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 pub fn authorize(config: &Config, user: &str) -> Option { let groups = get_groups(); for (identity, persist) in &config.identitys { if identity.starts_with(":") { let group = &identity[1..]; if groups.contains(&group.to_string()) { return Some(persist.clone()); }; } else if identity == user { return Some(persist.clone()); } } None } /// Authenticates an authorized user 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 pub fn authenticate(user: &str, persist: bool) -> bool { if persist && persist::get_persist(user) { secure::elevate_privilages(0, 0); return true; } let input = match rpassword::prompt_password(format!("crab ({}) password: ", user)) { Ok(data) => data, Err(_) => return false }; let mut auth = match pam::Authenticator::with_password("crab") { Ok(data) => data, Err(_) => return false }; auth.get_handler().set_credentials(user.to_owned(), input); if !auth.authenticate().is_ok() || !auth.open_session().is_ok() { return false; } if persist { persist::set_persist(user); } secure::elevate_privilages(0, 0); return true; }