diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2022-08-25 18:16:56 -0500 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2022-08-25 18:30:11 -0700 |
commit | 7b05ffda80151d0c1a73ec1a4d0cd359849968d5 (patch) | |
tree | 7ab6de8573149108ba8bc190bd8fabb526f541b9 /lib | |
parent | 9bd91a3751cfb01c9499a5ee7080349b9c8f0f65 (diff) | |
download | emacs-7b05ffda80151d0c1a73ec1a4d0cd359849968d5.tar.gz emacs-7b05ffda80151d0c1a73ec1a4d0cd359849968d5.tar.bz2 emacs-7b05ffda80151d0c1a73ec1a4d0cd359849968d5.zip |
Update from Gnulib by running admin/merge-gnulib
Diffstat (limited to 'lib')
-rw-r--r-- | lib/tempname.c | 182 |
1 files changed, 54 insertions, 128 deletions
diff --git a/lib/tempname.c b/lib/tempname.c index 5adfe629a88..11b4796b34b 100644 --- a/lib/tempname.c +++ b/lib/tempname.c @@ -20,16 +20,10 @@ # include "tempname.h" #endif -#include <sys/types.h> -#include <assert.h> #include <stdbool.h> - #include <errno.h> #include <stdio.h> -#ifndef P_tmpdir -# define P_tmpdir "/tmp" -#endif #ifndef TMP_MAX # define TMP_MAX 238328 #endif @@ -43,27 +37,23 @@ # error report this to bug-gnulib@gnu.org #endif -#include <stddef.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> -#include <stdalign.h> #include <stdint.h> #include <sys/random.h> #include <sys/stat.h> #include <time.h> #if _LIBC -# define struct_stat64 struct stat64 -# define __secure_getenv __libc_secure_getenv +# define struct_stat64 struct __stat64_t64 #else # define struct_stat64 struct stat # define __gen_tempname gen_tempname # define __mkdir mkdir # define __open open -# define __lstat64(file, buf) lstat (file, buf) -# define __stat64(file, buf) stat (file, buf) +# define __lstat64_time64(file, buf) lstat (file, buf) # define __getrandom getrandom # define __clock_gettime64 clock_gettime # define __timespec64 timespec @@ -77,100 +67,56 @@ typedef uint_fast64_t random_value; #define BASE_62_DIGITS 10 /* 62**10 < UINT_FAST64_MAX */ #define BASE_62_POWER (62LL * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62) -#if _LIBC || (defined CLOCK_MONOTONIC && HAVE_CLOCK_GETTIME) -# define HAS_CLOCK_ENTROPY true -#else -# define HAS_CLOCK_ENTROPY false -#endif +/* Return the result of mixing the entropy from R and S. + Assume that R and S are not particularly random, + and that the result should look randomish to an untrained eye. */ static random_value -random_bits (random_value var, bool use_getrandom) +mix_random_values (random_value r, random_value s) { - random_value r; - /* Without GRND_NONBLOCK it can be blocked for minutes on some systems. */ - if (use_getrandom && __getrandom (&r, sizeof r, GRND_NONBLOCK) == sizeof r) - return r; -#if HAS_CLOCK_ENTROPY - /* Add entropy if getrandom did not work. */ - struct __timespec64 tv; - __clock_gettime64 (CLOCK_MONOTONIC, &tv); - var ^= tv.tv_nsec; -#endif - return 2862933555777941757 * var + 3037000493; + /* As this code is used only when high-quality randomness is neither + available nor necessary, there is no need for fancier polynomials + such as those in the Linux kernel's 'random' driver. */ + return (2862933555777941757 * r + 3037000493) ^ s; } -#if _LIBC -/* Return nonzero if DIR is an existent directory. */ -static int -direxists (const char *dir) -{ - struct_stat64 buf; - return __stat64 (dir, &buf) == 0 && S_ISDIR (buf.st_mode); -} +/* Set *R to a random value. + Return true if *R is set to high-quality value taken from getrandom. + Otherwise return false, falling back to a low-quality *R that might + depend on S. -/* Path search algorithm, for tmpnam, tmpfile, etc. If DIR is - non-null and exists, uses it; otherwise uses the first of $TMPDIR, - P_tmpdir, /tmp that exists. Copies into TMPL a template suitable - for use with mk[s]temp. Will fail (-1) if DIR is non-null and - doesn't exist, none of the searched dirs exists, or there's not - enough space in TMPL. */ -int -__path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx, - int try_tmpdir) + This function returns false only when getrandom fails. + On GNU systems this should happen only early in the boot process, + when the fallback should be good enough for programs using tempname + because any attacker likely has root privileges already. */ + +static bool +random_bits (random_value *r, random_value s) { - const char *d; - size_t dlen, plen; + /* Without GRND_NONBLOCK it can be blocked for minutes on some systems. */ + if (__getrandom (r, sizeof *r, GRND_NONBLOCK) == sizeof *r) + return true; - if (!pfx || !pfx[0]) - { - pfx = "file"; - plen = 4; - } - else - { - plen = strlen (pfx); - if (plen > 5) - plen = 5; - } + /* If getrandom did not work, use ersatz entropy based on low-order + clock bits. On GNU systems getrandom should fail only + early in booting, when ersatz should be good enough. + Do not use ASLR-based entropy, as that would leak ASLR info into + the resulting file name which is typically public. - if (try_tmpdir) - { - d = __secure_getenv ("TMPDIR"); - if (d != NULL && direxists (d)) - dir = d; - else if (dir != NULL && direxists (dir)) - /* nothing */ ; - else - dir = NULL; - } - if (dir == NULL) - { - if (direxists (P_tmpdir)) - dir = P_tmpdir; - else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp")) - dir = "/tmp"; - else - { - __set_errno (ENOENT); - return -1; - } - } + Of course we are in a state of sin here. */ - dlen = strlen (dir); - while (dlen > 1 && dir[dlen - 1] == '/') - dlen--; /* remove trailing slashes */ + random_value v = s; - /* check we have room for "${dir}/${pfx}XXXXXX\0" */ - if (tmpl_len < dlen + 1 + plen + 6 + 1) - { - __set_errno (EINVAL); - return -1; - } +#if _LIBC || (defined CLOCK_REALTIME && HAVE_CLOCK_GETTIME) + struct __timespec64 tv; + __clock_gettime64 (CLOCK_REALTIME, &tv); + v = mix_random_values (v, tv.tv_sec); + v = mix_random_values (v, tv.tv_nsec); +#endif - sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx); - return 0; + *r = mix_random_values (v, clock ()); + return false; } -#endif /* _LIBC */ #if _LIBC static int try_tempname_len (char *, int, void *, int (*) (char *, void *), @@ -197,7 +143,7 @@ try_nocreate (char *tmpl, _GL_UNUSED void *flags) { struct_stat64 st; - if (__lstat64 (tmpl, &st) == 0 || errno == EOVERFLOW) + if (__lstat64_time64 (tmpl, &st) == 0 || errno == EOVERFLOW) __set_errno (EEXIST); return errno == ENOENT ? 0 : -1; } @@ -267,32 +213,17 @@ try_tempname_len (char *tmpl, int suffixlen, void *args, unsigned int attempts = ATTEMPTS_MIN; #endif - /* A random variable. The initial value is used only the for fallback path - on 'random_bits' on 'getrandom' failure. Its initial value tries to use - some entropy from the ASLR and ignore possible bits from the stack - alignment. */ - random_value v = ((uintptr_t) &v) / alignof (max_align_t); - -#if !HAS_CLOCK_ENTROPY - /* Arrange gen_tempname to return less predictable file names on - systems lacking clock entropy <https://bugs.gnu.org/57129>. */ - static random_value prev_v; - v ^= prev_v; -#endif + /* A random variable. */ + random_value v = 0; - /* How many random base-62 digits can currently be extracted from V. */ + /* A value derived from the random variable, and how many random + base-62 digits can currently be extracted from VDIGBUF. */ + random_value vdigbuf; int vdigits = 0; - /* Whether to consume entropy when acquiring random bits. On the - first try it's worth the entropy cost with __GT_NOCREATE, which - is inherently insecure and can use the entropy to make it a bit - more secure. On the (rare) second and later attempts it might - help against DoS attacks. */ - bool use_getrandom = tryfunc == try_nocreate; - - /* Least unfair value for V. If V is less than this, V can generate - BASE_62_DIGITS digits fairly. Otherwise it might be biased. */ - random_value const unfair_min + /* Least biased value for V. If V is less than this, V can generate + BASE_62_DIGITS unbiased digits. Otherwise the digits are biased. */ + random_value const biased_min = RANDOM_VALUE_MAX - RANDOM_VALUE_MAX % BASE_62_POWER; len = strlen (tmpl); @@ -312,18 +243,16 @@ try_tempname_len (char *tmpl, int suffixlen, void *args, { if (vdigits == 0) { - do - { - v = random_bits (v, use_getrandom); - use_getrandom = true; - } - while (unfair_min <= v); + /* Worry about bias only if the bits are high quality. */ + while (random_bits (&v, v) && biased_min <= v) + continue; + vdigbuf = v; vdigits = BASE_62_DIGITS; } - XXXXXX[i] = letters[v % 62]; - v /= 62; + XXXXXX[i] = letters[vdigbuf % 62]; + vdigbuf /= 62; vdigits--; } @@ -331,9 +260,6 @@ try_tempname_len (char *tmpl, int suffixlen, void *args, if (fd >= 0) { __set_errno (save_errno); -#if !HAS_CLOCK_ENTROPY - prev_v = v; -#endif return fd; } else if (errno != EEXIST) |