From 5895012edff524d54800389d629f0addcb65ab05 Mon Sep 17 00:00:00 2001 From: Nathan Nye Date: Fri, 3 Sep 2021 18:06:54 -0400 Subject: [PATCH] Action arguments, WhiteBeam 0.2.4 --- .../common/hash/hashes/argon2id.rs | 2 +- .../common/action/actions/add_environment.rs | 2 +- .../common/action/actions/add_flags.rs | 2 +- .../action/actions/canonicalize_path.rs | 2 +- .../action/actions/combine_directory.rs | 2 +- .../common/action/actions/consume_variadic.rs | 2 +- .../action/actions/filter_environment.rs | 2 +- .../action/actions/open_file_descriptor.rs | 2 +- .../common/action/actions/print_arguments.rs | 2 +- .../action/actions/redirect_function.rs | 86 +------------------ .../common/action/actions/split_file_path.rs | 2 +- .../action/actions/verify_can_execute.rs | 2 +- .../action/actions/verify_can_terminate.rs | 2 +- .../common/action/actions/verify_can_write.rs | 2 +- .../common/action/actions/verify_file_hash.rs | 2 +- src/library/common/action/mod.rs | 14 +-- src/library/common/db.rs | 81 ++++++++++++++++- src/library/common/hash/hashes/argon2id.rs | 2 +- src/library/platforms/linux/mod.rs | 8 +- 19 files changed, 111 insertions(+), 108 deletions(-) diff --git a/src/application/common/hash/hashes/argon2id.rs b/src/application/common/hash/hashes/argon2id.rs index 0243647..fe32a95 100644 --- a/src/application/common/hash/hashes/argon2id.rs +++ b/src/application/common/hash/hashes/argon2id.rs @@ -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::(password.as_bytes(), &salt).unwrap().to_string() + argon2.hash_password::(password.as_bytes(), &salt).unwrap().to_string() }} diff --git a/src/library/common/action/actions/add_environment.rs b/src/library/common/action/actions/add_environment.rs index 1143896..65b2cb1 100644 --- a/src/library/common/action/actions/add_environment.rs +++ b/src/library/common/action/actions/add_environment.rs @@ -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"); } diff --git a/src/library/common/action/actions/add_flags.rs b/src/library/common/action/actions/add_flags.rs index 393b0e2..0cd9b1e 100644 --- a/src/library/common/action/actions/add_flags.rs +++ b/src/library/common/action/actions/add_flags.rs @@ -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; diff --git a/src/library/common/action/actions/canonicalize_path.rs b/src/library/common/action/actions/canonicalize_path.rs index 7506edf..5a0d1f6 100644 --- a/src/library/common/action/actions/canonicalize_path.rs +++ b/src/library/common/action/actions/canonicalize_path.rs @@ -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; diff --git a/src/library/common/action/actions/combine_directory.rs b/src/library/common/action/actions/combine_directory.rs index 4dc7698..1f79113 100644 --- a/src/library/common/action/actions/combine_directory.rs +++ b/src/library/common/action/actions/combine_directory.rs @@ -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(); diff --git a/src/library/common/action/actions/consume_variadic.rs b/src/library/common/action/actions/consume_variadic.rs index 0bcee01..765836a 100644 --- a/src/library/common/action/actions/consume_variadic.rs +++ b/src/library/common/action/actions/consume_variadic.rs @@ -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; diff --git a/src/library/common/action/actions/filter_environment.rs b/src/library/common/action/actions/filter_environment.rs index 43a4822..d06a820 100644 --- a/src/library/common/action/actions/filter_environment.rs +++ b/src/library/common/action/actions/filter_environment.rs @@ -6,7 +6,7 @@ fn get_restricted() -> Vec { ) } -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; diff --git a/src/library/common/action/actions/open_file_descriptor.rs b/src/library/common/action/actions/open_file_descriptor.rs index 11eeec1..682ff86 100644 --- a/src/library/common/action/actions/open_file_descriptor.rs +++ b/src/library/common/action/actions/open_file_descriptor.rs @@ -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; diff --git a/src/library/common/action/actions/print_arguments.rs b/src/library/common/action/actions/print_arguments.rs index 13fe1de..a56041d 100644 --- a/src/library/common/action/actions/print_arguments.rs +++ b/src/library/common/action/actions/print_arguments.rs @@ -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; diff --git a/src/library/common/action/actions/redirect_function.rs b/src/library/common/action/actions/redirect_function.rs index 2e7fa2c..8867854 100644 --- a/src/library/common/action/actions/redirect_function.rs +++ b/src/library/common/action/actions/redirect_function.rs @@ -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(); }} \ No newline at end of file diff --git a/src/library/common/action/actions/split_file_path.rs b/src/library/common/action/actions/split_file_path.rs index 04f26d2..0fb0bc5 100644 --- a/src/library/common/action/actions/split_file_path.rs +++ b/src/library/common/action/actions/split_file_path.rs @@ -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; diff --git a/src/library/common/action/actions/verify_can_execute.rs b/src/library/common/action/actions/verify_can_execute.rs index af06b43..499a405 100644 --- a/src/library/common/action/actions/verify_can_execute.rs +++ b/src/library/common/action/actions/verify_can_execute.rs @@ -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; diff --git a/src/library/common/action/actions/verify_can_terminate.rs b/src/library/common/action/actions/verify_can_terminate.rs index e73c167..8696b8c 100644 --- a/src/library/common/action/actions/verify_can_terminate.rs +++ b/src/library/common/action/actions/verify_can_terminate.rs @@ -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"); diff --git a/src/library/common/action/actions/verify_can_write.rs b/src/library/common/action/actions/verify_can_write.rs index 3bb59d8..3d95c55 100644 --- a/src/library/common/action/actions/verify_can_write.rs +++ b/src/library/common/action/actions/verify_can_write.rs @@ -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; diff --git a/src/library/common/action/actions/verify_file_hash.rs b/src/library/common/action/actions/verify_file_hash.rs index 932c7c3..3804656 100644 --- a/src/library/common/action/actions/verify_file_hash.rs +++ b/src/library/common/action/actions/verify_file_hash.rs @@ -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; diff --git a/src/library/common/action/mod.rs b/src/library/common/action/mod.rs index 6cd9dcb..bb2d34b 100644 --- a/src/library/common/action/mod.rs +++ b/src/library/common/action/mod.rs @@ -2,12 +2,12 @@ use crate::common::db; pub struct ActionObject { pub alias: &'static str, - pub function: fn(String, db::HookRow, i64, Vec, bool, isize) -> (db::HookRow, Vec, bool, isize) + pub function: fn(String, db::HookRow, i64, Vec, Vec, bool, isize) -> (db::HookRow, Vec, 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")] @@ -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, mut $do_return: bool, mut $return_value: isize) -> (crate::common::db::HookRow, Vec, bool, isize) { + pub fn $alias ($src_prog: String, mut $hook: crate::common::db::HookRow, $arg_id: i64, mut $args: Vec, $act_args: Vec, mut $do_return: bool, mut $return_value: isize) -> (crate::common::db::HookRow, Vec, bool, isize) { $body ($hook, $args, $do_return, $return_value) } @@ -32,7 +32,7 @@ macro_rules! build_action { } // Load action modules -pub mod actions { +mod actions { automod::dir!(pub "common/action/actions"); } @@ -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 = 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) } } diff --git a/src/library/common/db.rs b/src/library/common/db.rs index ffe5fb1..7960086 100644 --- a/src/library/common/db.rs +++ b/src/library/common/db.rs @@ -18,6 +18,7 @@ use rusqlite::{params, Connection, OpenFlags}; pub static HOOK_CACHE: SyncLazy>> = SyncLazy::new(|| Mutex::new(vec![])); pub static ARG_CACHE: SyncLazy>> = SyncLazy::new(|| Mutex::new(vec![])); pub static WL_CACHE: SyncLazy>> = SyncLazy::new(|| Mutex::new(vec![])); +pub static ACT_ARG_CACHE: SyncLazy>> = SyncLazy::new(|| Mutex::new(vec![])); pub static RULE_CACHE: SyncLazy>> = SyncLazy::new(|| Mutex::new(vec![])); // TODO: BTreemap for Settings? pub static SET_CACHE: SyncLazy>> = SyncLazy::new(|| Mutex::new(vec![])); @@ -52,10 +53,18 @@ pub struct WhitelistRow { pub value: String } +#[derive(Clone)] +pub struct ActionArgumentRow { + pub id: i64, + pub value: String, + pub next: Option +} + #[derive(Clone)] pub struct RuleRow { pub arg: i64, - pub action: String + pub action: String, + pub actionarg: Option } #[derive(Clone)] @@ -141,14 +150,32 @@ pub fn get_whitelist_view(conn: &Connection) -> Result, Box Result, Box> { + // TODO: Log errors + let mut result_vec: Vec = 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, Box> { // TODO: Log errors let mut result_vec: Vec = 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 { @@ -199,6 +226,14 @@ pub fn populate_cache() -> Result<(), Box> { 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()?; @@ -219,6 +254,7 @@ pub fn populate_cache() -> Result<(), Box> { } 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); @@ -226,6 +262,45 @@ pub fn get_setting(param: String) -> String { (&setting_row_cloned.value).to_owned() } +pub fn get_action_arguments(initial_id: i64) -> Vec { + // 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 = 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") } diff --git a/src/library/common/hash/hashes/argon2id.rs b/src/library/common/hash/hashes/argon2id.rs index c2c6996..e13bbfa 100644 --- a/src/library/common/hash/hashes/argon2id.rs +++ b/src/library/common/hash/hashes/argon2id.rs @@ -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::(password.as_bytes(), &salt).unwrap().to_string() + argon2.hash_password::(password.as_bytes(), &salt).unwrap().to_string() }} diff --git a/src/library/platforms/linux/mod.rs b/src/library/platforms/linux/mod.rs index f8d8af9..f4918d6 100644 --- a/src/library/platforms/linux/mod.rs +++ b/src/library/platforms/linux/mod.rs @@ -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