Comparar commits

...

3 Commits

Autor SHA1 Mensagem Data
Albert S. 659f7bd320 getpwnam: Give precise error message if we cannot lookup the user 2020-09-14 19:45:58 +02:00
Albert S. bb0b2886e9 Fix embarassing, basic path traversal attack
Fix the most embarassing kind of path traversal vulnerability
imaginable for such a tool.

You could simply run raou ../../../../tmp/evil_entry

The C version contained various check on the config dir and its
entries which would have prevented this attack. In this port,
the checking functions were deemed unnecessary, as they
did lots of redundant checks too. Unfortunately, I missed this
trivial attack when I decided not to port them.

At the plus side, I found this now myself while sleep-deprived, so
there may be some hope for me after all.

Also, you should not use some non-released software from some
guys git ;-)
2020-09-14 19:44:08 +02:00
Albert S. dce3d063f7 rustfmt 2020-09-14 19:19:20 +02:00
1 arquivos alterados com 46 adições e 23 exclusões

Ver arquivo

@ -5,6 +5,7 @@ use std::fs::File;
use std::io::BufRead; use std::io::BufRead;
use std::io::BufReader; use std::io::BufReader;
use std::io::{Error, ErrorKind}; use std::io::{Error, ErrorKind};
use std::fs;
extern crate libc; extern crate libc;
use libc::passwd; use libc::passwd;
@ -47,7 +48,9 @@ struct Passwd {
fn initgroups(user: &str, group: libc::gid_t) -> std::io::Result<()> { fn initgroups(user: &str, group: libc::gid_t) -> std::io::Result<()> {
let userarg = CString::new(user); let userarg = CString::new(user);
return errnowrapper(unsafe { libc::initgroups(userarg.unwrap().as_ptr(), group) }); return errnowrapper(unsafe {
libc::initgroups(userarg.unwrap().as_ptr(), group)
});
} }
fn errnowrapper(ret: libc::c_int) -> std::io::Result<()> { fn errnowrapper(ret: libc::c_int) -> std::io::Result<()> {
@ -75,12 +78,22 @@ fn getpwnam(username: &str) -> std::io::Result<Passwd> {
fn getstr(str: *mut libc::c_char) -> String { fn getstr(str: *mut libc::c_char) -> String {
unsafe { CStr::from_ptr(str).to_string_lossy().into_owned() } unsafe { CStr::from_ptr(str).to_string_lossy().into_owned() }
} }
let username_c = CString::new(username).unwrap(); let username_c = CString::new(username).unwrap();
let username_ptr = username_c.as_ptr(); let username_ptr = username_c.as_ptr();
let pwnamresult: *mut libc::passwd = unsafe { libc::getpwnam(username_ptr) }; let pwnamresult: *mut libc::passwd = unsafe { libc::getpwnam(username_ptr) };
if pwnamresult.is_null() { if pwnamresult.is_null() {
return Err(Error::new(Error::last_os_error().kind(),"Lookup of user failed: ".to_owned() + &Error::last_os_error().to_string())); if Error::last_os_error().raw_os_error().unwrap() == 0 {
return Err(Error::new(
ErrorKind::NotFound,
format!("The username '{}' was not found", username),
));
} }
return Err(Error::new(
Error::last_os_error().kind(),
"Lookup of user failed: ".to_owned() +
&Error::last_os_error().to_string(),
));
}
unsafe { unsafe {
Ok(Passwd { Ok(Passwd {
pw_name: getstr((*pwnamresult).pw_name), pw_name: getstr((*pwnamresult).pw_name),
@ -172,8 +185,7 @@ fn clearenv() -> std::io::Result<()> {
} }
//TODO: AsRef for envs? //TODO: AsRef for envs?
fn setup_environment(passwd: &Passwd, envs: &[String]) -> std::io::Result<()> { fn setup_environment(passwd: &Passwd, envs: &[String]) -> std::io::Result<()> {
let saved_envs: Vec<String> = envs let saved_envs: Vec<String> = envs.iter()
.iter()
.map(|s| std::env::var(s).expect("No such var")) .map(|s| std::env::var(s).expect("No such var"))
.collect(); .collect();
clearenv()?; clearenv()?;
@ -201,7 +213,9 @@ fn become_user(passwd: &Passwd) -> std::io::Result<()> {
fn drop_privs(entry: &Entry) -> std::io::Result<()> { fn drop_privs(entry: &Entry) -> std::io::Result<()> {
if entry.no_new_privs { if entry.no_new_privs {
errnowrapper(unsafe { libc::prctl(libc::PR_SET_DUMPABLE, 0) })?; errnowrapper(unsafe { libc::prctl(libc::PR_SET_DUMPABLE, 0) })?;
errnowrapper(unsafe { libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) })?; errnowrapper(unsafe {
libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
})?;
} }
Ok(()) Ok(())
} }
@ -233,9 +247,16 @@ fn create_execv_args(entry: &Entry, cmdargs: &Vec<String>) -> Vec<*const libc::c
return args; return args;
} }
fn exec(entryname: &str, cmdargs: &Vec<String>) -> std::io::Result<()> { fn exec(entryname: &str, cmdargs: &Vec<String>) -> std::io::Result<()> {
let mut filepath: String = String::from("/etc/raou.d/"); let basedir: String = String::from("/etc/raou.d/");
filepath = filepath + entryname; 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() { if !std::path::Path::new(&filepath).exists() {
return Err(std::io::Error::new( return Err(std::io::Error::new(
ErrorKind::NotFound, ErrorKind::NotFound,
@ -255,12 +276,13 @@ fn exec(entryname: &str, cmdargs: &Vec<String>) -> std::io::Result<()> {
"Failed to switch user: ".to_owned() + &e.to_string(), "Failed to switch user: ".to_owned() + &e.to_string(),
)); ));
})?; })?;
setup_environment(&destuserpasswd, &entry.inherit_envs).or_else(|e| { setup_environment(&destuserpasswd, &entry.inherit_envs)
return Err(Error::new( .or_else(|e| {
ErrorKind::Other, return Err(Error::new(
"Environment setup failure: ".to_owned() + &e.to_string(), ErrorKind::Other,
)); "Environment setup failure: ".to_owned() + &e.to_string(),
})?; ));
})?;
drop_privs(&entry).or_else(|e| { drop_privs(&entry).or_else(|e| {
return Err(Error::new( return Err(Error::new(
@ -270,12 +292,13 @@ fn exec(entryname: &str, cmdargs: &Vec<String>) -> std::io::Result<()> {
})?; })?;
unsafe { unsafe {
errnowrapper(libc::execv(to_cstring(entry.cmd), args.as_ptr())).or_else(|e| { errnowrapper(libc::execv(to_cstring(entry.cmd), args.as_ptr()))
return Err(Error::new( .or_else(|e| {
ErrorKind::Other, return Err(Error::new(
"execv failed: ".to_owned() + &e.to_string(), ErrorKind::Other,
)); "execv failed: ".to_owned() + &e.to_string(),
})?; ));
})?;
} }
std::process::exit(0); std::process::exit(0);
} }