diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -80,6 +80,8 @@ #endif /* DDB */ #include +#include +#include #define PRISON0_HOSTUUID_MODULE "hostuuid" @@ -1027,6 +1029,10 @@ #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 @@ 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 @@ } } 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 @@ #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 @@ 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_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.h b/sys/security/mac/mac_syscalls.h --- a/sys/security/mac/mac_syscalls.h +++ b/sys/security/mac/mac_syscalls.h @@ -30,4 +30,14 @@ 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_ */ diff --git a/sys/security/mac/mac_syscalls.c b/sys/security/mac/mac_syscalls.c --- a/sys/security/mac/mac_syscalls.c +++ b/sys/security/mac/mac_syscalls.c @@ -302,6 +302,160 @@ 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) {