rudo/src/main.rs

169 lines
4.9 KiB
Rust

use std::{env, os::unix::process::CommandExt, process::Command};
mod app;
use app::App;
use cache::*;
mod cache;
mod rudoers;
mod user_info;
use libc::{close, getpwuid};
use rudoers::check_rudoers;
use users::{get_group_by_name, get_user_by_name};
mod authentication;
use crate::rudoers::make_example_rudoers_file;
use authentication::check_auth;
use std::ffi::CStr;
use user_info::get_command_path;
/// RUDOLPH THE RED NOSE REINDEER
/// HAD A VERY SHINY NOSE
/// AND IF YOU EVER SAW IT
/// YOU MIGHT EVEN SAY IT GLOWS
const VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn execute(app: App) -> Result<(), String> {
if !app.cmd.is_empty() && !app.shell {
if let Some(close_from) = app.close_from {
for i in close_from.. {
let res = unsafe { close(i) };
if res == -1 {
break;
}
}
}
let run_as_name = if let Some(nym) = app.user {
nym
} else {
String::from("root")
};
let run_as = get_user_by_name(&run_as_name).unwrap();
let command_path = get_command_path(app.cmd.first().unwrap())?;
let args = app.cmd;
let args = &args[1..];
let mut cmd = Command::new(command_path);
cmd.uid(run_as.uid()).args(args);
if let Some(chdir) = app.chdir {
cmd.current_dir(chdir);
}
if let Some(group) = app.group {
let group_id: u32 = if group.starts_with("#") {
if let Ok(id) = group[1..].parse() {
id
} else {
return Err(format!["Error: Failed to parse group ID '{}'", &group[1..]]);
}
} else {
if let Some(group) = get_group_by_name(&group) {
group.gid()
} else {
return Err(format!["Error: Group '{}' not found", group]);
}
};
cmd.gid(group_id);
}
for var in app.preserve_env {
let value = env::var(&var).unwrap_or_default();
cmd.env(var, value);
}
// if app.preserve_groups {
// if let Some(user) = get_user_by_name(&get_username()?) {}
// }
if app.set_home {
let ptr = unsafe { (*getpwuid(run_as.uid())).pw_dir };
let home_dir = match unsafe { CStr::from_ptr(ptr).to_str() } {
Ok(s) => s,
Err(e) => {
return Err(format![
"Error: Failed to read home dir for {run_as_name}: {e}"
]);
}
};
cmd.env("HOME", home_dir);
}
// execute the child
let mut child = if let Ok(child) = cmd.spawn() {
child
} else {
return Err(format![
"Error: {}: Command not found.",
args.first().unwrap()
]);
};
if app.background {
std::process::exit(0);
} else {
child.wait().unwrap();
}
} else {
return Err(String::from("Error: No command specified."));
}
Ok(())
}
pub fn run() -> Result<(), String> {
let app = App::parse()?;
if app.help {
println!["{}", App::usage()];
return Ok(());
}
if app.version {
println!["Rudo v{VERSION}"];
return Ok(());
}
if app.gen_config {
println!["{}", make_example_rudoers_file()];
return Ok(());
}
let alias = match &app.user {
Some(x) => &x,
None => "root",
};
let parent_id = std::os::unix::process::parent_id();
let needs_password =
check_rudoers(&app)? && !check_auth_cache(alias, parent_id) || app.validate;
if needs_password {
check_auth(app.stdin)?;
}
let failed_to_update = String::from("Error: Rudo: Failed to update auth cache: ");
if !app.no_update && !app.remove_timestamp {
if let Err(e) = update_auth_cache(alias, parent_id) {
return Err(format!["{failed_to_update}{e}"]);
}
}
if app.validate {
return Ok(());
}
if app.remove_timestamp {
if let Err(e) = clear_auth_cache(alias, parent_id) {
return Err(format!["{failed_to_update}{e}"]);
}
if app.cmd.is_empty() {
return Ok(());
}
}
if app.reset_timestamp {
if let Err(e) = clear_auth_cache(alias, parent_id) {
return Err(format!["{failed_to_update}{e}"]);
}
if let Err(e) = update_auth_cache(alias, parent_id) {
return Err(format!["{failed_to_update}{e}"]);
}
if app.cmd.is_empty() {
return Ok(());
}
}
// Run the command
execute(app)?;
Ok(())
}
fn main() {
match run() {
Ok(()) => (),
Err(e) => {
eprintln!["{e}"];
std::process::exit(1);
}
}
}