1 2#include <assert.h> 3#include <errno.h> 4#include <fcntl.h> 5#include <limits.h> 6#include <stdint.h> 7#include <string.h> 8#ifndef _WIN32 9# include <unistd.h> 10#endif 11 12#include <stdlib.h> 13#include <sys/types.h> 14#ifndef _WIN32 15# include <sys/stat.h> 16# include <sys/time.h> 17#endif 18#ifdef __linux__ 19# ifdef __dietlibc__ 20# define _LINUX_SOURCE 21# else 22# include <sys/syscall.h> 23# endif 24# include <poll.h> 25#endif 26 27#include "core.h" 28#include "private/common.h" 29#include "randombytes.h" 30#include "randombytes_sysrandom.h" 31#include "utils.h" 32 33#ifdef _WIN32 34/* `RtlGenRandom` is used over `CryptGenRandom` on Microsoft Windows based systems: 35 * - `CryptGenRandom` requires pulling in `CryptoAPI` which causes unnecessary 36 * memory overhead if this API is not being used for other purposes 37 * - `RtlGenRandom` is thus called directly instead. A detailed explanation 38 * can be found here: https://blogs.msdn.microsoft.com/michael_howard/2005/01/14/cryptographically-secure-random-number-on-windows-without-using-cryptoapi/ 39 * 40 * In spite of the disclaimer on the `RtlGenRandom` documentation page that was 41 * written back in the Windows XP days, this function is here to stay. The CRT 42 * function `rand_s()` directly depends on it, so touching it would break many 43 * applications released since Windows XP. 44 * 45 * Also note that Rust, Firefox and BoringSSL (thus, Google Chrome and everything 46 * based on Chromium) also depend on it, and that libsodium allows the RNG to be 47 * replaced without patching nor recompiling the library. 48 */ 49# include <windows.h> 50# define RtlGenRandom SystemFunction036 51# if defined(__cplusplus) 52extern "C" 53# endif 54BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); 55# pragma comment(lib, "advapi32.lib") 56#endif 57 58#if defined(__OpenBSD__) || defined(__CloudABI__) 59# define HAVE_SAFE_ARC4RANDOM 1 60#endif 61 62#ifndef SSIZE_MAX 63# define SSIZE_MAX (SIZE_MAX / 2 - 1) 64#endif 65 66#ifdef HAVE_SAFE_ARC4RANDOM 67 68static uint32_t 69randombytes_sysrandom(void) 70{ 71 return arc4random(); 72} 73 74static void 75randombytes_sysrandom_stir(void) 76{ 77} 78 79static void 80randombytes_sysrandom_buf(void * const buf, const size_t size) 81{ 82 arc4random_buf(buf, size); 83} 84 85static int 86randombytes_sysrandom_close(void) 87{ 88 return 0; 89} 90 91#else /* __OpenBSD__ */ 92 93typedef struct SysRandom_ { 94 int random_data_source_fd; 95 int initialized; 96 int getrandom_available; 97} SysRandom; 98 99static SysRandom stream = { 100 SODIUM_C99(.random_data_source_fd =) -1, 101 SODIUM_C99(.initialized =) 0, 102 SODIUM_C99(.getrandom_available =) 0 103}; 104 105#ifndef _WIN32 106static ssize_t 107safe_read(const int fd, void * const buf_, size_t size) 108{ 109 unsigned char *buf = (unsigned char *) buf_; 110 ssize_t readnb; 111 112 assert(size > (size_t) 0U); 113 assert(size <= SSIZE_MAX); 114 do { 115 while ((readnb = read(fd, buf, size)) < (ssize_t) 0 && 116 (errno == EINTR || errno == EAGAIN)); /* LCOV_EXCL_LINE */ 117 if (readnb < (ssize_t) 0) { 118 return readnb; /* LCOV_EXCL_LINE */ 119 } 120 if (readnb == (ssize_t) 0) { 121 break; /* LCOV_EXCL_LINE */ 122 } 123 size -= (size_t) readnb; 124 buf += readnb; 125 } while (size > (ssize_t) 0); 126 127 return (ssize_t) (buf - (unsigned char *) buf_); 128} 129#endif 130 131#ifndef _WIN32 132# if defined(__linux__) && !defined(USE_BLOCKING_RANDOM) && !defined(NO_BLOCKING_RANDOM_POLL) 133static int 134randombytes_block_on_dev_random(void) 135{ 136 struct pollfd pfd; 137 int fd; 138 int pret; 139 140 fd = open("/dev/random", O_RDONLY); 141 if (fd == -1) { 142 return 0; 143 } 144 pfd.fd = fd; 145 pfd.events = POLLIN; 146 pfd.revents = 0; 147 do { 148 pret = poll(&pfd, 1, -1); 149 } while (pret < 0 && (errno == EINTR || errno == EAGAIN)); 150 if (pret != 1) { 151 (void) close(fd); 152 errno = EIO; 153 return -1; 154 } 155 return close(fd); 156} 157# endif 158 159static int 160randombytes_sysrandom_random_dev_open(void) 161{ 162/* LCOV_EXCL_START */ 163 struct stat st; 164 static const char *devices[] = { 165# ifndef USE_BLOCKING_RANDOM 166 "/dev/urandom", 167# endif 168 "/dev/random", NULL 169 }; 170 const char **device = devices; 171 int fd; 172 173# if defined(__linux__) && !defined(USE_BLOCKING_RANDOM) && !defined(NO_BLOCKING_RANDOM_POLL) 174 if (randombytes_block_on_dev_random() != 0) { 175 return -1; 176 } 177# endif 178 do { 179 fd = open(*device, O_RDONLY); 180 if (fd != -1) { 181 if (fstat(fd, &st) == 0 && 182# ifdef __COMPCERT__ 183 1 184# elif defined(S_ISNAM) 185 (S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode)) 186# else 187 S_ISCHR(st.st_mode) 188# endif 189 ) { 190# if defined(F_SETFD) && defined(FD_CLOEXEC) 191 (void) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 192# endif 193 return fd; 194 } 195 (void) close(fd); 196 } else if (errno == EINTR) { 197 continue; 198 } 199 device++; 200 } while (*device != NULL); 201 202 errno = EIO; 203 return -1; 204/* LCOV_EXCL_STOP */ 205} 206 207# if defined(__dietlibc__) || (defined(SYS_getrandom) && defined(__NR_getrandom)) 208static int 209_randombytes_linux_getrandom(void * const buf, const size_t size) 210{ 211 int readnb; 212 213 assert(size <= 256U); 214 do { 215# ifdef __dietlibc__ 216 readnb = getrandom(buf, size, 0); 217# else 218 readnb = syscall(SYS_getrandom, buf, (int) size, 0); 219# endif 220 } while (readnb < 0 && (errno == EINTR || errno == EAGAIN)); 221 222 return (readnb == (int) size) - 1; 223} 224 225static int 226randombytes_linux_getrandom(void * const buf_, size_t size) 227{ 228 unsigned char *buf = (unsigned char *) buf_; 229 size_t chunk_size = 256U; 230 231 do { 232 if (size < chunk_size) { 233 chunk_size = size; 234 assert(chunk_size > (size_t) 0U); 235 } 236 if (_randombytes_linux_getrandom(buf, chunk_size) != 0) { 237 return -1; 238 } 239 size -= chunk_size; 240 buf += chunk_size; 241 } while (size > (size_t) 0U); 242 243 return 0; 244} 245# endif 246 247static void 248randombytes_sysrandom_init(void) 249{ 250 const int errno_save = errno; 251 252# if defined(SYS_getrandom) && defined(__NR_getrandom) 253 { 254 unsigned char fodder[16]; 255 256 if (randombytes_linux_getrandom(fodder, sizeof fodder) == 0) { 257 stream.getrandom_available = 1; 258 errno = errno_save; 259 return; 260 } 261 stream.getrandom_available = 0; 262 } 263# endif 264 265 if ((stream.random_data_source_fd = 266 randombytes_sysrandom_random_dev_open()) == -1) { 267 sodium_misuse(); /* LCOV_EXCL_LINE */ 268 } 269 errno = errno_save; 270} 271 272#else /* _WIN32 */ 273 274static void 275randombytes_sysrandom_init(void) 276{ 277} 278#endif 279 280static void 281randombytes_sysrandom_stir(void) 282{ 283 if (stream.initialized == 0) { 284 randombytes_sysrandom_init(); 285 stream.initialized = 1; 286 } 287} 288 289static void 290randombytes_sysrandom_stir_if_needed(void) 291{ 292 if (stream.initialized == 0) { 293 randombytes_sysrandom_stir(); 294 } 295} 296 297static int 298randombytes_sysrandom_close(void) 299{ 300 int ret = -1; 301 302#ifndef _WIN32 303 if (stream.random_data_source_fd != -1 && 304 close(stream.random_data_source_fd) == 0) { 305 stream.random_data_source_fd = -1; 306 stream.initialized = 0; 307 ret = 0; 308 } 309# if defined(SYS_getrandom) && defined(__NR_getrandom) 310 if (stream.getrandom_available != 0) { 311 ret = 0; 312 } 313# endif 314#else /* _WIN32 */ 315 if (stream.initialized != 0) { 316 stream.initialized = 0; 317 ret = 0; 318 } 319#endif 320 return ret; 321} 322 323static void 324randombytes_sysrandom_buf(void * const buf, const size_t size) 325{ 326 randombytes_sysrandom_stir_if_needed(); 327#if defined(ULONG_LONG_MAX) && defined(SIZE_MAX) 328# if SIZE_MAX > ULONG_LONG_MAX 329 /* coverity[result_independent_of_operands] */ 330 assert(size <= ULONG_LONG_MAX); 331# endif 332#endif 333#ifndef _WIN32 334# if defined(SYS_getrandom) && defined(__NR_getrandom) 335 if (stream.getrandom_available != 0) { 336 if (randombytes_linux_getrandom(buf, size) != 0) { 337 sodium_misuse(); /* LCOV_EXCL_LINE */ 338 } 339 return; 340 } 341# endif 342 if (stream.random_data_source_fd == -1 || 343 safe_read(stream.random_data_source_fd, buf, size) != (ssize_t) size) { 344 sodium_misuse(); /* LCOV_EXCL_LINE */ 345 } 346#else 347 COMPILER_ASSERT(randombytes_BYTES_MAX <= 0xffffffffUL); 348 if (size > (size_t) 0xffffffffUL) { 349 sodium_misuse(); /* LCOV_EXCL_LINE */ 350 } 351 if (! RtlGenRandom((PVOID) buf, (ULONG) size)) { 352 sodium_misuse(); /* LCOV_EXCL_LINE */ 353 } 354#endif 355} 356 357static uint32_t 358randombytes_sysrandom(void) 359{ 360 uint32_t r; 361 362 randombytes_sysrandom_buf(&r, sizeof r); 363 364 return r; 365} 366 367#endif /* __OpenBSD__ */ 368 369static const char * 370randombytes_sysrandom_implementation_name(void) 371{ 372 return "sysrandom"; 373} 374 375struct randombytes_implementation randombytes_sysrandom_implementation = { 376 SODIUM_C99(.implementation_name =) randombytes_sysrandom_implementation_name, 377 SODIUM_C99(.random =) randombytes_sysrandom, 378 SODIUM_C99(.stir =) randombytes_sysrandom_stir, 379 SODIUM_C99(.uniform =) NULL, 380 SODIUM_C99(.buf =) randombytes_sysrandom_buf, 381 SODIUM_C99(.close =) randombytes_sysrandom_close 382}; 383