From 811d3648de6aa0a6d06ae30f1819da1c40a90bac Mon Sep 17 00:00:00 2001 From: gui774ume Date: Fri, 28 Feb 2025 22:31:28 +0100 Subject: [PATCH] [CWS] review changes --- docs/cloud-workload-security/backend_linux.md | 40 +++---- .../backend_linux.schema.json | 16 +-- .../linux_expressions.md | 42 ++++---- docs/cloud-workload-security/secl_linux.json | 80 +++++++------- .../ebpf/c/include/constants/custom.h | 3 +- .../ebpf/c/include/events_definition.h | 2 +- .../ebpf/c/include/helpers/approvers.h | 23 +++- pkg/security/ebpf/c/include/helpers/sysctl.h | 26 +++-- pkg/security/ebpf/c/include/maps.h | 1 + .../ebpf/c/include/structs/syscalls.h | 4 + pkg/security/probe/kfilters/approvers.go | 1 + .../probe/kfilters/capabilities_linux.go | 1 + pkg/security/probe/kfilters/sysctl.go | 41 +++++++ pkg/security/secl/model/accessors_unix.go | 100 +++++++++--------- pkg/security/secl/model/model_unix.go | 16 +-- .../secl/model/unmarshallers_linux.go | 38 +++---- pkg/security/secl/schemas/sysctl.schema.json | 9 +- pkg/security/serializers/serializers_base.go | 16 +-- .../serializers_base_linux_easyjson.go | 40 +++---- pkg/security/serializers/serializers_linux.go | 16 +-- pkg/security/tests/sysctl_test.go | 48 ++++++++- pkg/security/utils/cgroup.go | 5 +- 22 files changed, 344 insertions(+), 224 deletions(-) create mode 100644 pkg/security/probe/kfilters/sysctl.go diff --git a/docs/cloud-workload-security/backend_linux.md b/docs/cloud-workload-security/backend_linux.md index 6983ddeffd531..7dc5ba87fa9da 100644 --- a/docs/cloud-workload-security/backend_linux.md +++ b/docs/cloud-workload-security/backend_linux.md @@ -1638,21 +1638,21 @@ CSM Threats event for Linux systems have the following JSON schema: "type": "boolean", "description": "name_truncated indicates if the name field is truncated" }, - "current_value": { + "value": { "type": "string", - "description": "current_value is the value of the system control parameter before the event" + "description": "value is the new and/or current value for the system control parameter depending on the action type" }, - "current_value_truncated": { + "value_truncated": { "type": "boolean", - "description": "current_value_truncated indicates if the current_value field is truncated" + "description": "value_truncated indicates if the value field is truncated" }, - "new_value": { + "old_value": { "type": "string", - "description": "new_value is the newly set value of the system control" + "description": "old_value is the old value of the system control parameter" }, - "new_value_truncated": { + "old_value_truncated": { "type": "boolean", - "description": "new_value_truncated indicates if the new_value field is truncated" + "description": "old_value_truncated indicates if the old_value field is truncated" } }, "additionalProperties": false, @@ -4415,21 +4415,21 @@ CSM Threats event for Linux systems have the following JSON schema: "type": "boolean", "description": "name_truncated indicates if the name field is truncated" }, - "current_value": { + "value": { "type": "string", - "description": "current_value is the value of the system control parameter before the event" + "description": "value is the new and/or current value for the system control parameter depending on the action type" }, - "current_value_truncated": { + "value_truncated": { "type": "boolean", - "description": "current_value_truncated indicates if the current_value field is truncated" + "description": "value_truncated indicates if the value field is truncated" }, - "new_value": { + "old_value": { "type": "string", - "description": "new_value is the newly set value of the system control" + "description": "old_value is the old value of the system control parameter" }, - "new_value_truncated": { + "old_value_truncated": { "type": "boolean", - "description": "new_value_truncated indicates if the new_value field is truncated" + "description": "old_value_truncated indicates if the old_value field is truncated" } }, "additionalProperties": false, @@ -4445,10 +4445,10 @@ CSM Threats event for Linux systems have the following JSON schema: | `file_position` | file_position is the position in the sysctl control parameter file at which the action occurred | | `name` | name is the name of the system control parameter | | `name_truncated` | name_truncated indicates if the name field is truncated | -| `current_value` | current_value is the value of the system control parameter before the event | -| `current_value_truncated` | current_value_truncated indicates if the current_value field is truncated | -| `new_value` | new_value is the newly set value of the system control | -| `new_value_truncated` | new_value_truncated indicates if the new_value field is truncated | +| `value` | value is the new and/or current value for the system control parameter depending on the action type | +| `value_truncated` | value_truncated indicates if the value field is truncated | +| `old_value` | old_value is the old value of the system control parameter | +| `old_value_truncated` | old_value_truncated indicates if the old_value field is truncated | ## `Syscall` diff --git a/docs/cloud-workload-security/backend_linux.schema.json b/docs/cloud-workload-security/backend_linux.schema.json index 2785a9aed3256..b8d4d80bac344 100644 --- a/docs/cloud-workload-security/backend_linux.schema.json +++ b/docs/cloud-workload-security/backend_linux.schema.json @@ -1627,21 +1627,21 @@ "type": "boolean", "description": "name_truncated indicates if the name field is truncated" }, - "current_value": { + "value": { "type": "string", - "description": "current_value is the value of the system control parameter before the event" + "description": "value is the new and/or current value for the system control parameter depending on the action type" }, - "current_value_truncated": { + "value_truncated": { "type": "boolean", - "description": "current_value_truncated indicates if the current_value field is truncated" + "description": "value_truncated indicates if the value field is truncated" }, - "new_value": { + "old_value": { "type": "string", - "description": "new_value is the newly set value of the system control" + "description": "old_value is the old value of the system control parameter" }, - "new_value_truncated": { + "old_value_truncated": { "type": "boolean", - "description": "new_value_truncated indicates if the new_value field is truncated" + "description": "old_value_truncated indicates if the old_value field is truncated" } }, "additionalProperties": false, diff --git a/docs/cloud-workload-security/linux_expressions.md b/docs/cloud-workload-security/linux_expressions.md index 4479910286916..13e23b5f7e5c2 100644 --- a/docs/cloud-workload-security/linux_expressions.md +++ b/docs/cloud-workload-security/linux_expressions.md @@ -1750,13 +1750,13 @@ A sysctl parameter was read or modified | Property | Definition | | -------- | ------------- | | [`sysctl.action`](#sysctl-action-doc) | Action performed on the system control parameter | -| [`sysctl.current_value`](#sysctl-current_value-doc) | Current value of the system control parameter | -| [`sysctl.current_value_truncated`](#sysctl-current_value_truncated-doc) | Indicates that the current value field is truncated | | [`sysctl.file_position`](#sysctl-file_position-doc) | Position in the sysctl control parameter file at which the action occurred | | [`sysctl.name`](#sysctl-name-doc) | Name of the system control parameter | | [`sysctl.name_truncated`](#sysctl-name_truncated-doc) | Indicates that the name field is truncated | -| [`sysctl.new_value`](#sysctl-new_value-doc) | In case of Write accesses, new value for the system control parameter | -| [`sysctl.new_value_truncated`](#sysctl-new_value_truncated-doc) | Indicates that the new_value field is truncated | +| [`sysctl.old_value`](#sysctl-old_value-doc) | Old value of the system control parameter | +| [`sysctl.old_value_truncated`](#sysctl-old_value_truncated-doc) | Indicates that the old value field is truncated | +| [`sysctl.value`](#sysctl-value-doc) | New and/or current value for the system control parameter depending on the action type | +| [`sysctl.value_truncated`](#sysctl-value_truncated-doc) | Indicates that the value field is truncated | ### Event `unlink` @@ -3431,52 +3431,52 @@ Constants: [SysCtl Actions](#sysctl-actions) -### `sysctl.current_value` {#sysctl-current_value-doc} -Type: string +### `sysctl.file_position` {#sysctl-file_position-doc} +Type: int -Definition: Current value of the system control parameter +Definition: Position in the sysctl control parameter file at which the action occurred -### `sysctl.current_value_truncated` {#sysctl-current_value_truncated-doc} -Type: bool +### `sysctl.name` {#sysctl-name-doc} +Type: string -Definition: Indicates that the current value field is truncated +Definition: Name of the system control parameter -### `sysctl.file_position` {#sysctl-file_position-doc} -Type: int +### `sysctl.name_truncated` {#sysctl-name_truncated-doc} +Type: bool -Definition: Position in the sysctl control parameter file at which the action occurred +Definition: Indicates that the name field is truncated -### `sysctl.name` {#sysctl-name-doc} +### `sysctl.old_value` {#sysctl-old_value-doc} Type: string -Definition: Name of the system control parameter +Definition: Old value of the system control parameter -### `sysctl.name_truncated` {#sysctl-name_truncated-doc} +### `sysctl.old_value_truncated` {#sysctl-old_value_truncated-doc} Type: bool -Definition: Indicates that the name field is truncated +Definition: Indicates that the old value field is truncated -### `sysctl.new_value` {#sysctl-new_value-doc} +### `sysctl.value` {#sysctl-value-doc} Type: string -Definition: In case of Write accesses, new value for the system control parameter +Definition: New and/or current value for the system control parameter depending on the action type -### `sysctl.new_value_truncated` {#sysctl-new_value_truncated-doc} +### `sysctl.value_truncated` {#sysctl-value_truncated-doc} Type: bool -Definition: Indicates that the new_value field is truncated +Definition: Indicates that the value field is truncated diff --git a/docs/cloud-workload-security/secl_linux.json b/docs/cloud-workload-security/secl_linux.json index ef528a7f3f70c..4f39dda295052 100644 --- a/docs/cloud-workload-security/secl_linux.json +++ b/docs/cloud-workload-security/secl_linux.json @@ -7295,16 +7295,6 @@ "definition": "Action performed on the system control parameter", "property_doc_link": "sysctl-action-doc" }, - { - "name": "sysctl.current_value", - "definition": "Current value of the system control parameter", - "property_doc_link": "sysctl-current_value-doc" - }, - { - "name": "sysctl.current_value_truncated", - "definition": "Indicates that the current value field is truncated", - "property_doc_link": "sysctl-current_value_truncated-doc" - }, { "name": "sysctl.file_position", "definition": "Position in the sysctl control parameter file at which the action occurred", @@ -7321,14 +7311,24 @@ "property_doc_link": "sysctl-name_truncated-doc" }, { - "name": "sysctl.new_value", - "definition": "In case of Write accesses, new value for the system control parameter", - "property_doc_link": "sysctl-new_value-doc" + "name": "sysctl.old_value", + "definition": "Old value of the system control parameter", + "property_doc_link": "sysctl-old_value-doc" + }, + { + "name": "sysctl.old_value_truncated", + "definition": "Indicates that the old value field is truncated", + "property_doc_link": "sysctl-old_value_truncated-doc" + }, + { + "name": "sysctl.value", + "definition": "New and/or current value for the system control parameter depending on the action type", + "property_doc_link": "sysctl-value-doc" }, { - "name": "sysctl.new_value_truncated", - "definition": "Indicates that the new_value field is truncated", - "property_doc_link": "sysctl-new_value_truncated-doc" + "name": "sysctl.value_truncated", + "definition": "Indicates that the value field is truncated", + "property_doc_link": "sysctl-value_truncated-doc" } ] }, @@ -11084,10 +11084,10 @@ "examples": [] }, { - "name": "sysctl.current_value", - "link": "sysctl-current_value-doc", - "type": "string", - "definition": "Current value of the system control parameter", + "name": "sysctl.file_position", + "link": "sysctl-file_position-doc", + "type": "int", + "definition": "Position in the sysctl control parameter file at which the action occurred", "prefixes": [ "sysctl" ], @@ -11096,10 +11096,10 @@ "examples": [] }, { - "name": "sysctl.current_value_truncated", - "link": "sysctl-current_value_truncated-doc", - "type": "bool", - "definition": "Indicates that the current value field is truncated", + "name": "sysctl.name", + "link": "sysctl-name-doc", + "type": "string", + "definition": "Name of the system control parameter", "prefixes": [ "sysctl" ], @@ -11108,10 +11108,10 @@ "examples": [] }, { - "name": "sysctl.file_position", - "link": "sysctl-file_position-doc", - "type": "int", - "definition": "Position in the sysctl control parameter file at which the action occurred", + "name": "sysctl.name_truncated", + "link": "sysctl-name_truncated-doc", + "type": "bool", + "definition": "Indicates that the name field is truncated", "prefixes": [ "sysctl" ], @@ -11120,10 +11120,10 @@ "examples": [] }, { - "name": "sysctl.name", - "link": "sysctl-name-doc", + "name": "sysctl.old_value", + "link": "sysctl-old_value-doc", "type": "string", - "definition": "Name of the system control parameter", + "definition": "Old value of the system control parameter", "prefixes": [ "sysctl" ], @@ -11132,10 +11132,10 @@ "examples": [] }, { - "name": "sysctl.name_truncated", - "link": "sysctl-name_truncated-doc", + "name": "sysctl.old_value_truncated", + "link": "sysctl-old_value_truncated-doc", "type": "bool", - "definition": "Indicates that the name field is truncated", + "definition": "Indicates that the old value field is truncated", "prefixes": [ "sysctl" ], @@ -11144,10 +11144,10 @@ "examples": [] }, { - "name": "sysctl.new_value", - "link": "sysctl-new_value-doc", + "name": "sysctl.value", + "link": "sysctl-value-doc", "type": "string", - "definition": "In case of Write accesses, new value for the system control parameter", + "definition": "New and/or current value for the system control parameter depending on the action type", "prefixes": [ "sysctl" ], @@ -11156,10 +11156,10 @@ "examples": [] }, { - "name": "sysctl.new_value_truncated", - "link": "sysctl-new_value_truncated-doc", + "name": "sysctl.value_truncated", + "link": "sysctl-value_truncated-doc", "type": "bool", - "definition": "Indicates that the new_value field is truncated", + "definition": "Indicates that the value field is truncated", "prefixes": [ "sysctl" ], diff --git a/pkg/security/ebpf/c/include/constants/custom.h b/pkg/security/ebpf/c/include/constants/custom.h index a76003af74150..89692a5af22db 100644 --- a/pkg/security/ebpf/c/include/constants/custom.h +++ b/pkg/security/ebpf/c/include/constants/custom.h @@ -234,7 +234,6 @@ static __attribute__((always_inline)) u64 is_network_flow_monitor_enabled() { return is_network_flow_monitor_enabled; } -#define SYSCTL_SHOT 0 #define SYSCTL_OK 1 #define MAX_SYSCTL_BUFFER_LEN 1024 @@ -242,7 +241,7 @@ static __attribute__((always_inline)) u64 is_network_flow_monitor_enabled() { #define SYSCTL_EVENT_GEN_KEY 0 #define SYSCTL_NAME_TRUNCATED (1 << 0) -#define SYSCTL_CURRENT_VALUE_TRUNCATED (1 << 1) +#define SYSCTL_OLD_VALUE_TRUNCATED (1 << 1) #define SYSCTL_NEW_VALUE_TRUNCATED (1 << 2) static __attribute__((always_inline)) u64 has_tracing_helpers_in_cgroup_sysctl() { diff --git a/pkg/security/ebpf/c/include/events_definition.h b/pkg/security/ebpf/c/include/events_definition.h index d7a9abb907c2e..9183382edcd14 100644 --- a/pkg/security/ebpf/c/include/events_definition.h +++ b/pkg/security/ebpf/c/include/events_definition.h @@ -487,7 +487,7 @@ struct sysctl_event_t { u32 action; u32 file_position; u16 name_len; - u16 current_value_len; + u16 old_value_len; u16 new_value_len; u16 flags; char sysctl_buffer[MAX_SYSCTL_BUFFER_LEN]; diff --git a/pkg/security/ebpf/c/include/helpers/approvers.h b/pkg/security/ebpf/c/include/helpers/approvers.h index d7c4f5fd0df95..a52189f9cf27a 100644 --- a/pkg/security/ebpf/c/include/helpers/approvers.h +++ b/pkg/security/ebpf/c/include/helpers/approvers.h @@ -336,7 +336,22 @@ enum SYSCALL_STATE __attribute__((always_inline)) bpf_approvers(struct syscall_c return DISCARDED; } -enum SYSCALL_STATE __attribute__((always_inline)) approve_syscall(struct syscall_cache_t *syscall, enum SYSCALL_STATE (*check_approvers)(struct syscall_cache_t *syscall)) { +enum SYSCALL_STATE __attribute__((always_inline)) sysctl_approvers(struct syscall_cache_t *syscall) { + u32 key = 0; + struct u32_flags_filter_t *filter = bpf_map_lookup_elem(&sysctl_action_approvers, &key); + if (filter == NULL || !filter->is_set) { + return DISCARDED; + } + + if ((syscall->sysctl.action & filter->flags) > 0) { + monitor_event_approved(syscall->type, FLAG_APPROVER_TYPE); + return APPROVED; + } + + return DISCARDED; +} + +enum SYSCALL_STATE __attribute__((always_inline)) approve_syscall_with_tgid(u32 tgid, struct syscall_cache_t *syscall, enum SYSCALL_STATE (*check_approvers)(struct syscall_cache_t *syscall)) { if (syscall->policy.mode == NO_FILTER) { return syscall->state = ACCEPTED; } @@ -349,7 +364,6 @@ enum SYSCALL_STATE __attribute__((always_inline)) approve_syscall(struct syscall syscall->state = check_approvers(syscall); } - u32 tgid = bpf_get_current_pid_tgid() >> 32; u64 *cookie = bpf_map_lookup_elem(&traced_pids, &tgid); if (cookie != NULL) { u64 now = bpf_ktime_get_ns(); @@ -370,4 +384,9 @@ enum SYSCALL_STATE __attribute__((always_inline)) approve_syscall(struct syscall return syscall->state; } +enum SYSCALL_STATE __attribute__((always_inline)) approve_syscall(struct syscall_cache_t *syscall, enum SYSCALL_STATE (*check_approvers)(struct syscall_cache_t *syscall)) { + u32 tgid = bpf_get_current_pid_tgid() >> 32; + return approve_syscall_with_tgid(tgid, syscall, check_approvers); +} + #endif diff --git a/pkg/security/ebpf/c/include/helpers/sysctl.h b/pkg/security/ebpf/c/include/helpers/sysctl.h index 08f59a7e70de8..714d376afbd4e 100644 --- a/pkg/security/ebpf/c/include/helpers/sysctl.h +++ b/pkg/security/ebpf/c/include/helpers/sysctl.h @@ -2,6 +2,7 @@ #define _HELPERS_SYSCTL_H_ #include "constants/custom.h" +#include "helpers/approvers.h" #include "helpers/container.h" #include "helpers/process.h" @@ -24,7 +25,7 @@ __attribute__((always_inline)) struct sysctl_event_t *reset_sysctl_event() { evt->action = SYSCTL_UNKNOWN; evt->file_position = 0; evt->name_len = 0; - evt->current_value_len = 0; + evt->old_value_len = 0; evt->new_value_len = 0; evt->flags = 0; evt->sysctl_buffer[0] = 0; @@ -57,6 +58,19 @@ __attribute__((always_inline)) void handle_cgroup_sysctl(struct bpf_sysctl *ctx) } evt->file_position = ctx->file_pos; + // check approvers + struct policy_t policy = fetch_policy(EVENT_SYSCTL); + struct syscall_cache_t syscall = { + .policy = policy, + .type = EVENT_SYSCTL, + .sysctl = { + .action = evt->action, + } + }; + if (approve_syscall_with_tgid(evt->process.pid, &syscall, sysctl_approvers) == DISCARDED) { + return; + } + // copy the name of the control parameter u32 cursor = 0; u32 ret = bpf_sysctl_get_name(ctx, &evt->sysctl_buffer[0], MAX_SYSCTL_OBJ_LEN - 2, 0); @@ -74,20 +88,20 @@ __attribute__((always_inline)) void handle_cgroup_sysctl(struct bpf_sysctl *ctx) ret = bpf_sysctl_get_current_value(ctx, &evt->sysctl_buffer[cursor & (MAX_SYSCTL_OBJ_LEN-1)], MAX_SYSCTL_OBJ_LEN - 1); switch ((int)ret) { case -E2BIG: - evt->flags |= SYSCTL_CURRENT_VALUE_TRUNCATED; - evt->current_value_len = MAX_SYSCTL_OBJ_LEN; + evt->flags |= SYSCTL_OLD_VALUE_TRUNCATED; + evt->old_value_len = MAX_SYSCTL_OBJ_LEN; break; case -EINVAL: - evt->current_value_len = 1; + evt->old_value_len = 1; evt->sysctl_buffer[cursor & (MAX_SYSCTL_BUFFER_LEN - 1)] = 0; break; default: - evt->current_value_len = ret + 1; + evt->old_value_len = ret + 1; break; } // advance cursor in sysctl_buffer - cursor += evt->current_value_len; + cursor += evt->old_value_len; // copy the new value for the control parameter ret = bpf_sysctl_get_new_value(ctx, &evt->sysctl_buffer[cursor & (2*MAX_SYSCTL_OBJ_LEN - 1)], MAX_SYSCTL_OBJ_LEN - 1); diff --git a/pkg/security/ebpf/c/include/maps.h b/pkg/security/ebpf/c/include/maps.h index 3ced3b7416171..09297dc25d500 100644 --- a/pkg/security/ebpf/c/include/maps.h +++ b/pkg/security/ebpf/c/include/maps.h @@ -31,6 +31,7 @@ BPF_ARRAY_MAP(selinux_enforce_status, u16, 2) BPF_ARRAY_MAP(splice_entry_flags_approvers, struct u32_flags_filter_t, 1) BPF_ARRAY_MAP(splice_exit_flags_approvers, struct u32_flags_filter_t, 1) BPF_ARRAY_MAP(bpf_cmd_approvers, struct u64_flags_filter_t, 1) +BPF_ARRAY_MAP(sysctl_action_approvers, struct u32_flags_filter_t, 1) BPF_ARRAY_MAP(syscalls_stats_enabled, u32, 1) BPF_ARRAY_MAP(syscall_ctx_gen_id, u32, 1) BPF_ARRAY_MAP(syscall_ctx, char[MAX_SYSCALL_CTX_SIZE], MAX_SYSCALL_CTX_ENTRIES) diff --git a/pkg/security/ebpf/c/include/structs/syscalls.h b/pkg/security/ebpf/c/include/structs/syscalls.h index 13c16d7ce4539..117cf22f72ee5 100644 --- a/pkg/security/ebpf/c/include/structs/syscalls.h +++ b/pkg/security/ebpf/c/include/structs/syscalls.h @@ -237,6 +237,10 @@ struct syscall_cache_t { struct { u8 in_flight; } stat; + + struct { + u32 action; + } sysctl; }; }; diff --git a/pkg/security/probe/kfilters/approvers.go b/pkg/security/probe/kfilters/approvers.go index 7910f06771d78..92f5fc31a5aa9 100644 --- a/pkg/security/probe/kfilters/approvers.go +++ b/pkg/security/probe/kfilters/approvers.go @@ -188,4 +188,5 @@ func init() { KFilterGetters["splice"] = spliceKFiltersGetter KFilterGetters["chdir"] = fimKFiltersGetter(model.FileChdirEventType, []eval.Field{"file"}) KFilterGetters["bpf"] = bpfKFiltersGetter + KFilterGetters["sysctl"] = sysctlKFiltersGetter } diff --git a/pkg/security/probe/kfilters/capabilities_linux.go b/pkg/security/probe/kfilters/capabilities_linux.go index 75f6d00cf5036..c592756a131b8 100644 --- a/pkg/security/probe/kfilters/capabilities_linux.go +++ b/pkg/security/probe/kfilters/capabilities_linux.go @@ -71,4 +71,5 @@ func init() { allCapabilities["splice"] = spliceCapabilities allCapabilities["chdir"] = mergeCapabilities(buildBasenameCapabilities("chdir", "file"), processCapabilities) allCapabilities["bpf"] = bpfCapabilities + allCapabilities["sysctl"] = sysctlCapabilities } diff --git a/pkg/security/probe/kfilters/sysctl.go b/pkg/security/probe/kfilters/sysctl.go new file mode 100644 index 0000000000000..055b020b774c4 --- /dev/null +++ b/pkg/security/probe/kfilters/sysctl.go @@ -0,0 +1,41 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +// Package kfilters holds kfilters related files +package kfilters + +import ( + "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" + "github.com/DataDog/datadog-agent/pkg/security/secl/rules" +) + +var sysctlCapabilities = rules.FieldCapabilities{ + { + Field: "sysctl.action", + TypeBitmask: eval.ScalarValueType | eval.BitmaskValueType, + }, +} + +func sysctlKFiltersGetter(approvers rules.Approvers) (ActiveKFilters, []eval.Field, error) { + var ( + kfilters []activeKFilter + fieldHandled []eval.Field + ) + + for field, values := range approvers { + switch field { + case "sysctl.action": + kfilter, err := getFlagsKFilter("sysctl_action_approvers", uintValues[uint32](values)...) + if err != nil { + return nil, nil, err + } + kfilters = append(kfilters, kfilter) + fieldHandled = append(fieldHandled, field) + } + } + return newActiveKFilters(kfilters...), fieldHandled, nil +} diff --git a/pkg/security/secl/model/accessors_unix.go b/pkg/security/secl/model/accessors_unix.go index 0bf5ab526cc73..88cc3a08764f6 100644 --- a/pkg/security/secl/model/accessors_unix.go +++ b/pkg/security/secl/model/accessors_unix.go @@ -20320,72 +20320,72 @@ func (_ *Model) GetEvaluator(field eval.Field, regID eval.RegisterID) (eval.Eval Field: field, Weight: eval.FunctionWeight, }, nil - case "sysctl.current_value": - return &eval.StringEvaluator{ - EvalFnc: func(ctx *eval.Context) string { + case "sysctl.file_position": + return &eval.IntEvaluator{ + EvalFnc: func(ctx *eval.Context) int { ctx.AppendResolvedField(field) ev := ctx.Event.(*Event) - return ev.SysCtl.CurrentValue + return int(ev.SysCtl.FilePosition) }, Field: field, Weight: eval.FunctionWeight, }, nil - case "sysctl.current_value_truncated": - return &eval.BoolEvaluator{ - EvalFnc: func(ctx *eval.Context) bool { + case "sysctl.name": + return &eval.StringEvaluator{ + EvalFnc: func(ctx *eval.Context) string { ctx.AppendResolvedField(field) ev := ctx.Event.(*Event) - return ev.SysCtl.CurrentValueTruncated + return ev.SysCtl.Name }, Field: field, Weight: eval.FunctionWeight, }, nil - case "sysctl.file_position": - return &eval.IntEvaluator{ - EvalFnc: func(ctx *eval.Context) int { + case "sysctl.name_truncated": + return &eval.BoolEvaluator{ + EvalFnc: func(ctx *eval.Context) bool { ctx.AppendResolvedField(field) ev := ctx.Event.(*Event) - return int(ev.SysCtl.FilePosition) + return ev.SysCtl.NameTruncated }, Field: field, Weight: eval.FunctionWeight, }, nil - case "sysctl.name": + case "sysctl.old_value": return &eval.StringEvaluator{ EvalFnc: func(ctx *eval.Context) string { ctx.AppendResolvedField(field) ev := ctx.Event.(*Event) - return ev.SysCtl.Name + return ev.SysCtl.OldValue }, Field: field, Weight: eval.FunctionWeight, }, nil - case "sysctl.name_truncated": + case "sysctl.old_value_truncated": return &eval.BoolEvaluator{ EvalFnc: func(ctx *eval.Context) bool { ctx.AppendResolvedField(field) ev := ctx.Event.(*Event) - return ev.SysCtl.NameTruncated + return ev.SysCtl.OldValueTruncated }, Field: field, Weight: eval.FunctionWeight, }, nil - case "sysctl.new_value": + case "sysctl.value": return &eval.StringEvaluator{ EvalFnc: func(ctx *eval.Context) string { ctx.AppendResolvedField(field) ev := ctx.Event.(*Event) - return ev.SysCtl.NewValue + return ev.SysCtl.Value }, Field: field, Weight: eval.FunctionWeight, }, nil - case "sysctl.new_value_truncated": + case "sysctl.value_truncated": return &eval.BoolEvaluator{ EvalFnc: func(ctx *eval.Context) bool { ctx.AppendResolvedField(field) ev := ctx.Event.(*Event) - return ev.SysCtl.NewValueTruncated + return ev.SysCtl.ValueTruncated }, Field: field, Weight: eval.FunctionWeight, @@ -22291,13 +22291,13 @@ func (ev *Event) GetFields() []eval.Field { "splice.pipe_exit_flag", "splice.retval", "sysctl.action", - "sysctl.current_value", - "sysctl.current_value_truncated", "sysctl.file_position", "sysctl.name", "sysctl.name_truncated", - "sysctl.new_value", - "sysctl.new_value_truncated", + "sysctl.old_value", + "sysctl.old_value_truncated", + "sysctl.value", + "sysctl.value_truncated", "unlink.file.change_time", "unlink.file.filesystem", "unlink.file.gid", @@ -25157,19 +25157,19 @@ func (ev *Event) GetFieldMetadata(field eval.Field) (eval.EventType, reflect.Kin return "splice", reflect.Int, "int", nil case "sysctl.action": return "sysctl", reflect.Int, "int", nil - case "sysctl.current_value": - return "sysctl", reflect.String, "string", nil - case "sysctl.current_value_truncated": - return "sysctl", reflect.Bool, "bool", nil case "sysctl.file_position": return "sysctl", reflect.Int, "int", nil case "sysctl.name": return "sysctl", reflect.String, "string", nil case "sysctl.name_truncated": return "sysctl", reflect.Bool, "bool", nil - case "sysctl.new_value": + case "sysctl.old_value": + return "sysctl", reflect.String, "string", nil + case "sysctl.old_value_truncated": + return "sysctl", reflect.Bool, "bool", nil + case "sysctl.value": return "sysctl", reflect.String, "string", nil - case "sysctl.new_value_truncated": + case "sysctl.value_truncated": return "sysctl", reflect.Bool, "bool", nil case "unlink.file.change_time": return "unlink", reflect.Int, "int", nil @@ -40343,20 +40343,6 @@ func (ev *Event) SetFieldValue(field eval.Field, value interface{}) error { } ev.SysCtl.Action = uint32(rv) return nil - case "sysctl.current_value": - rv, ok := value.(string) - if !ok { - return &eval.ErrValueTypeMismatch{Field: "sysctl.current_value"} - } - ev.SysCtl.CurrentValue = rv - return nil - case "sysctl.current_value_truncated": - rv, ok := value.(bool) - if !ok { - return &eval.ErrValueTypeMismatch{Field: "sysctl.current_value_truncated"} - } - ev.SysCtl.CurrentValueTruncated = rv - return nil case "sysctl.file_position": rv, ok := value.(int) if !ok { @@ -40378,19 +40364,33 @@ func (ev *Event) SetFieldValue(field eval.Field, value interface{}) error { } ev.SysCtl.NameTruncated = rv return nil - case "sysctl.new_value": + case "sysctl.old_value": + rv, ok := value.(string) + if !ok { + return &eval.ErrValueTypeMismatch{Field: "sysctl.old_value"} + } + ev.SysCtl.OldValue = rv + return nil + case "sysctl.old_value_truncated": + rv, ok := value.(bool) + if !ok { + return &eval.ErrValueTypeMismatch{Field: "sysctl.old_value_truncated"} + } + ev.SysCtl.OldValueTruncated = rv + return nil + case "sysctl.value": rv, ok := value.(string) if !ok { - return &eval.ErrValueTypeMismatch{Field: "sysctl.new_value"} + return &eval.ErrValueTypeMismatch{Field: "sysctl.value"} } - ev.SysCtl.NewValue = rv + ev.SysCtl.Value = rv return nil - case "sysctl.new_value_truncated": + case "sysctl.value_truncated": rv, ok := value.(bool) if !ok { - return &eval.ErrValueTypeMismatch{Field: "sysctl.new_value_truncated"} + return &eval.ErrValueTypeMismatch{Field: "sysctl.value_truncated"} } - ev.SysCtl.NewValueTruncated = rv + ev.SysCtl.ValueTruncated = rv return nil case "unlink.file.change_time": rv, ok := value.(int) diff --git a/pkg/security/secl/model/model_unix.go b/pkg/security/secl/model/model_unix.go index a162780da5c5a..0468f340ceac2 100644 --- a/pkg/security/secl/model/model_unix.go +++ b/pkg/security/secl/model/model_unix.go @@ -956,12 +956,12 @@ func (it *FlowsIterator) Len(ctx *eval.Context) int { // SysCtlEvent is used to represent a system control parameter event type SysCtlEvent struct { - Action uint32 `field:"action"` // SECLDoc[action] Definition:`Action performed on the system control parameter` Constants:`SysCtl Actions` - FilePosition uint32 `field:"file_position"` // SECLDoc[file_position] Definition:`Position in the sysctl control parameter file at which the action occurred` - Name string `field:"name"` // SECLDoc[name] Definition:`Name of the system control parameter` - NameTruncated bool `field:"name_truncated"` // SECLDoc[name_truncated] Definition:`Indicates that the name field is truncated` - CurrentValue string `field:"current_value"` // SECLDoc[current_value] Definition:`Current value of the system control parameter` - CurrentValueTruncated bool `field:"current_value_truncated"` // SECLDoc[current_value_truncated] Definition:`Indicates that the current value field is truncated` - NewValue string `field:"new_value"` // SECLDoc[new_value] Definition:`In case of Write accesses, new value for the system control parameter` - NewValueTruncated bool `field:"new_value_truncated"` // SECLDoc[new_value_truncated] Definition:`Indicates that the new_value field is truncated` + Action uint32 `field:"action"` // SECLDoc[action] Definition:`Action performed on the system control parameter` Constants:`SysCtl Actions` + FilePosition uint32 `field:"file_position"` // SECLDoc[file_position] Definition:`Position in the sysctl control parameter file at which the action occurred` + Name string `field:"name"` // SECLDoc[name] Definition:`Name of the system control parameter` + NameTruncated bool `field:"name_truncated"` // SECLDoc[name_truncated] Definition:`Indicates that the name field is truncated` + OldValue string `field:"old_value"` // SECLDoc[old_value] Definition:`Old value of the system control parameter` + OldValueTruncated bool `field:"old_value_truncated"` // SECLDoc[old_value_truncated] Definition:`Indicates that the old value field is truncated` + Value string `field:"value"` // SECLDoc[value] Definition:`New and/or current value for the system control parameter depending on the action type` + ValueTruncated bool `field:"value_truncated"` // SECLDoc[value_truncated] Definition:`Indicates that the value field is truncated` } diff --git a/pkg/security/secl/model/unmarshallers_linux.go b/pkg/security/secl/model/unmarshallers_linux.go index c9430b83ed421..d7e485ecb1122 100644 --- a/pkg/security/secl/model/unmarshallers_linux.go +++ b/pkg/security/secl/model/unmarshallers_linux.go @@ -1517,25 +1517,18 @@ func (e *SysCtlEvent) UnmarshalBinary(data []byte) (int, error) { e.FilePosition = binary.NativeEndian.Uint32(data[4:8]) nameLen := int(binary.NativeEndian.Uint16(data[8:10])) - currentValueLen := int(binary.NativeEndian.Uint16(data[10:12])) + oldValueLen := int(binary.NativeEndian.Uint16(data[10:12])) newValueLen := int(binary.NativeEndian.Uint16(data[12:14])) flags := binary.NativeEndian.Uint16(data[14:16]) // handle truncated fields - switch { - case flags&(1<<0) > 0: - e.NameTruncated = true - fallthrough - case flags&(1<<1) > 0: - e.CurrentValueTruncated = true - fallthrough - case flags&(1<<2) > 0: - e.NewValueTruncated = true - } + e.NameTruncated = flags&(1<<0) > 0 + e.OldValueTruncated = flags&(1<<1) > 0 + e.ValueTruncated = flags&(1<<2) > 0 cursor += 16 // parse name and values - if nameLen+currentValueLen+newValueLen > len(data[cursor:]) { + if nameLen+oldValueLen+newValueLen > len(data[cursor:]) { return 0, ErrNotEnoughData } @@ -1547,18 +1540,25 @@ func (e *SysCtlEvent) UnmarshalBinary(data []byte) (int, error) { e.Name = strings.TrimSpace(e.Name) cursor += nameLen - e.CurrentValue, err = UnmarshalString(data[cursor:cursor+currentValueLen], currentValueLen) + e.OldValue, err = UnmarshalString(data[cursor:cursor+oldValueLen], oldValueLen) if err != nil { return 0, err } - e.CurrentValue = strings.TrimSpace(e.CurrentValue) - cursor += currentValueLen + e.OldValue = strings.TrimSpace(e.OldValue) + cursor += oldValueLen - e.NewValue, err = UnmarshalString(data[cursor:cursor+newValueLen], newValueLen) - if err != nil { - return 0, err + if e.Action == uint32(SysCtlReadAction) { + e.Value = e.OldValue + } else if e.Action == uint32(SysCtlWriteAction) { + e.Value, err = UnmarshalString(data[cursor:cursor+newValueLen], newValueLen) + if err != nil { + return 0, err + } + e.Value = strings.TrimSpace(e.Value) } - e.NewValue = strings.TrimSpace(e.NewValue) + + // make sure the cursor is incremented either way cursor += newValueLen + return cursor, nil } diff --git a/pkg/security/secl/schemas/sysctl.schema.json b/pkg/security/secl/schemas/sysctl.schema.json index 7382c85fffcb5..5889782188c8f 100644 --- a/pkg/security/secl/schemas/sysctl.schema.json +++ b/pkg/security/secl/schemas/sysctl.schema.json @@ -17,21 +17,20 @@ "type": "object", "required": [ "action", - "current_value", - "name", - "new_value" + "value", + "name" ], "properties": { "action": { "type": "string" }, - "current_value": { + "value": { "type": "string" }, "name": { "type": "string" }, - "new_value": { + "old_value": { "type": "string" } } diff --git a/pkg/security/serializers/serializers_base.go b/pkg/security/serializers/serializers_base.go index 35b29f61afeb5..0a3d4e2235192 100644 --- a/pkg/security/serializers/serializers_base.go +++ b/pkg/security/serializers/serializers_base.go @@ -272,14 +272,14 @@ type SysCtlEventSerializer struct { Name string `json:"name,omitempty"` // name_truncated indicates if the name field is truncated NameTruncated bool `json:"name_truncated,omitempty"` - // current_value is the value of the system control parameter before the event - CurrentValue string `json:"current_value,omitempty"` - // current_value_truncated indicates if the current_value field is truncated - CurrentValueTruncated bool `json:"current_value_truncated,omitempty"` - // new_value is the newly set value of the system control - NewValue string `json:"new_value,omitempty"` - // new_value_truncated indicates if the new_value field is truncated - NewValueTruncated bool `json:"new_value_truncated,omitempty"` + // value is the new and/or current value for the system control parameter depending on the action type + Value string `json:"value,omitempty"` + // value_truncated indicates if the value field is truncated + ValueTruncated bool `json:"value_truncated,omitempty"` + // old_value is the old value of the system control parameter + OldValue string `json:"old_value,omitempty"` + // old_value_truncated indicates if the old_value field is truncated + OldValueTruncated bool `json:"old_value_truncated,omitempty"` } func newMatchedRulesSerializer(r *model.MatchedRule) MatchedRuleSerializer { diff --git a/pkg/security/serializers/serializers_base_linux_easyjson.go b/pkg/security/serializers/serializers_base_linux_easyjson.go index 4865742738c04..92a4f0b995ce0 100644 --- a/pkg/security/serializers/serializers_base_linux_easyjson.go +++ b/pkg/security/serializers/serializers_base_linux_easyjson.go @@ -163,14 +163,14 @@ func easyjsonA1e47abeDecodeGithubComDataDogDatadogAgentPkgSecuritySerializers2(i out.Name = string(in.String()) case "name_truncated": out.NameTruncated = bool(in.Bool()) - case "current_value": - out.CurrentValue = string(in.String()) - case "current_value_truncated": - out.CurrentValueTruncated = bool(in.Bool()) - case "new_value": - out.NewValue = string(in.String()) - case "new_value_truncated": - out.NewValueTruncated = bool(in.Bool()) + case "value": + out.Value = string(in.String()) + case "value_truncated": + out.ValueTruncated = bool(in.Bool()) + case "old_value": + out.OldValue = string(in.String()) + case "old_value_truncated": + out.OldValueTruncated = bool(in.Bool()) default: in.SkipRecursive() } @@ -221,45 +221,45 @@ func easyjsonA1e47abeEncodeGithubComDataDogDatadogAgentPkgSecuritySerializers2(o } out.Bool(bool(in.NameTruncated)) } - if in.CurrentValue != "" { - const prefix string = ",\"current_value\":" + if in.Value != "" { + const prefix string = ",\"value\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } - out.String(string(in.CurrentValue)) + out.String(string(in.Value)) } - if in.CurrentValueTruncated { - const prefix string = ",\"current_value_truncated\":" + if in.ValueTruncated { + const prefix string = ",\"value_truncated\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } - out.Bool(bool(in.CurrentValueTruncated)) + out.Bool(bool(in.ValueTruncated)) } - if in.NewValue != "" { - const prefix string = ",\"new_value\":" + if in.OldValue != "" { + const prefix string = ",\"old_value\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } - out.String(string(in.NewValue)) + out.String(string(in.OldValue)) } - if in.NewValueTruncated { - const prefix string = ",\"new_value_truncated\":" + if in.OldValueTruncated { + const prefix string = ",\"old_value_truncated\":" if first { first = false out.RawString(prefix[1:]) } else { out.RawString(prefix) } - out.Bool(bool(in.NewValueTruncated)) + out.Bool(bool(in.OldValueTruncated)) } out.RawByte('}') } diff --git a/pkg/security/serializers/serializers_linux.go b/pkg/security/serializers/serializers_linux.go index a8d1859d4de2a..dba4ef87427f1 100644 --- a/pkg/security/serializers/serializers_linux.go +++ b/pkg/security/serializers/serializers_linux.go @@ -1098,14 +1098,14 @@ func newNetworkFlowMonitorSerializer(nm *model.NetworkFlowMonitorEvent, e *model func newSysCtlEventSerializer(sce *model.SysCtlEvent, _ *model.Event) *SysCtlEventSerializer { return &SysCtlEventSerializer{ - Action: model.SysCtlAction(sce.Action).String(), - FilePosition: sce.FilePosition, - Name: sce.Name, - NameTruncated: sce.NameTruncated, - CurrentValue: sce.CurrentValue, - CurrentValueTruncated: sce.CurrentValueTruncated, - NewValue: sce.NewValue, - NewValueTruncated: sce.NewValueTruncated, + Action: model.SysCtlAction(sce.Action).String(), + FilePosition: sce.FilePosition, + Name: sce.Name, + NameTruncated: sce.NameTruncated, + Value: sce.Value, + ValueTruncated: sce.ValueTruncated, + OldValue: sce.OldValue, + OldValueTruncated: sce.OldValueTruncated, } } diff --git a/pkg/security/tests/sysctl_test.go b/pkg/security/tests/sysctl_test.go index 8eb591a4bedb8..e865b9750ea81 100644 --- a/pkg/security/tests/sysctl_test.go +++ b/pkg/security/tests/sysctl_test.go @@ -22,6 +22,14 @@ import ( "github.com/DataDog/datadog-agent/pkg/security/utils" ) +func readSysctlValue(name string) (string, error) { + data, err := os.ReadFile(path.Join("/proc/sys", name)) + if err != nil { + return "", err + } + return string(data), nil +} + func writeSysctlValue(name string, value string) error { file, err := os.OpenFile(path.Join("/proc/sys", name), os.O_WRONLY, 0644) if err != nil { @@ -47,13 +55,29 @@ func TestSysctlEvent(t *testing.T) { ruleDefs := []*rules.RuleDefinition{ { - ID: "test_sysctl", - Expression: `sysctl.name == "kernel/kptr_restrict" && sysctl.current_value == "0" && sysctl.new_value == "0" && sysctl.action == SYSCTL_WRITE && process.file.name == "testsuite"`, + ID: "test_sysctl_write", + Expression: `sysctl.name == "kernel/kptr_restrict" && sysctl.old_value == "1" && sysctl.value == "0" && sysctl.action == SYSCTL_WRITE && process.file.name == "testsuite"`, + }, + { + ID: "test_sysctl_read", + Expression: `sysctl.name == "kernel/kptr_restrict" && sysctl.value == "0" && sysctl.action == SYSCTL_READ && process.file.name == "testsuite"`, }, } + // keep the initial value for later + initialValue, err := readSysctlValue("kernel/kptr_restrict") + if err != nil { + t.Fatalf("couldn't read kernel/kptr_restrict: %s", err) + } + defer func() { + // reset initial value + if err = writeSysctlValue("kernel/kptr_restrict", initialValue); err != nil { + t.Fatalf("couldn't reset kernel/kptr_restrict to %s: %s", initialValue, err) + } + }() + // make sure the correct value is set before the test starts - if err := writeSysctlValue("kernel/kptr_restrict", "0"); err != nil { + if err := writeSysctlValue("kernel/kptr_restrict", "1"); err != nil { t.Fatalf("couldn't set kernel/kptr_restrict: %v", err) } @@ -63,7 +87,7 @@ func TestSysctlEvent(t *testing.T) { } defer test.Close() - t.Run("test_sysctl", func(t *testing.T) { + t.Run("test_sysctl_write", func(t *testing.T) { test.WaitSignal(t, func() error { return writeSysctlValue("kernel/kptr_restrict", "0") }, func(event *model.Event, _ *rules.Rule) { @@ -76,4 +100,20 @@ func TestSysctlEvent(t *testing.T) { test.validateSysctlSchema(t, event) }) }) + + t.Run("test_sysctl_read", func(t *testing.T) { + test.WaitSignal(t, func() error { + _, err := readSysctlValue("kernel/kptr_restrict") + return err + }, func(event *model.Event, _ *rules.Rule) { + assert.Equal(t, "sysctl", event.GetType(), "wrong event type") + assert.Equal(t, uint32(0), event.SysCtl.FilePosition, "wrong file position") + assert.Equal(t, "0", event.SysCtl.OldValue, "wrong old value") + + value, _ := event.GetFieldValue("event.async") + assert.Equal(t, value.(bool), false) + + test.validateSysctlSchema(t, event) + }) + }) } diff --git a/pkg/security/utils/cgroup.go b/pkg/security/utils/cgroup.go index 2ccfc80dc160f..746043b508eb6 100644 --- a/pkg/security/utils/cgroup.go +++ b/pkg/security/utils/cgroup.go @@ -302,7 +302,7 @@ func checkPidExists(sysFScGroupPath string, expectedPid uint32) (bool, error) { // GetCgroup2MountPoint checks if cgroup v2 is available and returns its mount point func GetCgroup2MountPoint() (string, error) { - file, err := os.Open("/proc/self/mountinfo") + file, err := os.Open(kernel.HostProc("/1/mountinfo")) if err != nil { return "", fmt.Errorf("couldn't resolve cgroup2 mount point: failed to open /proc/self/mountinfo: %w", err) } @@ -315,7 +315,8 @@ func GetCgroup2MountPoint() (string, error) { // The cgroup2 mount entry will have "cgroup2" as the filesystem type if len(fields) >= 10 && fields[len(fields)-3] == "cgroup2" { - return fields[4], nil // The 5th field is the mount point + // The 5th field is the mount point + return filepath.Join(kernel.SysFSRoot(), strings.TrimPrefix(fields[4], "/sys")), nil } }