The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/sys_getrandom.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: sys_getrandom.c,v 1.2 2021/12/28 13:22:43 riastradh Exp $      */
    2 
    3 /*-
    4  * Copyright (c) 2020 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Taylor R. Campbell.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   29  * POSSIBILITY OF SUCH DAMAGE.
   30  */
   31 
   32 /*
   33  * getrandom() system call
   34  */
   35 
   36 #include <sys/cdefs.h>
   37 __KERNEL_RCSID(0, "$NetBSD: sys_getrandom.c,v 1.2 2021/12/28 13:22:43 riastradh Exp $");
   38 
   39 #include <sys/types.h>
   40 #include <sys/param.h>
   41 
   42 #include <sys/atomic.h>
   43 #include <sys/cprng.h>
   44 #include <sys/entropy.h>
   45 #include <sys/kmem.h>
   46 #include <sys/lwp.h>
   47 #include <sys/proc.h>
   48 #include <sys/random.h>
   49 #include <sys/sched.h>
   50 #include <sys/signalvar.h>
   51 #include <sys/syscallargs.h>
   52 #include <sys/uio.h>
   53 
   54 #include <crypto/nist_hash_drbg/nist_hash_drbg.h>
   55 
   56 #define RANDOM_BUFSIZE  512
   57 
   58 int
   59 dogetrandom(struct uio *uio, unsigned int flags)
   60 {
   61         uint8_t seed[NIST_HASH_DRBG_SEEDLEN_BYTES] = {0};
   62         struct nist_hash_drbg drbg;
   63         uint8_t *buf;
   64         int extractflags = 0;
   65         int error;
   66 
   67         KASSERT((flags & ~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)) == 0);
   68         KASSERT((flags & (GRND_RANDOM|GRND_INSECURE)) !=
   69             (GRND_RANDOM|GRND_INSECURE));
   70 
   71         /* Get a buffer for transfers.  */
   72         buf = kmem_alloc(RANDOM_BUFSIZE, KM_SLEEP);
   73 
   74         /*
   75          * Fast path: for short reads other than from /dev/random, if
   76          * seeded or if INSECURE, just draw from per-CPU cprng_strong.
   77          */
   78         if (uio->uio_resid <= RANDOM_BUFSIZE &&
   79             !ISSET(flags, GRND_RANDOM) &&
   80             (entropy_ready() || ISSET(flags, GRND_INSECURE))) {
   81                 /* Generate data and transfer it out.  */
   82                 cprng_strong(user_cprng, buf, uio->uio_resid, 0);
   83                 error = uiomove(buf, uio->uio_resid, uio);
   84                 goto out;
   85         }
   86 
   87         /*
   88          * Try to get a seed from the entropy pool.  Fail if we would
   89          * block.  If GRND_INSECURE, always return something even if it
   90          * is partial entropy; if !GRND_INSECURE, set ENTROPY_HARDFAIL
   91          * in order to tell entropy_extract not to bother drawing
   92          * anything from a partial pool if we can't get full entropy.
   93          */
   94         if (!ISSET(flags, GRND_NONBLOCK) && !ISSET(flags, GRND_INSECURE))
   95                 extractflags |= ENTROPY_WAIT|ENTROPY_SIG;
   96         if (!ISSET(flags, GRND_INSECURE))
   97                 extractflags |= ENTROPY_HARDFAIL;
   98         error = entropy_extract(seed, sizeof seed, extractflags);
   99         if (error && !ISSET(flags, GRND_INSECURE))
  100                 goto out;
  101 
  102         /* Instantiate the DRBG.  */
  103         if (nist_hash_drbg_instantiate(&drbg, seed, sizeof seed, NULL, 0,
  104                 NULL, 0))
  105                 panic("nist_hash_drbg_instantiate");
  106 
  107         /* Promptly zero the seed.  */
  108         explicit_memset(seed, 0, sizeof seed);
  109 
  110         /* Generate data.  */
  111         error = 0;
  112         while (uio->uio_resid) {
  113                 size_t n = MIN(uio->uio_resid, RANDOM_BUFSIZE);
  114 
  115                 /*
  116                  * Clamp /dev/random output to the entropy capacity and
  117                  * seed size.  Programs can't rely on long reads.
  118                  */
  119                 if (ISSET(flags, GRND_RANDOM)) {
  120                         n = MIN(n, ENTROPY_CAPACITY);
  121                         n = MIN(n, sizeof seed);
  122                         /*
  123                          * Guarantee never to return more than one
  124                          * buffer in this case to minimize bookkeeping.
  125                          */
  126                         CTASSERT(ENTROPY_CAPACITY <= RANDOM_BUFSIZE);
  127                         CTASSERT(sizeof seed <= RANDOM_BUFSIZE);
  128                 }
  129 
  130                 /*
  131                  * Try to generate a block of data, but if we've hit
  132                  * the DRBG reseed interval, reseed.
  133                  */
  134                 if (nist_hash_drbg_generate(&drbg, buf, n, NULL, 0)) {
  135                         /*
  136                          * Get a fresh seed without blocking -- we have
  137                          * already generated some output so it is not
  138                          * useful to block.  This can fail only if the
  139                          * request is obscenely large, so it is OK for
  140                          * either /dev/random or /dev/urandom to fail:
  141                          * we make no promises about gigabyte-sized
  142                          * reads happening all at once.
  143                          */
  144                         error = entropy_extract(seed, sizeof seed,
  145                             ENTROPY_HARDFAIL);
  146                         if (error)
  147                                 break;
  148 
  149                         /* Reseed and try again.  */
  150                         if (nist_hash_drbg_reseed(&drbg, seed, sizeof seed,
  151                                 NULL, 0))
  152                                 panic("nist_hash_drbg_reseed");
  153 
  154                         /* Promptly zero the seed.  */
  155                         explicit_memset(seed, 0, sizeof seed);
  156 
  157                         /* If it fails now, that's a bug.  */
  158                         if (nist_hash_drbg_generate(&drbg, buf, n, NULL, 0))
  159                                 panic("nist_hash_drbg_generate");
  160                 }
  161 
  162                 /* Transfer n bytes out.  */
  163                 error = uiomove(buf, n, uio);
  164                 if (error)
  165                         break;
  166 
  167                 /*
  168                  * If this is /dev/random, stop here, return what we
  169                  * have, and force the next read to reseed.  Programs
  170                  * can't rely on /dev/random for long reads.
  171                  */
  172                 if (ISSET(flags, GRND_RANDOM)) {
  173                         error = 0;
  174                         break;
  175                 }
  176 
  177                 /* Now's a good time to yield if needed.  */
  178                 preempt_point();
  179 
  180                 /* Check for interruption after at least 256 bytes.  */
  181                 CTASSERT(RANDOM_BUFSIZE >= 256);
  182                 if (__predict_false(curlwp->l_flag & LW_PENDSIG) &&
  183                     sigispending(curlwp, 0)) {
  184                         error = EINTR;
  185                         break;
  186                 }
  187         }
  188 
  189 out:    /* Zero the buffer and free it.  */
  190         explicit_memset(buf, 0, RANDOM_BUFSIZE);
  191         kmem_free(buf, RANDOM_BUFSIZE);
  192 
  193         return error;
  194 }
  195 
  196 int
  197 sys_getrandom(struct lwp *l, const struct sys_getrandom_args *uap,
  198     register_t *retval)
  199 {
  200         /* {
  201                 syscallarg(void *)      buf;
  202                 syscallarg(size_t)      buflen;
  203                 syscallarg(unsigned)    flags;
  204         } */
  205         void *buf = SCARG(uap, buf);
  206         size_t buflen = SCARG(uap, buflen);
  207         int flags = SCARG(uap, flags);
  208         int error;
  209 
  210         /* Set up an iov and uio to read into the user's buffer.  */
  211         struct iovec iov = { .iov_base = buf, .iov_len = buflen };
  212         struct uio uio = {
  213                 .uio_iov = &iov,
  214                 .uio_iovcnt = 1,
  215                 .uio_offset = 0,
  216                 .uio_resid = buflen,
  217                 .uio_rw = UIO_READ,
  218                 .uio_vmspace = curproc->p_vmspace,
  219         };
  220 
  221         /* Validate the flags.  */
  222         if (flags & ~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)) {
  223                 /* Unknown flags.  */
  224                 error = EINVAL;
  225                 goto out;
  226         }
  227         if ((flags & (GRND_RANDOM|GRND_INSECURE)) ==
  228             (GRND_RANDOM|GRND_INSECURE)) {
  229                 /* Nonsensical combination.  */
  230                 error = EINVAL;
  231                 goto out;
  232         }
  233 
  234         /* Do it.  */
  235         error = dogetrandom(&uio, flags);
  236 
  237 out:    /*
  238          * If we transferred anything, return the number of bytes
  239          * transferred and suppress error; otherwise return the error.
  240          */
  241         *retval = buflen - uio.uio_resid;
  242         if (*retval)
  243                 error = 0;
  244         return error;
  245 }

Cache object: 3534ccaf5a1f294f0c8fdc4ce177abad


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.