summaryrefslogtreecommitdiffstats
path: root/sys/kern/vfs_lookup.c
diff options
context:
space:
mode:
authorbeck <beck@openbsd.org>2019-05-13 22:55:27 +0000
committerbeck <beck@openbsd.org>2019-05-13 22:55:27 +0000
commit1de745bca436d2f8732f18785f95bf07a0af4fd5 (patch)
tree0b7e97ab8390170d430b7d0f71291029c45228b4 /sys/kern/vfs_lookup.c
parentRemove unused pad check, which is handled by tls1_cbc_remove_padding() now. (diff)
downloadwireguard-openbsd-1de745bca436d2f8732f18785f95bf07a0af4fd5.tar.xz
wireguard-openbsd-1de745bca436d2f8732f18785f95bf07a0af4fd5.zip
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_lookup.c')
-rw-r--r--sys/kern/vfs_lookup.c60
1 files changed, 56 insertions, 4 deletions
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index e9637a7f3e4..a57f6f99915 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vfs_lookup.c,v 1.76 2019/01/03 21:52:31 beck Exp $ */
+/* $OpenBSD: vfs_lookup.c,v 1.77 2019/05/13 22:55:27 beck Exp $ */
/* $NetBSD: vfs_lookup.c,v 1.17 1996/02/09 19:00:59 christos Exp $ */
/*
@@ -61,6 +61,29 @@ void unveil_start_relative(struct proc *p, struct nameidata *ni);
void unveil_check_component(struct proc *p, struct nameidata *ni, struct vnode *dp );
int unveil_check_final(struct proc *p, struct nameidata *ni);
+int
+component_push(struct componentname *cnp, char *component, size_t len)
+{
+ if (cnp->cn_rpi + len + 1 >= MAXPATHLEN)
+ return 0;
+ if (cnp->cn_rpi > 1)
+ cnp->cn_rpbuf[cnp->cn_rpi++] = '/';
+ memcpy(cnp->cn_rpbuf + cnp->cn_rpi, component, len);
+ cnp->cn_rpi+=len;
+ cnp->cn_rpbuf[cnp->cn_rpi] = '\0';
+ return 1;
+}
+
+void
+component_pop(struct componentname *cnp)
+{
+ while(cnp->cn_rpi && cnp->cn_rpbuf[cnp->cn_rpi] != '/' )
+ cnp->cn_rpi--;
+ if (cnp->cn_rpi == 0 && cnp->cn_rpbuf[0] == '/')
+ cnp->cn_rpi++;
+ cnp->cn_rpbuf[cnp->cn_rpi] = '\0';
+}
+
void
ndinitat(struct nameidata *ndp, u_long op, u_long flags,
enum uio_seg segflg, int dirfd, const char *namep, struct proc *p)
@@ -190,6 +213,11 @@ fail:
if (cnp->cn_pnbuf[0] == '/') {
dp = ndp->ni_rootdir;
vref(dp);
+ if (cnp->cn_flags & REALPATH && cnp->cn_rpi == 0) {
+ cnp->cn_rpbuf[0] = '/';
+ cnp->cn_rpbuf[1] = '\0';
+ cnp->cn_rpi = 1;
+ }
} else if (ndp->ni_dirfd == AT_FDCWD) {
dp = fdp->fd_cdir;
vref(dp);
@@ -303,6 +331,13 @@ badlink:
vref(dp);
ndp->ni_unveil_match = NULL;
unveil_check_component(p, ndp, dp);
+ if (cnp->cn_flags & REALPATH) {
+ cnp->cn_rpbuf[0] = '/';
+ cnp->cn_rpbuf[1] = '\0';
+ cnp->cn_rpi = 1;
+ }
+ } else if (cnp->cn_flags & REALPATH) {
+ component_pop(cnp);
}
}
pool_put(&namei_pool, cnp->cn_pnbuf);
@@ -441,6 +476,19 @@ dirloop:
printf("{%s}: ", cnp->cn_nameptr);
*cp = c; }
#endif
+ if (cnp->cn_flags & REALPATH) {
+ size_t len = cp - cnp->cn_nameptr;
+ if (len == 2 && cnp->cn_nameptr[0] == '.' &&
+ cnp->cn_nameptr[1] == '.')
+ component_pop(cnp);
+ else if (!(len == 1 && cnp->cn_nameptr[0] == '.')) {
+ if (!component_push(cnp, cnp->cn_nameptr, len)) {
+ error = ENAMETOOLONG;
+ goto bad;
+ }
+ }
+ }
+
ndp->ni_pathlen -= cnp->cn_namelen;
ndp->ni_next = cp;
/*
@@ -529,10 +577,12 @@ dirloop:
printf("not found\n");
#endif
/*
- * Allow for unveiling of a file in a directory
- * where we don't have access to create it ourselves
+ * Allow for unveiling or realpath'ing a file in a
+ * directory where we don't have access to create it
+ * ourselves
*/
- if (ndp->ni_pledge == PLEDGE_UNVEIL && error == EACCES)
+ if ((ndp->ni_pledge == PLEDGE_UNVEIL ||
+ (cnp->cn_flags & REALPATH)) && error == EACCES)
error = EJUSTRETURN;
if (error != EJUSTRETURN)
@@ -808,3 +858,5 @@ bad:
*vpp = NULL;
return (error);
}
+
+