finalize adding groups

This commit is contained in:
tylermurphy534 2022-11-10 19:19:20 -05:00
parent b457c08923
commit 9bb5154ae8
4 changed files with 134 additions and 145 deletions

126
src/auth.rs Normal file
View file

@ -0,0 +1,126 @@
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<Config> {
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<String> {
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<bool> {
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;
}

View file

@ -1,11 +1,11 @@
use std::env; use std::env;
use std::process::ExitCode; use std::process::ExitCode;
use pwd::Passwd; use pwd::Passwd;
use nix::unistd::{self, Uid, Group};
mod persist; mod auth;
mod flags; mod flags;
mod persist;
mod secure; mod secure;
@ -50,7 +50,7 @@ fn main() -> ExitCode {
} }
// Load the command config from /etc // Load the command config from /etc
let config = match config("/etc/crab.conf") { let config = match auth::load_config("/etc/crab.conf") {
Some(data) => data, Some(data) => data,
None => return ExitCode::from(ERROR_CONFIG) None => return ExitCode::from(ERROR_CONFIG)
}; };
@ -65,7 +65,7 @@ fn main() -> ExitCode {
}; };
// check if the user is authorized // check if the user is authorized
let persist = match allowed(&config, &user.name) { let persist = match auth::authorize(&config, &user.name) {
Some(data) => data && !flags.dont_persist, Some(data) => data && !flags.dont_persist,
None => { None => {
eprintln!("Operation Not Permitted."); eprintln!("Operation Not Permitted.");
@ -74,7 +74,7 @@ fn main() -> ExitCode {
}; };
// authenticate the user // authenticate the user
if !validate(&user.name, persist) { if !auth::authenticate(&user.name, persist) {
eprintln!("Authentication failed."); eprintln!("Authentication failed.");
return ExitCode::from(ERROR_AUTH_FAILED); return ExitCode::from(ERROR_AUTH_FAILED);
} }
@ -101,137 +101,3 @@ Options:
-d If your user is set to persist, dont save persistance"; -d If your user is set to persist, dont save persistance";
println!("{}", help); println!("{}", help);
} }
struct Config {
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) {
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;
}
secure::elevate_privilages(0, 0);
if persist {
persist::set_persist(user);
}
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> {
// 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) => {
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})
}

View file

@ -78,9 +78,7 @@ fn get_persist_config() -> Option<Value> {
}; };
let data = match secure::read_file(PERSIST_PATH, &format!("{}", session)) { let data = match secure::read_file(PERSIST_PATH, &format!("{}", session)) {
Some(data) => data, Some(data) => data,
None => { None => "{}".to_string()
"{}".to_string()
}
}; };
let json: Value = match serde_json::from_str(&data) { let json: Value = match serde_json::from_str(&data) {
Ok(data) => data, Ok(data) => data,

View file

@ -11,11 +11,10 @@ use nix::unistd::{self, Uid, Gid};
/// A ``io::Result<()>`` if the write succeded or failed /// A ``io::Result<()>`` if the write succeded or failed
pub fn write_file(dir: &str, file: &str, data: &str) -> Result<(), io::Error> { pub fn write_file(dir: &str, file: &str, data: &str) -> Result<(), io::Error> {
fs::create_dir_all(dir)?; fs::create_dir_all(dir)?;
set_file_permissions(0, 0, 0o600, dir)?; set_file_permissions(0, 0, 0o100600, dir)?;
let path = path(dir, file); let path = path(dir, file);
fs::write(&path, "")?;
set_file_permissions(0, 0, 0o600, &path)?;
fs::write(&path, data)?; fs::write(&path, data)?;
set_file_permissions(0, 0, 0o100600, &path)?;
Ok(()) Ok(())
} }