git: 0fe74ae624fc - main - jail: Consistently handle the pr_allow bitmask
Jamie Gritton
jamie at FreeBSD.org
Sun Dec 27 04:25:19 UTC 2020
The branch main has been updated by jamie:
URL: https://cgit.FreeBSD.org/src/commit/?id=0fe74ae624fcbd9378eeee30f257b08f4eae5abc
commit 0fe74ae624fcbd9378eeee30f257b08f4eae5abc
Author: Jamie Gritton <jamie at FreeBSD.org>
AuthorDate: 2020-12-27 04:25:02 +0000
Commit: Jamie Gritton <jamie at FreeBSD.org>
CommitDate: 2020-12-27 04:25:02 +0000
jail: Consistently handle the pr_allow bitmask
Return a boolean (i.e. 0 or 1) from prison_allow, instead of the flag
value itself, which is what sysctl expects.
Add prison_set_allow(), which can set or clear a permission bit, and
propagates cleared bits down to child jails.
Use prison_allow() and prison_set_allow() in the various jail.allow.*
sysctls, and others that depend on thoe permissions.
Add locking around checking both pr_allow and pr_enforce_statfs in
prison_priv_check().
---
sys/kern/kern_jail.c | 77 +++++++++++++++++++++++++++++++++++++++-------------
sys/kern/kern_mib.c | 37 +++++++++++++++----------
sys/kern/kern_priv.c | 31 ++-------------------
sys/kern/kern_prot.c | 22 ++++-----------
sys/sys/jail.h | 1 +
5 files changed, 89 insertions(+), 79 deletions(-)
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
index bdcebcdfaf6c..a140b6f537d1 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -140,6 +140,8 @@ static int get_next_prid(struct prison **insprp);
static int do_jail_attach(struct thread *td, struct prison *pr);
static void prison_complete(void *context, int pending);
static void prison_deref(struct prison *pr, int flags);
+static void prison_set_allow_locked(struct prison *pr, unsigned flag,
+ int enable);
static char *prison_path(struct prison *pr1, struct prison *pr2);
static void prison_remove_one(struct prison *pr);
#ifdef RACCT
@@ -1726,12 +1728,9 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
}
}
}
- if ((tallow = ch_allow & ~pr_allow)) {
- /* Clear allow bits in all children. */
- FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend)
- tpr->pr_allow &= ~tallow;
- }
pr->pr_allow = (pr->pr_allow & ~ch_allow) | pr_allow;
+ if ((tallow = ch_allow & ~pr_allow))
+ prison_set_allow_locked(pr, tallow, 0);
/*
* Persistent prisons get an extra reference, and prisons losing their
* persist flag lose that reference. Only do this for existing prisons
@@ -2589,13 +2588,15 @@ prison_find_name(struct prison *mypr, const char *name)
}
/*
- * See if a prison has the specific flag set.
+ * See if a prison has the specific flag set. The prison should be locked,
+ * unless checking for flags that are only set at jail creation (such as
+ * PR_IP4 and PR_IP6), or only the single bit is examined, without regard
+ * to any other prison data.
*/
int
prison_flag(struct ucred *cred, unsigned flag)
{
- /* This is an atomic read, so no locking is necessary. */
return (cred->cr_prison->pr_flags & flag);
}
@@ -2603,8 +2604,7 @@ int
prison_allow(struct ucred *cred, unsigned flag)
{
- /* This is an atomic read, so no locking is necessary. */
- return (cred->cr_prison->pr_allow & flag);
+ return ((cred->cr_prison->pr_allow & flag) != 0);
}
/*
@@ -2802,6 +2802,38 @@ prison_proc_free(struct prison *pr)
mtx_unlock(&pr->pr_mtx);
}
+/*
+ * Set or clear a permission bit in the pr_allow field, passing restrictions
+ * (cleared permission) down to child jails.
+ */
+void
+prison_set_allow(struct ucred *cred, unsigned flag, int enable)
+{
+ struct prison *pr;
+
+ pr = cred->cr_prison;
+ sx_slock(&allprison_lock);
+ mtx_lock(&pr->pr_mtx);
+ prison_set_allow_locked(pr, flag, enable);
+ mtx_unlock(&pr->pr_mtx);
+ sx_sunlock(&allprison_lock);
+}
+
+static void
+prison_set_allow_locked(struct prison *pr, unsigned flag, int enable)
+{
+ struct prison *cpr;
+ int descend;
+
+ if (enable != 0)
+ pr->pr_allow |= flag;
+ else {
+ pr->pr_allow &= ~flag;
+ FOREACH_PRISON_DESCENDANT_LOCKED(pr, cpr, descend)
+ cpr->pr_allow &= ~flag;
+ }
+}
+
/*
* Check if a jail supports the given address family.
*
@@ -3117,6 +3149,8 @@ prison_enforce_statfs(struct ucred *cred, struct mount *mp, struct statfs *sp)
int
prison_priv_check(struct ucred *cred, int priv)
{
+ struct prison *pr;
+ int error;
/*
* Some policies have custom handlers. This routine should not be
@@ -3388,11 +3422,14 @@ prison_priv_check(struct ucred *cred, int priv)
case PRIV_VFS_UNMOUNT:
case PRIV_VFS_MOUNT_NONUSER:
case PRIV_VFS_MOUNT_OWNER:
- if (cred->cr_prison->pr_allow & PR_ALLOW_MOUNT &&
- cred->cr_prison->pr_enforce_statfs < 2)
- return (0);
+ pr = cred->cr_prison;
+ prison_lock(pr);
+ if (pr->pr_allow & PR_ALLOW_MOUNT && pr->pr_enforce_statfs < 2)
+ error = 0;
else
- return (EPERM);
+ error = EPERM;
+ prison_unlock(pr);
+ return (error);
/*
* Jails should hold no disposition on the PRIV_VFS_READ_DIR
@@ -3685,14 +3722,16 @@ SYSCTL_UINT(_security_jail, OID_AUTO, jail_max_af_ips, CTLFLAG_RW,
static int
sysctl_jail_default_allow(SYSCTL_HANDLER_ARGS)
{
- struct prison *pr;
- int allow, error, i;
-
- pr = req->td->td_ucred->cr_prison;
- allow = (pr == &prison0) ? jail_default_allow : pr->pr_allow;
+ int error, i;
/* Get the current flag value, and convert it to a boolean. */
- i = (allow & arg2) ? 1 : 0;
+ if (req->td->td_ucred->cr_prison == &prison0) {
+ mtx_lock(&prison0.pr_mtx);
+ i = (jail_default_allow & arg2) != 0;
+ mtx_unlock(&prison0.pr_mtx);
+ } else
+ i = prison_allow(req->td->td_ucred, arg2);
+
if (arg1 != NULL)
i = !i;
error = sysctl_handle_int(oidp, &i, 0, req);
diff --git a/sys/kern/kern_mib.c b/sys/kern/kern_mib.c
index abd04b47023b..483bbe453b0c 100644
--- a/sys/kern/kern_mib.c
+++ b/sys/kern/kern_mib.c
@@ -346,25 +346,27 @@ sysctl_hostname(SYSCTL_HANDLER_ARGS)
KASSERT(len <= sizeof(tmpname),
("length %d too long for %s", len, __func__));
- pr = req->td->td_ucred->cr_prison;
- if (!(pr->pr_allow & PR_ALLOW_SET_HOSTNAME) && req->newptr)
- return (EPERM);
/*
* Make a local copy of hostname to get/set so we don't have to hold
* the jail mutex during the sysctl copyin/copyout activities.
*/
+ pr = req->td->td_ucred->cr_prison;
mtx_lock(&pr->pr_mtx);
bcopy((char *)pr + pr_offset, tmpname, len);
mtx_unlock(&pr->pr_mtx);
error = sysctl_handle_string(oidp, tmpname, len, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
- if (req->newptr != NULL && error == 0) {
- /*
- * Copy the locally set hostname to all jails that share
- * this host info.
- */
- sx_slock(&allprison_lock);
+ /*
+ * Copy the locally set hostname to all jails that share
+ * this host info.
+ */
+ sx_slock(&allprison_lock);
+ if (!(pr->pr_allow & PR_ALLOW_SET_HOSTNAME))
+ error = EPERM;
+ else {
while (!(pr->pr_flags & PR_HOST))
pr = pr->pr_parent;
mtx_lock(&pr->pr_mtx);
@@ -375,8 +377,8 @@ sysctl_hostname(SYSCTL_HANDLER_ARGS)
else
bcopy(tmpname, (char *)cpr + pr_offset, len);
mtx_unlock(&pr->pr_mtx);
- sx_sunlock(&allprison_lock);
}
+ sx_sunlock(&allprison_lock);
return (error);
}
@@ -465,13 +467,18 @@ sysctl_hostid(SYSCTL_HANDLER_ARGS)
* instead of a string, and is used only for hostid.
*/
pr = req->td->td_ucred->cr_prison;
- if (!(pr->pr_allow & PR_ALLOW_SET_HOSTNAME) && req->newptr)
- return (EPERM);
+ mtx_lock(&pr->pr_mtx);
tmpid = pr->pr_hostid;
+ mtx_unlock(&pr->pr_mtx);
+
error = sysctl_handle_long(oidp, &tmpid, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
- if (req->newptr != NULL && error == 0) {
- sx_slock(&allprison_lock);
+ sx_slock(&allprison_lock);
+ if (!(pr->pr_allow & PR_ALLOW_SET_HOSTNAME))
+ error = EPERM;
+ else {
while (!(pr->pr_flags & PR_HOST))
pr = pr->pr_parent;
mtx_lock(&pr->pr_mtx);
@@ -482,8 +489,8 @@ sysctl_hostid(SYSCTL_HANDLER_ARGS)
else
cpr->pr_hostid = tmpid;
mtx_unlock(&pr->pr_mtx);
- sx_sunlock(&allprison_lock);
}
+ sx_sunlock(&allprison_lock);
return (error);
}
diff --git a/sys/kern/kern_priv.c b/sys/kern/kern_priv.c
index b621de58f685..c1bd373bcb9e 100644
--- a/sys/kern/kern_priv.c
+++ b/sys/kern/kern_priv.c
@@ -63,46 +63,21 @@ static bool
suser_enabled(struct ucred *cred)
{
- return (prison_allow(cred, PR_ALLOW_SUSER) ? true : false);
-}
-
-static void inline
-prison_suser_set(struct prison *pr, int enabled)
-{
-
- if (enabled) {
- pr->pr_allow |= PR_ALLOW_SUSER;
- } else {
- pr->pr_allow &= ~PR_ALLOW_SUSER;
- }
+ return (prison_allow(cred, PR_ALLOW_SUSER));
}
static int
sysctl_kern_suser_enabled(SYSCTL_HANDLER_ARGS)
{
- struct prison *pr, *cpr;
struct ucred *cred;
- int descend, error, enabled;
+ int error, enabled;
cred = req->td->td_ucred;
enabled = suser_enabled(cred);
-
error = sysctl_handle_int(oidp, &enabled, 0, req);
if (error || !req->newptr)
return (error);
-
- pr = cred->cr_prison;
- sx_slock(&allprison_lock);
- mtx_lock(&pr->pr_mtx);
-
- prison_suser_set(pr, enabled);
- if (!enabled) {
- FOREACH_PRISON_DESCENDANT_LOCKED(pr, cpr, descend) {
- prison_suser_set(cpr, 0);
- }
- }
- mtx_unlock(&pr->pr_mtx);
- sx_sunlock(&allprison_lock);
+ prison_set_allow(cred, PR_ALLOW_SUSER, enabled);
return (0);
}
diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c
index 73b89582230d..529a6de4b2c8 100644
--- a/sys/kern/kern_prot.c
+++ b/sys/kern/kern_prot.c
@@ -1646,28 +1646,16 @@ p_cansched(struct thread *td, struct proc *p)
static int
sysctl_unprivileged_proc_debug(SYSCTL_HANDLER_ARGS)
{
- struct prison *pr;
int error, val;
- val = prison_allow(req->td->td_ucred, PR_ALLOW_UNPRIV_DEBUG) != 0;
+ val = prison_allow(req->td->td_ucred, PR_ALLOW_UNPRIV_DEBUG);
error = sysctl_handle_int(oidp, &val, 0, req);
if (error != 0 || req->newptr == NULL)
return (error);
- pr = req->td->td_ucred->cr_prison;
- mtx_lock(&pr->pr_mtx);
- switch (val) {
- case 0:
- pr->pr_allow &= ~(PR_ALLOW_UNPRIV_DEBUG);
- break;
- case 1:
- pr->pr_allow |= PR_ALLOW_UNPRIV_DEBUG;
- break;
- default:
- error = EINVAL;
- }
- mtx_unlock(&pr->pr_mtx);
-
- return (error);
+ if (val != 0 && val != 1)
+ return (EINVAL);
+ prison_set_allow(req->td->td_ucred, PR_ALLOW_UNPRIV_DEBUG, val);
+ return (0);
}
/*
diff --git a/sys/sys/jail.h b/sys/sys/jail.h
index b95406079ea1..96201b0638b3 100644
--- a/sys/sys/jail.h
+++ b/sys/sys/jail.h
@@ -405,6 +405,7 @@ void prison_hold(struct prison *pr);
void prison_hold_locked(struct prison *pr);
void prison_proc_hold(struct prison *);
void prison_proc_free(struct prison *);
+void prison_set_allow(struct ucred *cred, unsigned flag, int enable);
int prison_ischild(struct prison *, struct prison *);
int prison_equal_ip4(struct prison *, struct prison *);
int prison_get_ip4(struct ucred *cred, struct in_addr *ia);
More information about the dev-commits-src-main
mailing list