From bd04eda61efde9b884d0094a64157a3d19c9c327 Mon Sep 17 00:00:00 2001 From: Hilko Bengen Date: Mon, 27 Nov 2023 00:24:30 +0100 Subject: [PATCH] Enable build on other (non-Linux) Unix systems This is probably going to be useful mostly for development and testing, not for production --- Cargo.toml | 4 +++- build.rs | 3 ++- src/bin/laurel/main.rs | 46 +++++++++++++++++++++++------------------- src/coalesce.rs | 34 +++++++++++++++++++++---------- src/lib.rs | 3 ++- src/proc.rs | 18 ++++++++--------- src/rotate.rs | 1 + 7 files changed, 65 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 51cb0a0..016fdc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ serde_json = { version = "1", features = ["preserve_order"] } indexmap = { version = "2", features = ["serde"] } toml = "0.5" nix = "0.26" -caps = "0.5" libc = "0.2" exacl = ">= 0.6" regex = "1" @@ -35,6 +34,9 @@ log = "0.4" simple_logger = ">= 1" syslog = "6" +[target.'cfg(target_os = "linux")'.dependencies] +caps = "0.5" + [build-dependencies] bindgen = ">= 0.60" diff --git a/build.rs b/build.rs index 5eaff70..8ae9386 100644 --- a/build.rs +++ b/build.rs @@ -129,7 +129,7 @@ fn main() -> Result<(), Box> { fs::write(dest_path, buf)?; - // sockaddr + #[cfg(target_os = "linux")] bindgen::Builder::default() .header("src/sockaddr.h") .rust_target(bindgen::RustTarget::Stable_1_47) @@ -143,6 +143,7 @@ fn main() -> Result<(), Box> { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=const.rs.in"); + #[cfg(target_os = "linux")] println!("cargo:rerun-if-changed=src/sockaddr.h"); println!("cargo:rerun-if-changed={}", msg_file); println!("cargo:rerun-if-changed={}", fields_file); diff --git a/src/bin/laurel/main.rs b/src/bin/laurel/main.rs index b397bad..440cb76 100644 --- a/src/bin/laurel/main.rs +++ b/src/bin/laurel/main.rs @@ -2,7 +2,6 @@ //! the Linux Audit daemon and reformats events as JSON lines. use getopts::Options; -use std::collections::HashSet; use std::env; use std::error::Error; use std::fs; @@ -18,10 +17,12 @@ use std::sync::{ }; use std::time::{Duration, SystemTime}; -use nix::unistd::{chown, execve, setresgid, setresuid, Uid, User}; +use nix::unistd::{chown, execve, Uid, User}; +#[cfg(target_os = "linux")] +use nix::unistd::{setresgid, setresuid}; -use caps::securebits::set_keepcaps; -use caps::{CapSet, Capability}; +#[cfg(target_os = "linux")] +use caps::{securebits::set_keepcaps, CapSet, Capability}; use serde::Serialize; @@ -56,27 +57,26 @@ impl AddAssign for Stats { /// - CAP_DAC_READ_SEARCH+CAP_SYS_PTRACE are required for accessing /// environment variables from arbitrary processes /// (/proc/$PID/environ). +#[cfg(target_os = "linux")] fn drop_privileges(runas_user: &User) -> Result<(), Box> { set_keepcaps(true)?; - let uid = runas_user.uid; let gid = runas_user.gid; - setresgid(gid, gid, gid).map_err(|e| format!("setresgid({}): {}", gid, e))?; setresuid(uid, uid, uid).map_err(|e| format!("setresuid({}): {}", uid, e))?; - let mut capabilities = HashSet::new(); #[cfg(feature = "procfs")] { + let mut capabilities = std::collections::HashSet::new(); capabilities.insert(Capability::CAP_SYS_PTRACE); capabilities.insert(Capability::CAP_DAC_READ_SEARCH); + caps::set(None, CapSet::Permitted, &capabilities) + .map_err(|e| format!("set permitted capabilities: {}", e))?; + caps::set(None, CapSet::Effective, &capabilities) + .map_err(|e| format!("set effective capabilities: {}", e))?; + caps::set(None, CapSet::Inheritable, &capabilities) + .map_err(|e| format!("set inheritable capabilities: {}", e))?; } - caps::set(None, CapSet::Permitted, &capabilities) - .map_err(|e| format!("set permitted capabilities: {}", e))?; - caps::set(None, CapSet::Effective, &capabilities) - .map_err(|e| format!("set effective capabilities: {}", e))?; - caps::set(None, CapSet::Inheritable, &capabilities) - .map_err(|e| format!("set inheritable capabilities: {}", e))?; set_keepcaps(false)?; Ok(()) @@ -270,10 +270,11 @@ fn run_app() -> Result<(), Box> { log::warn!("Not dropping privileges -- not running as root"); } else if runas_user.uid.is_root() { log::warn!("Not dropping privileges -- no user configured"); - } else if let Err(e) = drop_privileges(&runas_user) { - // Logged to syslog by caller - return Err(e); + } else { + #[cfg(target_os = "linux")] + drop_privileges(&runas_user)?; } + #[cfg(target_os = "linux")] if let Err(e) = caps::clear(None, CapSet::Ambient) { log::warn!("could not set ambient capabilities: {}", e); } @@ -359,11 +360,14 @@ fn run_app() -> Result<(), Box> { .map(|(k, v)| CString::new(format!("{}={}", k, v)).unwrap()) .collect(); - let mut capabilities = HashSet::new(); - capabilities.insert(Capability::CAP_SYS_PTRACE); - capabilities.insert(Capability::CAP_DAC_READ_SEARCH); - if let Err(e) = caps::set(None, CapSet::Ambient, &capabilities) { - log::warn!("could not set ambient capabilities: {}", e); + #[cfg(target_os = "linux")] + { + let mut capabilities = std::collections::HashSet::new(); + capabilities.insert(Capability::CAP_SYS_PTRACE); + capabilities.insert(Capability::CAP_DAC_READ_SEARCH); + if let Err(e) = caps::set(None, CapSet::Ambient, &capabilities) { + log::warn!("could not set ambient capabilities: {}", e); + } } execve(&argv[0], &argv, &env)?; } diff --git a/src/coalesce.rs b/src/coalesce.rs index 38cac30..baa4faf 100644 --- a/src/coalesce.rs +++ b/src/coalesce.rs @@ -10,8 +10,9 @@ use crate::constants::{msg_type::*, ARCH_NAMES, SYSCALL_NAMES}; use crate::label_matcher::LabelMatcher; use crate::parser::parse; use crate::proc::{ContainerInfo, ProcTable, Process, ProcessKey}; -#[cfg(feature = "procfs")] +#[cfg(all(feature = "procfs", target_os = "linux"))] use crate::procfs; +#[cfg(target_os = "linux")] use crate::sockaddr::SocketAddr; use crate::types::*; use crate::userdb::UserDB; @@ -97,6 +98,7 @@ const EXPIRE_DONE_TIMEOUT: u64 = 120_000; /// generate translation of SocketAddr enum to a format similar to /// what auditd log_format=ENRICHED produces +#[cfg(target_os = "linux")] fn translate_socketaddr(rv: &mut Record, sa: SocketAddr) -> Value { let f = SimpleKey::Literal("saddr_fam"); let m = match sa { @@ -245,7 +247,7 @@ fn translate_socketaddr(rv: &mut Record, sa: SocketAddr) -> Value { /// /// As an extra sanity check, exe is compared with normalized /// PATH.name. If they are equal, no script is returned. -#[cfg(feature = "procfs")] +#[cfg(all(feature = "procfs", target_os = "linux"))] fn path_script_name(path: &Record, pid: u32, cwd: &[u8], exe: &[u8]) -> Option { use std::{ ffi::OsStr, @@ -587,7 +589,7 @@ impl<'a> Coalesce<'a> { proc.key = pr.key; proc.parent = pr.parent; proc.labels = pr.labels.clone(); - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] { proc.container_info = pr.container_info.clone(); } @@ -606,7 +608,7 @@ impl<'a> Coalesce<'a> { .cloned(); proc.labels.extend(propagated_labels); } - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] if self.settings.enrich_container { if let Ok(Some(id)) = procfs::parse_proc_pid_cgroup(proc.pid) { proc.container_info = Some(ContainerInfo { id }); @@ -725,7 +727,7 @@ impl<'a> Coalesce<'a> { } // ENV - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] if let (Some(proc), false) = (¤t_process, self.settings.execve_env.is_empty()) { if let Ok(vars) = procfs::get_environ(proc.pid, |k| self.settings.execve_env.contains(k)) @@ -742,7 +744,7 @@ impl<'a> Coalesce<'a> { } // Handle script enrichment - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] let script: Option = match (self.settings.enrich_script, self.settings.label_script) { (false, None) => None, _ => match (¤t_process, ev.body.get(&PATH), syscall_is_exec) { @@ -766,7 +768,7 @@ impl<'a> Coalesce<'a> { }, }; - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] if let (Some(ref mut proc), Some(script)) = (&mut current_process, &script) { if let Some(label_script) = self.settings.label_script { for label in label_script.matches(script.as_ref()) { @@ -803,6 +805,7 @@ impl<'a> Coalesce<'a> { if let (Key::Name(name), Value::Str(vr, _)) = (k, v) { match name.as_ref() { b"saddr" if self.settings.translate_universal => { + #[cfg(target_os = "linux")] if let Ok(sa) = SocketAddr::parse(&rv.raw[vr.clone()]) { let kv = ( Key::Literal("SADDR"), @@ -936,7 +939,7 @@ impl<'a> Coalesce<'a> { sc.elems.push((Key::Literal("PPID"), Value::Map(m))); } - #[cfg(featuree = "procfs")] + #[cfg(all(featuree = "procfs", target_os = "linux"))] if let (true, Some(script)) = (self.settings.enrich_script, script) { let (k, v) = ( Key::Literal("SCRIPT"), @@ -970,7 +973,7 @@ impl<'a> Coalesce<'a> { sc.elems.push((Key::Literal("LABELS"), Value::List(labels))); } - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] if let (true, Some(c)) = (self.settings.enrich_container, &proc.container_info) { let mut ci = Record::default(); let r = ci.put(&c.id); @@ -1219,6 +1222,10 @@ mod test { &mut c, strip_enriched(include_bytes!("testdata/record-execve.txt")), )?; + let gid0name = nix::unistd::Group::from_gid(0.into()) + .unwrap() + .unwrap() + .name; let output = event_to_json(ec.borrow().last().unwrap()); println!("{}", output); assert!( @@ -1226,7 +1233,7 @@ mod test { "output contains translated UID" ); assert!( - output.contains(r#""EGID":"root","#), + output.contains(&format!(r#""EGID":"{gid0name}","#)), "output contains translated EGID" ); assert!( @@ -1277,6 +1284,11 @@ mod test { fn translate_uids() { let ec = Rc::new(RefCell::new(None)); + let gid0name = nix::unistd::Group::from_gid(0.into()) + .unwrap() + .unwrap() + .name; + let mut c = Coalesce::new(|e: &Event| *ec.borrow_mut() = Some(e.clone())); c.settings.translate_userdb = true; c.settings.translate_universal = true; @@ -1296,7 +1308,7 @@ mod test { } if k.to_string().ends_with("GID") { gids += 1; - assert!(&v == "root", "Got {}={:?}, expected root", k, v); + assert!(&v == gid0name.as_str(), "Got {}={:?}, expected root", k, v); } } assert!( diff --git a/src/lib.rs b/src/lib.rs index e11723f..4fc75ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,10 +5,11 @@ pub mod label_matcher; pub mod logger; pub mod parser; pub mod proc; -#[cfg(feature = "procfs")] +#[cfg(all(feature = "procfs", target_os = "linux"))] pub mod procfs; pub mod quoted_string; pub mod rotate; +#[cfg(target_os = "linux")] pub mod sockaddr; pub mod types; pub mod userdb; diff --git a/src/proc.rs b/src/proc.rs index 91f1a08..38b715d 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -12,7 +12,7 @@ use serde::{Serialize, Serializer}; use crate::label_matcher::LabelMatcher; use crate::types::EventID; -#[cfg(feature = "procfs")] +#[cfg(all(feature = "procfs", target_os = "linux"))] use crate::procfs; #[derive(Clone, Debug, Default)] @@ -104,11 +104,11 @@ pub struct Process { pub comm: Option>, /// Labels assigned to process pub labels: HashSet>, - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] pub container_info: Option, } -#[cfg(feature = "procfs")] +#[cfg(all(feature = "procfs", target_os = "linux"))] impl From for Process { fn from(p: procfs::ProcPidInfo) -> Self { Self { @@ -129,7 +129,7 @@ impl From for Process { impl Process { /// Generate a shadow process table entry from /proc/$PID for a given PID - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] pub fn parse_proc(pid: u32) -> Result> { procfs::parse_proc_pid(pid).map(|p| p.into()) } @@ -159,7 +159,7 @@ impl ProcTable { current: BTreeMap::new(), }; - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] { for pid in procfs::get_pids()? { // /proc/ access is racy. Ignore errors here. @@ -211,7 +211,7 @@ impl ProcTable { /// shadow process table, an attempt is made to fetch the /// information from another source, i.e. /proc. pub fn get_or_retrieve(&mut self, pid: u32) -> Option<&Process> { - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] if self.get_pid(pid).is_none() { self.insert_from_procfs(pid); } @@ -220,7 +220,7 @@ impl ProcTable { /// Fetch process information from procfs, insert into shadow /// process table. - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] pub fn insert_from_procfs(&mut self, pid: u32) -> Option<&Process> { if let Ok(p) = Process::parse_proc(pid) { let key = p.key; @@ -236,7 +236,7 @@ impl ProcTable { /// /// It should be possible to run this every few seconds without /// incurring load. - #[cfg(feature = "procfs")] + #[cfg(all(feature = "procfs", target_os = "linux"))] pub fn expire(&mut self) { use std::collections::BTreeSet; @@ -288,7 +288,7 @@ impl ProcTable { /// No expire mechanism has been implemented for the case where /// there's no procfs support. - #[cfg(not(feature = "procfs"))] + #[cfg(not(all(feature = "procfs", target_os = "linux")))] pub fn expire(&self) {} pub fn set_labels(&mut self, key: &ProcessKey, labels: &HashSet>) { diff --git a/src/rotate.rs b/src/rotate.rs index f120bf3..76c0a97 100644 --- a/src/rotate.rs +++ b/src/rotate.rs @@ -83,6 +83,7 @@ impl FileRotate { let mut acl = vec![ AclEntry::allow_user("", Perm::from_bits_truncate(6), None), AclEntry::allow_group("", Perm::from_bits_truncate(4), None), + #[cfg(any(target_os = "linux", target_os = "freebsd"))] AclEntry::allow_other(Perm::empty(), None), ]; for uid in &self.uids {