Compare commits

...

5 Commits

2 changed files with 61 additions and 32 deletions

View File

@ -7,9 +7,16 @@ specified by the administrator.
Originally written in C, it's now reimplemented in Rust.
By default, raou looks in /etc/raou.d/ for config files. If you run
"raou backup", it will look for /etc/raou.d/backup.
### When to use raou (over sudo)
Generally, it's not a replacement for sudo. The primary use case of raou is a situation in which you would want to allow a user to run a privileged operation as root without entering passwords. You may not want to use sudo for that, particularly if you don't have it installed already. Some further arguments for raou:
- Simpler config
- Less complexity, less attack surface
- Writte in a memory-safe language
### Config
By default, raou looks in ```/etc/raou.d/``` for config files. If you run
"raou backup", it will look for ```/etc/raou.d/backup```.
Example config file:
```
user john
@ -20,29 +27,29 @@ path /usr/local/bin/script.sh
**user** is the name of the user who you want to give permissions to
execute **path** as the **target_user**.
**path** must contain the absolute path.
**path** must contain the absolute path of the to be executed command.
Optional fields
---------------
**args**: If you want to leave out optional arguments (argv) to *path*,
simply don't include this. Otherwise, simply specify them
#### Optional fields
**args** (string): If you want to leave out optional arguments (argv) to *path*,
simply don't include this. Otherwise, specify them here.
```
...
args -v -ltr
```
**allow_args**: Allow arbitrary arguments, so:
**allow_args** (1 or 0, default 0): Allow arbitrary arguments, so:
```
raou backup /path
```
Will launch "path" as specified in the file for the backup entry with "/path" as argv[1] instead of the argument specified with "args" in the config file.
Will execute the command specified in **path** of the ```backup``` entry with "/path" as argv[1] instead of the argument specified with "args" in the config file.
**no_new_privs**: Defaults to 1. Processes launched with this option active
won't be able to gain more privileges, even when they call setuid programs.
**no_new_privs** (1 or 0, default 1): Processes launched with this option active
won't be able to gain more privileges, even when they call setuid programs. This can break some programs.
**env_vars**: A comma-separated list of environment variables to inherit
**env_vars** (string): A comma-separated list of environment variables to inherit
from the current environment. Everything else will be wiped (but others
like HOME, SHELL etc. will be appropriately set).
**argv0**: Set this option if you want to provide your own value as "argv0"
**argv0** (string): Set this option if you want to provide your own value as "argv0"
The default is the name of the launched binary (not the whole path).

View File

@ -90,8 +90,11 @@ fn getpwnam(username: &str) -> std::io::Result<Passwd> {
}
return Err(Error::new(
Error::last_os_error().kind(),
"Lookup of user failed: ".to_owned() +
&Error::last_os_error().to_string(),
format!(
"Lookup of user {} failed: {}",
username,
&Error::last_os_error().to_string()
),
));
}
unsafe {
@ -250,18 +253,32 @@ fn exec(entryname: &str, cmdargs: &Vec<String>) -> std::io::Result<()> {
let basedir: String = String::from("/etc/raou.d/");
let filepath: String = basedir.to_string() + entryname;
let realpath = fs::canonicalize(&filepath)?;
if !realpath.starts_with(basedir) {
return Err(std::io::Error::new(
ErrorKind::InvalidInput,
"Specified entry is outside base directory",
));
}
if !std::path::Path::new(&filepath).exists() {
return Err(std::io::Error::new(
ErrorKind::NotFound,
"The entry ".to_owned() + &filepath + " does not exist",
));
let realpath = fs::canonicalize(&filepath);
match realpath {
Ok(p) => {
if !p.starts_with(basedir) {
return Err(std::io::Error::new(
ErrorKind::InvalidInput,
"Specified entry is outside base directory",
));
}
if !p.is_file() {
return Err(std::io::Error::new(
ErrorKind::Other,
"Error: Entry not a file",
));
}
}
Err(e) => {
if e.kind() == ErrorKind::NotFound {
return Err(std::io::Error::new(
ErrorKind::NotFound,
format!("The entry {} does not exist", entryname),
));
} else {
return Err(e);
}
}
}
let entry: Entry = create_entry_from_file(&filepath)?;
let destuserpasswd: Passwd = getpwnam(&entry.dest_user)?;
@ -273,21 +290,21 @@ fn exec(entryname: &str, cmdargs: &Vec<String>) -> std::io::Result<()> {
become_user(&destuserpasswd).or_else(|e| {
return Err(Error::new(
ErrorKind::PermissionDenied,
"Failed to switch user: ".to_owned() + &e.to_string(),
format!("Failed to switch user: {}", &e.to_string()),
));
})?;
setup_environment(&destuserpasswd, &entry.inherit_envs)
.or_else(|e| {
return Err(Error::new(
ErrorKind::Other,
"Environment setup failure: ".to_owned() + &e.to_string(),
format!("Environment setup failure: {}", &e.to_string()),
));
})?;
drop_privs(&entry).or_else(|e| {
return Err(Error::new(
ErrorKind::Other,
"Failed to drop priviliges: ".to_owned() + &e.to_string(),
format!("Failed to drop priviliges: {}", &e.to_string()),
));
})?;
@ -296,7 +313,7 @@ fn exec(entryname: &str, cmdargs: &Vec<String>) -> std::io::Result<()> {
.or_else(|e| {
return Err(Error::new(
ErrorKind::Other,
"execv failed: ".to_owned() + &e.to_string(),
format!("execv failed: {}", &e.to_string()),
));
})?;
}
@ -307,7 +324,12 @@ fn main() -> Result<(), std::io::Error> {
let cmdargs: Vec<String> = argv.collect();
let entryname = cmdargs.get(1);
if entryname.is_some() {
match exec(&entryname.unwrap(), &cmdargs) {
let entry = entryname.unwrap();
if !entry.chars().all(|c| c.is_alphanumeric() || c == '.') {
eprintln!("Entry names can only contain alphanumeric characters and dots");
std::process::exit(1);
}
match exec(entry, &cmdargs) {
Err(e) => {
eprintln!("The following error ocurred:");
eprintln!("{}", e);