Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat : process arguments matching #1869

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
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
37 changes: 37 additions & 0 deletions KubeArmor/BPF/common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright 2024 Authors of KubeArmor */
/* This module contains the common structures shared by lsm and system monitor*/
# include "throttling.h"
#ifndef __COMMON_H
#define __COMMON_H
#define MAX_ENTRIES 10240
#define MAX_ARGUMENT_SIZE 256
#define MAX_STR_ARR_ELEM 20

// arguments matching

// values stored for argument map
struct argVal{
Aryan-sharma11 marked this conversation as resolved.
Show resolved Hide resolved
char argsArray[80];
};
struct cmd_args_key {
u64 tgid ;
u64 ind;
};

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, MAX_ENTRIES);
__type(key, struct cmd_args_key);
__type(value, struct argVal);
__uint(pinning, LIBBPF_PIN_BY_NAME);
} args_store SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1); // Adjust max_entries based on expected usage
__type(key, u32);
__type(value, struct argVal); // Store the args in this struct
} cmd_args_buf SEC(".maps");

#endif /* __COMMON_H */
109 changes: 61 additions & 48 deletions KubeArmor/BPF/enforcer.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@

#include "shared.h"
#include "syscalls.h"
#include "common.h"

SEC("lsm/bprm_check_security")
int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) {
struct task_struct *t = (struct task_struct *)bpf_get_current_task();
event *task_info;
int retval = ret;

bool match = false;

// no of arguments
unsigned int num_of_args = BPF_CORE_READ(bprm , argc);
bool argmatch = false;

bool match = false;
struct outer_key okey;
get_outer_key(&okey, t);

u32 *inner = bpf_map_lookup_elem(&kubearmor_containers, &okey);

if (!inner) {
Expand All @@ -38,7 +41,7 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) {
bufs_k *pk = bpf_map_lookup_elem(&bufk, &two);
if (pk == NULL)
return 0;

bpf_map_update_elem(&bufk, &two, z, BPF_ANY);
// Extract full path from file structure provided by LSM Hook
bufs_t *path_buf = get_buf(PATH_BUFFER);
if (path_buf == NULL)
Expand Down Expand Up @@ -79,58 +82,59 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) {
if (src_ptr == NULL)
fromSourceCheck = false;


if (fromSourceCheck) {

bpf_probe_read_str(store->source, MAX_STRING_SIZE, src_ptr);

val = bpf_map_lookup_elem(inner, store);
if (val && (val->processmask & RULE_EXEC)) {
match = true;
goto decision;
}

#pragma unroll
for (int i = 0; i < 64; i++) {
if (store->path[i] == '\0')
break;

if (store->path[i] == '/') {
bpf_map_update_elem(&bufk, &two, z, BPF_ANY);

match = false;

bpf_probe_read_str(pk->path, i + 2, store->path);
// Check Subdir with From Source
bpf_probe_read_str(pk->source, MAX_STRING_SIZE, store->source);
dirval = bpf_map_lookup_elem(inner, pk);
if (dirval) {
if ((dirval->processmask & RULE_DIR) &&
(dirval->processmask & RULE_EXEC)) {
match = true;
if ((dirval->processmask & RULE_RECURSIVE) &&
(~dirval->processmask &
RULE_HINT)) { // true directory match and not a hint suggests
// there are no possibility of child dir
val = dirval;
goto decision;
} else if (dirval->processmask &
RULE_RECURSIVE) { // It's a directory match but also a
// hint, it's possible that a
// subdirectory exists that can also
// match so we continue the loop to look
// for a true match in subdirectories
recursivebuthint = true;
val = dirval;
#pragma unroll
for (int i = 0; i < 64; i++) {
if (store->path[i] == '\0')
break;

if (store->path[i] == '/') {
bpf_map_update_elem(&bufk, &two, z, BPF_ANY);

match = false;

bpf_probe_read_str(pk->path, i + 2, store->path);
// Check Subdir with From Source
bpf_probe_read_str(pk->source, MAX_STRING_SIZE, store->source);
dirval = bpf_map_lookup_elem(inner, pk);
if (dirval) {
if ((dirval->processmask & RULE_DIR) &&
(dirval->processmask & RULE_EXEC)) {
match = true;
if ((dirval->processmask & RULE_RECURSIVE) &&
(~dirval->processmask &
RULE_HINT)) { // true directory match and not a hint suggests
// there are no possibility of child dir
val = dirval;

goto decision;
} else if (dirval->processmask &
RULE_RECURSIVE) { // It's a directory match but also a
// hint, it's possible that a
// subdirectory exists that can also
// match so we continue the loop to look
// for a true match in subdirectories
recursivebuthint = true;
val = dirval;
} else {
continue; // We continue the loop to see if we have more nested
// directories and set match to false
}
}
} else {
continue; // We continue the loop to see if we have more nested
// directories and set match to false
break;
}
}
} else {
break;
}
}
}

if (recursivebuthint) {
match = true;
goto decision;
Expand Down Expand Up @@ -162,6 +166,7 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) {
val = bpf_map_lookup_elem(inner, pk);

if (val && (val->processmask & RULE_EXEC)) {

match = true;
goto decision;
}
Expand Down Expand Up @@ -206,7 +211,6 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) {
}
}
}

if (recursivebuthint) {
match = true;
goto decision;
Expand All @@ -217,11 +221,21 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) {
}
}

decision:

decision:
if (match) {
if (val && (val->processmask & RULE_ARGSET)){
argmatch = matchArguments( num_of_args , &okey , store , pk);
if(argmatch){
// if arguments matches allow the process to be executed
return 0;
}
}

if (val && (val->processmask & RULE_OWNER)) {
if (!is_owner(bprm->file)) {
if((val->processmask & RULE_ARGSET) && argmatch){
return 0;
}
retval = -EPERM;
} else {
// Owner Only Rule Match, No need to enforce
Expand All @@ -232,7 +246,6 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) {
retval = -EPERM;
}
}

if (retval == -EPERM) {
goto ringbuf;
}
Expand Down
94 changes: 92 additions & 2 deletions KubeArmor/BPF/shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "throttling.h"
#include "common.h"

char LICENSE[] SEC("license") = "Dual BSD/GPL";
#define EPERM 13
Expand Down Expand Up @@ -47,6 +48,7 @@ typedef struct bufkey {
char source[MAX_STRING_SIZE];
} bufs_k;


#undef container_of
#define container_of(ptr, type, member) \
({ \
Expand Down Expand Up @@ -74,6 +76,33 @@ struct {
__uint(max_entries, 3);
} bufk SEC(".maps");

typedef struct argskey{
struct outer_key okey;
bufs_k store;
char arg[MAX_STRING_SIZE];
} arg_bufs_k;

//-- Maps and structs for argument matching--//
// argument matching

// Key for argument map => okey+bufkey+argname

struct {
__uint(type,BPF_MAP_TYPE_PERCPU_ARRAY);
__type(key, u32);
__type(value, arg_bufs_k);
__uint(max_entries, 1);
} args_bufk SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 100);
__type(key, arg_bufs_k); // Composite key of okey+bufkey+argname
__type(value, u8); // Value is a u8 integer
__uint(pinning, LIBBPF_PIN_BY_NAME);
} kubearmor_arguments SEC(".maps");

//--------------------------------------------//

typedef struct {
u64 ts;

Expand Down Expand Up @@ -109,14 +138,15 @@ struct {
#define RULE_RECURSIVE 1 << 5
#define RULE_HINT 1 << 6
#define RULE_DENY 1 << 7
#define RULE_ARGSET 1 << 8

#define MASK_WRITE 0x00000002
#define MASK_READ 0x00000004
#define MASK_APPEND 0x00000008

struct data_t {
u8 processmask;
u8 filemask;
u16 processmask;
u16 filemask;
};

enum
Expand Down Expand Up @@ -715,6 +745,66 @@ static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id,
bpf_ringbuf_submit(task_info, 0);
return retval;
}
static inline bool matchArguments( unsigned int num_of_args , struct outer_key *okey , bufs_k *store , bufs_k *pk ) {

bool argmatch = false;
unsigned int *x ;

unsigned int argKey;
struct argVal *argval ;

u32 arg_k = 0;
arg_bufs_k *a_key = bpf_map_lookup_elem(&args_bufk, &arg_k);
if (a_key == NULL)
return 0;

// clearing to avoid processing garbage values
__builtin_memset(&a_key->okey, 0, sizeof(a_key->okey));
__builtin_memset(&a_key->store, 0, sizeof(a_key->store));

bpf_probe_read(&a_key->okey.mnt_ns, sizeof(okey->mnt_ns) , &okey->mnt_ns);
bpf_probe_read(&a_key->okey.pid_ns, sizeof(okey->pid_ns) , &okey->pid_ns);
bpf_probe_read_str(&a_key->store.path, sizeof(store->path) , &store->path);

struct cmd_args_key cmd_args_buf_k;
cmd_args_buf_k.tgid = bpf_get_current_pid_tgid();

if (pk->path[0] == '\0') {
// pk->path[0] will be null for fromSource rules
bpf_probe_read_str(&a_key->store.source, sizeof(store->source) , store->source);
}

for( u8 i = 0 ; i< num_of_args && i < 16; i++ ){
cmd_args_buf_k.ind = i;
bpf_printk(" tgid %llu ind %d" , cmd_args_buf_k.tgid , cmd_args_buf_k.ind);

argval = bpf_map_lookup_elem(&args_store , &cmd_args_buf_k);

bpf_printk("argval %d %s " , argval , argval->argsArray);
if(argval){
__builtin_memset(a_key->arg, 0, sizeof(a_key->arg));
bpf_probe_read_str(&a_key->arg, sizeof(a_key->arg), &argval->argsArray);
x = bpf_map_lookup_elem(&kubearmor_arguments ,a_key);
if (x){
argmatch = true;
}
else {
argmatch = false;
if (i != 0) {
break;
}
}
}
else {

struct argVal try ;
int y = bpf_map_update_elem(&args_store, &cmd_args_buf_k, &try, BPF_NOEXIST);
bpf_printk("update elem return %d" , y);
}
}

return argmatch;
}

/*
How do we check what to deny or not?
Expand Down
Loading
Loading