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