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
60 changes: 56 additions & 4 deletions fact-ebpf/src/bpf/bound_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ __always_inline static void path_write_char(char* p, unsigned int offset, char c
*path_safe_access(p, offset) = c;
}

__always_inline static struct bound_path_t* _path_read(struct path* path, bool use_bpf_d_path) {
struct bound_path_t* bound_path = get_bound_path();
__always_inline static struct bound_path_t* _path_read(struct path* path, bound_path_buffer_t key, bool use_bpf_d_path) {
struct bound_path_t* bound_path = get_bound_path(key);
if (bound_path == NULL) {
return NULL;
}
Expand All @@ -37,11 +37,19 @@ __always_inline static struct bound_path_t* _path_read(struct path* path, bool u
}

__always_inline static struct bound_path_t* path_read(struct path* path) {
return _path_read(path, true);
return _path_read(path, BOUND_PATH_MAIN, true);
}

__always_inline static struct bound_path_t* path_read_no_d_path(struct path* path) {
return _path_read(path, false);
return _path_read(path, BOUND_PATH_MAIN, false);
}

__always_inline static struct bound_path_t* path_read_alt(struct path* path) {
return _path_read(path, BOUND_PATH_ALTERNATE, true);
}

__always_inline static struct bound_path_t* path_read_alt_no_d_path(struct path* path) {
return _path_read(path, BOUND_PATH_ALTERNATE, false);
}

enum path_append_status_t {
Expand Down Expand Up @@ -69,3 +77,47 @@ __always_inline static enum path_append_status_t path_append_dentry(struct bound

return 0;
}

__always_inline static struct bound_path_t* _path_read_append_d_entry(struct path* dir, struct dentry* dentry, bound_path_buffer_t key) {
struct bound_path_t* path = _path_read(dir, key, path_hooks_support_bpf_d_path);

if (path == NULL) {
bpf_printk("Failed to read path");
return NULL;
}
path_write_char(path->path, path->len - 1, '/');

switch (path_append_dentry(path, dentry)) {
case PATH_APPEND_SUCCESS:
break;
case PATH_APPEND_INVALID_LENGTH:
bpf_printk("Invalid path length: %u", path->len);
return NULL;
case PATH_APPEND_READ_ERROR:
bpf_printk("Failed to read final path component");
return NULL;
}
return path;
}

/**
* Read the path and append the supplied dentry.
*
* A very common pattern in the kernel is to provide a struct path to a
* directory and a dentry to an element in said directory, this helper
* provides a short way of resolving the full path in one call.
*/
__always_inline static struct bound_path_t* path_read_append_d_entry(struct path* dir, struct dentry* dentry) {
return _path_read_append_d_entry(dir, dentry, BOUND_PATH_MAIN);
}

/**
* Read the path and append the supplied dentry.
*
* This works essentially the same as path_read_append_d_entry, but does
* so in an alternate buffer. Useful for operations that take more than
* one path, like path_rename.
*/
__always_inline static struct bound_path_t* path_read_alt_append_d_entry(struct path* dir, struct dentry* dentry) {
return _path_read_append_d_entry(dir, dentry, BOUND_PATH_ALTERNATE);
}
18 changes: 18 additions & 0 deletions fact-ebpf/src/bpf/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,21 @@ __always_inline static void submit_ownership_event(struct metrics_by_hook_t* m,

__submit_event(event, m, FILE_ACTIVITY_CHOWN, filename, inode, use_bpf_d_path);
}

__always_inline static void submit_rename_event(struct metrics_by_hook_t* m,
const char new_filename[PATH_MAX],
const char old_filename[PATH_MAX],
inode_key_t* new_inode,
inode_key_t* old_inode,
bool use_bpf_d_path) {
struct event_t* event = bpf_ringbuf_reserve(&rb, sizeof(struct event_t), 0);
if (event == NULL) {
m->ringbuffer_full++;
return;
}

bpf_probe_read_str(event->rename.old_filename, PATH_MAX, old_filename);
inode_copy_or_reset(&event->rename.old_inode, old_inode);

__submit_event(event, m, FILE_ACTIVITY_RENAME, new_filename, new_inode, use_bpf_d_path);
}
2 changes: 1 addition & 1 deletion fact-ebpf/src/bpf/inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ typedef enum inode_monitored_t {
* check if the parent of the provided inode is monitored and provide
* different results for handling more complicated scenarios.
*/
__always_inline static inode_monitored_t inode_is_monitored(const inode_value_t* inode) {
__always_inline static inode_monitored_t inode_is_monitored(const inode_value_t* volatile inode) {
if (inode != NULL) {
return MONITORED;
}
Expand Down
76 changes: 52 additions & 24 deletions fact-ebpf/src/bpf/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,28 +74,11 @@ int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) {

m->path_unlink.total++;

struct bound_path_t* path = NULL;
if (path_hooks_support_bpf_d_path) {
path = path_read(dir);
} else {
path = path_read_no_d_path(dir);
}

struct bound_path_t* path = path_read_append_d_entry(dir, dentry);
if (path == NULL) {
bpf_printk("Failed to read path");
goto error;
}
path_write_char(path->path, path->len - 1, '/');

switch (path_append_dentry(path, dentry)) {
case PATH_APPEND_SUCCESS:
break;
case PATH_APPEND_INVALID_LENGTH:
bpf_printk("Invalid path length: %u", path->len);
goto error;
case PATH_APPEND_READ_ERROR:
bpf_printk("Failed to read final path component");
goto error;
m->path_unlink.error++;
return 0;
}

inode_key_t inode_key = inode_to_key(dentry->d_inode);
Expand All @@ -120,10 +103,6 @@ int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) {
&inode_key,
path_hooks_support_bpf_d_path);
return 0;

error:
m->path_unlink.error++;
return 0;
}

SEC("lsm/path_chmod")
Expand Down Expand Up @@ -229,3 +208,52 @@ int BPF_PROG(trace_path_chown, struct path* path, unsigned long long uid, unsign

return 0;
}

SEC("lsm/path_rename")
int BPF_PROG(trace_path_rename, struct path* old_dir,
struct dentry* old_dentry, struct path* new_dir,
struct dentry* new_dentry, unsigned int flags) {
struct metrics_t* m = get_metrics();
if (m == NULL) {
return 0;
}

m->path_rename.total++;

struct bound_path_t* new_path = path_read_append_d_entry(new_dir, new_dentry);
if (new_path == NULL) {
bpf_printk("Failed to read path");
goto error;
}

struct bound_path_t* old_path = path_read_alt_append_d_entry(old_dir, old_dentry);
if (old_path == NULL) {
bpf_printk("Failed to read path");
goto error;
}

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)) {
m->path_rename.ignored++;
return 0;
}

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

error:
m->path_rename.error++;
return 0;
}
12 changes: 8 additions & 4 deletions fact-ebpf/src/bpf/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,16 @@ struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__type(key, __u32);
__type(value, struct bound_path_t);
__uint(max_entries, 1);
__uint(max_entries, 2);
} bound_path_heap SEC(".maps");

__always_inline static struct bound_path_t* get_bound_path() {
unsigned int zero = 0;
return bpf_map_lookup_elem(&bound_path_heap, &zero);
typedef enum {
BOUND_PATH_MAIN = 0,
BOUND_PATH_ALTERNATE = 1,
} bound_path_buffer_t;

__always_inline static struct bound_path_t* get_bound_path(bound_path_buffer_t key) {
return bpf_map_lookup_elem(&bound_path_heap, &key);
}

struct {
Expand Down
6 changes: 6 additions & 0 deletions fact-ebpf/src/bpf/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ typedef enum file_activity_type_t {
FILE_ACTIVITY_UNLINK,
FILE_ACTIVITY_CHMOD,
FILE_ACTIVITY_CHOWN,
FILE_ACTIVITY_RENAME,
} file_activity_type_t;

struct event_t {
Expand All @@ -73,6 +74,10 @@ struct event_t {
unsigned int gid;
} old, new;
} chown;
struct {
char old_filename[PATH_MAX];
inode_key_t old_inode;
} rename;
};
};

Expand Down Expand Up @@ -104,4 +109,5 @@ struct metrics_t {
struct metrics_by_hook_t path_unlink;
struct metrics_by_hook_t path_chmod;
struct metrics_by_hook_t path_chown;
struct metrics_by_hook_t path_rename;
};
1 change: 1 addition & 0 deletions fact-ebpf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ impl metrics_t {
m.path_unlink = m.path_unlink.accumulate(&other.path_unlink);
m.path_chmod = m.path_chmod.accumulate(&other.path_chmod);
m.path_chown = m.path_chown.accumulate(&other.path_chown);
m.path_rename = m.path_rename.accumulate(&other.path_rename);
m
}
}
Expand Down
25 changes: 23 additions & 2 deletions fact/src/bpf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ mod bpf_tests {
tokio::time::sleep(Duration::from_millis(500)).await;

// Create a file
let file = NamedTempFile::new_in(monitored_path).expect("Failed to create temporary file");
let file = NamedTempFile::new_in(&monitored_path).expect("Failed to create temporary file");
println!("Created {file:?}");

// Trigger permission changes
Expand All @@ -286,6 +286,12 @@ mod bpf_tests {
let current = Process::current();
let file_path = file.path().to_path_buf();

// Trigger a file rename
let renamed_path = monitored_path.join("target");
std::fs::rename(&file, &renamed_path).expect("Failed to rename file");
// Move the file back so it can be properly closed
std::fs::rename(&renamed_path, &file).expect("Failed to rename file");

let expected_events = [
Event::new(
EventTestData::Creation,
Expand All @@ -303,6 +309,22 @@ mod bpf_tests {
current.clone(),
)
.unwrap(),
Event::new(
EventTestData::Rename(file_path.clone()),
host_info::get_hostname(),
renamed_path.clone(),
PathBuf::new(),
current.clone(),
)
.unwrap(),
Event::new(
EventTestData::Rename(renamed_path),
host_info::get_hostname(),
file_path.clone(),
PathBuf::new(),
current.clone(),
)
.unwrap(),
Event::new(
EventTestData::Unlink,
host_info::get_hostname(),
Expand All @@ -312,7 +334,6 @@ mod bpf_tests {
)
.unwrap(),
];

// Close the file, removing it
file.close().expect("Failed to close temp file");

Expand Down
Loading