summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortylermurphy534 <tylermurphy534@gmail.com>2022-11-19 17:37:56 +0000
committertylermurphy534 <tylermurphy534@gmail.com>2022-11-19 17:37:56 +0000
commitce6af95b74106d65c3d12f745d215732076562b9 (patch)
tree6f6c2ef19b93c80b9cc4567a4a94ce3621ff8eb4 /src
parentupdate aur depends (diff)
parentfix install file part 2 (diff)
downloadcrab-ce6af95b74106d65c3d12f745d215732076562b9.tar.gz
crab-ce6af95b74106d65c3d12f745d215732076562b9.tar.bz2
crab-ce6af95b74106d65c3d12f745d215732076562b9.zip
Merge pull request '0.6.0' (#1) from dev into main
Reviewed-on: https://g.tylerm.dev/tylermurphy534/crab/pulls/1
Diffstat (limited to '')
-rw-r--r--src/auth.rs264
-rw-r--r--src/flags.rs120
-rw-r--r--src/help.rs10
-rw-r--r--src/main.rs142
-rw-r--r--src/persist.rs147
-rw-r--r--src/secure.rs109
6 files changed, 555 insertions, 237 deletions
diff --git a/src/auth.rs b/src/auth.rs
new file mode 100644
index 0000000..99a8216
--- /dev/null
+++ b/src/auth.rs
@@ -0,0 +1,264 @@
+use nix::unistd::{User, Group, Uid, Gid, self};
+use crate::persist;
+
+
+pub struct Config {
+ pub permit: bool,
+ pub persist: bool,
+ pub nopass: bool,
+ pub user_uid: Option<Uid>,
+ pub user_gid: Option<Gid>,
+ pub privlaged_uid: Uid,
+}
+
+
+/// 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(Vec<Config>)` - If the config was sucessfully parsed.
+pub fn load_config_file(path: &str) -> Option<Vec<Config>> {
+ let file = match std::fs::read_to_string(path) {
+ Err(e) => {
+ eprintln!("{}: {}", &path, e);
+ return None
+ },
+ Ok(data) => data
+ };
+
+ let mut configs = vec![];
+
+ for (line_num, line) in file.split("\n").enumerate() {
+
+ let args: Vec<&str> = line.split(" ").collect();
+ let len = args.len();
+
+ if line.starts_with("#") || line.trim() == "" {
+ continue;
+ }
+
+ if len < 2 {
+ config_error(line_num, "Not enough arguments");
+ continue;
+ }
+
+ let permit = match args[0] {
+ "permit" => true,
+ "deny" => false,
+ _ => {
+ config_error(line_num, "The first argument must be `permit` or `deny");
+ continue;
+ }
+ };
+
+ let (user_name, privlaged_name, name_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-1)
+ },
+ None => (args[len-1].to_string(), "root".to_string(), len-1)
+ };
+
+ let persist = args[1..name_index].contains(&"persist");
+
+ 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) =
+ 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)
+}
+
+
+/// Print a crab config error to the standard output
+fn config_error(line_num: usize, message: &str) {
+ 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> {
+ return match User::from_name(name) {
+ Ok(result) => match result {
+ Some(data) => Some(data.uid),
+ None => None
+ },
+ Err(_) => None
+ }
+}
+
+
+/// 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> {
+ return match User::from_uid(uid) {
+ Ok(result) => match result {
+ Some(data) => Some(data.name),
+ None => None
+ },
+ Err(_) => None
+ }
+}
+
+
+/// 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> {
+ return match Group::from_name(name) {
+ Ok(result) => match result {
+ Some(data) => Some(data.gid),
+ None => None
+ },
+ Err(_) => None
+ }
+}
+
+
+/// 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<Gid> {
+ let groups = match unistd::getgroups() {
+ Ok(data) => data,
+ Err(_) => return vec![]
+ };
+ groups
+}
+
+
+/// Returns if the user is authorized given a sepcific config and user name.
+/// #### Arguments
+/// * `config` - A config struct contaning the authorization settings for crab
+/// * `uid` - The uid to check is authorized
+/// #### Returns
+/// * `None` - If the uid is not authorized
+/// * `Some(Config)` - If the uid is authorized, returns the specific index of the associated config
+pub fn authorize(configs: &Vec<Config>, uid: Uid) -> Option<usize> {
+ let groups = get_groups();
+ for (config_index, config) in configs.iter().enumerate() {
+ if config.user_gid.is_some() {
+ if groups.contains(&config.user_gid.unwrap()) {
+ if config.permit {
+ return Some(config_index)
+ } else {
+ return None
+ }
+ }
+ } else if config.user_uid.is_some() {
+ if config.user_uid.unwrap() == uid {
+ if config.permit {
+ return Some(config_index)
+ } else {
+ return None
+ }
+ }
+ }
+ }
+ 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
+/// * `config` - The config associated with the user to authenticate with
+/// * `force_pass` - Force a password prompt even if persistance is set to true
+/// * `uid` - The user uid that is authenticating
+/// #### Returns
+/// * `true` - If the user authenticated sucessfully
+/// * `false` - If the user failed to authenticate
+pub fn authenticate(config: &Config, force_pass: bool, uid: Uid) -> bool {
+ let name = match get_name_from_uid(uid) {
+ Some(data) => data,
+ None => return false
+ };
+ if config.nopass || ( !force_pass && config.persist && persist::get_persist(&name) ) {
+ 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
+ };
+ auth.get_handler().set_credentials(&name, input);
+ if !auth.authenticate().is_ok() || !auth.open_session().is_ok() {
+ return false;
+ }
+ if !force_pass && config.persist {
+ persist::set_persist(&name);
+ } else if force_pass {
+ persist::remove_persist(&name);
+ }
+ return true;
+}
diff --git a/src/flags.rs b/src/flags.rs
index 9432b8f..a945483 100644
--- a/src/flags.rs
+++ b/src/flags.rs
@@ -1,66 +1,92 @@
+
+const HELP_FLAG: &str = "help h";
+const VERSION_FLAG: &str = "version v";
+const DONT_PERSIST: &str = "d";
+
+
pub struct Flags {
- pub help: bool,
- pub version: bool,
- pub dont_persist: bool,
- pub arg_count: usize
+ pub help: bool,
+ pub version: bool,
+ pub dont_persist: bool,
+ pub arg_count: usize
}
+
+/// Parses an list of String arguments into a set of flags that can be used by crab
+/// #### Arguments
+/// * `args` - The list of String arguments
+/// #### Returns
+/// * `None` - If there is an invalid argument in the list
+/// * `Some(Flags)` - If the arguments were secussfully parsed, returning the flags
pub fn parse(args: &[String]) -> Option<Flags> {
- let mut flags = Flags {
- help: false,
- version: false,
- dont_persist: false,
- arg_count: 0
- };
- for arg in args {
- if !is_arg(&arg) { break; }
- flags.arg_count += 1;
- if arg.starts_with("--") {
- let flag = &arg[2..];
- if !set_flag(&flag, &mut flags) {
- eprintln!("Invalid argument: {}", arg);
- return None
- }
- } else {
- let flag = &arg[1..];
- for char in flag.chars() {
- if !set_flag(&char.to_string(), &mut flags) {
- eprintln!("Invalid argument: {}", arg);
- return None
+ let mut flags = Flags {
+ help: false,
+ version: false,
+ dont_persist: false,
+ arg_count: 0
+ };
+ for arg in args {
+ if !is_arg(&arg) { break; }
+ flags.arg_count += 1;
+ if arg.starts_with("--") {
+ let flag = &arg[2..];
+ if !set_flag(&flag, &mut flags) {
+ eprintln!("Invalid argument: {}", arg);
+ return None
+ }
+ } else {
+ let flag = &arg[1..];
+ for char in flag.chars() {
+ if !set_flag(&char.to_string(), &mut flags) {
+ eprintln!("Invalid argument: {}", arg);
+ return None
+ }
+ }
}
- }
}
- }
- Some(flags)
+ Some(flags)
}
+
+/// Checks if a given string is a given argument
fn is_arg(arg: &str) -> bool {
return arg.starts_with("-");
}
-const HELP_FLAG: &str = "help h";
-const VERSION_FLAG: &str = "version v";
-const DONT_PERSIST: &str = "d";
+/// Sets a flag in a `Flags` struct
+/// #### Arguments
+/// * `arg` - The argument to check
+/// * `flags` - The `Flags` stuct to update
+/// #### Returns
+/// * `true` - If the argument passed is a valid flag
+/// * `false` - If the argument passed is not a valid flag
fn set_flag(arg: &str, flags: &mut Flags) -> bool {
- if has_flag_set(&arg, HELP_FLAG) {
- flags.help = true;
- return true
- } else if has_flag_set(&arg, VERSION_FLAG) {
- flags.version = true;
- return true
- } else if has_flag_set(&arg, DONT_PERSIST) {
- flags.dont_persist = true;
- return true
- }
- false
+ if has_flag_set(&arg, HELP_FLAG) {
+ flags.help = true;
+ return true
+ } else if has_flag_set(&arg, VERSION_FLAG) {
+ flags.version = true;
+ return true
+ } else if has_flag_set(&arg, DONT_PERSIST) {
+ flags.dont_persist = true;
+ return true
+ }
+ false
}
+
+/// Returns if a given argument is a certain flag
+/// #### Arguments
+/// * `arg` - The arch to check
+/// #### Returns
+/// * `true` - If the argument matches the flag
+/// * `false` - If the argument doesn't match the flag
fn has_flag_set(arg: &str, check: &str) -> bool {
- for check_arg in check.split(" ") {
- if check_arg == arg {
- return true
+ for check_arg in check.split(" ") {
+ if check_arg == arg {
+ return true
+ }
}
- }
- return false
+ return false
}
diff --git a/src/help.rs b/src/help.rs
deleted file mode 100644
index e6f4a72..0000000
--- a/src/help.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-pub fn help() {
- let help =
-"Usage:
- crab [-d] command [args]
-Options:
- -v --version Get the current version of the package
- -h --help Generates the crab help message
- -d If your user is set to persist, dont save persistance";
- println!("{}", help);
-}
diff --git a/src/main.rs b/src/main.rs
index 28f08cd..56df611 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,142 +1,100 @@
use std::env;
use std::process::ExitCode;
-use pwd::Passwd;
-use nix::unistd;
-extern crate time;
-const ERROR_ARGS: u8 = 1;
-const ERROR_CONFIG: u8 = 2;
-const ERROR_NO_USER: u8 = 3;
-const ERROR_NOT_AUTHORIZED: u8 = 4;
-const ERROR_AUTH_FAILED: u8 = 5;
-const ERROR_RUN_ROOT: u8 = 6;
-
-mod persist;
+mod auth;
mod flags;
-mod help;
+mod persist;
mod secure;
+
+const ERROR_ARGS: u8 = 1;
+const ERROR_CONFIG: u8 = 2;
+const ERROR_NOT_AUTHORIZED: u8 = 3;
+const ERROR_AUTH_FAILED: u8 = 4;
+const ERROR_ELEVATE_PRIVILEGES: u8 = 5;
+
+
fn main() -> ExitCode {
+
+ // gets the arguments from the env
let args: Vec<String> = env::args().collect();
+
+ // pase the arguments into valid flags that are usuable by crab
let flags = match flags::parse(&args[1..]) {
Some(data) => data,
+ // if there is an invalid flag, print the help message and exit
None => {
- help::help();
+ help();
return ExitCode::from(ERROR_ARGS);
}
};
+
+ // If the version arg flag is set, print the crab version
if flags.version {
- println!("crab version 0.0.5");
+ println!("crab version 0.0.6");
return ExitCode::SUCCESS;
}
+
+ // If the help arg flag is set, print the crab help message
if flags.help {
- help::help();
+ help();
return ExitCode::SUCCESS;
}
+
+ // If the amount of acutal command arguments is less than two, a.k.a just `crab`, print the command usage
if args.len() - flags.arg_count < 2 {
println!("usage: crab [-d] command [args]");
return ExitCode::SUCCESS;
}
- let config = match config("/etc/crab.conf") {
+
+ // Load the command config from /etc
+ let configs = match auth::load_config_file("/etc/crab.conf") {
Some(data) => data,
None => return ExitCode::from(ERROR_CONFIG)
};
- let user = match Passwd::current_user() {
+
+
+ // check if the user is authorized
+ let auth = match auth::authorize(&configs, nix::unistd::getuid()) {
Some(data) => data,
None => {
- eprintln!("You dont exist.");
- return ExitCode::from(ERROR_NO_USER);
- }
- };
- let persist = match allowed(&config, &user.name) {
- Some(data) => data && !flags.dont_persist,
- None => {
eprintln!("Operation Not Permitted.");
return ExitCode::from(ERROR_NOT_AUTHORIZED);
}
};
- if !validate(&user.name, persist) {
+ // authenticate the user
+ if !auth::authenticate(&configs[auth], flags.dont_persist, nix::unistd::getuid()) {
eprintln!("Authentication failed.");
return ExitCode::from(ERROR_AUTH_FAILED);
}
- if !unistd::setuid(unistd::geteuid()).is_ok() || !unistd::setgid(unistd::getegid()).is_ok() {
- eprintln!("Failed to set root permissions");
- return ExitCode::from(ERROR_RUN_ROOT);
+ // 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
let start = 1 + flags.arg_count;
let err = exec::execvp(&args[start], &args[start..]);
+ // print an error if an error was returned
eprintln!("{}", err);
ExitCode::SUCCESS
}
-struct Config {
- users: Vec<(String, bool)>
-}
-fn validate(user: &str, persist: bool) -> bool {
- if persist && persist::get_persist(user) {
- 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);
- }
- return true;
-}
-
-fn allowed(config: &Config, user: &str) -> Option<bool> {
- for (name, persist) in &config.users {
- if name == user {
- return Some(persist.clone());
- }
- }
- None
-}
-
-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 users = vec![];
- for (line_num, line) in file.split("\n").enumerate() {
- let args: Vec<&str> = line.split(" ").collect();
- if line.trim() == "" {
- continue;
- }
- if args.len() < 2 {
- eprintln!("Error in config at line {}: Not enough arguments", line_num);
- continue;
- }
- let user: 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
- };
- users.push((user, persist));
- }
- Some(Config{users})
+/// Prints the help message to the standard output
+fn help() {
+ let help =
+"Usage:
+ crab [-d] command [args]
+Options:
+ -v --version Get the current version of the package
+ -h --help Generates the crab help message
+ -d If your user is set to persist, dont save persistance";
+ println!("{}", help);
}
diff --git a/src/persist.rs b/src/persist.rs
index 6a813dc..2bca386 100644
--- a/src/persist.rs
+++ b/src/persist.rs
@@ -1,69 +1,116 @@
use std::time::SystemTime;
use serde_json::Value;
-
use crate::secure;
+
const PERSIST_TIME: u64 = 60 * 3;
const PERSIST_PATH: &str = "/var/run/crab";
+
+/// Returns true or false if a user is currently persisted from
+/// a prior authentication. If the persist file had been tampered
+/// with, or not trusted, it will be ignored, and return false.
+/// #### Arguments
+/// * `user` - The user to check if is persisted
+/// #### Returns
+/// * `true` - If the user is persisted
+/// * `false` - If the user is not persisted, or if the persist file is not trusted
pub fn get_persist(user: &str) -> bool {
- let json = match get_terminal_config() {
- Some(data) => data,
- None => return false
- };
- let timestamp = match json[user].as_u64() {
- Some(data) => data,
- None => return false
- };
- return now() - timestamp < PERSIST_TIME && timestamp - 1 < now();
+ let json = match get_persist_config() {
+ Some(data) => data,
+ None => return false
+ };
+ let timestamp = match json[user].as_u64() {
+ Some(data) => data,
+ None => return false
+ };
+ return now() - timestamp < PERSIST_TIME && timestamp - 1 < now();
}
+
+/// Updates a user in the current sessions persist file
+/// #### Arguments
+/// * `user` - The user to set persisted
pub fn set_persist(user: &str) {
- let mut json = match get_terminal_config() {
- Some(data) => data,
- None => return
- };
- json[user] = Value::from(now());
- let id = match get_terminal_process() {
- Some(data) => data,
- None => return
- };
- match secure::write_file(PERSIST_PATH, &format!("{}", id), &json.to_string()) {
- Ok(_) => {},
- Err(e) => {
- eprintln!("Internal Error: {}", e)
- }
- };
+ let mut json = match get_persist_config() {
+ Some(data) => data,
+ None => return
+ };
+ json[user] = Value::from(now());
+ 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");
+ }
+ };
+}
+
+
+/// 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");
+ }
+ };
}
-fn get_terminal_process() -> Option<i32> {
- let id: i32 = match std::process::id().try_into() {
- Ok(data) => data,
- Err(_) => return None
- };
- let stat = match procinfo::pid::stat(id) {
- Ok(data) => data,
- Err(_) => return None
- };
- Some(stat.session)
+
+/// Gets the current session that crab is running in
+/// #### Returns
+/// * `None` - If crab failed to get the current session
+/// * `Some(i32)` - If the session is retrieved, returns the i32/pid_t session id
+fn get_current_session() -> Option<i32> {
+ let pid: i32 = match std::process::id().try_into() {
+ Ok(data) => data,
+ Err(_) => return None
+ };
+ let pid_stat = match procinfo::pid::stat(pid) {
+ Ok(data) => data,
+ Err(_) => return None
+ };
+ Some(pid_stat.session)
}
-fn get_terminal_config() -> Option<Value> {
- let id = match get_terminal_process() {
- Some(data) => data,
- None => return None
- };
- let data = match secure::read_file(PERSIST_PATH, &format!("{}", id)) {
- Some(data) => data,
- None => "{}".to_string()
- };
- let json: Value = match serde_json::from_str(&data) {
- Ok(data) => data,
- Err(_) => return None
- };
- Some(json)
+
+/// Gets the current persist file for the current session
+/// #### Returns
+/// * `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
+fn get_persist_config() -> Option<Value> {
+ let session = match get_current_session() {
+ Some(data) => data,
+ None => return None
+ };
+ let data = match secure::read_file(PERSIST_PATH, &format!("{}", session)) {
+ Some(data) => data,
+ None => "{}".to_string()
+ };
+ let json: Value = match serde_json::from_str(&data) {
+ Ok(data) => data,
+ Err(_) => return None
+ };
+ Some(json)
}
+
+// Gets the current time in seconds since the Unix Epoch
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();
}
diff --git a/src/secure.rs b/src/secure.rs
index 1fc3a11..7c85ece 100644
--- a/src/secure.rs
+++ b/src/secure.rs
@@ -1,51 +1,84 @@
-use std::{os::{unix::prelude::PermissionsExt, linux::fs::MetadataExt}, fs, io::ErrorKind};
-use nix::unistd;
+use std::{os::{unix::prelude::PermissionsExt, linux::fs::MetadataExt}, fs, io::{self, ErrorKind}, path::Path};
+use nix::unistd::{self, Uid, Gid};
-pub fn write_file(dir: &str, file: &str, data: &str) -> Result<(), Box<dyn std::error::Error>> {
- std::fs::create_dir_all(dir)?;
- make_file_root(dir)?;
- let path = path(dir, file);
- std::fs::write(&path, "")?;
- make_file_root(&path)?;
- std::fs::write(&path, data)?;
- Ok(())
+
+/// Writes a file securly to a specified path with given data
+/// #### Arguments
+/// * `dir` - The directory of the secure folder
+/// * `file` - The file name to write
+/// * `data` - The data to write
+/// #### Returns
+/// 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, 0o100600, dir)?;
+ let path = path(dir, file);
+ fs::write(&path, data)?;
+ set_file_permissions(0, 0, 0o100600, &path)?;
+ Ok(())
}
+
+/// Reads the file secutly to a specified path. If the file has
+/// been tampered (permissions have been changed), it ignores the
+/// file.
+/// #### Arguments
+/// * `dir` - The directory of the secure folder
+/// * `file` - The file name to write
+/// #### Returns
+/// * `None` - If the files doesnt exist or isnt trusted
+/// * `Some(String) - If the file is trusted, it returns the file's contents
pub fn read_file(dir: &str, file: &str) -> Option<String> {
- let path = path(dir,file);
- if !is_file_root(&path) {
- return None;
- }
- match std::fs::read_to_string(&path) {
- Ok(data) => return Some(data),
- Err(_) => return None
- };
+ let path = path(dir,file);
+ if !check_file_permissions(0, 0, 0o100600, &path) {
+ return None;
+ }
+ match fs::read_to_string(&path) {
+ Ok(data) => return Some(data),
+ Err(_) => return None
+ };
}
-fn make_file_root(path: &str) -> Result<(), Box<dyn std::error::Error>> {
- unistd::chown(std::path::Path::new(path), Some(unistd::Uid::from(0)), Some(unistd::Gid::from(0)))?;
- let metadata = std::fs::metadata(path)?;
- let mut perms = metadata.permissions();
- perms.set_mode(0o100600);
- fs::set_permissions(path, perms)?;
- Ok(())
-}
-fn is_file_root(path: &str) -> bool {
- return check_file_permissions(0, 0, 0o100600, path);
+/// Sets the permission for a secure file
+/// #### Arguments
+/// * `uid` - The user to own the file
+/// * `gid` - The group to own the file
+/// * `mode` - The mode permissions of the file
+/// * `path` - The path of the secure file
+/// #### Returns
+/// A ``io::Result<()>`` if the write succeded or failed
+fn set_file_permissions(uid: u32, gid: u32, mode: u32, path: &str) -> Result<(), io::Error> {
+ unistd::chown(Path::new(path), Some(Uid::from(uid)), Some(Gid::from(gid)))?;
+ let metadata = fs::metadata(path)?;
+ let mut perms = metadata.permissions();
+ perms.set_mode(mode);
+ fs::set_permissions(path, perms)?;
+ Ok(())
}
+
+/// Checks if the files permissions equals the given parameters
+/// #### Arguments
+/// * `uid` - The user to own the file
+/// * `gid` - The group to own the file
+/// * `mode` - The mode permissions of the file
+/// * `path` - The path of the secure file
+/// #### Returns
+/// True or false if the files permissions match
fn check_file_permissions(uid: u32, gid: u32, mode: u32, path: &str) -> bool {
- let metadata = match std::fs::metadata(path) {
- Ok(data) => data,
- Err(e) => {
- return e.kind() == ErrorKind::NotFound
- }
- };
- let perms = metadata.permissions();
- return perms.mode() == mode && metadata.st_uid() == uid && metadata.st_gid() == gid;
+ let metadata = match fs::metadata(path) {
+ Ok(data) => data,
+ Err(e) => {
+ return e.kind() == ErrorKind::NotFound;
+ }
+ };
+ let perms = metadata.permissions();
+ return perms.mode() == mode && metadata.st_uid() == uid && metadata.st_gid() == gid;
}
+
+/// Get the path of a file given a directory and file name
fn path(dir: &str, file: &str) -> String {
- return format!("{}/{}", dir, file);
-} \ No newline at end of file
+ return format!("{}/{}.persist", dir, file);
+}