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::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})
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue