From: Todd C. Miller Date: Fri, 20 Feb 2015 21:37:02 +0000 (-0700) Subject: Add regress for mkdtemp and mkstemps from OpenBSD X-Git-Tag: SUDO_1_8_13^2~42 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=49128a79e36bd8c41729f6358a5b715d0f0dea54;p=sudo Add regress for mkdtemp and mkstemps from OpenBSD --- diff --git a/MANIFEST b/MANIFEST index f58ea9ce0..5e1d2095b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -119,6 +119,7 @@ lib/util/regress/fnmatch/fnm_test.in lib/util/regress/glob/files lib/util/regress/glob/globtest.c lib/util/regress/glob/globtest.in +lib/util/regress/mktemp/mktemp_test.c lib/util/regress/progname/progname_test.c lib/util/regress/sudo_conf/conf_test.c lib/util/regress/sudo_conf/test1.in diff --git a/configure b/configure index 575586e29..f9b9c34d8 100755 --- a/configure +++ b/configure @@ -19651,6 +19651,7 @@ esac " done + COMPAT_TEST_PROGS="${COMPAT_TEST_PROGS}${COMPAT_TEST_PROGS+ }mktemp_test" fi for ac_func in snprintf vsnprintf do : diff --git a/configure.ac b/configure.ac index 7dc24f3c3..10b7a2049 100644 --- a/configure.ac +++ b/configure.ac @@ -2571,6 +2571,7 @@ if test X"$ac_cv_func_mkstemps$ac_cv_func_mkdtemp" != X"yesyes"; then AC_LIBOBJ(mktemp) # If either mkdtemp() or mkstemps() is missing, replace both. SUDO_APPEND_COMPAT_EXP(sudo_mkdtemp sudo_mkstemps) + COMPAT_TEST_PROGS="${COMPAT_TEST_PROGS}${COMPAT_TEST_PROGS+ }mktemp_test" fi AX_FUNC_SNPRINTF if test X"$ac_cv_have_working_snprintf$ac_cv_have_working_vsnprintf" = X"yesyes"; then diff --git a/lib/util/Makefile.in b/lib/util/Makefile.in index c6649cfd5..fb71b8861 100644 --- a/lib/util/Makefile.in +++ b/lib/util/Makefile.in @@ -103,6 +103,8 @@ LTOBJS = alloc.lo event.lo fatal.lo key_val.lo gethostname.lo gidlist.lo \ ATOFOO_TEST_OBJS = atofoo_test.lo locale_stub.lo +MKTEMP_TEST_OBJS = mktemp_test.lo locale_stub.lo + PARSELN_TEST_OBJS = parseln_test.lo locale_stub.lo PROGNAME_TEST_OBJS = progname_test.lo progname.lo @@ -176,6 +178,9 @@ globtest: $(GLOBTEST_OBJS) libsudo_util.la hltq_test: $(HLTQ_TEST_OBJS) libsudo_util.la $(LIBTOOL) --mode=link $(CC) -o $@ $(HLTQ_TEST_OBJS) libsudo_util.la $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS) +mktemp_test: $(MKTEMP_TEST_OBJS) libsudo_util.la + $(LIBTOOL) --mode=link $(CC) -o $@ $(MKTEMP_TEST_OBJS) libsudo_util.la $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS) + parseln_test: $(PARSELN_TEST_OBJS) libsudo_util.la $(LIBTOOL) --mode=link $(CC) -o $@ $(PARSELN_TEST_OBJS) libsudo_util.la $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS) @@ -225,6 +230,9 @@ check: $(TEST_PROGS) ./globtest $(srcdir)/regress/glob/globtest.in || rval=`expr $$rval + $$?`; \ rm -rf fake; \ fi; \ + if test -f mktemp_test; then \ + ./mktemp_test || rval=`expr $$rval + $$?`; \ + fi; \ ./atofoo_test || rval=`expr $$rval + $$?`; \ ./hltq_test || rval=`expr $$rval + $$?`; \ ./progname_test || rval=`expr $$rval + $$?`; \ @@ -347,7 +355,6 @@ fatal.lo: $(srcdir)/fatal.c $(incdir)/compat/stdbool.h $(incdir)/sudo_alloc.h \ fnm_test.lo: $(srcdir)/regress/fnmatch/fnm_test.c $(incdir)/compat/fnmatch.h \ $(incdir)/sudo_compat.h $(top_builddir)/config.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/fnmatch/fnm_test.c -fnm_test.o: fnm_test.lo fnmatch.lo: $(srcdir)/fnmatch.c $(incdir)/compat/charclass.h \ $(incdir)/compat/fnmatch.h $(incdir)/sudo_compat.h \ $(top_builddir)/config.h @@ -383,12 +390,14 @@ glob.lo: $(srcdir)/glob.c $(incdir)/compat/charclass.h $(incdir)/compat/glob.h \ globtest.lo: $(srcdir)/regress/glob/globtest.c $(incdir)/compat/glob.h \ $(incdir)/sudo_compat.h $(top_builddir)/config.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/glob/globtest.c -globtest.o: globtest.lo hltq_test.lo: $(srcdir)/regress/tailq/hltq_test.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_fatal.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ $(top_builddir)/config.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/tailq/hltq_test.c +inet_pton.lo: $(srcdir)/inet_pton.c $(incdir)/sudo_compat.h \ + $(top_builddir)/config.h + $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/inet_pton.c isblank.lo: $(srcdir)/isblank.c $(incdir)/sudo_compat.h $(top_builddir)/config.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/isblank.c key_val.lo: $(srcdir)/key_val.c $(incdir)/compat/stdbool.h \ @@ -426,6 +435,11 @@ mksigname.lo: $(srcdir)/mksigname.c $(incdir)/sudo_compat.h \ $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/mksigname.c mktemp.lo: $(srcdir)/mktemp.c $(incdir)/sudo_compat.h $(top_builddir)/config.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/mktemp.c +mktemp_test.lo: $(srcdir)/regress/mktemp/mktemp_test.c \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_util.h \ + $(top_builddir)/config.h + $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/mktemp/mktemp_test.c parseln.lo: $(srcdir)/parseln.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ diff --git a/lib/util/mktemp.c b/lib/util/mktemp.c index fa86d123e..2a64cc9ca 100644 --- a/lib/util/mktemp.c +++ b/lib/util/mktemp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2003, 2004, 2008-2011, 2013, 2014 + * Copyright (c) 2001, 2003, 2004, 2008-2011, 2013, 2015 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -30,6 +30,12 @@ #ifdef HAVE_STDLIB_H # include #endif /* HAVE_STDLIB_H */ +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ #include #ifdef HAVE_UNISTD_H # include @@ -45,6 +51,7 @@ #define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" #define NUM_CHARS (sizeof(TEMPCHARS) - 1) +#define MIN_X 6 #ifndef INT_MAX #define INT_MAX 0x7fffffff @@ -122,15 +129,15 @@ mktemp_internal(char *path, int slen, int mode) char *start, *cp, *ep; const char tempchars[] = TEMPCHARS; unsigned int r, tries; + size_t len; int fd; - for (ep = path; *ep; ep++) - ; - if (path + slen >= ep) { + len = strlen(path); + if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) { errno = EINVAL; return -1; } - ep -= slen; + ep = path + len - slen; tries = 1; for (start = ep; start > path && start[-1] == 'X'; start--) { @@ -138,6 +145,10 @@ mktemp_internal(char *path, int slen, int mode) tries *= NUM_CHARS; } tries *= 2; + if (ep - start < MIN_X) { + errno = EINVAL; + return -1; + } do { for (cp = start; cp != ep; cp++) { diff --git a/lib/util/regress/mktemp/mktemp_test.c b/lib/util/regress/mktemp/mktemp_test.c new file mode 100644 index 000000000..b6b6d7640 --- /dev/null +++ b/lib/util/regress/mktemp/mktemp_test.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2010 Philip Guenther + * + * Public domain. + * + * Verify that mkdtemp() and mkstemps() doesn't overrun or underrun + * the template buffer and that it can generate names that don't + * contain any X's + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ +#include + +#define SUDO_ERROR_WRAP 0 + +#include "sudo_compat.h" +#include "sudo_util.h" +#include "sudo_fatal.h" + +#ifndef MAP_ANON +# if defined(MAP_ANONYMOUS) +# define MAP_ANON MAP_ANONYMOUS +# endif +#endif + +#define MAX_TEMPLATE_LEN 10 +#define MAX_TRIES 100 +#define MIN_Xs 6 + +#define SUFFIX ".suff" +#define SLEN (sizeof SUFFIX - 1) + +__dso_public int main(int argc, char *argv[]); + +/* + * verify that a path generated by mkdtemp() or mkstemp() looks like a + * reasonable expansion of the template and matches the fd. Returns true + * if all the X's were replaced with non-X's + */ +int +check(int fd, char const *kind, char const *path, char const *prefix, + size_t plen, char const *suffix, size_t slen, int tlen) +{ + struct stat sb, fsb; + char const *p; + + if (tlen < MIN_Xs) { + if (fd != -1) + sudo_fatalx("%s(%s) succeed with too few Xs", kind, path); + if (errno != EINVAL) + sudo_fatal("%s(%s) failed with wrong errno: %d", kind, path, errno); + return 1; + } + if (fd == -1) + sudo_fatal("%s(%s)", kind, path); + if (stat(path, &sb)) + sudo_fatal("%s: stat(%s)", kind, path); + if (fd >= 0) { + if (fstat(fd, &fsb)) + sudo_fatal("%s: fstat(%d==%s)", kind, fd, path); + if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino) + sudo_fatalx("%s: stat mismatch", kind); + } + if (memcmp(path, prefix, plen) != 0) + sudo_fatalx("%s: prefix changed! %s vs %s", kind, prefix, path); + if (memcmp(path + plen + tlen, suffix, slen + 1) != 0) + sudo_fatalx("%s: suffix changed! %s vs %s", kind, suffix, path); + for (p = path + plen; p < path + plen + tlen; p++) + if (*p == '\0') + sudo_fatalx("%s: unexpected truncation", kind); + else if (*p == 'X') + return 0; + return 1; +} + +void +try_mkdtemp(char *p, char const *prefix, int len) +{ + size_t plen = strlen(prefix); + int fd, tries, ok; + + for (tries = 0; tries < MAX_TRIES; tries++) { + memcpy(p, prefix, plen); + memset(p + plen, 'X', len); + p[plen + len] = '\0'; + fd = mkdtemp(p) ? -2 : -1; + ok = check(fd, "mkdtemp", p, prefix, plen, "", 0, len); + rmdir(p); + if (ok) + return; + } + sudo_fatalx("mkdtemp: exceeded MAX_TRIES"); +} + +void +try_mkstemps(char *p, char const *prefix, int len, char const *suffix) +{ + size_t plen = strlen(prefix); + size_t slen = strlen(suffix); + int tries, fd, ok; + + for (tries = 0; tries < MAX_TRIES; tries++) { + memcpy(p, prefix, plen); + memset(p + plen, 'X', len); + memcpy(p + plen + len, suffix, slen + 1); + fd = mkstemps(p, slen); + ok = check(fd, "mkstemp", p, prefix, plen, suffix, slen, len); + close(fd); + unlink(p); + if (ok) + return; + } + sudo_fatalx("mkstemps: exceeded MAX_TRIES"); +} + +int +main(int argc, char *argv[]) +{ + char cwd[PATH_MAX + 1]; + char *p; + size_t clen; + long pg; + int i; + + initprogname(argc > 0 ? argv[0] : "mktemp_test"); + + pg = sysconf(_SC_PAGESIZE); + if (getcwd(cwd, sizeof cwd - 1) == NULL) + sudo_fatal("getcwd"); + clen = strlen(cwd); + cwd[clen++] = '/'; + cwd[clen] = '\0'; +#ifdef MAP_ANON + p = mmap(NULL, pg * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); +#else + i = open("/dev/zero", O_RDWR); + if (i == -1) + sudo_fatal("/dev/zero"); + p = mmap(NULL, pg * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE, i, 0); +#endif + if (p == MAP_FAILED) + sudo_fatal("mmap"); + if (mprotect(p, pg, PROT_NONE) || mprotect(p + pg * 2, pg, PROT_NONE)) + sudo_fatal("mprotect"); + p += pg; + + i = MAX_TEMPLATE_LEN + 1; + while (i-- > 0) { + /* try first at the start of a page, no prefix */ + try_mkdtemp(p, "", i); + /* now at the end of the page, no prefix */ + try_mkdtemp(p + pg - i - 1, "", i); + /* start of the page, prefixed with the cwd */ + try_mkdtemp(p, cwd, i); + /* how about at the end of the page, prefixed with cwd? */ + try_mkdtemp(p + pg - clen - i - 1, cwd, i); + + /* again, with mkstemps() and an empty suffix */ + /* try first at the start of a page, no prefix */ + try_mkstemps(p, "", i, ""); + /* now at the end of the page, no prefix */ + try_mkstemps(p + pg - i - 1, "", i, ""); + /* start of the page, prefixed with the cwd */ + try_mkstemps(p, cwd, i, ""); + /* how about at the end of the page, prefixed with cwd? */ + try_mkstemps(p + pg - clen - i - 1, cwd, i, ""); + + /* mkstemps() and a non-empty suffix */ + /* try first at the start of a page, no prefix */ + try_mkstemps(p, "", i, SUFFIX); + /* now at the end of the page, no prefix */ + try_mkstemps(p + pg - i - SLEN - 1, "", i, SUFFIX); + /* start of the page, prefixed with the cwd */ + try_mkstemps(p, cwd, i, SUFFIX); + /* how about at the end of the page, prefixed with cwd? */ + try_mkstemps(p + pg - clen - i - SLEN - 1, cwd, i, SUFFIX); + } + + return 0; +} diff --git a/mkdep.pl b/mkdep.pl index b3326f0ab..d6774a9c8 100755 --- a/mkdep.pl +++ b/mkdep.pl @@ -70,7 +70,7 @@ sub mkdep { $makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo solaris_audit.lo sssd.lo:; # 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:\@LTLIBOBJS\@:clock_gettime.lo closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getgrouplist.lo getline.lo getopt_long.lo glob.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo pw_dup.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo strtonum.lo utimes.lo globtest.o fnm_test.o inet_pton:; + $makefile =~ s:\@LTLIBOBJS\@:clock_gettime.lo closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getgrouplist.lo getline.lo getopt_long.lo glob.lo inet_ntop_lo inet_pton.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo pw_dup.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo strtonum.lo utimes.lo:; # Parse OBJS lines my %objs;