(root)/
libxcrypt-4.4.36/
lib/
util-get-random-bytes.c
       1  /* Retrieval of cryptographically random bytes from the operating system.
       2   *
       3   * Written by Zack Weinberg <zackw at panix.com> in 2017.
       4   *
       5   * No copyright is claimed, and the software is hereby placed in the public
       6   * domain.  In case this attempt to disclaim copyright and place the software
       7   * in the public domain is deemed null and void, then the software is
       8   * Copyright (c) 2017 Zack Weinberg and it is hereby released to the
       9   * general public under the following terms:
      10   *
      11   * Redistribution and use in source and binary forms, with or without
      12   * modification, are permitted.
      13   *
      14   * There's ABSOLUTELY NO WARRANTY, express or implied.
      15   */
      16  
      17  #include "crypt-port.h"
      18  
      19  #include <errno.h>
      20  #include <stdlib.h>
      21  
      22  #ifdef HAVE_FCNTL_H
      23  #include <fcntl.h>
      24  #endif
      25  #ifdef HAVE_SYS_RANDOM_H
      26  #include <sys/random.h>
      27  #endif
      28  #ifdef HAVE_SYS_SYSCALL_H
      29  #include <sys/syscall.h>
      30  #endif
      31  #ifdef HAVE_SYS_STAT_H
      32  #include <sys/stat.h>
      33  #endif
      34  
      35  /* If we have O_CLOEXEC, we use it, but if we don't, we don't worry
      36     about it.  */
      37  #ifndef O_CLOEXEC
      38  #define O_CLOEXEC 0
      39  #endif
      40  
      41  /* There is no universally portable way to access a system CSPRNG.
      42     If the C library provides any of the following functions, we try them,
      43     in order of preference: arc4random_buf, getentropy, getrandom.
      44     If none of those are available or they don't work, we attempt to
      45     make direct system calls for getentropy and getrandom.  If *that*
      46     doesn't work, we try opening and reading /dev/urandom.
      47  
      48     This function returns true if the exact number of requested bytes
      49     was successfully read, false otherwise; if it returns false, errno
      50     has been set.  It may block.  It cannot be used to read more than
      51     256 bytes at a time (this is a limitation inherited from
      52     getentropy() and enforced regardless of the actual back-end in use).
      53  
      54     If we fall all the way back to /dev/urandom, we open and close it on
      55     each call.  */
      56  
      57  bool
      58  get_random_bytes(void *buf, size_t buflen)
      59  {
      60    if (buflen == 0)
      61      return true;
      62  
      63    /* Some, but not all, of the primitives below are limited to
      64       producing no more than 256 bytes of random data.  Impose this
      65       constraint on our callers regardless of which primitive is
      66       actually used.  */
      67    if (buflen > 256)
      68      {
      69        errno = EIO;
      70        return false;
      71      }
      72  
      73    /* To eliminate the possibility of one of the primitives below failing
      74       with EFAULT, force a crash now if the buffer is unwritable.  */
      75    explicit_bzero (buf, buflen);
      76  
      77  #ifdef HAVE_ARC4RANDOM_BUF
      78    /* arc4random_buf, if it exists, can never fail.  */
      79    arc4random_buf (buf, buflen);
      80    return true;
      81  
      82  #else /* no arc4random_buf */
      83  
      84  #ifdef HAVE_GETENTROPY
      85    /* getentropy may exist but lack kernel support.  */
      86    static bool getentropy_doesnt_work;
      87    if (!getentropy_doesnt_work)
      88      {
      89        if (!getentropy (buf, buflen))
      90          return true;
      91        getentropy_doesnt_work = true;
      92      }
      93  #endif
      94  
      95  #ifdef HAVE_GETRANDOM
      96    /* Likewise getrandom.  */
      97    static bool getrandom_doesnt_work;
      98    if (!getrandom_doesnt_work)
      99      {
     100        if ((size_t)getrandom (buf, buflen, 0) == buflen)
     101          return true;
     102        getrandom_doesnt_work = true;
     103      }
     104  #endif
     105  
     106    /* If we can make arbitrary syscalls, try getentropy and getrandom
     107       again that way.  */
     108  #ifdef HAVE_SYSCALL
     109  #ifdef SYS_getentropy
     110    static bool sys_getentropy_doesnt_work;
     111    if (!sys_getentropy_doesnt_work)
     112      {
     113        if (!syscall (SYS_getentropy, buf, buflen))
     114          return true;
     115        sys_getentropy_doesnt_work = true;
     116      }
     117  #endif
     118  
     119  #ifdef SYS_getrandom
     120    static bool sys_getrandom_doesnt_work;
     121    if (!sys_getrandom_doesnt_work)
     122      {
     123        if ((size_t)syscall (SYS_getrandom, buf, buflen, 0) == buflen)
     124          return true;
     125        sys_getrandom_doesnt_work = true;
     126      }
     127  #endif
     128  #endif
     129  
     130  #if defined HAVE_SYS_STAT_H && defined HAVE_FCNTL_H && defined HAVE_UNISTD_H
     131    /* Try reading from /dev/urandom.  */
     132    static bool dev_urandom_doesnt_work;
     133    if (!dev_urandom_doesnt_work)
     134      {
     135        int fd = open ("/dev/urandom", O_RDONLY|O_CLOEXEC);
     136        if (fd == -1)
     137          dev_urandom_doesnt_work = true;
     138        else
     139          {
     140            ssize_t nread = read (fd, buf, buflen);
     141            if (nread < 0 || (size_t)nread < buflen)
     142              dev_urandom_doesnt_work = true;
     143  
     144            close(fd);
     145            return !dev_urandom_doesnt_work;
     146          }
     147      }
     148  #endif
     149  #endif /* no arc4random_buf */
     150  
     151    /* if we get here, we're just completely hosed */
     152    errno = ENOSYS;
     153    return false;
     154  }