diff --git a/readme.md b/readme.md index 971c469..7d6566d 100644 --- a/readme.md +++ b/readme.md @@ -12,11 +12,19 @@ Run `uninstall.sh` as root to uninstall crab. If you are on an arch based distro, crab is avaliable on the [AUR](https://aur.archlinux.org/packages/crab) as `crab`. # Configuration -Crab supports multiple users with persistence. Each line of the config is the username, then `true` of `false` if the crab authentication persists. +Each line in the configuration specifies a different rule. Each rule is applied from top to bottom, +so the first onethat matches a user is what is used. The first word is either `permit` or `deny` to +allow or deny a certain group. Then the tags `persist` and `nopass` can be added to allow authoriziation +persistance or skipping respectively. Then a user can be specified by putting their name, or a group by a +colon then the groups name. Finally, if you dont want to run that user as root, you can add `as` and then +a user name to run the process as. All lines starting in a # will be ignored. For Example ``` -root true -tylerm false +deny :docker +permit nopass persist linus as root +permit :wheel persist +#deny stallman +permit nvidia as fu ``` The default configuration file is stored in `/usr/share/crab/crab.conf` and must be coppied to `/etc/crab.conf`. diff --git a/src/auth.rs b/src/auth.rs index 82a09cb..99a8216 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,8 +1,7 @@ -use nix::unistd::{User, Group, Uid, Gid}; -use crate::{persist, secure}; +use nix::unistd::{User, Group, Uid, Gid, self}; +use crate::persist; -#[derive(Debug)] pub struct Config { pub permit: bool, pub persist: bool, @@ -55,20 +54,31 @@ pub fn load_config_file(path: &str) -> Option> { } }; - let (user_name, privlaged_name, as_index) = match args.iter().position(|&a| a == "as") { + let (user_name, privlaged_name, name_index) = match args.iter().position(|&a| a == "as") { Some(index) => { if index != len - 2 { config_error(line_num, "Target user not specified or to many arguments after `as`"); continue; } - (args[index-1].to_string(), args[index+1].to_string(), index) + (args[index-1].to_string(), args[index+1].to_string(), index-1) }, None => (args[len-1].to_string(), "root".to_string(), len-1) }; - let persist = args[1..as_index].contains(&"persist"); + let persist = args[1..name_index].contains(&"persist"); - let nopass = args[1..as_index].contains(&"nopass"); + let nopass = args[1..name_index].contains(&"nopass"); + + + for &check in args[1..name_index].iter() { + match check { + "persist" => continue, + "nopass" => continue, + _ => { + config_error(line_num, &format!("Unexpected token `{}`", check)) + } + } + } let (user_uid, user_gid) = @@ -111,11 +121,18 @@ pub fn load_config_file(path: &str) -> Option> { } +/// Print a crab config error to the standard output fn config_error(line_num: usize, message: &str) { eprintln!("Error in config at line {}: {}", line_num, message); } +/// Returns a Uid from a Users name +/// #### Arguments +/// * `name` - The name of the user +/// #### Returns +/// * `None` - If the user doesn't exist +/// * `Some(Gid)` - If the user exists fn get_uid_from_name(name: &str) -> Option { return match User::from_name(name) { Ok(result) => match result { @@ -127,6 +144,12 @@ fn get_uid_from_name(name: &str) -> Option { } +/// Returns a Uesrs name from a Uid +/// #### Arguments +/// * `uid` - The uid of the user +/// #### Returns +/// * `None` - If the user doesn't exist +/// * `Some(Gid)` - If the user exists fn get_name_from_uid(uid: Uid) -> Option { return match User::from_uid(uid) { Ok(result) => match result { @@ -138,6 +161,12 @@ fn get_name_from_uid(uid: Uid) -> Option { } +/// Returns a Gid from a Groups name +/// #### Arguments +/// * `name` - The name of the group +/// #### Returns +/// * `None` - If the group doesn't exist +/// * `Some(Gid)` - If the group exists fn get_gid_from_name(name: &str) -> Option { return match Group::from_name(name) { Ok(result) => match result { @@ -155,7 +184,7 @@ fn get_gid_from_name(name: &str) -> Option { /// 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() { + let groups = match unistd::getgroups() { Ok(data) => data, Err(_) => return vec![] }; @@ -212,7 +241,6 @@ pub fn authenticate(config: &Config, force_pass: bool, uid: Uid) -> bool { None => return false }; if config.nopass || ( !force_pass && config.persist && persist::get_persist(&name) ) { - secure::elevate_privilages(config.privlaged_uid); return true; } let input = match rpassword::prompt_password(format!("crab ({}) password: ", &name)) { @@ -227,9 +255,10 @@ pub fn authenticate(config: &Config, force_pass: bool, uid: Uid) -> bool { if !auth.authenticate().is_ok() || !auth.open_session().is_ok() { return false; } - if config.persist { + if !force_pass && config.persist { persist::set_persist(&name); + } else if force_pass { + persist::remove_persist(&name); } - secure::elevate_privilages(config.privlaged_uid); return true; } diff --git a/src/main.rs b/src/main.rs index cca85de..56df611 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,10 +8,11 @@ mod persist; mod secure; -const ERROR_ARGS: u8 = 1; -const ERROR_CONFIG: u8 = 2; -const ERROR_NOT_AUTHORIZED: u8 = 3; -const ERROR_AUTH_FAILED: u8 = 4; +const ERROR_ARGS: u8 = 1; +const ERROR_CONFIG: u8 = 2; +const ERROR_NOT_AUTHORIZED: u8 = 3; +const ERROR_AUTH_FAILED: u8 = 4; +const ERROR_ELEVATE_PRIVILEGES: u8 = 5; fn main() -> ExitCode { @@ -69,6 +70,12 @@ fn main() -> ExitCode { return ExitCode::from(ERROR_AUTH_FAILED); } + // elevate privileges + if nix::unistd::setuid(configs[auth].privlaged_uid).is_err() { + eprintln!("Failed to elevate privileges."); + return ExitCode::from(ERROR_ELEVATE_PRIVILEGES); + }; + // execute the passed command let start = 1 + flags.arg_count; let err = exec::execvp(&args[start], &args[start..]); diff --git a/src/persist.rs b/src/persist.rs index 762d8b1..2bca386 100644 --- a/src/persist.rs +++ b/src/persist.rs @@ -28,7 +28,7 @@ pub fn get_persist(user: &str) -> bool { } -/// Updates the current sessions persist file +/// Updates a user in the current sessions persist file /// #### Arguments /// * `user` - The user to set persisted pub fn set_persist(user: &str) { @@ -43,8 +43,30 @@ pub fn set_persist(user: &str) { }; match secure::write_file(PERSIST_PATH, &format!("{}", session), &json.to_string()) { Ok(_) => {}, - Err(e) => { - eprintln!("crab: An Internal Has Error: {}", e); + Err(_) => { + eprintln!("crab: An internal error has occured"); + } + }; +} + + +/// Removes a user from the current sessions persist file +/// #### Arguments +/// * `user` - The user to set non-persisted +pub fn remove_persist(user: &str) { + let mut json = match get_persist_config() { + Some(data) => data, + None => return + }; + json[user] = Value::from(0); + let session = match get_current_session() { + Some(data) => data, + None => return + }; + match secure::write_file(PERSIST_PATH, &format!("{}", session), &json.to_string()) { + Ok(_) => {}, + Err(_) => { + eprintln!("crab: An internal error has occured"); } }; } diff --git a/src/secure.rs b/src/secure.rs index 018bc4f..7c85ece 100644 --- a/src/secure.rs +++ b/src/secure.rs @@ -1,5 +1,5 @@ -use std::{os::{unix::prelude::PermissionsExt, linux::fs::MetadataExt}, fs, io::{self, ErrorKind}}; -use nix::unistd::{self, Uid}; +use std::{os::{unix::prelude::PermissionsExt, linux::fs::MetadataExt}, fs, io::{self, ErrorKind}, path::Path}; +use nix::unistd::{self, Uid, Gid}; /// Writes a file securly to a specified path with given data @@ -40,19 +40,6 @@ pub fn read_file(dir: &str, file: &str) -> Option { } -/// Elecate the privlages of the current process -/// #### Arguments -/// * `uid` - The uid to set the process to -/// #### Returns -/// If the process fails to elevate, it returns false -pub fn elevate_privilages(uid: Uid) -> bool { - if unistd::setuid(uid).is_err() { - return false; - } - true -} - - /// Sets the permission for a secure file /// #### Arguments /// * `uid` - The user to own the file @@ -62,7 +49,7 @@ pub fn elevate_privilages(uid: Uid) -> bool { /// #### Returns /// A ``io::Result<()>`` if the write succeded or failed fn set_file_permissions(uid: u32, gid: u32, mode: u32, path: &str) -> Result<(), io::Error> { - unistd::chown(std::path::Path::new(path), Some(unistd::Uid::from(uid)), Some(unistd::Gid::from(gid)))?; + unistd::chown(Path::new(path), Some(Uid::from(uid)), Some(Gid::from(gid)))?; let metadata = fs::metadata(path)?; let mut perms = metadata.permissions(); perms.set_mode(mode);