From 5e1014de53008a1114fce1b7b86ba929f98d3261 Mon Sep 17 00:00:00 2001 From: Taylor R Campbell <riastradh@NetBSD.org> Date: Mon, 22 Jul 2019 02:17:00 +0000 Subject: [PATCH] New system call getrandom(buf, len, flags). Semantics matches Linux. Usage: uint8_t buf[32]; if (getrandom(buf, sizeof buf, GRND_NONBLOCK)) err("unable to generate secrets"); /* buf is now a 256-bit key fit for cryptography. */ getrandom blocks (or fails with EWOULDBLOCK, with GRND_NONBLOCK) only until the system has been seeded; then it never blocks (or fails with EWOULDBLOCK) again. Threat model excludes kernel memory disclosure. Applications should use fault injection in tests to ensure the blocking/EWOULDBLOCK code path is exercised, since most systems will be seeded at boot. No need for device nodes in a chroot like /dev/random in order to use getrandom. --- lib/libc/sys/Makefile.inc | 7 +- lib/libc/sys/getrandom.2 | 207 ++++++++++++++++++++++++++++ sys/kern/files.kern | 1 + sys/kern/kern_rndq.c | 33 +++++ sys/kern/sys_getrandom.c | 281 ++++++++++++++++++++++++++++++++++++++ sys/kern/syscalls.master | 2 + sys/sys/random.h | 45 ++++++ sys/sys/rnd.h | 1 + 8 files changed, 574 insertions(+), 3 deletions(-) create mode 100644 lib/libc/sys/getrandom.2 create mode 100644 sys/kern/sys_getrandom.c create mode 100644 sys/sys/random.h diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 398468f09734..ab68c4baba5e 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -110,7 +110,7 @@ ASM= access.S acct.S \ fstatvfs1.S fstatat.S __futimes50.S futimens.S \ __getcwd.S __getdents30.S __getfh30.S getvfsstat.S getgroups.S\ __getitimer50.S __getlogin.S getpeername.S getpgid.S getpgrp.S \ - getpriority.S getrlimit.S __getrusage50.S getsid.S \ + getpriority.S getrandom.S getrlimit.S __getrusage50.S getsid.S \ getsockname.S getsockopt.S getsockopt2.S __gettimeofday50.S \ ioctl.S \ kqueue.S kqueue1.S ktrace.S \ @@ -250,8 +250,9 @@ MAN+= accept.2 access.2 acct.2 adjtime.2 bind.2 brk.2 chdir.2 \ flock.2 fork.2 fsync.2 getcontext.2 getdents.2 \ getfh.2 getvfsstat.2 getgid.2 getgroups.2 \ getitimer.2 getlogin.2 getpeername.2 getpgrp.2 getpid.2 \ - getpriority.2 getrlimit.2 getrusage.2 getsid.2 getsockname.2 \ - getsockopt.2 gettimeofday.2 getuid.2 intro.2 ioctl.2 issetugid.2 \ + getpriority.2 getrandom.2 getrlimit.2 getrusage.2 getsid.2 \ + getsockname.2 getsockopt.2 gettimeofday.2 getuid.2 \ + intro.2 ioctl.2 issetugid.2 \ kill.2 kqueue.2 ktrace.2 _ksem.2 \ lfs_bmapv.2 lfs_markv.2 lfs_segclean.2 lfs_segwait.2 \ link.2 listen.2 lseek.2 \ diff --git a/lib/libc/sys/getrandom.2 b/lib/libc/sys/getrandom.2 new file mode 100644 index 000000000000..7f052881cfe6 --- /dev/null +++ b/lib/libc/sys/getrandom.2 @@ -0,0 +1,207 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 2019 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Taylor R. Campbell. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd July 21, 2019 +.Dt GETRANDOM 2 +.Os +.Sh NAME +.Nm getrandom +.Nd wait for system entropy to be seeded and randomly fill buffer +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/random.h +.Ft int +.Fn getrandom "void *buf" "size_t len" "int flags" +.Sh DESCRIPTION +The +.Fn getrandom +function blocks until the system has been seeded with entropy, and then +fills the buffer starting at +.Fa buf +with +.Fa len +bytes chosen uniformly at random, which are unpredictable secrets fit +for use in cryptography. +.Pp +The following flags may be +.Em or Ns 'ed +together to form the +.Fa flags +argument: +.Bl -tag -width GRND_NONBLOCK +.It Dv GRND_NONBLOCK +If the buffer cannot be filled immediately, fail with +.Er EWOULDBLOCK +instead of blocking. +The buffer may be partially initialized in this case, but the caller +has no way to ascertain how much of it was initialized. +.Pp +The length +.Fa len +must not exceed 33554431; however, callers should limit their requests +to 32 bytes and use a stream cipher to expand 32-byte keys into longer +pads, like the increasingly inaccurately named +.Xr arc4random 3 +does with ChaCha. +.It Dv GRND_RANDOM +Enable compatibility with legacy and Linux applications by blocking +more often. +.Pp +The length +.Fa len +must not exceed 512; however, callers should limit their requests +to 32 bytes and use a stream cipher to expand 32-byte keys into longer +pads. +.El +.Sh SECURITY MODEL +The +.Fn getrandom +system call assumes the operating system has access to a hardware +entropy source, such as a human flipping coins without surveillance or +a seed file managed by +.Xr rndctl 8 . +After enough coin flips (256 is enough) have been written by root to +.Pa /dev/random +once, or after the seed file has been read once, +.Fn getrandom +will consider the system to be unpredictable and will never block +again. +.Pp +Once the system is seeded, the output of +.Fn getrandom +is indistinguishable from uniform random with more than negligible +advantage by standards of modern cryptography. +.Pp +The +.Fn getrandom +system call +.Em does not +protect against an adversary who can find a kernel memory disclosure +bug in +.Nx +and use it to dump the operating system's seed. +Absent such an adversary, entropy does not +.Dq run out . +.Sh RETURN VALUES +.Rv -std getrandom +.Sh EXAMPLES +Generate a key, but abort immediately if the system is not yet seeded: +.Bd -literal + uint8_t key[32]; + + if (getrandom(key, sizeof key, GRND_NONBLOCK)) + err("getrandom"); + + ... do cryptography using key ... +.Ed +.Pp +Generate a key, or block until the system has been seeded, if the +application can tolerate indefinite blocking: +.Bd -literal + uint8_t key[32]; + + printf("if this hangs get out a coin and start flipping\en"); + printf("and then do `echo ththhh... >> /dev/random'\en"); + fflush(stdout); + if (ferror(stdout)) + err("printf"); + + if (getrandom(key, sizeof key, 0)) + err("getrandom"); + + ... do cryptography using key .... +.Ed +.Pp +Beware that the blocking code path is unlikely to be exercised, and +may need special attention like fault injection to test it. +Consider aborting without blocking instead. +.Sh ERRORS +.Fn getrandom +will fail if: +.Bl -tag -width [EFAULT] +.It Bq Er EFAULT +Part of +.Fa buf +points outside the process's allocated address space. +.It Bq Er EINTR +A signal was delivered before +.Fn getrandom +could complete. +.It Bq Er EINVAL +The +.Fa len +argument was too large or the +.Fa flags +argument was invalid. +.It Bq Er EWOULDBLOCK +The request cannot be served immediately and +.Fa GRND_NONBLOCK +was specified. +.El +.Sh SEE ALSO +.Xr arc4random 3 , +.Xr rnd 4 , +.Xr boot.cfg 5 , +.Xr cprng 9 , +.Xr rnd 9 +.Sh HISTORY +The +.Fn getrandom +function first appeared in Linux 3.17 and +.Nx 10.0 . +.Sh CAVEATS +Applications cannot generate secrets before the system has been +seeded. +If +.Fn getrandom +fails because the system is not yet seeded, applications can either +abort or block until the system has been seeded (or proceed without +secrets, e.g. by disabling networking functionality). +.Pp +Since many systems are seeded automatically by +.Xr sysinst 8 +and +.Xr rndctl 8 , +or have hardware entropy sources built-in, the failure is unlikely to +happen during what are otherwise extensive tests. +Applications should therefore be careful to test +.Fn getrandom +failure with fault injection simulating failure or blocking. +.Sh BUGS +There is no way to do a multiplexed wait for the system to be seeded, +like with +.Xr select 2 , +.Xr kqueue 2 , +etc. +Applications can read a single byte from +.Pa /dev/random , +but like +.Dv GRND_RANDOM +it may block indefinitely even if the system has been seeded. diff --git a/sys/kern/files.kern b/sys/kern/files.kern index 63dca2a29579..4175655c1790 100644 --- a/sys/kern/files.kern +++ b/sys/kern/files.kern @@ -154,6 +154,7 @@ file kern/subr_xcall.c kern file kern/sys_aio.c aio file kern/sys_descrip.c kern file kern/sys_generic.c kern +file kern/sys_getrandom.c kern file kern/sys_module.c kern file kern/sys_mqueue.c mqueue file kern/sys_lwp.c kern diff --git a/sys/kern/kern_rndq.c b/sys/kern/kern_rndq.c index 7135a07b481d..54c718c35d50 100644 --- a/sys/kern/kern_rndq.c +++ b/sys/kern/kern_rndq.c @@ -1723,3 +1723,36 @@ rnd_system_ioctl(struct file *fp, u_long cmd, void *addr) return ret; } + +/* + * rnd_wait_for_initial_entropy() + * + * Wait until the entropy pool has been seeded, or until + * interruption. Return 0 on success, or EINTR/ERESTART on + * interruption. Never fails any other way. + * + * WARNING: This function may almost never block, so care must be + * taken to avoid relying on it not to block in a code path that + * uses it. + */ +int +rnd_wait_for_initial_entropy(void) +{ + int error = 0; + + /* + * If the pool has not yet been seeded, wait for it under the + * lock. (Someone else may have concurrently seeded it and + * already signalled the condvar by the time we acquire the + * lock.) + */ + mutex_spin_enter(&rnd_global.lock); + while (!rnd_initial_entropy) { + error = cv_wait_sig(&rnd_global.cv, &rnd_global.lock); + if (error) + break; + } + mutex_spin_exit(&rnd_global.lock); + + return error; +} diff --git a/sys/kern/sys_getrandom.c b/sys/kern/sys_getrandom.c new file mode 100644 index 000000000000..1c2234713f4e --- /dev/null +++ b/sys/kern/sys_getrandom.c @@ -0,0 +1,281 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Taylor R. Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/cprng.h> +#include <sys/errno.h> +#include <sys/fcntl.h> +#include <sys/proc.h> +#include <sys/random.h> +#include <sys/rnd.h> +#include <sys/syscallargs.h> +#include <sys/systm.h> + +/* + * getrandom_sensible(ubuf, len, block) + * + * If the entropy pool has been seeded, copy len uniform random + * bytes into ubuf, or return EINTR if interrupted. If the + * entropy pool has not yet been seeded, then block, or return + * EWOULDBLOCK if block=false. + * + * Limited to 33554431 bytes, but callers should only ever read at + * most 32 bytes as a key for a stream cipher to expand them into + * a longer pad. + */ +static int +getrandom_sensible(void *ubuf, size_t len, bool block) +{ + char name[16]; + struct cprng_strong *cprng; + uint8_t buf[32]; + uint8_t *p = ubuf; + size_t n = len; + int cflags; + int error; + + CTASSERT(sizeof buf <= CPRNG_MAX_LEN); + + /* + * Reject overly large requests to limit time spent in kernel. + * This number is from Linux; it is unreasonably high -- 32 is + * sufficient for any modern cryptography. + */ + if (len > 33554431) + return EIO; + + /* Check whether we have been seeded. */ + if (!rnd_initial_entropy) { + /* + * If we have not been seeded, either return + * EWOULDBLOCK, or block until we have been. + */ + if (!block) + return EWOULDBLOCK; + error = rnd_wait_for_initial_entropy(); + if (error) + return error; + } + + /* We have now been seeded. */ + KASSERT(rnd_initial_entropy); + + /* + * Flags: + * + * - CPRNG_HARD: Compute AES128_k0(0) || AES128_k1(0) || + * ..., rather than AES128_k(0) || AES128_k(1) || ..., + * where the the 128-bit keys k, k0, k1 are drawn + * independently from the >>128-bit entropy pool. + * (Alternative: Change CPRNG to just use AES-256, or, + * better, ChaCha, instead of AES-128.) + * + * - CPRNG_INIT_ANY: Suppress warning message if pool is + * not yet seeded during cprng_strong_create -- + * shouldn't happen anyway because we just waited for + * the pool to be seeded. + * + * - CPRNG_REKEY_ANY: Generate k0, k1, ..., as needed + * without waiting. Suppress warning message on reseed + * during cprng_strong -- we just waited for the pool to + * be seeded. + */ + cflags = 0; + cflags |= CPRNG_HARD; + cflags |= CPRNG_INIT_ANY; + cflags |= CPRNG_REKEY_ANY; + + /* + * Create a private CPRNG instance named `grndu1234' in pid + * 1234. + * + * XXX cprng_strong_create may block for allocation, silly. + */ + (void)snprintf(name, sizeof name, "grndu%jd", + (intmax_t)curproc->p_pid); + cprng = cprng_strong_create(name, IPL_NONE, cflags); + + /* Generate data. */ + while (n) { + size_t m = cprng_strong(cprng, buf, MIN(n, sizeof buf), 0); + + KASSERT(0 < m); + KASSERT(m <= MIN(n, sizeof buf)); + error = copyout(buf, p, m); + p += m; + n -= m; + + /* Check for interruption if this is a large request. */ + if (__predict_false(len > 256) && + __predict_false(curlwp->l_flag & LW_PENDSIG) && + sigispending(curlwp, 0)) { + error = EINTR; + break; + } + } + + /* Destroy the private CPRNG instance. */ + cprng_strong_destroy(cprng); + + return error; +} + +/* + * getrandom_silly(ubuf, len, block) + * + * Copy len uniform random bytes into ubuf, or return EINTR if + * interrupted. Block, or return EWOULDBLOCK if block=false, if + * the system hasn't been seeded or if there's `not enough + * entropy' because of `entropy depletion'. + * + * This silly concept of `entropy depletion' has no place in + * modern cryptography, with which a secret 32-byte key can be + * expanded into an arbitrarily long bit string that cannot be + * distinguished from uniform random by anyone without the key. + * + * Limited to 512 bytes, but callers should only ever read at most + * 32 bytes as a key for a stream cipher to expand them into a + * longer pad. + */ +static int +getrandom_silly(void *ubuf, size_t len, bool block) +{ + char name[16]; + struct cprng_strong *cprng; + uint8_t buf[32]; + uint8_t *p = ubuf; + size_t n = len; + int cflags; + int error; + + CTASSERT(sizeof buf <= CPRNG_MAX_LEN); + + if (len > 512) + return EIO; + + /* + * Flags: + * + * - CPRNG_HARD: Compute AES128_k0(0) || AES128_k1(0) || + * ..., rather than AES128_k(0) || AES128_k(1) || ..., + * where the the 128-bit keys k, k0, k1 are drawn + * independently from the >>128-bit entropy pool. + * (Alternative: Change CPRNG to just use AES-256, or, + * better, ChaCha, instead of AES-128.) + * + * - CPRNG_INIT_ANY: Suppress warning message if pool is + * not yet seeded during cprng_strong_create -- + * shouldn't happen anyway because we just waited for + * the pool to be seeded. + * + * - CPRNG_USE_CV: Block in cprng_strong when we don't + * pass FNONBLOCK. + */ + cflags = 0; + cflags |= CPRNG_HARD; + cflags |= CPRNG_INIT_ANY; + if (block) + cflags |= CPRNG_USE_CV; + + /* + * Create a private CPRNG instance named `grnd1234' in pid + * 1234. + * + * XXX cprng_strong_create may block for allocation, silly. + */ + (void)snprintf(name, sizeof name, "grnd%jd", + (intmax_t)curproc->p_pid); + cprng = cprng_strong_create(name, IPL_NONE, cflags); + + /* Generate data. */ + while (n) { + size_t m = cprng_strong(cprng, buf, MIN(n, sizeof buf), + (block ? 0 : FNONBLOCK)); + + if (m == 0) { + /* + * If we're blocking, infer that a short read + * means interruption. If we're not blocking, + * infer that a short read means we would have + * blocked. + */ + error = (block ? EINTR : EWOULDBLOCK); + break; + } + + KASSERT(0 < m); + KASSERT(m <= MIN(n, sizeof buf)); + error = copyout(buf, p, m); + if (error) + break; + p += m; + n -= m; + + /* + * Don't bother checking for interruption; the request + * is limited to 512 bytes, and we're not to get that + * much without blocking anyway. + */ + } + + /* Destroy the private CPRNG instance. */ + cprng_strong_destroy(cprng); + + return error; +} + +/* + * getrandom(buf, len, flags) + * + * Fill buf with len uniform random bytes fit for use in + * cryptography if possible; fail or block if not, according to + * flags. + */ +int +sys_getrandom(struct lwp *l, const struct sys_getrandom_args *uap, + register_t *retval) +{ + /* { + syscallarg(void *) buf; + syscallarg(size_t) len; + syscallarg(int) flags; + } */ + void *ubuf = SCARG(uap, buf); + size_t len = SCARG(uap, len); + int flags = SCARG(uap, flags); + bool block = !(flags & GRND_NONBLOCK); + + /* Discriminate between the sensible and silly versions. */ + if (flags & GRND_RANDOM) + return getrandom_silly(ubuf, len, block); + else + return getrandom_sensible(ubuf, len, block); +} diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 4703e37b0ee6..26c05994ea25 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -1001,3 +1001,5 @@ siginfo_t *info); } 482 STD { int|sys||clock_getcpuclockid2(idtype_t idtype, \ id_t id, clockid_t *clock_id); } +483 STD { int|sys||getrandom(void *buf, size_t len, \ + int flags); } diff --git a/sys/sys/random.h b/sys/sys/random.h new file mode 100644 index 000000000000..93edd37af9de --- /dev/null +++ b/sys/sys/random.h @@ -0,0 +1,45 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Taylor R. Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_RANDOM_H +#define _SYS_RANDOM_H + +#include <sys/types.h> +#include <sys/cdefs.h> + +#define GRND_NONBLOCK 1 +#define GRND_RANDOM 2 + +__BEGIN_DECLS +int getrandom(void *, size_t, int); +__END_DECLS + +#endif /* _SYS_RANDOM_H */ diff --git a/sys/sys/rnd.h b/sys/sys/rnd.h index 98df0b6f9908..f18b0b34d559 100644 --- a/sys/sys/rnd.h +++ b/sys/sys/rnd.h @@ -48,6 +48,7 @@ void rnd_seed(void *, size_t); int rnd_system_ioctl(struct file *, u_long, void *); extern int rnd_initial_entropy; +int rnd_wait_for_initial_entropy(void); #endif /* _KERNEL */