getrandom.c 5.6 KB

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