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 | |
| 15 | #include <openssl/rand.h> |
| 16 | |
Steven Valdez | b0b45c6 | 2017-01-17 16:23:54 -0500 | [diff] [blame] | 17 | #if !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_FUCHSIA) && \ |
Michael Ryleev | 938d65f | 2017-05-01 09:39:18 -0700 | [diff] [blame] | 18 | !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE) && !defined(OPENSSL_TRUSTY) |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 19 | |
| 20 | #include <assert.h> |
| 21 | #include <errno.h> |
| 22 | #include <fcntl.h> |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 23 | #include <stdio.h> |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 24 | #include <string.h> |
| 25 | #include <unistd.h> |
| 26 | |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 27 | #if defined(OPENSSL_LINUX) |
Robert Sloan | 572a4e2 | 2017-04-17 10:52:19 -0700 | [diff] [blame] | 28 | #include <linux/random.h> |
| 29 | #include <sys/ioctl.h> |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 30 | #include <sys/syscall.h> |
| 31 | #endif |
| 32 | |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 33 | #include <openssl/thread.h> |
| 34 | #include <openssl/mem.h> |
| 35 | |
Adam Langley | e9ada86 | 2015-05-11 17:20:37 -0700 | [diff] [blame] | 36 | #include "internal.h" |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 37 | #include "../delocate.h" |
| 38 | #include "../../internal.h" |
Adam Langley | e9ada86 | 2015-05-11 17:20:37 -0700 | [diff] [blame] | 39 | |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 40 | |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 41 | #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 Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 78 | /* rand_lock is used to protect the |*_requested| variables. */ |
| 79 | DEFINE_STATIC_MUTEX(rand_lock); |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 80 | |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 81 | /* The following constants are magic values of |urandom_fd|. */ |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 82 | static const int kUnset = 0; |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 83 | static const int kHaveGetrandom = -3; |
| 84 | |
| 85 | /* 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] | 86 | * |rand_lock|. */ |
| 87 | DEFINE_BSS_GET(int, urandom_fd_requested); |
Adam Langley | e9ada86 | 2015-05-11 17:20:37 -0700 | [diff] [blame] | 88 | |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 89 | /* 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] | 90 | DEFINE_BSS_GET(int, urandom_fd); |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 91 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 92 | DEFINE_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. */ |
| 98 | static 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 Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 105 | |
| 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. */ |
| 110 | static void init_once(void) { |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 111 | 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 Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 114 | |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 115 | #if defined(USE_SYS_getrandom) |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 116 | uint8_t dummy; |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 117 | long getrandom_ret = |
| 118 | syscall(SYS_getrandom, &dummy, sizeof(dummy), GRND_NONBLOCK); |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 119 | |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 120 | if (getrandom_ret == 1) { |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 121 | *urandom_fd_bss_get() = kHaveGetrandom; |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 122 | return; |
| 123 | } else if (getrandom_ret == -1 && errno == EAGAIN) { |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 124 | 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 Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 129 | 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 Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 135 | *urandom_fd_bss_get() = kHaveGetrandom; |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 136 | return; |
| 137 | } |
| 138 | } |
| 139 | #endif /* USE_SYS_getrandom */ |
| 140 | |
| 141 | if (fd == kUnset) { |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 142 | do { |
| 143 | fd = open("/dev/urandom", O_RDONLY); |
| 144 | } while (fd == -1 && errno == EINTR); |
Adam Langley | e9ada86 | 2015-05-11 17:20:37 -0700 | [diff] [blame] | 145 | } |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 146 | |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 147 | if (fd < 0) { |
| 148 | abort(); |
| 149 | } |
| 150 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 151 | 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 Sloan | 572a4e2 | 2017-04-17 10:52:19 -0700 | [diff] [blame] | 164 | #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 Sloan | 572a4e2 | 2017-04-17 10:52:19 -0700 | [diff] [blame] | 169 | for (;;) { |
| 170 | int entropy_bits; |
| 171 | if (ioctl(fd, RNDGETENTCNT, &entropy_bits)) { |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 172 | message( |
| 173 | "RNDGETENTCNT on /dev/urandom failed. We cannot continue in this " |
| 174 | "case when in FIPS mode.\n"); |
Robert Sloan | 572a4e2 | 2017-04-17 10:52:19 -0700 | [diff] [blame] | 175 | abort(); |
| 176 | } |
| 177 | |
| 178 | static const int kBitsNeeded = 256; |
| 179 | if (entropy_bits >= kBitsNeeded) { |
| 180 | break; |
| 181 | } |
| 182 | |
Robert Sloan | 572a4e2 | 2017-04-17 10:52:19 -0700 | [diff] [blame] | 183 | usleep(250000); |
| 184 | } |
| 185 | #endif |
| 186 | |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 187 | int flags = fcntl(fd, F_GETFD); |
| 188 | if (flags == -1) { |
Adam Langley | 4139edb | 2016-01-13 15:00:54 -0800 | [diff] [blame] | 189 | /* 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 Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 198 | } |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 199 | *urandom_fd_bss_get() = fd; |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 200 | } |
| 201 | |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 202 | void RAND_set_urandom_fd(int fd) { |
| 203 | fd = dup(fd); |
| 204 | if (fd < 0) { |
| 205 | abort(); |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 206 | } |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 207 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 208 | 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 Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 215 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 216 | 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 Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 227 | close(fd); |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 228 | } else if (*urandom_fd_bss_get() != fd) { |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 229 | abort(); // Already initialized. |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 230 | } |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 231 | } |
| 232 | |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 233 | #if defined(USE_SYS_getrandom) && defined(__has_feature) |
| 234 | #if __has_feature(memory_sanitizer) |
| 235 | void __msan_unpoison(void *, size_t); |
| 236 | #endif |
| 237 | #endif |
| 238 | |
Steven Valdez | 909b19f | 2016-11-21 15:35:44 -0500 | [diff] [blame] | 239 | /* fill_with_entropy writes |len| bytes of entropy into |out|. It returns one |
| 240 | * on success and zero on error. */ |
| 241 | static char fill_with_entropy(uint8_t *out, size_t len) { |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 242 | while (len > 0) { |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 243 | ssize_t r; |
| 244 | |
Robert Sloan | 9254e68 | 2017-04-24 09:42:06 -0700 | [diff] [blame] | 245 | if (*urandom_fd_bss_get() == kHaveGetrandom) { |
David Benjamin | 1b24967 | 2016-12-06 18:25:50 -0500 | [diff] [blame] | 246 | #if defined(USE_SYS_getrandom) |
| 247 | do { |
| 248 | r = syscall(SYS_getrandom, out, len, 0 /* no flags */); |
| 249 | } while (r == -1 && errno == EINTR); |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 250 | |
| 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 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 | } |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 291 | } |
| 292 | |
Steven Valdez | b0b45c6 | 2017-01-17 16:23:54 -0500 | [diff] [blame] | 293 | #endif /* !OPENSSL_WINDOWS && !defined(OPENSSL_FUCHSIA) && \ |
Michael Ryleev | 938d65f | 2017-05-01 09:39:18 -0700 | [diff] [blame] | 294 | !BORINGSSL_UNSAFE_DETERMINISTIC_MODE && !OPENSSL_TRUSTY */ |