doc string, refactor, config token error
This commit is contained in:
parent
58208a1268
commit
de24d5499a
5 changed files with 90 additions and 37 deletions
14
readme.md
14
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`.
|
If you are on an arch based distro, crab is avaliable on the [AUR](https://aur.archlinux.org/packages/crab) as `crab`.
|
||||||
|
|
||||||
# Configuration
|
# 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
|
For Example
|
||||||
```
|
```
|
||||||
root true
|
deny :docker
|
||||||
tylerm false
|
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`.
|
The default configuration file is stored in `/usr/share/crab/crab.conf` and must be coppied to `/etc/crab.conf`.
|
||||||
|
|
51
src/auth.rs
51
src/auth.rs
|
@ -1,8 +1,7 @@
|
||||||
use nix::unistd::{User, Group, Uid, Gid};
|
use nix::unistd::{User, Group, Uid, Gid, self};
|
||||||
use crate::{persist, secure};
|
use crate::persist;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub permit: bool,
|
pub permit: bool,
|
||||||
pub persist: bool,
|
pub persist: bool,
|
||||||
|
@ -55,20 +54,31 @@ pub fn load_config_file(path: &str) -> Option<Vec<Config>> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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) => {
|
Some(index) => {
|
||||||
if index != len - 2 {
|
if index != len - 2 {
|
||||||
config_error(line_num, "Target user not specified or to many arguments after `as`");
|
config_error(line_num, "Target user not specified or to many arguments after `as`");
|
||||||
continue;
|
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)
|
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) =
|
let (user_uid, user_gid) =
|
||||||
|
@ -111,11 +121,18 @@ pub fn load_config_file(path: &str) -> Option<Vec<Config>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Print a crab config error to the standard output
|
||||||
fn config_error(line_num: usize, message: &str) {
|
fn config_error(line_num: usize, message: &str) {
|
||||||
eprintln!("Error in config at line {}: {}", line_num, message);
|
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<Uid> {
|
fn get_uid_from_name(name: &str) -> Option<Uid> {
|
||||||
return match User::from_name(name) {
|
return match User::from_name(name) {
|
||||||
Ok(result) => match result {
|
Ok(result) => match result {
|
||||||
|
@ -127,6 +144,12 @@ fn get_uid_from_name(name: &str) -> Option<Uid> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 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<String> {
|
fn get_name_from_uid(uid: Uid) -> Option<String> {
|
||||||
return match User::from_uid(uid) {
|
return match User::from_uid(uid) {
|
||||||
Ok(result) => match result {
|
Ok(result) => match result {
|
||||||
|
@ -138,6 +161,12 @@ fn get_name_from_uid(uid: Uid) -> Option<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 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<Gid> {
|
fn get_gid_from_name(name: &str) -> Option<Gid> {
|
||||||
return match Group::from_name(name) {
|
return match Group::from_name(name) {
|
||||||
Ok(result) => match result {
|
Ok(result) => match result {
|
||||||
|
@ -155,7 +184,7 @@ fn get_gid_from_name(name: &str) -> Option<Gid> {
|
||||||
/// either the function coudn't retrieve the users groups, or the user is not in
|
/// either the function coudn't retrieve the users groups, or the user is not in
|
||||||
/// any groups.
|
/// any groups.
|
||||||
fn get_groups() -> Vec<Gid> {
|
fn get_groups() -> Vec<Gid> {
|
||||||
let groups = match nix::unistd::getgroups() {
|
let groups = match unistd::getgroups() {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => return vec![]
|
Err(_) => return vec![]
|
||||||
};
|
};
|
||||||
|
@ -212,7 +241,6 @@ pub fn authenticate(config: &Config, force_pass: bool, uid: Uid) -> bool {
|
||||||
None => return false
|
None => return false
|
||||||
};
|
};
|
||||||
if config.nopass || ( !force_pass && config.persist && persist::get_persist(&name) ) {
|
if config.nopass || ( !force_pass && config.persist && persist::get_persist(&name) ) {
|
||||||
secure::elevate_privilages(config.privlaged_uid);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let input = match rpassword::prompt_password(format!("crab ({}) password: ", &name)) {
|
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() {
|
if !auth.authenticate().is_ok() || !auth.open_session().is_ok() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if config.persist {
|
if !force_pass && config.persist {
|
||||||
persist::set_persist(&name);
|
persist::set_persist(&name);
|
||||||
|
} else if force_pass {
|
||||||
|
persist::remove_persist(&name);
|
||||||
}
|
}
|
||||||
secure::elevate_privilages(config.privlaged_uid);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ const ERROR_ARGS: u8 = 1;
|
||||||
const ERROR_CONFIG: u8 = 2;
|
const ERROR_CONFIG: u8 = 2;
|
||||||
const ERROR_NOT_AUTHORIZED: u8 = 3;
|
const ERROR_NOT_AUTHORIZED: u8 = 3;
|
||||||
const ERROR_AUTH_FAILED: u8 = 4;
|
const ERROR_AUTH_FAILED: u8 = 4;
|
||||||
|
const ERROR_ELEVATE_PRIVILEGES: u8 = 5;
|
||||||
|
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
|
@ -69,6 +70,12 @@ fn main() -> ExitCode {
|
||||||
return ExitCode::from(ERROR_AUTH_FAILED);
|
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
|
// execute the passed command
|
||||||
let start = 1 + flags.arg_count;
|
let start = 1 + flags.arg_count;
|
||||||
let err = exec::execvp(&args[start], &args[start..]);
|
let err = exec::execvp(&args[start], &args[start..]);
|
||||||
|
|
|
@ -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
|
/// #### Arguments
|
||||||
/// * `user` - The user to set persisted
|
/// * `user` - The user to set persisted
|
||||||
pub fn set_persist(user: &str) {
|
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()) {
|
match secure::write_file(PERSIST_PATH, &format!("{}", session), &json.to_string()) {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
eprintln!("crab: An Internal Has Error: {}", e);
|
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");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::{os::{unix::prelude::PermissionsExt, linux::fs::MetadataExt}, fs, io::{self, ErrorKind}};
|
use std::{os::{unix::prelude::PermissionsExt, linux::fs::MetadataExt}, fs, io::{self, ErrorKind}, path::Path};
|
||||||
use nix::unistd::{self, Uid};
|
use nix::unistd::{self, Uid, Gid};
|
||||||
|
|
||||||
|
|
||||||
/// Writes a file securly to a specified path with given data
|
/// Writes a file securly to a specified path with given data
|
||||||
|
@ -40,19 +40,6 @@ pub fn read_file(dir: &str, file: &str) -> Option<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// 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
|
/// Sets the permission for a secure file
|
||||||
/// #### Arguments
|
/// #### Arguments
|
||||||
/// * `uid` - The user to own the file
|
/// * `uid` - The user to own the file
|
||||||
|
@ -62,7 +49,7 @@ pub fn elevate_privilages(uid: Uid) -> bool {
|
||||||
/// #### Returns
|
/// #### Returns
|
||||||
/// A ``io::Result<()>`` if the write succeded or failed
|
/// A ``io::Result<()>`` if the write succeded or failed
|
||||||
fn set_file_permissions(uid: u32, gid: u32, mode: u32, path: &str) -> Result<(), io::Error> {
|
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 metadata = fs::metadata(path)?;
|
||||||
let mut perms = metadata.permissions();
|
let mut perms = metadata.permissions();
|
||||||
perms.set_mode(mode);
|
perms.set_mode(mode);
|
||||||
|
|
Loading…
Reference in a new issue