diff options
author | Bob Beck <beck@cvs.openbsd.org> | 2019-05-13 22:55:28 +0000 |
---|---|---|
committer | Bob Beck <beck@cvs.openbsd.org> | 2019-05-13 22:55:28 +0000 |
commit | 0215314fb9f62f0109926d50f4579463d7fd4484 (patch) | |
tree | cd23070c4494c084a95b86f9890ed659d764ea97 /sys/kern/vfs_syscalls.c | |
parent | 3c556fa41d41f2f348bfa95db84e9e95458dd36b (diff) |
Add a kernel implementation of realpath() as __realpath().
We want this so that we can stop allowing readlink() on traversed
vnodes in unveil().
This includes all the kernel side and the system call.
This is not yet used in libc for realpath, so nothing calls this yet.
The libc wrapper will be committed later.
Testing by many, and ports build by naddy@
ok deraadt@
Diffstat (limited to 'sys/kern/vfs_syscalls.c')
-rw-r--r-- | sys/kern/vfs_syscalls.c | 91 |
1 files changed, 90 insertions, 1 deletions
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index be31bc6229c..5b649af01d7 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_syscalls.c,v 1.314 2019/03/24 18:14:20 beck Exp $ */ +/* $OpenBSD: vfs_syscalls.c,v 1.315 2019/05/13 22:55:27 beck Exp $ */ /* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ */ /* @@ -868,6 +868,95 @@ change_dir(struct nameidata *ndp, struct proc *p) } int +sys___realpath(struct proc *p, void *v, register_t *retval) +{ + struct sys___realpath_args /* { + syscallarg(const char *) pathname; + syscallarg(char *) resolved; + } */ *uap = v; + char *pathname; + char *cwdbuf; + char *rpbuf; + struct nameidata nd; + size_t pathlen; + int cwdlen = MAXPATHLEN * 4; + int error = 0; + + pathname = pool_get(&namei_pool, PR_WAITOK); + rpbuf = pool_get(&namei_pool, PR_WAITOK); + cwdbuf = malloc(cwdlen, M_TEMP, M_WAITOK); + + if ((error = copyinstr(SCARG(uap, pathname), pathname, MAXPATHLEN, + &pathlen))) + goto end; + + if (pathlen < 2) { + error = EINVAL; + goto end; + } + + /* Get cwd for relative path if needed, prepend to rpbuf */ + rpbuf[0] = '\0'; + if (pathname[0] != '/') { + char *path, *bp, *bend; + int lenused; + + bp = &cwdbuf[cwdlen]; + bend = bp; + *(--bp) = '\0'; + + error = vfs_getcwd_common(p->p_fd->fd_cdir, NULL, &bp, path, + cwdlen/2, GETCWD_CHECK_ACCESS, p); + + if (error) + goto end; + + lenused = bend - bp; + *retval = lenused; + + if (strlcpy(rpbuf, bp, MAXPATHLEN) >= MAXPATHLEN) { + error = ENAMETOOLONG; + goto end; + } + } + + if (pathlen == 2 && pathname[0] == '/') + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | SAVENAME | REALPATH, + UIO_SYSSPACE, pathname, p); + else + NDINIT(&nd, CREATE, FOLLOW | LOCKLEAF | LOCKPARENT | SAVENAME | + REALPATH, UIO_SYSSPACE, pathname, p); + + nd.ni_cnd.cn_rpbuf = rpbuf; + nd.ni_cnd.cn_rpi = strlen(rpbuf); + + nd.ni_pledge = PLEDGE_RPATH; + nd.ni_unveil = UNVEIL_READ; + if ((error = namei(&nd)) != 0) + goto end; + + /* release lock and reference from namei */ + if (nd.ni_vp) { + VOP_UNLOCK(nd.ni_vp); + vrele(nd.ni_vp); + } + if (nd.ni_dvp && nd.ni_dvp != nd.ni_vp){ + VOP_UNLOCK(nd.ni_dvp); + vrele(nd.ni_dvp); + } + + error = copyoutstr(nd.ni_cnd.cn_rpbuf, SCARG(uap, resolved), + MAXPATHLEN, NULL); + + pool_put(&namei_pool, nd.ni_cnd.cn_pnbuf); +end: + pool_put(&namei_pool, rpbuf); + pool_put(&namei_pool, pathname); + free(cwdbuf, M_TEMP, cwdlen); + return (error); +} + +int sys_unveil(struct proc *p, void *v, register_t *retval) { struct sys_unveil_args /* { |