git: 9da71e6353a2 - stable/14 - ktrace: Record detailed ECAPMODE violations

From: Jake Freeland <jfree_at_FreeBSD.org>
Date: Sun, 12 May 2024 00:08:25 UTC
The branch stable/14 has been updated by jfree:

URL: https://cgit.FreeBSD.org/src/commit/?id=9da71e6353a24aed8bffb04e45931215806b16fd

commit 9da71e6353a24aed8bffb04e45931215806b16fd
Author:     Jake Freeland <jfree@FreeBSD.org>
AuthorDate: 2024-04-06 18:29:45 +0000
Commit:     Jake Freeland <jfree@FreeBSD.org>
CommitDate: 2024-05-11 23:57:44 +0000

    ktrace: Record detailed ECAPMODE violations
    
    When a Capsicum violation occurs in the kernel, ktrace will now record
    detailed information pertaining to the violation.
    
    For example:
    - When a namei lookup violation occurs, ktrace will record the path.
    - When a signal violation occurs, ktrace will record the signal number.
    - When a sendto(2) violation occurs, ktrace will record the recipient
      sockaddr.
    
    For all violations, the syscall and ABI is recorded.
    
    kdump is also modified to display this new information to the user.
    
    Reviewed by:    oshogbo, markj
    Approved by:    markj (mentor)
    MFC after:      1 month
    Differential Revision:  https://reviews.freebsd.org/D40676
    
    (cherry picked from commit 9bec84131215ed554f435e208399f36e982246f1)
---
 sys/kern/kern_ktrace.c    | 43 +++++++++++++++++++++++--------
 sys/kern/sys_capability.c | 16 +++++-------
 sys/sys/capsicum.h        |  7 +++++
 sys/sys/ktrace.h          | 32 +++++++++++++++++------
 usr.bin/kdump/kdump.c     | 65 +++++++++++++++++++++++++++++++++++++----------
 5 files changed, 122 insertions(+), 41 deletions(-)

diff --git a/sys/kern/kern_ktrace.c b/sys/kern/kern_ktrace.c
index 4c1936edc301..555762185411 100644
--- a/sys/kern/kern_ktrace.c
+++ b/sys/kern/kern_ktrace.c
@@ -925,14 +925,17 @@ ktrstructarray(const char *name, enum uio_seg seg, const void *data,
 }
 
 void
-ktrcapfail(enum ktr_cap_fail_type type, const cap_rights_t *needed,
-    const cap_rights_t *held)
+ktrcapfail(enum ktr_cap_violation type, const void *data)
 {
 	struct thread *td = curthread;
 	struct ktr_request *req;
 	struct ktr_cap_fail *kcf;
+	union ktr_cap_data *kcd;
 
-	if (__predict_false(curthread->td_pflags & TDP_INKTRACE))
+	if (__predict_false(td->td_pflags & TDP_INKTRACE))
+		return;
+	if (type != CAPFAIL_SYSCALL &&
+	    (td->td_sa.callp->sy_flags & SYF_CAPENABLED) == 0)
 		return;
 
 	req = ktr_getrequest(KTR_CAPFAIL);
@@ -940,14 +943,32 @@ ktrcapfail(enum ktr_cap_fail_type type, const cap_rights_t *needed,
 		return;
 	kcf = &req->ktr_data.ktr_cap_fail;
 	kcf->cap_type = type;
-	if (needed != NULL)
-		kcf->cap_needed = *needed;
-	else
-		cap_rights_init(&kcf->cap_needed);
-	if (held != NULL)
-		kcf->cap_held = *held;
-	else
-		cap_rights_init(&kcf->cap_held);
+	kcf->cap_code = td->td_sa.code;
+	kcf->cap_svflags = td->td_proc->p_sysent->sv_flags;
+	if (data != NULL) {
+		kcd = &kcf->cap_data;
+		switch (type) {
+		case CAPFAIL_NOTCAPABLE:
+		case CAPFAIL_INCREASE:
+			kcd->cap_needed = *(const cap_rights_t *)data;
+			kcd->cap_held = *((const cap_rights_t *)data + 1);
+			break;
+		case CAPFAIL_SYSCALL:
+		case CAPFAIL_SIGNAL:
+		case CAPFAIL_PROTO:
+			kcd->cap_int = *(const int *)data;
+			break;
+		case CAPFAIL_SOCKADDR:
+			kcd->cap_sockaddr = *(const struct sockaddr *)data;
+			break;
+		case CAPFAIL_NAMEI:
+			strlcpy(kcd->cap_path, data, MAXPATHLEN);
+			break;
+		case CAPFAIL_CPUSET:
+		default:
+			break;
+		}
+	}
 	ktr_enqueuerequest(td, req);
 	ktrace_exit(td);
 }
diff --git a/sys/kern/sys_capability.c b/sys/kern/sys_capability.c
index fac264a78531..d59ad37f35ec 100644
--- a/sys/kern/sys_capability.c
+++ b/sys/kern/sys_capability.c
@@ -154,14 +154,13 @@ MALLOC_DECLARE(M_FILECAPS);
 
 static inline int
 _cap_check(const cap_rights_t *havep, const cap_rights_t *needp,
-    enum ktr_cap_fail_type type)
+    enum ktr_cap_violation type)
 {
+	const cap_rights_t rights[] = { *needp, *havep };
 
 	if (!cap_rights_contains(havep, needp)) {
-#ifdef KTRACE
-		if (KTRPOINT(curthread, KTR_CAPFAIL))
-			ktrcapfail(type, needp, havep);
-#endif
+		if (CAP_TRACING(curthread))
+			ktrcapfail(type, rights);
 		return (ENOTCAPABLE);
 	}
 	return (0);
@@ -180,11 +179,10 @@ cap_check(const cap_rights_t *havep, const cap_rights_t *needp)
 int
 cap_check_failed_notcapable(const cap_rights_t *havep, const cap_rights_t *needp)
 {
+	const cap_rights_t rights[] = { *needp, *havep };
 
-#ifdef KTRACE
-	if (KTRPOINT(curthread, KTR_CAPFAIL))
-		ktrcapfail(CAPFAIL_NOTCAPABLE, needp, havep);
-#endif
+	if (CAP_TRACING(curthread))
+		ktrcapfail(CAPFAIL_NOTCAPABLE, rights);
 	return (ENOTCAPABLE);
 }
 
diff --git a/sys/sys/capsicum.h b/sys/sys/capsicum.h
index 93c5f0c9c390..05712abf37d1 100644
--- a/sys/sys/capsicum.h
+++ b/sys/sys/capsicum.h
@@ -45,6 +45,7 @@
 #include <sys/caprights.h>
 #include <sys/file.h>
 #include <sys/fcntl.h>
+#include <sys/ktrace.h>
 
 #ifndef _KERNEL
 #include <stdbool.h>
@@ -418,6 +419,12 @@ __END_DECLS
 
 #include <sys/systm.h>
 
+#ifdef KTRACE
+#define CAP_TRACING(td) KTRPOINT((td), KTR_CAPFAIL)
+#else
+#define CAP_TRACING(td) 0
+#endif
+
 #define IN_CAPABILITY_MODE(td) (((td)->td_ucred->cr_flags & CRED_FLAG_CAPMODE) != 0)
 
 struct filedesc;
diff --git a/sys/sys/ktrace.h b/sys/sys/ktrace.h
index 594f912b02ef..6d8280c415a6 100644
--- a/sys/sys/ktrace.h
+++ b/sys/sys/ktrace.h
@@ -34,8 +34,10 @@
 #ifndef _SYS_KTRACE_H_
 #define _SYS_KTRACE_H_
 
+#include <sys/param.h>
 #include <sys/caprights.h>
 #include <sys/signal.h>
+#include <sys/socket.h>
 #include <sys/_uio.h>
 
 /*
@@ -207,16 +209,31 @@ struct ktr_proc_ctor {
  * KTR_CAPFAIL - trace capability check failures
  */
 #define KTR_CAPFAIL	12
-enum ktr_cap_fail_type {
+enum ktr_cap_violation {
 	CAPFAIL_NOTCAPABLE,	/* insufficient capabilities in cap_check() */
-	CAPFAIL_INCREASE,	/* attempt to increase capabilities */
+	CAPFAIL_INCREASE,	/* attempt to increase rights on a capability */
 	CAPFAIL_SYSCALL,	/* disallowed system call */
-	CAPFAIL_LOOKUP,		/* disallowed VFS lookup */
+	CAPFAIL_SIGNAL,		/* sent signal to process other than self */
+	CAPFAIL_PROTO,		/* disallowed protocol */
+	CAPFAIL_SOCKADDR,	/* restricted address lookup */
+	CAPFAIL_NAMEI,		/* restricted namei lookup */
+	CAPFAIL_CPUSET,		/* restricted CPU set modification */
 };
+
+union ktr_cap_data {
+	cap_rights_t	cap_rights[2];
+#define	cap_needed	cap_rights[0]
+#define	cap_held	cap_rights[1]
+	int		cap_int;
+	struct sockaddr	cap_sockaddr;
+	char		cap_path[MAXPATHLEN];
+};
+
 struct ktr_cap_fail {
-	enum ktr_cap_fail_type cap_type;
-	cap_rights_t	cap_needed;
-	cap_rights_t	cap_held;
+	enum ktr_cap_violation cap_type;
+	short	cap_code;
+	u_int	cap_svflags;
+	union ktr_cap_data cap_data;
 };
 
 /*
@@ -319,8 +336,7 @@ void	ktruserret(struct thread *);
 void	ktrstruct(const char *, const void *, size_t);
 void	ktrstruct_error(const char *, const void *, size_t, int);
 void	ktrstructarray(const char *, enum uio_seg, const void *, int, size_t);
-void	ktrcapfail(enum ktr_cap_fail_type, const cap_rights_t *,
-	    const cap_rights_t *);
+void	ktrcapfail(enum ktr_cap_violation, const void *);
 #define ktrcaprights(s) \
 	ktrstruct("caprights", (s), sizeof(cap_rights_t))
 #define	ktritimerval(s) \
diff --git a/usr.bin/kdump/kdump.c b/usr.bin/kdump/kdump.c
index d69c019bd6be..abbe57b0b75e 100644
--- a/usr.bin/kdump/kdump.c
+++ b/usr.bin/kdump/kdump.c
@@ -2137,35 +2137,74 @@ invalid:
 void
 ktrcapfail(struct ktr_cap_fail *ktr)
 {
+	union ktr_cap_data *kcd = &ktr->cap_data;
+
 	switch (ktr->cap_type) {
 	case CAPFAIL_NOTCAPABLE:
 		/* operation on fd with insufficient capabilities */
 		printf("operation requires ");
-		sysdecode_cap_rights(stdout, &ktr->cap_needed);
+		sysdecode_cap_rights(stdout, &kcd->cap_needed);
 		printf(", descriptor holds ");
-		sysdecode_cap_rights(stdout, &ktr->cap_held);
+		sysdecode_cap_rights(stdout, &kcd->cap_held);
 		break;
 	case CAPFAIL_INCREASE:
 		/* requested more capabilities than fd already has */
 		printf("attempt to increase capabilities from ");
-		sysdecode_cap_rights(stdout, &ktr->cap_held);
+		sysdecode_cap_rights(stdout, &kcd->cap_held);
 		printf(" to ");
-		sysdecode_cap_rights(stdout, &ktr->cap_needed);
+		sysdecode_cap_rights(stdout, &kcd->cap_needed);
 		break;
 	case CAPFAIL_SYSCALL:
 		/* called restricted syscall */
-		printf("disallowed system call");
+		printf("system call not allowed: ");
+		syscallname(ktr->cap_code, ktr->cap_svflags);
+		if (syscallabi(ktr->cap_svflags) == SYSDECODE_ABI_FREEBSD) {
+			switch (ktr->cap_code) {
+			case SYS_sysarch:
+				printf(", op: ");
+				print_integer_arg(sysdecode_sysarch_number,
+				    kcd->cap_int);
+				break;
+			case SYS_fcntl:
+				printf(", cmd: ");
+				print_integer_arg(sysdecode_fcntl_cmd,
+				    kcd->cap_int);
+				break;
+			}
+		}
 		break;
-	case CAPFAIL_LOOKUP:
-		/* absolute or AT_FDCWD path, ".." path, etc. */
-		printf("restricted VFS lookup");
+	case CAPFAIL_SIGNAL:
+		/* sent signal to proc other than self */
+		syscallname(ktr->cap_code, ktr->cap_svflags);
+		printf(": signal delivery not allowed: ");
+		print_integer_arg(sysdecode_signal, kcd->cap_int);
 		break;
-	default:
-		printf("unknown capability failure: ");
-		sysdecode_cap_rights(stdout, &ktr->cap_needed);
-		printf(" ");
-		sysdecode_cap_rights(stdout, &ktr->cap_held);
+	case CAPFAIL_PROTO:
+		/* created socket with restricted protocol */
+		syscallname(ktr->cap_code, ktr->cap_svflags);
+		printf(": protocol not allowed: ");
+		print_integer_arg(sysdecode_ipproto, kcd->cap_int);
 		break;
+	case CAPFAIL_SOCKADDR:
+		/* unable to look up address */
+		syscallname(ktr->cap_code, ktr->cap_svflags);
+		printf(": restricted address lookup: ");
+		ktrsockaddr(&kcd->cap_sockaddr);
+		return;
+	case CAPFAIL_NAMEI:
+		/* absolute or AT_FDCWD path, ".." path, etc. */
+		syscallname(ktr->cap_code, ktr->cap_svflags);
+		printf(": restricted VFS lookup: %s\n", kcd->cap_path);
+		return;
+	case CAPFAIL_CPUSET:
+		/* modification of an external cpuset */
+		syscallname(ktr->cap_code, ktr->cap_svflags);
+		printf(": restricted cpuset operation\n");
+		return;
+	default:
+		syscallname(ktr->cap_code, ktr->cap_svflags);
+		printf(": unknown capability failure\n");
+		return;
 	}
 	printf("\n");
 }