From 0a7e4286b342dae02c806d72d96845c3d8fde414 Mon Sep 17 00:00:00 2001 From: Avril Date: Sun, 17 Jul 2022 23:23:28 +0100 Subject: [PATCH] Special cased rules for ownership for now. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for rng's current commit: Blessing − 吉 --- src/plugin/searcher.rs | 105 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 13 deletions(-) diff --git a/src/plugin/searcher.rs b/src/plugin/searcher.rs index cfcb19b..b8f1621 100644 --- a/src/plugin/searcher.rs +++ b/src/plugin/searcher.rs @@ -47,6 +47,8 @@ pub mod locations { const ROOT_OWNED_ONLY = 1 << 4; /// Only qualify user-owned files (can be unioned with other `_OWNED_ONLY` constants) const USER_OWNED_ONLY = 1 << 5; + /// Owned by user or by root. + const USER_OR_ROOT_OWNED = Self::ROOT_OWNED_ONLY.bits | Self::USER_OWNED_ONLY.bits; /// Disqualify files that are accessible at all by anyone other than the current user (or root, which can accesss everything.) const USER_ACCESSIBLE_ONLY = 1 << 6; /// Disallow *all* untrusted files. @@ -124,10 +126,59 @@ pub mod locations { } set!($name => |_, _| _panic_with_msg()) }; - ($name:ident: $($func_body:tt)+) => { - set!($name => |path, filename| { $($func_body)+}) + /*FUCK THIS, I HATE ITERATON IN MACROS< WHY DOES IT HAVE TO BE SO RECURSIVELY LEAKILY RETARDED AAAA(@ compose ($f_name:ident, $f_file:ident) via $operator:tt $ignore:tt; $first:ident, $second:ident $($rest:tt)*) + => (table[Self::$first.bits() as usize]($f_name, $f_file) $operator set!(@ compose ($f_name, $f_file) via $operator &&; Self::$second.bits(), $($rest)*)); + (@ compose ($f_name:ident, $f_file:ident) $(via $operator:tt)?; $($first:ident)? $(,)?) + => ($(table[Self::$first.bits() as usize])?); + ($name:ident $(; use $operator:tt)? => $($other:ident),+) => { + set!($name => |path, filename| { + set!(@ compose (path, filename) via $($operator)? &&; $($other),+) + }); + }*/ + } + + /// In lookups, use process EUID instead of UID. + const USE_EFFECTIVE_UID: bool = true; + + /// Get UID or **E**UID. + #[inline(always)] + fn get_uid() -> u32 + { + extern "C" { + fn getuid() -> u32; // uid_t + //XXX: Should we use process's UID or EUID? It seems we should use EUID. + fn geteuid() -> u32; // uid_t + } + // SAFETY: These are effectively pure. + unsafe { + if E { + geteuid() + } else { + getuid() + } } } + + #[inline(always)] + fn check_uid(against: impl PartialEq) -> bool + { + //TODO: Allow these to be reset, somehow? Idk... Make them regular static atomics? + lazy_static! { + static ref USER_UID_CACHED: u32 = get_uid::(); + static ref USER_EUID_CACHED: u32 = get_uid::(); + } + + against == if CACHED { + if USE_EFFECTIVE_UID { + *USER_EUID_CACHED + } else { + *USER_UID_CACHED + } + } else { + get_uid::() + } + } + set!(NAME_MATCH as ! "This should already have been checked"); set!(NON_SYMLINK => |path: &Path, _| !path.is_symlink()); @@ -163,15 +214,10 @@ pub mod locations { }); set!(USER_OWNED_ONLY => |path, _| { use std::os::unix::fs::MetadataExt as _; - extern "C" { - fn getuid() -> u32; // uid_t - //XXX: Should we use process's UID or EUID? It seems we should use EUID. - fn geteuid() -> u32; // uid_t - } - // SAFETY: This is a pure function for all that matters. - let user_id = unsafe { geteuid() }; + + //let user_id = get_uid::(); path.metadata() - .map(move |meta| meta.uid() == user_id) + .map(move |meta| check_uid::(meta.uid())) .unwrap_or(false) }); set!(USER_ACCESSIBLE_ONLY => |path, _| { @@ -192,6 +238,19 @@ pub mod locations { .map(|meta| !meta.permissions().mode() & OTHER_ACCESSORS == 0) //XXX: Test this .unwrap_or(false) }); + set!(USER_OR_ROOT_OWNED => |path, _| { + use std::os::unix::fs::MetadataExt as _; + + //let user_id = get_uid::(); + path.metadata() + .map(move |meta| match meta.uid() { + 0 => true, + uid if check_uid::(uid) => true, + _ => false + }) + .unwrap_or(false) + }); + //set!(USER_OR_ROOT_OWNED; use || => USER_OWNED_ONLY, ROOT_OWNED_ONLY); //TODO: Composition of functions should be done via OR, how to insert all possible compositions into table? We get confusing error messages when we try inside `set!()` table } @@ -398,16 +457,36 @@ impl PathLookup return true; } + macro_rules! check { + ($name:ident) => { + if RULES.contains(Rule::$name) { + Rule::RULE_FUNCTION_CHECK_TABLE[Rule::$name.bits() as usize](path, filename) + } else { + true + } + }; + (try $($op:tt)? $name:ident) => { + if $($op)? check!($name) { + return false; + } + } + } + + // XXX: Ugh, for now, this shit must be checked independently. + check!(try ! USER_OR_ROOT_OWNED); + + // Check rest of rules in sequence. Hopefully this will be unrolled since the first `if` statement is all const-fn on a static constant. while bit > 0 { // SAFETY: We know `INACCESSIBLE` is the largest if RULES.contains(unsafe { Rule::from_bits_unchecked(bit) }) { - + if !Rule::RULE_FUNCTION_CHECK_TABLE[bit as usize](path, filename) { + return false; + } } bit >>= 1; } - - todo!("TODO: extra Untrusted lookup rules checked on `path` here.") + true } lazy_static! { static ref TRUSTED_EXT_REGEX_MAP: Vec =