Home | History | Annotate | Line # | Download | only in sysrandom
      1 
      2 #include <assert.h>
      3 #include <errno.h>
      4 #include <fcntl.h>
      5 #include <limits.h>
      6 #include <stdint.h>
      7 #include <string.h>
      8 #ifndef _WIN32
      9 # include <unistd.h>
     10 #endif
     11 
     12 #include <stdlib.h>
     13 #include <sys/types.h>
     14 #ifndef _WIN32
     15 # include <sys/stat.h>
     16 # include <sys/time.h>
     17 #endif
     18 #ifdef __linux__
     19 # ifdef __dietlibc__
     20 #  define _LINUX_SOURCE
     21 # else
     22 #  include <sys/syscall.h>
     23 # endif
     24 # include <poll.h>
     25 #endif
     26 
     27 #include "core.h"
     28 #include "private/common.h"
     29 #include "randombytes.h"
     30 #include "randombytes_sysrandom.h"
     31 #include "utils.h"
     32 
     33 #ifdef _WIN32
     34 /* `RtlGenRandom` is used over `CryptGenRandom` on Microsoft Windows based systems:
     35  *  - `CryptGenRandom` requires pulling in `CryptoAPI` which causes unnecessary
     36  *     memory overhead if this API is not being used for other purposes
     37  *  - `RtlGenRandom` is thus called directly instead. A detailed explanation
     38  *     can be found here: https://blogs.msdn.microsoft.com/michael_howard/2005/01/14/cryptographically-secure-random-number-on-windows-without-using-cryptoapi/
     39  *
     40  * In spite of the disclaimer on the `RtlGenRandom` documentation page that was
     41  * written back in the Windows XP days, this function is here to stay. The CRT
     42  * function `rand_s()` directly depends on it, so touching it would break many
     43  * applications released since Windows XP.
     44  *
     45  * Also note that Rust, Firefox and BoringSSL (thus, Google Chrome and everything
     46  * based on Chromium) also depend on it, and that libsodium allows the RNG to be
     47  * replaced without patching nor recompiling the library.
     48  */
     49 # include <windows.h>
     50 # define RtlGenRandom SystemFunction036
     51 # if defined(__cplusplus)
     52 extern "C"
     53 # endif
     54 BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
     55 # pragma comment(lib, "advapi32.lib")
     56 #endif
     57 
     58 #if defined(__OpenBSD__) || defined(__CloudABI__)
     59 # define HAVE_SAFE_ARC4RANDOM 1
     60 #endif
     61 
     62 #ifndef SSIZE_MAX
     63 # define SSIZE_MAX (SIZE_MAX / 2 - 1)
     64 #endif
     65 
     66 #ifdef HAVE_SAFE_ARC4RANDOM
     67 
     68 static uint32_t
     69 randombytes_sysrandom(void)
     70 {
     71     return arc4random();
     72 }
     73 
     74 static void
     75 randombytes_sysrandom_stir(void)
     76 {
     77 }
     78 
     79 static void
     80 randombytes_sysrandom_buf(void * const buf, const size_t size)
     81 {
     82     arc4random_buf(buf, size);
     83 }
     84 
     85 static int
     86 randombytes_sysrandom_close(void)
     87 {
     88     return 0;
     89 }
     90 
     91 #else /* __OpenBSD__ */
     92 
     93 typedef struct SysRandom_ {
     94     int random_data_source_fd;
     95     int initialized;
     96     int getrandom_available;
     97 } SysRandom;
     98 
     99 static SysRandom stream = {
    100     SODIUM_C99(.random_data_source_fd =) -1,
    101     SODIUM_C99(.initialized =) 0,
    102     SODIUM_C99(.getrandom_available =) 0
    103 };
    104 
    105 #ifndef _WIN32
    106 static ssize_t
    107 safe_read(const int fd, void * const buf_, size_t size)
    108 {
    109     unsigned char *buf = (unsigned char *) buf_;
    110     ssize_t        readnb;
    111 
    112     assert(size > (size_t) 0U);
    113     assert(size <= SSIZE_MAX);
    114     do {
    115         while ((readnb = read(fd, buf, size)) < (ssize_t) 0 &&
    116                (errno == EINTR || errno == EAGAIN)); /* LCOV_EXCL_LINE */
    117         if (readnb < (ssize_t) 0) {
    118             return readnb; /* LCOV_EXCL_LINE */
    119         }
    120         if (readnb == (ssize_t) 0) {
    121             break; /* LCOV_EXCL_LINE */
    122         }
    123         size -= (size_t) readnb;
    124         buf += readnb;
    125     } while (size > (ssize_t) 0);
    126 
    127     return (ssize_t) (buf - (unsigned char *) buf_);
    128 }
    129 #endif
    130 
    131 #ifndef _WIN32
    132 # if defined(__linux__) && !defined(USE_BLOCKING_RANDOM) && !defined(NO_BLOCKING_RANDOM_POLL)
    133 static int
    134 randombytes_block_on_dev_random(void)
    135 {
    136     struct pollfd pfd;
    137     int           fd;
    138     int           pret;
    139 
    140     fd = open("/dev/random", O_RDONLY);
    141     if (fd == -1) {
    142         return 0;
    143     }
    144     pfd.fd = fd;
    145     pfd.events = POLLIN;
    146     pfd.revents = 0;
    147     do {
    148         pret = poll(&pfd, 1, -1);
    149     } while (pret < 0 && (errno == EINTR || errno == EAGAIN));
    150     if (pret != 1) {
    151         (void) close(fd);
    152         errno = EIO;
    153         return -1;
    154     }
    155     return close(fd);
    156 }
    157 # endif
    158 
    159 static int
    160 randombytes_sysrandom_random_dev_open(void)
    161 {
    162 /* LCOV_EXCL_START */
    163     struct stat        st;
    164     static const char *devices[] = {
    165 # ifndef USE_BLOCKING_RANDOM
    166         "/dev/urandom",
    167 # endif
    168         "/dev/random", NULL
    169     };
    170     const char       **device = devices;
    171     int                fd;
    172 
    173 # if defined(__linux__) && !defined(USE_BLOCKING_RANDOM) && !defined(NO_BLOCKING_RANDOM_POLL)
    174     if (randombytes_block_on_dev_random() != 0) {
    175         return -1;
    176     }
    177 # endif
    178     do {
    179         fd = open(*device, O_RDONLY);
    180         if (fd != -1) {
    181             if (fstat(fd, &st) == 0 &&
    182 # ifdef __COMPCERT__
    183                 1
    184 # elif defined(S_ISNAM)
    185                 (S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode))
    186 # else
    187                 S_ISCHR(st.st_mode)
    188 # endif
    189                ) {
    190 # if defined(F_SETFD) && defined(FD_CLOEXEC)
    191                 (void) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
    192 # endif
    193                 return fd;
    194             }
    195             (void) close(fd);
    196         } else if (errno == EINTR) {
    197             continue;
    198         }
    199         device++;
    200     } while (*device != NULL);
    201 
    202     errno = EIO;
    203     return -1;
    204 /* LCOV_EXCL_STOP */
    205 }
    206 
    207 # if defined(__dietlibc__) || (defined(SYS_getrandom) && defined(__NR_getrandom))
    208 static int
    209 _randombytes_linux_getrandom(void * const buf, const size_t size)
    210 {
    211     int readnb;
    212 
    213     assert(size <= 256U);
    214     do {
    215 #  ifdef __dietlibc__
    216         readnb = getrandom(buf, size, 0);
    217 #  else
    218         readnb = syscall(SYS_getrandom, buf, (int) size, 0);
    219 #  endif
    220     } while (readnb < 0 && (errno == EINTR || errno == EAGAIN));
    221 
    222     return (readnb == (int) size) - 1;
    223 }
    224 
    225 static int
    226 randombytes_linux_getrandom(void * const buf_, size_t size)
    227 {
    228     unsigned char *buf = (unsigned char *) buf_;
    229     size_t         chunk_size = 256U;
    230 
    231     do {
    232         if (size < chunk_size) {
    233             chunk_size = size;
    234             assert(chunk_size > (size_t) 0U);
    235         }
    236         if (_randombytes_linux_getrandom(buf, chunk_size) != 0) {
    237             return -1;
    238         }
    239         size -= chunk_size;
    240         buf += chunk_size;
    241     } while (size > (size_t) 0U);
    242 
    243     return 0;
    244 }
    245 # endif
    246 
    247 static void
    248 randombytes_sysrandom_init(void)
    249 {
    250     const int     errno_save = errno;
    251 
    252 # if defined(SYS_getrandom) && defined(__NR_getrandom)
    253     {
    254         unsigned char fodder[16];
    255 
    256         if (randombytes_linux_getrandom(fodder, sizeof fodder) == 0) {
    257             stream.getrandom_available = 1;
    258             errno = errno_save;
    259             return;
    260         }
    261         stream.getrandom_available = 0;
    262     }
    263 # endif
    264 
    265     if ((stream.random_data_source_fd =
    266          randombytes_sysrandom_random_dev_open()) == -1) {
    267         sodium_misuse(); /* LCOV_EXCL_LINE */
    268     }
    269     errno = errno_save;
    270 }
    271 
    272 #else /* _WIN32 */
    273 
    274 static void
    275 randombytes_sysrandom_init(void)
    276 {
    277 }
    278 #endif
    279 
    280 static void
    281 randombytes_sysrandom_stir(void)
    282 {
    283     if (stream.initialized == 0) {
    284         randombytes_sysrandom_init();
    285         stream.initialized = 1;
    286     }
    287 }
    288 
    289 static void
    290 randombytes_sysrandom_stir_if_needed(void)
    291 {
    292     if (stream.initialized == 0) {
    293         randombytes_sysrandom_stir();
    294     }
    295 }
    296 
    297 static int
    298 randombytes_sysrandom_close(void)
    299 {
    300     int ret = -1;
    301 
    302 #ifndef _WIN32
    303     if (stream.random_data_source_fd != -1 &&
    304         close(stream.random_data_source_fd) == 0) {
    305         stream.random_data_source_fd = -1;
    306         stream.initialized = 0;
    307         ret = 0;
    308     }
    309 # if defined(SYS_getrandom) && defined(__NR_getrandom)
    310     if (stream.getrandom_available != 0) {
    311         ret = 0;
    312     }
    313 # endif
    314 #else /* _WIN32 */
    315     if (stream.initialized != 0) {
    316         stream.initialized = 0;
    317         ret = 0;
    318     }
    319 #endif
    320     return ret;
    321 }
    322 
    323 static void
    324 randombytes_sysrandom_buf(void * const buf, const size_t size)
    325 {
    326     randombytes_sysrandom_stir_if_needed();
    327 #if defined(ULONG_LONG_MAX) && defined(SIZE_MAX)
    328 # if SIZE_MAX > ULONG_LONG_MAX
    329     /* coverity[result_independent_of_operands] */
    330     assert(size <= ULONG_LONG_MAX);
    331 # endif
    332 #endif
    333 #ifndef _WIN32
    334 # if defined(SYS_getrandom) && defined(__NR_getrandom)
    335     if (stream.getrandom_available != 0) {
    336         if (randombytes_linux_getrandom(buf, size) != 0) {
    337             sodium_misuse(); /* LCOV_EXCL_LINE */
    338         }
    339         return;
    340     }
    341 # endif
    342     if (stream.random_data_source_fd == -1 ||
    343         safe_read(stream.random_data_source_fd, buf, size) != (ssize_t) size) {
    344         sodium_misuse(); /* LCOV_EXCL_LINE */
    345     }
    346 #else
    347     COMPILER_ASSERT(randombytes_BYTES_MAX <= 0xffffffffUL);
    348     if (size > (size_t) 0xffffffffUL) {
    349         sodium_misuse(); /* LCOV_EXCL_LINE */
    350     }
    351     if (! RtlGenRandom((PVOID) buf, (ULONG) size)) {
    352         sodium_misuse(); /* LCOV_EXCL_LINE */
    353     }
    354 #endif
    355 }
    356 
    357 static uint32_t
    358 randombytes_sysrandom(void)
    359 {
    360     uint32_t r;
    361 
    362     randombytes_sysrandom_buf(&r, sizeof r);
    363 
    364     return r;
    365 }
    366 
    367 #endif /* __OpenBSD__ */
    368 
    369 static const char *
    370 randombytes_sysrandom_implementation_name(void)
    371 {
    372     return "sysrandom";
    373 }
    374 
    375 struct randombytes_implementation randombytes_sysrandom_implementation = {
    376     SODIUM_C99(.implementation_name =) randombytes_sysrandom_implementation_name,
    377     SODIUM_C99(.random =) randombytes_sysrandom,
    378     SODIUM_C99(.stir =) randombytes_sysrandom_stir,
    379     SODIUM_C99(.uniform =) NULL,
    380     SODIUM_C99(.buf =) randombytes_sysrandom_buf,
    381     SODIUM_C99(.close =) randombytes_sysrandom_close
    382 };
    383