1 /* Obtain a series of random bytes.
2
3 Copyright 2020-2021 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 # define NTSTATUS LONG
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
initialize(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
getrandom(void * buffer,size_t length,unsigned int flags)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