The branch main has been updated by kevans:

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

commit bd55cbb50c58876281f925cfd91961544f0153ad
Author:     Kyle Evans <[email protected]>
AuthorDate: 2025-11-07 04:19:31 +0000
Commit:     Kyle Evans <[email protected]>
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_ */

Reply via email to