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)
52extern "C"
53# endif
54BOOLEAN 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
68static uint32_t
69randombytes_sysrandom(void)
70{
71    return arc4random();
72}
73
74static void
75randombytes_sysrandom_stir(void)
76{
77}
78
79static void
80randombytes_sysrandom_buf(void * const buf, const size_t size)
81{
82    arc4random_buf(buf, size);
83}
84
85static int
86randombytes_sysrandom_close(void)
87{
88    return 0;
89}
90
91#else /* __OpenBSD__ */
92
93typedef struct SysRandom_ {
94    int random_data_source_fd;
95    int initialized;
96    int getrandom_available;
97} SysRandom;
98
99static 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
106static ssize_t
107safe_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)
133static int
134randombytes_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
159static int
160randombytes_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))
208static 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
225static int
226randombytes_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
247static void
248randombytes_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
274static void
275randombytes_sysrandom_init(void)
276{
277}
278#endif
279
280static void
281randombytes_sysrandom_stir(void)
282{
283    if (stream.initialized == 0) {
284        randombytes_sysrandom_init();
285        stream.initialized = 1;
286    }
287}
288
289static void
290randombytes_sysrandom_stir_if_needed(void)
291{
292    if (stream.initialized == 0) {
293        randombytes_sysrandom_stir();
294    }
295}
296
297static int
298randombytes_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
323static void
324randombytes_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
357static uint32_t
358randombytes_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
369static const char *
370randombytes_sysrandom_implementation_name(void)
371{
372    return "sysrandom";
373}
374
375struct 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