Skip to content
Open
Show file tree
Hide file tree
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
65 changes: 30 additions & 35 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ aya = { version = "0.13.1", default-features = false }
anyhow = { version = "1", default-features = false, features = ["std", "backtrace"] }
clap = { version = "4.5.41", features = ["derive", "env"] }
env_logger = { version = "0.11.5", default-features = false, features = ["humantime"] }
glob = "0.3.3"
globset = "0.4.18"
http-body-util = "0.1.3"
hyper = { version = "1.6.0", default-features = false }
hyper-tls = "0.6.0"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ In order to run these tests as part of the unit test suite y use the
following command:

```shell
cargo test --config 'target."cfg(all())".runner="sudo -E" --features=bpf-test
cargo test --config 'target."cfg(all())".runner="sudo -E"' --features=bpf-test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this one!

```

## Create compile_commands.json
Expand Down
19 changes: 18 additions & 1 deletion fact-ebpf/src/bpf/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
#include "builtins.h"
#include "types.h"
#include "maps.h"
#include "inode.h"

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
// clang-format on

__always_inline static bool is_monitored(struct bound_path_t* path) {
__always_inline static bool path_is_monitored(struct bound_path_t* path) {
if (!filter_by_prefix()) {
// no path configured, allow all
return true;
Expand All @@ -30,3 +31,19 @@ __always_inline static bool is_monitored(struct bound_path_t* path) {
path->len = len;
return res;
}

__always_inline static bool is_monitored(inode_key_t inode, struct bound_path_t* path, inode_key_t** submit) {
const inode_value_t* volatile inode_value = inode_get(&inode);

switch (inode_is_monitored(inode_value)) {
case NOT_MONITORED:
if (path_is_monitored(path)) {
return true;
}
*submit = NULL;
return false;
case MONITORED:
break;
}
return true;
}
84 changes: 29 additions & 55 deletions fact-ebpf/src/bpf/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,13 @@ int BPF_PROG(trace_file_open, struct file* file) {
}

inode_key_t inode_key = inode_to_key(file->f_inode);
const inode_value_t* inode = inode_get(&inode_key);
switch (inode_is_monitored(inode)) {
case NOT_MONITORED:
if (!is_monitored(path)) {
goto ignored;
}
break;
case MONITORED:
break;
inode_key_t* inode_to_submit = &inode_key;

if (!is_monitored(inode_key, path, &inode_to_submit)) {
goto ignored;
}

submit_open_event(&m->file_open, event_type, path->path, &inode_key);
submit_open_event(&m->file_open, event_type, path->path, inode_to_submit);

return 0;

Expand All @@ -82,24 +77,16 @@ int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) {
}

inode_key_t inode_key = inode_to_key(dentry->d_inode);
const inode_value_t* inode = inode_get(&inode_key);

switch (inode_is_monitored(inode)) {
case NOT_MONITORED:
if (!is_monitored(path)) {
m->path_unlink.ignored++;
return 0;
}
break;
inode_key_t* inode_to_submit = &inode_key;

case MONITORED:
inode_remove(&inode_key);
break;
if (!is_monitored(inode_key, path, &inode_to_submit)) {
m->path_unlink.ignored++;
return 0;
}

submit_unlink_event(&m->path_unlink,
path->path,
&inode_key);
inode_to_submit);
return 0;
}

Expand All @@ -120,24 +107,17 @@ int BPF_PROG(trace_path_chmod, struct path* path, umode_t mode) {
}

inode_key_t inode_key = inode_to_key(path->dentry->d_inode);
const inode_value_t* inode = inode_get(&inode_key);

switch (inode_is_monitored(inode)) {
case NOT_MONITORED:
if (!is_monitored(bound_path)) {
m->path_chmod.ignored++;
return 0;
}
break;
inode_key_t* inode_to_submit = &inode_key;

case MONITORED:
break;
if (!is_monitored(inode_key, bound_path, &inode_to_submit)) {
m->path_chmod.ignored++;
return 0;
}

umode_t old_mode = BPF_CORE_READ(path, dentry, d_inode, i_mode);
submit_mode_event(&m->path_chmod,
bound_path->path,
&inode_key,
inode_to_submit,
mode,
old_mode);

Expand All @@ -164,18 +144,11 @@ int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsign
}

inode_key_t inode_key = inode_to_key(path->dentry->d_inode);
const inode_value_t* inode = inode_get(&inode_key);

switch (inode_is_monitored(inode)) {
case NOT_MONITORED:
if (!is_monitored(bound_path)) {
m->path_chown.ignored++;
return 0;
}
break;
inode_key_t* inode_to_submit = &inode_key;

case MONITORED:
break;
if (!is_monitored(inode_key, bound_path, &inode_to_submit)) {
m->path_chown.ignored++;
return 0;
}

struct dentry* d = BPF_CORE_READ(path, dentry);
Expand All @@ -184,7 +157,7 @@ int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsign

submit_ownership_event(&m->path_chown,
bound_path->path,
&inode_key,
inode_to_submit,
uid,
gid,
old_uid,
Expand Down Expand Up @@ -217,23 +190,24 @@ int BPF_PROG(trace_path_rename, struct path* old_dir,
}

inode_key_t old_inode = inode_to_key(old_dentry->d_inode);
const inode_value_t* volatile old_inode_value = inode_get(&old_inode);
inode_key_t new_inode = inode_to_key(new_dentry->d_inode);
const inode_value_t* volatile new_inode_value = inode_get(&new_inode);

if (inode_is_monitored(old_inode_value) == NOT_MONITORED &&
inode_is_monitored(new_inode_value) == NOT_MONITORED &&
!is_monitored(old_path) &&
!is_monitored(new_path)) {
inode_key_t* old_inode_submit = &old_inode;
inode_key_t* new_inode_submit = &new_inode;

bool old_monitored = is_monitored(old_inode, old_path, &old_inode_submit);
bool new_monitored = is_monitored(new_inode, new_path, &new_inode_submit);

if (!old_monitored && !new_monitored) {
m->path_rename.ignored++;
return 0;
}

submit_rename_event(&m->path_rename,
new_path->path,
old_path->path,
&old_inode,
&new_inode);
old_inode_submit,
new_inode_submit);
return 0;

error:
Expand Down
23 changes: 17 additions & 6 deletions fact-ebpf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,22 @@ impl TryFrom<&PathBuf> for path_prefix_t {
prefix: value.display().to_string(),
});
};
let len = if filename.len() > LPM_SIZE_MAX as usize {
LPM_SIZE_MAX as usize
} else {
filename.len()
};

// Take the start of the path until the first occurence of a wildcard
// character. This is used as a filter in the kernel in cases where
// the inode has failed to match. The full wildcard string is used
// for further processing in userspace.
//
// unwrap is safe here - if there are no matches, the full string is the
// only item in the iterator
let filename_prefix = filename.split(['*', '?', '[', '{']).next().unwrap();
let len = filename_prefix.len().min(LPM_SIZE_MAX as usize);

unsafe {
let mut cfg: path_prefix_t = std::mem::zeroed();
memcpy(
cfg.path.as_mut_ptr() as *mut _,
filename.as_ptr() as *const _,
filename_prefix.as_ptr() as *const _,
len,
);
cfg.bit_len = (len * 8) as u32;
Expand All @@ -63,6 +68,12 @@ impl PartialEq for path_prefix_t {

unsafe impl Pod for path_prefix_t {}

impl inode_key_t {
pub fn empty(&self) -> bool {
self.inode == 0 && self.dev == 0
}
}

impl PartialEq for inode_key_t {
fn eq(&self, other: &Self) -> bool {
self.inode == other.inode && self.dev == other.dev
Expand Down
2 changes: 2 additions & 0 deletions fact/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ anyhow = { workspace = true }
aya = { workspace = true }
clap = { workspace = true }
env_logger = { workspace = true }
glob = { workspace = true }
globset = { workspace = true }
http-body-util = { workspace = true }
hyper = { workspace = true }
hyper-tls = { workspace = true }
Expand Down
Loading
Loading