Line data Source code
1 : /*
2 : * random_seed.c
3 : *
4 : * Copyright (c) 2013 Metaparadigm Pte. Ltd.
5 : * Michael Clark <michael@metaparadigm.com>
6 : *
7 : * This library is free software; you can redistribute it and/or modify
8 : * it under the terms of the MIT license. See COPYING for details.
9 : *
10 : */
11 :
12 : #include "random_seed.h"
13 : #include "config.h"
14 : #include "strerror_override.h"
15 : #include <stdio.h>
16 : #include <stdlib.h>
17 : #ifdef HAVE_BSD_STDLIB_H
18 : #include <bsd/stdlib.h>
19 : #endif
20 :
21 : #define DEBUG_SEED(s)
22 :
23 : #if defined(__APPLE__) || defined(__unix__) || defined(__linux__)
24 : #define HAVE_DEV_RANDOM 1
25 : #endif
26 :
27 : #ifdef HAVE_ARC4RANDOM
28 : #undef HAVE_GETRANDOM
29 : #undef HAVE_DEV_RANDOM
30 : #undef HAVE_CRYPTGENRANDOM
31 : #endif
32 :
33 : #if defined ENABLE_RDRAND
34 :
35 : /* cpuid */
36 :
37 : #if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
38 : #define HAS_X86_CPUID 1
39 :
40 : static void do_cpuid(int regs[], int h)
41 : {
42 : /* clang-format off */
43 : __asm__ __volatile__("cpuid"
44 : : "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
45 : : "a"(h));
46 : /* clang-format on */
47 : }
48 :
49 : #elif defined _MSC_VER
50 :
51 : #define HAS_X86_CPUID 1
52 : #define do_cpuid __cpuid
53 :
54 : #endif
55 :
56 : /* has_rdrand */
57 :
58 : #if HAS_X86_CPUID
59 :
60 : static int get_rdrand_seed(void);
61 :
62 : /* Valid values are -1 (haven't tested), 0 (no), and 1 (yes). */
63 : static int _has_rdrand = -1;
64 :
65 : static int has_rdrand(void)
66 : {
67 : if (_has_rdrand != -1)
68 : {
69 : return _has_rdrand;
70 : }
71 :
72 : /* CPUID.01H:ECX.RDRAND[bit 30] == 1 */
73 : int regs[4];
74 : do_cpuid(regs, 1);
75 : if (!(regs[2] & (1 << 30)))
76 : {
77 : _has_rdrand = 0;
78 : return 0;
79 : }
80 :
81 : /*
82 : * Some CPUs advertise RDRAND in CPUID, but return 0xFFFFFFFF
83 : * unconditionally. To avoid locking up later, test RDRAND here. If over
84 : * 3 trials RDRAND has returned the same value, declare it broken.
85 : * Example CPUs are AMD Ryzen 3000 series
86 : * and much older AMD APUs, such as the E1-1500
87 : * https://github.com/systemd/systemd/issues/11810
88 : * https://linuxreviews.org/RDRAND_stops_returning_random_values_on_older_AMD_CPUs_after_suspend
89 : */
90 : _has_rdrand = 0;
91 : int prev = get_rdrand_seed();
92 : for (int i = 0; i < 3; i++)
93 : {
94 : int temp = get_rdrand_seed();
95 : if (temp != prev)
96 : {
97 : _has_rdrand = 1;
98 : break;
99 : }
100 :
101 : prev = temp;
102 : }
103 :
104 : return _has_rdrand;
105 : }
106 :
107 : #endif
108 :
109 : /* get_rdrand_seed - GCC x86 and X64 */
110 :
111 : #if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
112 :
113 : #define HAVE_RDRAND 1
114 :
115 : static int get_rdrand_seed(void)
116 : {
117 : DEBUG_SEED("get_rdrand_seed");
118 : int _eax;
119 : /* rdrand eax */
120 : /* clang-format off */
121 : __asm__ __volatile__("1: .byte 0x0F\n"
122 : " .byte 0xC7\n"
123 : " .byte 0xF0\n"
124 : " jnc 1b;\n"
125 : : "=a" (_eax));
126 : /* clang-format on */
127 : return _eax;
128 : }
129 :
130 : #endif
131 :
132 : #if defined _MSC_VER
133 :
134 : #if _MSC_VER >= 1700
135 : #define HAVE_RDRAND 1
136 :
137 : /* get_rdrand_seed - Visual Studio 2012 and above */
138 :
139 : static int get_rdrand_seed(void)
140 : {
141 : DEBUG_SEED("get_rdrand_seed");
142 : int r;
143 : while (_rdrand32_step(&r) == 0)
144 : ;
145 : return r;
146 : }
147 :
148 : #elif defined _M_IX86
149 : #define HAVE_RDRAND 1
150 :
151 : /* get_rdrand_seed - Visual Studio 2010 and below - x86 only */
152 :
153 : /* clang-format off */
154 : static int get_rdrand_seed(void)
155 : {
156 : DEBUG_SEED("get_rdrand_seed");
157 : int _eax;
158 : retry:
159 : /* rdrand eax */
160 : __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0
161 : __asm jnc retry
162 : __asm mov _eax, eax
163 : return _eax;
164 : }
165 : /* clang-format on */
166 :
167 : #endif
168 : #endif
169 :
170 : #endif /* defined ENABLE_RDRAND */
171 :
172 : #ifdef HAVE_GETRANDOM
173 :
174 : #include <stdlib.h>
175 : #ifdef HAVE_SYS_RANDOM_H
176 : #include <sys/random.h>
177 : #endif
178 :
179 143 : static int get_getrandom_seed(int *seed)
180 : {
181 : DEBUG_SEED("get_getrandom_seed");
182 :
183 : ssize_t ret;
184 :
185 : do
186 : {
187 143 : ret = getrandom(seed, sizeof(*seed), GRND_NONBLOCK);
188 143 : } while ((ret == -1) && (errno == EINTR));
189 :
190 143 : if (ret == -1)
191 : {
192 0 : if (errno == ENOSYS) /* syscall not available in kernel */
193 0 : return -1;
194 0 : if (errno == EAGAIN) /* entropy not yet initialized */
195 0 : return -1;
196 :
197 0 : fprintf(stderr, "error from getrandom(): %s", strerror(errno));
198 0 : return -1;
199 : }
200 :
201 143 : if (ret != sizeof(*seed))
202 0 : return -1;
203 :
204 143 : return 0;
205 : }
206 : #endif /* defined HAVE_GETRANDOM */
207 :
208 : /* get_dev_random_seed */
209 :
210 : #ifdef HAVE_DEV_RANDOM
211 :
212 : #include <fcntl.h>
213 : #include <string.h>
214 : #if HAVE_UNISTD_H
215 : #include <unistd.h>
216 : #endif /* HAVE_UNISTD_H */
217 : #include <stdlib.h>
218 : #include <sys/stat.h>
219 :
220 : static const char *dev_random_file = "/dev/urandom";
221 :
222 0 : static int get_dev_random_seed(int *seed)
223 : {
224 : DEBUG_SEED("get_dev_random_seed");
225 :
226 : struct stat buf;
227 0 : if (stat(dev_random_file, &buf))
228 0 : return -1;
229 0 : if ((buf.st_mode & S_IFCHR) == 0)
230 0 : return -1;
231 :
232 : /* coverity[toctou] */
233 0 : int fd = open(dev_random_file, O_RDONLY);
234 0 : if (fd < 0)
235 : {
236 0 : fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno));
237 0 : return -1;
238 : }
239 :
240 0 : ssize_t nread = read(fd, seed, sizeof(*seed));
241 :
242 0 : close(fd);
243 :
244 0 : if (nread != sizeof(*seed))
245 : {
246 0 : fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno));
247 0 : return -1;
248 : }
249 :
250 0 : return 0;
251 : }
252 :
253 : #endif
254 :
255 : /* get_cryptgenrandom_seed */
256 :
257 : #ifdef WIN32
258 :
259 : #define HAVE_CRYPTGENRANDOM 1
260 :
261 : /* clang-format off */
262 : #include <windows.h>
263 :
264 : /* Caution: these blank lines must remain so clang-format doesn't reorder
265 : includes to put windows.h after wincrypt.h */
266 :
267 : #include <wincrypt.h>
268 : /* clang-format on */
269 : #ifndef __GNUC__
270 : #pragma comment(lib, "advapi32.lib")
271 : #endif
272 :
273 : static int get_cryptgenrandom_seed(int *seed)
274 : {
275 : HCRYPTPROV hProvider = 0;
276 : DWORD dwFlags = CRYPT_VERIFYCONTEXT;
277 :
278 : DEBUG_SEED("get_cryptgenrandom_seed");
279 :
280 : /* WinNT 4 and Win98 do no support CRYPT_SILENT */
281 : // E.Rouault: commented out to avoid warning C4996: 'GetVersion': was declared deprecated
282 : //if (LOBYTE(LOWORD(GetVersion())) > 4)
283 : dwFlags |= CRYPT_SILENT;
284 :
285 : if (!CryptAcquireContextA(&hProvider, 0, 0, PROV_RSA_FULL, dwFlags))
286 : {
287 : fprintf(stderr, "error CryptAcquireContextA 0x%08lx", GetLastError());
288 : return -1;
289 : }
290 : else
291 : {
292 : BOOL ret = CryptGenRandom(hProvider, sizeof(*seed), (BYTE *)seed);
293 : CryptReleaseContext(hProvider, 0);
294 : if (!ret)
295 : {
296 : fprintf(stderr, "error CryptGenRandom 0x%08lx", GetLastError());
297 : return -1;
298 : }
299 : }
300 :
301 : return 0;
302 : }
303 :
304 : #endif
305 :
306 : /* get_time_seed */
307 :
308 : #ifndef HAVE_ARC4RANDOM
309 : #include <time.h>
310 :
311 0 : static int get_time_seed(void)
312 : {
313 : DEBUG_SEED("get_time_seed");
314 :
315 : /* coverity[store_truncates_time_t] */
316 0 : return (unsigned)time(NULL) * 433494437;
317 : }
318 : #endif
319 :
320 : /* json_c_get_random_seed */
321 :
322 143 : int json_c_get_random_seed(void)
323 : {
324 : #ifdef OVERRIDE_GET_RANDOM_SEED
325 : OVERRIDE_GET_RANDOM_SEED;
326 : #endif
327 : #if defined HAVE_RDRAND && HAVE_RDRAND
328 : if (has_rdrand())
329 : return get_rdrand_seed();
330 : #endif
331 : #ifdef HAVE_ARC4RANDOM
332 : /* arc4random never fails, so use it if it's available */
333 : return arc4random();
334 : #else
335 : #ifdef HAVE_GETRANDOM
336 : {
337 143 : int seed = 0;
338 143 : if (get_getrandom_seed(&seed) == 0)
339 143 : return seed;
340 : }
341 : #endif
342 : #if defined HAVE_DEV_RANDOM && HAVE_DEV_RANDOM
343 : {
344 0 : int seed = 0;
345 0 : if (get_dev_random_seed(&seed) == 0)
346 0 : return seed;
347 : }
348 : #endif
349 : #if defined HAVE_CRYPTGENRANDOM && HAVE_CRYPTGENRANDOM
350 : {
351 : int seed = 0;
352 : if (get_cryptgenrandom_seed(&seed) == 0)
353 : return seed;
354 : }
355 : #endif
356 0 : return get_time_seed();
357 : #endif /* !HAVE_ARC4RANDOM */
358 : }
|