Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [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 | |
| 17 | #if !defined(OPENSSL_WINDOWS) |
| 18 | |
| 19 | #include <assert.h> |
| 20 | #include <errno.h> |
| 21 | #include <fcntl.h> |
| 22 | #include <stdlib.h> |
| 23 | #include <string.h> |
| 24 | #include <unistd.h> |
| 25 | |
| 26 | #include <openssl/thread.h> |
| 27 | #include <openssl/mem.h> |
| 28 | |
| 29 | |
| 30 | /* This file implements a PRNG by reading from /dev/urandom, optionally with a |
| 31 | * fork-safe buffer. |
| 32 | * |
| 33 | * If buffering is enabled then it maintains a global, linked list of buffers. |
| 34 | * Threads which need random bytes grab a buffer from the list under a lock and |
| 35 | * copy out the bytes that they need. In the rare case that the buffer is |
| 36 | * empty, it's refilled from /dev/urandom outside of the lock. |
| 37 | * |
| 38 | * Large requests are always serviced from /dev/urandom directly. |
| 39 | * |
| 40 | * Each buffer contains the PID of the process that created it and it's tested |
| 41 | * against the current PID each time. Thus processes that fork will discard all |
| 42 | * the buffers filled by the parent process. There are two problems with this: |
| 43 | * |
| 44 | * 1) glibc maintains a cache of the current PID+PPID and, if this cache isn't |
| 45 | * correctly invalidated, the getpid() will continue to believe that |
| 46 | * it's the old process. Glibc depends on the glibc wrappers for fork, |
| 47 | * vfork and clone being used in order to invalidate the getpid() cache. |
| 48 | * |
| 49 | * 2) If a process forks, dies and then its child forks, it's possible that |
| 50 | * the third process will end up with the same PID as the original process. |
| 51 | * If the second process never used any random values then this will mean |
| 52 | * that the third process has stale, cached values and won't notice. |
| 53 | */ |
| 54 | |
| 55 | /* BUF_SIZE is intended to be a 4K allocation with malloc overhead. struct |
| 56 | * rand_buffer also fits in this space and the remainder is entropy. */ |
| 57 | #define BUF_SIZE (4096 - 16) |
| 58 | |
| 59 | /* rand_buffer contains unused, random bytes. These structures form a linked |
| 60 | * list via the |next| pointer, which is NULL in the final element. */ |
| 61 | struct rand_buffer { |
| 62 | size_t used; /* used contains the number of bytes of |rand| that have |
| 63 | been consumed. */ |
| 64 | struct rand_buffer *next; |
| 65 | pid_t pid; /* pid contains the pid at the time that the buffer was |
| 66 | created so that data is not duplicated after a fork. */ |
| 67 | pid_t ppid; /* ppid contains the parent pid in order to try and reduce |
| 68 | the possibility of duplicated PID confusing the |
| 69 | detection of a fork. */ |
| 70 | uint8_t rand[]; |
| 71 | }; |
| 72 | |
| 73 | /* rand_bytes_per_buf is the number of actual entropy bytes in a buffer. */ |
| 74 | static const size_t rand_bytes_per_buf = BUF_SIZE - sizeof(struct rand_buffer); |
| 75 | |
| 76 | /* list_head is the start of a global, linked-list of rand_buffer objects. It's |
| 77 | * protected by CRYPTO_LOCK_RAND. */ |
| 78 | static struct rand_buffer *list_head; |
| 79 | |
| 80 | /* urandom_fd is a file descriptor to /dev/urandom. It's protected by |
| 81 | * CRYPTO_LOCK_RAND. */ |
| 82 | static int urandom_fd = -2; |
| 83 | |
| 84 | /* urandom_buffering controls whether buffering is enabled (1) or not (0). This |
| 85 | * is protected by CRYPTO_LOCK_RAND. */ |
| 86 | static int urandom_buffering = 0; |
| 87 | |
| 88 | /* urandom_get_fd_locked returns a file descriptor to /dev/urandom. The caller |
| 89 | * of this function must hold CRYPTO_LOCK_RAND. */ |
David Benjamin | c44d2f4 | 2014-08-20 16:24:00 -0400 | [diff] [blame] | 90 | static int urandom_get_fd_locked(void) { |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 91 | if (urandom_fd != -2) |
| 92 | return urandom_fd; |
| 93 | |
Adam Langley | 0113a4f | 2014-07-10 17:07:14 -0700 | [diff] [blame] | 94 | urandom_fd = open("/dev/urandom", O_RDONLY); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 95 | return urandom_fd; |
| 96 | } |
| 97 | |
| 98 | /* RAND_cleanup frees all buffers, closes any cached file descriptor |
| 99 | * and resets the global state. */ |
| 100 | void RAND_cleanup(void) { |
| 101 | struct rand_buffer *cur; |
| 102 | |
| 103 | CRYPTO_w_lock(CRYPTO_LOCK_RAND); |
| 104 | while ((cur = list_head)) { |
| 105 | list_head = cur->next; |
| 106 | OPENSSL_free(cur); |
| 107 | } |
| 108 | if (urandom_fd >= 0) { |
| 109 | close(urandom_fd); |
| 110 | } |
| 111 | urandom_fd = -2; |
| 112 | list_head = NULL; |
| 113 | CRYPTO_w_unlock(CRYPTO_LOCK_RAND); |
| 114 | } |
| 115 | |
| 116 | /* read_full reads exactly |len| bytes from |fd| into |out| and returns 1. In |
| 117 | * the case of an error it returns 0. */ |
| 118 | static char read_full(int fd, uint8_t *out, size_t len) { |
| 119 | ssize_t r; |
| 120 | |
| 121 | while (len > 0) { |
| 122 | do { |
| 123 | r = read(fd, out, len); |
| 124 | } while (r == -1 && errno == EINTR); |
| 125 | |
| 126 | if (r <= 0) { |
| 127 | return 0; |
| 128 | } |
| 129 | out += r; |
| 130 | len -= r; |
| 131 | } |
| 132 | |
| 133 | return 1; |
| 134 | } |
| 135 | |
| 136 | /* urandom_rand_pseudo_bytes puts |num| random bytes into |out|. It returns |
| 137 | * one on success and zero otherwise. */ |
| 138 | int RAND_bytes(uint8_t *out, size_t requested) { |
| 139 | int fd; |
| 140 | struct rand_buffer *buf; |
| 141 | size_t todo; |
| 142 | pid_t pid, ppid; |
| 143 | |
| 144 | if (requested == 0) { |
| 145 | return 1; |
| 146 | } |
| 147 | |
| 148 | CRYPTO_w_lock(CRYPTO_LOCK_RAND); |
| 149 | fd = urandom_get_fd_locked(); |
| 150 | |
| 151 | if (fd < 0) { |
| 152 | CRYPTO_w_unlock(CRYPTO_LOCK_RAND); |
| 153 | abort(); |
| 154 | return 0; |
| 155 | } |
| 156 | |
| 157 | /* If buffering is not enabled, or if the request is large, then the |
| 158 | * result comes directly from urandom. */ |
| 159 | if (!urandom_buffering || requested > BUF_SIZE / 2) { |
| 160 | CRYPTO_w_unlock(CRYPTO_LOCK_RAND); |
| 161 | if (!read_full(fd, out, requested)) { |
| 162 | abort(); |
| 163 | return 0; |
| 164 | } |
| 165 | return 1; |
| 166 | } |
| 167 | |
| 168 | pid = getpid(); |
| 169 | ppid = getppid(); |
| 170 | |
| 171 | for (;;) { |
| 172 | buf = list_head; |
| 173 | if (buf && buf->pid == pid && buf->ppid == ppid && |
| 174 | rand_bytes_per_buf - buf->used >= requested) { |
| 175 | memcpy(out, &buf->rand[buf->used], requested); |
| 176 | buf->used += requested; |
| 177 | CRYPTO_w_unlock(CRYPTO_LOCK_RAND); |
| 178 | return 1; |
| 179 | } |
| 180 | |
| 181 | /* If we don't immediately have enough entropy with the correct |
| 182 | * PID, remove the buffer from the list in order to gain |
| 183 | * exclusive access and unlock. */ |
| 184 | if (buf) { |
| 185 | list_head = buf->next; |
| 186 | } |
| 187 | CRYPTO_w_unlock(CRYPTO_LOCK_RAND); |
| 188 | |
| 189 | if (!buf) { |
| 190 | buf = (struct rand_buffer *)OPENSSL_malloc(BUF_SIZE); |
| 191 | /* The buffer doesn't contain any random bytes yet |
| 192 | * so we mark it as fully used so that it will be |
| 193 | * filled below. */ |
| 194 | buf->used = rand_bytes_per_buf; |
| 195 | buf->next = NULL; |
| 196 | buf->pid = pid; |
| 197 | buf->ppid = ppid; |
| 198 | } |
| 199 | |
| 200 | if (buf->pid == pid && buf->ppid == ppid) { |
| 201 | break; |
| 202 | } |
| 203 | |
| 204 | /* We have forked and so cannot use these bytes as they |
| 205 | * may have been used in another process. */ |
| 206 | OPENSSL_free(buf); |
| 207 | CRYPTO_w_lock(CRYPTO_LOCK_RAND); |
| 208 | } |
| 209 | |
| 210 | while (requested > 0) { |
| 211 | todo = rand_bytes_per_buf - buf->used; |
| 212 | if (todo > requested) { |
| 213 | todo = requested; |
| 214 | } |
| 215 | memcpy(out, &buf->rand[buf->used], todo); |
| 216 | requested -= todo; |
| 217 | out += todo; |
| 218 | buf->used += todo; |
| 219 | |
| 220 | if (buf->used < rand_bytes_per_buf) { |
| 221 | break; |
| 222 | } |
| 223 | |
| 224 | if (!read_full(fd, buf->rand, rand_bytes_per_buf)) { |
| 225 | OPENSSL_free(buf); |
| 226 | abort(); |
| 227 | return 0; |
| 228 | } |
| 229 | |
| 230 | buf->used = 0; |
| 231 | } |
| 232 | |
| 233 | CRYPTO_w_lock(CRYPTO_LOCK_RAND); |
| 234 | assert(list_head != buf); |
| 235 | buf->next = list_head; |
| 236 | list_head = buf; |
| 237 | CRYPTO_w_unlock(CRYPTO_LOCK_RAND); |
| 238 | return 1; |
| 239 | } |
| 240 | |
| 241 | #endif /* !OPENSSL_WINDOWS */ |