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
18 changes: 17 additions & 1 deletion fact-ebpf/src/bpf/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ __always_inline static void __submit_event(struct submit_event_args_t* args,
event->monitored = args->monitored;
inode_copy(&event->inode, &args->inode);
inode_copy(&event->parent_inode, &args->parent_inode);
bpf_probe_read_str(event->filename, PATH_MAX, args->filename);
if (args->filename != NULL) {
bpf_probe_read_str(event->filename, PATH_MAX, args->filename);
} else {
event->filename[0] = '\0';
}

struct helper_t* helper = get_helper();
if (helper == NULL) {
Expand Down Expand Up @@ -144,3 +148,15 @@ __always_inline static void submit_rmdir_event(struct submit_event_args_t* args)

__submit_event(args, path_hooks_support_bpf_d_path);
}

__always_inline static void submit_xattr_event(struct submit_event_args_t* args,
file_activity_type_t event_type,
const char* xattr_name) {
if (!reserve_event(args)) {
return;
}
args->event->type = event_type;
bpf_probe_read_str(args->event->xattr.name, XATTR_NAME_MAX_LEN, xattr_name);

__submit_event(args, false);
}
42 changes: 42 additions & 0 deletions fact-ebpf/src/bpf/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,48 @@ int BPF_PROG(trace_d_instantiate, struct dentry* dentry, struct inode* inode) {
return 0;
}

__always_inline static int handle_xattr(struct metrics_by_hook_t* hook_metrics,
struct dentry* dentry,
const char* xattr_name,
file_activity_type_t event_type) {
struct submit_event_args_t args = {.metrics = hook_metrics};

args.metrics->total++;

args.inode = inode_to_key(dentry->d_inode);
args.parent_inode = inode_to_key(BPF_CORE_READ(dentry, d_parent, d_inode));

args.monitored = inode_is_monitored(inode_get(&args.inode), inode_get(&args.parent_inode));

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

submit_xattr_event(&args, event_type, xattr_name);
return 0;
}

SEC("lsm/inode_setxattr")
int BPF_PROG(trace_inode_setxattr, struct mnt_idmap* idmap, struct dentry* dentry,
const char* name, const void* value, size_t size, int flags) {
struct metrics_t* m = get_metrics();
if (m == NULL) {
return 0;
}
return handle_xattr(&m->inode_setxattr, dentry, name, FILE_ACTIVITY_SETXATTR);
}

SEC("lsm/inode_removexattr")
int BPF_PROG(trace_inode_removexattr, struct mnt_idmap* idmap, struct dentry* dentry,
const char* name) {
struct metrics_t* m = get_metrics();
if (m == NULL) {
return 0;
}
return handle_xattr(&m->inode_removexattr, dentry, name, FILE_ACTIVITY_REMOVEXATTR);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

SEC("lsm/path_rmdir")
int BPF_PROG(trace_path_rmdir, struct path* dir, struct dentry* dentry) {
struct metrics_t* m = get_metrics();
Expand Down
11 changes: 11 additions & 0 deletions fact-ebpf/src/bpf/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@

#define LINEAGE_MAX 2

// Matches Linux kernel XATTR_NAME_MAX (255) + null terminator.
// https://github.com/torvalds/linux/blob/66affa37cfac0aec061cc4bcf4a065b0c52f7e19/include/uapi/linux/limits.h#L15
#define XATTR_NAME_MAX_LEN 256

#define LPM_SIZE_MAX 256

typedef struct lineage_t {
Expand Down Expand Up @@ -64,6 +68,8 @@ typedef enum file_activity_type_t {
FILE_ACTIVITY_RENAME,
DIR_ACTIVITY_CREATION,
DIR_ACTIVITY_UNLINK,
FILE_ACTIVITY_SETXATTR,
FILE_ACTIVITY_REMOVEXATTR,
} file_activity_type_t;

struct event_t {
Expand All @@ -90,6 +96,9 @@ struct event_t {
inode_key_t inode;
monitored_t monitored;
} rename;
struct {
char name[XATTR_NAME_MAX_LEN];
} xattr;
};
};

Expand Down Expand Up @@ -132,4 +141,6 @@ struct metrics_t {
struct metrics_by_hook_t path_mkdir;
struct metrics_by_hook_t d_instantiate;
struct metrics_by_hook_t path_rmdir;
struct metrics_by_hook_t inode_setxattr;
struct metrics_by_hook_t inode_removexattr;
};
2 changes: 2 additions & 0 deletions fact-ebpf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ impl metrics_t {
self.path_mkdir = self.path_mkdir.accumulate(&other.path_mkdir);
self.path_rmdir = self.path_rmdir.accumulate(&other.path_rmdir);
self.d_instantiate = self.d_instantiate.accumulate(&other.d_instantiate);
self.inode_setxattr = self.inode_setxattr.accumulate(&other.inode_setxattr);
self.inode_removexattr = self.inode_removexattr.accumulate(&other.inode_removexattr);
self
}
}
Expand Down
87 changes: 86 additions & 1 deletion fact/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use std::{
use globset::GlobSet;
use serde::Serialize;

use fact_ebpf::{PATH_MAX, event_t, file_activity_type_t, inode_key_t, monitored_t};
use fact_ebpf::{
PATH_MAX, XATTR_NAME_MAX_LEN, event_t, file_activity_type_t, inode_key_t, monitored_t,
};

use crate::host_info;
use process::Process;
Expand Down Expand Up @@ -131,6 +133,10 @@ impl Event {
matches!(self.file, FileData::Creation(_) | FileData::MkDir(_))
}

pub fn is_xattr(&self) -> bool {
matches!(self.file, FileData::SetXattr(_) | FileData::RemoveXattr(_))
}

pub fn is_mkdir(&self) -> bool {
matches!(self.file, FileData::MkDir(_))
}
Expand Down Expand Up @@ -162,6 +168,8 @@ impl Event {
FileData::Chmod(data) => &data.inner.inode,
FileData::Chown(data) => &data.inner.inode,
FileData::Rename(data) => &data.new.inode,
FileData::SetXattr(data) => &data.inner.inode,
FileData::RemoveXattr(data) => &data.inner.inode,
}
}

Expand All @@ -176,6 +184,8 @@ impl Event {
FileData::Chmod(data) => &data.inner.parent_inode,
FileData::Chown(data) => &data.inner.parent_inode,
FileData::Rename(data) => &data.new.parent_inode,
FileData::SetXattr(data) => &data.inner.parent_inode,
FileData::RemoveXattr(data) => &data.inner.parent_inode,
}
}

Expand All @@ -199,6 +209,8 @@ impl Event {
FileData::Chmod(data) => &data.inner.filename,
FileData::Chown(data) => &data.inner.filename,
FileData::Rename(data) => &data.new.filename,
FileData::SetXattr(data) => &data.inner.filename,
FileData::RemoveXattr(data) => &data.inner.filename,
}
}

Expand All @@ -219,6 +231,8 @@ impl Event {
FileData::Chmod(data) => &data.inner.host_file,
FileData::Chown(data) => &data.inner.host_file,
FileData::Rename(data) => &data.new.host_file,
FileData::SetXattr(data) => &data.inner.host_file,
FileData::RemoveXattr(data) => &data.inner.host_file,
}
}

Expand All @@ -243,6 +257,8 @@ impl Event {
FileData::Chmod(data) => data.inner.host_file = host_path,
FileData::Chown(data) => data.inner.host_file = host_path,
FileData::Rename(data) => data.new.host_file = host_path,
FileData::SetXattr(data) => data.inner.host_file = host_path,
FileData::RemoveXattr(data) => data.inner.host_file = host_path,
}
}

Expand All @@ -264,6 +280,8 @@ impl Event {
FileData::Chmod(data) => data.inner.monitored,
FileData::Chown(data) => data.inner.monitored,
FileData::Rename(data) => data.new.monitored,
FileData::SetXattr(data) => data.inner.monitored,
FileData::RemoveXattr(data) => data.inner.monitored,
}
}

Expand Down Expand Up @@ -356,6 +374,8 @@ pub enum FileData {
Chmod(ChmodFileData),
Chown(ChownFileData),
Rename(RenameFileData),
SetXattr(XattrFileData),
RemoveXattr(XattrFileData),
}

impl FileData {
Expand Down Expand Up @@ -407,6 +427,26 @@ impl FileData {
};
FileData::Rename(data)
}
file_activity_type_t::FILE_ACTIVITY_SETXATTR => {
let xattr_name = slice_to_string(
&unsafe { extra_data.xattr }.name[..XATTR_NAME_MAX_LEN as usize],
)?;
FileData::SetXattr(XattrFileData {
inner,
xattr_name,
operation: XattrOperation::Set,
})
}
file_activity_type_t::FILE_ACTIVITY_REMOVEXATTR => {
let xattr_name = slice_to_string(
&unsafe { extra_data.xattr }.name[..XATTR_NAME_MAX_LEN as usize],
)?;
FileData::RemoveXattr(XattrFileData {
inner,
xattr_name,
operation: XattrOperation::Remove,
})
}
invalid => unreachable!("Invalid event type: {invalid:?}"),
};

Expand All @@ -433,6 +473,14 @@ impl From<FileData> for fact_api::file_activity::File {
FileData::RmDir(_) => {
unreachable!("RmDir event reached protobuf conversion");
}
FileData::SetXattr(event) => {
let f_act = fact_api::FileXattrChange::from(event);
fact_api::file_activity::File::Xattr(f_act)
}
FileData::RemoveXattr(event) => {
let f_act = fact_api::FileXattrChange::from(event);
fact_api::file_activity::File::Xattr(f_act)
}
FileData::Unlink(event) => {
let activity = Some(fact_api::FileActivityBase::from(event));
let f_act = fact_api::FileUnlink { activity };
Expand Down Expand Up @@ -465,6 +513,8 @@ impl PartialEq for FileData {
(FileData::Unlink(this), FileData::Unlink(other)) => this == other,
(FileData::Chmod(this), FileData::Chmod(other)) => this == other,
(FileData::Rename(this), FileData::Rename(other)) => this == other,
(FileData::SetXattr(this), FileData::SetXattr(other)) => this == other,
(FileData::RemoveXattr(this), FileData::RemoveXattr(other)) => this == other,
_ => false,
}
}
Expand Down Expand Up @@ -595,6 +645,41 @@ impl PartialEq for RenameFileData {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub enum XattrOperation {
Set,
Remove,
}

#[derive(Debug, Clone, Serialize)]
pub struct XattrFileData {
inner: BaseFileData,
xattr_name: String,
operation: XattrOperation,
}

impl From<XattrFileData> for fact_api::FileXattrChange {
fn from(value: XattrFileData) -> Self {
let activity = fact_api::FileActivityBase::from(value.inner);
let operation = match value.operation {
XattrOperation::Set => fact_api::file_xattr_change::Operation::Set,
XattrOperation::Remove => fact_api::file_xattr_change::Operation::Remove,
};
fact_api::FileXattrChange {
activity: Some(activity),
xattr_name: value.xattr_name,
operation: operation.into(),
}
}
}

#[cfg(test)]
impl PartialEq for XattrFileData {
fn eq(&self, other: &Self) -> bool {
self.xattr_name == other.xattr_name && self.inner == other.inner
}
}

#[cfg(test)]
mod test_utils {
use std::os::raw::c_char;
Expand Down
18 changes: 18 additions & 0 deletions fact/src/metrics/kernel_metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub struct KernelMetrics {
path_mkdir: EventCounter,
path_rmdir: EventCounter,
d_instantiate: EventCounter,
inode_setxattr: EventCounter,
inode_removexattr: EventCounter,
map: PerCpuArray<MapData, metrics_t>,
}

Expand Down Expand Up @@ -61,6 +63,16 @@ impl KernelMetrics {
"Events processed by the d_instantiate LSM hook",
&[], // Labels are not needed since `collect` will add them all
);
let inode_setxattr = EventCounter::new(
"kernel_inode_setxattr_events",
"Events processed by the inode_setxattr LSM hook",
&[], // Labels are not needed since `collect` will add them all
);
let inode_removexattr = EventCounter::new(
"kernel_inode_removexattr_events",
"Events processed by the inode_removexattr LSM hook",
&[], // Labels are not needed since `collect` will add them all
);

file_open.register(reg);
path_unlink.register(reg);
Expand All @@ -70,6 +82,8 @@ impl KernelMetrics {
path_mkdir.register(reg);
path_rmdir.register(reg);
d_instantiate.register(reg);
inode_setxattr.register(reg);
inode_removexattr.register(reg);

KernelMetrics {
file_open,
Expand All @@ -80,6 +94,8 @@ impl KernelMetrics {
path_mkdir,
path_rmdir,
d_instantiate,
inode_setxattr,
inode_removexattr,
map: kernel_metrics,
}
}
Expand Down Expand Up @@ -132,6 +148,8 @@ impl KernelMetrics {
KernelMetrics::refresh_labels(&self.path_mkdir, &metrics.path_mkdir);
KernelMetrics::refresh_labels(&self.path_rmdir, &metrics.path_rmdir);
KernelMetrics::refresh_labels(&self.d_instantiate, &metrics.d_instantiate);
KernelMetrics::refresh_labels(&self.inode_setxattr, &metrics.inode_setxattr);
KernelMetrics::refresh_labels(&self.inode_removexattr, &metrics.inode_removexattr);

Ok(())
}
Expand Down
Loading
Loading