Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 1 | /* 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 | |
Robert Sloan | 8ff0355 | 2017-06-14 12:40:58 -0700 | [diff] [blame^] | 15 | #if !defined(_GNU_SOURCE) |
| 16 | #define _GNU_SOURCE /* needed for syscall() on Linux. */ |
| 17 | #endif |
| 18 | |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 19 | #include <openssl/rand.h> |
| 20 | |
Steven Valdez | b0b45c6 | 2017-01-17 16:23:54 -0500 | [diff] [blame] | 21 | #if !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_FUCHSIA) && \ |
Michael Ryleev | 938d65f | 2017-05-01 09:39:18 -0700 | [diff] [blame] | 22 | !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE) && !defined(OPENSSL_TRUSTY) |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 23 | |
| 24 | #include <assert.h> |
| 25 | #include <errno.h> |
| 26 | #include <fcntl.h> |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 27 | #include <stdio.h> |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 28 | #include <string.h> |
| 29 | #include <unistd.h> |
| 30 | |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 31 | #if defined(OPENSSL_LINUX) |
Robert Sloan | 572a4e2 | 2017-04-17 10:52:19 -0700 | [diff] [blame] | 32 | #include <linux/random.h> |
| 33 | #include <sys/ioctl.h> |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 34 | #include <sys/syscall.h> |
| 35 | #endif |
| 36 | |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 37 | #include <openssl/thread.h> |
| 38 | #include <openssl/mem.h> |
| 39 | |
Adam Langley | e9ada86 | 2015-05-11 17:20:37 -0700 | [diff] [blame] | 40 | #include "internal.h" |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 41 | #include "../delocate.h" |
| 42 | #include "../../internal.h" |
Adam Langley | e9ada86 | 2015-05-11 17:20:37 -0700 | [diff] [blame] | 43 | |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 44 | |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 45 | #if defined(OPENSSL_LINUX) |
| 46 | |
| 47 | #if defined(OPENSSL_X86_64) |
| 48 | #define EXPECTED_SYS_getrandom 318 |
| 49 | #elif defined(OPENSSL_X86) |
| 50 | #define EXPECTED_SYS_getrandom 355 |
| 51 | #elif defined(OPENSSL_AARCH64) |
| 52 | #define EXPECTED_SYS_getrandom 278 |
| 53 | #elif defined(OPENSSL_ARM) |
| 54 | #define EXPECTED_SYS_getrandom 384 |
| 55 | #elif defined(OPENSSL_PPC64LE) |
| 56 | #define EXPECTED_SYS_getrandom 359 |
| 57 | #endif |
| 58 | |
| 59 | #if defined(EXPECTED_SYS_getrandom) |
| 60 | #define USE_SYS_getrandom |
| 61 | |
| 62 | #if defined(SYS_getrandom) |
| 63 | |
| 64 | #if SYS_getrandom != EXPECTED_SYS_getrandom |
| 65 | #error "system call number for getrandom is not the expected value" |
| 66 | #endif |
| 67 | |
| 68 | #else /* SYS_getrandom */ |
| 69 | |
| 70 | #define SYS_getrandom EXPECTED_SYS_getrandom |
| 71 | |
| 72 | #endif /* SYS_getrandom */ |
| 73 | |
| 74 | #endif /* EXPECTED_SYS_getrandom */ |
| 75 | |
| 76 | #if !defined(GRND_NONBLOCK) |
| 77 | #define GRND_NONBLOCK 1 |
| 78 | #endif |
| 79 | |
| 80 | #endif /* OPENSSL_LINUX */ |
| 81 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 82 | /* rand_lock is used to protect the |*_requested| variables. */ |
| 83 | DEFINE_STATIC_MUTEX(rand_lock); |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 84 | |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 85 | /* The following constants are magic values of |urandom_fd|. */ |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 86 | static const int kUnset = 0; |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 87 | static const int kHaveGetrandom = -3; |
| 88 | |
| 89 | /* urandom_fd_requested is set by |RAND_set_urandom_fd|. It's protected by |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 90 | * |rand_lock|. */ |
| 91 | DEFINE_BSS_GET(int, urandom_fd_requested); |
Adam Langley | e9ada86 | 2015-05-11 17:20:37 -0700 | [diff] [blame] | 92 | |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 93 | /* urandom_fd is a file descriptor to /dev/urandom. It's protected by |once|. */ |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 94 | DEFINE_BSS_GET(int, urandom_fd); |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 95 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 96 | DEFINE_STATIC_ONCE(rand_once); |
| 97 | |
| 98 | #if defined(USE_SYS_getrandom) || defined(BORINGSSL_FIPS) |
| 99 | /* message writes |msg| to stderr. We use this because referencing |stderr| |
| 100 | * with |fprintf| generates relocations, which is a problem inside the FIPS |
| 101 | * module. */ |
| 102 | static void message(const char *msg) { |
| 103 | ssize_t r; |
| 104 | do { |
| 105 | r = write(2, msg, strlen(msg)); |
| 106 | } while (r == -1 && errno == EINTR); |
| 107 | } |
| 108 | #endif |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 109 | |
| 110 | /* init_once initializes the state of this module to values previously |
| 111 | * requested. This is the only function that modifies |urandom_fd| and |
| 112 | * |urandom_buffering|, whose values may be read safely after calling the |
| 113 | * once. */ |
| 114 | static void init_once(void) { |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 115 | CRYPTO_STATIC_MUTEX_lock_read(rand_lock_bss_get()); |
| 116 | int fd = *urandom_fd_requested_bss_get(); |
| 117 | CRYPTO_STATIC_MUTEX_unlock_read(rand_lock_bss_get()); |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 118 | |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 119 | #if defined(USE_SYS_getrandom) |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 120 | uint8_t dummy; |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 121 | long getrandom_ret = |
| 122 | syscall(SYS_getrandom, &dummy, sizeof(dummy), GRND_NONBLOCK); |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 123 | |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 124 | if (getrandom_ret == 1) { |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 125 | *urandom_fd_bss_get() = kHaveGetrandom; |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 126 | return; |
| 127 | } else if (getrandom_ret == -1 && errno == EAGAIN) { |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 128 | message( |
| 129 | "getrandom indicates that the entropy pool has not been initialized. " |
| 130 | "Rather than continue with poor entropy, this process will block until " |
| 131 | "entropy is available.\n"); |
| 132 | |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 133 | do { |
| 134 | getrandom_ret = |
| 135 | syscall(SYS_getrandom, &dummy, sizeof(dummy), 0 /* no flags */); |
| 136 | } while (getrandom_ret == -1 && errno == EINTR); |
| 137 | |
| 138 | if (getrandom_ret == 1) { |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 139 | *urandom_fd_bss_get() = kHaveGetrandom; |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 140 | return; |
| 141 | } |
| 142 | } |
| 143 | #endif /* USE_SYS_getrandom */ |
| 144 | |
| 145 | if (fd == kUnset) { |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 146 | do { |
| 147 | fd = open("/dev/urandom", O_RDONLY); |
| 148 | } while (fd == -1 && errno == EINTR); |
Adam Langley | e9ada86 | 2015-05-11 17:20:37 -0700 | [diff] [blame] | 149 | } |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 150 | |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 151 | if (fd < 0) { |
| 152 | abort(); |
| 153 | } |
| 154 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 155 | assert(kUnset == 0); |
| 156 | if (fd == kUnset) { |
| 157 | /* Because we want to keep |urandom_fd| in the BSS, we have to initialise |
| 158 | * it to zero. But zero is a valid file descriptor too. Thus if open |
| 159 | * returns zero for /dev/urandom, we dup it to get a non-zero number. */ |
| 160 | fd = dup(fd); |
| 161 | close(kUnset); |
| 162 | |
| 163 | if (fd <= 0) { |
| 164 | abort(); |
| 165 | } |
| 166 | } |
| 167 | |
Robert Sloan | 572a4e2 | 2017-04-17 10:52:19 -0700 | [diff] [blame] | 168 | #if defined(BORINGSSL_FIPS) |
| 169 | /* In FIPS mode we ensure that the kernel has sufficient entropy before |
| 170 | * continuing. This is automatically handled by getrandom, which requires |
| 171 | * that the entropy pool has been initialised, but for urandom we have to |
| 172 | * poll. */ |
Robert Sloan | 572a4e2 | 2017-04-17 10:52:19 -0700 | [diff] [blame] | 173 | for (;;) { |
| 174 | int entropy_bits; |
| 175 | if (ioctl(fd, RNDGETENTCNT, &entropy_bits)) { |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 176 | message( |
| 177 | "RNDGETENTCNT on /dev/urandom failed. We cannot continue in this " |
| 178 | "case when in FIPS mode.\n"); |
Robert Sloan | 572a4e2 | 2017-04-17 10:52:19 -0700 | [diff] [blame] | 179 | abort(); |
| 180 | } |
| 181 | |
| 182 | static const int kBitsNeeded = 256; |
| 183 | if (entropy_bits >= kBitsNeeded) { |
| 184 | break; |
| 185 | } |
| 186 | |
Robert Sloan | 572a4e2 | 2017-04-17 10:52:19 -0700 | [diff] [blame] | 187 | usleep(250000); |
| 188 | } |
| 189 | #endif |
| 190 | |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 191 | int flags = fcntl(fd, F_GETFD); |
| 192 | if (flags == -1) { |
Adam Langley | 4139edb | 2016-01-13 15:00:54 -0800 | [diff] [blame] | 193 | /* Native Client doesn't implement |fcntl|. */ |
| 194 | if (errno != ENOSYS) { |
| 195 | abort(); |
| 196 | } |
| 197 | } else { |
| 198 | flags |= FD_CLOEXEC; |
| 199 | if (fcntl(fd, F_SETFD, flags) == -1) { |
| 200 | abort(); |
| 201 | } |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 202 | } |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 203 | *urandom_fd_bss_get() = fd; |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 204 | } |
| 205 | |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 206 | void RAND_set_urandom_fd(int fd) { |
| 207 | fd = dup(fd); |
| 208 | if (fd < 0) { |
| 209 | abort(); |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 210 | } |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 211 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 212 | assert(kUnset == 0); |
| 213 | if (fd == kUnset) { |
| 214 | /* Because we want to keep |urandom_fd| in the BSS, we have to initialise |
| 215 | * it to zero. But zero is a valid file descriptor too. Thus if dup |
| 216 | * returned zero we dup it again to get a non-zero number. */ |
| 217 | fd = dup(fd); |
| 218 | close(kUnset); |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 219 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 220 | if (fd <= 0) { |
| 221 | abort(); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | CRYPTO_STATIC_MUTEX_lock_write(rand_lock_bss_get()); |
| 226 | *urandom_fd_requested_bss_get() = fd; |
| 227 | CRYPTO_STATIC_MUTEX_unlock_write(rand_lock_bss_get()); |
| 228 | |
| 229 | CRYPTO_once(rand_once_bss_get(), init_once); |
| 230 | if (*urandom_fd_bss_get() == kHaveGetrandom) { |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 231 | close(fd); |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 232 | } else if (*urandom_fd_bss_get() != fd) { |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 233 | abort(); // Already initialized. |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 234 | } |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 235 | } |
| 236 | |
Robert Sloan | 8ff0355 | 2017-06-14 12:40:58 -0700 | [diff] [blame^] | 237 | #if defined(USE_SYS_getrandom) && defined(OPENSSL_MSAN) |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 238 | void __msan_unpoison(void *, size_t); |
| 239 | #endif |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 240 | |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 241 | /* fill_with_entropy writes |len| bytes of entropy into |out|. It returns one |
| 242 | * on success and zero on error. */ |
| 243 | static char fill_with_entropy(uint8_t *out, size_t len) { |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 244 | while (len > 0) { |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 245 | ssize_t r; |
| 246 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 247 | if (*urandom_fd_bss_get() == kHaveGetrandom) { |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 248 | #if defined(USE_SYS_getrandom) |
| 249 | do { |
| 250 | r = syscall(SYS_getrandom, out, len, 0 /* no flags */); |
| 251 | } while (r == -1 && errno == EINTR); |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 252 | |
Robert Sloan | 8ff0355 | 2017-06-14 12:40:58 -0700 | [diff] [blame^] | 253 | #if defined(OPENSSL_MSAN) |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 254 | if (r > 0) { |
| 255 | /* MSAN doesn't recognise |syscall| and thus doesn't notice that we |
| 256 | * have initialised the output buffer. */ |
| 257 | __msan_unpoison(out, r); |
| 258 | } |
Robert Sloan | 8ff0355 | 2017-06-14 12:40:58 -0700 | [diff] [blame^] | 259 | #endif /* OPENSSL_MSAN */ |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 260 | |
| 261 | #else /* USE_SYS_getrandom */ |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 262 | abort(); |
| 263 | #endif |
| 264 | } else { |
| 265 | do { |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 266 | r = read(*urandom_fd_bss_get(), out, len); |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 267 | } while (r == -1 && errno == EINTR); |
| 268 | } |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 269 | |
| 270 | if (r <= 0) { |
| 271 | return 0; |
| 272 | } |
| 273 | out += r; |
| 274 | len -= r; |
| 275 | } |
| 276 | |
| 277 | return 1; |
| 278 | } |
| 279 | |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 280 | /* CRYPTO_sysrand puts |requested| random bytes into |out|. */ |
| 281 | void CRYPTO_sysrand(uint8_t *out, size_t requested) { |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 282 | if (requested == 0) { |
Adam Langley | e9ada86 | 2015-05-11 17:20:37 -0700 | [diff] [blame] | 283 | return; |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 284 | } |
| 285 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 286 | CRYPTO_once(rand_once_bss_get(), init_once); |
Kenny Root | a04d78d | 2015-09-25 00:26:37 +0000 | [diff] [blame] | 287 | |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 288 | if (!fill_with_entropy(out, requested)) { |
Kenny Root | a04d78d | 2015-09-25 00:26:37 +0000 | [diff] [blame] | 289 | abort(); |
Adam Langley | 1e4884f | 2015-09-24 10:57:52 -0700 | [diff] [blame] | 290 | } |
Robert Sloan | 8ff0355 | 2017-06-14 12:40:58 -0700 | [diff] [blame^] | 291 | |
| 292 | #if defined(BORINGSSL_FIPS_BREAK_CRNG) |
| 293 | // This breaks the "continuous random number generator test" defined in FIPS |
| 294 | // 140-2, section 4.9.2, and implemented in rand_get_seed(). |
| 295 | OPENSSL_memset(out, 0, requested); |
| 296 | #endif |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 297 | } |
| 298 | |
Steven Valdez | b0b45c6 | 2017-01-17 16:23:54 -0500 | [diff] [blame] | 299 | #endif /* !OPENSSL_WINDOWS && !defined(OPENSSL_FUCHSIA) && \ |
Michael Ryleev | 938d65f | 2017-05-01 09:39:18 -0700 | [diff] [blame] | 300 | !BORINGSSL_UNSAFE_DETERMINISTIC_MODE && !OPENSSL_TRUSTY */ |