commit c019dee2f403ed33379be5cb4756f11fb1de56fe Author: Jacob Welsh AuthorDate: Sat Nov 19 04:33:54 2022 +0000 Commit: Jacob Welsh CommitDate: Sat Nov 19 04:33:54 2022 +0000 Type: fix for new bug Revert "m4, configure, lib: always use getrandom(), dropping alternatives and fallback complexity" This reverts commit f51020888d8d5d2d6f776b91439f43bbe3fcde28 (though config.h needs updating as the build system was meanwhile rewritten). While it would be nice to just use getrandom, it's Linux specific and too new to be counted on. diff --git a/src/lib/randgen.c b/src/lib/randgen.c index 104bb66be8..f6b2da9c99 100644 --- a/src/lib/randgen.c +++ b/src/lib/randgen.c @@ -41,14 +41,26 @@ int rand_get_last_seed(unsigned int *seed_r) } #endif -ssize_t getrandom(void *, size_t, unsigned); - -ssize_t getrandom(void * a, size_t b, unsigned c) { - return 0; -} +/* get randomness from either getrandom, arc4random or /dev/urandom */ + +#if defined(HAVE_GETRANDOM) && HAVE_DECL_GETRANDOM != 0 +# include +# define USE_GETRANDOM +static bool getrandom_present = TRUE; +#elif defined(HAVE_ARC4RANDOM) +# if defined(HAVE_LIBBSD) +# include +# endif +# define USE_ARC4RANDOM +#else +static bool getrandom_present = FALSE; +# define USE_RANDOM_DEV +#endif static int init_refcount = 0; +static int urandom_fd = -1; +#if defined(USE_GETRANDOM) || defined(USE_RANDOM_DEV) /* Use a small buffer when reading randomness. This is mainly to make small random reads more efficient, such as i_rand*(). When reading larger amount of randomness this buffer is bypassed. @@ -61,21 +73,56 @@ static unsigned char random_next[RANDOM_READ_BUFFER_SIZE]; static size_t random_next_pos = 0; static size_t random_next_size = 0; -static ssize_t random_read(unsigned char *buf, size_t size) +static void random_open_urandom(void) +{ + urandom_fd = open(DEV_URANDOM_PATH, O_RDONLY); + if (urandom_fd == -1) { + if (errno == ENOENT) { + i_fatal("open("DEV_URANDOM_PATH") failed: doesn't exist," + "currently we require it"); + } else { + i_fatal("open("DEV_URANDOM_PATH") failed: %m"); + } + } + fd_close_on_exec(urandom_fd, TRUE); +} + +static inline int random_read(unsigned char *buf, size_t size) { ssize_t ret = 0; - ret = getrandom(buf, size, 0); +# if defined(USE_GETRANDOM) + if (getrandom_present) { + ret = getrandom(buf, size, 0); + if (ret < 0 && errno == ENOSYS) { + getrandom_present = FALSE; + /* It gets complicated here... While the libc (and its + headers) indicated that getrandom() was available when + we were compiled, the kernel disagreed just now at + runtime. Fall back to reading /dev/urandom. */ + random_open_urandom(); + } + } + /* this is here to avoid clang complain, + because getrandom_present will be always FALSE + if USE_GETRANDOM is not defined */ + if (!getrandom_present) +# endif + ret = read(urandom_fd, buf, size); if (unlikely(ret <= 0)) { if (ret == 0) { - /* doesn't seem possible but who knows */ - i_fatal("getrandom() failed: EOF"); + i_fatal("read("DEV_URANDOM_PATH") failed: EOF"); } else if (errno != EINTR) { - i_fatal("getrandom() failed: %m"); + if (getrandom_present) { + i_fatal("getrandom() failed: %m"); + } else { + i_fatal("read("DEV_URANDOM_PATH") failed: %m"); + } } } i_assert(ret > 0 || errno == EINTR); return ret; } +#endif void random_fill(void *buf, size_t size) { @@ -90,6 +137,9 @@ void random_fill(void *buf, size_t size) } #endif +#if defined(USE_ARC4RANDOM) + arc4random_buf(buf, size); +#else size_t pos; ssize_t ret; @@ -120,6 +170,7 @@ void random_fill(void *buf, size_t size) } } } +#endif /* defined(USE_ARC4RANDOM) */ } void random_init(void) @@ -135,6 +186,7 @@ void random_init(void) #ifdef DEBUG if (env_seed != NULL && str_to_uint(env_seed, &seed) >= 0) { kiss_init(seed); + /* getrandom_present = FALSE; not needed, only used in random_read() */ goto normal_exit; } #else @@ -142,6 +194,9 @@ void random_init(void) i_warning("DOVECOT_SRAND is not available in non-debug builds"); #endif /* DEBUG */ +#if defined(USE_RANDOM_DEV) + random_open_urandom(); +#endif /* DO NOT REMOVE THIS - It is also needed to make sure getrandom really works. */ @@ -151,6 +206,7 @@ void random_init(void) if (strcmp(env_seed, "kiss") != 0) i_fatal("DOVECOT_SRAND not a number or 'kiss'"); kiss_init(seed); + i_close_fd(&urandom_fd); } normal_exit: @@ -160,5 +216,7 @@ normal_exit: void random_deinit(void) { - --init_refcount; + if (--init_refcount > 0) + return; + i_close_fd(&urandom_fd); }