blob: 202f2a4728d09bfb5120a6b401dca86d8901ee43 [file] [log] [blame]
Adam Langleyd9e397b2015-01-22 14:27:53 -08001/* Copyright (c) 2014, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15#include <openssl/rand.h>
16
Steven Valdezb0b45c62017-01-17 16:23:54 -050017#if !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_FUCHSIA) && \
Michael Ryleev938d65f2017-05-01 09:39:18 -070018 !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE) && !defined(OPENSSL_TRUSTY)
Adam Langleyd9e397b2015-01-22 14:27:53 -080019
20#include <assert.h>
21#include <errno.h>
22#include <fcntl.h>
David Benjamin1b249672016-12-06 18:25:50 -050023#include <stdio.h>
Adam Langleyd9e397b2015-01-22 14:27:53 -080024#include <string.h>
25#include <unistd.h>
26
Steven Valdez909b19f2016-11-21 15:35:44 -050027#if defined(OPENSSL_LINUX)
Robert Sloan572a4e22017-04-17 10:52:19 -070028#include <linux/random.h>
29#include <sys/ioctl.h>
Steven Valdez909b19f2016-11-21 15:35:44 -050030#include <sys/syscall.h>
31#endif
32
Adam Langleyd9e397b2015-01-22 14:27:53 -080033#include <openssl/thread.h>
34#include <openssl/mem.h>
35
Adam Langleye9ada862015-05-11 17:20:37 -070036#include "internal.h"
Robert Sloan9254e682017-04-24 09:42:06 -070037#include "../delocate.h"
38#include "../../internal.h"
Adam Langleye9ada862015-05-11 17:20:37 -070039
Adam Langleyd9e397b2015-01-22 14:27:53 -080040
Steven Valdez909b19f2016-11-21 15:35:44 -050041#if defined(OPENSSL_LINUX)
42
43#if defined(OPENSSL_X86_64)
44#define EXPECTED_SYS_getrandom 318
45#elif defined(OPENSSL_X86)
46#define EXPECTED_SYS_getrandom 355
47#elif defined(OPENSSL_AARCH64)
48#define EXPECTED_SYS_getrandom 278
49#elif defined(OPENSSL_ARM)
50#define EXPECTED_SYS_getrandom 384
51#elif defined(OPENSSL_PPC64LE)
52#define EXPECTED_SYS_getrandom 359
53#endif
54
55#if defined(EXPECTED_SYS_getrandom)
56#define USE_SYS_getrandom
57
58#if defined(SYS_getrandom)
59
60#if SYS_getrandom != EXPECTED_SYS_getrandom
61#error "system call number for getrandom is not the expected value"
62#endif
63
64#else /* SYS_getrandom */
65
66#define SYS_getrandom EXPECTED_SYS_getrandom
67
68#endif /* SYS_getrandom */
69
70#endif /* EXPECTED_SYS_getrandom */
71
72#if !defined(GRND_NONBLOCK)
73#define GRND_NONBLOCK 1
74#endif
75
76#endif /* OPENSSL_LINUX */
77
Robert Sloan9254e682017-04-24 09:42:06 -070078/* rand_lock is used to protect the |*_requested| variables. */
79DEFINE_STATIC_MUTEX(rand_lock);
Adam Langleyd9e397b2015-01-22 14:27:53 -080080
David Benjamin1b249672016-12-06 18:25:50 -050081/* The following constants are magic values of |urandom_fd|. */
Robert Sloan9254e682017-04-24 09:42:06 -070082static const int kUnset = 0;
David Benjamin1b249672016-12-06 18:25:50 -050083static const int kHaveGetrandom = -3;
84
85/* urandom_fd_requested is set by |RAND_set_urandom_fd|. It's protected by
Robert Sloan9254e682017-04-24 09:42:06 -070086 * |rand_lock|. */
87DEFINE_BSS_GET(int, urandom_fd_requested);
Adam Langleye9ada862015-05-11 17:20:37 -070088
Kenny Rootb8494592015-09-25 02:29:14 +000089/* urandom_fd is a file descriptor to /dev/urandom. It's protected by |once|. */
Robert Sloan9254e682017-04-24 09:42:06 -070090DEFINE_BSS_GET(int, urandom_fd);
Adam Langleyd9e397b2015-01-22 14:27:53 -080091
Robert Sloan9254e682017-04-24 09:42:06 -070092DEFINE_STATIC_ONCE(rand_once);
93
94#if defined(USE_SYS_getrandom) || defined(BORINGSSL_FIPS)
95/* message writes |msg| to stderr. We use this because referencing |stderr|
96 * with |fprintf| generates relocations, which is a problem inside the FIPS
97 * module. */
98static void message(const char *msg) {
99 ssize_t r;
100 do {
101 r = write(2, msg, strlen(msg));
102 } while (r == -1 && errno == EINTR);
103}
104#endif
Kenny Rootb8494592015-09-25 02:29:14 +0000105
106/* init_once initializes the state of this module to values previously
107 * requested. This is the only function that modifies |urandom_fd| and
108 * |urandom_buffering|, whose values may be read safely after calling the
109 * once. */
110static void init_once(void) {
Robert Sloan9254e682017-04-24 09:42:06 -0700111 CRYPTO_STATIC_MUTEX_lock_read(rand_lock_bss_get());
112 int fd = *urandom_fd_requested_bss_get();
113 CRYPTO_STATIC_MUTEX_unlock_read(rand_lock_bss_get());
Kenny Rootb8494592015-09-25 02:29:14 +0000114
Steven Valdez909b19f2016-11-21 15:35:44 -0500115#if defined(USE_SYS_getrandom)
Steven Valdez909b19f2016-11-21 15:35:44 -0500116 uint8_t dummy;
David Benjamin1b249672016-12-06 18:25:50 -0500117 long getrandom_ret =
118 syscall(SYS_getrandom, &dummy, sizeof(dummy), GRND_NONBLOCK);
Steven Valdez909b19f2016-11-21 15:35:44 -0500119
David Benjamin1b249672016-12-06 18:25:50 -0500120 if (getrandom_ret == 1) {
Robert Sloan9254e682017-04-24 09:42:06 -0700121 *urandom_fd_bss_get() = kHaveGetrandom;
David Benjamin1b249672016-12-06 18:25:50 -0500122 return;
123 } else if (getrandom_ret == -1 && errno == EAGAIN) {
Robert Sloan9254e682017-04-24 09:42:06 -0700124 message(
125 "getrandom indicates that the entropy pool has not been initialized. "
126 "Rather than continue with poor entropy, this process will block until "
127 "entropy is available.\n");
128
David Benjamin1b249672016-12-06 18:25:50 -0500129 do {
130 getrandom_ret =
131 syscall(SYS_getrandom, &dummy, sizeof(dummy), 0 /* no flags */);
132 } while (getrandom_ret == -1 && errno == EINTR);
133
134 if (getrandom_ret == 1) {
Robert Sloan9254e682017-04-24 09:42:06 -0700135 *urandom_fd_bss_get() = kHaveGetrandom;
David Benjamin1b249672016-12-06 18:25:50 -0500136 return;
137 }
138 }
139#endif /* USE_SYS_getrandom */
140
141 if (fd == kUnset) {
Kenny Rootb8494592015-09-25 02:29:14 +0000142 do {
143 fd = open("/dev/urandom", O_RDONLY);
144 } while (fd == -1 && errno == EINTR);
Adam Langleye9ada862015-05-11 17:20:37 -0700145 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800146
Kenny Rootb8494592015-09-25 02:29:14 +0000147 if (fd < 0) {
148 abort();
149 }
150
Robert Sloan9254e682017-04-24 09:42:06 -0700151 assert(kUnset == 0);
152 if (fd == kUnset) {
153 /* Because we want to keep |urandom_fd| in the BSS, we have to initialise
154 * it to zero. But zero is a valid file descriptor too. Thus if open
155 * returns zero for /dev/urandom, we dup it to get a non-zero number. */
156 fd = dup(fd);
157 close(kUnset);
158
159 if (fd <= 0) {
160 abort();
161 }
162 }
163
Robert Sloan572a4e22017-04-17 10:52:19 -0700164#if defined(BORINGSSL_FIPS)
165 /* In FIPS mode we ensure that the kernel has sufficient entropy before
166 * continuing. This is automatically handled by getrandom, which requires
167 * that the entropy pool has been initialised, but for urandom we have to
168 * poll. */
Robert Sloan572a4e22017-04-17 10:52:19 -0700169 for (;;) {
170 int entropy_bits;
171 if (ioctl(fd, RNDGETENTCNT, &entropy_bits)) {
Robert Sloan9254e682017-04-24 09:42:06 -0700172 message(
173 "RNDGETENTCNT on /dev/urandom failed. We cannot continue in this "
174 "case when in FIPS mode.\n");
Robert Sloan572a4e22017-04-17 10:52:19 -0700175 abort();
176 }
177
178 static const int kBitsNeeded = 256;
179 if (entropy_bits >= kBitsNeeded) {
180 break;
181 }
182
Robert Sloan572a4e22017-04-17 10:52:19 -0700183 usleep(250000);
184 }
185#endif
186
Kenny Rootb8494592015-09-25 02:29:14 +0000187 int flags = fcntl(fd, F_GETFD);
188 if (flags == -1) {
Adam Langley4139edb2016-01-13 15:00:54 -0800189 /* Native Client doesn't implement |fcntl|. */
190 if (errno != ENOSYS) {
191 abort();
192 }
193 } else {
194 flags |= FD_CLOEXEC;
195 if (fcntl(fd, F_SETFD, flags) == -1) {
196 abort();
197 }
Kenny Rootb8494592015-09-25 02:29:14 +0000198 }
Robert Sloan9254e682017-04-24 09:42:06 -0700199 *urandom_fd_bss_get() = fd;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800200}
201
Kenny Rootb8494592015-09-25 02:29:14 +0000202void RAND_set_urandom_fd(int fd) {
203 fd = dup(fd);
204 if (fd < 0) {
205 abort();
Adam Langleyd9e397b2015-01-22 14:27:53 -0800206 }
Kenny Rootb8494592015-09-25 02:29:14 +0000207
Robert Sloan9254e682017-04-24 09:42:06 -0700208 assert(kUnset == 0);
209 if (fd == kUnset) {
210 /* Because we want to keep |urandom_fd| in the BSS, we have to initialise
211 * it to zero. But zero is a valid file descriptor too. Thus if dup
212 * returned zero we dup it again to get a non-zero number. */
213 fd = dup(fd);
214 close(kUnset);
Kenny Rootb8494592015-09-25 02:29:14 +0000215
Robert Sloan9254e682017-04-24 09:42:06 -0700216 if (fd <= 0) {
217 abort();
218 }
219 }
220
221 CRYPTO_STATIC_MUTEX_lock_write(rand_lock_bss_get());
222 *urandom_fd_requested_bss_get() = fd;
223 CRYPTO_STATIC_MUTEX_unlock_write(rand_lock_bss_get());
224
225 CRYPTO_once(rand_once_bss_get(), init_once);
226 if (*urandom_fd_bss_get() == kHaveGetrandom) {
David Benjamin1b249672016-12-06 18:25:50 -0500227 close(fd);
Robert Sloan9254e682017-04-24 09:42:06 -0700228 } else if (*urandom_fd_bss_get() != fd) {
Kenny Rootb8494592015-09-25 02:29:14 +0000229 abort(); // Already initialized.
Adam Langleyd9e397b2015-01-22 14:27:53 -0800230 }
Kenny Rootb8494592015-09-25 02:29:14 +0000231}
232
Robert Sloan69939df2017-01-09 10:53:07 -0800233#if defined(USE_SYS_getrandom) && defined(__has_feature)
234#if __has_feature(memory_sanitizer)
235void __msan_unpoison(void *, size_t);
236#endif
237#endif
238
Steven Valdez909b19f2016-11-21 15:35:44 -0500239/* fill_with_entropy writes |len| bytes of entropy into |out|. It returns one
240 * on success and zero on error. */
241static char fill_with_entropy(uint8_t *out, size_t len) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800242 while (len > 0) {
Robert Sloan69939df2017-01-09 10:53:07 -0800243 ssize_t r;
244
Robert Sloan9254e682017-04-24 09:42:06 -0700245 if (*urandom_fd_bss_get() == kHaveGetrandom) {
David Benjamin1b249672016-12-06 18:25:50 -0500246#if defined(USE_SYS_getrandom)
247 do {
248 r = syscall(SYS_getrandom, out, len, 0 /* no flags */);
249 } while (r == -1 && errno == EINTR);
Robert Sloan69939df2017-01-09 10:53:07 -0800250
251#if defined(__has_feature)
252#if __has_feature(memory_sanitizer)
253 if (r > 0) {
254 /* MSAN doesn't recognise |syscall| and thus doesn't notice that we
255 * have initialised the output buffer. */
256 __msan_unpoison(out, r);
257 }
258#endif /* memory_sanitizer */
259#endif /*__has_feature */
260
261#else /* USE_SYS_getrandom */
David Benjamin1b249672016-12-06 18:25:50 -0500262 abort();
263#endif
264 } else {
265 do {
Robert Sloan9254e682017-04-24 09:42:06 -0700266 r = read(*urandom_fd_bss_get(), out, len);
David Benjamin1b249672016-12-06 18:25:50 -0500267 } while (r == -1 && errno == EINTR);
268 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800269
270 if (r <= 0) {
271 return 0;
272 }
273 out += r;
274 len -= r;
275 }
276
277 return 1;
278}
279
Kenny Rootb8494592015-09-25 02:29:14 +0000280/* CRYPTO_sysrand puts |requested| random bytes into |out|. */
281void CRYPTO_sysrand(uint8_t *out, size_t requested) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800282 if (requested == 0) {
Adam Langleye9ada862015-05-11 17:20:37 -0700283 return;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800284 }
285
Robert Sloan9254e682017-04-24 09:42:06 -0700286 CRYPTO_once(rand_once_bss_get(), init_once);
Kenny Roota04d78d2015-09-25 00:26:37 +0000287
Steven Valdez909b19f2016-11-21 15:35:44 -0500288 if (!fill_with_entropy(out, requested)) {
Kenny Roota04d78d2015-09-25 00:26:37 +0000289 abort();
Adam Langley1e4884f2015-09-24 10:57:52 -0700290 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800291}
292
Steven Valdezb0b45c62017-01-17 16:23:54 -0500293#endif /* !OPENSSL_WINDOWS && !defined(OPENSSL_FUCHSIA) && \
Michael Ryleev938d65f2017-05-01 09:39:18 -0700294 !BORINGSSL_UNSAFE_DETERMINISTIC_MODE && !OPENSSL_TRUSTY */