X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=crypto%2Frand%2Frand_unix.c;h=0925446580b4d2fba27b4ba15359005818851ead;hp=798908120dfe46537c84d7daa4e859d5a4302c71;hb=2a7e6ed86be20bd472696a3eafe5d20ec9579dab;hpb=913cebc8f44d50479704040c77d9ed20eea839bc diff --git a/crypto/rand/rand_unix.c b/crypto/rand/rand_unix.c index 798908120d..0925446580 100644 --- a/crypto/rand/rand_unix.c +++ b/crypto/rand/rand_unix.c @@ -1,25 +1,32 @@ /* * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ -#define _GNU_SOURCE +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif #include "e_os.h" #include #include "internal/cryptlib.h" #include -#include "rand_lcl.h" -#include "internal/rand_int.h" +#include +#include "rand_local.h" +#include "crypto/rand.h" #include #include "internal/dso.h" -#if defined(__linux) +#ifdef __linux # include +# ifdef DEVRANDOM_WAIT +# include +# include +# endif #endif -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) && !defined(OPENSSL_SYS_UEFI) # include # include # include @@ -27,8 +34,12 @@ #if defined(__OpenBSD__) || defined(__NetBSD__) # include #endif -#ifdef OPENSSL_SYS_UNIX + +#if (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS)) \ + || defined(__DJGPP__) # include +# include +# include # include # include @@ -70,13 +81,22 @@ static uint64_t get_timer_bits(void); # define OSSL_POSIX_TIMER_OKAY # endif # endif +#endif /* (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS)) + || defined(__DJGPP__) */ + +#if defined(OPENSSL_RAND_SEED_NONE) +/* none means none. this simplifies the following logic */ +# undef OPENSSL_RAND_SEED_OS +# undef OPENSSL_RAND_SEED_GETRANDOM +# undef OPENSSL_RAND_SEED_LIBRANDOM +# undef OPENSSL_RAND_SEED_DEVRANDOM +# undef OPENSSL_RAND_SEED_RDTSC +# undef OPENSSL_RAND_SEED_RDCPU +# undef OPENSSL_RAND_SEED_EGD #endif -int syscall_random(void *buf, size_t buflen); - -#if (defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI)) && \ - !defined(OPENSSL_RAND_SEED_NONE) -# error "UEFI and VXWorks only support seeding NONE" +#if defined(OPENSSL_SYS_UEFI) && !defined(OPENSSL_RAND_SEED_NONE) +# error "UEFI only supports seeding NONE" #endif #if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) \ @@ -153,6 +173,14 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool) return rand_pool_entropy_available(pool); } +void rand_pool_cleanup(void) +{ +} + +void rand_pool_keep_random_devices_open(int keep) +{ +} + # else # if defined(OPENSSL_RAND_SEED_EGD) && \ @@ -179,22 +207,29 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool) # if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND) /* * sysctl_random(): Use sysctl() to read a random number from the kernel - * Returns the size on success, 0 on failure. + * Returns the number of bytes returned in buf on success, -1 on failure. */ -static size_t sysctl_random(char *buf, size_t buflen) +static ssize_t sysctl_random(char *buf, size_t buflen) { int mib[2]; size_t done = 0; size_t len; + /* + * Note: sign conversion between size_t and ssize_t is safe even + * without a range check, see comment in syscall_random() + */ + /* * On FreeBSD old implementations returned longs, newer versions support * variable sizes up to 256 byte. The code below would not work properly * when the sysctl returns long and we want to request something not a * multiple of longs, which should never be the case. */ - if (!ossl_assert(buflen % sizeof(long) == 0)) - return 0; + if (!ossl_assert(buflen % sizeof(long) == 0)) { + errno = EINVAL; + return -1; + } /* * On NetBSD before 4.0 KERN_ARND was an alias for KERN_URND, and only @@ -204,7 +239,8 @@ static size_t sysctl_random(char *buf, size_t buflen) * Just return an error on older NetBSD versions. */ #if defined(__NetBSD__) && __NetBSD_Version__ < 400000000 - return 0; + errno = ENOSYS; + return -1; #endif mib[0] = CTL_KERN; @@ -213,7 +249,7 @@ static size_t sysctl_random(char *buf, size_t buflen) do { len = buflen; if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) - return done; + return done > 0 ? done : -1; done += len; buf += len; buflen -= len; @@ -223,12 +259,80 @@ static size_t sysctl_random(char *buf, size_t buflen) } # endif +# if defined(OPENSSL_RAND_SEED_GETRANDOM) + +# if defined(__linux) && !defined(__NR_getrandom) +# if defined(__arm__) +# define __NR_getrandom (__NR_SYSCALL_BASE+384) +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__x86_64__) +# if defined(__ILP32__) +# define __NR_getrandom (__X32_SYSCALL_BIT + 318) +# else +# define __NR_getrandom 318 +# endif +# elif defined(__xtensa__) +# define __NR_getrandom 338 +# elif defined(__s390__) || defined(__s390x__) +# define __NR_getrandom 349 +# elif defined(__bfin__) +# define __NR_getrandom 389 +# elif defined(__powerpc__) +# define __NR_getrandom 359 +# elif defined(__mips__) || defined(__mips64) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_getrandom (__NR_Linux + 353) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_getrandom (__NR_Linux + 313) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_getrandom (__NR_Linux + 317) +# endif +# elif defined(__hppa__) +# define __NR_getrandom (__NR_Linux + 339) +# elif defined(__sparc__) +# define __NR_getrandom 347 +# elif defined(__ia64__) +# define __NR_getrandom 1339 +# elif defined(__alpha__) +# define __NR_getrandom 511 +# elif defined(__sh__) +# if defined(__SH5__) +# define __NR_getrandom 373 +# else +# define __NR_getrandom 384 +# endif +# elif defined(__avr32__) +# define __NR_getrandom 317 +# elif defined(__microblaze__) +# define __NR_getrandom 385 +# elif defined(__m68k__) +# define __NR_getrandom 352 +# elif defined(__cris__) +# define __NR_getrandom 356 +# elif defined(__aarch64__) +# define __NR_getrandom 278 +# else /* generic */ +# define __NR_getrandom 278 +# endif +# endif + /* * syscall_random(): Try to get random data using a system call - * returns the number of bytes returned in buf, or <= 0 on error. + * returns the number of bytes returned in buf, or < 0 on error. */ -int syscall_random(void *buf, size_t buflen) +static ssize_t syscall_random(void *buf, size_t buflen) { + /* + * Note: 'buflen' equals the size of the buffer which is used by the + * get_entropy() callback of the RAND_DRBG. It is roughly bounded by + * + * 2 * RAND_POOL_FACTOR * (RAND_DRBG_STRENGTH / 8) = 2^14 + * + * which is way below the OSSL_SSIZE_MAX limit. Therefore sign conversion + * between size_t and ssize_t is safe even without a range check. + */ + /* * Do runtime detection to find getentropy(). * @@ -239,12 +343,12 @@ int syscall_random(void *buf, size_t buflen) * - Linux since 3.17 with glibc 2.25 * - FreeBSD since 12.0 (1200061) */ -# if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) - extern int getentropy(void *bufer, size_t length) __attribute__((weak)); +# if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux) + extern int getentropy(void *buffer, size_t length) __attribute__((weak)); if (getentropy != NULL) - return getentropy(buf, buflen) == 0 ? buflen : 0; -# else + return getentropy(buf, buflen) == 0 ? (ssize_t)buflen : -1; +# elif !defined(FIPS_MODE) union { void *p; int (*f)(void *buffer, size_t length); @@ -258,21 +362,226 @@ int syscall_random(void *buf, size_t buflen) p_getentropy.p = DSO_global_lookup("getentropy"); ERR_pop_to_mark(); if (p_getentropy.p != NULL) - return p_getentropy.f(buf, buflen) == 0 ? buflen : 0; + return p_getentropy.f(buf, buflen) == 0 ? (ssize_t)buflen : -1; # endif /* Linux supports this since version 3.17 */ -# if defined(__linux) && defined(SYS_getrandom) - return (int)syscall(SYS_getrandom, buf, buflen, 0); +# if defined(__linux) && defined(__NR_getrandom) + return syscall(__NR_getrandom, buf, buflen, 0); +# elif (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND) + return sysctl_random(buf, buflen); +# else + errno = ENOSYS; + return -1; # endif +} +# endif /* defined(OPENSSL_RAND_SEED_GETRANDOM) */ + +# if defined(OPENSSL_RAND_SEED_DEVRANDOM) +static const char *random_device_paths[] = { DEVRANDOM }; +static struct random_device { + int fd; + dev_t dev; + ino_t ino; + mode_t mode; + dev_t rdev; +} random_devices[OSSL_NELEM(random_device_paths)]; +static int keep_random_devices_open = 1; + +# if defined(__linux) && defined(DEVRANDOM_WAIT) +static void *shm_addr; + +# if !defined(FIPS_MODE) +static void cleanup_shm(void) +{ + shmdt(shm_addr); +} +# endif -# if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND) - return (int)sysctl_random(buf, buflen); -# endif +/* + * Ensure that the system randomness source has been adequately seeded. + * This is done by having the first start of libcrypto, wait until the device + * /dev/random becomes able to supply a byte of entropy. Subsequent starts + * of the library and later reseedings do not need to do this. + */ +static int wait_random_seeded(void) +{ + static int seeded = OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID < 0; + static const int kernel_version[] = { DEVRANDOM_SAFE_KERNEL }; + int kernel[2]; + int shm_id, fd, r; + char c, *p; + struct utsname un; + fd_set fds; + + if (!seeded) { + /* See if anything has created the global seeded indication */ + if ((shm_id = shmget(OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID, 1, 0)) == -1) { + /* + * Check the kernel's version and fail if it is too recent. + * + * Linux kernels from 4.8 onwards do not guarantee that + * /dev/urandom is properly seeded when /dev/random becomes + * readable. However, such kernels support the getentropy(2) + * system call and this should always succeed which renders + * this alternative but essentially identical source moot. + */ + if (uname(&un) == 0) { + kernel[0] = atoi(un.release); + p = strchr(un.release, '.'); + kernel[1] = p == NULL ? 0 : atoi(p + 1); + if (kernel[0] > kernel_version[0] + || (kernel[0] == kernel_version[0] + && kernel[1] >= kernel_version[1])) { + return 0; + } + } + /* Open /dev/random and wait for it to be readable */ + if ((fd = open(DEVRANDOM_WAIT, O_RDONLY)) != -1) { + if (DEVRANDM_WAIT_USE_SELECT && fd < FD_SETSIZE) { + FD_ZERO(&fds); + FD_SET(fd, &fds); + while ((r = select(fd + 1, &fds, NULL, NULL, NULL)) < 0 + && errno == EINTR); + } else { + while ((r = read(fd, &c, 1)) < 0 && errno == EINTR); + } + close(fd); + if (r == 1) { + seeded = 1; + /* Create the shared memory indicator */ + shm_id = shmget(OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID, 1, + IPC_CREAT | S_IRUSR | S_IRGRP | S_IROTH); + } + } + } + if (shm_id != -1) { + seeded = 1; + /* + * Map the shared memory to prevent its premature destruction. + * If this call fails, it isn't a big problem. + */ + shm_addr = shmat(shm_id, NULL, SHM_RDONLY); +# ifndef FIPS_MODE + /* TODO 3.0: The FIPS provider doesn't have OPENSSL_atexit */ + if (shm_addr != (void *)-1) + OPENSSL_atexit(&cleanup_shm); +# endif + } + } + return seeded; +} +# else /* defined __linux */ +static int wait_random_seeded(void) +{ + return 1; +} +# endif - return -1; +/* + * Verify that the file descriptor associated with the random source is + * still valid. The rationale for doing this is the fact that it is not + * uncommon for daemons to close all open file handles when daemonizing. + * So the handle might have been closed or even reused for opening + * another file. + */ +static int check_random_device(struct random_device * rd) +{ + struct stat st; + + return rd->fd != -1 + && fstat(rd->fd, &st) != -1 + && rd->dev == st.st_dev + && rd->ino == st.st_ino + && ((rd->mode ^ st.st_mode) & ~(S_IRWXU | S_IRWXG | S_IRWXO)) == 0 + && rd->rdev == st.st_rdev; } +/* + * Open a random device if required and return its file descriptor or -1 on error + */ +static int get_random_device(size_t n) +{ + struct stat st; + struct random_device * rd = &random_devices[n]; + + /* reuse existing file descriptor if it is (still) valid */ + if (check_random_device(rd)) + return rd->fd; + + /* open the random device ... */ + if ((rd->fd = open(random_device_paths[n], O_RDONLY)) == -1) + return rd->fd; + + /* ... and cache its relevant stat(2) data */ + if (fstat(rd->fd, &st) != -1) { + rd->dev = st.st_dev; + rd->ino = st.st_ino; + rd->mode = st.st_mode; + rd->rdev = st.st_rdev; + } else { + close(rd->fd); + rd->fd = -1; + } + + return rd->fd; +} + +/* + * Close a random device making sure it is a random device + */ +static void close_random_device(size_t n) +{ + struct random_device * rd = &random_devices[n]; + + if (check_random_device(rd)) + close(rd->fd); + rd->fd = -1; +} + +int rand_pool_init(void) +{ + size_t i; + + for (i = 0; i < OSSL_NELEM(random_devices); i++) + random_devices[i].fd = -1; + + return 1; +} + +void rand_pool_cleanup(void) +{ + size_t i; + + for (i = 0; i < OSSL_NELEM(random_devices); i++) + close_random_device(i); +} + +void rand_pool_keep_random_devices_open(int keep) +{ + if (!keep) + rand_pool_cleanup(); + + keep_random_devices_open = keep; +} + +# else /* !defined(OPENSSL_RAND_SEED_DEVRANDOM) */ + +int rand_pool_init(void) +{ + return 1; +} + +void rand_pool_cleanup(void) +{ +} + +void rand_pool_keep_random_devices_open(int keep) +{ +} + +# endif /* defined(OPENSSL_RAND_SEED_DEVRANDOM) */ + /* * Try the various seeding methods in turn, exit when successful. * @@ -292,25 +601,33 @@ int syscall_random(void *buf, size_t buflen) */ size_t rand_pool_acquire_entropy(RAND_POOL *pool) { -# ifdef OPENSSL_RAND_SEED_NONE +# if defined(OPENSSL_RAND_SEED_NONE) return rand_pool_entropy_available(pool); # else - size_t bytes_needed; - size_t entropy_available = 0; - unsigned char *buffer; - -# ifdef OPENSSL_RAND_SEED_GETRANDOM - bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); - buffer = rand_pool_add_begin(pool, bytes_needed); - if (buffer != NULL) { - size_t bytes = 0; - - if (syscall_random(buffer, bytes_needed) == (int)bytes_needed) - bytes = bytes_needed; + size_t entropy_available; - rand_pool_add_end(pool, bytes, 8 * bytes); - entropy_available = rand_pool_entropy_available(pool); +# if defined(OPENSSL_RAND_SEED_GETRANDOM) + { + size_t bytes_needed; + unsigned char *buffer; + ssize_t bytes; + /* Maximum allowed number of consecutive unsuccessful attempts */ + int attempts = 3; + + bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + while (bytes_needed != 0 && attempts-- > 0) { + buffer = rand_pool_add_begin(pool, bytes_needed); + bytes = syscall_random(buffer, bytes_needed); + if (bytes > 0) { + rand_pool_add_end(pool, bytes, 8 * bytes); + bytes_needed -= bytes; + attempts = 3; /* reset counter after successful attempt */ + } else if (bytes < 0 && errno != EINTR) { + break; + } + } } + entropy_available = rand_pool_entropy_available(pool); if (entropy_available > 0) return entropy_available; # endif @@ -321,68 +638,82 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool) } # endif -# ifdef OPENSSL_RAND_SEED_DEVRANDOM - bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); - if (bytes_needed > 0) { - static const char *paths[] = { DEVRANDOM, NULL }; - FILE *fp; - int i; - - for (i = 0; paths[i] != NULL; i++) { - if ((fp = fopen(paths[i], "rb")) == NULL) +# if defined(OPENSSL_RAND_SEED_DEVRANDOM) + if (wait_random_seeded()) { + size_t bytes_needed; + unsigned char *buffer; + size_t i; + + bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + for (i = 0; bytes_needed > 0 && i < OSSL_NELEM(random_device_paths); + i++) { + ssize_t bytes = 0; + /* Maximum number of consecutive unsuccessful attempts */ + int attempts = 3; + const int fd = get_random_device(i); + + if (fd == -1) continue; - setbuf(fp, NULL); - buffer = rand_pool_add_begin(pool, bytes_needed); - if (buffer != NULL) { - size_t bytes = 0; - if (fread(buffer, 1, bytes_needed, fp) == bytes_needed) - bytes = bytes_needed; - rand_pool_add_end(pool, bytes, 8 * bytes); - entropy_available = rand_pool_entropy_available(pool); + while (bytes_needed != 0 && attempts-- > 0) { + buffer = rand_pool_add_begin(pool, bytes_needed); + bytes = read(fd, buffer, bytes_needed); + + if (bytes > 0) { + rand_pool_add_end(pool, bytes, 8 * bytes); + bytes_needed -= bytes; + attempts = 3; /* reset counter on successful attempt */ + } else if (bytes < 0 && errno != EINTR) { + break; + } } - fclose(fp); - if (entropy_available > 0) - return entropy_available; + if (bytes < 0 || !keep_random_devices_open) + close_random_device(i); - bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + bytes_needed = rand_pool_bytes_needed(pool, 1); } + entropy_available = rand_pool_entropy_available(pool); + if (entropy_available > 0) + return entropy_available; } # endif -# ifdef OPENSSL_RAND_SEED_RDTSC +# if defined(OPENSSL_RAND_SEED_RDTSC) entropy_available = rand_acquire_entropy_from_tsc(pool); if (entropy_available > 0) return entropy_available; # endif -# ifdef OPENSSL_RAND_SEED_RDCPU +# if defined(OPENSSL_RAND_SEED_RDCPU) entropy_available = rand_acquire_entropy_from_cpu(pool); if (entropy_available > 0) return entropy_available; # endif -# ifdef OPENSSL_RAND_SEED_EGD - bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); - if (bytes_needed > 0) { +# if defined(OPENSSL_RAND_SEED_EGD) + { static const char *paths[] = { DEVRANDOM_EGD, NULL }; + size_t bytes_needed; + unsigned char *buffer; int i; - for (i = 0; paths[i] != NULL; i++) { + bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + for (i = 0; bytes_needed > 0 && paths[i] != NULL; i++) { + size_t bytes = 0; + int num; + buffer = rand_pool_add_begin(pool, bytes_needed); - if (buffer != NULL) { - size_t bytes = 0; - int num = RAND_query_egd_bytes(paths[i], - buffer, (int)bytes_needed); - if (num == (int)bytes_needed) - bytes = bytes_needed; + num = RAND_query_egd_bytes(paths[i], + buffer, (int)bytes_needed); + if (num == (int)bytes_needed) + bytes = bytes_needed; - rand_pool_add_end(pool, bytes, 8 * bytes); - entropy_available = rand_pool_entropy_available(pool); - } - if (entropy_available > 0) - return entropy_available; + rand_pool_add_end(pool, bytes, 8 * bytes); + bytes_needed = rand_pool_bytes_needed(pool, 1); } + entropy_available = rand_pool_entropy_available(pool); + if (entropy_available > 0) + return entropy_available; } # endif @@ -392,18 +723,22 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool) # endif #endif -#ifdef OPENSSL_SYS_UNIX +#if (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS)) \ + || defined(__DJGPP__) int rand_pool_add_nonce_data(RAND_POOL *pool) { struct { pid_t pid; CRYPTO_THREAD_ID tid; uint64_t time; - } data = { 0 }; + } data; + + /* Erase the entire structure including any padding */ + memset(&data, 0, sizeof(data)); /* * Add process id, thread id, and a high resolution timestamp to - * ensure that the nonce is unique whith high probability for + * ensure that the nonce is unique with high probability for * different process instances. */ data.pid = getpid(); @@ -416,15 +751,21 @@ int rand_pool_add_nonce_data(RAND_POOL *pool) int rand_pool_add_additional_data(RAND_POOL *pool) { struct { + int fork_id; CRYPTO_THREAD_ID tid; uint64_t time; - } data = { 0 }; + } data; + + /* Erase the entire structure including any padding */ + memset(&data, 0, sizeof(data)); /* * Add some noise from the thread id and a high resolution timer. + * The fork_id adds some extra fork-safety. * The thread id adds a little randomness if the drbg is accessed * concurrently (which is the case for the drbg). */ + data.fork_id = openssl_get_fork_id(); data.tid = CRYPTO_THREAD_get_current_id(); data.time = get_timer_bits(); @@ -432,7 +773,6 @@ int rand_pool_add_additional_data(RAND_POOL *pool) } - /* * Get the current time with the highest possible resolution * @@ -512,4 +852,5 @@ static uint64_t get_timer_bits(void) # endif return time(NULL); } -#endif +#endif /* (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS)) + || defined(__DJGPP__) */