| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| |
| #include <elf.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/random.h> |
| #include <pthread.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/time.h> |
| |
| #if HAVE_SYS_AUXV_H |
| # include <sys/auxv.h> |
| #endif |
| |
| #include "alloc-util.h" |
| #include "env-util.h" |
| #include "errno-util.h" |
| #include "fd-util.h" |
| #include "fileio.h" |
| #include "io-util.h" |
| #include "missing_random.h" |
| #include "missing_syscall.h" |
| #include "missing_threads.h" |
| #include "parse-util.h" |
| #include "random-util.h" |
| #include "sha256.h" |
| #include "time-util.h" |
| |
| /* This is a "best effort" kind of thing, but has no real security value. So, this should only be used by |
| * random_bytes(), which is not meant for crypto. This could be made better, but we're *not* trying to roll a |
| * userspace prng here, or even have forward secrecy, but rather just do the shortest thing that is at least |
| * better than libc rand(). */ |
| static void fallback_random_bytes(void *p, size_t n) { |
| static thread_local uint64_t fallback_counter = 0; |
| struct { |
| char label[32]; |
| uint64_t call_id, block_id; |
| usec_t stamp_mono, stamp_real; |
| pid_t pid, tid; |
| uint8_t auxval[16]; |
| } state = { |
| /* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */ |
| .label = "systemd fallback random bytes v1", |
| .call_id = fallback_counter++, |
| .stamp_mono = now(CLOCK_MONOTONIC), |
| .stamp_real = now(CLOCK_REALTIME), |
| .pid = getpid(), |
| .tid = gettid(), |
| }; |
| |
| #if HAVE_SYS_AUXV_H |
| memcpy(state.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(state.auxval)); |
| #endif |
| |
| while (n > 0) { |
| struct sha256_ctx ctx; |
| |
| sha256_init_ctx(&ctx); |
| sha256_process_bytes(&state, sizeof(state), &ctx); |
| if (n < SHA256_DIGEST_SIZE) { |
| uint8_t partial[SHA256_DIGEST_SIZE]; |
| sha256_finish_ctx(&ctx, partial); |
| memcpy(p, partial, n); |
| break; |
| } |
| sha256_finish_ctx(&ctx, p); |
| p = (uint8_t *) p + SHA256_DIGEST_SIZE; |
| n -= SHA256_DIGEST_SIZE; |
| ++state.block_id; |
| } |
| } |
| |
| void random_bytes(void *p, size_t n) { |
| static bool have_getrandom = true, have_grndinsecure = true; |
| _cleanup_close_ int fd = -EBADF; |
| |
| if (n == 0) |
| return; |
| |
| for (;;) { |
| ssize_t l; |
| |
| if (!have_getrandom) |
| break; |
| |
| l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK); |
| if (l > 0) { |
| if ((size_t) l == n) |
| return; /* Done reading, success. */ |
| p = (uint8_t *) p + l; |
| n -= l; |
| continue; /* Interrupted by a signal; keep going. */ |
| } else if (l == 0) |
| break; /* Weird, so fallback to /dev/urandom. */ |
| else if (ERRNO_IS_NOT_SUPPORTED(errno)) { |
| have_getrandom = false; |
| break; /* No syscall, so fallback to /dev/urandom. */ |
| } else if (errno == EINVAL && have_grndinsecure) { |
| have_grndinsecure = false; |
| continue; /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */ |
| } else if (errno == EAGAIN && !have_grndinsecure) |
| break; /* Will block, but no GRND_INSECURE, so fallback to /dev/urandom. */ |
| |
| break; /* Unexpected, so just give up and fallback to /dev/urandom. */ |
| } |
| |
| fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); |
| if (fd >= 0 && loop_read_exact(fd, p, n, false) == 0) |
| return; |
| |
| /* This is a terrible fallback. Oh well. */ |
| fallback_random_bytes(p, n); |
| } |
| |
| int crypto_random_bytes(void *p, size_t n) { |
| static bool have_getrandom = true, seen_initialized = false; |
| _cleanup_close_ int fd = -EBADF; |
| |
| if (n == 0) |
| return 0; |
| |
| for (;;) { |
| ssize_t l; |
| |
| if (!have_getrandom) |
| break; |
| |
| l = getrandom(p, n, 0); |
| if (l > 0) { |
| if ((size_t) l == n) |
| return 0; /* Done reading, success. */ |
| p = (uint8_t *) p + l; |
| n -= l; |
| continue; /* Interrupted by a signal; keep going. */ |
| } else if (l == 0) |
| return -EIO; /* Weird, should never happen. */ |
| else if (ERRNO_IS_NOT_SUPPORTED(errno)) { |
| have_getrandom = false; |
| break; /* No syscall, so fallback to /dev/urandom. */ |
| } |
| return -errno; |
| } |
| |
| if (!seen_initialized) { |
| _cleanup_close_ int ready_fd = -EBADF; |
| int r; |
| |
| ready_fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY); |
| if (ready_fd < 0) |
| return -errno; |
| r = fd_wait_for_event(ready_fd, POLLIN, USEC_INFINITY); |
| if (r < 0) |
| return r; |
| seen_initialized = true; |
| } |
| |
| fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); |
| if (fd < 0) |
| return -errno; |
| return loop_read_exact(fd, p, n, false); |
| } |
| |
| size_t random_pool_size(void) { |
| _cleanup_free_ char *s = NULL; |
| int r; |
| |
| /* Read pool size, if possible */ |
| r = read_one_line_file("/proc/sys/kernel/random/poolsize", &s); |
| if (r < 0) |
| log_debug_errno(r, "Failed to read pool size from kernel: %m"); |
| else { |
| unsigned sz; |
| |
| r = safe_atou(s, &sz); |
| if (r < 0) |
| log_debug_errno(r, "Failed to parse pool size: %s", s); |
| else |
| /* poolsize is in bits on 2.6, but we want bytes */ |
| return CLAMP(sz / 8, RANDOM_POOL_SIZE_MIN, RANDOM_POOL_SIZE_MAX); |
| } |
| |
| /* Use the minimum as default, if we can't retrieve the correct value */ |
| return RANDOM_POOL_SIZE_MIN; |
| } |
| |
| int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { |
| _cleanup_close_ int opened_fd = -EBADF; |
| int r; |
| |
| assert(seed || size == 0); |
| |
| if (size == 0) |
| return 0; |
| |
| if (fd < 0) { |
| opened_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY); |
| if (opened_fd < 0) |
| return -errno; |
| |
| fd = opened_fd; |
| } |
| |
| if (credit) { |
| _cleanup_free_ struct rand_pool_info *info = NULL; |
| |
| /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any |
| * chance for confusion here. */ |
| if (size > INT_MAX / 8) |
| return -EOVERFLOW; |
| |
| info = malloc(offsetof(struct rand_pool_info, buf) + size); |
| if (!info) |
| return -ENOMEM; |
| |
| info->entropy_count = size * 8; |
| info->buf_size = size; |
| memcpy(info->buf, seed, size); |
| |
| if (ioctl(fd, RNDADDENTROPY, info) < 0) |
| return -errno; |
| } else { |
| r = loop_write(fd, seed, size, false); |
| if (r < 0) |
| return r; |
| } |
| |
| return 1; |
| } |
| |
| uint64_t random_u64_range(uint64_t m) { |
| uint64_t x, remainder; |
| |
| /* Generates a random number in the range 0…m-1, unbiased. (Java's algorithm) */ |
| |
| if (m == 0) /* Let's take m == 0 as special case to return an integer from the full range */ |
| return random_u64(); |
| if (m == 1) |
| return 0; |
| |
| remainder = UINT64_MAX % m; |
| |
| do { |
| x = random_u64(); |
| } while (x >= UINT64_MAX - remainder); |
| |
| return x % m; |
| } |