From 456346845f7851eef779a555493fc808fa639d5c Mon Sep 17 00:00:00 2001 From: luytan Date: Mon, 3 Aug 2026 09:40:53 +0200 Subject: [PATCH 1/2] feat(cardwire-ebpf): fallback to weakened state if sys_dents fails --- crates/cardwire-ebpf-userspace/src/lib.rs | 85 ++++++++++++++++++----- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/crates/cardwire-ebpf-userspace/src/lib.rs b/crates/cardwire-ebpf-userspace/src/lib.rs index 8e7ffe01..33c2ba00 100644 --- a/crates/cardwire-ebpf-userspace/src/lib.rs +++ b/crates/cardwire-ebpf-userspace/src/lib.rs @@ -1,12 +1,14 @@ //! main lib code of cardwire-ebpf mod errors; +use std::{fs, path::Path}; + pub use crate::errors::{CardwireEbpfError, CardwireEbpfResult}; use aya::{ Btf, Ebpf, maps::{Array, HashMap, MapError, RingBuf}, programs::{Lsm, TracePoint} }; use aya_log::EbpfLogger; -use log::{Log, error, info}; +use log::{Log, error, info, warn}; use tokio::io::{Interest, unix::AsyncFd}; pub enum EbpfSettings { @@ -62,20 +64,14 @@ impl EbpfBlocker { close_program .attach("sched", "sched_process_exit") .map_err(CardwireEbpfError::aya)?; - // to hide files - let cardwire_sys_enter_getdents64: &mut TracePoint = ebpf - .program_mut("tracepoint_enter_getdents64") - .ok_or_else(|| CardwireEbpfError::missing_lsm("tracepoint_enter_getdents64"))? - .try_into() - .map_err(CardwireEbpfError::aya)?; - cardwire_sys_enter_getdents64 - .load() - .map_err(CardwireEbpfError::aya)?; + /* + This part can get rejected by the kernel if the lockdown is enabled, we warn but we do not exit carwired, it will just run in a weakened state + sys_exit_getdents64 re-write userspace memory to hide an entry (file/folder), it can be rejected + */ + + let mut did_sys_exit_getdents64_success = false; - cardwire_sys_enter_getdents64 - .attach("syscalls", "sys_enter_getdents64") - .map_err(CardwireEbpfError::aya)?; // to hide files let cardwire_sys_exit_getdents64: &mut TracePoint = ebpf .program_mut("tracepoint_exit_getdents64") @@ -83,13 +79,57 @@ impl EbpfBlocker { .try_into() .map_err(CardwireEbpfError::aya)?; - cardwire_sys_exit_getdents64 + // Try to load the program, if success attach it, else just warn the user + match cardwire_sys_exit_getdents64 .load() + .map_err(CardwireEbpfError::aya) + { + Ok(_) => { + did_sys_exit_getdents64_success = true; + cardwire_sys_exit_getdents64 + .attach("syscalls", "sys_exit_getdents64") + .map_err(CardwireEbpfError::aya)?; + } + Err(err) => { + // If we cannot load the program, it usually mean the kernel lockdown is enabled + let lockdown = is_lockdown_enabled(); + warn!( + "Failed to load sys_exit_getdents64. Lockdown status: {}", + lockdown + ); + warn!("{}", err); + warn!("falling back to a weakened cardwired..."); + } + }; + // to hide files + let cardwire_sys_enter_getdents64: &mut TracePoint = ebpf + .program_mut("tracepoint_enter_getdents64") + .ok_or_else(|| CardwireEbpfError::missing_lsm("tracepoint_enter_getdents64"))? + .try_into() .map_err(CardwireEbpfError::aya)?; - cardwire_sys_exit_getdents64 - .attach("syscalls", "sys_exit_getdents64") - .map_err(CardwireEbpfError::aya)?; + if did_sys_exit_getdents64_success { + match cardwire_sys_enter_getdents64 + .load() + .map_err(CardwireEbpfError::aya) + { + Ok(_) => { + cardwire_sys_enter_getdents64 + .attach("syscalls", "sys_enter_getdents64") + .map_err(CardwireEbpfError::aya)?; + } + Err(err) => { + let lockdown = is_lockdown_enabled(); + warn!( + "Failed to load sys_enter_getdents64. Lockdown status: {}", + lockdown + ); + warn!("{}", err); + warn!("falling back to a weakened cardwired..."); + } + }; + } + // Try to load the program, if success attach it, else just warn the user Ok(Self { ebpf }) } @@ -370,6 +410,17 @@ impl EbpfBlocker { } } +fn is_lockdown_enabled() -> bool { + let path = Path::new("/sys/kernel/security/lockdown"); + if let Ok(entry) = fs::read_to_string(path) + && (entry.contains("[integrity]") || entry.contains("[confidentiality]")) + { + return true; + } + + false +} + #[cfg(test)] mod tests { use super::*; From 26ca08cbcfc2bdd48f3d0e0bf13e9d8f91502571 Mon Sep 17 00:00:00 2001 From: luytan Date: Mon, 3 Aug 2026 21:53:15 +0200 Subject: [PATCH 2/2] refactor(cardwire-ebpf): clean up code and comments --- crates/cardwire-ebpf-userspace/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/cardwire-ebpf-userspace/src/lib.rs b/crates/cardwire-ebpf-userspace/src/lib.rs index 33c2ba00..07d603d9 100644 --- a/crates/cardwire-ebpf-userspace/src/lib.rs +++ b/crates/cardwire-ebpf-userspace/src/lib.rs @@ -34,8 +34,8 @@ impl EbpfBlocker { let btf = Btf::from_sys_fs().map_err(CardwireEbpfError::aya)?; - let load_list: [&str; 3] = ["file_open", "inode_permission", "inode_getattr"]; - for entity in load_list { + let lsm_load_list: [&str; 3] = ["file_open", "inode_permission", "inode_getattr"]; + for entity in lsm_load_list { let program: &mut Lsm = ebpf .program_mut(entity) .ok_or_else(|| CardwireEbpfError::missing_lsm(entity))? @@ -68,18 +68,18 @@ impl EbpfBlocker { /* This part can get rejected by the kernel if the lockdown is enabled, we warn but we do not exit carwired, it will just run in a weakened state sys_exit_getdents64 re-write userspace memory to hide an entry (file/folder), it can be rejected + Only load sys_enter_getdents64 (syscall that will populate the CW_DIRENT MAP) if sys_exit_getdents64 doesnt fail, else the map will overflow */ let mut did_sys_exit_getdents64_success = false; - // to hide files let cardwire_sys_exit_getdents64: &mut TracePoint = ebpf .program_mut("tracepoint_exit_getdents64") .ok_or_else(|| CardwireEbpfError::missing_lsm("tracepoint_exit_getdents64"))? .try_into() .map_err(CardwireEbpfError::aya)?; - // Try to load the program, if success attach it, else just warn the user + // Try to load the program into the kernel, if success attach it, else just warn the user match cardwire_sys_exit_getdents64 .load() .map_err(CardwireEbpfError::aya) @@ -101,7 +101,9 @@ impl EbpfBlocker { warn!("falling back to a weakened cardwired..."); } }; - // to hide files + + // Now we try to load sys_enter_getdents64 + let cardwire_sys_enter_getdents64: &mut TracePoint = ebpf .program_mut("tracepoint_enter_getdents64") .ok_or_else(|| CardwireEbpfError::missing_lsm("tracepoint_enter_getdents64"))? @@ -129,8 +131,6 @@ impl EbpfBlocker { } }; } - // Try to load the program, if success attach it, else just warn the user - Ok(Self { ebpf }) }