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
randombytes_sysrandom(void)69 randombytes_sysrandom(void)
70 {
71 return arc4random();
72 }
73
74 static void
randombytes_sysrandom_stir(void)75 randombytes_sysrandom_stir(void)
76 {
77 }
78
79 static void
randombytes_sysrandom_buf(void * const buf,const size_t size)80 randombytes_sysrandom_buf(void * const buf, const size_t size)
81 {
82 arc4random_buf(buf, size);
83 }
84
85 static int
randombytes_sysrandom_close(void)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
safe_read(const int fd,void * const buf_,size_t size)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
randombytes_block_on_dev_random(void)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
randombytes_sysrandom_random_dev_open(void)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
_randombytes_linux_getrandom(void * const buf,const size_t size)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
randombytes_linux_getrandom(void * const buf_,size_t size)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
randombytes_sysrandom_init(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
randombytes_sysrandom_init(void)275 randombytes_sysrandom_init(void)
276 {
277 }
278 #endif
279
280 static void
randombytes_sysrandom_stir(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
randombytes_sysrandom_stir_if_needed(void)290 randombytes_sysrandom_stir_if_needed(void)
291 {
292 if (stream.initialized == 0) {
293 randombytes_sysrandom_stir();
294 }
295 }
296
297 static int
randombytes_sysrandom_close(void)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
randombytes_sysrandom_buf(void * const buf,const size_t size)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
randombytes_sysrandom(void)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 *
randombytes_sysrandom_implementation_name(void)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