Skip to content

Commit

Permalink
Action arguments, WhiteBeam 0.2.4
Browse files Browse the repository at this point in the history
  • Loading branch information
noproto committed Sep 3, 2021
1 parent aad4164 commit 5895012
Show file tree
Hide file tree
Showing 19 changed files with 111 additions and 108 deletions.
2 changes: 1 addition & 1 deletion src/application/common/hash/hashes/argon2id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ build_hash! { ARGON2ID (reader, salt_opt) {
// Argon2 with default params (Argon2id v19)
let argon2 = argon2::Argon2::default();
// Hash password to PHC string ($argon2id$v=19$...)
argon2.hash_password_simple::<String>(password.as_bytes(), &salt).unwrap().to_string()
argon2.hash_password::<String>(password.as_bytes(), &salt).unwrap().to_string()
}}
2 changes: 1 addition & 1 deletion src/library/common/action/actions/add_environment.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build_action! { AddEnvironment (_src_prog, hook, _arg_id, args, do_return, return_value) {
build_action! { AddEnvironment (_src_prog, hook, _arg_id, args, _act_args, do_return, return_value) {
if !((&hook.symbol).contains("exec") && (&hook.library).contains("libc.so")) {
unimplemented!("WhiteBeam: AddEnvironment action is unsupported outside of Execution hooks");
}
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/add_flags.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build_action! { AddFlags (_src_prog, hook, _arg_id, args, do_return, return_value) {
build_action! { AddFlags (_src_prog, hook, _arg_id, args, _act_args, do_return, return_value) {
let library: &str = &hook.library;
let library_basename: &str = library.rsplit('/').next().unwrap_or(library);
let symbol: &str = &hook.symbol;
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/canonicalize_path.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build_action! { CanonicalizePath (_src_prog, hook, arg_id, args, do_return, return_value) {
build_action! { CanonicalizePath (_src_prog, hook, arg_id, args, _act_args, do_return, return_value) {
let library: &str = &hook.library;
let library_basename: &str = library.rsplit('/').next().unwrap_or(library);
let symbol: &str = &hook.symbol;
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/combine_directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn normalize_path(path: &std::path::Path) -> std::path::PathBuf {
ret
}

build_action! { CombineDirectory (_src_prog, hook, arg_id, args, do_return, return_value) {
build_action! { CombineDirectory (_src_prog, hook, arg_id, args, _act_args, do_return, return_value) {
let dirfd_index = args.iter().position(|arg| arg.id == arg_id).expect("WhiteBeam: Lost track of environment");
let dirfd_argument: crate::common::db::ArgumentRow = args[dirfd_index].clone();
let path_argument: crate::common::db::ArgumentRow = args[dirfd_index+1].clone();
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/consume_variadic.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build_action! { ConsumeVariadic (_src_prog, hook, arg_id, args, do_return, return_value) {
build_action! { ConsumeVariadic (_src_prog, hook, arg_id, args, _act_args, do_return, return_value) {
let variadic_start = args.iter().position(|arg| arg.id == arg_id).expect("WhiteBeam: Lost track of environment");
let variadic_start_id: i64 = args[variadic_start].id;
let library: &str = &hook.library;
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/filter_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fn get_restricted() -> Vec<std::ffi::OsString> {
)
}

build_action! { FilterEnvironment (_src_prog, hook, arg_id, args, do_return, return_value) {
build_action! { FilterEnvironment (_src_prog, hook, arg_id, args, _act_args, do_return, return_value) {
// Enforce LD_AUDIT, LD_BIND_NOT, WB_PROG
// TODO: Avoid leaking memory (NB: this action is often called before execve on Linux)
let library: &str = &hook.library;
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/open_file_descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fn fail(library_basename: &str, symbol: &str) -> isize {
}
}

build_action! { OpenFileDescriptor (src_prog, hook, arg_id, args, do_return, return_value) {
build_action! { OpenFileDescriptor (src_prog, hook, arg_id, args, _act_args, do_return, return_value) {
// TODO: Refactor
// TODO: No O_CLOEXEC leads to inherited fd's in children
let library: &str = &hook.library;
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/print_arguments.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build_action! { PrintArguments (_src_prog, hook, _arg_id, args, do_return, return_value) {
build_action! { PrintArguments (_src_prog, hook, _arg_id, args, _act_args, do_return, return_value) {
// strace/l(a)trace-like functionality
// TODO: Refactor to use Display
let library: &str = &hook.library;
Expand Down
86 changes: 4 additions & 82 deletions src/library/common/action/actions/redirect_function.rs
Original file line number Diff line number Diff line change
@@ -1,83 +1,5 @@
#[allow(dead_code)]
fn build_library_path(library_basename: &str) -> String {
#[cfg(target_os = "linux")]
let lib_path = format!("/lib/{}-linux-gnu/{}", crate::common::db::get_setting(String::from("SystemArchitecture")), library_basename);
#[cfg(not(target_os = "linux"))]
unimplemented!("WhiteBeam: This platform is not supported by the RedirectFunction action");
lib_path
}

pub fn get_redirected_function(library: &str, symbol: &str) -> (String, String) {
// Use respective 64 bit functions? ftruncate64, openat64, __openat64_2
let library_basename: &str = library.rsplit('/').next().unwrap_or(library);
match (library_basename, symbol) {
// Execution
("libc.so.6", "execl") |
("libc.so.6", "execle") |
("libc.so.6", "execlp") |
("libc.so.6", "execv") |
("libc.so.6", "execvp") |
("libc.so.6", "execvpe") => {
(String::from(library), String::from("execve"))
},
// Filesystem
("libc.so.6", "truncate") |
("libc.so.6", "truncate64") => {
(String::from(library), String::from("ftruncate"))
},
("libc.so.6", "fopen") |
("libc.so.6", "fopen64") => {
(String::from(library), String::from("fdopen"))
},
("libc.so.6", "symlink") => {
(String::from(library), String::from("symlinkat"))
},
("libc.so.6", "mkdir") => {
(String::from(library), String::from("mkdirat"))
},
("libc.so.6", "unlink") |
("libc.so.6", "rmdir") => {
(String::from(library), String::from("unlinkat"))
},
("libc.so.6", "link") => {
(String::from(library), String::from("linkat"))
},
("libc.so.6", "rename") => {
(String::from(library), String::from("renameat"))
},
("libc.so.6", "chown") |
("libc.so.6", "lchown") => {
(String::from(library), String::from("fchownat"))
},
("libc.so.6", "chmod") => {
(String::from(library), String::from("fchmodat"))
},
("libc.so.6", "creat") |
("libc.so.6", "open") |
("libc.so.6", "creat64") |
("libc.so.6", "open64") => {
(String::from(library), String::from("openat"))
},
("libc.so.6", "mknod") => {
(String::from(library), String::from("mknodat"))
},
("libc.so.6", "__open") |
("libc.so.6", "__open_2") |
("libc.so.6", "__open64") |
("libc.so.6", "__open64_2") => {
(String::from(library), String::from("__openat_2"))
},
("libc.so.6", "__xmknod") => {
(String::from(library), String::from("__xmknodat"))
},
_ => (String::from(library), String::from(symbol))
}
}

build_action! { RedirectFunction (_src_prog, hook, _arg_id, args, do_return, return_value) {
let library: &str = &hook.library;
let symbol: &str = &hook.symbol;
let redirected_function = get_redirected_function(library, symbol);
hook.library = redirected_function.0;
hook.symbol = redirected_function.1;
build_action! { RedirectFunction (_src_prog, hook, _arg_id, args, act_args, do_return, return_value) {
assert!(act_args.len() == 2);
hook.library = act_args[0].clone();
hook.symbol = act_args[1].clone();
}}
2 changes: 1 addition & 1 deletion src/library/common/action/actions/split_file_path.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build_action! { SplitFilePath (_src_prog, hook, arg_id, args, do_return, return_value) {
build_action! { SplitFilePath (_src_prog, hook, arg_id, args, _act_args, do_return, return_value) {
let path_index = args.iter().position(|arg| arg.id == arg_id).expect("WhiteBeam: Lost track of environment");
let path_argument: crate::common::db::ArgumentRow = args[path_index].clone();
let path_value = path_argument.real as *const libc::c_char;
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/verify_can_execute.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build_action! { VerifyCanExecute (src_prog, hook, arg_id, args, do_return, return_value) {
build_action! { VerifyCanExecute (src_prog, hook, arg_id, args, _act_args, do_return, return_value) {
// TODO: Depending on LogVerbosity, log all use of this action
// TODO: Use OsString?
let library: &str = &hook.library;
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/verify_can_terminate.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build_action! { VerifyCanTerminate (src_prog, hook, arg_id, args, do_return, return_value) {
build_action! { VerifyCanTerminate (src_prog, hook, arg_id, args, _act_args, do_return, return_value) {
#[cfg(feature = "whitelist_test")]
return (hook, args, do_return, return_value);
let pid_index = args.iter().position(|arg| arg.id == arg_id).expect("WhiteBeam: Lost track of environment");
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/verify_can_write.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build_action! { VerifyCanWrite (src_prog, hook, arg_id, args, do_return, return_value) {
build_action! { VerifyCanWrite (src_prog, hook, arg_id, args, _act_args, do_return, return_value) {
let directory_index = args.iter().position(|arg| arg.id == arg_id).expect("WhiteBeam: Lost track of environment");
let directory_argument: crate::common::db::ArgumentRow = args[directory_index].clone();
let library: &str = &hook.library;
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/action/actions/verify_file_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn fail(library_basename: &str, symbol: &str, argument_path: &str) {
}
}

build_action! { VerifyFileHash (src_prog, hook, arg_id, args, do_return, return_value) {
build_action! { VerifyFileHash (src_prog, hook, arg_id, args, _act_args, do_return, return_value) {
// TODO: Depending on LogVerbosity, log all use of this action
// NB: For Execution hooks, system executables that aren't read world may be whitelisted as ANY
let library: &str = &hook.library;
Expand Down
14 changes: 9 additions & 5 deletions src/library/common/action/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ use crate::common::db;

pub struct ActionObject {
pub alias: &'static str,
pub function: fn(String, db::HookRow, i64, Vec<db::ArgumentRow>, bool, isize) -> (db::HookRow, Vec<crate::common::db::ArgumentRow>, bool, isize)
pub function: fn(String, db::HookRow, i64, Vec<db::ArgumentRow>, Vec<String>, bool, isize) -> (db::HookRow, Vec<crate::common::db::ArgumentRow>, bool, isize)
}

// Action template
macro_rules! build_action {
($alias:ident ($src_prog:ident, $hook:ident, $arg_id:ident, $args:ident, $do_return:ident, $return_value:ident) $body:block) => {
($alias:ident ($src_prog:ident, $hook:ident, $arg_id:ident, $args:ident, $act_args:ident, $do_return:ident, $return_value:ident) $body:block) => {
#[allow(unused_imports)]
use crate::common::event;
#[cfg(target_os = "windows")]
Expand All @@ -22,7 +22,7 @@ macro_rules! build_action {
#[allow(non_snake_case)]
#[allow(unused_assignments)]
#[allow(unused_mut)]
pub fn $alias ($src_prog: String, mut $hook: crate::common::db::HookRow, $arg_id: i64, mut $args: Vec<crate::common::db::ArgumentRow>, mut $do_return: bool, mut $return_value: isize) -> (crate::common::db::HookRow, Vec<crate::common::db::ArgumentRow>, bool, isize) {
pub fn $alias ($src_prog: String, mut $hook: crate::common::db::HookRow, $arg_id: i64, mut $args: Vec<crate::common::db::ArgumentRow>, $act_args: Vec<String>, mut $do_return: bool, mut $return_value: isize) -> (crate::common::db::HookRow, Vec<crate::common::db::ArgumentRow>, bool, isize) {
$body
($hook, $args, $do_return, $return_value)
}
Expand All @@ -32,7 +32,7 @@ macro_rules! build_action {
}

// Load action modules
pub mod actions {
mod actions {
automod::dir!(pub "common/action/actions");
}

Expand All @@ -45,8 +45,12 @@ pub fn process_action(src_prog: String, rule: db::RuleRow, hook: db::HookRow, ar
let arg_id: i64 = rule.arg;
let do_return = false;
let return_value = 0 as isize;
let act_args: Vec<String> = match rule.actionarg {
Some(id) => db::get_action_arguments(id),
None => vec![]
};
match ACTION_INDEX.iter().find(|a| a.alias == action) {
Some(action) => {(action.function)(src_prog, hook, arg_id, args, do_return, return_value)}
Some(action) => {(action.function)(src_prog, hook, arg_id, args, act_args, do_return, return_value)}
None => panic!("WhiteBeam: Invalid action: {}", action)
}
}
Expand Down
81 changes: 78 additions & 3 deletions src/library/common/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use rusqlite::{params, Connection, OpenFlags};
pub static HOOK_CACHE: SyncLazy<Mutex<Vec<HookRow>>> = SyncLazy::new(|| Mutex::new(vec![]));
pub static ARG_CACHE: SyncLazy<Mutex<Vec<ArgumentRow>>> = SyncLazy::new(|| Mutex::new(vec![]));
pub static WL_CACHE: SyncLazy<Mutex<Vec<WhitelistRow>>> = SyncLazy::new(|| Mutex::new(vec![]));
pub static ACT_ARG_CACHE: SyncLazy<Mutex<Vec<ActionArgumentRow>>> = SyncLazy::new(|| Mutex::new(vec![]));
pub static RULE_CACHE: SyncLazy<Mutex<Vec<RuleRow>>> = SyncLazy::new(|| Mutex::new(vec![]));
// TODO: BTreemap for Settings?
pub static SET_CACHE: SyncLazy<Mutex<Vec<SettingRow>>> = SyncLazy::new(|| Mutex::new(vec![]));
Expand Down Expand Up @@ -52,10 +53,18 @@ pub struct WhitelistRow {
pub value: String
}

#[derive(Clone)]
pub struct ActionArgumentRow {
pub id: i64,
pub value: String,
pub next: Option<i64>
}

#[derive(Clone)]
pub struct RuleRow {
pub arg: i64,
pub action: String
pub action: String,
pub actionarg: Option<i64>
}

#[derive(Clone)]
Expand Down Expand Up @@ -141,14 +150,32 @@ pub fn get_whitelist_view(conn: &Connection) -> Result<Vec<WhitelistRow>, Box<dy
Ok(result_vec)
}

pub fn get_action_argument_table(conn: &Connection) -> Result<Vec<ActionArgumentRow>, Box<dyn Error>> {
// TODO: Log errors
let mut result_vec: Vec<ActionArgumentRow> = Vec::new();
let mut stmt = conn.prepare("SELECT id, value, next FROM ActionArgument")?;
let result_iter = stmt.query_map(params![], |row| {
Ok(ActionArgumentRow {
id: row.get(0)?,
value: row.get(1)?,
next: row.get(2)?
})
})?;
for result in result_iter {
result_vec.push(result?);
}
Ok(result_vec)
}

pub fn get_rule_view(conn: &Connection) -> Result<Vec<RuleRow>, Box<dyn Error>> {
// TODO: Log errors
let mut result_vec: Vec<RuleRow> = Vec::new();
let mut stmt = conn.prepare("SELECT arg, action FROM RuleView")?;
let mut stmt = conn.prepare("SELECT arg, action, actionarg FROM RuleView")?;
let result_iter = stmt.query_map(params![], |row| {
Ok(RuleRow {
arg: row.get(0)?,
action: row.get(1)?
action: row.get(1)?,
actionarg: row.get(2)?
})
})?;
for result in result_iter {
Expand Down Expand Up @@ -199,6 +226,14 @@ pub fn populate_cache() -> Result<(), Box<dyn Error>> {
wl_cache_lock.push(row);
}
};
// Action argument cache
{
let mut act_arg_cache_lock = ACT_ARG_CACHE.lock()?;
act_arg_cache_lock.clear();
for row in get_action_argument_table(&conn)? {
act_arg_cache_lock.push(row);
}
};
// Rule cache
{
let mut rule_cache_lock = RULE_CACHE.lock()?;
Expand All @@ -219,13 +254,53 @@ pub fn populate_cache() -> Result<(), Box<dyn Error>> {
}

pub fn get_setting(param: String) -> String {
// TODO: Improve performance
// TODO: Log errors
let set_cache_lock = SET_CACHE.lock().expect("WhiteBeam: Failed to lock mutex");
let setting_option: Option<&SettingRow> = set_cache_lock.iter().find(|setting| setting.param == param);
let setting_row_cloned: SettingRow = setting_option.expect("WhiteBeam: Lost track of environment").clone();
(&setting_row_cloned.value).to_owned()
}

pub fn get_action_arguments(initial_id: i64) -> Vec<String> {
// TODO: Improve performance
// TODO: Log errors
let act_arg_cache_lock = ACT_ARG_CACHE.lock().expect("WhiteBeam: Failed to lock mutex");
let mut current_id: i64 = initial_id;
let mut result_vec: Vec<String> = Vec::new();
loop {
match act_arg_cache_lock.iter().find(|actarg| actarg.id == current_id) {
Some(act_arg) => {
result_vec.push(act_arg.value.clone());
match act_arg.next {
Some(next_arg) => { current_id = next_arg; }
None => break
}
},
None => break
}
}
result_vec
}

pub fn get_redirect(hook_id: i64) -> Option<(String, String)> {
let arg_id: i64 = {
let arg_cache_lock = ARG_CACHE.lock().expect("WhiteBeam: Failed to lock mutex");
// TODO: Zero argument case
arg_cache_lock.iter().find(|arg| (arg.hook == hook_id) && (arg.parent == None) && (arg.position == 0)).expect("WhiteBeam: Lost track of environment").id
};
let act_arg_id = {
let rule_cache_lock = RULE_CACHE.lock().expect("WhiteBeam: Failed to lock mutex");
match rule_cache_lock.iter().find(|rule| (rule.arg == arg_id) && (rule.action == "RedirectFunction") && (rule.actionarg.is_some())) {
Some(rule) => rule.actionarg.expect("WhiteBeam: Lost track of environment"),
None => { return None }
}
};
let redirected_function = get_action_arguments(act_arg_id);
assert!(redirected_function.len() == 2);
Some((redirected_function[0].clone(), redirected_function[1].clone()))
}

pub fn get_prevention() -> bool {
get_setting(String::from("Prevention")) == String::from("true")
}
Expand Down
2 changes: 1 addition & 1 deletion src/library/common/hash/hashes/argon2id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ build_hash! { ARGON2ID (reader, salt_opt) {
// Argon2 with default params (Argon2id v19)
let argon2 = argon2::Argon2::default();
// Hash password to PHC string ($argon2id$v=19$...)
argon2.hash_password_simple::<String>(password.as_bytes(), &salt).unwrap().to_string()
argon2.hash_password::<String>(password.as_bytes(), &salt).unwrap().to_string()
}}
8 changes: 5 additions & 3 deletions src/library/platforms/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,15 +273,17 @@ unsafe extern "C" fn la_symbind64(sym: *const libc::Elf64_Sym, _ndx: libc::c_uin
}
{
let hook_cache_lock = db::HOOK_CACHE.lock().expect("WhiteBeam: Failed to lock mutex");
// TODO: Use .find() instead
let hook_cache_iter = hook_cache_lock.iter();
for hook in hook_cache_iter {
if (hook.symbol == symbol_str) && (hook.library == library_path_str) {
//libc::printf("WhiteBeam hook: %s\n\0".as_ptr() as *const libc::c_char, symname);
{
// TODO: Move symbol resolution to the generic hook
// Get some information ahead of time of what the redirected symbol/library will be
let redirected_function = crate::common::action::actions::redirect_function::get_redirected_function(&hook.library, &hook.symbol);
let addr = resolve_symbol(&redirected_function.0, &redirected_function.1);
let addr = match db::get_redirect(hook.id) {
Some(redirected_function) => { resolve_symbol(&redirected_function.0, &redirected_function.1) },
None => resolve_symbol(&hook.library, &hook.symbol)
};
crate::common::hook::FN_STACK.lock().unwrap().push((hook.id, addr as usize));
};
return crate::common::hook::generic_hook as usize
Expand Down

0 comments on commit 5895012

Please sign in to comment.