update config format

This commit is contained in:
tylermurphy534 2022-11-11 01:25:10 -05:00
parent f528a215f5
commit 58208a1268
12 changed files with 348 additions and 317 deletions

68
Cargo.lock generated
View file

@ -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"

View file

@ -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
View file

@ -1 +1,5 @@
root true permit nopass linus as root
deny :docker
#deny jane
permit persist :wheel
permit jane as doe

View file

@ -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

View file

@ -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
} }

View file

@ -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

View file

@ -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,8 +20,8 @@ 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);
@ -24,27 +30,122 @@ pub fn load_config(path: &str) -> Option<Config> {
Ok(data) => data Ok(data) => data
}; };
let mut identitys = vec![]; let mut configs = vec![];
for (line_num, line) in file.split("\n").enumerate() { for (line_num, line) in file.split("\n").enumerate() {
let args: Vec<&str> = line.split(" ").collect(); let args: Vec<&str> = line.split(" ").collect();
let len = args.len();
if line.starts_with("#") || line.trim() == "" { if line.starts_with("#") || line.trim() == "" {
continue; continue;
} }
if args.len() < 2 {
eprintln!("Error in config at line {}: Not enough arguments", line_num); if len < 2 {
config_error(line_num, "Not enough arguments");
continue; continue;
} }
let identity: String = args[0].to_string();
let persist: bool = match args[1].parse() { let permit = match args[0] {
Err(e) => { "permit" => true,
eprintln!("Error in config at line {}: {}", line_num, e); "deny" => false,
_ => {
config_error(line_num, "The first argument must be `permit` or `deny");
continue; continue;
}, }
Ok(data) => data
}; };
identitys.push((identity, persist));
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
} }
Some(Config{identitys})
} }
@ -53,38 +154,41 @@ 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 (identity, persist) in &config.identitys { for (config_index, config) in configs.iter().enumerate() {
if identity.starts_with(":") { if config.user_gid.is_some() {
let group = &identity[1..]; if groups.contains(&config.user_gid.unwrap()) {
if groups.contains(&group.to_string()) { if config.permit {
return Some(persist.clone()); return Some(config_index)
}; } else {
} else if identity == user { return None
return Some(persist.clone()); }
}
} else if config.user_uid.is_some() {
if config.user_uid.unwrap() == uid {
if config.permit {
return Some(config_index)
} else {
return None
}
}
} }
} }
None None
@ -96,17 +200,22 @@ 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,
None => return false
};
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: ", user)) { let input = match rpassword::prompt_password(format!("crab ({}) password: ", &name)) {
Ok(data) => data, Ok(data) => data,
Err(_) => return false Err(_) => return false
}; };
@ -114,13 +223,13 @@ pub fn authenticate(user: &str, persist: bool) -> bool {
Ok(data) => data, Ok(data) => data,
Err(_) => return false Err(_) => return false
}; };
auth.get_handler().set_credentials(user.to_owned(), input); auth.get_handler().set_credentials(&name, input);
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 persist { if config.persist {
persist::set_persist(user); persist::set_persist(&name);
} }
secure::elevate_privilages(0, 0); secure::elevate_privilages(config.privlaged_uid);
return true; return true;
} }

View file

@ -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);
} }

View file

@ -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
@ -40,17 +40,13 @@ pub fn read_file(dir: &str, file: &str) -> Option<String> {
} }
/// 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;
}
if unistd::setgid(Gid::from(gid)).is_err() {
return false; return false;
} }
true true

View 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