Skip to content

Commit

Permalink
bpf & pkg: Add selectors for audit ids
Browse files Browse the repository at this point in the history
This commits introduces the "matchLoginUids" selectors, support
filtering based on the audit id of process.

A process is given an audit ID on user login, that is the loginuid.
This ID set during user authentication and handed down to any child
process started by the initial process of the user, even when the user's
identity changes.

the `matchLoginUids` filter supports defining up to four operators,
- `Equal`
- `NotEqual`
- `GreaterThan`
- `LessThan`
which are logically `AND` together.

The loginuid is used to track user's activity. For example, the following
matchLoginUids filter allow to follow operation performed by user id 1000.
```
- matchLoginUids:
  - operator: "Equal"
    values:
    - 1000
```

Signed-off-by: Jianlin Lv <jianlv@ebay.com>
  • Loading branch information
jianlv01 committed Mar 24, 2024
1 parent 10188c6 commit e76ffe8
Show file tree
Hide file tree
Showing 22 changed files with 619 additions and 30 deletions.
2 changes: 1 addition & 1 deletion bpf/lib/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@

struct msg_execve_key {
__u32 pid; // Process TGID
__u8 pad[4];
__u32 auid; // loginuid
__u64 ktime;
}; // All fields aligned so no 'packed' attribute.

Expand Down
1 change: 1 addition & 0 deletions bpf/process/bpf_execve_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ execve_send(struct sched_execve_args *ctx)
#endif
curr->key.pid = p->pid;
curr->key.ktime = p->ktime;
curr->key.auid = p->auid;
curr->nspid = p->nspid;
curr->pkey = event->parent;
if (curr->flags & EVENT_COMMON_FLAG_CLONE) {
Expand Down
5 changes: 1 addition & 4 deletions bpf/process/bpf_exit.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,7 @@ static inline __attribute__((always_inline)) void event_exit_send(void *ctx, __u
exit->common.ktime = ktime_get_ns();

exit->current.pid = tgid;
exit->current.pad[0] = 0;
exit->current.pad[1] = 0;
exit->current.pad[2] = 0;
exit->current.pad[3] = 0;
exit->current.auid = enter->key.auid;
exit->current.ktime = enter->key.ktime;

/**
Expand Down
5 changes: 1 addition & 4 deletions bpf/process/bpf_generic_retkprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,9 @@ BPF_KRETPROBE(generic_retkprobe_event, unsigned long ret)

if (enter) {
e->current.pid = enter->key.pid;
e->current.auid = enter->key.auid;
e->current.ktime = enter->key.ktime;
}
e->current.pad[0] = 0;
e->current.pad[1] = 0;
e->current.pad[2] = 0;
e->current.pad[3] = 0;

e->func_id = config->func_id;
e->common.size = size;
Expand Down
5 changes: 0 additions & 5 deletions bpf/process/generic_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,6 @@ generic_process_init(struct msg_generic_kprobe *e, u8 op, struct event_config *c
e->common.size = 0;
e->common.ktime = ktime_get_ns();

e->current.pad[0] = 0;
e->current.pad[1] = 0;
e->current.pad[2] = 0;
e->current.pad[3] = 0;

e->action = 0;

/**
Expand Down
116 changes: 114 additions & 2 deletions bpf/process/pfilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,52 @@ process_filter_pid(__u32 i, __u32 off, __u32 *f, __u64 ty, __u64 flags,
return __process_filter_pid(ty, flags, sel, pid, enter);
}

static inline __attribute__((always_inline)) int
__process_filter_loginuid(__u32 ty, __u32 sel, struct execve_map_value *enter)
{
__u32 auid;

auid = enter->key.auid;

switch (ty) {
case op_filter_lt:
if (sel > auid)
return PFILTER_ACCEPT;
break;
case op_filter_gt:
if (sel < auid)
return PFILTER_ACCEPT;
break;
case op_filter_eq:
if (sel == auid)
return PFILTER_ACCEPT;
break;
case op_filter_neq:
if (sel != auid)
return PFILTER_ACCEPT;
break;
default:
return PFILTER_REJECT;
}
return PFILTER_REJECT;
}

static inline __attribute__((always_inline)) int
process_filter_loginuid(__u32 i, __u32 off, __u32 *f, __u64 ty, __u64 flags,
struct execve_map_value *enter, struct msg_ns *n,
struct msg_capabilities *c)
{
__u32 sel;
__u32 o = off;

o = o / 4;
asm volatile("%[o] &= 0x3ff;\n" ::[o] "+r"(o)
:);
sel = f[o];

return __process_filter_loginuid(ty, sel, enter);
}

static inline __attribute__((always_inline)) int
process_filter_namespace(__u32 i, __u32 off, __u32 *f, __u64 ty, __u64 nsid,
struct execve_map_value *enter, struct msg_ns *n,
Expand Down Expand Up @@ -310,7 +356,7 @@ selector_match(__u32 *f, __u32 index, __u64 ty, __u64 flags, __u64 len,
int res1 = 0, res2 = 0, res3 = 0, res4 = 0;

/* For NotIn op we AND results so default to 1 so we fallthru open */
if (ty == op_filter_notin)
if (ty == op_filter_notin || ty == op_filter_neq)
res1 = res2 = res3 = res4 = 1;

/* Unrolling this loop was problematic for clang so rather
Expand Down Expand Up @@ -342,7 +388,7 @@ selector_match(__u32 *f, __u32 index, __u64 ty, __u64 flags, __u64 len,
res1 = process_filter(0, index, f, ty, flags, enter, n, c);
index = next_pid_value(index, f, ty);

if (ty == op_filter_notin)
if (ty == op_filter_notin || ty == op_filter_neq)
return res1 & res2 & res3 & res4;
else
return res1 | res2 | res3 | res4;
Expand Down Expand Up @@ -374,13 +420,59 @@ struct nc_filter {
u32 value; /* contains all namespaces to monitor (i.e. bit 0 is for ns_uts, bit 1 for ns_ipc etc.) */
} __attribute__((packed));

struct loginuid_filter {
u32 op;
u32 len; /* number of values */
u32 val[]; /* values */
} __attribute__((packed));

#define VALUES_MASK 0x1f /* max 4 values with 4 bytes each | 0x1f == 31 */

/* If you update the value of NUM_NS_FILTERS_SMALL below you should
* also update parseMatchNamespaces() in kernel.go
*/
#define NUM_NS_FILTERS_SMALL 4

static inline __attribute__((always_inline)) int
__process_filter_loginuids(__u32 *f, __u32 *index, struct execve_map_value *enter) {
int res = PFILTER_ACCEPT;
struct loginuid_filter *loginuid;

loginuid = (struct loginuid_filter *)((u64)f + (*index & INDEX_MASK));
*index += sizeof(struct loginuid_filter); /* 4: op */
res = selector_match(f, *index, loginuid->op, 0, loginuid->len,
enter, NULL, NULL, &process_filter_loginuid);

*index += ((loginuid->len * sizeof(loginuid->val[0])) & VALUES_MASK);

if (res == PFILTER_REJECT)
return res;

return res;
}

static inline __attribute__((always_inline)) int
process_filter_loginuids(__u32 *f, __u32 index, struct execve_map_value *enter, int len) {
int i = 0;
int loginuid_len = 0;
int res = PFILTER_ACCEPT;
struct loginuid_filter *loginuid;

#pragma unroll
for (i = 0; i < 4; i++) {
loginuid = (struct loginuid_filter *)((u64)f + (index & INDEX_MASK));
index += sizeof(struct loginuid_filter); /* 4: op */
res = selector_match(f, index, loginuid->op, 0, loginuid->len, enter, NULL, NULL, &process_filter_loginuid);

index += ((loginuid->len * sizeof(loginuid->val[0])) & VALUES_MASK);
loginuid_len += sizeof(struct loginuid_filter) + ((loginuid->len * sizeof(loginuid->val[0])) & VALUES_MASK);

if (res == PFILTER_REJECT || loginuid_len >= len)
return res;
}
return res;
}

static inline __attribute__((always_inline)) int
selector_process_filter(__u32 *f, __u32 index, struct execve_map_value *enter,
struct msg_selector_data *sel, struct msg_ns *n,
Expand Down Expand Up @@ -408,6 +500,10 @@ selector_process_filter(__u32 *f, __u32 index, struct execve_map_value *enter,
/* selector section offset by reading the relative offset in the array */
index += *(__u32 *)((__u64)f + (index & INDEX_MASK));
index &= INDEX_MASK;

len = *(__u32 *)((__u64)f +
(index &
INDEX_MASK));
index += 4; /* skip selector size field */

/* matchPid */
Expand Down Expand Up @@ -513,6 +609,22 @@ selector_process_filter(__u32 *f, __u32 index, struct execve_map_value *enter,
return res;
#endif

/* matchLoginuids */
len = *(__u32 *)((__u64)f +
(index &
INDEX_MASK)); /* (sizeof(LoginUid1) + sizeof(LoginUid2) + ... + 4) */
index += 4; /* 4: LoginUid1 header */
len -= 4;

if (len > 0) {
res = process_filter_loginuids(f, index, enter, len);
if (res == PFILTER_REJECT)
return res;

index += (len & VALUES_MASK);
}
if (res == PFILTER_REJECT)
return res;
return res;
}

Expand Down
2 changes: 2 additions & 0 deletions bpf/process/types/basic.h
Original file line number Diff line number Diff line change
Expand Up @@ -1768,6 +1768,8 @@ selector_arg_offset(__u8 *f, struct msg_generic_kprobe *e, __u32 selidx,
seloff += *(__u32 *)((__u64)f + (seloff & INDEX_MASK));
/* skip the matchCapabilityChanges by reading its length */
seloff += *(__u32 *)((__u64)f + (seloff & INDEX_MASK));
/* skip the matchLoginuids by reading its length */
seloff += *(__u32 *)((__u64)f + (seloff & INDEX_MASK));
}

/* Making binary selectors fixes size helps on some kernels */
Expand Down
24 changes: 24 additions & 0 deletions docs/content/en/docs/concepts/tracing-policy/selectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ A `TracingPolicy` can contain from 0 to 5 selectors. A selector is composed of
- [`matchCapabilityChanges`](#capability-changes-filter): filter on Linux capabilities changes.
- [`matchActions`](#actions-filter): apply an action on selector matching.
- [`matchReturnActions`](#return-actions-filter): apply an action on return selector matching.
- [`matchLoginUids`](#auid-filter): filter on audit ID.

## Arguments filter

Expand Down Expand Up @@ -1541,3 +1542,26 @@ exist.
Return actions filters are a list of actions that execute when an return selector
matches. They are defined under `matchReturnActions` and currently support all
the [Actions filter](#actions-filter) `action` types.

## Auid filter

Auids filters can be specified under the `matchLoginUids` field and provide filtering
based on the value of audit id of the process. For example, the following
`matchLoginUids` filter tells the BPF code that observe only hooks for which the
audit id is not equal to `-1`:

```yaml
- matchLoginUids:
- operator: "NotEqual"
values:
- 4294967295
```

The available operators for `matchLoginUids` are:
- `Equal`
- `NotEqual`
- `GreaterThan`
- `LessThan`

In a selector, the `matchLoginUids` filter supports defining up to four operators,
which are logically `AND` together.
2 changes: 1 addition & 1 deletion pkg/api/processapi/processapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ type MsgExec struct {

type MsgExecveKey struct {
Pid uint32 `align:"pid"`
Pad uint32 `align:"pad"`
Auid uint32 `align:"auid"`
Ktime uint64 `align:"ktime"`
}

Expand Down
12 changes: 6 additions & 6 deletions pkg/grpc/exec/exec_test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func CreateEvents[EXEC notify.Message, EXIT notify.Message](Pid uint32, Ktime ui
},
Parent: tetragonAPI.MsgExecveKey{
Pid: 0,
Pad: 0,
Auid: 0,
Ktime: 0,
},
ParentFlags: 0,
Expand Down Expand Up @@ -140,7 +140,7 @@ func CreateEvents[EXEC notify.Message, EXIT notify.Message](Pid uint32, Ktime ui
},
Parent: tetragonAPI.MsgExecveKey{
Pid: 1,
Pad: 0,
Auid: 0,
Ktime: 0,
},
ParentFlags: 0,
Expand Down Expand Up @@ -181,7 +181,7 @@ func CreateEvents[EXEC notify.Message, EXIT notify.Message](Pid uint32, Ktime ui
},
Parent: tetragonAPI.MsgExecveKey{
Pid: ParentPid,
Pad: 0,
Auid: 0,
Ktime: ParentKtime,
},
ParentFlags: 0,
Expand Down Expand Up @@ -215,7 +215,7 @@ func CreateEvents[EXEC notify.Message, EXIT notify.Message](Pid uint32, Ktime ui
},
ProcessKey: tetragonAPI.MsgExecveKey{
Pid: Pid,
Pad: 0,
Auid: 0,
Ktime: Ktime,
},
Info: tetragonAPI.MsgExitInfo{
Expand All @@ -241,7 +241,7 @@ func CreateCloneEvents[CLONE notify.Message, EXIT notify.Message](Pid uint32, Kt
},
Parent: tetragonAPI.MsgExecveKey{
Pid: ParentPid,
Pad: 0,
Auid: 0,
Ktime: ParentKtime,
},
PID: Pid,
Expand All @@ -263,7 +263,7 @@ func CreateCloneEvents[CLONE notify.Message, EXIT notify.Message](Pid uint32, Kt
},
ProcessKey: tetragonAPI.MsgExecveKey{
Pid: Pid,
Pad: 0,
Auid: 0,
Ktime: Ktime,
},
Info: tetragonAPI.MsgExitInfo{
Expand Down
Loading

0 comments on commit e76ffe8

Please sign in to comment.