|  | /* Obtain a series of random bytes. | 
|  |  | 
|  | Copyright 2020-2022 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is free software: you can redistribute it and/or modify | 
|  | it under the terms of the GNU Lesser General Public License as | 
|  | published by the Free Software Foundation; either version 2.1 of the | 
|  | License, or (at your option) any later version. | 
|  |  | 
|  | This file is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU Lesser General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU Lesser General Public License | 
|  | along with this program.  If not, see <https://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | /* Written by Paul Eggert.  */ | 
|  |  | 
|  | #include <config.h> | 
|  |  | 
|  | #include <sys/random.h> | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <stdbool.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #if defined _WIN32 && ! defined __CYGWIN__ | 
|  | # define WIN32_LEAN_AND_MEAN | 
|  | # include <windows.h> | 
|  | # if HAVE_BCRYPT_H | 
|  | #  include <bcrypt.h> | 
|  | # else | 
|  | #  define NTSTATUS LONG | 
|  | typedef void * BCRYPT_ALG_HANDLE; | 
|  | #  define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002 | 
|  | #  if HAVE_LIB_BCRYPT | 
|  | extern NTSTATUS WINAPI BCryptGenRandom (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG); | 
|  | #  endif | 
|  | # endif | 
|  | # if !HAVE_LIB_BCRYPT | 
|  | #  include <wincrypt.h> | 
|  | #  ifndef CRYPT_VERIFY_CONTEXT | 
|  | #   define CRYPT_VERIFY_CONTEXT 0xF0000000 | 
|  | #  endif | 
|  | # endif | 
|  | #endif | 
|  |  | 
|  | #include "minmax.h" | 
|  |  | 
|  | #if defined _WIN32 && ! defined __CYGWIN__ | 
|  |  | 
|  | /* Don't assume that UNICODE is not defined.  */ | 
|  | # undef LoadLibrary | 
|  | # define LoadLibrary LoadLibraryA | 
|  | # undef CryptAcquireContext | 
|  | # define CryptAcquireContext CryptAcquireContextA | 
|  |  | 
|  | # if !HAVE_LIB_BCRYPT | 
|  |  | 
|  | /* Avoid warnings from gcc -Wcast-function-type.  */ | 
|  | #  define GetProcAddress \ | 
|  | (void *) GetProcAddress | 
|  |  | 
|  | /* BCryptGenRandom with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag works only | 
|  | starting with Windows 7.  */ | 
|  | typedef NTSTATUS (WINAPI * BCryptGenRandomFuncType) (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG); | 
|  | static BCryptGenRandomFuncType BCryptGenRandomFunc = NULL; | 
|  | static BOOL initialized = FALSE; | 
|  |  | 
|  | static void | 
|  | initialize (void) | 
|  | { | 
|  | HMODULE bcrypt = LoadLibrary ("bcrypt.dll"); | 
|  | if (bcrypt != NULL) | 
|  | { | 
|  | BCryptGenRandomFunc = | 
|  | (BCryptGenRandomFuncType) GetProcAddress (bcrypt, "BCryptGenRandom"); | 
|  | } | 
|  | initialized = TRUE; | 
|  | } | 
|  |  | 
|  | # else | 
|  |  | 
|  | #  define BCryptGenRandomFunc BCryptGenRandom | 
|  |  | 
|  | # endif | 
|  |  | 
|  | #else | 
|  | /* These devices exist on all platforms except native Windows.  */ | 
|  |  | 
|  | /* Name of a device through which the kernel returns high quality random | 
|  | numbers, from an entropy pool.  When the pool is empty, the call blocks | 
|  | until entropy sources have added enough bits of entropy.  */ | 
|  | # ifndef NAME_OF_RANDOM_DEVICE | 
|  | #  define NAME_OF_RANDOM_DEVICE "/dev/random" | 
|  | # endif | 
|  |  | 
|  | /* Name of a device through which the kernel returns random or pseudo-random | 
|  | numbers.  It uses an entropy pool, but, in order to avoid blocking, adds | 
|  | bits generated by a pseudo-random number generator, as needed.  */ | 
|  | # ifndef NAME_OF_NONCE_DEVICE | 
|  | #  define NAME_OF_NONCE_DEVICE "/dev/urandom" | 
|  | # endif | 
|  |  | 
|  | #endif | 
|  |  | 
|  | /* Set BUFFER (of size LENGTH) to random bytes under the control of FLAGS. | 
|  | Return the number of bytes written (> 0). | 
|  | Upon error, return -1 and set errno.  */ | 
|  | ssize_t | 
|  | getrandom (void *buffer, size_t length, unsigned int flags) | 
|  | #undef getrandom | 
|  | { | 
|  | #if defined _WIN32 && ! defined __CYGWIN__ | 
|  | /* BCryptGenRandom, defined in <bcrypt.h> | 
|  | <https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom> | 
|  | with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag | 
|  | works in Windows 7 and newer.  */ | 
|  | static int bcrypt_not_working /* = 0 */; | 
|  | if (!bcrypt_not_working) | 
|  | { | 
|  | # if !HAVE_LIB_BCRYPT | 
|  | if (!initialized) | 
|  | initialize (); | 
|  | # endif | 
|  | if (BCryptGenRandomFunc != NULL | 
|  | && BCryptGenRandomFunc (NULL, buffer, length, | 
|  | BCRYPT_USE_SYSTEM_PREFERRED_RNG) | 
|  | == 0 /*STATUS_SUCCESS*/) | 
|  | return length; | 
|  | bcrypt_not_working = 1; | 
|  | } | 
|  | # if !HAVE_LIB_BCRYPT | 
|  | /* CryptGenRandom, defined in <wincrypt.h> | 
|  | <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom> | 
|  | works in older releases as well, but is now deprecated. | 
|  | CryptAcquireContext, defined in <wincrypt.h> | 
|  | <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta>  */ | 
|  | { | 
|  | static int crypt_initialized /* = 0 */; | 
|  | static HCRYPTPROV provider; | 
|  | if (!crypt_initialized) | 
|  | { | 
|  | if (CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL, | 
|  | CRYPT_VERIFY_CONTEXT)) | 
|  | crypt_initialized = 1; | 
|  | else | 
|  | crypt_initialized = -1; | 
|  | } | 
|  | if (crypt_initialized >= 0) | 
|  | { | 
|  | if (!CryptGenRandom (provider, length, buffer)) | 
|  | { | 
|  | errno = EIO; | 
|  | return -1; | 
|  | } | 
|  | return length; | 
|  | } | 
|  | } | 
|  | # endif | 
|  | errno = ENOSYS; | 
|  | return -1; | 
|  | #elif HAVE_GETRANDOM | 
|  | return getrandom (buffer, length, flags); | 
|  | #else | 
|  | static int randfd[2] = { -1, -1 }; | 
|  | bool devrandom = (flags & GRND_RANDOM) != 0; | 
|  | int fd = randfd[devrandom]; | 
|  |  | 
|  | if (fd < 0) | 
|  | { | 
|  | static char const randdevice[][MAX (sizeof NAME_OF_NONCE_DEVICE, | 
|  | sizeof NAME_OF_RANDOM_DEVICE)] | 
|  | = { NAME_OF_NONCE_DEVICE, NAME_OF_RANDOM_DEVICE }; | 
|  | int oflags = (O_RDONLY + O_CLOEXEC | 
|  | + (flags & GRND_NONBLOCK ? O_NONBLOCK : 0)); | 
|  | fd = open (randdevice[devrandom], oflags); | 
|  | if (fd < 0) | 
|  | { | 
|  | if (errno == ENOENT || errno == ENOTDIR) | 
|  | errno = ENOSYS; | 
|  | return -1; | 
|  | } | 
|  | randfd[devrandom] = fd; | 
|  | } | 
|  |  | 
|  | return read (fd, buffer, length); | 
|  | #endif | 
|  | } |