summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorBob Beck <beck@cvs.openbsd.org>2019-01-03 21:52:32 +0000
committerBob Beck <beck@cvs.openbsd.org>2019-01-03 21:52:32 +0000
commita80f96e99892275f32e8240770ead14bd5114e6f (patch)
treecfa92e1e78c732b4c8a9ec5bec4ccaa7f8927e7f /sys/kern
parente8944bb89356dbfd29a3d47b7a73fe025c80f844 (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/kern')
-rw-r--r--sys/kern/kern_unveil.c37
-rw-r--r--sys/kern/vfs_lookup.c7
-rw-r--r--sys/kern/vfs_syscalls.c16
3 files changed, 38 insertions, 22 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;