diff --git a/share/man/man4/mac.4 b/share/man/man4/mac.4 --- a/share/man/man4/mac.4 +++ b/share/man/man4/mac.4 @@ -115,6 +115,8 @@ .Bl -ohang .It Sy "File System" File system mounts, modifying directories, modifying files, etc. +.It Sy Jails +Creating, listing, modifying, removing, and attaching to jails .It Sy KLD Loading, unloading, and retrieving statistics on loaded kernel modules .It Sy Network 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 @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -1699,6 +1700,11 @@ /* If there's no prison to update, create a new one and link it in. */ created = pr == NULL; if (created) { +#ifdef MAC + error = mac_prison_check_create(td->td_ucred, opts); + if (error != 0) + goto done_deref; +#endif for (tpr = mypr; tpr != NULL; tpr = tpr->pr_parent) if (tpr->pr_childcount >= tpr->pr_childmax) { error = EPERM; @@ -1853,6 +1859,11 @@ prison_hold(pr); drflags |= PD_DEREF; } +#ifdef MAC + error = mac_prison_check_set(td->td_ucred, pr, opts); + if (error != 0) + goto done_deref; +#endif #if defined(VIMAGE) && (defined(INET) || defined(INET6)) if ((pr->pr_flags & PR_VNET) && (ch_flags & (PR_IP4_USER | PR_IP6_USER))) { @@ -2244,6 +2255,13 @@ if (created) { sx_assert(&allprison_lock, SX_XLOCKED); prison_knote(ppr, NOTE_JAIL_CHILD | pr->pr_id); +#ifdef MAC + /* + * Note that mac_prison_created() assumes that it's called in a + * sleepable context. + */ + mac_prison_created(td->td_ucred, pr); +#endif mtx_lock(&pr->pr_mtx); drflags |= PD_LOCKED; pr->pr_state = PRISON_STATE_ALIVE; @@ -2251,6 +2269,14 @@ /* Attach this process to the prison if requested. */ if (flags & JAIL_ATTACH) { +#ifdef MAC + error = mac_prison_check_attach(td->td_ucred, pr); + if (error != 0) { + vfs_opterror(opts, + "attach operation denied by MAC policy"); + goto done_deref; + } +#endif error = do_jail_attach(td, pr, prison_lock_xlock(pr, drflags & PD_LOCK_FLAGS)); drflags &= ~(PD_LOCKED | PD_LIST_XLOCKED); @@ -2554,12 +2580,6 @@ drflags |= PD_DEREF; mtx_lock(&pr->pr_mtx); drflags |= PD_LOCKED; - if (!(prison_isalive(pr) || (flags & JAIL_DYING))) { - error = ENOENT; - vfs_opterror(opts, "jail %d is dying", - pr->pr_id); - goto done; - } goto found_prison; } if (flags & JAIL_AT_DESC) { @@ -2591,7 +2611,29 @@ prison_ischild(mypr, pr)) { mtx_lock(&pr->pr_mtx); drflags |= PD_LOCKED; +#ifdef MAC + /* + * We special-case this one check because we + * don't want MAC to break jail enumeration. We + * need to just move on to the next accessible + * and alive prison. + */ + error = mac_prison_check_get(td->td_ucred, pr, + opts); + if (error != 0) { + mtx_unlock(&pr->pr_mtx); + drflags &= ~PD_LOCKED; + continue; + } + + /* + * Avoid potentially expensive trip back into + * the MAC framework. + */ + goto found_prison_nomac_alive; +#else goto found_prison; +#endif } } error = ENOENT; @@ -2606,13 +2648,6 @@ pr = prison_find_child(mypr, jid); if (pr != NULL) { drflags |= PD_LOCKED; - if (!(prison_isalive(pr) || - (flags & JAIL_DYING))) { - error = ENOENT; - vfs_opterror(opts, "jail %d is dying", - jid); - goto done; - } goto found_prison; } error = ENOENT; @@ -2631,12 +2666,6 @@ pr = prison_find_name(mypr, name); if (pr != NULL) { drflags |= PD_LOCKED; - if (!(prison_isalive(pr) || (flags & JAIL_DYING))) { - error = ENOENT; - vfs_opterror(opts, "jail \"%s\" is dying", - name); - goto done; - } goto found_prison; } error = ENOENT; @@ -2650,6 +2679,25 @@ goto done; found_prison: +#ifdef MAC + error = mac_prison_check_get(td->td_ucred, pr, opts); + if (error != 0) + goto done; +#endif + if (!(prison_isalive(pr) || (flags & JAIL_DYING))) { + error = ENOENT; + if (pr->pr_name[0] != '0' && isdigit(pr->pr_name[0])) { + vfs_opterror(opts, "jail %d is dying", + pr->pr_id); + } else { + vfs_opterror(opts, "jail \"%s\" (%d) is dying", + pr->pr_name, pr->pr_id); + } + goto done; + } +#ifdef MAC + found_prison_nomac_alive: +#endif /* Get the parameters of the prison. */ if (!(drflags & PD_DEREF)) { prison_hold(pr); @@ -2889,6 +2937,14 @@ sx_xunlock(&allprison_lock); return (EINVAL); } +#ifdef MAC + error = mac_prison_check_remove(td->td_ucred, pr); + if (error != 0) { + mtx_unlock(&pr->pr_mtx); + sx_xunlock(&allprison_lock); + return (error); + } +#endif prison_hold(pr); prison_remove(pr); return (0); @@ -2911,6 +2967,10 @@ return (error); error = priv_check_cred(jdcred, PRIV_JAIL_REMOVE); crfree(jdcred); +#ifdef MAC + if (error == 0) + error = mac_prison_check_remove(td->td_ucred, pr); +#endif if (error) { prison_free(pr); return (error); @@ -2955,14 +3015,25 @@ return (EINVAL); } +#ifdef MAC + error = mac_prison_check_attach(td->td_ucred, pr); + if (error != 0) + goto unlock; +#endif + /* Do not allow a process to attach to a prison that is not alive. */ if (!prison_isalive(pr)) { - mtx_unlock(&pr->pr_mtx); - sx_sunlock(&allprison_lock); - return (EINVAL); + error = EINVAL; + goto unlock; } return (do_jail_attach(td, pr, PD_LOCKED | PD_LIST_SLOCKED)); + +unlock: + + mtx_unlock(&pr->pr_mtx); + sx_sunlock(&allprison_lock); + return (error); } /* @@ -2984,6 +3055,10 @@ goto fail; drflags |= PD_DEREF; error = priv_check_cred(jdcred, PRIV_JAIL_ATTACH); +#ifdef MAC + if (error == 0) + error = mac_prison_check_attach(td->td_ucred, pr); +#endif crfree(jdcred); if (error) goto fail; @@ -3084,6 +3159,13 @@ prison_deref(oldcred->cr_prison, drflags); crfree(oldcred); prison_knote(pr, NOTE_JAIL_ATTACH | td->td_proc->p_pid); +#ifdef MAC + /* + * Note that mac_prison_attached() assumes that it's called in a + * sleepable context. + */ + mac_prison_attached(td->td_ucred, pr, td->td_proc); +#endif /* * If the prison was killed while changing credentials, die along 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 @@ -86,6 +86,7 @@ struct timespec; struct ucred; struct vattr; +struct vfsoptlist; struct vnode; struct vop_setlabel_args; @@ -351,6 +352,16 @@ void mac_prison_relabel(struct ucred *cred, struct prison *pr, struct label *newlabel); void mac_prison_destroy(struct prison *pr); +int mac_prison_check_attach(struct ucred *cred, struct prison *pr); +int mac_prison_check_create(struct ucred *cred, struct vfsoptlist *opts); +int mac_prison_check_get(struct ucred *cred, struct prison *pr, + struct vfsoptlist *opts); +int mac_prison_check_set(struct ucred *cred, struct prison *pr, + struct vfsoptlist *opts); +int mac_prison_check_remove(struct ucred *cred, struct prison *pr); +void mac_prison_created(struct ucred *cred, struct prison *pr); +void mac_prison_attached(struct ucred *cred, struct prison *pr, + struct proc *p); int mac_priv_check_impl(struct ucred *cred, int priv); #ifdef MAC 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 @@ -101,6 +101,7 @@ struct thread; struct ucred; struct vattr; +struct vfsoptlist; struct vnode; struct in_addr; @@ -419,6 +420,23 @@ 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_prison_check_attach_t)(struct ucred *cred, + struct prison *pr, struct label *prlabel); +typedef int (*mpo_prison_check_create_t)(struct ucred *cred, + struct vfsoptlist *opts); +typedef int (*mpo_prison_check_get_t)(struct ucred *cred, + struct prison *pr, struct label *prlabel, + struct vfsoptlist *opts); +typedef int (*mpo_prison_check_set_t)(struct ucred *cred, + struct prison *pr, struct label *prlabel, + struct vfsoptlist *opts); +typedef int (*mpo_prison_check_remove_t)(struct ucred *cred, + struct prison *pr, struct label *prlabel); +typedef void (*mpo_prison_created_t)(struct ucred *cred, + struct prison *pr, struct label *prlabel); +typedef void (*mpo_prison_attached_t)(struct ucred *cred, + struct prison *pr, struct label *prlabel, struct proc *p, + struct label *proclabel); typedef int (*mpo_priv_check_t)(struct ucred *cred, int priv); typedef int (*mpo_priv_grant_t)(struct ucred *cred, int priv); @@ -882,6 +900,13 @@ 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_prison_check_attach_t mpo_prison_check_attach; + mpo_prison_check_create_t mpo_prison_check_create; + mpo_prison_check_get_t mpo_prison_check_get; + mpo_prison_check_set_t mpo_prison_check_set; + mpo_prison_check_remove_t mpo_prison_check_remove; + mpo_prison_created_t mpo_prison_created; + mpo_prison_attached_t mpo_prison_attached; 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 --- a/sys/security/mac/mac_prison.c +++ b/sys/security/mac/mac_prison.c @@ -142,3 +142,87 @@ return (error); } + +MAC_CHECK_PROBE_DEFINE3(prison_check_attach, "struct ucred *", + "struct prison *", "struct label *"); +int +mac_prison_check_attach(struct ucred *cred, struct prison *pr) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(prison_check_attach, cred, pr, pr->pr_label); + MAC_CHECK_PROBE3(prison_check_attach, error, cred, pr, pr->pr_label); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE2(prison_check_create, "struct ucred *", + "struct vfsoptlist *"); +int +mac_prison_check_create(struct ucred *cred, struct vfsoptlist *opts) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(prison_check_create, cred, opts); + MAC_CHECK_PROBE2(prison_check_create, error, cred, opts); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(prison_check_get, "struct ucred *", + "struct prison *", "struct label *", "struct vfsoptlist *"); +int +mac_prison_check_get(struct ucred *cred, struct prison *pr, + struct vfsoptlist *opts) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(prison_check_get, cred, pr, pr->pr_label, + opts); + MAC_CHECK_PROBE4(prison_check_get, error, cred, pr, pr->pr_label, opts); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE4(prison_check_set, "struct ucred *", + "struct prison *", "struct label *", "struct vfsoptlist *"); +int +mac_prison_check_set(struct ucred *cred, struct prison *pr, + struct vfsoptlist *opts) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(prison_check_set, cred, pr, pr->pr_label, + opts); + MAC_CHECK_PROBE4(prison_check_set, error, cred, pr, pr->pr_label, opts); + + return (error); +} + +MAC_CHECK_PROBE_DEFINE3(prison_check_remove, "struct ucred *", + "struct prison *", "struct label *"); +int +mac_prison_check_remove(struct ucred *cred, struct prison *pr) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(prison_check_remove, cred, pr, pr->pr_label); + MAC_CHECK_PROBE3(prison_check_remove, error, cred, pr, pr->pr_label); + + return (error); +} + +void +mac_prison_created(struct ucred *cred, struct prison *pr) +{ + + MAC_POLICY_PERFORM(prison_created, cred, pr, pr->pr_label); +} + +void +mac_prison_attached(struct ucred *cred, struct prison *pr, struct proc *p) +{ + + MAC_POLICY_PERFORM(prison_attached, cred, pr, pr->pr_label, p, + p->p_label); +} diff --git a/sys/security/mac_stub/mac_stub.c b/sys/security/mac_stub/mac_stub.c --- a/sys/security/mac_stub/mac_stub.c +++ b/sys/security/mac_stub/mac_stub.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -852,6 +853,74 @@ } +static void +stub_prison_relabel(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct label *newlabel) +{ + +} + +static int +stub_prison_check_relabel(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct label *newlabel) +{ + + return (0); +} + +static int +stub_prison_check_attach(struct ucred *cred, struct prison *pr, + struct label *prlabel) +{ + + return (0); +} + +static int +stub_prison_check_create(struct ucred *cred, struct vfsoptlist *opts) +{ + + return (0); +} + +static int +stub_prison_check_get(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct vfsoptlist *opts) +{ + + return (0); +} + +static int +stub_prison_check_set(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct vfsoptlist *opts) +{ + + return (0); +} + +static int +stub_prison_check_remove(struct ucred *cred, struct prison *pr, + struct label *prlabel) +{ + + return (0); +} + +static void +stub_prison_created(struct ucred *cred, struct prison *pr, + struct label *prlabel) +{ + +} + +static void +stub_prison_attached(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct proc *p, struct label *proclabel) +{ + +} + static int stub_priv_check(struct ucred *cred, int priv) { @@ -1841,6 +1910,20 @@ .mpo_posixshm_destroy_label = stub_destroy_label, .mpo_posixshm_init_label = stub_init_label, + .mpo_prison_init_label = stub_init_label_waitcheck, + .mpo_prison_destroy_label = stub_destroy_label, + .mpo_prison_externalize_label = stub_externalize_label, + .mpo_prison_internalize_label = stub_internalize_label, + .mpo_prison_relabel = stub_prison_relabel, + .mpo_prison_check_relabel = stub_prison_check_relabel, + .mpo_prison_check_attach = stub_prison_check_attach, + .mpo_prison_check_create = stub_prison_check_create, + .mpo_prison_check_get = stub_prison_check_get, + .mpo_prison_check_set = stub_prison_check_set, + .mpo_prison_check_remove = stub_prison_check_remove, + .mpo_prison_created = stub_prison_created, + .mpo_prison_attached = stub_prison_attached, + .mpo_priv_check = stub_priv_check, .mpo_priv_grant = stub_priv_grant, diff --git a/sys/security/mac_test/mac_test.c b/sys/security/mac_test/mac_test.c --- a/sys/security/mac_test/mac_test.c +++ b/sys/security/mac_test/mac_test.c @@ -51,6 +51,7 @@ #include #include +#include #include #include #include @@ -99,6 +100,7 @@ #define MAGIC_PIPE 0xdc6c9919 #define MAGIC_POSIX_SEM 0x78ae980c #define MAGIC_POSIX_SHM 0x4e853fc9 +#define MAGIC_PRISON 0x9639acdb #define MAGIC_PROC 0x3b4be98f #define MAGIC_CRED 0x9a5a4987 #define MAGIC_VNODE 0x1a67a45c @@ -1591,6 +1593,151 @@ COUNTER_INC(posixshm_init_label); } +COUNTER_DECL(prison_init_label); +static int +test_prison_init_label(struct label *label, int flag) +{ + + if (flag & M_WAITOK) + WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, + "test_prison_init_label() at %s:%d", __FILE__, + __LINE__); + + LABEL_INIT(label, MAGIC_PRISON); + COUNTER_INC(prison_init_label); + return (0); +} + +COUNTER_DECL(prison_destroy_label); +static void +test_prison_destroy_label(struct label *label) +{ + + LABEL_DESTROY(label, MAGIC_PRISON); + COUNTER_INC(prison_destroy_label); +} + +COUNTER_DECL(prison_externalize_label); +static int +test_prison_externalize_label(struct label *label, char *element_name, + struct sbuf *sb, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_PRISON); + COUNTER_INC(prison_externalize_label); + + return (0); +} + +COUNTER_DECL(prison_internalize_label); +static int +test_prison_internalize_label(struct label *label, char *element_name, + char *element_data, int *claimed) +{ + + LABEL_CHECK(label, MAGIC_PRISON); + COUNTER_INC(prison_internalize_label); + + return (0); +} + +COUNTER_DECL(prison_relabel); +static void +test_prison_relabel(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct label *newlabel) +{ + + LABEL_CHECK(prlabel, MAGIC_PRISON); + LABEL_CHECK(newlabel, MAGIC_PRISON); + COUNTER_INC(prison_relabel); +} + +COUNTER_DECL(prison_check_relabel); +static int +test_prison_check_relabel(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct label *newlabel) +{ + + LABEL_CHECK(prlabel, MAGIC_PRISON); + LABEL_CHECK(newlabel, MAGIC_PRISON); + COUNTER_INC(prison_check_relabel); + return (0); +} + +COUNTER_DECL(prison_check_attach); +static int +test_prison_check_attach(struct ucred *cred, struct prison *pr, + struct label *prlabel) +{ + + LABEL_CHECK(prlabel, MAGIC_PRISON); + COUNTER_INC(prison_check_attach); + return (0); +} + +COUNTER_DECL(prison_check_create); +static int +test_prison_check_create(struct ucred *cred, struct vfsoptlist *opts) +{ + + COUNTER_INC(prison_check_create); + return (0); +} + +COUNTER_DECL(prison_check_get); +static int +test_prison_check_get(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct vfsoptlist *opts) +{ + + LABEL_CHECK(prlabel, MAGIC_PRISON); + COUNTER_INC(prison_check_get); + return (0); +} + +COUNTER_DECL(prison_check_set); +static int +test_prison_check_set(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct vfsoptlist *opts) +{ + + LABEL_CHECK(prlabel, MAGIC_PRISON); + COUNTER_INC(prison_check_set); + return (0); +} + +COUNTER_DECL(prison_check_remove); +static int +test_prison_check_remove(struct ucred *cred, struct prison *pr, + struct label *prlabel) +{ + + LABEL_CHECK(prlabel, MAGIC_PRISON); + COUNTER_INC(prison_check_remove); + return (0); +} + +COUNTER_DECL(prison_created); +static void +test_prison_created(struct ucred *cred, struct prison *pr, + struct label *prlabel) +{ + + LABEL_CHECK(prlabel, MAGIC_PRISON); + COUNTER_INC(prison_created); +} + +COUNTER_DECL(prison_attached); +static void +test_prison_attached(struct ucred *cred, struct prison *pr, + struct label *prlabel, struct proc *p, struct label *proclabel) +{ + + LABEL_CHECK(prlabel, MAGIC_PRISON); + LABEL_CHECK(proclabel, MAGIC_PROC); + COUNTER_INC(prison_attached); +} + COUNTER_DECL(proc_check_debug); static int test_proc_check_debug(struct ucred *cred, struct proc *p) @@ -3208,6 +3355,20 @@ .mpo_posixshm_destroy_label = test_posixshm_destroy_label, .mpo_posixshm_init_label = test_posixshm_init_label, + .mpo_prison_init_label = test_prison_init_label, + .mpo_prison_destroy_label = test_prison_destroy_label, + .mpo_prison_externalize_label = test_prison_externalize_label, + .mpo_prison_internalize_label = test_prison_internalize_label, + .mpo_prison_relabel = test_prison_relabel, + .mpo_prison_check_relabel = test_prison_check_relabel, + .mpo_prison_check_attach = test_prison_check_attach, + .mpo_prison_check_create = test_prison_check_create, + .mpo_prison_check_get = test_prison_check_get, + .mpo_prison_check_set = test_prison_check_set, + .mpo_prison_check_remove = test_prison_check_remove, + .mpo_prison_created = test_prison_created, + .mpo_prison_attached = test_prison_attached, + .mpo_proc_check_debug = test_proc_check_debug, .mpo_proc_check_sched = test_proc_check_sched, .mpo_proc_check_signal = test_proc_check_signal,