diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -5264,6 +5264,7 @@ security/mac/mac_pipe.c optional mac security/mac/mac_posix_sem.c optional mac security/mac/mac_posix_shm.c optional mac +security/mac/mac_prison.c optional mac security/mac/mac_priv.c optional mac security/mac/mac_process.c optional mac security/mac/mac_socket.c optional mac 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 @@ -275,8 +275,17 @@ uint8_t *file, *data; size_t size; char buf[sizeof(prison0.pr_hostuuid)]; +#ifdef MAC + int error __diagused; +#endif bool valid; +#ifdef MAC + error = mac_prison_init(&prison0, M_WAITOK); + MPASS(error == 0); + + mtx_unlock(&prison0.pr_mtx); +#endif prison0.pr_cpuset = cpuset_ref(thread0.td_cpuset); prison0.pr_osreldate = osreldate; strlcpy(prison0.pr_osrelease, osrelease, sizeof(prison0.pr_osrelease)); @@ -1828,7 +1837,14 @@ if (error) goto done_deref; +#ifdef MAC + error = mac_prison_init(pr, M_WAITOK); + MPASS(error == 0); + + mtx_assert(&pr->pr_mtx, MA_OWNED); +#else mtx_lock(&pr->pr_mtx); +#endif drflags |= PD_LOCKED; } else { /* @@ -3540,6 +3556,16 @@ KASSERT( refcount_load(&prison0.pr_ref) != 0, ("prison0 pr_ref=0")); +#ifdef MAC + /* + * The MAC framework will call into any + * policies that want to hook + * prison_destroy_label, so ideally we + * call this prior to any final state + * invalidation to be safe. + */ + mac_prison_destroy(pr); +#endif pr->pr_state = PRISON_STATE_INVALID; TAILQ_REMOVE(&allprison, pr, pr_list); LIST_REMOVE(pr, pr_sibling); diff --git a/sys/security/mac/mac_framework.h b/sys/security/mac/mac_framework.h --- a/sys/security/mac/mac_framework.h +++ b/sys/security/mac/mac_framework.h @@ -73,6 +73,7 @@ struct msg; struct msqid_kernel; struct pipepair; +struct prison; struct proc; struct semid_kernel; struct shmfd; @@ -346,6 +347,11 @@ void mac_posixshm_destroy(struct shmfd *); void mac_posixshm_init(struct shmfd *); +int mac_prison_init(struct prison *pr, int flag); +void mac_prison_relabel(struct ucred *cred, struct prison *pr, + struct label *newlabel); +void mac_prison_destroy(struct prison *pr); + int mac_priv_check_impl(struct ucred *cred, int priv); #ifdef MAC extern bool mac_priv_check_fp_flag; diff --git a/sys/security/mac/mac_framework.c b/sys/security/mac/mac_framework.c --- a/sys/security/mac/mac_framework.c +++ b/sys/security/mac/mac_framework.c @@ -374,6 +374,7 @@ MPC_FLAG(mount_init_label, MPC_OBJECT_MOUNT); MPC_FLAG(posixsem_init_label, MPC_OBJECT_POSIXSEM); MPC_FLAG(posixshm_init_label, MPC_OBJECT_POSIXSHM); + MPC_FLAG(prison_init_label, MPC_OBJECT_PRISON); MPC_FLAG(sysvmsg_init_label, MPC_OBJECT_SYSVMSG); MPC_FLAG(sysvmsq_init_label, MPC_OBJECT_SYSVMSQ); MPC_FLAG(sysvsem_init_label, MPC_OBJECT_SYSVSEM); diff --git a/sys/security/mac/mac_internal.h b/sys/security/mac/mac_internal.h --- a/sys/security/mac/mac_internal.h +++ b/sys/security/mac/mac_internal.h @@ -177,6 +177,7 @@ #define MPC_OBJECT_SYSVSHM 0x0000000000020000 #define MPC_OBJECT_SYNCACHE 0x0000000000040000 #define MPC_OBJECT_IP6Q 0x0000000000080000 +#define MPC_OBJECT_PRISON 0x0000000000100000 /* * MAC Framework global variables. @@ -252,6 +253,14 @@ char *outbuf, size_t outbuflen); int mac_pipe_internalize_label(struct label *label, char *string); +int mac_prison_check_relabel(struct ucred *cred, struct prison *pr, + struct label *newlabel); +int mac_prison_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen); +int mac_prison_internalize_label(struct label *label, char *string); +void mac_prison_relabel(struct ucred *cred, struct prison *pr, + struct label *newlabel); + int mac_socket_label_set(struct ucred *cred, struct socket *so, struct label *label); void mac_socket_copy_label(struct label *src, struct label *dest); diff --git a/sys/security/mac/mac_policy.h b/sys/security/mac/mac_policy.h --- a/sys/security/mac/mac_policy.h +++ b/sys/security/mac/mac_policy.h @@ -88,6 +88,7 @@ struct msg; struct msqid_kernel; struct pipepair; +struct prison; struct proc; struct sbuf; struct semid_kernel; @@ -407,6 +408,18 @@ typedef void (*mpo_posixshm_destroy_label_t)(struct label *label); typedef void (*mpo_posixshm_init_label_t)(struct label *label); +typedef int (*mpo_prison_init_label_t)(struct label *label, int flag); +typedef int (*mpo_prison_check_relabel_t)(struct ucred *cred, + struct prison *pr, struct label *prlabel, + struct label *newlabel); +typedef void (*mpo_prison_destroy_label_t)(struct label *label); +typedef int (*mpo_prison_externalize_label_t)(struct label *label, + char *element_name, struct sbuf *sb, int *claimed); +typedef int (*mpo_prison_internalize_label_t)(struct label *label, + char *element_name, char *element_data, int *claimed); +typedef void (*mpo_prison_relabel_t)(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct label *newlabel); + typedef int (*mpo_priv_check_t)(struct ucred *cred, int priv); typedef int (*mpo_priv_grant_t)(struct ucred *cred, int priv); @@ -863,6 +876,13 @@ mpo_posixshm_destroy_label_t mpo_posixshm_destroy_label; mpo_posixshm_init_label_t mpo_posixshm_init_label; + mpo_prison_init_label_t mpo_prison_init_label; + mpo_prison_check_relabel_t mpo_prison_check_relabel; + mpo_prison_destroy_label_t mpo_prison_destroy_label; + mpo_prison_externalize_label_t mpo_prison_externalize_label; + mpo_prison_internalize_label_t mpo_prison_internalize_label; + mpo_prison_relabel_t mpo_prison_relabel; + mpo_priv_check_t mpo_priv_check; mpo_priv_grant_t mpo_priv_grant; diff --git a/sys/security/mac/mac_prison.c b/sys/security/mac/mac_prison.c new file mode 100644 --- /dev/null +++ b/sys/security/mac/mac_prison.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2025 Kyle Evans + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include "opt_mac.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static void +mac_prison_label_free(struct label *label) +{ + if (label == NULL) + return; + + MAC_POLICY_PERFORM_NOSLEEP(prison_destroy_label, label); + mac_labelzone_free(label); +} + +static struct label * +mac_prison_label_alloc(int flag) +{ + struct label *label; + int error; + + label = mac_labelzone_alloc(flag); + if (label == NULL) + return (NULL); + + if (flag & M_WAITOK) + MAC_POLICY_CHECK(prison_init_label, label, flag); + else + MAC_POLICY_CHECK_NOSLEEP(prison_init_label, label, flag); + if (error) { + mac_prison_label_free(label); + return (NULL); + } + return (label); +} + +/* + * The caller's expecting us to return with the prison locked if we were + * successful, since we're also setting pr->pr_label. On error, it remains + * unlocked. + */ +int +mac_prison_init(struct prison *pr, int flag) +{ + struct label *prlabel; + + mtx_assert(&pr->pr_mtx, MA_NOTOWNED); + if ((mac_labeled & MPC_OBJECT_PRISON) == 0) { + mtx_lock(&pr->pr_mtx); + pr->pr_label = NULL; + return (0); + } + + prlabel = mac_prison_label_alloc(flag); + if (prlabel == NULL) { + KASSERT((flag & M_WAITOK) == 0, + ("MAC policy prison_init_label failed under M_WAITOK")); + return (ENOMEM); + } + + mtx_lock(&pr->pr_mtx); + pr->pr_label = prlabel; + return (0); +} + +void +mac_prison_destroy(struct prison *pr) +{ + mtx_assert(&pr->pr_mtx, MA_OWNED); + mac_prison_label_free(pr->pr_label); + pr->pr_label = NULL; +} + +int +mac_prison_externalize_label(struct label *label, char *elements, + char *outbuf, size_t outbuflen) +{ + int error; + + MAC_POLICY_EXTERNALIZE(prison, label, elements, outbuf, outbuflen); + return (error); +} + +int +mac_prison_internalize_label(struct label *label, char *string) +{ + int error; + + MAC_POLICY_INTERNALIZE(prison, label, string); + return (error); +} + +void +mac_prison_relabel(struct ucred *cred, struct prison *pr, + struct label *newlabel) +{ + mtx_assert(&pr->pr_mtx, MA_OWNED); + MAC_POLICY_PERFORM_NOSLEEP(prison_relabel, cred, pr, pr->pr_label, + newlabel); +} + +MAC_CHECK_PROBE_DEFINE4(prison_check_relabel, "struct ucred *", + "struct prison *", "struct label *", "struct label *"); +int +mac_prison_check_relabel(struct ucred *cred, struct prison *pr, + struct label *newlabel) +{ + int error; + + mtx_assert(&pr->pr_mtx, MA_OWNED); + MAC_POLICY_CHECK_NOSLEEP(prison_check_relabel, cred, pr, + pr->pr_label, newlabel); + MAC_CHECK_PROBE4(prison_check_relabel, error, cred, pr, + pr->pr_label, newlabel); + + return (error); +} diff --git a/sys/sys/jail.h b/sys/sys/jail.h --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -198,6 +198,7 @@ struct prison_ip *pr_addrs[PR_FAMILY_MAX]; /* (p,n) IPs of jail */ struct prison_racct *pr_prison_racct; /* (c) racct jail proxy */ struct knlist *pr_klist; /* (m) attached knotes */ + struct label *pr_label; /* (m) MAC label */ LIST_HEAD(, jaildesc) pr_descs; /* (a) attached descriptors */ void *pr_sparep; int pr_childcount; /* (a) number of child jails */