update config format
This commit is contained in:
parent
f528a215f5
commit
58208a1268
12 changed files with 348 additions and 317 deletions
68
Cargo.lock
generated
68
Cargo.lock
generated
|
@ -34,13 +34,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crab"
|
name = "crab"
|
||||||
version = "0.0.5"
|
version = "0.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"exec",
|
"exec",
|
||||||
"nix",
|
"nix",
|
||||||
"pam",
|
"pam",
|
||||||
"procinfo",
|
"procinfo",
|
||||||
"pwd",
|
|
||||||
"rpassword",
|
"rpassword",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"time",
|
"time",
|
||||||
|
@ -144,15 +143,6 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.47"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "procinfo"
|
name = "procinfo"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -165,25 +155,6 @@ dependencies = [
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pwd"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72c71c0c79b9701efe4e1e4b563b2016dd4ee789eb99badcb09d61ac4b92e4a2"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rpassword"
|
name = "rpassword"
|
||||||
version = "7.1.0"
|
version = "7.1.0"
|
||||||
|
@ -241,37 +212,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.103"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror"
|
|
||||||
version = "1.0.37"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror-impl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror-impl"
|
|
||||||
version = "1.0.37"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
|
@ -288,12 +228,6 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "users"
|
name = "users"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
[package]
|
[package]
|
||||||
name = "crab"
|
name = "crab"
|
||||||
version = "0.0.5"
|
version = "0.0.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pam = "0.7.0"
|
pam = "0.7.0"
|
||||||
pwd = "1.4.0"
|
|
||||||
nix = "0.25.0"
|
nix = "0.25.0"
|
||||||
exec = "0.3.1"
|
exec = "0.3.1"
|
||||||
rpassword = "7.1.0"
|
rpassword = "7.1.0"
|
||||||
|
|
6
conf
6
conf
|
@ -1 +1,5 @@
|
||||||
root true
|
permit nopass linus as root
|
||||||
|
deny :docker
|
||||||
|
#deny jane
|
||||||
|
permit persist :wheel
|
||||||
|
permit jane as doe
|
|
@ -1,6 +1,6 @@
|
||||||
pkgbase = crab
|
pkgbase = crab
|
||||||
pkgdesc = A rusty permission authentication system
|
pkgdesc = A rusty permission authentication system
|
||||||
pkgver = 0.0.5
|
pkgver = 0.0.6
|
||||||
pkgrel = 2
|
pkgrel = 2
|
||||||
url = https://g.tylerm.dev/tylermurphy534/crab.git
|
url = https://g.tylerm.dev/tylermurphy534/crab.git
|
||||||
arch = x86_64
|
arch = x86_64
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Maintainer: Tyler Murphy <tylermurphy534@gmail.com>
|
# Maintainer: Tyler Murphy <tylermurphy534@gmail.com>
|
||||||
pkgname=crab
|
pkgname=crab
|
||||||
pkgver=0.0.5
|
pkgver=0.0.6
|
||||||
pkgrel=2
|
pkgrel=2
|
||||||
pkgdesc="A rusty permission authentication system"
|
pkgdesc="A rusty permission authentication system"
|
||||||
arch=('x86_64' 'i686')
|
arch=('x86_64' 'i686')
|
||||||
|
@ -18,7 +18,7 @@ build() {
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd crab
|
cd crab
|
||||||
install -D --mode=6755 --owner=root --group=root ./target/release/crab ${pkgdir}/usr/bin/crab
|
install -D --mode=4755 --owner=root --group=root ./target/release/crab ${pkgdir}/usr/bin/crab
|
||||||
install -D --mode=660 --owner=root --group=root pam ${pkgdir}/etc/pam.d/crab
|
install -D --mode=600 --owner=root --group=root pam ${pkgdir}/etc/pam.d/crab
|
||||||
install -D --mode=660 --owner=root --group=root conf ${pkgdir}/usr/share/crab/crab.conf
|
install -D --mode=600 --owner=root --group=root conf ${pkgdir}/usr/share/crab/crab.conf
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,14 @@ fi
|
||||||
# Copy executable to bin
|
# Copy executable to bin
|
||||||
cp ./target/release/crab /usr/bin/crab
|
cp ./target/release/crab /usr/bin/crab
|
||||||
chown root:root /bin/crab
|
chown root:root /bin/crab
|
||||||
chmod 6755 /bin/crab
|
chmod 4755 /bin/crab
|
||||||
|
|
||||||
# Set up config files
|
# Set up config files
|
||||||
cp pam /etc/pam.d/crab
|
cp pam /etc/pam.d/crab
|
||||||
|
chmod 600 /etc/pam.d/crab
|
||||||
|
|
||||||
mkdir /usr/share/crab
|
mkdir /usr/share/crab
|
||||||
|
chmod 600 /usr/share/crab
|
||||||
|
|
||||||
cp conf /usr/share/crab/crab.conf
|
cp conf /usr/share/crab/crab.conf
|
||||||
chmod 660 /usr/share/crab/crab.conf
|
chmod 600 /usr/share/crab/crab.conf
|
||||||
|
|
277
src/auth.rs
277
src/auth.rs
|
@ -1,9 +1,15 @@
|
||||||
use nix::unistd::Group;
|
use nix::unistd::{User, Group, Uid, Gid};
|
||||||
use crate::{persist, secure};
|
use crate::{persist, secure};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub identitys: Vec<(String, bool)>,
|
pub permit: bool,
|
||||||
|
pub persist: bool,
|
||||||
|
pub nopass: bool,
|
||||||
|
pub user_uid: Option<Uid>,
|
||||||
|
pub user_gid: Option<Gid>,
|
||||||
|
pub privlaged_uid: Uid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,37 +20,132 @@ pub struct Config {
|
||||||
/// * `path` - The path to the config file
|
/// * `path` - The path to the config file
|
||||||
/// #### Returns
|
/// #### Returns
|
||||||
/// * `None` - If the config failed to load
|
/// * `None` - If the config failed to load
|
||||||
/// * `Some(Config)` - If the config was sucessfully parsed.
|
/// * `Some(Vec<Config>)` - If the config was sucessfully parsed.
|
||||||
pub fn load_config(path: &str) -> Option<Config> {
|
pub fn load_config_file(path: &str) -> Option<Vec<Config>> {
|
||||||
let file = match std::fs::read_to_string(path) {
|
let file = match std::fs::read_to_string(path) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}: {}", &path, e);
|
eprintln!("{}: {}", &path, e);
|
||||||
return None
|
return None
|
||||||
},
|
},
|
||||||
Ok(data) => data
|
Ok(data) => data
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut identitys = vec![];
|
let mut configs = vec![];
|
||||||
for (line_num, line) in file.split("\n").enumerate() {
|
|
||||||
let args: Vec<&str> = line.split(" ").collect();
|
for (line_num, line) in file.split("\n").enumerate() {
|
||||||
if line.starts_with("#") || line.trim() == "" {
|
|
||||||
continue;
|
let args: Vec<&str> = line.split(" ").collect();
|
||||||
}
|
let len = args.len();
|
||||||
if args.len() < 2 {
|
|
||||||
eprintln!("Error in config at line {}: Not enough arguments", line_num);
|
if line.starts_with("#") || line.trim() == "" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let identity: String = args[0].to_string();
|
|
||||||
let persist: bool = match args[1].parse() {
|
if len < 2 {
|
||||||
Err(e) => {
|
config_error(line_num, "Not enough arguments");
|
||||||
eprintln!("Error in config at line {}: {}", line_num, e);
|
continue;
|
||||||
continue;
|
}
|
||||||
},
|
|
||||||
Ok(data) => data
|
let permit = match args[0] {
|
||||||
};
|
"permit" => true,
|
||||||
identitys.push((identity, persist));
|
"deny" => false,
|
||||||
}
|
_ => {
|
||||||
Some(Config{identitys})
|
config_error(line_num, "The first argument must be `permit` or `deny");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (user_name, privlaged_name, as_index) = match args.iter().position(|&a| a == "as") {
|
||||||
|
Some(index) => {
|
||||||
|
if index != len - 2 {
|
||||||
|
config_error(line_num, "Target user not specified or to many arguments after `as`");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(args[index-1].to_string(), args[index+1].to_string(), index)
|
||||||
|
},
|
||||||
|
None => (args[len-1].to_string(), "root".to_string(), len-1)
|
||||||
|
};
|
||||||
|
|
||||||
|
let persist = args[1..as_index].contains(&"persist");
|
||||||
|
|
||||||
|
let nopass = args[1..as_index].contains(&"nopass");
|
||||||
|
|
||||||
|
|
||||||
|
let (user_uid, user_gid) =
|
||||||
|
if user_name.starts_with(":") {
|
||||||
|
match get_gid_from_name(&user_name[1..]) {
|
||||||
|
Some(gid) => (None, Some(gid)),
|
||||||
|
None => {
|
||||||
|
config_error(line_num, &format!("Group `{}` does not exist", &user_name[1..]));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match get_uid_from_name(&user_name) {
|
||||||
|
Some(uid) => (Some(uid), None),
|
||||||
|
None => {
|
||||||
|
config_error(line_num, &format!("User `{}` does not exist", user_name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let privlaged_uid = match get_uid_from_name(&privlaged_name) {
|
||||||
|
Some(uid) => uid,
|
||||||
|
None => {
|
||||||
|
config_error(line_num, &format!("User `{}` does not exist", privlaged_name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
configs.push(Config {
|
||||||
|
permit,
|
||||||
|
persist,
|
||||||
|
nopass,
|
||||||
|
user_uid,
|
||||||
|
user_gid,
|
||||||
|
privlaged_uid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some(configs)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn config_error(line_num: usize, message: &str) {
|
||||||
|
eprintln!("Error in config at line {}: {}", line_num, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_uid_from_name(name: &str) -> Option<Uid> {
|
||||||
|
return match User::from_name(name) {
|
||||||
|
Ok(result) => match result {
|
||||||
|
Some(data) => Some(data.uid),
|
||||||
|
None => None
|
||||||
|
},
|
||||||
|
Err(_) => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_name_from_uid(uid: Uid) -> Option<String> {
|
||||||
|
return match User::from_uid(uid) {
|
||||||
|
Ok(result) => match result {
|
||||||
|
Some(data) => Some(data.name),
|
||||||
|
None => None
|
||||||
|
},
|
||||||
|
Err(_) => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_gid_from_name(name: &str) -> Option<Gid> {
|
||||||
|
return match Group::from_name(name) {
|
||||||
|
Ok(result) => match result {
|
||||||
|
Some(data) => Some(data.gid),
|
||||||
|
None => None
|
||||||
|
},
|
||||||
|
Err(_) => None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,41 +154,44 @@ pub fn load_config(path: &str) -> Option<Config> {
|
||||||
/// A vector of strings of the groups the user is in. If the vector is empty,
|
/// 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
|
/// either the function coudn't retrieve the users groups, or the user is not in
|
||||||
/// any groups.
|
/// any groups.
|
||||||
fn get_groups() -> Vec<String> {
|
fn get_groups() -> Vec<Gid> {
|
||||||
let groups = match nix::unistd::getgroups() {
|
let groups = match nix::unistd::getgroups() {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => return vec![]
|
Err(_) => return vec![]
|
||||||
};
|
};
|
||||||
let names = groups.iter()
|
groups
|
||||||
.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.
|
/// Returns if the user is authorized given a sepcific config and user name.
|
||||||
/// #### Arguments
|
/// #### Arguments
|
||||||
/// * `config` - A config struct contaning the authorization settings for crab
|
/// * `config` - A config struct contaning the authorization settings for crab
|
||||||
/// * `user` - The name of the user to check is authorized
|
/// * `uid` - The uid to check is authorized
|
||||||
/// #### Returns
|
/// #### Returns
|
||||||
/// * `None` - If the user is not authorized
|
/// * `None` - If the uid is not authorized
|
||||||
/// * `Some(bool)` - If the user is authorized, returning the boolean if the
|
/// * `Some(Config)` - If the uid is authorized, returns the specific index of the associated config
|
||||||
/// user is set to persist
|
pub fn authorize(configs: &Vec<Config>, uid: Uid) -> Option<usize> {
|
||||||
pub fn authorize(config: &Config, user: &str) -> Option<bool> {
|
let groups = get_groups();
|
||||||
let groups = get_groups();
|
for (config_index, config) in configs.iter().enumerate() {
|
||||||
for (identity, persist) in &config.identitys {
|
if config.user_gid.is_some() {
|
||||||
if identity.starts_with(":") {
|
if groups.contains(&config.user_gid.unwrap()) {
|
||||||
let group = &identity[1..];
|
if config.permit {
|
||||||
if groups.contains(&group.to_string()) {
|
return Some(config_index)
|
||||||
return Some(persist.clone());
|
} else {
|
||||||
};
|
return None
|
||||||
} else if identity == user {
|
}
|
||||||
return Some(persist.clone());
|
}
|
||||||
}
|
} else if config.user_uid.is_some() {
|
||||||
}
|
if config.user_uid.unwrap() == uid {
|
||||||
None
|
if config.permit {
|
||||||
|
return Some(config_index)
|
||||||
|
} else {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,31 +200,36 @@ pub fn authorize(config: &Config, user: &str) -> Option<bool> {
|
||||||
/// read the persist file, and then skip authentication if
|
/// read the persist file, and then skip authentication if
|
||||||
/// the is still persisted.
|
/// the is still persisted.
|
||||||
/// #### Arguments
|
/// #### Arguments
|
||||||
/// * `user` - The login name of the linux user
|
/// * `config` - The config associated with the user to authenticate with
|
||||||
/// * `persist` - If the user's login should persist
|
/// * `force_pass` - Force a password prompt even if persistance is set to true
|
||||||
|
/// * `uid` - The user uid that is authenticating
|
||||||
/// #### Returns
|
/// #### Returns
|
||||||
/// * `true` - If the user authenticated sucessfully, or the user is persisted
|
/// * `true` - If the user authenticated sucessfully
|
||||||
/// * `false` - If the user failed to authenticate
|
/// * `false` - If the user failed to authenticate
|
||||||
pub fn authenticate(user: &str, persist: bool) -> bool {
|
pub fn authenticate(config: &Config, force_pass: bool, uid: Uid) -> bool {
|
||||||
if persist && persist::get_persist(user) {
|
let name = match get_name_from_uid(uid) {
|
||||||
secure::elevate_privilages(0, 0);
|
Some(data) => data,
|
||||||
return true;
|
None => return false
|
||||||
}
|
};
|
||||||
let input = match rpassword::prompt_password(format!("crab ({}) password: ", user)) {
|
if config.nopass || ( !force_pass && config.persist && persist::get_persist(&name) ) {
|
||||||
Ok(data) => data,
|
secure::elevate_privilages(config.privlaged_uid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let input = match rpassword::prompt_password(format!("crab ({}) password: ", &name)) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => return false
|
||||||
|
};
|
||||||
|
let mut auth = match pam::Authenticator::with_password("crab") {
|
||||||
|
Ok(data) => data,
|
||||||
Err(_) => return false
|
Err(_) => return false
|
||||||
};
|
};
|
||||||
let mut auth = match pam::Authenticator::with_password("crab") {
|
auth.get_handler().set_credentials(&name, input);
|
||||||
Ok(data) => data,
|
if !auth.authenticate().is_ok() || !auth.open_session().is_ok() {
|
||||||
Err(_) => return false
|
return false;
|
||||||
};
|
}
|
||||||
auth.get_handler().set_credentials(user.to_owned(), input);
|
if config.persist {
|
||||||
if !auth.authenticate().is_ok() || !auth.open_session().is_ok() {
|
persist::set_persist(&name);
|
||||||
return false;
|
}
|
||||||
}
|
secure::elevate_privilages(config.privlaged_uid);
|
||||||
if persist {
|
return true;
|
||||||
persist::set_persist(user);
|
|
||||||
}
|
|
||||||
secure::elevate_privilages(0, 0);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
88
src/flags.rs
88
src/flags.rs
|
@ -5,10 +5,10 @@ const DONT_PERSIST: &str = "d";
|
||||||
|
|
||||||
|
|
||||||
pub struct Flags {
|
pub struct Flags {
|
||||||
pub help: bool,
|
pub help: bool,
|
||||||
pub version: bool,
|
pub version: bool,
|
||||||
pub dont_persist: bool,
|
pub dont_persist: bool,
|
||||||
pub arg_count: usize
|
pub arg_count: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,32 +19,32 @@ pub struct Flags {
|
||||||
/// * `None` - If there is an invalid argument in the list
|
/// * `None` - If there is an invalid argument in the list
|
||||||
/// * `Some(Flags)` - If the arguments were secussfully parsed, returning the flags
|
/// * `Some(Flags)` - If the arguments were secussfully parsed, returning the flags
|
||||||
pub fn parse(args: &[String]) -> Option<Flags> {
|
pub fn parse(args: &[String]) -> Option<Flags> {
|
||||||
let mut flags = Flags {
|
let mut flags = Flags {
|
||||||
help: false,
|
help: false,
|
||||||
version: false,
|
version: false,
|
||||||
dont_persist: false,
|
dont_persist: false,
|
||||||
arg_count: 0
|
arg_count: 0
|
||||||
};
|
};
|
||||||
for arg in args {
|
for arg in args {
|
||||||
if !is_arg(&arg) { break; }
|
if !is_arg(&arg) { break; }
|
||||||
flags.arg_count += 1;
|
flags.arg_count += 1;
|
||||||
if arg.starts_with("--") {
|
if arg.starts_with("--") {
|
||||||
let flag = &arg[2..];
|
let flag = &arg[2..];
|
||||||
if !set_flag(&flag, &mut flags) {
|
if !set_flag(&flag, &mut flags) {
|
||||||
eprintln!("Invalid argument: {}", arg);
|
eprintln!("Invalid argument: {}", arg);
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let flag = &arg[1..];
|
let flag = &arg[1..];
|
||||||
for char in flag.chars() {
|
for char in flag.chars() {
|
||||||
if !set_flag(&char.to_string(), &mut flags) {
|
if !set_flag(&char.to_string(), &mut flags) {
|
||||||
eprintln!("Invalid argument: {}", arg);
|
eprintln!("Invalid argument: {}", arg);
|
||||||
return None
|
return None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
Some(flags)
|
||||||
Some(flags)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,17 +62,17 @@ fn is_arg(arg: &str) -> bool {
|
||||||
/// * `true` - If the argument passed is a valid flag
|
/// * `true` - If the argument passed is a valid flag
|
||||||
/// * `false` - If the argument passed is not a valid flag
|
/// * `false` - If the argument passed is not a valid flag
|
||||||
fn set_flag(arg: &str, flags: &mut Flags) -> bool {
|
fn set_flag(arg: &str, flags: &mut Flags) -> bool {
|
||||||
if has_flag_set(&arg, HELP_FLAG) {
|
if has_flag_set(&arg, HELP_FLAG) {
|
||||||
flags.help = true;
|
flags.help = true;
|
||||||
return true
|
return true
|
||||||
} else if has_flag_set(&arg, VERSION_FLAG) {
|
} else if has_flag_set(&arg, VERSION_FLAG) {
|
||||||
flags.version = true;
|
flags.version = true;
|
||||||
return true
|
return true
|
||||||
} else if has_flag_set(&arg, DONT_PERSIST) {
|
} else if has_flag_set(&arg, DONT_PERSIST) {
|
||||||
flags.dont_persist = true;
|
flags.dont_persist = true;
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,10 +83,10 @@ fn set_flag(arg: &str, flags: &mut Flags) -> bool {
|
||||||
/// * `true` - If the argument matches the flag
|
/// * `true` - If the argument matches the flag
|
||||||
/// * `false` - If the argument doesn't match the flag
|
/// * `false` - If the argument doesn't match the flag
|
||||||
fn has_flag_set(arg: &str, check: &str) -> bool {
|
fn has_flag_set(arg: &str, check: &str) -> bool {
|
||||||
for check_arg in check.split(" ") {
|
for check_arg in check.split(" ") {
|
||||||
if check_arg == arg {
|
if check_arg == arg {
|
||||||
return true
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return false
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -1,6 +1,5 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
use pwd::Passwd;
|
|
||||||
|
|
||||||
|
|
||||||
mod auth;
|
mod auth;
|
||||||
|
@ -11,9 +10,8 @@ mod secure;
|
||||||
|
|
||||||
const ERROR_ARGS: u8 = 1;
|
const ERROR_ARGS: u8 = 1;
|
||||||
const ERROR_CONFIG: u8 = 2;
|
const ERROR_CONFIG: u8 = 2;
|
||||||
const ERROR_NO_USER: u8 = 3;
|
const ERROR_NOT_AUTHORIZED: u8 = 3;
|
||||||
const ERROR_NOT_AUTHORIZED: u8 = 4;
|
const ERROR_AUTH_FAILED: u8 = 4;
|
||||||
const ERROR_AUTH_FAILED: u8 = 5;
|
|
||||||
|
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
|
@ -33,7 +31,7 @@ fn main() -> ExitCode {
|
||||||
|
|
||||||
// If the version arg flag is set, print the crab version
|
// If the version arg flag is set, print the crab version
|
||||||
if flags.version {
|
if flags.version {
|
||||||
println!("crab version 0.0.5");
|
println!("crab version 0.0.6");
|
||||||
return ExitCode::SUCCESS;
|
return ExitCode::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,23 +48,15 @@ fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the command config from /etc
|
// Load the command config from /etc
|
||||||
let config = match auth::load_config("/etc/crab.conf") {
|
let configs = match auth::load_config_file("/etc/crab.conf") {
|
||||||
Some(data) => data,
|
Some(data) => data,
|
||||||
None => return ExitCode::from(ERROR_CONFIG)
|
None => return ExitCode::from(ERROR_CONFIG)
|
||||||
};
|
};
|
||||||
|
|
||||||
// get the current user login
|
|
||||||
let user = match Passwd::current_user() {
|
|
||||||
Some(data) => data,
|
|
||||||
None => {
|
|
||||||
eprintln!("You dont exist.");
|
|
||||||
return ExitCode::from(ERROR_NO_USER);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// check if the user is authorized
|
// check if the user is authorized
|
||||||
let persist = match auth::authorize(&config, &user.name) {
|
let auth = match auth::authorize(&configs, nix::unistd::getuid()) {
|
||||||
Some(data) => data && !flags.dont_persist,
|
Some(data) => data,
|
||||||
None => {
|
None => {
|
||||||
eprintln!("Operation Not Permitted.");
|
eprintln!("Operation Not Permitted.");
|
||||||
return ExitCode::from(ERROR_NOT_AUTHORIZED);
|
return ExitCode::from(ERROR_NOT_AUTHORIZED);
|
||||||
|
@ -74,7 +64,7 @@ fn main() -> ExitCode {
|
||||||
};
|
};
|
||||||
|
|
||||||
// authenticate the user
|
// authenticate the user
|
||||||
if !auth::authenticate(&user.name, persist) {
|
if !auth::authenticate(&configs[auth], flags.dont_persist, nix::unistd::getuid()) {
|
||||||
eprintln!("Authentication failed.");
|
eprintln!("Authentication failed.");
|
||||||
return ExitCode::from(ERROR_AUTH_FAILED);
|
return ExitCode::from(ERROR_AUTH_FAILED);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,15 +16,15 @@ const PERSIST_PATH: &str = "/var/run/crab";
|
||||||
/// * `true` - If the user is persisted
|
/// * `true` - If the user is persisted
|
||||||
/// * `false` - If the user is not persisted, or if the persist file is not trusted
|
/// * `false` - If the user is not persisted, or if the persist file is not trusted
|
||||||
pub fn get_persist(user: &str) -> bool {
|
pub fn get_persist(user: &str) -> bool {
|
||||||
let json = match get_persist_config() {
|
let json = match get_persist_config() {
|
||||||
Some(data) => data,
|
Some(data) => data,
|
||||||
None => return false
|
None => return false
|
||||||
};
|
};
|
||||||
let timestamp = match json[user].as_u64() {
|
let timestamp = match json[user].as_u64() {
|
||||||
Some(data) => data,
|
Some(data) => data,
|
||||||
None => return false
|
None => return false
|
||||||
};
|
};
|
||||||
return now() - timestamp < PERSIST_TIME && timestamp - 1 < now();
|
return now() - timestamp < PERSIST_TIME && timestamp - 1 < now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,21 +32,21 @@ pub fn get_persist(user: &str) -> bool {
|
||||||
/// #### 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) {
|
||||||
let mut json = match get_persist_config() {
|
let mut json = match get_persist_config() {
|
||||||
Some(data) => data,
|
Some(data) => data,
|
||||||
None => return
|
None => return
|
||||||
};
|
};
|
||||||
json[user] = Value::from(now());
|
json[user] = Value::from(now());
|
||||||
let session = match get_current_session() {
|
let session = match get_current_session() {
|
||||||
Some(data) => data,
|
Some(data) => data,
|
||||||
None => return
|
None => return
|
||||||
};
|
};
|
||||||
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(e) => {
|
||||||
eprintln!("crab: An Internal Has Error: {}", e);
|
eprintln!("crab: An Internal Has Error: {}", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,15 +55,15 @@ pub fn set_persist(user: &str) {
|
||||||
/// * `None` - If crab failed to get the current session
|
/// * `None` - If crab failed to get the current session
|
||||||
/// * `Some(i32)` - If the session is retrieved, returns the i32/pid_t session id
|
/// * `Some(i32)` - If the session is retrieved, returns the i32/pid_t session id
|
||||||
fn get_current_session() -> Option<i32> {
|
fn get_current_session() -> Option<i32> {
|
||||||
let pid: i32 = match std::process::id().try_into() {
|
let pid: i32 = match std::process::id().try_into() {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => return None
|
Err(_) => return None
|
||||||
};
|
};
|
||||||
let pid_stat = match procinfo::pid::stat(pid) {
|
let pid_stat = match procinfo::pid::stat(pid) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => return None
|
Err(_) => return None
|
||||||
};
|
};
|
||||||
Some(pid_stat.session)
|
Some(pid_stat.session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,23 +72,23 @@ fn get_current_session() -> Option<i32> {
|
||||||
/// * `None` - If the persist file is untrusted or doesnt exist
|
/// * `None` - If the persist file is untrusted or doesnt exist
|
||||||
/// * `Some(Value)` - If the persist file is retrieved, returns the serde_json Value of the file
|
/// * `Some(Value)` - If the persist file is retrieved, returns the serde_json Value of the file
|
||||||
fn get_persist_config() -> Option<Value> {
|
fn get_persist_config() -> Option<Value> {
|
||||||
let session = match get_current_session() {
|
let session = match get_current_session() {
|
||||||
Some(data) => data,
|
Some(data) => data,
|
||||||
None => return None
|
None => return None
|
||||||
};
|
};
|
||||||
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 => "{}".to_string()
|
None => "{}".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,
|
||||||
Err(_) => return None
|
Err(_) => return None
|
||||||
};
|
};
|
||||||
Some(json)
|
Some(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Gets the current time in seconds since the Unix Epoch
|
// Gets the current time in seconds since the Unix Epoch
|
||||||
fn now() -> u64 {
|
fn now() -> u64 {
|
||||||
return SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
|
return SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}};
|
||||||
use nix::unistd::{self, Uid, Gid};
|
use nix::unistd::{self, Uid};
|
||||||
|
|
||||||
|
|
||||||
/// Writes a file securly to a specified path with given data
|
/// Writes a file securly to a specified path with given data
|
||||||
|
@ -10,12 +10,12 @@ use nix::unistd::{self, Uid, Gid};
|
||||||
/// #### Returns
|
/// #### Returns
|
||||||
/// 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, 0o100600, dir)?;
|
set_file_permissions(0, 0, 0o100600, dir)?;
|
||||||
let path = path(dir, file);
|
let path = path(dir, file);
|
||||||
fs::write(&path, data)?;
|
fs::write(&path, data)?;
|
||||||
set_file_permissions(0, 0, 0o100600, &path)?;
|
set_file_permissions(0, 0, 0o100600, &path)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,31 +29,27 @@ pub fn write_file(dir: &str, file: &str, data: &str) -> Result<(), io::Error> {
|
||||||
/// * `None` - If the files doesnt exist or isnt trusted
|
/// * `None` - If the files doesnt exist or isnt trusted
|
||||||
/// * `Some(String) - If the file is trusted, it returns the file's contents
|
/// * `Some(String) - If the file is trusted, it returns the file's contents
|
||||||
pub fn read_file(dir: &str, file: &str) -> Option<String> {
|
pub fn read_file(dir: &str, file: &str) -> Option<String> {
|
||||||
let path = path(dir,file);
|
let path = path(dir,file);
|
||||||
if !check_file_permissions(0, 0, 0o100600, &path) {
|
if !check_file_permissions(0, 0, 0o100600, &path) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
match fs::read_to_string(&path) {
|
match fs::read_to_string(&path) {
|
||||||
Ok(data) => return Some(data),
|
Ok(data) => return Some(data),
|
||||||
Err(_) => return None
|
Err(_) => return None
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Ekevate tge oruvukages if the current process
|
/// Elecate the privlages of the current process
|
||||||
/// #### Arguments
|
/// #### Arguments
|
||||||
/// * `uid` - The uid to set the process to
|
/// * `uid` - The uid to set the process to
|
||||||
/// * `gid` - The gid to set the process to
|
|
||||||
/// #### Returns
|
/// #### Returns
|
||||||
/// If the process failes to elevate, it returns false
|
/// If the process fails to elevate, it returns false
|
||||||
pub fn elevate_privilages(uid: u32, gid: u32) -> bool {
|
pub fn elevate_privilages(uid: Uid) -> bool {
|
||||||
if unistd::setuid(Uid::from(uid)).is_err() {
|
if unistd::setuid(uid).is_err() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if unistd::setgid(Gid::from(gid)).is_err() {
|
true
|
||||||
return false;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,12 +62,12 @@ pub fn elevate_privilages(uid: u32, gid: u32) -> 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(std::path::Path::new(path), Some(unistd::Uid::from(uid)), Some(unistd::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);
|
||||||
fs::set_permissions(path, perms)?;
|
fs::set_permissions(path, perms)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,18 +80,18 @@ fn set_file_permissions(uid: u32, gid: u32, mode: u32, path: &str) -> Result<(),
|
||||||
/// #### Returns
|
/// #### Returns
|
||||||
/// True or false if the files permissions match
|
/// True or false if the files permissions match
|
||||||
fn check_file_permissions(uid: u32, gid: u32, mode: u32, path: &str) -> bool {
|
fn check_file_permissions(uid: u32, gid: u32, mode: u32, path: &str) -> bool {
|
||||||
let metadata = match fs::metadata(path) {
|
let metadata = match fs::metadata(path) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return e.kind() == ErrorKind::NotFound;
|
return e.kind() == ErrorKind::NotFound;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let perms = metadata.permissions();
|
let perms = metadata.permissions();
|
||||||
return perms.mode() == mode && metadata.st_uid() == uid && metadata.st_gid() == gid;
|
return perms.mode() == mode && metadata.st_uid() == uid && metadata.st_gid() == gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Get the path of a file given a directory and file name
|
/// Get the path of a file given a directory and file name
|
||||||
fn path(dir: &str, file: &str) -> String {
|
fn path(dir: &str, file: &str) -> String {
|
||||||
return format!("{}/{}.persist", dir, file);
|
return format!("{}/{}.persist", dir, file);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,6 @@ if [[ $(/usr/bin/id -u) -ne 0 ]]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Delete crab files
|
# Delete crab files
|
||||||
|
rm /etc/pam.d/crab
|
||||||
rm /usr/bin/crab
|
rm /usr/bin/crab
|
||||||
chown root:root /bin/crab
|
rm -fr /usr/share/crab
|
||||||
chmod 6755 /bin/crab
|
|
||||||
|
|
||||||
# Set up config files
|
|
||||||
cp pam /etc/pam.d/crab
|
|
||||||
cp -n conf /etc/crab.conf
|
|
||||||
chmod 660 /etc/crab.conf
|
|
||||||
|
|
Loading…
Reference in a new issue