diff options
author | Bob Beck <beck@cvs.openbsd.org> | 2019-01-03 21:52:32 +0000 |
---|---|---|
committer | Bob Beck <beck@cvs.openbsd.org> | 2019-01-03 21:52:32 +0000 |
commit | a80f96e99892275f32e8240770ead14bd5114e6f (patch) | |
tree | cfa92e1e78c732b4c8a9ec5bec4ccaa7f8927e7f /sys | |
parent | e8944bb89356dbfd29a3d47b7a73fe025c80f844 (diff) |
Fix a collection of covering unveil bugs that prevent unveil's of upper
level directories from working when you don't traverse into them starting
from /. Most found by brynet@ and a few others.
ok brynet@ deraadt@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/kern_unveil.c | 37 | ||||
-rw-r--r-- | sys/kern/vfs_lookup.c | 7 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 16 | ||||
-rw-r--r-- | sys/sys/proc.h | 3 |
4 files changed, 39 insertions, 24 deletions
diff --git a/sys/kern/kern_unveil.c b/sys/kern/kern_unveil.c index 6d193a80779..a6386a07ad1 100644 --- a/sys/kern/kern_unveil.c +++ b/sys/kern/kern_unveil.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_unveil.c,v 1.17 2018/10/29 00:11:37 deraadt Exp $ */ +/* $OpenBSD: kern_unveil.c,v 1.18 2019/01/03 21:52:31 beck Exp $ */ /* * Copyright (c) 2017-2018 Bob Beck <beck@openbsd.org> @@ -19,6 +19,7 @@ #include <sys/param.h> #include <sys/mount.h> +#include <sys/filedesc.h> #include <sys/proc.h> #include <sys/namei.h> #include <sys/pool.h> @@ -238,22 +239,23 @@ unveil_copy(struct process *parent, struct process *child) if (parent->ps_uvpcwd) child->ps_uvpcwd = child->ps_uvpaths + (parent->ps_uvpcwd - parent->ps_uvpaths); - child->ps_uvpcwdgone = parent->ps_uvpcwdgone; child->ps_uvdone = parent->ps_uvdone; child->ps_uvshrink = parent->ps_uvshrink; } /* * Walk up from vnode dp, until we find a matching unveil, or the root vnode - * returns NULL if no unveil to be found above dp. + * returns -1 if no unveil to be found above dp. */ ssize_t -unveil_find_cover(struct vnode *dp, struct proc *p, struct vnode *rootvnode) +unveil_find_cover(struct vnode *dp, struct proc *p) { - struct vnode *vp = NULL, *parent = NULL; + struct vnode *vp = NULL, *parent = NULL, *root; ssize_t ret = -1; int error; + /* use the correct root to stop at, chrooted or not.. */ + root = p->p_fd->fd_rdir ? p->p_fd->fd_rdir : rootvnode; vp = dp; do { @@ -267,9 +269,19 @@ unveil_find_cover(struct vnode *dp, struct proc *p, struct vnode *rootvnode) .cn_namelen = 2, .cn_consume = 0 }; + + /* + * if we are at the root of a filesystem, take the .. in the + * above filesystem + */ + if (vp != root) + vp = ((vp->v_flag & VROOT) && + vp->v_mount->mnt_vnodecovered) ? + vp->v_mount->mnt_vnodecovered : vp; + if (vget(vp, LK_EXCLUSIVE|LK_RETRY) != 0) return -1; - /* Get parent vnode of dvp using lookup of '..' */ + /* Get parent vnode of vp using lookup of '..' */ /* This returns with vp unlocked but ref'ed*/ error = VOP_LOOKUP(vp, &parent, &cn); if (error) { @@ -291,13 +303,16 @@ unveil_find_cover(struct vnode *dp, struct proc *p, struct vnode *rootvnode) (void) unveil_lookup(parent, p, &ret); vput(parent); + if (ret >= 0) + break; + if (vp == parent) { ret = -1; break; } vp = parent; parent = NULL; - } while (vp != rootvnode); + } while (vp != root); return ret; } @@ -449,7 +464,7 @@ unveil_add_vnode(struct process *pr, struct vnode *vp, struct vnode *rootvnode) pr->ps_uvvcount++; /* find out what we are covered by */ - uv->uv_cover = unveil_find_cover(vp, pr->ps_mainproc, rootvnode); + uv->uv_cover = unveil_find_cover(vp, pr->ps_mainproc); /* * Find anyone covered by what we are covered by @@ -460,7 +475,7 @@ unveil_add_vnode(struct process *pr, struct vnode *vp, struct vnode *rootvnode) if (pr->ps_uvpaths[i].uv_cover == uv->uv_cover) pr->ps_uvpaths[j].uv_cover = unveil_find_cover(pr->ps_uvpaths[j].uv_vp, - pr->ps_mainproc, rootvnode); + pr->ps_mainproc); } return (uv); @@ -702,8 +717,8 @@ unveil_start_relative(struct proc *p, struct nameidata *ni) */ if (uv && (unveil_flagmatch(ni, uv->uv_flags))) { #ifdef DEBUG_UNVEIL - printf("unveil: %s(%d): cwd unveil matches", - p->p_p->ps_comm, p->p_p->ps_pid); + printf("unveil: %s(%d): cwd unveil at %p matches", + p->p_p->ps_comm, p->p_p->ps_pid, uv); #endif ni->ni_unveil_match = uv; } diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index d9596addf93..e9637a7f3e4 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_lookup.c,v 1.75 2018/10/28 22:42:33 beck Exp $ */ +/* $OpenBSD: vfs_lookup.c,v 1.76 2019/01/03 21:52:31 beck Exp $ */ /* $NetBSD: vfs_lookup.c,v 1.17 1996/02/09 19:00:59 christos Exp $ */ /* @@ -188,8 +188,6 @@ fail: * Check if starting from root directory or current directory. */ if (cnp->cn_pnbuf[0] == '/') { - curproc->p_p->ps_uvpcwd = NULL; - curproc->p_p->ps_uvpcwdgone = 0; dp = ndp->ni_rootdir; vref(dp); } else if (ndp->ni_dirfd == AT_FDCWD) { @@ -304,7 +302,6 @@ badlink: dp = ndp->ni_rootdir; vref(dp); ndp->ni_unveil_match = NULL; - curproc->p_p->ps_uvpcwd = NULL; unveil_check_component(p, ndp, dp); } } @@ -500,8 +497,6 @@ dirloop: ndp->ni_dvp = dp; ndp->ni_vp = dp; vref(dp); - curproc->p_p->ps_uvpcwd = NULL; - curproc->p_p->ps_uvpcwdgone = 0; ndp->ni_unveil_match = NULL; goto nextname; } diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 101bd3aca24..254974344b5 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_syscalls.c,v 1.309 2018/12/23 10:46:51 natano Exp $ */ +/* $OpenBSD: vfs_syscalls.c,v 1.310 2019/01/03 21:52:31 beck Exp $ */ /* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ */ /* @@ -92,6 +92,8 @@ int dofutimens(struct proc *, int, struct timespec [2]); int dounmount_leaf(struct mount *, int, struct proc *); int unveil_add(struct proc *, struct nameidata *, const char *); void unveil_removevnode(struct vnode *vp); +ssize_t unveil_find_cover(struct vnode *, struct proc *); +struct unveil *unveil_lookup(struct vnode *, struct proc *, ssize_t *); /* * Virtual File System System Calls @@ -342,8 +344,6 @@ checkdirs(struct vnode *olddp) fdp->fd_rdir = newdp; } pr->ps_uvpcwd = NULL; - /* XXX */ - pr->ps_uvpcwdgone = 1; } if (rootvnode == olddp) { free_count++; @@ -801,7 +801,6 @@ sys_chdir(struct proc *p, void *v, register_t *retval) if ((error = change_dir(&nd, p)) != 0) return (error); p->p_p->ps_uvpcwd = nd.ni_unveil_match; - p->p_p->ps_uvpcwdgone = 0; old_cdir = fdp->fd_cdir; fdp->fd_cdir = nd.ni_vp; vrele(old_cdir); @@ -932,8 +931,15 @@ sys_unveil(struct proc *p, void *v, register_t *retval) if (nd.ni_dvp && nd.ni_dvp != nd.ni_vp) VOP_UNLOCK(nd.ni_dvp); - if (allow) + if (allow) { error = unveil_add(p, &nd, permissions); + p->p_p->ps_uvpcwd = unveil_lookup(p->p_fd->fd_cdir, p, NULL); + if (p->p_p->ps_uvpcwd == NULL) { + ssize_t i = unveil_find_cover(p->p_fd->fd_cdir, p); + if (i >= 0) + p->p_p->ps_uvpcwd = &p->p_p->ps_uvpaths[i]; + } + } else error = EPERM; diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 5c7f381bb1c..1273ee2910a 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.261 2018/11/12 15:09:17 visa Exp $ */ +/* $OpenBSD: proc.h,v 1.262 2019/01/03 21:52:31 beck Exp $ */ /* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */ /*- @@ -211,7 +211,6 @@ struct process { size_t ps_uvncount; /* count of unveil names allocated */ int ps_uvshrink; /* do we need to shrink vnode list */ int ps_uvdone; /* no more unveil is permitted */ - int ps_uvpcwdgone; /* need to reevaluate cwd unveil */ /* End area that is zeroed on creation. */ #define ps_endzero ps_startcopy |