git: bd55cbb50c58 - main - kern: add a mac.label jail parameter

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Fri, 16 Jan 2026 00:24:23 UTC
The branch main has been updated by kevans:

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

commit bd55cbb50c58876281f925cfd91961544f0153ad
Author:     Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2025-11-07 04:19:31 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2026-01-16 00:23:39 +0000

    kern: add a mac.label jail parameter
    
    Have it take a `struct mac` and we'll paper over the difference for
    jail(8)/jls(8) in libjail(3).  The mac_syscalls.h model is taken from
    mac_set_proc_*() that were previously done.
    
    Reviewed by:    olce
    Differential Revision:  https://reviews.freebsd.org/D53958
---
 sys/kern/kern_jail.c            |  52 +++++++++++++-
 sys/security/mac/mac_syscalls.c | 154 ++++++++++++++++++++++++++++++++++++++++
 sys/security/mac/mac_syscalls.h |  10 +++
 3 files changed, 215 insertions(+), 1 deletion(-)

diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
index b5f61e3957d9..5111b98bf221 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -80,6 +80,8 @@
 #endif /* DDB */
 
 #include <security/mac/mac_framework.h>
+#include <security/mac/mac_policy.h>
+#include <security/mac/mac_syscalls.h>
 
 #define	PRISON0_HOSTUUID_MODULE	"hostuuid"
 
@@ -1027,6 +1029,10 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
 #endif
 	unsigned long hid;
 	size_t namelen, onamelen, pnamelen;
+#ifdef MAC
+	void *mac_set_prison_data = NULL;
+	int gotmaclabel;
+#endif
 	int created, cuflags, descend, drflags, enforce;
 	int error, errmsg_len, errmsg_pos;
 	int gotchildmax, gotenforce, gothid, gotrsnum, gotslevel;
@@ -1349,6 +1355,17 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
 		pr_flags |= PR_HOST;
 	}
 
+#ifdef MAC
+	/* Process the mac.label vfsopt */
+	error = mac_set_prison_prepare(td, opts, &mac_set_prison_data);
+	if (error == ENOENT)
+		gotmaclabel = 0;
+	else if (error != 0)
+		goto done_errmsg;
+	else
+		gotmaclabel = 1;
+#endif
+
 #ifdef INET
 	error = vfs_getopt(opts, "ip4.addr", &op, &ip4s);
 	if (error == ENOENT)
@@ -2182,6 +2199,17 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
 		}
 	}
 	pr->pr_flags = (pr->pr_flags & ~ch_flags) | pr_flags;
+
+#ifdef MAC
+	/* Apply any request MAC label before we let modules do their work. */
+	if (gotmaclabel) {
+		error = mac_set_prison_core(td, pr, mac_set_prison_data);
+		if (error) {
+			vfs_opterror(opts, "mac relabel denied");
+			goto done_deref;
+		}
+	}
+#endif
 	mtx_unlock(&pr->pr_mtx);
 	drflags &= ~PD_LOCKED;
 	/*
@@ -2370,6 +2398,10 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
 #endif
 #ifdef INET6
 	prison_ip_free(ip6);
+#endif
+#ifdef MAC
+	if (mac_set_prison_data != NULL)
+		mac_set_prison_finish(td, error == 0, mac_set_prison_data);
 #endif
 	if (jfp_out != NULL)
 		fdrop(jfp_out, td);
@@ -2835,9 +2867,22 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags)
 	if (error != 0 && error != ENOENT)
 		goto done;
 
-	/* Get the module parameters. */
+#ifdef MAC
+	/*
+	 * We get the MAC label last because we'll let the MAC framework drop
+	 * pr_mtx to externalize the label.
+	 */
+	error = mac_get_prison(td, pr, opts);
+	mtx_assert(&pr->pr_mtx, MA_NOTOWNED);
+	drflags &= ~PD_LOCKED;
+	if (error != 0 && error != ENOENT)
+		goto done;
+#else
 	mtx_unlock(&pr->pr_mtx);
 	drflags &= ~PD_LOCKED;
+#endif
+
+	/* Get the module parameters. */
 	error = osd_jail_call(pr, PR_METHOD_GET, opts);
 	if (error)
 		goto done;
@@ -5107,6 +5152,11 @@ SYSCTL_JAIL_PARAM(_host, hostid, CTLTYPE_ULONG | CTLFLAG_RW,
 SYSCTL_JAIL_PARAM_NODE(cpuset, "Jail cpuset");
 SYSCTL_JAIL_PARAM(_cpuset, id, CTLTYPE_INT | CTLFLAG_RD, "I", "Jail cpuset ID");
 
+#ifdef MAC
+SYSCTL_JAIL_PARAM_STRUCT(_mac, label, CTLFLAG_RW, sizeof(struct mac),
+    "S,mac", "Jail MAC label");
+#endif
+
 #ifdef INET
 SYSCTL_JAIL_PARAM_SYS_NODE(ip4, CTLFLAG_RDTUN,
     "Jail IPv4 address virtualization");
diff --git a/sys/security/mac/mac_syscalls.c b/sys/security/mac/mac_syscalls.c
index 2a8b8d1f18ce..1035c6dbb84b 100644
--- a/sys/security/mac/mac_syscalls.c
+++ b/sys/security/mac/mac_syscalls.c
@@ -302,6 +302,160 @@ mac_set_proc_finish(struct thread *const td, bool proc_label_set,
 	mac_cred_label_free(intlabel);
 }
 
+int
+mac_get_prison(struct thread *const td, struct prison *pr,
+    struct vfsoptlist *opts)
+{
+	char *buffer = NULL, *u_buffer;
+	struct label *intlabel = NULL;
+	struct mac mac;
+	int error;
+	bool locked = true;
+
+	mtx_assert(&pr->pr_mtx, MA_OWNED);
+#ifdef COMPAT_FREEBSD32
+	if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
+		struct mac32 mac32;
+
+		error = vfs_copyopt(opts, "mac.label", &mac32, sizeof(mac32));
+		if (error == 0) {
+			CP(mac32, mac, m_buflen);
+			PTRIN_CP(mac32, mac, m_string);
+		}
+	} else
+#endif
+		error = vfs_copyopt(opts, "mac.label", &mac, sizeof(mac));
+	if (error) {
+		if (error != ENOENT)
+			vfs_opterror(opts, "bad mac.label");
+		goto out_nomac;
+	}
+
+	if (!(mac_labeled & MPC_OBJECT_PRISON)) {
+		error = EINVAL;
+		goto out;
+	}
+
+	intlabel = mac_prison_label_alloc(M_NOWAIT);
+	if (intlabel == NULL) {
+		error = ENOMEM;
+		goto out;
+	}
+
+	mac_prison_copy_label(pr->pr_label, intlabel);
+
+	/*
+	 * Externalization may want to acquire an rmlock.  We already tapped out
+	 * a copy of the label from when the jail_get(2) operation started and
+	 * we're expected to be called near the end of jail_get(2) when the lock
+	 * is about to be dropped anyways, so this is safe.
+	 */
+	mtx_unlock(&pr->pr_mtx);
+	locked = false;
+
+	error = mac_label_copyin_string(&mac, &u_buffer, M_WAITOK);
+	if (error) {
+		vfs_opterror(opts, "mac.label: string copy failure");
+		goto out;
+	}
+
+	buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO);
+	if (buffer == NULL) {
+		error = ENOMEM;
+		goto out;
+	}
+
+	error = mac_prison_externalize_label(intlabel, mac.m_string,
+	    buffer, mac.m_buflen);
+
+	if (error == 0)
+		error = copyout(buffer, u_buffer, strlen(buffer)+1);
+
+out:
+	mac_prison_label_free(intlabel);
+	free_copied_label(&mac);
+	free(buffer, M_MACTEMP);
+
+out_nomac:
+	if (locked) {
+		MPASS(error != 0);
+		mtx_unlock(&pr->pr_mtx);
+	}
+
+	return (error);
+}
+
+int
+mac_set_prison_prepare(struct thread *const td, struct vfsoptlist *opts,
+    void **const mac_set_prison_data)
+{
+	struct mac mac;
+	struct label *intlabel;
+	int error;
+
+#ifdef COMPAT_FREEBSD32
+	if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
+		struct mac32 mac32;
+
+		error = vfs_copyopt(opts, "mac.label", &mac32, sizeof(mac32));
+		if (error == 0) {
+			CP(mac32, mac, m_buflen);
+			PTRIN_CP(mac32, mac, m_string);
+		}
+	} else
+#endif
+		error = vfs_copyopt(opts, "mac.label", &mac, sizeof(mac));
+	if (error) {
+		if (error != ENOENT)
+			vfs_opterror(opts, "bad mac.label");
+		return (error);
+	}
+
+	error = mac_label_copyin_string(&mac, NULL, M_WAITOK);
+	if (error) {
+		vfs_opterror(opts, "mac.label: string copy failure");
+		return (error);
+	}
+
+	/*
+	 * If the option wasn't set, then we return ENOENT above.  If we don't
+	 * have any policies applicable to prisons, we can return EINVAL early.
+	 */
+	if (!(mac_labeled & MPC_OBJECT_PRISON)) {
+		vfs_opterror(opts, "no labelled jail policies");
+		return (EINVAL);
+	}
+
+	intlabel = mac_prison_label_alloc(M_WAITOK);
+	error = mac_prison_internalize_label(intlabel, mac.m_string);
+	if (error) {
+		mac_prison_label_free(intlabel);
+		vfs_opterror(opts, "internalize_label error");
+		return (error);
+	}
+
+	*mac_set_prison_data = intlabel;
+	return (0);
+}
+
+int
+mac_set_prison_core(struct thread *const td, struct prison *pr,
+    void *const mac_set_prison_data)
+{
+	struct label *const intlabel = mac_set_prison_data;
+
+	return (mac_prison_label_set(td->td_ucred, pr, intlabel));
+}
+
+void
+mac_set_prison_finish(struct thread *const td, bool prison_label_set __unused,
+    void *const mac_set_prison_data)
+{
+	struct label *const intlabel = mac_set_prison_data;
+
+	mac_prison_label_free(intlabel);
+}
+
 int
 sys___mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap)
 {
diff --git a/sys/security/mac/mac_syscalls.h b/sys/security/mac/mac_syscalls.h
index f95ff3ef1264..76c8e6d188bb 100644
--- a/sys/security/mac/mac_syscalls.h
+++ b/sys/security/mac/mac_syscalls.h
@@ -30,4 +30,14 @@ int	mac_set_proc_core(struct thread *const td, struct ucred *const newcred,
 void	mac_set_proc_finish(struct thread *const td, bool proc_label_set,
 	    void *const mac_set_proc_data);
 
+struct vfsoptlist;
+int	mac_get_prison(struct thread *const td, struct prison *pr,
+	    struct vfsoptlist *opts);
+int	mac_set_prison_prepare(struct thread *const td, struct vfsoptlist *opts,
+	    void **const mac_set_prison_data);
+int	mac_set_prison_core(struct thread *const td, struct prison *pr,
+	    void *const mac_set_prison_data);
+void	mac_set_prison_finish(struct thread *const td, bool prison_label_set,
+	    void *const mac_set_prison_data);
+
 #endif /* !_SECURITY_MAC_MAC_SYSCALLS_H_ */