diff --git a/MANIFEST b/MANIFEST index f1c1ee3e4..626b5930a 100644 --- a/MANIFEST +++ b/MANIFEST @@ -276,6 +276,7 @@ lib/util/pw_dup.c lib/util/pwrite.c lib/util/rcstr.c lib/util/reallocarray.c +lib/util/realpath.c lib/util/regex.c lib/util/regress/closefrom/closefrom_test.c lib/util/regress/corpus/seed/sudo_conf/sudo.conf.1 diff --git a/config.h.in b/config.h.in index 8ed7fb241..c1fcad17d 100644 --- a/config.h.in +++ b/config.h.in @@ -207,6 +207,10 @@ don't. */ #undef HAVE_DECL_SSIZE_MAX +/* Define to 1 if you have the declaration of 'SYMLOOP_MAX', and to 0 if you + don't. */ +#undef HAVE_DECL_SYMLOOP_MAX + /* Define to 1 if you have the declaration of 'sys_sigabbrev', and to 0 if you don't. */ #undef HAVE_DECL_SYS_SIGABBREV @@ -243,6 +247,10 @@ you don't. */ #undef HAVE_DECL__POSIX_PATH_MAX +/* Define to 1 if you have the declaration of '_POSIX_SYMLOOP_MAX', and to 0 + if you don't. */ +#undef HAVE_DECL__POSIX_SYMLOOP_MAX + /* Define to 1 if you have the declaration of '_sys_siglist', and to 0 if you don't. */ #undef HAVE_DECL__SYS_SIGLIST @@ -758,6 +766,9 @@ /* Define to 1 if you have the 'reallocarray' function. */ #undef HAVE_REALLOCARRAY +/* Define to 1 if you have the 'realpath' function. */ +#undef HAVE_REALPATH + /* Define to 1 if you have the 'revoke' function. */ #undef HAVE_REVOKE diff --git a/configure b/configure index e348b9fce..8aec22aeb 100755 --- a/configure +++ b/configure @@ -23692,6 +23692,33 @@ esac esac fi +done + + for ac_func in realpath +do : + ac_fn_c_check_func "$LINENO" "realpath" "ac_cv_func_realpath" +if test "x$ac_cv_func_realpath" = xyes +then : + printf "%s\n" "#define HAVE_REALPATH 1" >>confdefs.h + +else case e in #( + e) + case " $LIBOBJS " in + *" realpath.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS realpath.$ac_objext" + ;; +esac + + + for _sym in sudo_realpath; do + COMPAT_EXP="${COMPAT_EXP}${_sym} +" + done + + ;; +esac +fi + done for ac_func in strlcpy @@ -28018,6 +28045,19 @@ else case e in #( esac fi printf "%s\n" "#define HAVE_DECL_SSIZE_MAX $ac_have_decl" >>confdefs.h +ac_fn_check_decl "$LINENO" "SYMLOOP_MAX" "ac_cv_have_decl_SYMLOOP_MAX" " +#include +#include + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl_SYMLOOP_MAX" = xyes +then : + ac_have_decl=1 +else case e in #( + e) ac_have_decl=0 ;; +esac +fi +printf "%s\n" "#define HAVE_DECL_SYMLOOP_MAX $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "SIZE_MAX" "ac_cv_have_decl_SIZE_MAX" " #include @@ -28132,6 +28172,25 @@ fi printf "%s\n" "#define HAVE_DECL__POSIX_PATH_MAX $ac_have_decl" >>confdefs.h +fi +if test "$ac_cv_have_decl_SYMLOOP_MAX" != "yes" +then : + + ac_fn_check_decl "$LINENO" "_POSIX_SYMLOOP_MAX" "ac_cv_have_decl__POSIX_SYMLOOP_MAX" " +#include +#include + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl__POSIX_SYMLOOP_MAX" = xyes +then : + ac_have_decl=1 +else case e in #( + e) ac_have_decl=0 ;; +esac +fi +printf "%s\n" "#define HAVE_DECL__POSIX_SYMLOOP_MAX $ac_have_decl" >>confdefs.h + + fi diff --git a/configure.ac b/configure.ac index be60b9362..650131543 100644 --- a/configure.ac +++ b/configure.ac @@ -2884,6 +2884,10 @@ AC_CHECK_FUNCS([pw_dup], [], [ AC_LIBOBJ(pw_dup) SUDO_APPEND_COMPAT_EXP(sudo_pw_dup) ]) +AC_CHECK_FUNCS([realpath], [], [ + AC_LIBOBJ(realpath) + SUDO_APPEND_COMPAT_EXP(sudo_realpath) +]) AC_CHECK_FUNCS([strlcpy], [], [ AC_LIBOBJ(strlcpy) SUDO_APPEND_COMPAT_EXP(sudo_strlcpy) @@ -3275,7 +3279,7 @@ AC_INCLUDES_DEFAULT dnl dnl Check for incomplete limits.h and missing SIZE_MAX. dnl -AC_CHECK_DECLS([LLONG_MAX, LLONG_MIN, ULLONG_MAX, PATH_MAX, SSIZE_MAX], [], [], [ +AC_CHECK_DECLS([LLONG_MAX, LLONG_MIN, ULLONG_MAX, PATH_MAX, SSIZE_MAX, SYMLOOP_MAX], [], [], [ #include #include ]) @@ -3321,6 +3325,12 @@ AS_IF([test "$ac_cv_have_decl_PATH_MAX" != "yes"], [ #include ]]) ]) +AS_IF([test "$ac_cv_have_decl_SYMLOOP_MAX" != "yes"], [ + AC_CHECK_DECLS([_POSIX_SYMLOOP_MAX], [], [], [[ +#include +#include + ]]) +]) dnl dnl Check for strsignal() or sys_siglist diff --git a/include/sudo_compat.h b/include/sudo_compat.h index f5639dfc8..53baa8700 100644 --- a/include/sudo_compat.h +++ b/include/sudo_compat.h @@ -103,6 +103,14 @@ # endif #endif +#if defined(HAVE_DECL_SYMLOOP_MAX) && !HAVE_DECL_SYMLOOP_MAX +# if defined(HAVE_DECL__POSIX_SYMLOOP_MAX) && HAVE_DECL__POSIX_SYMLOOP_MAX +# define SYMLOOP_MAX _POSIX_SYMLOOP_MAX +# else +# define SYMLOOP_MAX 8 +# endif +#endif + /* ACCESSPERMS and ALLPERMS are handy BSDisms. */ #ifndef ACCESSPERMS # define ACCESSPERMS 00777 @@ -552,6 +560,11 @@ sudo_dso_public void *sudo_reallocarray(void *ptr, size_t nmemb, size_t size); # undef reallocarray # define reallocarray(_a, _b, _c) sudo_reallocarray((_a), (_b), (_c)) #endif /* HAVE_REALLOCARRAY */ +#ifndef HAVE_REALPATH +sudo_dso_public void *sudo_realpath(const char *path, char *resolved); +# undef realpath +# define realpath(_a, _b) sudo_realpath((_a), (_b)) +#endif /* HAVE_REALPATH */ #ifndef HAVE_DUP3 sudo_dso_public int sudo_dup3(int oldd, int newd, int flags); # undef dup3 diff --git a/lib/util/Makefile.in b/lib/util/Makefile.in index 7898eecea..129a53a9f 100644 --- a/lib/util/Makefile.in +++ b/lib/util/Makefile.in @@ -1041,11 +1041,11 @@ key_val.plog: key_val.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/key_val.c --i-file $< --output-file $@ lbuf.lo: $(srcdir)/lbuf.c $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_lbuf.h $(incdir)/sudo_queue.h \ - $(top_builddir)/config.h + $(incdir)/sudo_util.h $(top_builddir)/config.h $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/lbuf.c lbuf.i: $(srcdir)/lbuf.c $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_lbuf.h $(incdir)/sudo_queue.h \ - $(top_builddir)/config.h + $(incdir)/sudo_util.h $(top_builddir)/config.h $(CC) -E -o $@ $(CPPFLAGS) $< lbuf.plog: lbuf.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/lbuf.c --i-file $< --output-file $@ @@ -1311,6 +1311,12 @@ reallocarray.i: $(srcdir)/reallocarray.c $(incdir)/sudo_compat.h \ $(CC) -E -o $@ $(CPPFLAGS) $< reallocarray.plog: reallocarray.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/reallocarray.c --i-file $< --output-file $@ +realpath.lo: $(srcdir)/realpath.c $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/realpath.c +realpath.i: $(srcdir)/realpath.c $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +realpath.plog: realpath.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/realpath.c --i-file $< --output-file $@ regex.lo: $(srcdir)/regex.c $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_gettext.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(top_builddir)/config.h diff --git a/lib/util/realpath.c b/lib/util/realpath.c new file mode 100644 index 000000000..2d4f523a9 --- /dev/null +++ b/lib/util/realpath.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 1989, 1991, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)getcwd.c 8.5 (Berkeley) 2/7/95 + */ + +#include + +#ifndef HAVE_REALPATH + +#include +#include + +#include +#include +#include +#include + +/* + * char *realpath(const char *path, char *resolved); + * + * Find the real name of path, by removing all ".", ".." and symlink + * components. Returns (resolved) on success, or (NULL) on failure, + * in which case the path which caused trouble is left in (resolved). + */ +char * +sudo_realpath(const char *path, char *resolved) +{ + struct stat sb; + int idx = 0, nlnk = 0; + const char *q; + char *p, wbuf[2][PATH_MAX], *fres = NULL; + size_t len; + ssize_t n; + + if (path == NULL) { + errno = EINVAL; + return NULL; + } + + if (resolved == NULL) { + fres = resolved = malloc(PATH_MAX); + if (resolved == NULL) + return NULL; + } + + + /* + * Build real path one by one with paying an attention to ., + * .. and symbolic link. + */ + + /* + * `p' is where we'll put a new component with prepending + * a delimiter. + */ + p = resolved; + + if (*path == '\0') { + *p = '\0'; + errno = ENOENT; + goto out; + } + + /* If relative path, start from current working directory. */ + if (*path != '/') { + if (getcwd(resolved, PATH_MAX) == NULL) { + p[0] = '.'; + p[1] = '\0'; + goto out; + } + len = strlen(resolved); + if (len > 1) + p += len; + } + +loop: + /* Skip any slash. */ + while (*path == '/') + path++; + + if (*path == '\0') { + if (p == resolved) + *p++ = '/'; + *p = '\0'; + return resolved; + } + + /* Find the end of this component. */ + q = path; + do { + q++; + } while (*q != '/' && *q != '\0'); + + /* Test . or .. */ + if (path[0] == '.') { + if (q - path == 1) { + path = q; + goto loop; + } + if (path[1] == '.' && q - path == 2) { + /* Trim the last component. */ + if (p != resolved) + while (*--p != '/') + continue; + path = q; + goto loop; + } + } + + /* Append this component. */ + if (p - resolved + 1 + q - path + 1 > PATH_MAX) { + errno = ENAMETOOLONG; + if (p == resolved) + *p++ = '/'; + *p = '\0'; + goto out; + } + p[0] = '/'; + memcpy(&p[1], path, q - path); + p[1 + q - path] = '\0'; + + /* + * If this component is a symlink, toss it and prepend link + * target to unresolved path. + */ + if (lstat(resolved, &sb) == -1) + goto out; + + if (S_ISLNK(sb.st_mode)) { + if (nlnk++ >= SYMLOOP_MAX) { + errno = ELOOP; + goto out; + } + n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1); + if (n < 0) + goto out; + if (n == 0) { + errno = ENOENT; + goto out; + } + + /* Append unresolved path to link target and switch to it. */ + if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) { + errno = ENAMETOOLONG; + goto out; + } + memcpy(&wbuf[idx][n], q, len + 1); + path = wbuf[idx]; + idx ^= 1; + + /* If absolute symlink, start from root. */ + if (*path == '/') + p = resolved; + goto loop; + } + if (*q == '/' && !S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + goto out; + } + + /* Advance both resolved and unresolved path. */ + p += 1 + q - path; + path = q; + goto loop; +out: + free(fres); + return NULL; +} +#endif /* HAVE_REALPATH */ diff --git a/scripts/mkdep.pl b/scripts/mkdep.pl index a6e10bcc1..d6a483fea 100755 --- a/scripts/mkdep.pl +++ b/scripts/mkdep.pl @@ -120,7 +120,7 @@ sub mkdep { # XXX - fill in AUTH_OBJS from contents of the auth dir instead $makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:; $makefile =~ s:\@DIGEST\@:digest.lo digest_openssl.lo digest_gcrypt.lo:; - $makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_buf.lo arc4random_uniform.lo cfmakeraw.lo closefrom.lo dup3.lo explicit_bzero.lo fchmodat.lo fchownat.lo freezero.lo fstatat.lo fnmatch.lo getaddrinfo.lo getcwd.lo getentropy.lo getgrouplist.lo getdelim.lo getopt_long.lo getusershell.lo glob.lo gmtime_r.lo inet_ntop_lo inet_pton.lo isblank.lo localtime_r.lo memrchr.lo mkdirat.lo mksiglist.lo mksigname.lo mktemp.lo nanosleep.lo openat.lo pipe2.lo pread.lo pwrite.lo pw_dup.lo reallocarray.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo str2sig.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo timegm.lo unlinkat.lo utimens.lo:; + $makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_buf.lo arc4random_uniform.lo cfmakeraw.lo closefrom.lo dup3.lo explicit_bzero.lo fchmodat.lo fchownat.lo freezero.lo fstatat.lo fnmatch.lo getaddrinfo.lo getcwd.lo getentropy.lo getgrouplist.lo getdelim.lo getopt_long.lo getusershell.lo glob.lo gmtime_r.lo inet_ntop_lo inet_pton.lo isblank.lo localtime_r.lo memrchr.lo mkdirat.lo mksiglist.lo mksigname.lo mktemp.lo nanosleep.lo openat.lo pipe2.lo pread.lo pwrite.lo pw_dup.lo reallocarray.lo realpath.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo str2sig.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo timegm.lo unlinkat.lo utimens.lo:; # Parse OBJS lines my %objs;