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
This commit is contained in:
Kyle Evans
2025-11-06 22:19:31 -06:00
parent 626fe12e28
commit bd55cbb50c
3 changed files with 215 additions and 1 deletions
+51 -1
View File
@@ -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");
+154
View File
@@ -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)
{
+10
View File
@@ -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_ */