diff options
| author | 2019-05-13 22:55:27 +0000 | |
|---|---|---|
| committer | 2019-05-13 22:55:27 +0000 | |
| commit | 1de745bca436d2f8732f18785f95bf07a0af4fd5 (patch) | |
| tree | 0b7e97ab8390170d430b7d0f71291029c45228b4 /sys/kern/vfs_lookup.c | |
| parent | Remove unused pad check, which is handled by tls1_cbc_remove_padding() now. (diff) | |
| download | wireguard-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.c | 60 |
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); } + + |
