diff --git a/pkg/containers/path_resolver.go b/pkg/containers/path_resolver.go index d7102a19bfe3..2175b416737a 100644 --- a/pkg/containers/path_resolver.go +++ b/pkg/containers/path_resolver.go @@ -10,6 +10,7 @@ import ( "github.com/aquasecurity/tracee/pkg/bucketscache" "github.com/aquasecurity/tracee/pkg/errfmt" "github.com/aquasecurity/tracee/pkg/logger" + "github.com/aquasecurity/tracee/pkg/utils/proc" ) // ContainerPathResolver generates an accessible absolute path from the root @@ -45,36 +46,57 @@ func (cPathRes *ContainerPathResolver) GetHostAbsPath(mountNSAbsolutePath string pids := cPathRes.mountNSPIDsCache.GetBucket(uint32(mountNS)) for _, pid := range pids { - // cap.SYS_PTRACE is needed here. Instead of raising privileges, since - // this is called too frequently, if the needed event is being traced, - // the needed capabilities are added to the Base ring and are always set - // as effective. - // - // (Note: To change this behavior we need a privileged process/server) - - procRootPath := fmt.Sprintf("/proc/%d/root", int(pid)) - - // fs.FS interface requires relative paths, so the '/' prefix should be trimmed. - entries, err := fs.ReadDir(cPathRes.fs, strings.TrimPrefix(procRootPath, "/")) + procRoot, err := cPathRes.getProcessFSRoot(uint(pid)) if err != nil { - // This process is either not alive or we don't have permissions to access. // Try next pid in mount ns to find accessible path to mount ns files. - logger.Debugw( - "Finding mount NS path", - "Unreachable proc root path", procRootPath, - "error", err.Error(), - ) + logger.Debugw("Could not access process FS", "pid", pid, "error", err) continue } - if len(entries) == 0 { - return "", errfmt.Errorf("empty directory") - } - if err == nil { - return fmt.Sprintf("%s%s", procRootPath, mountNSAbsolutePath), nil - } + + return fmt.Sprintf("%s%s", procRoot, mountNSAbsolutePath), nil + } + + // No PIDs registered in this namespace, or couldn't access FS root of any of the PIDs found. + // Try finding one in procfs. + pid, err := proc.GetAnyProcessInNS("mnt", mountNS) + if err != nil { + // Couldn't find a process in this namespace using procfs + return "", ErrContainerFSUnreachable + } + + procRoot, err := cPathRes.getProcessFSRoot(pid) + if err != nil { + logger.Debugw("Could not access process FS", "pid", pid, "error", err) + return "", ErrContainerFSUnreachable + } + + // Register this process in the mount namespace + cPathRes.mountNSPIDsCache.AddBucketItem(uint32(mountNS), uint32(pid)) + + return fmt.Sprintf("%s%s", procRoot, mountNSAbsolutePath), nil +} + +func (cPathRes *ContainerPathResolver) getProcessFSRoot(pid uint) (string, error) { + // cap.SYS_PTRACE is needed here. Instead of raising privileges, since + // this is called too frequently, if the needed event is being traced, + // the needed capabilities are added to the Base ring and are always set + // as effective. + // + // (Note: To change this behavior we need a privileged process/server) + + procRootPath := fmt.Sprintf("/proc/%d/root", pid) + + // fs.FS interface requires relative paths, so the '/' prefix should be trimmed. + entries, err := fs.ReadDir(cPathRes.fs, strings.TrimPrefix(procRootPath, "/")) + if err != nil { + // This process is either not alive or we don't have permissions to access. + return "", errfmt.Errorf("failed accessing process FS root %s: %v", procRootPath, err) + } + if len(entries) == 0 { + return "", errfmt.Errorf("process FS root (%s) is empty", procRootPath) } - return "", ErrContainerFSUnreachable + return procRootPath, nil } var ( diff --git a/pkg/ebpf/c/common/buffer.h b/pkg/ebpf/c/common/buffer.h index 742a277082e5..49ace64fed4f 100644 --- a/pkg/ebpf/c/common/buffer.h +++ b/pkg/ebpf/c/common/buffer.h @@ -467,14 +467,6 @@ statfunc int events_perf_submit(program_data_t *p, long ret) // keep task_info updated bpf_probe_read_kernel(&p->task_info->context, sizeof(task_context_t), &p->event->context.task); - // Get Stack trace - if (p->config->options & OPT_CAPTURE_STACK_TRACES) { - int stack_id = bpf_get_stackid(p->ctx, &stack_addresses, BPF_F_USER_STACK); - if (stack_id >= 0) { - p->event->context.stack_id = stack_id; - } - } - u32 size = sizeof(event_context_t) + sizeof(u8) + p->event->args_buf.offset; // context + argnum + arg buffer size diff --git a/pkg/ebpf/c/maps.h b/pkg/ebpf/c/maps.h index fe415e403484..e515cf81ed50 100644 --- a/pkg/ebpf/c/maps.h +++ b/pkg/ebpf/c/maps.h @@ -265,18 +265,6 @@ struct sys_exit_init_tail { typedef struct sys_exit_init_tail sys_exit_init_tail_t; -// store stack traces -#define MAX_STACK_ADDRESSES 1024 // max amount of diff stack trace addrs to buffer - -struct stack_addresses { - __uint(type, BPF_MAP_TYPE_STACK_TRACE); - __uint(max_entries, MAX_STACK_ADDRESSES); - __type(key, u32); - __type(value, stack_trace_t); // 1 big byte array of the stack addresses -} stack_addresses SEC(".maps"); - -typedef struct stack_addresses stack_addresses_t; - // store fds paths by timestamp struct fd_arg_path_map { __uint(type, BPF_MAP_TYPE_LRU_HASH); diff --git a/pkg/ebpf/c/tracee.bpf.c b/pkg/ebpf/c/tracee.bpf.c index 36c88fad8021..8c4cb72818e2 100644 --- a/pkg/ebpf/c/tracee.bpf.c +++ b/pkg/ebpf/c/tracee.bpf.c @@ -2139,17 +2139,18 @@ int BPF_KPROBE(trace_security_bprm_check) unsigned long inode_nr = get_inode_nr_from_file(file); void *file_path = get_path_str(__builtin_preserve_access_index(&file->f_path)); - syscall_data_t *sys = &p.task_info->syscall_data; + struct pt_regs *task_regs = get_current_task_pt_regs(); + const char *const *argv = NULL; const char *const *envp = NULL; - switch (sys->id) { + switch (get_current_task_syscall_id()) { case SYSCALL_EXECVE: - argv = (const char *const *) sys->args.args[1]; - envp = (const char *const *) sys->args.args[2]; + argv = (const char *const *) get_syscall_arg2(p.event->task, task_regs, false); + envp = (const char *const *) get_syscall_arg3(p.event->task, task_regs, false); break; case SYSCALL_EXECVEAT: - argv = (const char *const *) sys->args.args[2]; - envp = (const char *const *) sys->args.args[3]; + argv = (const char *const *) get_syscall_arg3(p.event->task, task_regs, false); + envp = (const char *const *) get_syscall_arg4(p.event->task, task_regs, false); break; default: break; diff --git a/pkg/ebpf/event_filters.go b/pkg/ebpf/event_filters.go new file mode 100644 index 000000000000..2a984de70e15 --- /dev/null +++ b/pkg/ebpf/event_filters.go @@ -0,0 +1,51 @@ +package ebpf + +import ( + "fmt" + + "github.com/aquasecurity/tracee/pkg/events" + "github.com/aquasecurity/tracee/pkg/filters" + "github.com/aquasecurity/tracee/pkg/logger" +) + +type eventFilterHandler func(t *Tracee, eventFilters []map[string]filters.Filter[*filters.StringFilter]) error + +var eventFilterHandlers = map[events.ID]eventFilterHandler{} + +// handleEventFilters performs eBPF related actions according to event filters. +// For example, an event can use one of its filters to populate eBPF maps, or perhaps +// attach eBPF programs according to the filters. +func (t *Tracee) handleEventFilters() error { + // Iterate through registerd event filter handlers + for eventID, handler := range eventFilterHandlers { + // Make sure this event is selected + if _, err := t.eventsDependencies.GetEvent(eventID); err != nil { + continue + } + + // Construct filters for this event + eventFilters := make([]map[string]filters.Filter[*filters.StringFilter], 0) + for iterator := t.policyManager.CreateAllIterator(); iterator.HasNext(); { + policy := iterator.Next() + policyFilters := policy.DataFilter.GetEventFilters(eventID) + if len(policyFilters) == 0 { + continue + } + eventFilters = append(eventFilters, policyFilters) + } + if len(eventFilters) == 0 { + // No filters for this event + continue + } + + // Call handler + if err := handler(t, eventFilters); err != nil { + if err := t.eventsDependencies.RemoveEvent(eventID); err != nil { + logger.Warnw("Failed to remove event from dependencies manager", "remove reason", "failed handling event filters", "error", err) + } + return fmt.Errorf("failed to handle filters for event %s: %v", events.Core.GetDefinitionByID(eventID).GetName(), err) + } + } + + return nil +} diff --git a/pkg/ebpf/events_pipeline.go b/pkg/ebpf/events_pipeline.go index edfb72416627..909ad9e3c532 100644 --- a/pkg/ebpf/events_pipeline.go +++ b/pkg/ebpf/events_pipeline.go @@ -3,14 +3,10 @@ package ebpf import ( "bytes" "context" - "encoding/binary" "slices" - "strconv" "sync" - "unsafe" "github.com/aquasecurity/tracee/pkg/bufferdecoder" - "github.com/aquasecurity/tracee/pkg/capabilities" "github.com/aquasecurity/tracee/pkg/errfmt" "github.com/aquasecurity/tracee/pkg/events" "github.com/aquasecurity/tracee/pkg/logger" @@ -191,12 +187,6 @@ func (t *Tracee) decodeEvents(ctx context.Context, sourceChan chan []byte) (<-ch continue } - // Add stack trace if needed - var stackAddresses []uint64 - if t.config.Output.StackAddresses { - stackAddresses = t.getStackAddresses(eCtx.StackID) - } - containerInfo := t.containers.GetCgroupInfo(eCtx.CgroupID).Container containerData := trace.Container{ ID: containerInfo.ContainerId, @@ -262,7 +252,7 @@ func (t *Tracee) decodeEvents(ctx context.Context, sourceChan chan []byte) (<-ch evt.ArgsNum = int(argnum) evt.ReturnValue = int(eCtx.Retval) evt.Args = args - evt.StackAddresses = stackAddresses + evt.StackAddresses = nil evt.ContextFlags = flags evt.Syscall = syscall evt.Metadata = nil @@ -641,50 +631,6 @@ func (t *Tracee) sinkEvents(ctx context.Context, in <-chan *trace.Event) <-chan return errc } -// getStackAddresses returns the stack addresses for a given StackID -func (t *Tracee) getStackAddresses(stackID uint32) []uint64 { - stackAddresses := make([]uint64, maxStackDepth) - stackFrameSize := (strconv.IntSize / 8) - - // Lookup the StackID in the map - // The ID could have aged out of the Map, as it only holds a finite number of - // Stack IDs in it's Map - var stackBytes []byte - err := capabilities.GetInstance().EBPF(func() error { - bytes, e := t.StackAddressesMap.GetValue(unsafe.Pointer(&stackID)) - if e != nil { - stackBytes = bytes - } - return e - }) - if err != nil { - logger.Debugw("failed to get StackAddress", "error", err) - return stackAddresses[0:0] - } - - stackCounter := 0 - for i := 0; i < len(stackBytes); i += stackFrameSize { - stackAddresses[stackCounter] = 0 - stackAddr := binary.LittleEndian.Uint64(stackBytes[i : i+stackFrameSize]) - if stackAddr == 0 { - break - } - stackAddresses[stackCounter] = stackAddr - stackCounter++ - } - - // Attempt to remove the ID from the map so we don't fill it up - // But if this fails continue on - err = capabilities.GetInstance().EBPF(func() error { - return t.StackAddressesMap.DeleteKey(unsafe.Pointer(&stackID)) - }) - if err != nil { - logger.Debugw("failed to delete stack address from eBPF map", "error", err) - } - - return stackAddresses[0:stackCounter] -} - // WaitForPipeline waits for results from all error channels. func (t *Tracee) WaitForPipeline(errs ...<-chan error) error { errc := MergeErrors(errs...) diff --git a/pkg/ebpf/processor_funcs.go b/pkg/ebpf/processor_funcs.go index 253b16fd5116..f995e1f85f21 100644 --- a/pkg/ebpf/processor_funcs.go +++ b/pkg/ebpf/processor_funcs.go @@ -120,6 +120,7 @@ func processKernelReadFile(event *trace.Event) error { // processSchedProcessExec processes a sched_process_exec event by capturing the executed file. func (t *Tracee) processSchedProcessExec(event *trace.Event) error { // cache this pid by it's mnt ns + // TODO: don't do this if Tracee is not in the root PID NS? if event.ProcessID == 1 { t.pidsInMntns.ForceAddBucketItem(uint32(event.MountNS), uint32(event.HostProcessID)) } else { @@ -467,7 +468,7 @@ func (t *Tracee) removeContext(event *trace.Event) error { event.Container = trace.Container{} event.Kubernetes = trace.Kubernetes{} event.Syscall = "" - event.StackAddresses = []uint64{} + event.StackAddresses = nil event.ContextFlags = trace.ContextFlags{} event.ThreadEntityId = 0 event.ProcessEntityId = 0 diff --git a/pkg/ebpf/tracee.go b/pkg/ebpf/tracee.go index 0945566e034e..bff1e7e466a5 100644 --- a/pkg/ebpf/tracee.go +++ b/pkg/ebpf/tracee.go @@ -84,8 +84,7 @@ type Tracee struct { bpfModule *bpf.Module probes *probes.ProbeGroup // BPF Maps - StackAddressesMap *bpf.BPFMap - FDArgPathMap *bpf.BPFMap + FDArgPathMap *bpf.BPFMap // Perf Buffers eventsPerfMap *bpf.PerfBuffer // perf buffer for events fileWrPerfMap *bpf.PerfBuffer // perf buffer for file writes @@ -483,15 +482,6 @@ func (t *Tracee) Init(ctx gocontext.Context) error { return errfmt.Errorf("error initializing network capture: %v", err) } - // Get reference to stack trace addresses map - - stackAddressesMap, err := t.bpfModule.GetMap("stack_addresses") - if err != nil { - t.Close() - return errfmt.Errorf("error getting access to 'stack_addresses' eBPF Map %v", err) - } - t.StackAddressesMap = stackAddressesMap - // Get reference to fd arg path map fdArgPathMap, err := t.bpfModule.GetMap("fd_arg_path_map") @@ -518,6 +508,16 @@ func (t *Tracee) Init(ctx gocontext.Context) error { }, } + // Perform extra initializtion steps required by specific events according to their arguments + err = capabilities.GetInstance().EBPF( + func() error { + return t.handleEventFilters() + }, + ) + if err != nil { + return errfmt.WrapError(err) + } + return nil } diff --git a/pkg/events/core.go b/pkg/events/core.go index 8e8c59ad8509..30ab4419d14b 100644 --- a/pkg/events/core.go +++ b/pkg/events/core.go @@ -11447,16 +11447,6 @@ var CoreEvents = map[ID]Definition{ dependencies: Dependencies{ probes: []Probe{ {handle: probes.SecurityBPRMCheck, required: true}, - {handle: probes.SyscallEnter__Internal, required: true}, - }, - tailCalls: []TailCall{ - { - "sys_enter_init_tail", - "sys_enter_init", - []uint32{ - uint32(Execve), uint32(Execveat), - }, - }, }, }, sets: []string{"lsm_hooks", "proc", "proc_life"}, diff --git a/pkg/utils/proc/ns.go b/pkg/utils/proc/ns.go index 73ea445195ea..af46e1c4d4a3 100644 --- a/pkg/utils/proc/ns.go +++ b/pkg/utils/proc/ns.go @@ -191,3 +191,31 @@ func extractNSFromLink(link string) (int, error) { } return ns, nil } + +// GetAnyProcessInNS returns the PID of any process in the given namespace type and number. +// It returns the first process it finds when iterating over /proc that satisfies the request. +// To do so, it requires access to the /proc file system, and CAP_SYS_PTRACE capability. +func GetAnyProcessInNS(nsName string, nsNum int) (uint, error) { + entries, err := os.ReadDir("/proc") + if err != nil { + return 0, errfmt.Errorf("could not read proc dir: %v", err) + } + + for _, entry := range entries { + pid, err := strconv.ParseUint(entry.Name(), 10, 32) + if err != nil { + // Not a PID directory + continue + } + ns, err := GetProcNS(uint(pid), nsName) + if err != nil { + logger.Infow("Failed fetching process namespace", "pid", pid, "namespace", nsName, "error", err) + continue + } + if ns == nsNum { + return uint(pid), nil + } + } + + return 0, errfmt.Errorf("could not find any process in %s namesapce %d", nsName, nsNum) +}