diff --git a/bpf/alignchecker/bpf_alignchecker.c b/bpf/alignchecker/bpf_alignchecker.c index a98bebee4ae..32f35f1b9d5 100644 --- a/bpf/alignchecker/bpf_alignchecker.c +++ b/bpf/alignchecker/bpf_alignchecker.c @@ -14,7 +14,6 @@ struct msg_exit _msg_exit; struct msg_test _msg_test; struct msg_cgroup_event _msg_cgroup_event; struct msg_cred _msg_cred; -struct msg_cred_minimal _msg_cred_minimal; // from maps struct event _event; diff --git a/bpf/lib/bpf_cred.h b/bpf/lib/bpf_cred.h index ea3bcd2fc80..1c6f3837b1b 100644 --- a/bpf/lib/bpf_cred.h +++ b/bpf/lib/bpf_cred.h @@ -45,30 +45,6 @@ struct msg_cred { struct msg_user_namespace user_ns; } __attribute__((packed)); -/* - * TODO: we have msg_cred above that includes the full credentials - * definition and is used in kprobes. - * However since we are also moving to use creds for - * exec events, so let's do it step by step, as we already - * have the capabilities in execve inside the execve_map and - * the user space cache, so we start with this minimal - * credential object that holds only uids/gids, then we follow - * up by moving the capabilities into it, and make this cred - * the new storage in execve_map and user space process cache. - */ -struct msg_cred_minimal { - __u32 uid; - __u32 gid; - __u32 suid; - __u32 sgid; - __u32 euid; - __u32 egid; - __u32 fsuid; - __u32 fsgid; - __u32 securebits; - __u32 pad; -} __attribute__((packed)); - /* Execution and cred related flags shared with userspace */ #define EXEC_SETUID 0x01 /* This is a set-user-id execution */ #define EXEC_SETGID 0x02 /* This is a set-group-id execution */ diff --git a/bpf/lib/process.h b/bpf/lib/process.h index bdadea67115..4dc7aac4b05 100644 --- a/bpf/lib/process.h +++ b/bpf/lib/process.h @@ -288,9 +288,7 @@ struct msg_execve_event { struct msg_k8s kube; struct msg_execve_key parent; __u64 parent_flags; - struct msg_capabilities caps; - /* TODO: convert this msg_cred_minimal to msg_cred and include caps above inside */ - struct msg_cred_minimal creds; + struct msg_cred creds; struct msg_ns ns; struct msg_execve_key cleanup_key; /* if add anything above please also update the args of diff --git a/bpf/process/bpf_execve_event.c b/bpf/process/bpf_execve_event.c index 712fe4091ed..85600f8d80a 100644 --- a/bpf/process/bpf_execve_event.c +++ b/bpf/process/bpf_execve_event.c @@ -225,9 +225,7 @@ event_execve(struct sched_execve_args *ctx) BPF_CORE_READ_INTO(&event->kube.net_ns, task, nsproxy, net_ns, ns.inum); - // At this time objective and subjective creds are same - get_current_subj_caps(&event->caps, task); - get_current_subj_creds_uids(&event->creds, task); + get_current_subj_creds(&event->creds, task); get_namespaces(&event->ns, task); p->flags |= __event_get_cgroup_info(task, &event->kube); @@ -296,9 +294,9 @@ execve_send(struct sched_execve_args *ctx) #endif #ifdef __CAP_CHANGES_FILTER if (init_curr) { - curr->caps.permitted = event->caps.permitted; - curr->caps.effective = event->caps.effective; - curr->caps.inheritable = event->caps.inheritable; + curr->caps.permitted = event->creds.caps.permitted; + curr->caps.effective = event->creds.caps.effective; + curr->caps.inheritable = event->creds.caps.inheritable; } #endif // buffer can be written at clone stage with parent's info, if previous @@ -327,8 +325,7 @@ execve_send(struct sched_execve_args *ctx) size = validate_msg_execve_size( sizeof(struct msg_common) + sizeof(struct msg_k8s) + sizeof(struct msg_execve_key) + sizeof(__u64) + - sizeof(struct msg_capabilities) + - sizeof(struct msg_cred_minimal) + sizeof(struct msg_ns) + + sizeof(struct msg_cred) + sizeof(struct msg_ns) + sizeof(struct msg_execve_key) + p->size); perf_event_output_metric(ctx, MSG_OP_EXECVE, &tcpmon_map, BPF_F_CURRENT_CPU, event, size); return 0; diff --git a/bpf/process/bpf_fork.c b/bpf/process/bpf_fork.c index f4b8e0f1215..32c274504ac 100644 --- a/bpf/process/bpf_fork.c +++ b/bpf/process/bpf_fork.c @@ -22,6 +22,7 @@ BPF_KPROBE(event_wake_up_new_task, struct task_struct *task) { struct execve_map_value *curr, *parent; struct msg_clone_event msg; + struct msg_capabilities caps; u64 msg_size = sizeof(struct msg_clone_event); u32 tgid = 0; @@ -56,6 +57,15 @@ BPF_KPROBE(event_wake_up_new_task, struct task_struct *task) memcpy(&curr->bin, &parent->bin, sizeof(curr->bin)); curr->pkey = parent->key; + /* Store the thread leader capabilities so we can check later + * before the execve hook point if they changed or not. + * This needs to be converted later to credentials. + */ + get_current_subj_caps(&caps, task); + curr->caps.permitted = caps.permitted; + curr->caps.effective = caps.effective; + curr->caps.inheritable = caps.inheritable; + /* Setup the msg_clone_event and sent to the user. */ msg.common.op = MSG_OP_CLONE; msg.common.size = msg_size; diff --git a/bpf/process/bpf_process_event.h b/bpf/process/bpf_process_event.h index c7eb5a7d353..2b381b2f6f7 100644 --- a/bpf/process/bpf_process_event.h +++ b/bpf/process/bpf_process_event.h @@ -468,7 +468,7 @@ get_current_subj_caps(struct msg_capabilities *msg, struct task_struct *task) } static inline __attribute__((always_inline)) void -get_current_subj_creds_uids(struct msg_cred_minimal *info, struct task_struct *task) +get_current_subj_creds(struct msg_cred *info, struct task_struct *task) { const struct cred *cred; @@ -484,6 +484,9 @@ get_current_subj_creds_uids(struct msg_cred_minimal *info, struct task_struct *t probe_read(&info->fsuid, sizeof(__u32), _(&cred->fsuid)); probe_read(&info->fsgid, sizeof(__u32), _(&cred->fsgid)); probe_read(&info->securebits, sizeof(__u32), _(&cred->securebits)); + + /* Get capabilities */ + __get_caps(&info->caps, cred); } static inline __attribute__((always_inline)) void diff --git a/pkg/alignchecker/alignchecker.go b/pkg/alignchecker/alignchecker.go index cd5280b30dc..ca159b8fe22 100644 --- a/pkg/alignchecker/alignchecker.go +++ b/pkg/alignchecker/alignchecker.go @@ -32,8 +32,7 @@ func CheckStructAlignments(pathToObj string) error { "msg_execve_key": {processapi.MsgExecveKey{}}, "execve_map_value": {execvemap.ExecveValue{}}, "msg_cgroup_event": {processapi.MsgCgroupEvent{}}, - "msg_cred": {tracingapi.MsgGenericCred{}}, - "msg_cred_minimal": {processapi.MsgGenericCredMinimal{}}, + "msg_cred": {processapi.MsgGenericCred{}}, // configuration "event_config": {tracingapi.EventConfig{}}, diff --git a/pkg/api/processapi/processapi.go b/pkg/api/processapi/processapi.go index dffcec3da15..e3d9c2fcc7e 100644 --- a/pkg/api/processapi/processapi.go +++ b/pkg/api/processapi/processapi.go @@ -90,7 +90,7 @@ type MsgK8sUnix struct { Docker string } -type MsgGenericCredMinimal struct { +type MsgGenericCred struct { Uid uint32 Gid uint32 Suid uint32 @@ -101,6 +101,8 @@ type MsgGenericCredMinimal struct { FSgid uint32 SecureBits uint32 Pad uint32 + Cap MsgCapabilities + UserNs MsgUserNamespace } type MsgExecveEvent struct { @@ -108,8 +110,7 @@ type MsgExecveEvent struct { Kube MsgK8s Parent MsgExecveKey ParentFlags uint64 - Capabilities MsgCapabilities - Creds MsgGenericCredMinimal + Creds MsgGenericCred Namespaces MsgNamespaces CleanupProcess MsgExecveKey } diff --git a/pkg/api/tracingapi/client_kprobe.go b/pkg/api/tracingapi/client_kprobe.go index 8f3fa8d8a4c..87656cf4047 100644 --- a/pkg/api/tracingapi/client_kprobe.go +++ b/pkg/api/tracingapi/client_kprobe.go @@ -271,21 +271,6 @@ func (m MsgGenericKprobeArgNetDev) IsReturnArg() bool { return m.Index == ReturnArgIndex } -type MsgGenericCred struct { - Uid uint32 - Gid uint32 - Suid uint32 - Sgid uint32 - Euid uint32 - Egid uint32 - FSuid uint32 - FSgid uint32 - SecureBits uint32 - Pad uint32 - Cap processapi.MsgCapabilities - UserNs processapi.MsgUserNamespace -} - type MsgGenericKprobeArgCred struct { Index uint64 Uid uint32 diff --git a/pkg/grpc/process_manager_test.go b/pkg/grpc/process_manager_test.go index 2ca53e02c47..b57ca496387 100644 --- a/pkg/grpc/process_manager_test.go +++ b/pkg/grpc/process_manager_test.go @@ -175,16 +175,16 @@ func TestProcessManager_GetProcessExec(t *testing.T) { Common: processapi.MsgCommon{ Ktime: 1234, }, - Creds: processapi.MsgGenericCredMinimal{ + Creds: processapi.MsgGenericCred{ Uid: 1000, Gid: 1000, Euid: 10000, Egid: 10000, - }, - Capabilities: processapi.MsgCapabilities{ - Permitted: 1, - Effective: 1, - Inheritable: 1, + Cap: processapi.MsgCapabilities{ + Permitted: 1, + Effective: 1, + Inheritable: 1, + }, }, }, Process: processapi.MsgProcess{ diff --git a/pkg/process/process.go b/pkg/process/process.go index 433ba761b27..17693404481 100644 --- a/pkg/process/process.go +++ b/pkg/process/process.go @@ -278,9 +278,10 @@ func initProcessInternalExec( } else { parentExecID = GetProcessID(0, 1) } + creds := &event.Msg.Creds execID := GetExecID(&process) protoPod := GetPodInfo(containerID, process.Filename, args, process.NSPID) - apiCaps := caps.GetMsgCapabilities(event.Msg.Capabilities) + apiCaps := caps.GetMsgCapabilities(event.Msg.Creds.Cap) binary := path.GetBinaryAbsolutePath(process.Filename, cwd) apiNs, err := namespace.GetMsgNamespaces(event.Msg.Namespaces) if err != nil { @@ -294,7 +295,6 @@ func initProcessInternalExec( }).Warn("ExecveEvent: parsing namespaces failed") } - creds := &event.Msg.Creds apiCreds := &tetragon.ProcessCredentials{ Uid: &wrapperspb.UInt32Value{Value: creds.Uid}, Gid: &wrapperspb.UInt32Value{Value: creds.Gid}, diff --git a/pkg/sensors/exec/exec_test.go b/pkg/sensors/exec/exec_test.go index a87e14d22dc..bd581ec0013 100644 --- a/pkg/sensors/exec/exec_test.go +++ b/pkg/sensors/exec/exec_test.go @@ -33,6 +33,7 @@ import ( "github.com/cilium/tetragon/pkg/observer/observertesthelper" "github.com/cilium/tetragon/pkg/option" proc "github.com/cilium/tetragon/pkg/process" + "github.com/cilium/tetragon/pkg/reader/caps" "github.com/cilium/tetragon/pkg/reader/namespace" "github.com/cilium/tetragon/pkg/sensors" "github.com/cilium/tetragon/pkg/sensors/base" @@ -260,9 +261,14 @@ func TestEventExecve(t *testing.T) { testNop := testutils.RepoRootPath("contrib/tester-progs/nop") + myCaps := ec.NewCapabilitiesChecker().FromCapabilities(caps.GetCurrentCapabilities()) + myNs := ec.NewNamespacesChecker().FromNamespaces(namespace.GetCurrentNamespace()) + procChecker := ec.NewProcessChecker(). WithBinary(sm.Full(testNop)). - WithArguments(sm.Full("arg1 arg2 arg3")) + WithArguments(sm.Full("arg1 arg2 arg3")). + WithCap(myCaps). + WithNs(myNs) execChecker := ec.NewProcessExecChecker("").WithProcess(procChecker) checker := ec.NewUnorderedEventChecker(execChecker) @@ -892,6 +898,9 @@ func TestExecProcessCredentials(t *testing.T) { } }) + myCaps := ec.NewCapabilitiesChecker().FromCapabilities(caps.GetCurrentCapabilities()) + myNs := ec.NewNamespacesChecker().FromNamespaces(namespace.GetCurrentNamespace()) + if err := exec.Command(testNop).Run(); err != nil { t.Fatalf("Failed to execute test binary: %s\n", err) } @@ -905,10 +914,14 @@ func TestExecProcessCredentials(t *testing.T) { WithGid(0).WithEgid(gid).WithSgid(gid).WithFsgid(gid) procExecChecker := ec.NewProcessChecker(). - WithBinary(sm.Full(testNop)).WithProcessCredentials(creds).WithBinaryProperties(nil) + WithBinary(sm.Full(testNop)).WithProcessCredentials(creds).WithBinaryProperties(nil). + WithCap(myCaps). + WithNs(myNs) procGidExecChecker := ec.NewProcessChecker(). - WithBinary(sm.Full(testNop)).WithProcessCredentials(gidCreds).WithBinaryProperties(nil) + WithBinary(sm.Full(testNop)).WithProcessCredentials(gidCreds).WithBinaryProperties(nil). + WithCap(myCaps). + WithNs(myNs) execChecker := ec.NewProcessExecChecker("exec").WithProcess(procExecChecker) execGidChecker := ec.NewProcessExecChecker("exec").WithProcess(procGidExecChecker) diff --git a/pkg/sensors/exec/procevents/proc_reader.go b/pkg/sensors/exec/procevents/proc_reader.go index 212c52aedff..f225a9e97ce 100644 --- a/pkg/sensors/exec/procevents/proc_reader.go +++ b/pkg/sensors/exec/procevents/proc_reader.go @@ -208,7 +208,7 @@ func pushExecveEvents(p procs) { m.Unix.Msg.Parent.Pid = p.ppid m.Unix.Msg.Parent.Ktime = p.pktime - m.Unix.Msg.Capabilities = processapi.MsgCapabilities{} + m.Unix.Msg.Creds.Cap = processapi.MsgCapabilities{} m.Unix.Msg.Namespaces = processapi.MsgNamespaces{} m.Unix.Process.Size = p.size @@ -244,9 +244,11 @@ func pushExecveEvents(p procs) { m.Unix.Msg.Parent.Pid = p.ppid m.Unix.Msg.Parent.Ktime = p.pktime - m.Unix.Msg.Capabilities.Permitted = p.permitted - m.Unix.Msg.Capabilities.Effective = p.effective - m.Unix.Msg.Capabilities.Inheritable = p.inheritable + caps := processapi.MsgCapabilities{ + Permitted: p.permitted, + Effective: p.effective, + Inheritable: p.inheritable, + } m.Unix.Msg.Namespaces.UtsInum = p.uts_ns m.Unix.Msg.Namespaces.IpcInum = p.ipc_ns @@ -266,9 +268,10 @@ func pushExecveEvents(p procs) { // use euid to be compatible with ps m.Unix.Process.UID = p.uids[1] m.Unix.Process.AUID = p.auid - m.Unix.Msg.Creds = processapi.MsgGenericCredMinimal{ + m.Unix.Msg.Creds = processapi.MsgGenericCred{ Uid: p.uids[0], Euid: p.uids[1], Suid: p.uids[2], FSuid: p.uids[3], Gid: p.gids[0], Egid: p.gids[1], Sgid: p.gids[2], FSgid: p.gids[3], + Cap: caps, } m.Unix.Process.Flags = p.flags | flags m.Unix.Process.Ktime = p.ktime diff --git a/pkg/sensors/tracing/args.go b/pkg/sensors/tracing/args.go index 103c32d3b78..f9a673cf596 100644 --- a/pkg/sensors/tracing/args.go +++ b/pkg/sensors/tracing/args.go @@ -11,7 +11,7 @@ import ( "io" "github.com/cilium/tetragon/pkg/api/dataapi" - "github.com/cilium/tetragon/pkg/api/tracingapi" + processapi "github.com/cilium/tetragon/pkg/api/processapi" api "github.com/cilium/tetragon/pkg/api/tracingapi" gt "github.com/cilium/tetragon/pkg/generictypes" "github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1" @@ -74,7 +74,7 @@ func getTracepointMetaValue(arg *v1alpha1.KProbeArg) int { return 0 } -func getArg(r *bytes.Reader, a argPrinter) tracingapi.MsgGenericKprobeArg { +func getArg(r *bytes.Reader, a argPrinter) api.MsgGenericKprobeArg { var err error switch a.ty { @@ -176,7 +176,7 @@ func getArg(r *bytes.Reader, a argPrinter) tracingapi.MsgGenericKprobeArg { arg.Label = a.label return arg case gt.GenericCredType: - var cred api.MsgGenericCred + var cred processapi.MsgGenericCred var arg api.MsgGenericKprobeArgCred err := binary.Read(r, binary.LittleEndian, &cred) diff --git a/pkg/sensors/tracing/kprobe_threads_test.go b/pkg/sensors/tracing/kprobe_threads_test.go index 8096a787c61..e2a3d78cdbe 100644 --- a/pkg/sensors/tracing/kprobe_threads_test.go +++ b/pkg/sensors/tracing/kprobe_threads_test.go @@ -18,6 +18,7 @@ import ( "github.com/cilium/tetragon/pkg/logger" sm "github.com/cilium/tetragon/pkg/matchers/stringmatcher" "github.com/cilium/tetragon/pkg/observer/observertesthelper" + "github.com/cilium/tetragon/pkg/reader/caps" "github.com/cilium/tetragon/pkg/testutils" tus "github.com/cilium/tetragon/pkg/testutils/sensors" "github.com/stretchr/testify/assert" @@ -90,10 +91,13 @@ spec: cti.AssertPidsTids(t) + myCaps := ec.NewCapabilitiesChecker().FromCapabilities(caps.GetCurrentCapabilities()) + parentCheck := ec.NewProcessChecker(). WithBinary(sm.Suffix("threads-tester")). WithPid(cti.ParentPid). - WithTid(cti.ParentTid) + WithTid(cti.ParentTid). + WithCap(myCaps) execCheck := ec.NewProcessExecChecker(""). WithProcess(parentCheck) @@ -104,7 +108,8 @@ spec: child1Checker := ec.NewProcessChecker(). WithBinary(sm.Suffix("threads-tester")). WithPid(cti.Child1Pid). - WithTid(cti.Child1Tid) + WithTid(cti.Child1Tid). + WithCap(myCaps) child1KpChecker := ec.NewProcessKprobeChecker(""). WithProcess(child1Checker).WithParent(parentCheck) @@ -112,7 +117,8 @@ spec: thread1Checker := ec.NewProcessChecker(). WithBinary(sm.Suffix("threads-tester")). WithPid(cti.Thread1Pid). - WithTid(cti.Thread1Tid) + WithTid(cti.Thread1Tid). + WithCap(myCaps) thread1KpChecker := ec.NewProcessKprobeChecker(""). WithProcess(thread1Checker).WithParent(parentCheck)