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
9 changes: 9 additions & 0 deletions fact-ebpf/src/bpf/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ __always_inline static void submit_open_event(struct submit_event_args_t* args,
__submit_event(args, true);
}

__always_inline static void submit_link_event(struct submit_event_args_t* args) {
if (!reserve_event(args)) {
return;
}
args->event->type = FILE_ACTIVITY_LINK;

__submit_event(args, path_hooks_support_bpf_d_path);
}

__always_inline static void submit_unlink_event(struct submit_event_args_t* args) {
if (!reserve_event(args)) {
return;
Expand Down
59 changes: 53 additions & 6 deletions fact-ebpf/src/bpf/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,43 @@ 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->path_link};

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");
args.metrics->error++;
return 0;
}
args.filename = new_path->path;

// The inode is from the old file (being linked to), which is the same
// inode the new link will point to.
args.inode = inode_to_key(old_dentry->d_inode);
args.parent_inode = inode_to_key(new_dir->dentry->d_inode);
args.monitored = is_monitored(&args.inode, new_path, &args.parent_inode);

if (args.monitored == NOT_MONITORED) {
args.metrics->ignored++;
return 0;
}

if (args.monitored == MONITORED_BY_PARENT) {
inode_add(&args.inode);
}

submit_link_event(&args);
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 +149,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 +277,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 +291,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 +309,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
2 changes: 2 additions & 0 deletions fact-ebpf/src/bpf/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ typedef enum file_activity_type_t {
FILE_ACTIVITY_INIT = -1,
FILE_ACTIVITY_OPEN = 0,
FILE_ACTIVITY_CREATION,
FILE_ACTIVITY_LINK,
FILE_ACTIVITY_UNLINK,
FILE_ACTIVITY_CHMOD,
FILE_ACTIVITY_CHOWN,
Expand Down Expand Up @@ -187,6 +188,7 @@ struct metrics_d_instantiate_t {

struct metrics_t {
struct metrics_by_hook_t file_open;
struct metrics_by_hook_t path_link;
struct metrics_by_hook_t path_unlink;
struct metrics_by_hook_t path_chmod;
struct metrics_by_hook_t path_chown;
Expand Down
19 changes: 19 additions & 0 deletions fact/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ impl Event {
matches!(self.file, FileData::Unlink(_) | FileData::RmDir(_))
}

pub fn is_link(&self) -> bool {
matches!(self.file, FileData::Link(_))
}

pub fn is_rename(&self) -> bool {
matches!(self.file, FileData::Rename { .. })
}
Expand All @@ -180,6 +184,7 @@ impl Event {
| FileData::Creation(inner)
| FileData::MkDir(inner)
| FileData::RmDir(inner)
| FileData::Link(inner)
| FileData::Unlink(inner)
| FileData::Chmod(ChmodFileData { inner, .. })
| FileData::Chown(ChownFileData { inner, .. })
Expand All @@ -201,6 +206,7 @@ impl Event {
| FileData::Creation(inner)
| FileData::MkDir(inner)
| FileData::RmDir(inner)
| FileData::Link(inner)
| FileData::Unlink(inner)
| FileData::Chmod(ChmodFileData { inner, .. })
| FileData::Chown(ChownFileData { inner, .. })
Expand Down Expand Up @@ -233,6 +239,7 @@ impl Event {
| FileData::Creation(inner)
| FileData::MkDir(inner)
| FileData::RmDir(inner)
| FileData::Link(inner)
| FileData::Unlink(inner)
| FileData::Chmod(ChmodFileData { inner, .. })
| FileData::Chown(ChownFileData { inner, .. })
Expand Down Expand Up @@ -262,6 +269,7 @@ impl Event {
| FileData::Creation(inner)
| FileData::MkDir(inner)
| FileData::RmDir(inner)
| FileData::Link(inner)
| FileData::Unlink(inner)
| FileData::Chmod(ChmodFileData { inner, .. })
| FileData::Chown(ChownFileData { inner, .. })
Expand Down Expand Up @@ -295,6 +303,7 @@ impl Event {
| FileData::Creation(inner)
| FileData::MkDir(inner)
| FileData::RmDir(inner)
| FileData::Link(inner)
| FileData::Unlink(inner)
| FileData::Chmod(ChmodFileData { inner, .. })
| FileData::Chown(ChownFileData { inner, .. })
Expand Down Expand Up @@ -326,6 +335,7 @@ impl Event {
| FileData::Creation(inner)
| FileData::MkDir(inner)
| FileData::RmDir(inner)
| FileData::Link(inner)
| FileData::Unlink(inner)
| FileData::Chmod(ChmodFileData { inner, .. })
| FileData::Chown(ChownFileData { inner, .. })
Expand Down Expand Up @@ -444,6 +454,7 @@ pub enum FileData {
Creation(BaseFileData),
MkDir(BaseFileData),
RmDir(BaseFileData),
Link(BaseFileData),
Unlink(BaseFileData),
Chmod(ChmodFileData),
Chown(ChownFileData),
Expand Down Expand Up @@ -489,6 +500,7 @@ impl FileData {
file_activity_type_t::FILE_ACTIVITY_CREATION => FileData::Creation(inner),
file_activity_type_t::DIR_ACTIVITY_CREATION => FileData::MkDir(inner),
file_activity_type_t::DIR_ACTIVITY_UNLINK => FileData::RmDir(inner),
file_activity_type_t::FILE_ACTIVITY_LINK => FileData::Link(inner),
file_activity_type_t::FILE_ACTIVITY_UNLINK => FileData::Unlink(inner),
file_activity_type_t::FILE_ACTIVITY_CHMOD => {
let data = ChmodFileData {
Expand Down Expand Up @@ -563,6 +575,7 @@ impl FileData {
FileData::Creation(_) => "creation",
FileData::MkDir(_) => "mkdir",
FileData::RmDir(_) => "rmdir",
FileData::Link(_) => "link",
FileData::Unlink(_) => "unlink",
FileData::Chmod(_) => "permission",
FileData::Chown(_) => "ownership",
Expand Down Expand Up @@ -605,6 +618,11 @@ impl From<FileData> for fact_api::file_activity::File {
let f_act = fact_api::FileXattrChange::from(event);
fact_api::file_activity::File::XattrRemove(f_act)
}
FileData::Link(event) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since this type of event is being handled the same as a creation, you can probably just change the FileData::Creation(event) branch to FileData::Creation(event) | FileData::Link(event)

let activity = Some(fact_api::FileActivityBase::from(event));
let f_act = fact_api::FileCreation { activity };
fact_api::file_activity::File::Creation(f_act)
}
FileData::Unlink(event) => {
let activity = Some(fact_api::FileActivityBase::from(event));
let f_act = fact_api::FileUnlink { activity };
Expand Down Expand Up @@ -653,6 +671,7 @@ impl From<FileData> for opentelemetry::logs::AnyValue {
| FileData::RmDir(data)
| FileData::Mount(data)
| FileData::Umount(data)
| FileData::Link(data)
| FileData::Unlink(data) => AnyValue::from(data),
FileData::Chmod(data) => AnyValue::from(data),
FileData::Chown(data) => AnyValue::from(data),
Expand Down
Loading