Skip to content
Merged
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: 7 additions & 2 deletions bpf/include/seabee_maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@
#include "bpf/bpf_helpers.h"
#include "shared_rust_types.h"

struct seabee_task_data {
u32 pol_id;
u32 is_pinning; // treated like a bool
};

struct task_storage {
__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
// this flag is required for this map type
__uint(map_flags, BPF_F_NO_PREALLOC);
// must be 4 bytes
__type(key, u32);
// policy id
__type(value, u32);
// seabee task data
__type(value, struct seabee_task_data);
};

struct inode_storage {
Expand Down
80 changes: 69 additions & 11 deletions bpf/include/seabee_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
/// LSM return code for denying an operation.
#define DENY -1

extern struct inode_storage inode_storage;
extern struct task_storage task_storage;
extern struct policy_map policy_map;
extern struct map_to_pol_id map_to_pol_id;
Expand Down Expand Up @@ -55,22 +56,54 @@ static __always_inline struct c_policy_config *get_policy_config(u32 policy_id)
return bpf_map_lookup_elem(&policy_map, &policy_id);
}

/**
* @brief gets the data associated with current task
*
* @return the seabee_task_data for current task or NULL if task is not
* tracked by seabee
*/
static __always_inline struct seabee_task_data *get_task_data()
{
struct task_struct *task = get_task();
return bpf_task_storage_get(&task_storage, task, 0, 0);
}

/**
* @brief gets the policy id for a task
*
* @return the policy id for the task or NO_POL_ID
*/
static __always_inline u32 get_task_pol_id()
{
struct task_struct *task = get_task();
u32 *pol_id = bpf_task_storage_get(&task_storage, task, 0, 0);
if (pol_id) {
return *pol_id;
struct seabee_task_data *data = get_task_data();
if (data) {
return data->pol_id;
} else {
return NO_POL_ID;
}
}

/**
* @brief Checks if this task is associataed with a SeaBee policy id.
* If so, set a flag that determines whether or not the current task is
* executing BPF_OBJ_PIN
*
* @param b a u32 that will be the new flag. 0 for false. 1 for true.
*/
static __always_inline void set_task_pinning(u32 flag)
{
struct task_struct *task = get_task();
struct seabee_task_data *data =
bpf_task_storage_get(&task_storage, task, 0, 0);
if (data && data->pol_id != NO_POL_ID) {
data->is_pinning = flag;
// log
u64 log[3] = { (u64)task->comm, (u64)task->tgid, (u64)flag };
log_generic_msg(LOG_LEVEL_TRACE, LOG_REASON_DEBUG,
"set task %s(%d) pinning to %lu", log, sizeof(log));
}
}

/**
* @brief label a task with a policy id
*
Expand All @@ -81,16 +114,41 @@ static __always_inline void label_task(struct task_struct *task,
const unsigned char *task_name,
u32 policy_id)
{
u32 *label = bpf_task_storage_get(&task_storage, task, &policy_id,
BPF_LOCAL_STORAGE_GET_F_CREATE);
u64 data[3] = { (u64)task_name, (u64)task->tgid, policy_id };
struct seabee_task_data new_data = { policy_id, 0 };
struct seabee_task_data *new_data_blob = bpf_task_storage_get(
&task_storage, task, &new_data, BPF_LOCAL_STORAGE_GET_F_CREATE);
u64 log[3] = { (u64)task_name, (u64)task->tgid, (u64)policy_id };
if (new_data_blob) {
log_generic_msg(LOG_LEVEL_TRACE, LOG_REASON_DEBUG,
"label task %s(%d) as %d", log, sizeof(log));
} else {
log_generic_msg(LOG_LEVEL_ERROR, LOG_REASON_ERROR,
"failed to label task %s(%d) as %d", log, sizeof(log));
}
}

/**
* @brief label an inode with a policy id
*
* @param dentry the dentry associated with the inode
* @param inode the inode to label
* @param policy_id the label to use
*/
static __always_inline void label_inode(struct dentry *dentry,
struct inode *inode, u32 *policy_id)
{
const unsigned char *name = dentry->d_name.name;
u32 *label = bpf_inode_storage_get(&inode_storage, inode, policy_id,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (label) {
u64 data[2] = { (u64)name, *label };
log_generic_msg(LOG_LEVEL_TRACE, LOG_REASON_DEBUG,
"label task %s(%d) as %d", data, sizeof(data));
return;
"label file '%s' as %d", data, sizeof(data));
} else {
u64 data[1] = { (u64)name };
log_generic_msg(LOG_LEVEL_ERROR, LOG_REASON_ERROR,
"failed to label file: %s", data, sizeof(data));
}
log_generic_msg(LOG_LEVEL_ERROR, LOG_REASON_ERROR,
"failed to label task %s(%d) as %d", data, sizeof(data));
}

/**
Expand Down
19 changes: 4 additions & 15 deletions bpf/src/kernel_api/label_file.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,12 @@ int BPF_PROG(seabee_label_target_file, struct inode *dir, struct dentry *dentry)
return ALLOW;
}

// lookup filename, then collect inode and dev
// get policy_id and label inode
u32 *policy_id = bpf_map_lookup_elem(&filename_to_policy_id, &name_copy);

if (policy_id) {
u32 *label = bpf_inode_storage_get(&inode_storage, dentry->d_inode,
policy_id,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (label) {
u64 data[2] = { (u64)name, *label };
log_generic_msg(LOG_LEVEL_TRACE, LOG_REASON_DEBUG,
"label file '%s' as %d", data, sizeof(data));
// we don't want to delete file, just label it
return DENY;
}
u64 data[1] = { (u64)name };
log_generic_msg(LOG_LEVEL_ERROR, LOG_REASON_ERROR,
"failed to label file: %s", data, sizeof(data));
label_inode(dentry, dentry->d_inode, policy_id);
// we don't actually want to delete this inode
return DENY;
}

return ALLOW;
Expand Down
36 changes: 32 additions & 4 deletions bpf/src/seabee/seabee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -693,13 +693,41 @@ int BPF_PROG(seabee_unlabel_map, struct bpf_map *map, int ret)
#endif

/**
* @brief Label an eBPF pin when it is created, using the same label as
* the process that created it
* @brief Used to identify a bpf program is being pinned
*/
SEC("lsm/bpf")
int BPF_PROG(seabee_label_pin, int cmd, union bpf_attr *attr, unsigned int size,
int BPF_PROG(seabee_start_pin, int cmd, union bpf_attr *attr, unsigned int size,
int ret)
{
//TODO: implement
if (cmd == BPF_OBJ_PIN) {
set_task_pinning(true);
}
return ALLOW;
}

/**
* @brief Label an inode associted with a bpf pin
*
* This hook is called when a dentry becomes associted with an inode.
*/
SEC("lsm/d_instantiate")
int BPF_PROG(seabee_label_pin, struct dentry *dentry, struct inode *inode,
int ret)
{
struct seabee_task_data *data = get_task_data();
if (data && data->pol_id != NO_POL_ID && data->is_pinning) {
label_inode(dentry, inode, &data->pol_id);
}
return ALLOW;
}

/**
* @brief Used to identify that a process has finished pinning
*/
SEC("tracepoint/syscalls/sys_exit_bpf")
int BPF_PROG(seabee_stop_pin, struct dentry *dentry, struct inode *inode,
int ret)
{
set_task_pinning(false);
return ALLOW;
}
2 changes: 2 additions & 0 deletions tests/ground_truth.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ seabee_label_process = 0
seabee_label_map = 0
seabee_unlabel_map = 0
seabee_label_pin = 0
seabee_stop_pin = 0
seabee_start_pin = 0
24 changes: 10 additions & 14 deletions tests/src/policy/protect_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,15 @@ pub fn stop_test_tool(child: Child) -> Result<()> {
Ok(())
}

// TODO: fully implement pin protections
// fn deny_remove_pin() -> Result<(), Failed> {
// match std::fs::remove_file(TEST_TOOL_PIN) {
// Err(e) => match e.kind() {
// std::io::ErrorKind::PermissionDenied => Ok(()),
// k => Err(format!("Got: ErrorKind {k}, Expected: PermissionDenied").into()),
// },
// Ok(()) => Err("Got: Ok, Expected: Err".into()),
// }
// }
fn deny_remove_pin() -> Result<(), Failed> {
match std::fs::remove_file(TEST_TOOL_PIN) {
Err(e) => match e.kind() {
std::io::ErrorKind::PermissionDenied => Ok(()),
k => Err(format!("Got: ErrorKind {k}, Expected: PermissionDenied").into()),
},
Ok(()) => Err("Got: Ok, Expected: PermissionDenied".into()),
}
}

#[derive(Debug, Deserialize)]
struct ProgInfo {
Expand Down Expand Up @@ -125,8 +124,5 @@ fn deny_map_access() -> Result<(), Failed> {
}

pub fn tests() -> Vec<Trial> {
vec![
create_test!(deny_map_access),
//create_test!(deny_remove_pin)
]
vec![create_test!(deny_map_access), create_test!(deny_remove_pin)]
}