Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 71 additions & 20 deletions crates/cardwire-ebpf-userspace/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -32,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))?
Expand Down Expand Up @@ -62,35 +64,73 @@ 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
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;

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")
.ok_or_else(|| CardwireEbpfError::missing_lsm("tracepoint_exit_getdents64"))?
.try_into()
.map_err(CardwireEbpfError::aya)?;

cardwire_sys_exit_getdents64
// 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)?;
.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...");
}
};

// Now we try to load sys_enter_getdents64

cardwire_sys_exit_getdents64
.attach("syscalls", "sys_exit_getdents64")
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)?;

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...");
}
};
}
Ok(Self { ebpf })
}

Expand Down Expand Up @@ -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::*;
Expand Down