Skip to content
Draft
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
66 changes: 60 additions & 6 deletions fact-ebpf/src/bpf/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,50 @@ int BPF_PROG(trace_file_open, struct file* file) {
return 0;
}

SEC("lsm/path_link")
int BPF_PROG(trace_path_link, struct dentry* old_dentry, const struct path* new_dir, struct dentry* new_dentry) {
struct metrics_t* m = get_metrics();
if (m == NULL) {
return 0;
}
struct submit_event_args_t args = {.metrics = &m->file_open};

args.metrics->total++;

struct bound_path_t* new_path = path_read_append_d_entry((struct path*)new_dir, new_dentry);
if (new_path == NULL) {
bpf_printk("Failed to read new path");
m->file_open.error++;
return 0;
}
args.filename = new_path->path;

// The inode is from the old file (being linked to)
args.inode = inode_to_key(old_dentry->d_inode);

struct dentry* parent_dentry = BPF_CORE_READ(new_dir, dentry);
struct inode* parent_inode_ptr = parent_dentry ? BPF_CORE_READ(parent_dentry, d_inode) : NULL;
args.parent_inode = inode_to_key(parent_inode_ptr);

args.monitored = is_monitored(&args.inode, new_path, &args.parent_inode);
if (args.monitored == NOT_MONITORED) {
goto ignored;
}

// Add the inode to tracking if monitored by parent
if (args.monitored == MONITORED_BY_PARENT) {
inode_add(&args.inode);
}

submit_open_event(&args, FILE_ACTIVITY_CREATION);

return 0;

ignored:
m->file_open.ignored++;
return 0;
}

SEC("lsm/path_unlink")
int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) {
struct metrics_t* m = get_metrics();
Expand All @@ -112,8 +156,10 @@ int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) {
return 0;
}

// We only support files with one link for now
inode_remove(&args.inode);
// Only remove from kernel map if this is the last link
if (BPF_CORE_READ(dentry, d_inode, i_nlink) == 1) {
inode_remove(&args.inode);
}

submit_unlink_event(&args);
return 0;
Expand Down Expand Up @@ -238,7 +284,9 @@ int BPF_PROG(trace_path_rename, struct path* old_dir,
// Old inode is monitored, new path is not.
// If the old path is a directory userspace will remove any
// subdirectories and files too.
inode_remove(&old_inode);
if (BPF_CORE_READ(old_dentry, d_inode, i_nlink) == 1) {
inode_remove(&old_inode);
}
}
break;

Expand All @@ -250,7 +298,9 @@ int BPF_PROG(trace_path_rename, struct path* old_dir,
// which should never happen. When the inode crosses into a new
// mount, a new inode is created altogether. Still, we can cover
// our bases.
inode_remove(&old_inode);
if (BPF_CORE_READ(old_dentry, d_inode, i_nlink) == 1) {
inode_remove(&old_inode);
}
}
break;

Expand All @@ -266,15 +316,19 @@ int BPF_PROG(trace_path_rename, struct path* old_dir,
// Old inode is monitored and will land on a path that has a
// monitored parent but the path itself is not monitored, we
// stop tracking the inode
inode_remove(&old_inode);
if (BPF_CORE_READ(old_dentry, d_inode, i_nlink) == 1) {
inode_remove(&old_inode);
}
}
break;

case MONITORED_BY_INODE:
// If we landed here, the new path already has an inode that is
// being tracked and is about to be overwritten, we need to remove
// it from the map
inode_remove(&args.inode);
if (BPF_CORE_READ(new_dentry, d_inode, i_nlink) == 1) {
inode_remove(&args.inode);
}
if (old_monitored != MONITORED_BY_INODE) {
// Old inode is not monitored, but is landing in a monitored
// path that uses inode tracking.
Expand Down
Loading
Loading