From ee8781d936b68a29adedf2fe965572c644def872 Mon Sep 17 00:00:00 2001 From: beck <> Date: Tue, 28 May 2019 13:08:56 +0000 Subject: [PATCH] Enable the use of the kernel __realpath() system call in the libc wrapper. For now, this also still uses the existing realpath implmentation and emits a syslog if we see differening results. Once we have run with that for a little while we will remove the old code ok deraadt@ --- src/lib/libc/stdlib/realpath.c | 139 ++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 3 deletions(-) diff --git a/src/lib/libc/stdlib/realpath.c b/src/lib/libc/stdlib/realpath.c index 5d226d9a..76f88562 100644 --- a/src/lib/libc/stdlib/realpath.c +++ b/src/lib/libc/stdlib/realpath.c @@ -1,4 +1,4 @@ -/* $OpenBSD: realpath.c,v 1.22 2017/12/24 01:50:50 millert Exp $ */ +/* $OpenBSD: realpath.c,v 1.23 2019/05/28 13:08:56 beck Exp $ */ /* * Copyright (c) 2003 Constantin S. Svintsoff * @@ -32,6 +32,8 @@ #include #include #include +#include +#include /* A slightly modified copy of this file exists in libexec/ld.so */ @@ -42,8 +44,8 @@ * components. Returns (resolved) on success, or (NULL) on failure, * in which case the path which caused trouble is left in (resolved). */ -char * -realpath(const char *path, char *resolved) +static char * +urealpath(const char *path, char *resolved) { const char *p; char *q; @@ -51,6 +53,7 @@ realpath(const char *path, char *resolved) unsigned symlinks; int serrno, mem_allocated; ssize_t slen; + int trailingslash = 0; char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; if (path == NULL) { @@ -231,3 +234,133 @@ err: free(resolved); return (NULL); } + +/* + * Copyright (c) 2019 Bob Beck + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int __realpath(const char *pathname, char *resolved); +PROTO_NORMAL(__realpath); + +/* + * wrapper for kernel __realpath + */ + +char * +realpath(const char *path, char *resolved) +{ + char pbuf[PATH_MAX], rbuf[PATH_MAX], expected[PATH_MAX]; + struct syslog_data sdata = SYSLOG_DATA_INIT; + int usererrno = 0, kernelerrno = 0, trailingslash = 0, save_errno; + int kernelonly = (getenv("USE_KERNEL_REALPATH") != NULL); + ssize_t i; + + rbuf[0] = pbuf[0] = expected[0] = '\0'; + + if (!kernelonly) { + memset(expected, 0, sizeof(expected)); + if (urealpath(path, expected) == NULL) { + usererrno = errno; + expected[0] = '\0'; + } + } + + if (path == NULL) { + kernelerrno = EINVAL; + goto out; + } + if (path[0] == '\0') { + kernelerrno = ENOENT; + goto out; + } + if (strlcat(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf)) { + kernelerrno = ENAMETOOLONG; + goto out; + } + + if (pbuf[strlen(pbuf) - 1] == '/') + trailingslash = 1; + + if (__realpath(pbuf, rbuf) == -1) + kernelerrno = errno; + + /* + * XXX XXX XXX + * + * The old userland implementation strips trailing slashes. + * According to Dr. POSIX, realpathing "/bsd" should be fine, + * realpathing "/bsd/" should return ENOTDIR. + * + * Similar, but *different* to the above, The old userland + * implementation allows for realpathing "/nonexistent" but + * not "/nonexistent/", Both those should return ENOENT + * according to POSIX. + * + * This hack should go away once we decide to match POSIX. + * which we should as soon as is convenient. + */ + if (kernelerrno == ENOTDIR) { + /* Try again without the trailing slash. */ + kernelerrno = 0; + for (i = strlen(pbuf); i > 1 && pbuf[i - 1] == '/'; i--) + pbuf[i - 1] = '\0'; + rbuf[0] = '\0'; + if (__realpath(pbuf, rbuf) == -1) + kernelerrno = errno; + } + +out: + if (!kernelonly) { + /* syslog if kernel and userland are different */ + save_errno = errno; + if (strcmp(rbuf, expected) != 0 || (usererrno == 0 && + kernelerrno != 0)) + syslog_r(LOG_CRIT | LOG_CONS, &sdata, + "realpath '%s' -> '%s' errno %d, " + "expected '%s' errno %d", path, rbuf, + kernelerrno, expected, usererrno); + errno = save_errno; + + /* use userland result */ + if (usererrno) { + errno = usererrno; + return NULL; + } + else + errno = 0; + if (resolved == NULL) + resolved = strdup(expected); + else if (strlcpy(resolved, expected, PATH_MAX) >= PATH_MAX) { + errno = ENAMETOOLONG; + return NULL; + } + + } else { + /* use kernel result */ + if (kernelerrno) { + errno = kernelerrno; + return NULL; + } + else + errno = 0; + if (resolved == NULL) + resolved = strdup(rbuf); + else if (strlcpy(resolved, rbuf, PATH_MAX) >= PATH_MAX) { + errno = ENAMETOOLONG; + return NULL; + } + } + return (resolved); +}