diff options
Diffstat (limited to 'freebsd/crypto/openssl/crypto/rand/rand_unix.c')
-rw-r--r-- | freebsd/crypto/openssl/crypto/rand/rand_unix.c | 185 |
1 files changed, 136 insertions, 49 deletions
diff --git a/freebsd/crypto/openssl/crypto/rand/rand_unix.c b/freebsd/crypto/openssl/crypto/rand/rand_unix.c index a5d658e0..d92cea7a 100644 --- a/freebsd/crypto/openssl/crypto/rand/rand_unix.c +++ b/freebsd/crypto/openssl/crypto/rand/rand_unix.c @@ -16,14 +16,19 @@ #include <stdio.h> #include "internal/cryptlib.h" #include <openssl/rand.h> +#include <openssl/crypto.h> #include "rand_lcl.h" #include "internal/rand_int.h" #include <stdio.h> #include "internal/dso.h" -#if defined(__linux) -# include <asm/unistd.h> +#ifdef __linux +# include <sys/syscall.h> +# ifdef DEVRANDOM_WAIT +# include <sys/shm.h> +# include <sys/utsname.h> +# endif #endif -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) && !defined(OPENSSL_SYS_UEFI) # include <sys/types.h> # include <sys/sysctl.h> # include <sys/param.h> @@ -277,6 +282,17 @@ static ssize_t sysctl_random(char *buf, size_t buflen) # endif # if defined(OPENSSL_RAND_SEED_GETRANDOM) + +# if defined(__linux) && !defined(__NR_getrandom) +# if defined(__arm__) && defined(__NR_SYSCALL_BASE) +# define __NR_getrandom (__NR_SYSCALL_BASE+384) +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__x86_64__) && !defined(__ILP32__) +# define __NR_getrandom 318 +# 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. @@ -348,6 +364,91 @@ static struct random_device { } random_devices[OSSL_NELEM(random_device_paths)]; static int keep_random_devices_open = 1; +# if defined(__linux) && defined(DEVRANDOM_WAIT) +static void *shm_addr; + +static void cleanup_shm(void) +{ + shmdt(shm_addr); +} + +/* + * 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); + if (shm_addr != (void *)-1) + OPENSSL_atexit(&cleanup_shm); + } + } + return seeded; +} +# else /* defined __linux */ +static int wait_random_seeded(void) +{ + return 1; +} +# endif + /* * 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 @@ -474,12 +575,12 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool) # 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; + size_t entropy_available; # 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; @@ -509,36 +610,16 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool) # endif # if defined(OPENSSL_RAND_SEED_DEVRANDOM) - bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); - { + if (wait_random_seeded()) { + size_t bytes_needed; + unsigned char *buffer; size_t i; -#ifdef DEVRANDOM_WAIT - static int wait_done = 0; - /* - * On some implementations reading from /dev/urandom is possible - * before it is initialized. Therefore we wait for /dev/random - * to be readable to make sure /dev/urandom is initialized. - */ - if (!wait_done && bytes_needed > 0) { - int f = open(DEVRANDOM_WAIT, O_RDONLY); - - if (f >= 0) { - fd_set fds; - - FD_ZERO(&fds); - FD_SET(f, &fds); - while (select(f+1, &fds, NULL, NULL, NULL) < 0 - && errno == EINTR); - close(f); - } - wait_done = 1; - } -#endif - - for (i = 0; bytes_needed > 0 && i < OSSL_NELEM(random_device_paths); 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 allowed number of consecutive unsuccessful attempts */ + /* Maximum number of consecutive unsuccessful attempts */ int attempts = 3; const int fd = get_random_device(i); @@ -552,7 +633,7 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool) if (bytes > 0) { rand_pool_add_end(pool, bytes, 8 * bytes); bytes_needed -= bytes; - attempts = 3; /* reset counter after successful attempt */ + attempts = 3; /* reset counter on successful attempt */ } else if (bytes < 0 && errno != EINTR) { break; } @@ -560,7 +641,7 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool) 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) @@ -581,26 +662,29 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool) # endif # if defined(OPENSSL_RAND_SEED_EGD) - bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); - if (bytes_needed > 0) { + { 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 @@ -634,15 +718,18 @@ 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 }; /* * 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 <master> drbg). */ + data.fork_id = openssl_get_fork_id(); data.tid = CRYPTO_THREAD_get_current_id(); data.time = get_timer_bits(); |