Re: [PATCH, RFC -v3] random: introduce getrandom(2) system call

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Jul 18, 2014 at 11:59 AM, Theodore Ts'o <tytso@xxxxxxx> wrote:
> The getrandom(2) system call was requested by the LibreSSL Portable
> developers.  It is analoguous to the getentropy(2) system call in
> OpenBSD.
>
> The rationale of this system call is to provide resiliance against
> file descriptor exhaustion attacks, where the attacker consumes all
> available file descriptors, forcing the use of the fallback code where
> /dev/[u]random is not available.  Since the fallback code is often not
> well-tested, it is better to eliminate this potential failure mode
> entirely.
>
> The other feature provided by this new system call is the ability to
> request randomness from the /dev/urandom entropy pool, but to block
> until at least 128 bits of entropy has been accumulated in the
> /dev/urandom entropy pool.  Historically, the emphasis in the
> /dev/urandom development has been to ensure that urandom pool is
> initialized as quickly as possible after system boot, and preferably
> before the init scripts start execution.
>
> This is because changing /dev/urandom reads to block represents an
> interface change that could potentially break userspace which is not
> acceptable.  In practice, on most x86 desktop and server systems, in
> general the entropy pool can be initialized before it is needed (and
> in modern kernels, we will printk a warning message if not).  However,
> on an embedded system, this may not be the case.  And so with this new
> interface, we can provide the functionality of blocking until the
> urandom pool has been initialized.  Any userspace program which uses
> this new functionality must take care to assure that if it is used
> during the boot process, that it will not cause the init scripts or
> other portions of the system startup to hang indefinitely.
>
> SYNOPSIS
>         #include <linux/random.h>
>
>         int getrandom(void *buf, size_t buflen, unsigned int flags);
>
> DESCRIPTION
>         The system call getrandom() fills the buffer pointed to by buf
>         with up to buflen random bytes which can be used to seed user
>         space random number generators (i.e., DRBG's) or for other
>         cryptographic processes.  It should not be used Monte Carlo
>         simulations or for other probabilistic sampling applications.
>
>         If the GRND_RANDOM flags bit is set, then draw from the
>         /dev/random pool instead of the /dev/urandom pool.  The
>         /dev/random pool is limited based on the entropy that can be
>         obtained from environmental noise, so if there is insufficient
>         entropy, the requested number of bytes may not be returned.
>         If there is no entropy available at all, getrandom(2) will
>         either block, or return an error with errno set to EAGAIN if
>         the GRND_NONBLOCK bit is set in flags.
>
>         If the GRND_RANDOM bit is not set, then the /dev/urandom pool
>         will be used.  Unlike using read(2) to fetch data from
>         /dev/urandom, if the urandom pool has not been sufficiently
>         initialized, getrandom(2) will block or return -1 with the
>         errno set to EGAIN if the GRND_NONBLOCK bit is set in flags.
>
>         The getentropy(2) system call in OpenBSD can be emulated using
>         the following function:
>
>             int getentropy(void *buf, size_t buflen)
>             {
>                     int     ret;
>
>                     if (buflen > 256)
>                             goto failure;
>
>                     ret = getrandom(buf, buflen, 0);
>                     if (ret < 0)
>                             return ret;
>                     if (ret != buflen)

A small nit-pick, missing "{".

>                     failure:
>                             errno = EIO;
>                             return -1;
>                     }
>                     return 0;
>             }
>
> RETURN VALUE
>        On success, the number of bytes that was filled in the buf is
>        returned.  This may not be all the bytes requested by the
>        caller via buflen if insufficient entropy was present in the
>        /dev/random pool, or if the system call was interrupted by a
>        signal.
>
>        On error, -1 is returned, and errno is set appropriately.
>
> ERRORS
>         EINVAL          An invalid flag was passed to getrandom(2)
>
>         EFAULT          buf is outside the accessible address space.
>
>         EAGAIN          The requested entropy was not available, and the
>                         getentropy(2) would have blocked if GRND_BLOCK flag
>                         was set.
>
>         EINTR           While blocked waiting for entropy, the call was
>                         interrupted by a signal handler; see the description
>                         of how interrupted read(2) calls on "slow" devices
>                         are handled with and without the SA_RESTART flag
>                         in the signal(7) man page.
>
> NOTES
>         For small requests (buflen <= 256) getrandom(2) will not
>         return EINTR when reading from the urandom pool once the
>         entropy pool has been initialized, and it will return all of
>         the bytes that have been requested.  This is the recommended
>         way to use getrandom(2), and is designed for compatibility
>         with OpenBSD's getentropy() system call.
>
>         However, if you are using GRND_RANDOM, then getrandom(2) may
>         block until the entropy accounting determines that sufficient
>         environmental noise has been gathered such that getrandom(2)
>         will be operating as a NRBG instead of a DRBG for those people
>         who are working in the NIST SP 800-90 regime.  Since it may
>         block for a long time, these guarantees do *not* apply.  The
>         user may want to interrupt a hanging process using a signal,
>         so blocking until all of the requested bytes are returned
>         would be unfriendly.
>
>         For this reason, the user of getrandom(2) MUST always check
>         the return value, in case it returns some error, or if fewer
>         bytes than requested was returned.  In the case of
>         !GRND_RANDOM and small request, the latter should never
>         happen, but the careful userspace code (and all crypto code
>         should be careful) should check for this anyway!
>
> Signed-off-by: Theodore Ts'o <tytso@xxxxxxx>
> Reviewed-by: Zach Brown <zab@xxxxxxxxx>
> ---
>
> The change in the v3 version of the commit was to eliminate potential
> short reads and EINTR returns when reading from urandom (once the
> urandom pool is initialized).  This was based on comments and requests
> from Theo de Raadt.  See the NOTES section in the suggested man page for
> a more in-depth discussion of the issues involved.
>
>  arch/x86/syscalls/syscall_32.tbl  |  1 +
>  arch/x86/syscalls/syscall_64.tbl  |  1 +
>  drivers/char/random.c             | 42 ++++++++++++++++++++++++++++++++++++---
>  include/linux/syscalls.h          |  3 +++
>  include/uapi/asm-generic/unistd.h |  4 +++-
>  include/uapi/linux/random.h       |  9 +++++++++
>  6 files changed, 56 insertions(+), 4 deletions(-)
>
> diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl
> index d6b8679..f484e39 100644
> --- a/arch/x86/syscalls/syscall_32.tbl
> +++ b/arch/x86/syscalls/syscall_32.tbl
> @@ -360,3 +360,4 @@
>  351    i386    sched_setattr           sys_sched_setattr
>  352    i386    sched_getattr           sys_sched_getattr
>  353    i386    renameat2               sys_renameat2
> +354    i386    getrandom               sys_getrandom
> diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl
> index ec255a1..6705032 100644
> --- a/arch/x86/syscalls/syscall_64.tbl
> +++ b/arch/x86/syscalls/syscall_64.tbl
> @@ -323,6 +323,7 @@
>  314    common  sched_setattr           sys_sched_setattr
>  315    common  sched_getattr           sys_sched_getattr
>  316    common  renameat2               sys_renameat2
> +317    common  getrandom               sys_getrandom
>
>  #
>  # x32-specific system call numbers start at 512 to avoid cache impact
> diff --git a/drivers/char/random.c b/drivers/char/random.c
> index aa22fe5..91dd57e 100644
> --- a/drivers/char/random.c
> +++ b/drivers/char/random.c
> @@ -258,6 +258,8 @@
>  #include <linux/kmemcheck.h>
>  #include <linux/workqueue.h>
>  #include <linux/irq.h>
> +#include <linux/syscalls.h>
> +#include <linux/completion.h>
>
>  #include <asm/processor.h>
>  #include <asm/uaccess.h>
> @@ -469,6 +471,8 @@ static struct entropy_store nonblocking_pool = {
>                                         push_to_pool),
>  };
>
> +static DECLARE_COMPLETION(urandom_initialized);
> +
>  static __u32 const twist_table[8] = {
>         0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
>         0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
> @@ -657,6 +661,7 @@ retry:
>                 r->entropy_total = 0;
>                 if (r == &nonblocking_pool) {
>                         prandom_reseed_late();
> +                       complete_all(&urandom_initialized);
>                         pr_notice("random: %s pool is initialized\n", r->name);
>                 }
>         }
> @@ -1174,13 +1179,14 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
>  {
>         ssize_t ret = 0, i;
>         __u8 tmp[EXTRACT_SIZE];
> +       int large_request = (nbytes > 256);
>
>         trace_extract_entropy_user(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_);
>         xfer_secondary_pool(r, nbytes);
>         nbytes = account(r, nbytes, 0, 0);
>
>         while (nbytes) {
> -               if (need_resched()) {
> +               if (large_request && need_resched()) {
>                         if (signal_pending(current)) {
>                                 if (ret == 0)
>                                         ret = -ERESTARTSYS;
> @@ -1355,7 +1361,7 @@ static int arch_random_refill(void)
>  }
>
>  static ssize_t
> -random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
> +_random_read(int nonblock, char __user *buf, size_t nbytes)
>  {
>         ssize_t n;
>
> @@ -1379,7 +1385,7 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
>                 if (arch_random_refill())
>                         continue;
>
> -               if (file->f_flags & O_NONBLOCK)
> +               if (nonblock)
>                         return -EAGAIN;
>
>                 wait_event_interruptible(random_read_wait,
> @@ -1391,6 +1397,12 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
>  }
>
>  static ssize_t
> +random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
> +{
> +       return _random_read(file->f_flags & O_NONBLOCK, buf, nbytes);
> +}
> +
> +static ssize_t
>  urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
>  {
>         int ret;
> @@ -1533,6 +1545,30 @@ const struct file_operations urandom_fops = {
>         .llseek = noop_llseek,
>  };
>
> +SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
> +               unsigned int, flags)
> +{
> +       int r;
> +
> +       if (flags & ~(GRND_NONBLOCK|GRND_RANDOM))
> +               return -EINVAL;
> +
> +       if (count > INT_MAX)
> +               count = INT_MAX;
> +
> +       if (flags & GRND_RANDOM)
> +               return _random_read(flags & GRND_NONBLOCK, buf, count);
> +       if (flags & GRND_NONBLOCK) {
> +               if (!completion_done(&urandom_initialized))
> +                       return -EAGAIN;
> +       } else {
> +               r = wait_for_completion_interruptible(&urandom_initialized);
> +               if (r)
> +                       return r;
> +       }
> +       return urandom_read(NULL, buf, count, NULL);
> +}
> +
>  /***************************************************************
>   * Random UUID interface
>   *
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index b0881a0..cd82f72f 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -866,4 +866,7 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
>  asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
>                          unsigned long idx1, unsigned long idx2);
>  asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
> +asmlinkage long sys_getrandom(char __user * buf, size_t count,
> +                             unsigned int flags);
> +
>  #endif
> diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
> index 3336406..2926b1d 100644
> --- a/include/uapi/asm-generic/unistd.h
> +++ b/include/uapi/asm-generic/unistd.h
> @@ -699,9 +699,11 @@ __SYSCALL(__NR_sched_setattr, sys_sched_setattr)
>  __SYSCALL(__NR_sched_getattr, sys_sched_getattr)
>  #define __NR_renameat2 276
>  __SYSCALL(__NR_renameat2, sys_renameat2)
> +#define __NR_getrandom 277
> +__SYSCALL(__NR_getrandom, sys_getrandom)
>
>  #undef __NR_syscalls
> -#define __NR_syscalls 277
> +#define __NR_syscalls 278
>
>  /*
>   * All syscalls below here should go away really,
> diff --git a/include/uapi/linux/random.h b/include/uapi/linux/random.h
> index fff3528..3f93d16 100644
> --- a/include/uapi/linux/random.h
> +++ b/include/uapi/linux/random.h
> @@ -40,4 +40,13 @@ struct rand_pool_info {
>         __u32   buf[0];
>  };
>
> +/*
> + * Flags for getrandom(2)
> + *
> + * GRND_NONBLOCK       Don't block and return EAGAIN instead
> + * GRND_RANDOM         Use the /dev/random pool instead of /dev/urandom
> + */
> +#define GRND_NONBLOCK  0x0001
> +#define GRND_RANDOM    0x0002
> +
>  #endif /* _UAPI_LINUX_RANDOM_H */
> --
> 2.0.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



-- 
Thanks,
//richard
--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

  Powered by Linux