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 <string.h> 23 #include <unistd.h> 24 25 #include <openssl/thread.h> 26 #include <openssl/mem.h> 27 28 #include "internal.h" 29 #include "../internal.h" 30 31 32 /* This file implements a PRNG by reading from /dev/urandom, optionally with a 33 * buffer, which is unsafe across |fork|. */ 34 35 #define BUF_SIZE 4096 36 37 /* rand_buffer contains unused, random bytes, some of which may have been 38 * consumed already. */ 39 struct rand_buffer { 40 size_t used; 41 uint8_t rand[BUF_SIZE]; 42 }; 43 44 /* requested_lock is used to protect the |*_requested| variables. */ 45 static struct CRYPTO_STATIC_MUTEX requested_lock = CRYPTO_STATIC_MUTEX_INIT; 46 47 /* urandom_fd_requested is set by |RAND_set_urandom_fd|. It's protected by 48 * |requested_lock|. */ 49 static int urandom_fd_requested = -2; 50 51 /* urandom_fd is a file descriptor to /dev/urandom. It's protected by |once|. */ 52 static int urandom_fd = -2; 53 54 /* urandom_buffering_requested is set by |RAND_enable_fork_unsafe_buffering|. 55 * It's protected by |requested_lock|. */ 56 static int urandom_buffering_requested = 0; 57 58 /* urandom_buffering controls whether buffering is enabled (1) or not (0). This 59 * is protected by |once|. */ 60 static int urandom_buffering = 0; 61 62 static CRYPTO_once_t once = CRYPTO_ONCE_INIT; 63 64 /* init_once initializes the state of this module to values previously 65 * requested. This is the only function that modifies |urandom_fd| and 66 * |urandom_buffering|, whose values may be read safely after calling the 67 * once. */ 68 static void init_once(void) { 69 CRYPTO_STATIC_MUTEX_lock_read(&requested_lock); 70 urandom_buffering = urandom_buffering_requested; 71 int fd = urandom_fd_requested; 72 CRYPTO_STATIC_MUTEX_unlock(&requested_lock); 73 74 if (fd == -2) { 75 do { 76 fd = open("/dev/urandom", O_RDONLY); 77 } while (fd == -1 && errno == EINTR); 78 } 79 80 if (fd < 0) { 81 abort(); 82 } 83 84 int flags = fcntl(fd, F_GETFD); 85 if (flags == -1) { 86 /* Native Client doesn't implement |fcntl|. */ 87 if (errno != ENOSYS) { 88 abort(); 89 } 90 } else { 91 flags |= FD_CLOEXEC; 92 if (fcntl(fd, F_SETFD, flags) == -1) { 93 abort(); 94 } 95 } 96 urandom_fd = fd; 97 } 98 99 void RAND_cleanup(void) {} 100 101 void RAND_set_urandom_fd(int fd) { 102 fd = dup(fd); 103 if (fd < 0) { 104 abort(); 105 } 106 107 CRYPTO_STATIC_MUTEX_lock_write(&requested_lock); 108 urandom_fd_requested = fd; 109 CRYPTO_STATIC_MUTEX_unlock(&requested_lock); 110 111 CRYPTO_once(&once, init_once); 112 if (urandom_fd != fd) { 113 abort(); // Already initialized. 114 } 115 } 116 117 void RAND_enable_fork_unsafe_buffering(int fd) { 118 if (fd >= 0) { 119 fd = dup(fd); 120 if (fd < 0) { 121 abort(); 122 } 123 } else { 124 fd = -2; 125 } 126 127 CRYPTO_STATIC_MUTEX_lock_write(&requested_lock); 128 urandom_buffering_requested = 1; 129 urandom_fd_requested = fd; 130 CRYPTO_STATIC_MUTEX_unlock(&requested_lock); 131 132 CRYPTO_once(&once, init_once); 133 if (urandom_buffering != 1 || (fd >= 0 && urandom_fd != fd)) { 134 abort(); // Already initialized. 135 } 136 } 137 138 static struct rand_buffer *get_thread_local_buffer(void) { 139 struct rand_buffer *buf = 140 CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_URANDOM_BUF); 141 if (buf != NULL) { 142 return buf; 143 } 144 145 buf = OPENSSL_malloc(sizeof(struct rand_buffer)); 146 if (buf == NULL) { 147 return NULL; 148 } 149 buf->used = BUF_SIZE; /* To trigger a |read_full| on first use. */ 150 if (!CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_URANDOM_BUF, buf, 151 OPENSSL_free)) { 152 OPENSSL_free(buf); 153 return NULL; 154 } 155 156 return buf; 157 } 158 159 /* read_full reads exactly |len| bytes from |fd| into |out| and returns 1. In 160 * the case of an error it returns 0. */ 161 static char read_full(int fd, uint8_t *out, size_t len) { 162 ssize_t r; 163 164 while (len > 0) { 165 do { 166 r = read(fd, out, len); 167 } while (r == -1 && errno == EINTR); 168 169 if (r <= 0) { 170 return 0; 171 } 172 out += r; 173 len -= r; 174 } 175 176 return 1; 177 } 178 179 /* read_from_buffer reads |requested| random bytes from the buffer into |out|, 180 * refilling it if necessary to satisfy the request. */ 181 static void read_from_buffer(struct rand_buffer *buf, 182 uint8_t *out, size_t requested) { 183 size_t remaining = BUF_SIZE - buf->used; 184 185 while (requested > remaining) { 186 memcpy(out, &buf->rand[buf->used], remaining); 187 buf->used += remaining; 188 out += remaining; 189 requested -= remaining; 190 191 if (!read_full(urandom_fd, buf->rand, BUF_SIZE)) { 192 abort(); 193 return; 194 } 195 buf->used = 0; 196 remaining = BUF_SIZE; 197 } 198 199 memcpy(out, &buf->rand[buf->used], requested); 200 buf->used += requested; 201 } 202 203 /* CRYPTO_sysrand puts |requested| random bytes into |out|. */ 204 void CRYPTO_sysrand(uint8_t *out, size_t requested) { 205 if (requested == 0) { 206 return; 207 } 208 209 CRYPTO_once(&once, init_once); 210 if (urandom_buffering && requested < BUF_SIZE) { 211 struct rand_buffer *buf = get_thread_local_buffer(); 212 if (buf != NULL) { 213 read_from_buffer(buf, out, requested); 214 return; 215 } 216 } 217 218 if (!read_full(urandom_fd, out, requested)) { 219 abort(); 220 } 221 } 222 223 #endif /* !OPENSSL_WINDOWS */ 224