summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorTyler Murphy <tylermurphy534@gmail.com>2022-11-10 14:17:16 -0500
committerTyler Murphy <tylermurphy534@gmail.com>2022-11-10 14:17:16 -0500
commit83d045a58cd67286ae9d9fa2bee9103fa0cc931a (patch)
treee9d4cfbe4c6231adf4236847b59109aba7bf77a1 /src/main.rs
parentslight refactor (diff)
downloadcrab-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.rs115
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})
}