1 /* Obtain a series of random bytes. 2 3 Copyright 2020 Free Software Foundation, Inc. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <https://www.gnu.org/licenses/>. */ 17 18 /* Written by Paul Eggert. */ 19 20 #include <config.h> 21 22 #include <sys/random.h> 23 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <stdbool.h> 27 #include <unistd.h> 28 29 #if defined _WIN32 && ! defined __CYGWIN__ 30 # define WIN32_LEAN_AND_MEAN 31 # include <windows.h> 32 # if HAVE_BCRYPT_H 33 # include <bcrypt.h> 34 # else 35 # include <ntdef.h> /* NTSTATUS */ 36 typedef void * BCRYPT_ALG_HANDLE; 37 # define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002 38 # if HAVE_LIB_BCRYPT 39 extern NTSTATUS WINAPI BCryptGenRandom (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG); 40 # endif 41 # endif 42 # if !HAVE_LIB_BCRYPT 43 # include <wincrypt.h> 44 # ifndef CRYPT_VERIFY_CONTEXT 45 # define CRYPT_VERIFY_CONTEXT 0xF0000000 46 # endif 47 # endif 48 #endif 49 50 #include "minmax.h" 51 52 #if defined _WIN32 && ! defined __CYGWIN__ 53 54 /* Don't assume that UNICODE is not defined. */ 55 # undef LoadLibrary 56 # define LoadLibrary LoadLibraryA 57 # undef CryptAcquireContext 58 # define CryptAcquireContext CryptAcquireContextA 59 60 # if !HAVE_LIB_BCRYPT 61 62 /* Avoid warnings from gcc -Wcast-function-type. */ 63 # define GetProcAddress \ 64 (void *) GetProcAddress 65 66 /* BCryptGenRandom with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag works only 67 starting with Windows 7. */ 68 typedef NTSTATUS (WINAPI * BCryptGenRandomFuncType) (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG); 69 static BCryptGenRandomFuncType BCryptGenRandomFunc = NULL; 70 static BOOL initialized = FALSE; 71 72 static void 73 initialize (void) 74 { 75 HMODULE bcrypt = LoadLibrary ("bcrypt.dll"); 76 if (bcrypt != NULL) 77 { 78 BCryptGenRandomFunc = 79 (BCryptGenRandomFuncType) GetProcAddress (bcrypt, "BCryptGenRandom"); 80 } 81 initialized = TRUE; 82 } 83 84 # else 85 86 # define BCryptGenRandomFunc BCryptGenRandom 87 88 # endif 89 90 #else 91 /* These devices exist on all platforms except native Windows. */ 92 93 /* Name of a device through which the kernel returns high quality random 94 numbers, from an entropy pool. When the pool is empty, the call blocks 95 until entropy sources have added enough bits of entropy. */ 96 # ifndef NAME_OF_RANDOM_DEVICE 97 # define NAME_OF_RANDOM_DEVICE "/dev/random" 98 # endif 99 100 /* Name of a device through which the kernel returns random or pseudo-random 101 numbers. It uses an entropy pool, but, in order to avoid blocking, adds 102 bits generated by a pseudo-random number generator, as needed. */ 103 # ifndef NAME_OF_NONCE_DEVICE 104 # define NAME_OF_NONCE_DEVICE "/dev/urandom" 105 # endif 106 107 #endif 108 109 /* Set BUFFER (of size LENGTH) to random bytes under the control of FLAGS. 110 Return the number of bytes written (> 0). 111 Upon error, return -1 and set errno. */ 112 ssize_t 113 getrandom (void *buffer, size_t length, unsigned int flags) 114 #undef getrandom 115 { 116 #if defined _WIN32 && ! defined __CYGWIN__ 117 /* BCryptGenRandom, defined in <bcrypt.h> 118 <https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom> 119 with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag 120 works in Windows 7 and newer. */ 121 static int bcrypt_not_working /* = 0 */; 122 if (!bcrypt_not_working) 123 { 124 # if !HAVE_LIB_BCRYPT 125 if (!initialized) 126 initialize (); 127 # endif 128 if (BCryptGenRandomFunc != NULL 129 && BCryptGenRandomFunc (NULL, buffer, length, 130 BCRYPT_USE_SYSTEM_PREFERRED_RNG) 131 == 0 /*STATUS_SUCCESS*/) 132 return length; 133 bcrypt_not_working = 1; 134 } 135 # if !HAVE_LIB_BCRYPT 136 /* CryptGenRandom, defined in <wincrypt.h> 137 <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom> 138 works in older releases as well, but is now deprecated. 139 CryptAcquireContext, defined in <wincrypt.h> 140 <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta> */ 141 { 142 static int crypt_initialized /* = 0 */; 143 static HCRYPTPROV provider; 144 if (!crypt_initialized) 145 { 146 if (CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, 147 CRYPT_VERIFY_CONTEXT)) 148 crypt_initialized = 1; 149 else 150 crypt_initialized = -1; 151 } 152 if (crypt_initialized >= 0) 153 { 154 if (!CryptGenRandom (provider, length, buffer)) 155 { 156 errno = EIO; 157 return -1; 158 } 159 return length; 160 } 161 } 162 # endif 163 errno = ENOSYS; 164 return -1; 165 #elif HAVE_GETRANDOM 166 return getrandom (buffer, length, flags); 167 #else 168 static int randfd[2] = { -1, -1 }; 169 bool devrandom = (flags & GRND_RANDOM) != 0; 170 int fd = randfd[devrandom]; 171 172 if (fd < 0) 173 { 174 static char const randdevice[][MAX (sizeof NAME_OF_NONCE_DEVICE, 175 sizeof NAME_OF_RANDOM_DEVICE)] 176 = { NAME_OF_NONCE_DEVICE, NAME_OF_RANDOM_DEVICE }; 177 int oflags = (O_RDONLY + O_CLOEXEC 178 + (flags & GRND_NONBLOCK ? O_NONBLOCK : 0)); 179 fd = open (randdevice[devrandom], oflags); 180 if (fd < 0) 181 return fd; 182 randfd[devrandom] = fd; 183 } 184 185 return read (fd, buffer, length); 186 #endif 187 } 188