Page MenuHomeFreeBSD

D60963.1794548498.diff
No OneTemporary

Authored By
Unknown
Size
5 KB
Referenced Files
None
Subscribers
None

D60963.1794548498.diff

diff --git a/lib/libsys/cap_enter.2 b/lib/libsys/cap_enter.2
--- a/lib/libsys/cap_enter.2
+++ b/lib/libsys/cap_enter.2
@@ -64,6 +64,10 @@
may be used to create kernel-enforced sandboxes in which
appropriately-crafted applications or application components may be run.
.Pp
+Other threads are stopped while the call runs, so some of their
+interrupted system calls may return
+.Er EINTR .
+.Pp
.Fn cap_getmode
returns a flag indicating whether or not the process is in a capability mode
sandbox.
@@ -116,6 +120,18 @@
.El
.Pp
The
+.Fn cap_enter
+system call may also return the following error:
+.Bl -tag -width Er
+.It Bq Er EBUSY
+The file descriptor table or the current/root directory state of the
+process is shared with another process, see
+.Xr rfork 2 .
+Sharing would allow the other process to grant additional privileges
+to the process after it entered capability mode, without its consent.
+.El
+.Pp
+The
.Fn cap_getmode
system call may also return the following error:
.Bl -tag -width Er
diff --git a/lib/libsys/chroot.2 b/lib/libsys/chroot.2
--- a/lib/libsys/chroot.2
+++ b/lib/libsys/chroot.2
@@ -126,6 +126,11 @@
.Dv PROC_NO_NEW_PRIVS_CTL
.Xr procctl 2 .
.It Bq Er EPERM
+The effective user ID is not the super-user and the
+current/root directory state of the process is shared with
+another process, see
+.Xr rfork 2 .
+.It Bq Er EPERM
One or more file descriptors are open directories and the
.Ql Va kern.chroot_allow_open_directories
sysctl is not set to permit this.
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -2446,6 +2446,7 @@
PWDDESC_LOCK_INIT(newpdp);
refcount_init(&newpdp->pd_refcount, 1);
+ refcount_init(&newpdp->pd_sharecount, 1);
newpdp->pd_cmask = CMASK;
if (pdp == NULL) {
@@ -2527,6 +2528,7 @@
PWDDESC_XUNLOCK(pdp);
return;
}
+ MPASS(refcount_load(&pdp->pd_sharecount) == 0);
pwd = PWDDESC_XLOCKED_LOAD_PWD(pdp);
pwd_set(pdp, NULL);
PWDDESC_XUNLOCK(pdp);
@@ -2553,7 +2555,11 @@
struct pwddesc *
pdshare(struct pwddesc *pdp)
{
+ u_int old __diagused;
+
refcount_acquire(&pdp->pd_refcount);
+ old = refcount_acquire(&pdp->pd_sharecount);
+ MPASS(old > 0);
return (pdp);
}
@@ -2585,7 +2591,7 @@
p = td->td_proc;
/* Not shared. */
- if (refcount_load(&p->p_pd->pd_refcount) == 1)
+ if (refcount_load(&p->p_pd->pd_sharecount) == 1)
return;
pdp = pdcopy(p->p_pd);
@@ -2871,6 +2877,7 @@
atomic_thread_fence_seq_cst();
PROC_WAIT_UNLOCKED(p);
+ (void)refcount_release(&pdp->pd_sharecount);
pddrop(pdp);
}
diff --git a/sys/kern/sys_capability.c b/sys/kern/sys_capability.c
--- a/sys/kern/sys_capability.c
+++ b/sys/kern/sys_capability.c
@@ -102,19 +102,35 @@
{
struct ucred *newcred, *oldcred;
struct proc *p;
+ int error;
if (IN_CAPABILITY_MODE(td))
return (0);
- newcred = crget();
+ error = 0;
+ oldcred = newcred = crget();
p = td->td_proc;
PROC_LOCK(p);
+ if ((p->p_flag & P_HADTHREADS) != 0 &&
+ thread_single(p, SINGLE_BOUNDARY)) {
+ error = ERESTART;
+ goto out1;
+ }
+ if (refcount_load(&p->p_pd->pd_sharecount) > 1 ||
+ refcount_load(&p->p_fd->fd_refcnt) > 1) {
+ error = EBUSY;
+ goto out;
+ }
oldcred = crcopysafe(p, newcred);
newcred->cr_flags |= CRED_FLAG_CAPMODE;
proc_set_cred(p, newcred);
+out:
+ if ((p->p_flag & P_HADTHREADS) != 0)
+ thread_single_end(p, SINGLE_BOUNDARY);
+out1:
PROC_UNLOCK(p);
crfree(oldcred);
- return (0);
+ return (error);
}
/*
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -969,31 +969,51 @@
&unprivileged_chroot, 0,
"Unprivileged processes can use chroot(2)");
+static int
+chroot_check_priv(struct thread *td)
+{
+ struct proc *p;
+
+ if (priv_check(td, PRIV_VFS_CHROOT) == 0)
+ return (0);
+ if (unprivileged_chroot == 0)
+ return (EXTERROR(EPERM,
+ "security.bsd.unprivileged_chroot sysctl not enabled"));
+ p = td->td_proc;
+ if ((p->p_flag2 & P2_NO_NEW_PRIVS) == 0)
+ return (EXTERROR(EPERM,
+ "PROC_NO_NEW_PRIVS not enabled"));
+
+ /*
+ * Refuse chroot() when the pwd is shared with other
+ * processes, since they might have been created before
+ * P2_NO_NEW_PRIVS was set.
+ *
+ * The check races with fork(), but the race is harmless. If
+ * pd_sharecount was observed as 1 and another thread forked
+ * in the meantime, the child inherited P2_NO_NEW_PRIVS. Then
+ * neither the parent nor the child can gain privileges by
+ * executing a set-ID image, which prevents the issues
+ * otherwise possible due to the pwd sharing.
+ */
+ atomic_thread_fence_acq();
+ if (refcount_load(&p->p_pd->pd_sharecount) > 1)
+ return (EXTERROR(EPERM,
+ "root/current directory shared with another process"));
+ return (0);
+}
+
/*
* Takes locked vnode, unlocks it before returning.
*/
static int
kern_chroot(struct thread *td, struct vnode *vp)
{
- struct proc *p;
int error;
- error = priv_check(td, PRIV_VFS_CHROOT);
- if (error != 0) {
- p = td->td_proc;
- if (unprivileged_chroot == 0) {
- error = EXTERROR(EPERM,
- "security.bsd.unprivileged_chroot sysctl not enabled");
- goto e_vunlock;
- }
- if ((p->p_flag2 & P2_NO_NEW_PRIVS) == 0) {
- error = EXTERROR(EPERM,
- "PROC_NO_NEW_PRIVS not enabled");
- goto e_vunlock;
- }
- }
-
- error = change_dir(vp, td);
+ error = chroot_check_priv(td);
+ if (error == 0)
+ error = change_dir(vp, td);
if (error != 0)
goto e_vunlock;
#ifdef MAC
diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h
--- a/sys/sys/filedesc.h
+++ b/sys/sys/filedesc.h
@@ -103,7 +103,8 @@
struct pwddesc {
struct mtx pd_lock; /* protects members of this struct */
smrpwd_t pd_pwd; /* directories */
- u_int pd_refcount;
+ u_int pd_refcount; /* lifetime */
+ u_int pd_sharecount; /* processes sharing the descriptor */
u_short pd_cmask; /* mask for file creation */
};

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 13, 5:41 AM (20 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41693418
Default Alt Text
D60963.1794548498.diff (5 KB)

Event Timeline