summaryrefslogtreecommitdiff
path: root/sys/kern/vfs_syscalls.c
diff options
context:
space:
mode:
authorBob Beck <beck@cvs.openbsd.org>2019-05-13 22:55:28 +0000
committerBob Beck <beck@cvs.openbsd.org>2019-05-13 22:55:28 +0000
commit0215314fb9f62f0109926d50f4579463d7fd4484 (patch)
treecd23070c4494c084a95b86f9890ed659d764ea97 /sys/kern/vfs_syscalls.c
parent3c556fa41d41f2f348bfa95db84e9e95458dd36b (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.c91
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 /* {