diff options
author | Bob Beck <beck@cvs.openbsd.org> | 2016-04-28 14:25:09 +0000 |
---|---|---|
committer | Bob Beck <beck@cvs.openbsd.org> | 2016-04-28 14:25:09 +0000 |
commit | fce521a545176443545811717213822d0651a03e (patch) | |
tree | 7d6d4d18dd6d06dc40a644b1c240649b70006990 /sys/kern/vfs_lookup.c | |
parent | adc2de426ae23e1ae6dffc2ae80c3e8485d2b857 (diff) |
1) Split pledge whitelist path handling out of pledge_namei() and into
pledge_namei_wlpath(). Call the wlpath check only at the end of namei
after the namei lookup would otherwise succeed.
2) Add support to namei to keep the path that was looked up, without the
symlinks in it, and use that path for whitelist path lookups. This
means that paths in pledge whitelists will need to always be the
real path to an intended file to whitelist, without symlinks. Any
symlinks to the "real" file will then be allowed
ok deraadt@ semarie@
Diffstat (limited to 'sys/kern/vfs_lookup.c')
-rw-r--r-- | sys/kern/vfs_lookup.c | 132 |
1 files changed, 118 insertions, 14 deletions
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 245fc1ebfe4..fa921d04b22 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_lookup.c,v 1.61 2016/04/25 20:00:33 tedu Exp $ */ +/* $OpenBSD: vfs_lookup.c,v 1.62 2016/04/28 14:25:08 beck Exp $ */ /* $NetBSD: vfs_lookup.c,v 1.17 1996/02/09 19:00:59 christos Exp $ */ /* @@ -52,12 +52,49 @@ #include <sys/pledge.h> #include <sys/file.h> #include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/malloc.h> #ifdef KTRACE #include <sys/ktrace.h> #endif +void +push_component(struct nameidata *ndp, char *cp, size_t cplen) +{ + KASSERT(cplen < MAXPATHLEN); + if (ndp->ni_p_size - ndp->ni_p_length <= cplen) { + char *tmp = malloc(ndp->ni_p_size + MAXPATHLEN, M_TEMP, M_WAITOK); + memcpy(tmp, ndp->ni_p_path, ndp->ni_p_length); + ndp->ni_p_next = tmp + (ndp->ni_p_next - ndp->ni_p_path); + ndp->ni_p_prev = tmp + (ndp->ni_p_prev - ndp->ni_p_path); + free(ndp->ni_p_path, M_TEMP, ndp->ni_p_size); + ndp->ni_p_path = tmp; + ndp->ni_p_size += MAXPATHLEN; + } + memcpy(ndp->ni_p_next, cp, cplen); + ndp->ni_p_prev = ndp->ni_p_next; + ndp->ni_p_next += cplen; + ndp->ni_p_length += cplen; + *ndp->ni_p_next = '\0'; +#ifdef NAMEI_DIAGNOSTIC_PLEDGE + printf("push: ndp->ni_p_path = %s\n", ndp->ni_p_path); +#endif +} + +void +pop_symlink(struct nameidata *ndp) +{ + KASSERT(ndp->ni_p_next != ndp->ni_p_prev); + ndp->ni_p_length = ndp->ni_p_prev - ndp->ni_p_path; + ndp->ni_p_next = ndp->ni_p_prev; + *ndp->ni_p_next = '\0'; +#ifdef NAMEI_DIAGNOSTIC_PLEDGE + printf("pop: ndp->ni_p_path = %s\n", ndp->ni_p_path); +#endif +} + /* * Convert a pathname into a pointer to a vnode. * @@ -158,28 +195,51 @@ fail: */ if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL) ndp->ni_rootdir = rootvnode; - + error = pledge_namei(p, ndp, cnp->cn_pnbuf); if (error) goto fail; /* + * Decide if we need to call pledge_namei_wlpath after namei lookup, if + * so give namei a place to store a path for it to look at. + */ + if (!ISSET(p->p_p->ps_flags, PS_COREDUMP) && + ISSET(p->p_p->ps_flags, PS_PLEDGE) && p->p_p->ps_pledgepaths) { + ndp->ni_p_path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); + ndp->ni_p_next = ndp->ni_p_prev = ndp->ni_p_path; + ndp->ni_p_size = MAXPATHLEN; + ndp->ni_p_length = 0; + } else { + ndp->ni_p_path = NULL; + ndp->ni_p_size = 0; + } + + /* * Check if starting from root directory or current directory. */ if (cnp->cn_pnbuf[0] == '/') { dp = ndp->ni_rootdir; vref(dp); + if (ndp->ni_p_path != NULL) { + *ndp->ni_p_path = '/'; + ndp->ni_p_next++; + *ndp->ni_p_next = '\0'; + ndp->ni_p_length = 1; + } } else if (ndp->ni_dirfd == AT_FDCWD) { dp = fdp->fd_cdir; vref(dp); } else { struct file *fp = fd_getfile(fdp, ndp->ni_dirfd); if (fp == NULL) { + free(ndp->ni_p_path, M_TEMP, ndp->ni_p_size); pool_put(&namei_pool, cnp->cn_pnbuf); return (EBADF); } dp = (struct vnode *)fp->f_data; if (fp->f_type != DTYPE_VNODE || dp->v_type != VDIR) { + free(ndp->ni_p_path, M_TEMP, ndp->ni_p_size); pool_put(&namei_pool, cnp->cn_pnbuf); return (ENOTDIR); } @@ -188,12 +248,14 @@ fail: for (;;) { if (!dp->v_mount) { /* Give up if the directory is no longer mounted */ + free(ndp->ni_p_path, M_TEMP, ndp->ni_p_size); pool_put(&namei_pool, cnp->cn_pnbuf); return (ENOENT); } cnp->cn_nameptr = cnp->cn_pnbuf; ndp->ni_startdir = dp; if ((error = vfs_lookup(ndp)) != 0) { + free(ndp->ni_p_path, M_TEMP, ndp->ni_p_size); pool_put(&namei_pool, cnp->cn_pnbuf); return (error); } @@ -201,11 +263,24 @@ fail: * If not a symbolic link, return search result. */ if ((cnp->cn_flags & ISSYMLINK) == 0) { + error = pledge_namei_wlpath(p, ndp); + if (error) { +#ifdef NAMEI_DIAGNOSTIC_PLEDGE + printf("pledge_namei error %d for path %s\n", error, ndp->ni_p_path); +#endif + free(ndp->ni_p_path, M_TEMP, ndp->ni_p_size); + pool_put(&namei_pool, cnp->cn_pnbuf); + if (ndp->ni_vp) + vput(ndp->ni_vp); + ndp->ni_vp = NULL; + return(error); + } + free(ndp->ni_p_path, M_TEMP, ndp->ni_p_size); if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) pool_put(&namei_pool, cnp->cn_pnbuf); else cnp->cn_flags |= HASBUF; - return (0); + return(0); } if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN)) VOP_UNLOCK(ndp->ni_dvp, p); @@ -258,9 +333,17 @@ badlink: vrele(dp); dp = ndp->ni_rootdir; vref(dp); + if (ndp->ni_p_path != NULL) { + ndp->ni_p_next = ndp->ni_p_prev = ndp->ni_p_path; + *ndp->ni_p_path = '/'; + ndp->ni_p_next++; + *ndp->ni_p_next = '\0'; + ndp->ni_p_length = 1; + } } } pool_put(&namei_pool, cnp->cn_pnbuf); + free(ndp->ni_p_path, M_TEMP, ndp->ni_p_size); vrele(ndp->ni_dvp); vput(ndp->ni_vp); ndp->ni_vp = NULL; @@ -392,11 +475,17 @@ dirloop: } #ifdef NAMEI_DIAGNOSTIC +#ifdef NAMEI_DIAGNOSTIC_PLEDGE + if (p && p->p_p && p->p_p->ps_pledgepaths) +#endif { char c = *cp; *cp = '\0'; printf("{%s}: ", cnp->cn_nameptr); *cp = c; } #endif + if (ndp->ni_p_path != NULL) + push_component(ndp, cnp->cn_nameptr, cnp->cn_namelen); + ndp->ni_pathlen -= cnp->cn_namelen; ndp->ni_next = cp; /* @@ -478,7 +567,10 @@ dirloop: panic("leaf should be empty"); #endif #ifdef NAMEI_DIAGNOSTIC - printf("not found\n"); +#ifdef NAMEI_DIAGNOSTIC_PLEDGE + if (p && p->p_p && p->p_p->ps_pledgepaths) +#endif + printf("not found\n"); #endif if (error != EJUSTRETURN) goto bad; @@ -510,7 +602,10 @@ dirloop: return (0); } #ifdef NAMEI_DIAGNOSTIC - printf("found\n"); +#ifdef NAMEI_DIAGNOSTIC_PLEDGE + if (p && p->p_p && p->p_p->ps_pledgepaths) +#endif + printf("found\n"); #endif /* @@ -558,6 +653,8 @@ dirloop: ndp->ni_pathlen += slashes; ndp->ni_next -= slashes; cnp->cn_flags |= ISSYMLINK; + if (ndp->ni_p_path != NULL) + pop_symlink(ndp); return (0); } @@ -578,6 +675,8 @@ nextname: if (!(cnp->cn_flags & ISLASTCN)) { cnp->cn_nameptr = ndp->ni_next; vrele(ndp->ni_dvp); + if (ndp->ni_p_path != NULL) + push_component(ndp, "/", 1); goto dirloop; } @@ -658,16 +757,21 @@ vfs_relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp) */ #ifdef NAMEI_DIAGNOSTIC - /* XXX: Figure out the length of the last component. */ - cp = cnp->cn_nameptr; - while (*cp && (*cp != '/')) { - cp++; +#ifdef NAMEI_DIAGNOSTIC_PLEDGE + if (p && p->p_p && p->p_p->ps_pledgepaths) +#endif + { + /* XXX: Figure out the length of the last component. */ + cp = cnp->cn_nameptr; + while (*cp && (*cp != '/')) { + cp++; + } + if (cnp->cn_namelen != cp - cnp->cn_nameptr) + panic("relookup: bad len"); + if (*cp != 0) + panic("relookup: not last component"); + printf("{%s}: ", cnp->cn_nameptr); } - if (cnp->cn_namelen != cp - cnp->cn_nameptr) - panic("relookup: bad len"); - if (*cp != 0) - panic("relookup: not last component"); - printf("{%s}: ", cnp->cn_nameptr); #endif /* |