When mutt needs a random number (for example for naming the tmp files or generating a boundary for MIME attachments) it currently uses different implementation specific functions: random() and either lrand48() or rand() are used. Which one gets used in specific and the quality of its randomness is dependant on the code calling the function and the OS mutt is running on.
With this change, mutt will instead use OpenSSL or GnuTLS (if configured with this functionality) for its random numbers. When mutt is not configured with either of these two libraries, it will now use a built in PRNG function (the LFSR113 algorithm by Pierre L'Ecuyer) to generate high quality pseudo random numbers. --- Makefile.am | 3 +- init.c | 18 +-------- main.c | 1 - mutt_random.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++ mutt_random.h | 24 ++++++++++++ muttlib.c | 9 +++-- protos.h | 10 ----- 7 files changed, 135 insertions(+), 33 deletions(-) create mode 100644 mutt_random.c create mode 100644 mutt_random.h diff --git a/Makefile.am b/Makefile.am index 6f166d5a..f7a2816c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,7 +39,8 @@ mutt_SOURCES = \ score.c send.c sendlib.c signal.c sort.c \ status.c system.c thread.c charset.c history.c lib.c \ muttlib.c editmsg.c mbyte.c \ - url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c + url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c \ + mutt_random.c nodist_mutt_SOURCES = $(BUILT_SOURCES) diff --git a/init.c b/init.c index 26f8afcf..7b0756fd 100644 --- a/init.c +++ b/init.c @@ -33,6 +33,7 @@ #include "mutt_crypt.h" #include "mutt_idna.h" #include "group.h" +#include "mutt_random.h" #if defined(USE_SSL) #include "mutt_ssl.h" @@ -3424,23 +3425,6 @@ static int mutt_execute_commands (LIST *p) return 0; } -static void mutt_srandom (void) -{ - struct timeval tv; - unsigned seed; - - gettimeofday(&tv, NULL); - /* POSIX.1-2008 states that seed is 'unsigned' without specifying its width. - * Use as many of the lower order bits from the current time of day as the seed. - * If the upper bound is truncated, that is fine. - * - * tv_sec is integral of type integer or float. Cast to 'long long' before - * bitshift in case it is a float. - */ - seed = ((LONGLONG) tv.tv_sec << 20) | tv.tv_usec; - srandom(seed); -} - static char* mutt_find_cfg (const char *home, const char *xdg_cfg_home) { const char* names[] = diff --git a/main.c b/main.c index 293f7172..d5b85525 100644 --- a/main.c +++ b/main.c @@ -665,7 +665,6 @@ int main (int argc, char **argv, char **environ) mutt_error = mutt_nocurses_error; mutt_message = mutt_nocurses_error; - SRAND (time (NULL)); umask (077); memset (Options, 0, sizeof (Options)); diff --git a/mutt_random.c b/mutt_random.c new file mode 100644 index 00000000..3b0ea98d --- /dev/null +++ b/mutt_random.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 Remco Rijnders <re...@webconquest.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <sys/time.h> +#include <sys/types.h> +#include <string.h> +#include <unistd.h> + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mutt.h" +#include "mutt_random.h" + +#ifndef USE_SSL +static u_int32_t z1, z2, z3, z4; /* State variables for LFSR113 PRNG */ +#endif + +/* Return 32 random bits. If compiled with support for OpenSSL or + GnuTLS, the random functions of these libraries will be used, + otherwise mutt will provide its own PRNG which is an implementation + of the LFSR113 algorithm by Pierre L'Ecuyer. */ +u_int32_t mutt_random32 (void) +{ +#ifdef USE_SSL + union random_u { + unsigned char randombytes[4]; + u_int32_t result; + } random; + +#ifdef USE_SSL_OPENSSL + #include <openssl/rand.h> + + if (RAND_bytes(random.randombytes, sizeof(random.randombytes)) != 1) { +#elif USE_SSL_GNUTLS + #include <gnutls/crypto.h> + + if (gnutls_rnd(GNUTLS_RND_NONCE, random.randombytes, sizeof(random.randombytes)) < 0) { +#endif /* USE_SSL_OPENSSL / USE_SSL_GNUTLS */ + mutt_error _("Failed to get random bytes!"); + sleep (1); + mutt_exit (1); + } + + return random.result; +#else + u_int32_t b; + b = ((z1 << 6) ^ z1) >> 13; + z1 = ((z1 & 4294967294U) << 18) ^ b; + b = ((z2 << 2) ^ z2) >> 27; + z2 = ((z2 & 4294967288U) << 2) ^ b; + b = ((z3 << 13) ^ z3) >> 21; + z3 = ((z3 & 4294967280U) << 7) ^ b; + b = ((z4 << 3) ^ z4) >> 12; + z4 = ((z4 & 4294967168U) << 13) ^ b; + + return (z1 ^ z2 ^ z3 ^ z4); +#endif /* else USE_SSL */ +} + +/* Initialize the four seeds for the PRNG algorithm, only needed when +running without OpenSSL or GnuTLS support. */ +void mutt_srandom(void) +{ +#ifndef USE_SSL + struct timeval tv; + gettimeofday(&tv, NULL); + /* Use as many of the lower order bits from the current time of day + as the seed for z1. If the upper bound is truncated, that is + fine. Append the number of microseconds to the end. */ + z1 = ((u_int32_t) tv.tv_sec << 20) | tv.tv_usec; + z2 = z1 ^ getpid(); + z3 = z1 ^ z2 ^ getppid(); + gettimeofday(&tv, NULL); + z4 = z1 << 20 | tv.tv_usec; +#endif /* #ifndef USE_SSL */ +} + +/* Generate and Base64 encode 96 random bits and fill the buffer + output_B64 with the result. */ +void mutt_base64_random96(char output_B64[static 17]) +{ + u_int32_t randombits[3] = { mutt_random32(), mutt_random32(), mutt_random32() }; + + mutt_to_base64((unsigned char *) output_B64, (unsigned char *) randombits, + 12, 17); +} diff --git a/mutt_random.h b/mutt_random.h new file mode 100644 index 00000000..30724e72 --- /dev/null +++ b/mutt_random.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2020 Remco Rijnders <re...@webconquest.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef MUTT_RANDOM_H +#define MUTT_RANDOM_H +void mutt_srandom(void); +u_int32_t mutt_random32(void); +void mutt_base64_random96(char output_B64[static 17]); +#endif diff --git a/muttlib.c b/muttlib.c index 4670a931..9b500f1d 100644 --- a/muttlib.c +++ b/muttlib.c @@ -34,6 +34,7 @@ #endif #include "mutt_crypt.h" +#include "mutt_random.h" #include <string.h> #include <ctype.h> @@ -917,9 +918,9 @@ void mutt_merge_envelopes(ENVELOPE* base, ENVELOPE** extra) void _mutt_buffer_mktemp (BUFFER *buf, const char *prefix, const char *suffix, const char *src, int line) { - mutt_buffer_printf (buf, "%s/%s-%s-%d-%d-%ld%ld%s%s", + mutt_buffer_printf (buf, "%s/%s-%s-%d-%d-%u%u%s%s", NONULL (Tempdir), NONULL (prefix), NONULL (Hostname), - (int) getuid (), (int) getpid (), random (), random (), + (int) getuid (), (int) getpid (), mutt_random32(), mutt_random32(), suffix ? "." : "", NONULL (suffix)); dprint (3, (debugfile, "%s:%d: mutt_mktemp returns \"%s\".\n", src, line, mutt_b2s (buf))); if (unlink (mutt_b2s (buf)) && errno != ENOENT) @@ -930,9 +931,9 @@ void _mutt_buffer_mktemp (BUFFER *buf, const char *prefix, const char *suffix, void _mutt_mktemp (char *s, size_t slen, const char *prefix, const char *suffix, const char *src, int line) { - size_t n = snprintf (s, slen, "%s/%s-%s-%d-%d-%ld%ld%s%s", + size_t n = snprintf (s, slen, "%s/%s-%s-%d-%d-%u%u%s%s", NONULL (Tempdir), NONULL (prefix), NONULL (Hostname), - (int) getuid (), (int) getpid (), random (), random (), + (int) getuid (), (int) getpid (), mutt_random32(), mutt_random32(), suffix ? "." : "", NONULL (suffix)); if (n >= slen) dprint (1, (debugfile, "%s:%d: ERROR: insufficient buffer space to hold temporary filename! slen=%zu but need %zu\n", diff --git a/protos.h b/protos.h index 5f57fb89..90021655 100644 --- a/protos.h +++ b/protos.h @@ -461,16 +461,6 @@ void mutt_pattern_free (pattern_t **pat); #define LONGLONG long #endif -#ifdef HAVE_SRAND48 -#define LRAND lrand48 -#define SRAND srand48 -#define DRAND drand48 -#else -#define LRAND rand -#define SRAND srand -#define DRAND (double)rand -#endif /* HAVE_SRAND48 */ - /* HP-UX, ConvexOS and UNIXware don't have this macro */ #ifndef S_ISLNK #define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK ? 1 : 0) -- 2.26.2