summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-29 22:47:13 +0300
committerPaul Buetow <paul@buetow.org>2026-05-29 22:47:13 +0300
commit34821a6dd28ee4bd6282abdca07306605e8f6b1c (patch)
treea9f4679ef657492f90cd234eb9ee20c2454d4971
parent7a2207e215dfde246f0e9c4e415eff962f4c044c (diff)
test(classify): lock in keyctl-family audit (kind/family/ret)
Audit of keyctl(2), add_key(2), request_key(2) confirmed the existing tracing is correct: all three are KindKeyctl (operation + generic numeric args captured via keyctl_event, no fd/path probe), live in FamilySecurity alongside their *_key/landlock_*/lsm_*/seccomp siblings, and return an operation-dependent value or -1 that is not a byte transfer (UNCLASSIFIED). Add TestClassifyKeyctlAudit as a lock-in regression test, mirroring prior audits: it asserts the Security family on both enter and exit names, the UNCLASSIFIED return classification, and that add_key's const char * type/description arguments are key metadata that must not trip the generic pathname/open heuristics (PathnameField stays empty, kind stays KindKeyctl). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-rw-r--r--internal/generate/classify_test.go59
1 files changed, 59 insertions, 0 deletions
diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go
index 5301abb..25c7068 100644
--- a/internal/generate/classify_test.go
+++ b/internal/generate/classify_test.go
@@ -707,6 +707,65 @@ func TestClassifyRequestKey(t *testing.T) {
}
}
+// TestClassifyKeyctlAudit is a lock-in regression test for the keyctl(2)
+// audit. The key-management syscalls have these signatures:
+//
+// long keyctl(int op, unsigned long arg2, arg3, arg4, arg5)
+// key_serial_t add_key(const char *type, const char *desc,
+// const void *payload, size_t plen, key_serial_t keyring)
+// key_serial_t request_key(const char *type, const char *desc,
+// const char *callout_info, key_serial_t dest_keyring)
+//
+// keyctl's op selects a command and the remaining arguments are
+// operation-dependent unsigned longs — never an fd or a path. add_key and
+// request_key take string TYPE/DESCRIPTION arguments that are key metadata
+// (a key type name and a free-form description), NOT filesystem paths, so
+// they must not be classified as KindPathname/KindOpen. All three therefore
+// classify as KindKeyctl (operation + generic numeric args, captured via the
+// keyctl_event without any bpf_probe_read_user path/fd capture), live in the
+// FamilySecurity family alongside their *_key/landlock_*/lsm_*/seccomp
+// siblings, and return an operation-dependent value or -1 that is NOT a byte
+// transfer, so their exits stay UNCLASSIFIED.
+func TestClassifyKeyctlAudit(t *testing.T) {
+ for _, name := range []string{"keyctl", "add_key", "request_key"} {
+ // Family: Security on both enter and exit tracepoint names.
+ for _, prefix := range []string{"sys_enter_", "sys_exit_"} {
+ if fam := ClassifySyscallFamily(prefix + name); fam != FamilySecurity {
+ t.Errorf("%s%s: got family %s, want FamilySecurity", prefix, name, fam)
+ }
+ }
+
+ // Returns: UNCLASSIFIED (key serial / op-dependent value / -1, not
+ // a byte count), so the exit must NOT be tagged as a read/write/
+ // transfer byte transfer.
+ if got := ClassifyRet("sys_exit_" + name); got != Unclassified {
+ t.Errorf("ClassifyRet(sys_exit_%s) = %q, want UNCLASSIFIED", name, got)
+ }
+ }
+
+ // Contrast: add_key/request_key take a const char * "type"/"description"
+ // first argument, but it is key metadata, not a path. Such a field name
+ // must NOT trip the generic pathname/open heuristics — the name-only table
+ // maps these syscalls to KindKeyctl before any field is inspected.
+ addKey := ClassifyFormat(&Format{
+ Name: "sys_enter_add_key",
+ ExternalFields: []Field{
+ {Type: "long", Name: "__syscall_nr"},
+ {Type: "const char *", Name: "_type"},
+ {Type: "const char *", Name: "_description"},
+ {Type: "const void *", Name: "_payload"},
+ {Type: "size_t", Name: "plen"},
+ {Type: "key_serial_t", Name: "ringid"},
+ },
+ })
+ if addKey.Kind != KindKeyctl {
+ t.Errorf("add_key: got kind %d, want KindKeyctl (string args are key metadata, not paths)", addKey.Kind)
+ }
+ if addKey.PathnameField != "" {
+ t.Errorf("add_key: got PathnameField %q, want empty (no path capture)", addKey.PathnameField)
+ }
+}
+
func TestClassifyPtrace(t *testing.T) {
r := ClassifyFormat(&Format{
Name: "sys_enter_ptrace",