From ddd663a5f2aafc5a9b08c07930c89635c28289ef Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Thu, 24 May 2018 21:04:23 -0600 Subject: [PATCH] Import arc4random() from libressl. This takes an all-in-one approach instead of the one-file-per-OS approach that libressl takes. The fallback code does not have as many OS-specific bits as libressl. --- MANIFEST | 6 + config.h.in | 27 ++ configure | 170 ++++++++++ configure.ac | 37 ++- include/sudo_rand.h | 50 +++ lib/util/Makefile.in | 15 +- lib/util/arc4random.c | 212 ++++++++++++ lib/util/arc4random.h | 96 ++++++ lib/util/arc4random_uniform.c | 70 ++++ lib/util/chacha_private.h | 222 +++++++++++++ lib/util/getentropy.c | 597 ++++++++++++++++++++++++++++++++++ lib/util/util.exp.in | 1 + mkdep.pl | 2 +- 13 files changed, 1499 insertions(+), 6 deletions(-) create mode 100644 include/sudo_rand.h create mode 100644 lib/util/arc4random.c create mode 100644 lib/util/arc4random.h create mode 100644 lib/util/arc4random_uniform.c create mode 100644 lib/util/chacha_private.h create mode 100644 lib/util/getentropy.c diff --git a/MANIFEST b/MANIFEST index da994f947..11a9f9d8d 100644 --- a/MANIFEST +++ b/MANIFEST @@ -78,6 +78,7 @@ include/sudo_gettext.h include/sudo_lbuf.h include/sudo_plugin.h include/sudo_queue.h +include/sudo_rand.h include/sudo_util.h indent.pro init.d/aix.sh.in @@ -86,6 +87,10 @@ init.d/sudo.conf.in install-sh lib/util/Makefile.in lib/util/aix.c +lib/util/arc4random.c +lib/util/arc4random.h +lib/util/arc4random_uniform.c +lib/util/chacha_private.h lib/util/closefrom.c lib/util/digest.c lib/util/digest_gcrypt.c @@ -97,6 +102,7 @@ lib/util/fatal.c lib/util/fnmatch.c lib/util/getaddrinfo.c lib/util/getcwd.c +lib/util/getentropy.c lib/util/getgrouplist.c lib/util/gethostname.c lib/util/getline.c diff --git a/config.h.in b/config.h.in index fcd73d587..3e17bdb94 100644 --- a/config.h.in +++ b/config.h.in @@ -54,6 +54,9 @@ /* Define to 1 if you have the `arc4random' function. */ #undef HAVE_ARC4RANDOM +/* Define to 1 if you have the `arc4random_uniform' function. */ +#undef HAVE_ARC4RANDOM_UNIFORM + /* Define to 1 if you have the `asprintf' function. */ #undef HAVE_ASPRINTF @@ -214,6 +217,9 @@ /* Define to 1 if you have the `dlopen' function. */ #undef HAVE_DLOPEN +/* Define to 1 if you have the `dl_iterate_phdr' function. */ +#undef HAVE_DL_ITERATE_PHDR + /* Define to 1 if the compiler supports the __visibility__ attribute. */ #undef HAVE_DSO_VISIBILITY @@ -265,9 +271,15 @@ /* Define to 1 if you use the FWTK authsrv daemon. */ #undef HAVE_FWTK +/* Define to 1 if you are using gcrypt's sha2 functions. */ +#undef HAVE_GCRYPT + /* Define to 1 if you have the `getaddrinfo' function. */ #undef HAVE_GETADDRINFO +/* Define to 1 if you have the `getauxval' function. */ +#undef HAVE_GETAUXVAL + /* Define to 1 if you have the `getdomainname' function. */ #undef HAVE_GETDOMAINNAME @@ -515,6 +527,9 @@ /* Define to 1 if you have the `openpty' function. */ #undef HAVE_OPENPTY +/* Define to 1 if you are using OpenSSL's sha2 functions. */ +#undef HAVE_OPENSSL + /* Define to 1 if you use NRL OPIE. */ #undef HAVE_OPIE @@ -569,6 +584,12 @@ /* Define to 1 if you have the `pstat_getproc' function. */ #undef HAVE_PSTAT_GETPROC +/* Define to 1 if you have the `pthread_atfork' function. */ +#undef HAVE_PTHREAD_ATFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_PTHREAD_H + /* Define to 1 if you have the header file. */ #undef HAVE_PTY_H @@ -809,12 +830,18 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKIO_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STATVFS_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STROPTS_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SYSCALL_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SYSMACROS_H diff --git a/configure b/configure index 4bfee2f82..cfde56038 100755 --- a/configure +++ b/configure @@ -741,6 +741,7 @@ SUDOERS_TEST_PROGS COMPAT_TEST_PROGS LOCALEDIR_SUFFIX SUDO_NLS +LIBPTHREAD LIBMD LIBINTL LIBRT @@ -2869,6 +2870,8 @@ as_fn_append ac_header_list " sys/bsdtypes.h" as_fn_append ac_header_list " sys/select.h" as_fn_append ac_header_list " sys/stropts.h" as_fn_append ac_header_list " sys/sysmacros.h" +as_fn_append ac_header_list " sys/syscall.h" +as_fn_append ac_header_list " sys/statvfs.h" as_fn_append ac_func_list " fexecve" as_fn_append ac_func_list " killpg" as_fn_append ac_func_list " nl_langinfo" @@ -2877,6 +2880,7 @@ as_fn_append ac_func_list " pwrite" as_fn_append ac_func_list " openat" as_fn_append ac_func_list " faccessat" as_fn_append ac_func_list " wordexp" +as_fn_append ac_func_list " getauxval" as_fn_append ac_func_list " seteuid" # Check that the precious variables saved in the cache have kept the same # value. @@ -3070,6 +3074,7 @@ $as_echo "$as_me: Configuring Sudo version $PACKAGE_VERSION" >&6;} + # @@ -6416,6 +6421,8 @@ if test "${enable_openssl+set}" = set; then : no) ;; *) LIBMD="-lcrypto" DIGEST=digest_openssl.lo + $as_echo "#define HAVE_OPENSSL 1" >>confdefs.h + if test "$enableval" != "yes"; then if ${CPPFLAGS+:} false; then : @@ -6530,6 +6537,8 @@ if test "${enable_gcrypt+set}" = set; then : no) ;; *) LIBMD="-lgcrypt" DIGEST=digest_gcrypt.lo + $as_echo "#define HAVE_GCRYPT 1" >>confdefs.h + if test "$enableval" != "yes"; then if ${CPPFLAGS+:} false; then : @@ -17640,6 +17649,10 @@ fi + + + + @@ -18989,6 +19002,8 @@ done + + case "$host_os" in hpux*) if test X"$ac_cv_func_pread" = X"yes"; then @@ -19210,6 +19225,144 @@ esac done +fi +done + +for ac_func in arc4random_uniform +do : + ac_fn_c_check_func "$LINENO" "arc4random_uniform" "ac_cv_func_arc4random_uniform" +if test "x$ac_cv_func_arc4random_uniform" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_ARC4RANDOM_UNIFORM 1 +_ACEOF + +else + + case " $LIBOBJS " in + *" arc4random_uniform.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS arc4random_uniform.$ac_objext" + ;; +esac + + + for _sym in sudo_arc4random_uniform; do + COMPAT_EXP="${COMPAT_EXP}${_sym} +" + done + + for ac_func in arc4random +do : + ac_fn_c_check_func "$LINENO" "arc4random" "ac_cv_func_arc4random" +if test "x$ac_cv_func_arc4random" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_ARC4RANDOM 1 +_ACEOF + +else + + case " $LIBOBJS " in + *" arc4random.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS arc4random.$ac_objext" + ;; +esac + + + for _sym in sudo_arc4random; do + COMPAT_EXP="${COMPAT_EXP}${_sym} +" + done + + # arc4random.c needs getentropy() + for ac_func in getentropy +do : + ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" +if test "x$ac_cv_func_getentropy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_GETENTROPY 1 +_ACEOF + +else + + case " $LIBOBJS " in + *" getentropy.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getentropy.$ac_objext" + ;; +esac + + + for _sym in sudo_getentropy; do + COMPAT_EXP="${COMPAT_EXP}${_sym} +" + done + + +fi +done + + # arc4random.c wants pthread_atfork + for ac_header in pthread.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default" +if test "x$ac_cv_header_pthread_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PTHREAD_H 1 +_ACEOF + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lpthread" >&5 +$as_echo_n "checking for main in -lpthread... " >&6; } +if ${ac_cv_lib_pthread_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pthread_main=yes +else + ac_cv_lib_pthread_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_main" >&5 +$as_echo "$ac_cv_lib_pthread_main" >&6; } +if test "x$ac_cv_lib_pthread_main" = xyes; then : + LIBPTHREAD="-lpthread" +fi + + for ac_func in pthread_atfork +do : + ac_fn_c_check_func "$LINENO" "pthread_atfork" "ac_cv_func_pthread_atfork" +if test "x$ac_cv_func_pthread_atfork" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PTHREAD_ATFORK 1 +_ACEOF + +fi +done + + +fi + +done + + +fi +done + + fi done @@ -22533,6 +22686,21 @@ fi done +OLIBS="$LIBS" +LIBS="$LIBS $lt_cv_dlopen_libs" +for ac_func in dl_iterate_phdr +do : + ac_fn_c_check_func "$LINENO" "dl_iterate_phdr" "ac_cv_func_dl_iterate_phdr" +if test "x$ac_cv_func_dl_iterate_phdr" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DL_ITERATE_PHDR 1 +_ACEOF + +fi +done + +LIBS="$OLIBS" + if test ${with_netsvc-"no"} != "no"; then cat >>confdefs.h < +dnl Copyright (c) 1994-1996,1998-2018 Todd C. Miller dnl AC_PREREQ([2.59]) AC_INIT([sudo], [1.8.24], [https://bugzilla.sudo.ws/], [sudo]) @@ -79,6 +79,7 @@ AC_SUBST([LIBDL]) AC_SUBST([LIBRT]) AC_SUBST([LIBINTL]) AC_SUBST([LIBMD]) +AC_SUBST([LIBPTHREAD]) AC_SUBST([SUDO_NLS]) AC_SUBST([LOCALEDIR_SUFFIX]) AC_SUBST([COMPAT_TEST_PROGS]) @@ -1469,6 +1470,7 @@ AC_ARG_ENABLE(openssl, no) ;; *) LIBMD="-lcrypto" DIGEST=digest_openssl.lo + AC_DEFINE(HAVE_OPENSSL) if test "$enableval" != "yes"; then AX_APPEND_FLAG([-I${enableval}/include], [CPPFLAGS]) SUDO_APPEND_LIBPATH(SUDOERS_LDFLAGS, [${enableval}/lib]) @@ -1483,6 +1485,7 @@ AC_ARG_ENABLE(gcrypt, no) ;; *) LIBMD="-lgcrypt" DIGEST=digest_gcrypt.lo + AC_DEFINE(HAVE_GCRYPT) if test "$enableval" != "yes"; then AX_APPEND_FLAG([-I${enableval}/include], [CPPFLAGS]) SUDO_APPEND_LIBPATH(SUDOERS_LDFLAGS, [${enableval}/lib]) @@ -2346,7 +2349,7 @@ dnl AC_HEADER_DIRENT AC_HEADER_STDBOOL AC_HEADER_MAJOR -AC_CHECK_HEADERS_ONCE([netgroup.h paths.h spawn.h utmpx.h wordexp.h sys/sockio.h sys/bsdtypes.h sys/select.h sys/stropts.h sys/sysmacros.h]) +AC_CHECK_HEADERS_ONCE([netgroup.h paths.h spawn.h utmpx.h wordexp.h sys/sockio.h sys/bsdtypes.h sys/select.h sys/stropts.h sys/sysmacros.h sys/syscall.h sys/statvfs.h]) AC_CHECK_HEADERS([endian.h] [sys/endian.h] [machine/endian.h], [break]) AC_CHECK_HEADERS([procfs.h] [sys/procfs.h], [AC_CHECK_MEMBERS(struct psinfo.pr_ttydev, [AC_CHECK_FUNCS([_ttyname_dev])], [], [AC_INCLUDES_DEFAULT #ifdef HAVE_PROCFS_H @@ -2486,7 +2489,7 @@ dnl dnl Function checks dnl AC_FUNC_GETGROUPS -AC_CHECK_FUNCS_ONCE([fexecve killpg nl_langinfo pread pwrite openat faccessat wordexp]) +AC_CHECK_FUNCS_ONCE([fexecve killpg nl_langinfo pread pwrite openat faccessat wordexp getauxval]) case "$host_os" in hpux*) if test X"$ac_cv_func_pread" = X"yes"; then @@ -2547,6 +2550,24 @@ AC_CHECK_FUNCS([reallocarray], [], [ AC_LIBOBJ(reallocarray) SUDO_APPEND_COMPAT_EXP(sudo_reallocarray) ]) +AC_CHECK_FUNCS([arc4random_uniform], [], [ + AC_LIBOBJ(arc4random_uniform) + SUDO_APPEND_COMPAT_EXP(sudo_arc4random_uniform) + AC_CHECK_FUNCS([arc4random], [], [ + AC_LIBOBJ(arc4random) + SUDO_APPEND_COMPAT_EXP(sudo_arc4random) + # arc4random.c needs getentropy() + AC_CHECK_FUNCS([getentropy], [], [ + AC_LIBOBJ(getentropy) + SUDO_APPEND_COMPAT_EXP(sudo_getentropy) + ]) + # arc4random.c wants pthread_atfork + AC_CHECK_HEADERS([pthread.h], [ + AC_CHECK_LIB(pthread, main, [LIBPTHREAD="-lpthread"]) + AC_CHECK_FUNCS([pthread_atfork]) + ]) + ]) +]) utmp_style=LEGACY AC_CHECK_FUNCS([getutxid getutid], [utmp_style=POSIX; break]) @@ -3255,6 +3276,14 @@ AC_INCLUDES_DEFAULT fi ]) +dnl +dnl Check for dl_iterate_phdr, may require -ldl +dnl +OLIBS="$LIBS" +LIBS="$LIBS $lt_cv_dlopen_libs" +AC_CHECK_FUNCS([dl_iterate_phdr]) +LIBS="$OLIBS" + dnl dnl nsswitch.conf and its equivalents dnl @@ -4534,6 +4563,8 @@ AH_TEMPLATE(HAVE_KINFO_PROC_44BSD, [Define to 1 if your system has a 4.4BSD-styl AH_TEMPLATE(HAVE_KINFO_PROC_FREEBSD, [Define to 1 if your system has a FreeBSD-style kinfo_proc struct.]) AH_TEMPLATE(HAVE_KINFO_PROC2_NETBSD, [Define to 1 if your system has a NetBSD-style kinfo_proc2 struct.]) AH_TEMPLATE(HAVE_KINFO_PROC_OPENBSD, [Define to 1 if your system has an OpenBSD-style kinfo_proc struct.]) +AH_TEMPLATE(HAVE_OPENSSL, [Define to 1 if you are using OpenSSL's sha2 functions.]) +AH_TEMPLATE(HAVE_GCRYPT, [Define to 1 if you are using gcrypt's sha2 functions.]) dnl dnl Bits to copy verbatim into config.h.in dnl diff --git a/include/sudo_rand.h b/include/sudo_rand.h new file mode 100644 index 000000000..2e8308551 --- /dev/null +++ b/include/sudo_rand.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 Todd C. Miller + * + * Permission to use, copy, modify, and 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. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +#ifndef SUDO_RAND_H +#define SUDO_RAND_H + +#include /* For arc4random() on systems that have it */ + +/* + * All libc replacements are prefixed with "sudo_" to avoid namespace issues. + */ + +#ifndef HAVE_ARC4RANDOM +/* Note: not exported by libutil. */ +uint32_t sudo_arc4random(void); +# undef arc4random +# define arc4random() sudo_arc4random() +#endif /* ARC4RANDOM */ + +#ifndef HAVE_ARC4RANDOM_UNIFORM +__dso_public uint32_t sudo_arc4random_uniform(uint32_t upper_bound); +# undef arc4random_uniform +# define arc4random_uniform(_a) sudo_arc4random_uniform((_a)) +#endif /* ARC4RANDOM_UNIFORM */ + +#ifndef HAVE_GETENTROPY +/* Note: not exported by libutil. */ +int sudo_getentropy(void *buf, size_t buflen); +# undef getentropy +# define getentropy(_a, _b) sudo_getentropy((_a), (_b)) +#endif /* HAVE_GETENTROPY */ + +#endif /* SUDO_RAND_H */ diff --git a/lib/util/Makefile.in b/lib/util/Makefile.in index 429f69964..8f07bce8b 100644 --- a/lib/util/Makefile.in +++ b/lib/util/Makefile.in @@ -155,9 +155,9 @@ $(shlib_opt): $(shlib_exp) libsudo_util.la: $(LTOBJS) @LT_LDDEP@ case "$(LT_LDFLAGS)" in \ *-no-install*) \ - $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(LDFLAGS) $(LT_LDFLAGS) $(LTOBJS) @LIBINTL@ @LIBMD@ @LIBDL@ @LIBRT@;; \ + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(LDFLAGS) $(LT_LDFLAGS) $(LTOBJS) @LIBINTL@ @LIBMD@ @LIBPTHREAD@ @LIBDL@ @LIBRT@;; \ *) \ - $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(LDFLAGS) $(ASAN_LDFLAGS) $(SSP_LDFLAGS) $(LT_LDFLAGS) $(LTOBJS) -version-info $(SHLIB_VERSION) -rpath $(libexecdir)/sudo @LT_DEP_LIBS@ @LIBINTL@ @LIBMD@ @LIBDL@ @LIBRT@;; \ + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(LDFLAGS) $(ASAN_LDFLAGS) $(SSP_LDFLAGS) $(LT_LDFLAGS) $(LTOBJS) -version-info $(SHLIB_VERSION) -rpath $(libexecdir)/sudo @LT_DEP_LIBS@ @LIBINTL@ @LIBMD@ @LIBPTHREAD@ @LIBDL@ @LIBRT@;; \ esac siglist.c: mksiglist @@ -352,6 +352,13 @@ aix.lo: $(srcdir)/aix.c $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(top_builddir)/config.h $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/aix.c +arc4random.lo: $(srcdir)/arc4random.c $(incdir)/sudo_compat.h \ + $(incdir)/sudo_rand.h $(srcdir)/arc4random.h \ + $(srcdir)/chacha_private.h $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/arc4random.c +arc4random_uniform.lo: $(srcdir)/arc4random_uniform.c $(incdir)/sudo_compat.h \ + $(incdir)/sudo_rand.h $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/arc4random_uniform.c atofoo_test.lo: $(srcdir)/regress/atofoo/atofoo_test.c \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_util.h \ @@ -414,6 +421,10 @@ getaddrinfo.lo: $(srcdir)/getaddrinfo.c $(incdir)/compat/getaddrinfo.h \ $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/getaddrinfo.c getcwd.lo: $(srcdir)/getcwd.c $(incdir)/sudo_compat.h $(top_builddir)/config.h $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/getcwd.c +getentropy.lo: $(srcdir)/getentropy.c $(incdir)/sudo_compat.h \ + $(incdir)/sudo_digest.h $(incdir)/sudo_rand.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/getentropy.c getgrouplist.lo: $(srcdir)/getgrouplist.c $(incdir)/compat/nss_dbdefs.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_util.h $(top_builddir)/config.h diff --git a/lib/util/arc4random.c b/lib/util/arc4random.c new file mode 100644 index 000000000..11b4c5919 --- /dev/null +++ b/lib/util/arc4random.c @@ -0,0 +1,212 @@ +/* $OpenBSD: arc4random.c,v 1.54 2015/09/13 08:31:47 guenther Exp $ */ + +/* + * Copyright (c) 1996, David Mazieres + * Copyright (c) 2008, Damien Miller + * Copyright (c) 2013, Markus Friedl + * Copyright (c) 2014, Theo de Raadt + * + * Permission to use, copy, modify, and 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. + */ + +/* + * ChaCha based random number generator for OpenBSD. + */ + +#include + +#ifndef HAVE_ARC4RANDOM + +#include +#include + +#include +#include +#include +#include +#include +#include +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#endif + +#include "sudo_compat.h" +#include "sudo_rand.h" + +#define KEYSTREAM_ONLY +#include "chacha_private.h" + +#define minimum(a, b) ((a) < (b) ? (a) : (b)) + +#if defined(__GNUC__) || defined(_MSC_VER) +#define inline __inline +#else /* __GNUC__ || _MSC_VER */ +#define inline +#endif /* !__GNUC__ && !_MSC_VER */ + +#define KEYSZ 32 +#define IVSZ 8 +#define BLOCKSZ 64 +#define RSBUFSZ (16*BLOCKSZ) + +/* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */ +static struct _rs { + size_t rs_have; /* valid bytes at end of rs_buf */ + size_t rs_count; /* bytes till reseed */ +} *rs; + +/* Maybe be preserved in fork children, if _rs_allocate() decides. */ +static struct _rsx { + chacha_ctx rs_chacha; /* chacha context for random keystream */ + unsigned char rs_buf[RSBUFSZ]; /* keystream blocks */ +} *rsx; + +static inline int _rs_allocate(struct _rs **, struct _rsx **); +static inline void _rs_forkdetect(void); +#include "arc4random.h" + +static inline void _rs_rekey(unsigned char *dat, size_t datlen); + +static inline void +_rs_init(unsigned char *buf, size_t n) +{ + if (n < KEYSZ + IVSZ) + return; + + if (rs == NULL) { + if (_rs_allocate(&rs, &rsx) == -1) + abort(); + } + + chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0); + chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ); +} + +static void +_rs_stir(void) +{ + unsigned char rnd[KEYSZ + IVSZ]; + + if (getentropy(rnd, sizeof rnd) == -1) + _getentropy_fail(); + + if (!rs) + _rs_init(rnd, sizeof(rnd)); + else + _rs_rekey(rnd, sizeof(rnd)); + memset_s(rnd, sizeof(rnd), 0, sizeof(rnd)); /* discard source seed */ + + /* invalidate rs_buf */ + rs->rs_have = 0; + memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf)); + + rs->rs_count = 1600000; +} + +static inline void +_rs_stir_if_needed(size_t len) +{ + _rs_forkdetect(); + if (!rs || rs->rs_count <= len) + _rs_stir(); + if (rs->rs_count <= len) + rs->rs_count = 0; + else + rs->rs_count -= len; +} + +static inline void +_rs_rekey(unsigned char *dat, size_t datlen) +{ +#ifndef KEYSTREAM_ONLY + memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf)); +#endif + /* fill rs_buf with the keystream */ + chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf, + rsx->rs_buf, sizeof(rsx->rs_buf)); + /* mix in optional user provided data */ + if (dat) { + size_t i, m; + + m = minimum(datlen, KEYSZ + IVSZ); + for (i = 0; i < m; i++) + rsx->rs_buf[i] ^= dat[i]; + } + /* immediately reinit for backtracking resistance */ + _rs_init(rsx->rs_buf, KEYSZ + IVSZ); + memset(rsx->rs_buf, 0, KEYSZ + IVSZ); + rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ; +} + +static inline void +_rs_random_buf(void *_buf, size_t n) +{ + unsigned char *buf = (unsigned char *)_buf; + unsigned char *keystream; + size_t m; + + _rs_stir_if_needed(n); + while (n > 0) { + if (rs->rs_have > 0) { + m = minimum(n, rs->rs_have); + keystream = rsx->rs_buf + sizeof(rsx->rs_buf) + - rs->rs_have; + memcpy(buf, keystream, m); + memset(keystream, 0, m); + buf += m; + n -= m; + rs->rs_have -= m; + } + if (rs->rs_have == 0) + _rs_rekey(NULL, 0); + } +} + +static inline void +_rs_random_u32(uint32_t *val) +{ + unsigned char *keystream; + + _rs_stir_if_needed(sizeof(*val)); + if (rs->rs_have < sizeof(*val)) + _rs_rekey(NULL, 0); + keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have; + memcpy(val, keystream, sizeof(*val)); + memset(keystream, 0, sizeof(*val)); + rs->rs_have -= sizeof(*val); +} + +uint32_t +sudo_arc4random(void) +{ + uint32_t val; + + _ARC4_LOCK(); + _rs_random_u32(&val); + _ARC4_UNLOCK(); + return val; +} + +#ifdef notdef +void +sudo_arc4random_buf(void *buf, size_t n) +{ + _ARC4_LOCK(); + _rs_random_buf(buf, n); + _ARC4_UNLOCK(); +} +#endif + +#endif /* HAVE_ARC4RANDOM */ diff --git a/lib/util/arc4random.h b/lib/util/arc4random.h new file mode 100644 index 000000000..ec63c635e --- /dev/null +++ b/lib/util/arc4random.h @@ -0,0 +1,96 @@ +/* $OpenBSD: arc4random.h,v 1.4 2015/01/15 06:57:18 deraadt Exp $ */ + +/* + * Copyright (c) 1996, David Mazieres + * Copyright (c) 2008, Damien Miller + * Copyright (c) 2013, Markus Friedl + * Copyright (c) 2014, Theo de Raadt + * + * Permission to use, copy, modify, and 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. + */ + +/* + * Stub functions for portability. + */ + +#include + +#include +#ifdef HAVE_PTHREAD_H +#include + +static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER; +#define _ARC4_LOCK() pthread_mutex_lock(&arc4random_mtx) +#define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx) +#else +#define _ARC4_LOCK() +#define _ARC4_UNLOCK() +#endif /* HAVE_PTHREAD_H */ + +#ifdef HAVE_PTHREAD_ATFORK +# define _ARC4_ATFORK(f) pthread_atfork(NULL, NULL, (f)) +# else +# define _ARC4_ATFORK(f) +#endif + +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +# define MAP_ANON MAP_ANONYMOUS +#endif + +static inline void +_getentropy_fail(void) +{ + raise(SIGKILL); +} + +static volatile sig_atomic_t _rs_forked; + +#ifdef HAVE_PTHREAD_ATFORK +static inline void +_rs_forkhandler(void) +{ + _rs_forked = 1; +} +#endif /* HAVE_PTHREAD_ATFORK */ + +static inline void +_rs_forkdetect(void) +{ + static pid_t _rs_pid = 0; + pid_t pid = getpid(); + + if (_rs_pid == 0 || _rs_pid != pid || _rs_forked) { + _rs_pid = pid; + _rs_forked = 0; + if (rs) + memset(rs, 0, sizeof(*rs)); + } +} + +static inline int +_rs_allocate(struct _rs **rsp, struct _rsx **rsxp) +{ + if ((*rsp = (void *)mmap(NULL, sizeof(**rsp), PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) + return (-1); + + if ((*rsxp = (void *)mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) { + munmap((void *)*rsp, sizeof(**rsp)); + *rsp = NULL; + return (-1); + } + + _ARC4_ATFORK(_rs_forkhandler); + return (0); +} diff --git a/lib/util/arc4random_uniform.c b/lib/util/arc4random_uniform.c new file mode 100644 index 000000000..9567d22e7 --- /dev/null +++ b/lib/util/arc4random_uniform.c @@ -0,0 +1,70 @@ +/* $OpenBSD: arc4random_uniform.c,v 1.2 2015/09/13 08:31:47 guenther Exp $ */ + +/* + * Copyright (c) 2008, Damien Miller + * + * Permission to use, copy, modify, and 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. + */ + +#include + +#ifndef HAVE_ARC4RANDOM_UNIFORM + +#include +#include +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#endif + +#include "sudo_compat.h" +#include "sudo_rand.h" + +/* + * Calculate a uniformly distributed random number less than upper_bound + * avoiding "modulo bias". + * + * Uniformity is achieved by generating new random numbers until the one + * returned is outside the range [0, 2**32 % upper_bound). This + * guarantees the selected random number will be inside + * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound) + * after reduction modulo upper_bound. + */ +uint32_t +sudo_arc4random_uniform(uint32_t upper_bound) +{ + uint32_t r, min; + + if (upper_bound < 2) + return 0; + + /* 2**32 % x == (2**32 - x) % x */ + min = -upper_bound % upper_bound; + + /* + * This could theoretically loop forever but each retry has + * p > 0.5 (worst case, usually far better) of selecting a + * number inside the range we need, so it should rarely need + * to re-roll. + */ + for (;;) { + r = arc4random(); + if (r >= min) + break; + } + + return r % upper_bound; +} + +#endif /* HAVE_ARC4RANDOM_UNIFORM */ diff --git a/lib/util/chacha_private.h b/lib/util/chacha_private.h new file mode 100644 index 000000000..7c3680fa6 --- /dev/null +++ b/lib/util/chacha_private.h @@ -0,0 +1,222 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +/* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */ + +typedef unsigned char u8; +typedef unsigned int u32; + +typedef struct +{ + u32 input[16]; /* could be compressed */ +} chacha_ctx; + +#define U8C(v) (v##U) +#define U32C(v) (v##U) + +#define U8V(v) ((u8)(v) & U8C(0xFF)) +#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define U8TO32_LITTLE(p) \ + (((u32)((p)[0]) ) | \ + ((u32)((p)[1]) << 8) | \ + ((u32)((p)[2]) << 16) | \ + ((u32)((p)[3]) << 24)) + +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[16] = "expand 32-byte k"; +static const char tau[16] = "expand 16-byte k"; + +static void +chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits,u32 ivbits) +{ + const char *constants; + + x->input[4] = U8TO32_LITTLE(k + 0); + x->input[5] = U8TO32_LITTLE(k + 4); + x->input[6] = U8TO32_LITTLE(k + 8); + x->input[7] = U8TO32_LITTLE(k + 12); + if (kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[8] = U8TO32_LITTLE(k + 0); + x->input[9] = U8TO32_LITTLE(k + 4); + x->input[10] = U8TO32_LITTLE(k + 8); + x->input[11] = U8TO32_LITTLE(k + 12); + x->input[0] = U8TO32_LITTLE(constants + 0); + x->input[1] = U8TO32_LITTLE(constants + 4); + x->input[2] = U8TO32_LITTLE(constants + 8); + x->input[3] = U8TO32_LITTLE(constants + 12); +} + +static void +chacha_ivsetup(chacha_ctx *x,const u8 *iv) +{ + x->input[12] = 0; + x->input[13] = 0; + x->input[14] = U8TO32_LITTLE(iv + 0); + x->input[15] = U8TO32_LITTLE(iv + 4); +} + +static void +chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes) +{ + u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget = NULL; + u8 tmp[64]; + u_int i; + + if (!bytes) return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS(x0,j0); + x1 = PLUS(x1,j1); + x2 = PLUS(x2,j2); + x3 = PLUS(x3,j3); + x4 = PLUS(x4,j4); + x5 = PLUS(x5,j5); + x6 = PLUS(x6,j6); + x7 = PLUS(x7,j7); + x8 = PLUS(x8,j8); + x9 = PLUS(x9,j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + +#ifndef KEYSTREAM_ONLY + x0 = XOR(x0,U8TO32_LITTLE(m + 0)); + x1 = XOR(x1,U8TO32_LITTLE(m + 4)); + x2 = XOR(x2,U8TO32_LITTLE(m + 8)); + x3 = XOR(x3,U8TO32_LITTLE(m + 12)); + x4 = XOR(x4,U8TO32_LITTLE(m + 16)); + x5 = XOR(x5,U8TO32_LITTLE(m + 20)); + x6 = XOR(x6,U8TO32_LITTLE(m + 24)); + x7 = XOR(x7,U8TO32_LITTLE(m + 28)); + x8 = XOR(x8,U8TO32_LITTLE(m + 32)); + x9 = XOR(x9,U8TO32_LITTLE(m + 36)); + x10 = XOR(x10,U8TO32_LITTLE(m + 40)); + x11 = XOR(x11,U8TO32_LITTLE(m + 44)); + x12 = XOR(x12,U8TO32_LITTLE(m + 48)); + x13 = XOR(x13,U8TO32_LITTLE(m + 52)); + x14 = XOR(x14,U8TO32_LITTLE(m + 56)); + x15 = XOR(x15,U8TO32_LITTLE(m + 60)); +#endif + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0,x0); + U32TO8_LITTLE(c + 4,x1); + U32TO8_LITTLE(c + 8,x2); + U32TO8_LITTLE(c + 12,x3); + U32TO8_LITTLE(c + 16,x4); + U32TO8_LITTLE(c + 20,x5); + U32TO8_LITTLE(c + 24,x6); + U32TO8_LITTLE(c + 28,x7); + U32TO8_LITTLE(c + 32,x8); + U32TO8_LITTLE(c + 36,x9); + U32TO8_LITTLE(c + 40,x10); + U32TO8_LITTLE(c + 44,x11); + U32TO8_LITTLE(c + 48,x12); + U32TO8_LITTLE(c + 52,x13); + U32TO8_LITTLE(c + 56,x14); + U32TO8_LITTLE(c + 60,x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; +#ifndef KEYSTREAM_ONLY + m += 64; +#endif + } +} diff --git a/lib/util/getentropy.c b/lib/util/getentropy.c new file mode 100644 index 000000000..87e3ec844 --- /dev/null +++ b/lib/util/getentropy.c @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2014 Theo de Raadt + * Copyright (c) 2014 Bob Beck + * + * Permission to use, copy, modify, and 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. + * + * Emulation of getentropy(2) as documented at: + * http://man.openbsd.org/getentropy.2 + */ + +#include + +#ifndef HAVE_GETENTROPY + +#include +#include +#include +#include +#include +#ifdef HAVE_SYSCTL +# include +#endif +#ifdef HAVE_SYS_STATVFS_H +# include +#endif +#include +#include +#ifdef __linux__ +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#endif +#ifdef HAVE_GETAUXVAL +# include +#endif +#ifdef HAVE_DL_ITERATE_PHDR +# include +#endif + +#include "sudo_compat.h" +#include "sudo_digest.h" +#include "sudo_rand.h" + +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +# define MAP_ANON MAP_ANONYMOUS +#endif + +#define REPEAT 5 +#define min(a, b) (((a) < (b)) ? (a) : (b)) + +#define HX(a, b) \ + do { \ + if ((a)) \ + HD(errno); \ + else \ + HD(b); \ + } while (0) + +#define HR(x, l) (sudo_digest_update(ctx, (char *)(x), (l))) +#define HD(x) (sudo_digest_update(ctx, (char *)&(x), sizeof (x))) +#define HF(x) (sudo_digest_update(ctx, (char *)&(x), sizeof (void*))) + +int sudo_getentropy(void *buf, size_t len); + +static int getentropy_getrandom(void *buf, size_t len); +static int getentropy_sysctl(void *buf, size_t len); +static int getentropy_urandom(void *buf, size_t len, const char *path, + int devfscheck); +static int getentropy_fallback(void *buf, size_t len); +static int gotdata(char *buf, size_t len); +#ifdef HAVE_DL_ITERATE_PHDR +static int getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data); +#endif + +int +sudo_getentropy(void *buf, size_t len) +{ + int ret = -1; + + if (len > 256) { + errno = EIO; + return (-1); + } + + ret = getentropy_getrandom(buf, len); + if (ret != -1) + return (ret); + + ret = getentropy_sysctl(buf, len); + if (ret != -1) + return (ret); + + /* + * Try to get entropy with /dev/urandom + */ + ret = getentropy_urandom(buf, len, "/dev/urandom", 0); + if (ret != -1) + return (ret); + + /* + * Entropy collection via /dev/urandom has failed. + * + * No other API exists for collecting entropy, and we have no + * failsafe way to get it that is not sensitive to resource exhaustion. + * + * We have very few options: + * - Even syslog_r is unsafe to call at this low level, so + * there is no way to alert the user or program. + * - Cannot call abort() because some systems have unsafe + * corefiles. + * - Could raise(SIGKILL) resulting in silent program termination. + * - Return EIO, to hint that arc4random's stir function + * should raise(SIGKILL) + * - Do the best under the circumstances.... + * + * This code path exists to bring light to the issue that the OS + * does not provide a failsafe API for entropy collection. + * + * We hope this demonstrates that the OS should consider + * providing a new failsafe API which works in a chroot or + * when file descriptors are exhausted. + */ +#undef FAIL_INSTEAD_OF_TRYING_FALLBACK +#ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK + raise(SIGKILL); +#endif + ret = getentropy_fallback(buf, len); + if (ret != -1) + return (ret); + + errno = EIO; + return (ret); +} + +/* + * Basic sanity checking; wish we could do better. + */ +static int +gotdata(char *buf, size_t len) +{ + char any_set = 0; + size_t i; + + for (i = 0; i < len; ++i) + any_set |= buf[i]; + if (any_set == 0) + return (-1); + return (0); +} + +static int +getentropy_urandom(void *buf, size_t len, const char *path, int devfscheck) +{ + struct stat st; + size_t i; + int fd, flags; + int save_errno = errno; + +start: + + /* We do not use O_NOFOLLOW since /dev/urandom is a link on Solaris. */ + flags = O_RDONLY; +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif + fd = open(path, flags, 0); + if (fd == -1) { + if (errno == EINTR) + goto start; + goto nodevrandom; + } +#ifndef O_CLOEXEC + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); +#endif + + /* Lightly verify that the device node looks sane */ + if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) { + close(fd); + goto nodevrandom; + } + for (i = 0; i < len; ) { + size_t wanted = len - i; + ssize_t ret = read(fd, (char *)buf + i, wanted); + + if (ret == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + close(fd); + goto nodevrandom; + } + i += ret; + } + close(fd); + if (gotdata(buf, len) == 0) { + errno = save_errno; + return (0); /* satisfied */ + } +nodevrandom: + errno = EIO; + return (-1); +} + +#if defined(HAVE_SYSCTL) && defined(KERN_ARND) +static int +getentropy_sysctl(void *buf, size_t len) +{ + int save_errno = errno; + int mib[2]; + size_t i; + + mib[0] = CTL_KERN; + mib[1] = KERN_ARND; + + for (i = 0; i < len; ) { + size_t chunk = len - i; + + if (sysctl(mib, 2, (char *)buf + i, &chunk, NULL, 0) == -1) + goto sysctlfailed; + i += chunk; + } + if (gotdata(buf, len) == 0) { + errno = save_errno; + return (0); /* satisfied */ + } +sysctlfailed: + errno = EIO; + return (-1); +} +#elif defined(SYS__sysctl) && defined(RANDOM_UUID) +static int +getentropy_sysctl(void *buf, size_t len) +{ + static int mib[3]; + size_t i; + int save_errno = errno; + + mib[0] = CTL_KERN; + mib[1] = KERN_RANDOM; + mib[2] = RANDOM_UUID; + + for (i = 0; i < len; ) { + size_t chunk = min(len - i, 16); + + /* SYS__sysctl because some systems already removed sysctl() */ + struct __sysctl_args args = { + .name = mib, + .nlen = 3, + .oldval = (char *)buf + i, + .oldlenp = &chunk, + }; + if (syscall(SYS__sysctl, &args) != 0) + goto sysctlfailed; + i += chunk; + } + if (gotdata(buf, len) == 0) { + errno = save_errno; + return (0); /* satisfied */ + } +sysctlfailed: + errno = EIO; + return (-1); +} +#else +static int +getentropy_sysctl(void *buf, size_t len) +{ + errno = ENOTSUP; + return (-1); +} +#endif + +#if defined(SYS_getrandom) && defined(GRND_NONBLOCK) +static int +getentropy_getrandom(void *buf, size_t len) +{ + int pre_errno = errno; + int ret; + + /* + * Try descriptor-less getrandom(), in non-blocking mode. + * + * The design of Linux getrandom is broken. It has an + * uninitialized phase coupled with blocking behaviour, which + * is unacceptable from within a library at boot time without + * possible recovery. See http://bugs.python.org/issue26839#msg267745 + */ + do { + ret = syscall(SYS_getrandom, buf, len, GRND_NONBLOCK); + } while (ret == -1 && errno == EINTR); + + if (ret < 0 || (size_t)ret != len) + return (-1); + errno = pre_errno; + return (0); +} +#else +static int +getentropy_getrandom(void *buf, size_t len) +{ + errno = ENOTSUP; + return (-1); +} +#endif + +#ifdef HAVE_CLOCK_GETTIME +static const int cl[] = { + CLOCK_REALTIME, +#ifdef CLOCK_MONOTONIC + CLOCK_MONOTONIC, +#endif +#ifdef CLOCK_MONOTONIC_RAW + CLOCK_MONOTONIC_RAW, +#endif +#ifdef CLOCK_TAI + CLOCK_TAI, +#endif +#ifdef CLOCK_VIRTUAL + CLOCK_VIRTUAL, +#endif +#ifdef CLOCK_UPTIME + CLOCK_UPTIME, +#endif +#ifdef CLOCK_PROCESS_CPUTIME_ID + CLOCK_PROCESS_CPUTIME_ID, +#endif +#ifdef CLOCK_THREAD_CPUTIME_ID + CLOCK_THREAD_CPUTIME_ID, +#endif +}; +#endif /* HAVE_CLOCK_GETTIME */ + +#ifdef HAVE_DL_ITERATE_PHDR +static int +getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data) +{ + struct sudo_digest *ctx = data; + + sudo_digest_update(ctx, &info->dlpi_addr, sizeof (info->dlpi_addr)); + return (0); +} +#endif + +static int +getentropy_fallback(void *buf, size_t len) +{ + unsigned char *results = NULL; + int save_errno = errno, e, pgs = sysconf(_SC_PAGESIZE), faster = 0, repeat; + int ret = -1; + static int cnt; + struct timespec ts; + struct timeval tv; + struct rusage ru; + sigset_t sigset; + struct stat st; + struct sudo_digest *ctx; + static pid_t lastpid; + pid_t pid; + size_t i, ii, m, digest_len; + char *p; + + if ((ctx = sudo_digest_alloc(SUDO_DIGEST_SHA512)) == NULL) + goto done; + digest_len = sudo_digest_getlen(SUDO_DIGEST_SHA512); + if (digest_len == (size_t)-1 || (results = malloc(digest_len)) == NULL) + goto done; + + pid = getpid(); + if (lastpid == pid) { + faster = 1; + repeat = 2; + } else { + faster = 0; + lastpid = pid; + repeat = REPEAT; + } + for (i = 0; i < len; ) { + int j; + for (j = 0; j < repeat; j++) { + HX((e = gettimeofday(&tv, NULL)) == -1, tv); + if (e != -1) { + cnt += (int)tv.tv_sec; + cnt += (int)tv.tv_usec; + } +#ifdef HAVE_DL_ITERATE_PHDR + dl_iterate_phdr(getentropy_phdr, ctx); +#endif + +#ifdef HAVE_CLOCK_GETTIME + for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); ii++) + HX(clock_gettime(cl[ii], &ts) == -1, ts); +#endif /* HAVE_CLOCK_GETTIME */ + + HX((pid = getpid()) == -1, pid); + HX((pid = getsid(pid)) == -1, pid); + HX((pid = getppid()) == -1, pid); + HX((pid = getpgid(0)) == -1, pid); + HX((e = getpriority(0, 0)) == -1, e); + + if (!faster) { + ts.tv_sec = 0; + ts.tv_nsec = 1; + (void) nanosleep(&ts, NULL); + } + + HX(sigpending(&sigset) == -1, sigset); + HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1, + sigset); + + HF(sudo_getentropy); /* an addr in this library */ + HF(printf); /* an addr in libc */ + p = (char *)&p; + HD(p); /* an addr on stack */ + p = (char *)&errno; + HD(p); /* the addr of errno */ + + if (i == 0) { +#ifdef HAVE_SYS_STATVFS_H + struct statvfs stvfs; +#endif + struct termios tios; + off_t off; + + /* + * Prime-sized mappings encourage fragmentation; + * thus exposing some address entropy. + */ + struct mm { + size_t npg; + void *p; + } mm[] = { + { 17, MAP_FAILED }, { 3, MAP_FAILED }, + { 11, MAP_FAILED }, { 2, MAP_FAILED }, + { 5, MAP_FAILED }, { 3, MAP_FAILED }, + { 7, MAP_FAILED }, { 1, MAP_FAILED }, + { 57, MAP_FAILED }, { 3, MAP_FAILED }, + { 131, MAP_FAILED }, { 1, MAP_FAILED }, + }; + + for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) { + HX(mm[m].p = mmap(NULL, + mm[m].npg * pgs, + PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON, -1, + (off_t)0), mm[m].p); + if (mm[m].p != MAP_FAILED) { + size_t mo; + + /* Touch some memory... */ + p = mm[m].p; + mo = cnt % + (mm[m].npg * pgs - 1); + p[mo] = 1; + cnt += (int)((long)(mm[m].p) + / pgs); + } + + /* Check cnts and times... */ + for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); + ii++) { + HX((e = clock_gettime(cl[ii], + &ts)) == -1, ts); + if (e != -1) + cnt += (int)ts.tv_nsec; + } + + HX((e = getrusage(RUSAGE_SELF, + &ru)) == -1, ru); + if (e != -1) { + cnt += (int)ru.ru_utime.tv_sec; + cnt += (int)ru.ru_utime.tv_usec; + } + } + + for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) { + if (mm[m].p != MAP_FAILED) + munmap(mm[m].p, mm[m].npg * pgs); + mm[m].p = MAP_FAILED; + } + + HX(stat(".", &st) == -1, st); + HX(stat("/", &st) == -1, st); + +#ifdef HAVE_SYS_STATVFS_H + HX(statvfs(".", &stvfs) == -1, stvfs); + HX(statvfs("/", &stvfs) == -1, stvfs); +#endif + HX((e = fstat(0, &st)) == -1, st); + if (e == -1) { + if (S_ISREG(st.st_mode) || + S_ISFIFO(st.st_mode) || + S_ISSOCK(st.st_mode)) { +#ifdef HAVE_SYS_STATVFS_H + HX(fstatvfs(0, &stvfs) == -1, + stvfs); +#endif + HX((off = lseek(0, (off_t)0, + SEEK_CUR)) < 0, off); + } + if (S_ISCHR(st.st_mode)) { + HX(tcgetattr(0, &tios) == -1, + tios); +#if 0 + } else if (S_ISSOCK(st.st_mode)) { + struct sockaddr_storage ss; + socklen_t ssl; + memset(&ss, 0, sizeof ss); + ssl = sizeof(ss); + HX(getpeername(0, + (void *)&ss, &ssl) == -1, + ss); +#endif + } + } + + HX((e = getrusage(RUSAGE_CHILDREN, + &ru)) == -1, ru); + if (e != -1) { + cnt += (int)ru.ru_utime.tv_sec; + cnt += (int)ru.ru_utime.tv_usec; + } + } else { + /* Subsequent hashes absorb previous result */ + HR(results, digest_len); + } + + HX((e = gettimeofday(&tv, NULL)) == -1, tv); + if (e != -1) { + cnt += (int)tv.tv_sec; + cnt += (int)tv.tv_usec; + } + + HD(cnt); + } + +#ifdef HAVE_GETAUXVAL +#ifdef AT_RANDOM + /* Not as random as you think but we take what we are given */ + p = (char *) getauxval(AT_RANDOM); + if (p) + HR(p, 16); +#endif +#ifdef AT_SYSINFO_EHDR + p = (char *) getauxval(AT_SYSINFO_EHDR); + if (p) + HR(p, pgs); +#endif +#ifdef AT_BASE + p = (char *) getauxval(AT_BASE); + if (p) + HD(p); +#endif +#endif /* HAVE_GETAUXVAL */ + + sudo_digest_final(ctx, results); + sudo_digest_reset(ctx); + memcpy((char *)buf + i, results, min(digest_len, len - i)); + i += min(digest_len, len - i); + } + if (gotdata(buf, len) == 0) { + errno = save_errno; + ret = 0; /* satisfied */ + } else { + errno == EIO; + } +done: + sudo_digest_free(ctx); + if (results != NULL) { + memset_s(results, sizeof(results), 0, sizeof(results)); + free(results); + } + return (ret); +} + +#endif /* HAVE_GETENTROPY */ diff --git a/lib/util/util.exp.in b/lib/util/util.exp.in index e0e433683..e00076830 100644 --- a/lib/util/util.exp.in +++ b/lib/util/util.exp.in @@ -1,4 +1,5 @@ @COMPAT_EXP@initprogname +sudo_arc4random_uniform sudo_conf_askpass_path_v1 sudo_conf_clear_paths_v1 sudo_conf_debug_files_v1 diff --git a/mkdep.pl b/mkdep.pl index fdbf9ec5e..b18492f29 100755 --- a/mkdep.pl +++ b/mkdep.pl @@ -83,7 +83,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\@: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 nanosleep.lo pw_dup.lo reallocarray.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo strtonum.lo utimens.lo vsyslog.lo pipe2.lo:; + $makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_uniform.lo closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getentropy.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 nanosleep.lo pw_dup.lo reallocarray.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo strtonum.lo utimens.lo vsyslog.lo pipe2.lo:; # Parse OBJS lines my %objs; -- 2.40.0