diff options
| author | Conrad Meyer <cem@FreeBSD.org> | 2019-04-15 18:40:36 +0000 |
|---|---|---|
| committer | Conrad Meyer <cem@FreeBSD.org> | 2019-04-15 18:40:36 +0000 |
| commit | 13774e82285e8d5eb3afeff63760725f747f8581 (patch) | |
| tree | 4e63f007ef63568f5bed09510f908fbcaa77f22c /sys/dev/random | |
| parent | 16fba2f38a8258ea656bba74d52e55436464e66b (diff) | |
Notes
Diffstat (limited to 'sys/dev/random')
| -rw-r--r-- | sys/dev/random/fortuna.c | 16 | ||||
| -rw-r--r-- | sys/dev/random/random_harvestq.c | 5 | ||||
| -rw-r--r-- | sys/dev/random/random_infra.c | 20 | ||||
| -rw-r--r-- | sys/dev/random/randomdev.c | 113 | ||||
| -rw-r--r-- | sys/dev/random/randomdev.h | 2 |
5 files changed, 98 insertions, 58 deletions
diff --git a/sys/dev/random/fortuna.c b/sys/dev/random/fortuna.c index e8a407525138..a6cfccaa3a1a 100644 --- a/sys/dev/random/fortuna.c +++ b/sys/dev/random/fortuna.c @@ -451,16 +451,22 @@ random_fortuna_read(uint8_t *buf, u_int bytecount) RANDOM_RESEED_UNLOCK(); } +#ifdef _KERNEL +static bool block_seeded_status = false; +SYSCTL_BOOL(_kern_random, OID_AUTO, block_seeded_status, CTLFLAG_RWTUN, + &block_seeded_status, 0, + "If non-zero, pretend Fortuna is in an unseeded state. By setting " + "this as a tunable, boot can be tested as if the random device is " + "unavailable."); +#endif + bool random_fortuna_seeded(void) { #ifdef _KERNEL - /* When set, act as if we are not seeded. */ - KFAIL_POINT_CODE(DEBUG_FP, random_fortuna_seeded, { - if (RETURN_VALUE != 0) - fortuna_state.fs_counter = UINT128_ZERO; - }); + if (block_seeded_status) + return (false); #endif return (!uint128_is_zero(fortuna_state.fs_counter)); diff --git a/sys/dev/random/random_harvestq.c b/sys/dev/random/random_harvestq.c index 3f5ecc7ddea3..d31a6a89a8b7 100644 --- a/sys/dev/random/random_harvestq.c +++ b/sys/dev/random/random_harvestq.c @@ -421,11 +421,6 @@ random_harvestq_prime(void *unused __unused) if (keyfile != NULL) { data = preload_fetch_addr(keyfile); size = preload_fetch_size(keyfile); - /* skip the first bit of the stash so others like arc4 can also have some. */ - if (size > RANDOM_CACHED_SKIP_START) { - data += RANDOM_CACHED_SKIP_START; - size -= RANDOM_CACHED_SKIP_START; - } /* Trim the size. If the admin has a file with a funny size, we lose some. Tough. */ size -= (size % sizeof(event.he_entropy)); if (data != NULL && size != 0) { diff --git a/sys/dev/random/random_infra.c b/sys/dev/random/random_infra.c index d31b84b24582..324c40dcecd4 100644 --- a/sys/dev/random/random_infra.c +++ b/sys/dev/random/random_infra.c @@ -57,12 +57,18 @@ struct random_algorithm *p_random_alg_context = &random_alg_context; #if defined(RANDOM_LOADABLE) +static void +null_read_random(void *dummy __unused, u_int dummy2 __unused) +{ + panic("%s: no random module is loaded", __func__); +} + struct random_readers { int (*read_random_uio)(struct uio *, bool); - u_int (*read_random)(void *, u_int); + void (*read_random)(void *, u_int); } random_reader_context = { (int (*)(struct uio *, bool))nullop, - (u_int (*)(void *, u_int))nullop, + null_read_random, }; struct sx randomdev_config_lock; @@ -76,7 +82,7 @@ random_infra_sysinit(void *dummy __unused) SYSINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_FIRST, random_infra_sysinit, NULL); void -random_infra_init(int (*p_random_read_uio)(struct uio *, bool), u_int (*p_random_read)(void *, u_int)) +random_infra_init(int (*p_random_read_uio)(struct uio *, bool), void (*p_random_read)(void *, u_int)) { RANDOM_CONFIG_X_LOCK(); @@ -91,7 +97,7 @@ random_infra_uninit(void) RANDOM_CONFIG_X_LOCK(); random_reader_context.read_random_uio = (int (*)(struct uio *, bool))nullop; - random_reader_context.read_random = (u_int (*)(void *, u_int))nullop; + random_reader_context.read_random = null_read_random; RANDOM_CONFIG_X_UNLOCK(); } @@ -114,15 +120,13 @@ read_random_uio(struct uio *uio, bool nonblock) return (retval); } -u_int +void read_random(void *buf, u_int len) { - u_int retval; RANDOM_CONFIG_S_LOCK(); - retval = random_reader_context.read_random(buf, len); + random_reader_context.read_random(buf, len); RANDOM_CONFIG_S_UNLOCK(); - return (retval); } #endif /* defined(RANDOM_LOADABLE) */ diff --git a/sys/dev/random/randomdev.c b/sys/dev/random/randomdev.c index 94bd2f1a4147..19a73b28151a 100644 --- a/sys/dev/random/randomdev.c +++ b/sys/dev/random/randomdev.c @@ -63,7 +63,7 @@ __FBSDID("$FreeBSD$"); #define READ_RANDOM_UIO _read_random_uio #define READ_RANDOM _read_random static int READ_RANDOM_UIO(struct uio *, bool); -static u_int READ_RANDOM(void *, u_int); +static void READ_RANDOM(void *, u_int); #else #define READ_RANDOM_UIO read_random_uio #define READ_RANDOM read_random @@ -124,11 +124,53 @@ randomdev_read(struct cdev *dev __unused, struct uio *uio, int flags) return (READ_RANDOM_UIO(uio, (flags & O_NONBLOCK) != 0)); } +/* + * If the random device is not seeded, blocks until it is seeded. + * + * Returns zero when the random device is seeded. + * + * If the 'interruptible' parameter is true, and the device is unseeded, this + * routine may be interrupted. If interrupted, it will return either ERESTART + * or EINTR. + */ +#define SEEDWAIT_INTERRUPTIBLE true +#define SEEDWAIT_UNINTERRUPTIBLE false +static int +randomdev_wait_until_seeded(bool interruptible) +{ + int error, spamcount, slpflags; + + slpflags = interruptible ? PCATCH : 0; + + error = 0; + spamcount = 0; + while (!p_random_alg_context->ra_seeded()) { + /* keep tapping away at the pre-read until we seed/unblock. */ + p_random_alg_context->ra_pre_read(); + /* Only bother the console every 10 seconds or so */ + if (spamcount == 0) + printf("random: %s unblock wait\n", __func__); + spamcount = (spamcount + 1) % 100; + error = tsleep(&random_alg_context, slpflags, "randseed", + hz / 10); + if (error == ERESTART || error == EINTR) { + KASSERT(interruptible, + ("unexpected wake of non-interruptible sleep")); + break; + } + /* Squash tsleep timeout condition */ + if (error == EWOULDBLOCK) + error = 0; + KASSERT(error == 0, ("unexpected tsleep error %d", error)); + } + return (error); +} + int READ_RANDOM_UIO(struct uio *uio, bool nonblock) { uint8_t *random_buf; - int error, spamcount; + int error; ssize_t read_len, total_read, c; /* 16 MiB takes about 0.08 s CPU time on my 2017 AMD Zen CPU */ #define SIGCHK_PERIOD (16 * 1024 * 1024) @@ -140,26 +182,13 @@ READ_RANDOM_UIO(struct uio *uio, bool nonblock) random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); p_random_alg_context->ra_pre_read(); error = 0; - spamcount = 0; /* (Un)Blocking logic */ - while (!p_random_alg_context->ra_seeded()) { - if (nonblock) { + if (!p_random_alg_context->ra_seeded()) { + if (nonblock) error = EWOULDBLOCK; - break; - } - /* keep tapping away at the pre-read until we seed/unblock. */ - p_random_alg_context->ra_pre_read(); - /* Only bother the console every 10 seconds or so */ - if (spamcount == 0) - printf("random: %s unblock wait\n", __func__); - spamcount = (spamcount + 1)%100; - error = tsleep(&random_alg_context, PCATCH, "randseed", hz/10); - if (error == ERESTART || error == EINTR) - break; - /* Squash tsleep timeout condition */ - if (error == EWOULDBLOCK) - error = 0; - KASSERT(error == 0, ("unexpected tsleep error %d", error)); + else + error = randomdev_wait_until_seeded( + SEEDWAIT_INTERRUPTIBLE); } if (error == 0) { read_rate_increment((uio->uio_resid + sizeof(uint32_t))/sizeof(uint32_t)); @@ -210,30 +239,36 @@ READ_RANDOM_UIO(struct uio *uio, bool nonblock) * It cannot assumed that random_buf is a multiple of * RANDOM_BLOCKSIZE bytes. */ -u_int +void READ_RANDOM(void *random_buf, u_int len) { - u_int read_len; - uint8_t local_buf[len + RANDOM_BLOCKSIZE]; + u_int read_directly_len; KASSERT(random_buf != NULL, ("No suitable random buffer in %s", __func__)); p_random_alg_context->ra_pre_read(); - /* (Un)Blocking logic; if not seeded, return nothing. */ - if (p_random_alg_context->ra_seeded()) { - read_rate_increment((len + sizeof(uint32_t))/sizeof(uint32_t)); - if (len > 0) { - /* - * Belt-and-braces. - * Round up the read length to a crypto block size multiple, - * which is what the underlying generator is expecting. - */ - read_len = roundup(len, RANDOM_BLOCKSIZE); - p_random_alg_context->ra_read(local_buf, read_len); - memcpy(random_buf, local_buf, len); - } - } else - len = 0; - return (len); + /* (Un)Blocking logic */ + if (!p_random_alg_context->ra_seeded()) + (void)randomdev_wait_until_seeded(SEEDWAIT_UNINTERRUPTIBLE); + read_rate_increment(roundup2(len, sizeof(uint32_t))); + if (len == 0) + return; + /* + * The underlying generator expects multiples of + * RANDOM_BLOCKSIZE. + */ + read_directly_len = rounddown(len, RANDOM_BLOCKSIZE); + if (read_directly_len > 0) + p_random_alg_context->ra_read(random_buf, read_directly_len); + if (read_directly_len < len) { + uint8_t remainder_buf[RANDOM_BLOCKSIZE]; + + p_random_alg_context->ra_read(remainder_buf, + sizeof(remainder_buf)); + memcpy((char *)random_buf + read_directly_len, remainder_buf, + len - read_directly_len); + + explicit_bzero(remainder_buf, sizeof(remainder_buf)); + } } static __inline void diff --git a/sys/dev/random/randomdev.h b/sys/dev/random/randomdev.h index 0f3b359d1f1f..41300f237aaf 100644 --- a/sys/dev/random/randomdev.h +++ b/sys/dev/random/randomdev.h @@ -118,7 +118,7 @@ extern struct sx randomdev_config_lock; #define RANDOM_CONFIG_S_LOCK(x) sx_slock(&randomdev_config_lock) #define RANDOM_CONFIG_S_UNLOCK(x) sx_sunlock(&randomdev_config_lock) #define RANDOM_CONFIG_DEINIT_LOCK(x) sx_destroy(&randomdev_config_lock) -void random_infra_init(int (*)(struct uio *, bool), u_int (*)(void *, u_int)); +void random_infra_init(int (*)(struct uio *, bool), void (*)(void *, u_int)); void random_infra_uninit(void); #endif |
