Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
LeChatP committed Jul 29, 2024
1 parent 78fcd76 commit f8800a2
Show file tree
Hide file tree
Showing 10 changed files with 36,972 additions and 36,877 deletions.
7 changes: 4 additions & 3 deletions capable-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,15 @@ impl Access {
}
}
}


pub const MAX_REQUESTS: u32 = 256;
pub const PATH_SIZE: usize = 4096;
pub const FILENAME_SIZE: usize = 255;

#[repr(C)]
#[derive(Clone, Copy)]
pub struct Request {
pub f_mode: Access,
pub f_path: [u8; 8188],
pub f_path: [u8; PATH_SIZE],
}

#[cfg(feature = "aya")]
Expand Down
1 change: 1 addition & 0 deletions capable-ebpf/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[build]
target-dir = "../target"
target = "bpfel-unknown-none"
rustflags = [ "-C", "save-temps", "-C", "debuginfo=2"]

[unstable]
build-std = ["core"]
12 changes: 0 additions & 12 deletions capable-ebpf/src/capable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,26 @@ static mut PNSID_NSID_MAP: HashMap<Key, u64> = HashMap::with_max_entries(MAX_PID


pub fn try_capable(ctx: &ProbeContext) -> Result<u32, i64> {
info!(ctx, "capable");
unsafe {
let task: TaskStructPtr = bpf_get_current_task() as TaskStructPtr;
debug!(ctx, "debug1");
let task = bpf_probe_read_kernel(&task)?;
debug!(ctx, "debug2");
let ppid: i32 = get_ppid(task)?;
debug!(ctx, "debug3");
let pid: i32 = bpf_probe_read_kernel(&(*task).pid)? as i32;
debug!(ctx, "debug4");
let cap: u64 = (1 << ctx.arg::<u8>(2).unwrap()) as u64;
debug!(ctx, "debug5");
let uid: u64 = bpf_get_current_uid_gid();
debug!(ctx, "debug6");
let zero = 0;
let capval: u64 = *CAPABILITIES_MAP.get(&pid).unwrap_or(&zero);
debug!(ctx, "debug7");
let pinum_inum: u64 = Into::<u64>::into(get_parent_ns_inode(task)?) << 32
| Into::<u64>::into(get_ns_inode(task)?);
debug!(ctx, "debug8");
UID_GID_MAP
.insert(&pid, &uid, 0)
.expect("failed to insert uid");
debug!(ctx, "debug9");
PNSID_NSID_MAP
.insert(&pid, &pinum_inum, 0)
.expect("failed to insert pnsid");
debug!(ctx, "debug10");
PPID_MAP
.insert(&pid, &ppid, 0)
.expect("failed to insert ppid");
debug!(ctx, "debug11");
CAPABILITIES_MAP
.insert(&pid, &(capval | cap), 0)
.expect("failed to insert cap");
Expand Down
33 changes: 24 additions & 9 deletions capable-ebpf/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ use aya_ebpf::{
macros::{kretprobe, kprobe},
programs::{RetProbeContext, ProbeContext},
};
use aya_ebpf::macros::lsm;
use aya_ebpf::programs::LsmContext;
use aya_log_ebpf::info;
use crate::capable::try_capable;
use crate::open::{try_acl_may_create, try_acl_may_open, try_acl_may_delete, try_acl_may_linkat, try_acl_may_lookup, try_acl_may_follow_link};
use crate::open::{try_acl_may_create, try_acl_may_open, try_acl_may_delete, try_acl_may_linkat, try_acl_pick_link};
use crate::ret_open::try_ret_acl_may_action;

#[kprobe]
pub fn acl_may_open(ctx: ProbeContext) -> u32 {
info!(&ctx, "may_open_kprobe");
try_acl_may_open(&ctx).unwrap_or_else(|ret| ret as u32)
}

Expand All @@ -32,34 +36,45 @@ pub fn acl_may_create(ctx: ProbeContext) -> u32 {

#[kprobe]
pub fn acl_may_delete(ctx: ProbeContext) -> u32 {
try_acl_may_delete(&ctx).unwrap_or_else(|ret| ret as u32)
// try_acl_may_delete(&ctx).unwrap_or_else(|ret| ret as u32)
0
}

#[kprobe]
pub fn acl_may_linkat(ctx: ProbeContext) -> u32 {
try_acl_may_linkat(&ctx).unwrap_or_else(|ret| ret as u32)
//try_acl_may_linkat(&ctx).unwrap_or_else(|ret| ret as u32)
0
}

#[kprobe]
pub fn acl_may_lookup(ctx: ProbeContext) -> u32 {
try_acl_may_lookup(&ctx).unwrap_or_else(|ret| ret as u32)
#[lsm(hook = "")]
pub fn acl_link_path_walk(ctx: ProbeContext) -> u32 {
// try_acl_may_link_path_walk(&ctx).unwrap_or_else(|ret| ret as u32)
0
}

#[kprobe]
pub fn acl_may_follow_link(ctx: ProbeContext) -> u32 {
try_acl_may_follow_link(&ctx).unwrap_or_else(|ret| ret as u32)
pub fn acl_pick_link(ctx: ProbeContext) -> u32 {
// try_acl_may_follow_link(&ctx).unwrap_or_else(|ret| ret as u32)
0
}

#[kretprobe]
pub fn acl_may_ret(ctx: RetProbeContext) -> u32 {
try_ret_acl_may_action(&ctx).unwrap_or_else(|ret| ret as u32)
// try_ret_acl_may_action(&ctx).unwrap_or_else(|ret| ret as u32)
0
}

#[kprobe]
pub fn capable(ctx: ProbeContext) -> u32 {
try_capable(&ctx).unwrap_or_else(|ret| ret as u32)
}

// as we want to hook every acl requests, we need to obtain every acl modification requests.
#[lsm(hook = "inode_set_acl")]
pub fn bpf_lsm_change_acl(ctx: LsmContext) -> i32 {
0
}

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
unsafe { core::hint::unreachable_unchecked() }
Expand Down
166 changes: 98 additions & 68 deletions capable-ebpf/src/open.rs
Original file line number Diff line number Diff line change
@@ -1,141 +1,171 @@
use core::ffi::{c_long, c_void};
use core::mem;
use aya_ebpf::{
helpers::{bpf_get_current_task, bpf_probe_read_kernel},
macros::map,
maps::HashMap,
programs::ProbeContext,
};
use aya_ebpf::helpers::gen::bpf_probe_read_kernel_str;
use aya_log_ebpf::info;
use aya_ebpf::maps::LruPerCpuHashMap;
use aya_log_ebpf::{debug, info, warn};
use crate::ebpf_util::{TaskStructPtr, is_namespace_ok, MAX_PID, get_thread_pid};
use crate::vmlinux::{dentry, nameidata, path, pid};
use capable_common::{Access, Request};
use capable_common::{Access, FILENAME_SIZE, MAX_REQUESTS, PATH_SIZE, Request};
pub type DentryPtr = *mut dentry;
pub type PathPtr = *mut path;
pub type NameidataPtr = *mut nameidata;
pub type PidPtr = *mut pid;

#[map]
pub static mut PENDING_REQUESTS: HashMap<u64, Request> = HashMap::with_max_entries(MAX_PID, 0);

pub static mut PENDING_REQUESTS: LruPerCpuHashMap<u64, Request> = LruPerCpuHashMap::with_max_entries(MAX_REQUESTS, 0);


pub fn try_acl_may_create(ctx: &ProbeContext) -> Result<u32, i64> {
info!(ctx, "may_create");
try_acl_may_action(
let dentry = unsafe { ctx.arg::<DentryPtr>(2).expect("DentryPtr should be here") };

/*try_acl_may_action(
ctx,
Request {
f_path: unsafe { get_full_path(ctx.arg::<DentryPtr>(2).expect("DentryPtr should be here"))? },
f_mode: Access::CREATE,
},
)
dentry,
Access::CREATE,
)*/
Ok(0)
}

pub fn try_acl_may_open(ctx: &ProbeContext) -> Result<u32, i64> {
info!(ctx, "may_open");
try_acl_may_action(
//info!(ctx, "may_open");
let dentry = unsafe { get_dentry_from_pathptr(ctx.arg::<PathPtr>(1).expect("PathPtr should be here"))? };

//debug!(ctx,"d_name : {}", str);
//info!(ctx, "may_open dentry");
//let access = Access::from_bits(ctx.arg::<u32>(2).expect("bits")).expect("Should be valid Access type") | Access::OPEN;
//info!(ctx, "may_open access");
/**try_acl_may_action(
ctx,
Request {
f_path: unsafe { get_full_path(get_dentry_from_pathptr(*ctx.arg::<PathPtr>(1).expect("PathPtr should be here"))?)? },
f_mode: Access::from_bits(ctx.arg::<u32>(2).expect("bits")).expect("Should be valid Access type") | Access::OPEN,
},
)
dentry,
access,
)*/
Ok(0)
}

pub fn try_acl_may_delete(ctx: &ProbeContext) -> Result<u32, i64> {
info!(ctx, "may_delete");
try_acl_may_action(
/*try_acl_may_action(
ctx,
Request {
f_path: unsafe { get_full_path(ctx.arg::<DentryPtr>(2).expect("DentryPtr should be here"))? },
f_mode: Access::DELETE,
},
)
unsafe { ctx.arg::<DentryPtr>(2).expect("DentryPtr should be here") },
Access::DELETE,
)*/
Ok(0)
}

pub fn try_acl_may_linkat(ctx: &ProbeContext) -> Result<u32, i64> {
info!(ctx, "may_linkat");
try_acl_may_action(
/*try_acl_may_action(
ctx,
Request {
f_path: unsafe { get_full_path(get_dentry_from_pathptr(*ctx.arg::<PathPtr>(2).expect("PathPtr should be here"))?)? },
f_mode: Access::LINKAT,
},
)
unsafe { get_dentry_from_pathptr(ctx.arg::<PathPtr>(2).expect("PathPtr should be here"))? },
Access::LINKAT,
)*/
Ok(0)
}

pub fn try_acl_may_lookup(ctx: &ProbeContext) -> Result<u32, i64> {
info!(ctx, "may_lookup");
try_acl_may_action(
pub fn try_acl_may_link_path_walk(ctx: &ProbeContext) -> Result<u32, i64> {
info!(ctx, "may_link_path_walk");
/*try_acl_may_action(
ctx,
Request {
f_path: unsafe {
get_full_path(get_dentry_from_nameidata(ctx.arg::<NameidataPtr>(1).expect("Nameidata should be here"))?)?
},
f_mode: Access::LOOKUP,
},
)
unsafe { get_dentry_from_nameidata(ctx.arg::<NameidataPtr>(1).expect("Nameidata should be here"))? },
Access::LOOKUP,
)*/
Ok(0)
}

pub fn try_acl_may_follow_link(ctx: &ProbeContext) -> Result<u32, i64> {
info!(ctx, "may_follow_link");
try_acl_may_action(
pub fn try_acl_pick_link(ctx: &ProbeContext) -> Result<u32, i64> {
info!(ctx, "may_pick_link");
/*try_acl_may_action(
ctx,
Request {
f_path: unsafe {
get_full_path(get_dentry_from_nameidata(ctx.arg::<NameidataPtr>(0).expect("Nameidata should be here"))?)?
},
f_mode: Access::FOLLOW_LINK,
},
)
unsafe { get_dentry_from_nameidata(ctx.arg::<NameidataPtr>(0).expect("Nameidata should be here"))? },
Access::FOLLOW_LINK,
)*/
Ok(0)
}

const LOOP_MAX: u32 = 25;

unsafe fn get_dentry_from_pathptr(path: path) -> Result<DentryPtr, i64> {
bpf_probe_read_kernel(&path.dentry)
unsafe fn get_dentry_from_pathptr(path: PathPtr) -> Result<DentryPtr, i64> {
let path1 = bpf_probe_read_kernel(&path)?;
Ok(bpf_probe_read_kernel(&(*path1).dentry)?)
}

unsafe fn get_dentry_from_nameidata(path: NameidataPtr) -> Result<DentryPtr, i64> {
get_dentry_from_pathptr(bpf_probe_read_kernel(&(*path).path)?)
get_dentry_from_pathptr(&mut (*bpf_probe_read_kernel(&path)?).path)
}

const SIZE: usize = 8188;
unsafe fn get_full_path(dentry: DentryPtr) -> Result<[u8;SIZE], i64> {
let mut result = [0u8; SIZE];
let mut length = read_kernel_str(result[0] as *mut c_void, SIZE as u32, get_path_str_ptr(dentry)?)?;
let mut parent: DentryPtr = bpf_probe_read_kernel(&(*dentry).d_parent)?;
fn read_kernel_str_as_result(res : c_long) -> Result<usize, i64> {
if res < 0 {
Err(res as i64)
} else {
Ok(res as usize)
}
}

unsafe fn get_full_path(dentry: DentryPtr, result: *mut [u8; PATH_SIZE]) -> Result<(), i64> {
let dname = unsafe { bpf_probe_read_kernel(&(*dentry).d_name)? };
let name = unsafe { bpf_probe_read_kernel(&dname.name)? };
let mut length = unsafe { read_kernel_str(result as *mut c_void, FILENAME_SIZE as u32, name as *mut c_void)? };

//convert path_str as &str
let mut super_block = bpf_probe_read_kernel(&(*dentry).d_sb)?;
let root = bpf_probe_read_kernel(&(*super_block).s_root)? as DentryPtr;

let mut i = 0;
while length < 8187 || parent != 0 as DentryPtr || LOOP_MAX < i {
result[length] = b'/';
let mut parent: DentryPtr = bpf_probe_read_kernel(&(*dentry).d_parent)?;
while length < PATH_SIZE || dentry != root {
(*result)[length] = b'/';
length += 1;
length += read_kernel_str(result[length] as *mut c_void, (SIZE - length) as u32, get_path_str_ptr(parent)?)?;
let dname = unsafe { bpf_probe_read_kernel(&(*parent).d_name)? };
let name = unsafe { bpf_probe_read_kernel(&dname.name)? };
length += read_kernel_str((*result)[length] as *mut c_void, (FILENAME_SIZE - length) as u32, name as *mut c_void)?;
parent = bpf_probe_read_kernel(&(*parent).d_parent)?;
i += 1;
}
Ok(result)
(*result)[length] = b'/';
length += 1;
Ok(())
}

fn try_acl_may_action(ctx: &ProbeContext, request: Request) -> Result<u32, i64> {
unsafe fn input_request(key: u64, dentry: DentryPtr, mode: Access) -> Result<(), i64> {
//allocate to heap a Request type


match PENDING_REQUESTS.get_ptr_mut(&key).and_then(|ptr| unsafe { ptr.as_mut() }) {
None => Err(-1),
Some(request) => {
get_full_path(dentry, &mut request.f_path)?;
(*request).f_mode = mode;
return Ok(());
}
}

//let request = PENDING_REQUESTS.get_ptr_mut(&key).unwrap();
//get_full_path(dentry, &mut ((*request).f_path))?;
}

fn try_acl_may_action(ctx: &ProbeContext, dentry: DentryPtr, mode: Access) -> Result<u32, i64> {
unsafe {
if !is_namespace_ok() {
return Ok(0);
}
info!(ctx, "may_action");
let task: TaskStructPtr = bpf_get_current_task() as TaskStructPtr;
let task = bpf_probe_read_kernel(&task)?;
let pid = get_thread_pid(task)?;

// if access denied, then find out which user and group can access the file and add it to the UACL_MAP and GACL_MAP
PENDING_REQUESTS.insert(&pid,&request,0).expect("failed to insert request");
Ok(0)
input_request(pid, dentry, mode).map(|_| 0)
}
}

unsafe fn get_path_str_ptr(dentry: DentryPtr) -> Result<*mut c_void, i64> {
Ok(bpf_probe_read_kernel(bpf_probe_read_kernel(&(*dentry).d_name)?.name)? as *mut c_void)
}
fn result_kernel_str(result : c_long) -> Result<usize, i64> {
fn result_kernel_str(result: c_long) -> Result<usize, i64> {
if result < 0 {
Err(result as i64)
} else {
Expand Down
Loading

0 comments on commit f8800a2

Please sign in to comment.