finalize adding groups
This commit is contained in:
parent
b457c08923
commit
9bb5154ae8
4 changed files with 134 additions and 145 deletions
126
src/auth.rs
Normal file
126
src/auth.rs
Normal 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;
|
||||
}
|
144
src/main.rs
144
src/main.rs
|
@ -1,11 +1,11 @@
|
|||
use std::env;
|
||||
use std::process::ExitCode;
|
||||
use pwd::Passwd;
|
||||
use nix::unistd::{self, Uid, Group};
|
||||
|
||||
|
||||
mod persist;
|
||||
mod auth;
|
||||
mod flags;
|
||||
mod persist;
|
||||
mod secure;
|
||||
|
||||
|
||||
|
@ -50,7 +50,7 @@ fn main() -> ExitCode {
|
|||
}
|
||||
|
||||
// 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,
|
||||
None => return ExitCode::from(ERROR_CONFIG)
|
||||
};
|
||||
|
@ -65,7 +65,7 @@ fn main() -> ExitCode {
|
|||
};
|
||||
|
||||
// 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,
|
||||
None => {
|
||||
eprintln!("Operation Not Permitted.");
|
||||
|
@ -74,7 +74,7 @@ fn main() -> ExitCode {
|
|||
};
|
||||
|
||||
// authenticate the user
|
||||
if !validate(&user.name, persist) {
|
||||
if !auth::authenticate(&user.name, persist) {
|
||||
eprintln!("Authentication failed.");
|
||||
return ExitCode::from(ERROR_AUTH_FAILED);
|
||||
}
|
||||
|
@ -101,137 +101,3 @@ Options:
|
|||
-d If your user is set to persist, dont save persistance";
|
||||
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})
|
||||
}
|
||||
|
|
|
@ -78,9 +78,7 @@ fn get_persist_config() -> Option<Value> {
|
|||
};
|
||||
let data = match secure::read_file(PERSIST_PATH, &format!("{}", session)) {
|
||||
Some(data) => data,
|
||||
None => {
|
||||
"{}".to_string()
|
||||
}
|
||||
None => "{}".to_string()
|
||||
};
|
||||
let json: Value = match serde_json::from_str(&data) {
|
||||
Ok(data) => data,
|
||||
|
|
|
@ -11,11 +11,10 @@ use nix::unistd::{self, Uid, Gid};
|
|||
/// A ``io::Result<()>`` if the write succeded or failed
|
||||
pub fn write_file(dir: &str, file: &str, data: &str) -> Result<(), io::Error> {
|
||||
fs::create_dir_all(dir)?;
|
||||
set_file_permissions(0, 0, 0o600, dir)?;
|
||||
set_file_permissions(0, 0, 0o100600, dir)?;
|
||||
let path = path(dir, file);
|
||||
fs::write(&path, "")?;
|
||||
set_file_permissions(0, 0, 0o600, &path)?;
|
||||
fs::write(&path, data)?;
|
||||
set_file_permissions(0, 0, 0o100600, &path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue