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 @@ -235,6 +235,9 @@ {"allow.adjtime", "allow.noadjtime", PR_ALLOW_ADJTIME}, {"allow.settime", "allow.nosettime", PR_ALLOW_SETTIME}, {"allow.routing", "allow.norouting", PR_ALLOW_ROUTING}, + {"allow.unprivileged_subjail_tampering", + "allow.nounprivileged_subjail_tampering", + PR_ALLOW_UNPRIV_SUBJAIL_TAMPER}, }; static unsigned pr_allow_all = PR_ALLOW_ALL_STATIC; const size_t pr_flag_allow_size = sizeof(pr_flag_allow); @@ -4012,6 +4015,7 @@ case PRIV_DEBUG_DIFFCRED: case PRIV_DEBUG_SUGID: case PRIV_DEBUG_UNPRIV: + case PRIV_DEBUG_DIFFJAIL: /* * Allow jail to set various resource limits and login @@ -4049,8 +4053,10 @@ */ case PRIV_SCHED_DIFFCRED: case PRIV_SCHED_CPUSET: + case PRIV_SCHED_DIFFJAIL: case PRIV_SIGNAL_DIFFCRED: case PRIV_SIGNAL_SUGID: + case PRIV_SIGNAL_DIFFJAIL: /* * Allow jailed processes to write to sysctls marked as jail @@ -4694,6 +4700,10 @@ "B", "Jail may read the kernel message buffer"); SYSCTL_JAIL_PARAM(_allow, unprivileged_proc_debug, CTLTYPE_INT | CTLFLAG_RW, "B", "Unprivileged processes may use process debugging facilities"); +SYSCTL_JAIL_PARAM(_allow, unprivileged_subjail_tampering, + CTLTYPE_INT | CTLFLAG_RW, "B", + "Unprivileged processes may use tamper with same-uid processes in subjails" + " (signal/debug/cpuset)"); SYSCTL_JAIL_PARAM(_allow, suser, CTLTYPE_INT | CTLFLAG_RW, "B", "Processes in jail with uid 0 have privilege"); #ifdef VIMAGE diff --git a/sys/kern/kern_priv.c b/sys/kern/kern_priv.c --- a/sys/kern/kern_priv.c +++ b/sys/kern/kern_priv.c @@ -250,15 +250,29 @@ } /* - * Allow unprivileged process debugging on a per-jail basis. - * Do this here instead of prison_priv_check(), so it can also - * apply to prison0. + * Allow unprivileged process debugging and tampering on a per-jail + * basis. Do this here instead of prison_priv_check(), so these can + * also apply to prison0. */ - if (priv == PRIV_DEBUG_UNPRIV) { + switch (priv) { + case PRIV_DEBUG_UNPRIV: if (prison_allow(cred, PR_ALLOW_UNPRIV_DEBUG)) { error = 0; goto out; } + break; + + case PRIV_DEBUG_DIFFJAIL: + case PRIV_SCHED_DIFFJAIL: + case PRIV_SIGNAL_DIFFJAIL: + if (prison_allow(cred, PR_ALLOW_UNPRIV_SUBJAIL_TAMPER)) { + error = 0; + goto out; + } + break; + + default: + break; } return (priv_check_cred_post(cred, priv, error, false)); diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -1890,6 +1890,25 @@ return (ESRCH); } +/* + * Determine if u1 can tamper with the subject specified by u2, if they are in + * different jails and 'unprivileged_subjail_tampering' jail policy allows it. + * + * May be called if u1 and u2 are in the same jail, but it is expected that the + * caller has already done a prison_check() prior to calling it. + * + * Returns: 0 for permitted, EPERM otherwise + */ +static int +cr_can_tamper_with_subjail(struct ucred *u1, struct ucred *u2, int priv) +{ + MPASS(prison_check(u1, u2) == 0); + if (u1->cr_prison == u2->cr_prison) + return (0); + + return (priv_check_cred(u1, priv)); +} + /* * Helper for cr_cansee*() functions to abide by system-wide security.bsd.see_* * policies. Determines if u1 "can see" u2 according to these policies. @@ -2039,6 +2058,19 @@ return (error); } + /* + * At this point, the target may be in a different jail than the + * subject -- the subject must be in a parent jail to the target, + * whether it is prison0 or a subordinate of prison0 that has + * children. Additional privileges are required to allow this, as + * whether the creds are truly equivalent or not must be determined on + * a case-by-case basis. + */ + error = cr_can_tamper_with_subjail(cred, proc->p_ucred, + PRIV_SIGNAL_DIFFJAIL); + if (error) + return (error); + return (0); } @@ -2115,6 +2147,12 @@ if (error) return (error); } + + error = cr_can_tamper_with_subjail(td->td_ucred, p->p_ucred, + PRIV_SCHED_DIFFJAIL); + if (error) + return (error); + return (0); } @@ -2150,6 +2188,34 @@ CTLFLAG_MPSAFE, 0, 0, sysctl_unprivileged_proc_debug, "I", "Unprivileged processes may use process debugging facilities"); +/* + * By default, we do not allow unprivileged processes to tamper with processes + * in subjails, even those run with the same uid. There are valid setups in + * which this might be useful, so we offer a knob to enable it in the security + * namespace to be clear that this has security implications. + */ +static int +sysctl_unprivileged_subjail_tampering(SYSCTL_HANDLER_ARGS) +{ + int error, val; + + val = prison_allow(req->td->td_ucred, PR_ALLOW_UNPRIV_SUBJAIL_TAMPER); + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + if (val != 0 && val != 1) + return (EINVAL); + prison_set_allow(req->td->td_ucred, PR_ALLOW_UNPRIV_SUBJAIL_TAMPER, + val); + return (0); +} + +SYSCTL_PROC(_security_bsd, OID_AUTO, unprivileged_subjail_tampering, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_SECURE | + CTLFLAG_MPSAFE, 0, 0, sysctl_unprivileged_subjail_tampering, "I", + "Unprivileged processes may tamper with same-uid processes in subjails" + " (signal/debug/cpuset)"); + /* * Return true if the object owner/group ids are subset of the active * credentials. @@ -2235,6 +2301,11 @@ return (error); } + error = cr_can_tamper_with_subjail(td->td_ucred, p->p_ucred, + PRIV_DEBUG_DIFFJAIL); + if (error) + return (error); + /* Can't trace init when securelevel > 0. */ if (p == initproc) { error = securelevel_gt(td->td_ucred, 0); diff --git a/sys/sys/jail.h b/sys/sys/jail.h --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -260,6 +260,7 @@ #define PR_ALLOW_ADJTIME 0x00080000 #define PR_ALLOW_SETTIME 0x00100000 #define PR_ALLOW_ROUTING 0x00200000 +#define PR_ALLOW_UNPRIV_SUBJAIL_TAMPER 0x00400000 /* * PR_ALLOW_PRISON0 are the allow flags that we apply by default to prison0, @@ -267,14 +268,16 @@ * build time. PR_ALLOW_ALL_STATIC should contain any bit above that we expect * to be used on the system, while PR_ALLOW_PRISON0 will be some subset of that. */ -#define PR_ALLOW_ALL_STATIC 0x003f87ff -#define PR_ALLOW_PRISON0 (PR_ALLOW_ALL_STATIC) +#define PR_ALLOW_ALL_STATIC 0x007f87ff +#define PR_ALLOW_PRISON0 \ + (PR_ALLOW_ALL_STATIC & ~PR_ALLOW_UNPRIV_SUBJAIL_TAMPER) /* * PR_ALLOW_DIFFERENCES determines which flags are able to be * different between the parent and child jail upon creation. */ -#define PR_ALLOW_DIFFERENCES (PR_ALLOW_UNPRIV_DEBUG) +#define PR_ALLOW_DIFFERENCES \ + (PR_ALLOW_UNPRIV_DEBUG | PR_ALLOW_UNPRIV_SUBJAIL_TAMPER) /* * OSD methods diff --git a/sys/sys/priv.h b/sys/sys/priv.h --- a/sys/sys/priv.h +++ b/sys/sys/priv.h @@ -115,6 +115,7 @@ #define PRIV_DEBUG_SUGID 81 /* Exempt debugging setuid proc. */ #define PRIV_DEBUG_UNPRIV 82 /* Exempt unprivileged debug limit. */ #define PRIV_DEBUG_DENIED 83 /* Exempt P2_NOTRACE. */ +#define PRIV_DEBUG_DIFFJAIL 84 /* Exempt debugging other jails. */ /* * Dtrace privileges. @@ -193,6 +194,7 @@ #define PRIV_SCHED_CPUSET 206 /* Can manipulate cpusets. */ #define PRIV_SCHED_CPUSET_INTR 207 /* Can adjust IRQ to CPU binding. */ #define PRIV_SCHED_IDPRIO 208 /* Can set idle time scheduling. */ +#define PRIV_SCHED_DIFFJAIL 209 /* Exempt scheduling other jails. */ /* * POSIX semaphore privileges. @@ -204,6 +206,7 @@ */ #define PRIV_SIGNAL_DIFFCRED 230 /* Exempt signalling other users. */ #define PRIV_SIGNAL_SUGID 231 /* Non-conserv signal setuid proc. */ +#define PRIV_SIGNAL_DIFFJAIL 232 /* Exempt signalling other jails. */ /* * Sysctl privileges.