Branch data Line data Source code
1 : : #include "Python.h"
2 : : #include "pycore_initconfig.h"
3 : : #include "pycore_fileutils.h" // _Py_fstat_noraise()
4 : :
5 : : #ifdef MS_WINDOWS
6 : : # include <windows.h>
7 : : # include <bcrypt.h>
8 : : #else
9 : : # include <fcntl.h>
10 : : # ifdef HAVE_SYS_STAT_H
11 : : # include <sys/stat.h>
12 : : # endif
13 : : # ifdef HAVE_LINUX_RANDOM_H
14 : : # include <linux/random.h>
15 : : # endif
16 : : # if defined(HAVE_SYS_RANDOM_H) && (defined(HAVE_GETRANDOM) || defined(HAVE_GETENTROPY))
17 : : # include <sys/random.h>
18 : : # endif
19 : : # if !defined(HAVE_GETRANDOM) && defined(HAVE_GETRANDOM_SYSCALL)
20 : : # include <sys/syscall.h>
21 : : # endif
22 : : #endif
23 : :
24 : : #ifdef _Py_MEMORY_SANITIZER
25 : : # include <sanitizer/msan_interface.h>
26 : : #endif
27 : :
28 : : #if defined(__APPLE__) && defined(__has_builtin)
29 : : # if __has_builtin(__builtin_available)
30 : : # define HAVE_GETENTRYPY_GETRANDOM_RUNTIME __builtin_available(macOS 10.12, iOS 10.10, tvOS 10.0, watchOS 3.0, *)
31 : : # endif
32 : : #endif
33 : : #ifndef HAVE_GETENTRYPY_GETRANDOM_RUNTIME
34 : : # define HAVE_GETENTRYPY_GETRANDOM_RUNTIME 1
35 : : #endif
36 : :
37 : :
38 : : #ifdef Py_DEBUG
39 : : int _Py_HashSecret_Initialized = 0;
40 : : #else
41 : : static int _Py_HashSecret_Initialized = 0;
42 : : #endif
43 : :
44 : : #ifdef MS_WINDOWS
45 : :
46 : : /* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
47 : : API. Return 0 on success, or raise an exception and return -1 on error. */
48 : : static int
49 : : win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
50 : : {
51 : : while (size > 0)
52 : : {
53 : : DWORD chunk = (DWORD)Py_MIN(size, PY_DWORD_MAX);
54 : : NTSTATUS status = BCryptGenRandom(NULL, buffer, chunk, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
55 : : if (!BCRYPT_SUCCESS(status)) {
56 : : /* BCryptGenRandom() failed */
57 : : if (raise) {
58 : : PyErr_SetFromWindowsErr(0);
59 : : }
60 : : return -1;
61 : : }
62 : : buffer += chunk;
63 : : size -= chunk;
64 : : }
65 : : return 0;
66 : : }
67 : :
68 : : #else /* !MS_WINDOWS */
69 : :
70 : : #if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL)
71 : : #define PY_GETRANDOM 1
72 : :
73 : : /* Call getrandom() to get random bytes:
74 : :
75 : : - Return 1 on success
76 : : - Return 0 if getrandom() is not available (failed with ENOSYS or EPERM),
77 : : or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom not
78 : : initialized yet) and raise=0.
79 : : - Raise an exception (if raise is non-zero) and return -1 on error:
80 : : if getrandom() failed with EINTR, raise is non-zero and the Python signal
81 : : handler raised an exception, or if getrandom() failed with a different
82 : : error.
83 : :
84 : : getrandom() is retried if it failed with EINTR: interrupted by a signal. */
85 : : static int
86 : 173561 : py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
87 : : {
88 : : /* Is getrandom() supported by the running kernel? Set to 0 if getrandom()
89 : : failed with ENOSYS or EPERM. Need Linux kernel 3.17 or newer, or Solaris
90 : : 11.3 or newer */
91 : : static int getrandom_works = 1;
92 : : int flags;
93 : : char *dest;
94 : : long n;
95 : :
96 [ - + ]: 173561 : if (!getrandom_works) {
97 : 0 : return 0;
98 : : }
99 : :
100 : 173561 : flags = blocking ? 0 : GRND_NONBLOCK;
101 : 173561 : dest = buffer;
102 [ + + ]: 347122 : while (0 < size) {
103 : : #if defined(__sun) && defined(__SVR4)
104 : : /* Issue #26735: On Solaris, getrandom() is limited to returning up
105 : : to 1024 bytes. Call it multiple times if more bytes are
106 : : requested. */
107 : : n = Py_MIN(size, 1024);
108 : : #else
109 : 173561 : n = Py_MIN(size, LONG_MAX);
110 : : #endif
111 : :
112 : 173561 : errno = 0;
113 : : #ifdef HAVE_GETRANDOM
114 [ + + ]: 173561 : if (raise) {
115 : 170653 : Py_BEGIN_ALLOW_THREADS
116 : 170653 : n = getrandom(dest, n, flags);
117 : 170653 : Py_END_ALLOW_THREADS
118 : : }
119 : : else {
120 : 2908 : n = getrandom(dest, n, flags);
121 : : }
122 : : #else
123 : : /* On Linux, use the syscall() function because the GNU libc doesn't
124 : : expose the Linux getrandom() syscall yet. See:
125 : : https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */
126 : : if (raise) {
127 : : Py_BEGIN_ALLOW_THREADS
128 : : n = syscall(SYS_getrandom, dest, n, flags);
129 : : Py_END_ALLOW_THREADS
130 : : }
131 : : else {
132 : : n = syscall(SYS_getrandom, dest, n, flags);
133 : : }
134 : : # ifdef _Py_MEMORY_SANITIZER
135 : : if (n > 0) {
136 : : __msan_unpoison(dest, n);
137 : : }
138 : : # endif
139 : : #endif
140 : :
141 [ - + ]: 173561 : if (n < 0) {
142 : : /* ENOSYS: the syscall is not supported by the kernel.
143 : : EPERM: the syscall is blocked by a security policy (ex: SECCOMP)
144 : : or something else. */
145 [ # # # # ]: 0 : if (errno == ENOSYS || errno == EPERM) {
146 : 0 : getrandom_works = 0;
147 : 0 : return 0;
148 : : }
149 : :
150 : : /* getrandom(GRND_NONBLOCK) fails with EAGAIN if the system urandom
151 : : is not initialized yet. For _PyRandom_Init(), we ignore the
152 : : error and fall back on reading /dev/urandom which never blocks,
153 : : even if the system urandom is not initialized yet:
154 : : see the PEP 524. */
155 [ # # # # : 0 : if (errno == EAGAIN && !raise && !blocking) {
# # ]
156 : 0 : return 0;
157 : : }
158 : :
159 [ # # ]: 0 : if (errno == EINTR) {
160 [ # # ]: 0 : if (raise) {
161 [ # # ]: 0 : if (PyErr_CheckSignals()) {
162 : 0 : return -1;
163 : : }
164 : : }
165 : :
166 : : /* retry getrandom() if it was interrupted by a signal */
167 : 0 : continue;
168 : : }
169 : :
170 [ # # ]: 0 : if (raise) {
171 : 0 : PyErr_SetFromErrno(PyExc_OSError);
172 : : }
173 : 0 : return -1;
174 : : }
175 : :
176 : 173561 : dest += n;
177 : 173561 : size -= n;
178 : : }
179 : 173561 : return 1;
180 : : }
181 : :
182 : : #elif defined(HAVE_GETENTROPY)
183 : : #define PY_GETENTROPY 1
184 : :
185 : : /* Fill buffer with size pseudo-random bytes generated by getentropy():
186 : :
187 : : - Return 1 on success
188 : : - Return 0 if getentropy() syscall is not available (failed with ENOSYS or
189 : : EPERM).
190 : : - Raise an exception (if raise is non-zero) and return -1 on error:
191 : : if getentropy() failed with EINTR, raise is non-zero and the Python signal
192 : : handler raised an exception, or if getentropy() failed with a different
193 : : error.
194 : :
195 : : getentropy() is retried if it failed with EINTR: interrupted by a signal. */
196 : :
197 : : #if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability)
198 : : static int
199 : : py_getentropy(char *buffer, Py_ssize_t size, int raise)
200 : : __attribute__((availability(macos,introduced=10.12)))
201 : : __attribute__((availability(ios,introduced=10.0)))
202 : : __attribute__((availability(tvos,introduced=10.0)))
203 : : __attribute__((availability(watchos,introduced=3.0)));
204 : : #endif
205 : :
206 : : static int
207 : : py_getentropy(char *buffer, Py_ssize_t size, int raise)
208 : : {
209 : : /* Is getentropy() supported by the running kernel? Set to 0 if
210 : : getentropy() failed with ENOSYS or EPERM. */
211 : : static int getentropy_works = 1;
212 : :
213 : : if (!getentropy_works) {
214 : : return 0;
215 : : }
216 : :
217 : : while (size > 0) {
218 : : /* getentropy() is limited to returning up to 256 bytes. Call it
219 : : multiple times if more bytes are requested. */
220 : : Py_ssize_t len = Py_MIN(size, 256);
221 : : int res;
222 : :
223 : : if (raise) {
224 : : Py_BEGIN_ALLOW_THREADS
225 : : res = getentropy(buffer, len);
226 : : Py_END_ALLOW_THREADS
227 : : }
228 : : else {
229 : : res = getentropy(buffer, len);
230 : : }
231 : :
232 : : if (res < 0) {
233 : : /* ENOSYS: the syscall is not supported by the running kernel.
234 : : EPERM: the syscall is blocked by a security policy (ex: SECCOMP)
235 : : or something else. */
236 : : if (errno == ENOSYS || errno == EPERM) {
237 : : getentropy_works = 0;
238 : : return 0;
239 : : }
240 : :
241 : : if (errno == EINTR) {
242 : : if (raise) {
243 : : if (PyErr_CheckSignals()) {
244 : : return -1;
245 : : }
246 : : }
247 : :
248 : : /* retry getentropy() if it was interrupted by a signal */
249 : : continue;
250 : : }
251 : :
252 : : if (raise) {
253 : : PyErr_SetFromErrno(PyExc_OSError);
254 : : }
255 : : return -1;
256 : : }
257 : :
258 : : buffer += len;
259 : : size -= len;
260 : : }
261 : : return 1;
262 : : }
263 : : #endif /* defined(HAVE_GETENTROPY) && !(defined(__sun) && defined(__SVR4)) */
264 : :
265 : :
266 : : static struct {
267 : : int fd;
268 : : dev_t st_dev;
269 : : ino_t st_ino;
270 : : } urandom_cache = { -1 };
271 : :
272 : : /* Read random bytes from the /dev/urandom device:
273 : :
274 : : - Return 0 on success
275 : : - Raise an exception (if raise is non-zero) and return -1 on error
276 : :
277 : : Possible causes of errors:
278 : :
279 : : - open() failed with ENOENT, ENXIO, ENODEV, EACCES: the /dev/urandom device
280 : : was not found. For example, it was removed manually or not exposed in a
281 : : chroot or container.
282 : : - open() failed with a different error
283 : : - fstat() failed
284 : : - read() failed or returned 0
285 : :
286 : : read() is retried if it failed with EINTR: interrupted by a signal.
287 : :
288 : : The file descriptor of the device is kept open between calls to avoid using
289 : : many file descriptors when run in parallel from multiple threads:
290 : : see the issue #18756.
291 : :
292 : : st_dev and st_ino fields of the file descriptor (from fstat()) are cached to
293 : : check if the file descriptor was replaced by a different file (which is
294 : : likely a bug in the application): see the issue #21207.
295 : :
296 : : If the file descriptor was closed or replaced, open a new file descriptor
297 : : but don't close the old file descriptor: it probably points to something
298 : : important for some third-party code. */
299 : : static int
300 : 0 : dev_urandom(char *buffer, Py_ssize_t size, int raise)
301 : : {
302 : : int fd;
303 : : Py_ssize_t n;
304 : :
305 [ # # ]: 0 : if (raise) {
306 : : struct _Py_stat_struct st;
307 : : int fstat_result;
308 : :
309 [ # # ]: 0 : if (urandom_cache.fd >= 0) {
310 : 0 : Py_BEGIN_ALLOW_THREADS
311 : 0 : fstat_result = _Py_fstat_noraise(urandom_cache.fd, &st);
312 : 0 : Py_END_ALLOW_THREADS
313 : :
314 : : /* Does the fd point to the same thing as before? (issue #21207) */
315 [ # # ]: 0 : if (fstat_result
316 [ # # ]: 0 : || st.st_dev != urandom_cache.st_dev
317 [ # # ]: 0 : || st.st_ino != urandom_cache.st_ino) {
318 : : /* Something changed: forget the cached fd (but don't close it,
319 : : since it probably points to something important for some
320 : : third-party code). */
321 : 0 : urandom_cache.fd = -1;
322 : : }
323 : : }
324 [ # # ]: 0 : if (urandom_cache.fd >= 0)
325 : 0 : fd = urandom_cache.fd;
326 : : else {
327 : 0 : fd = _Py_open("/dev/urandom", O_RDONLY);
328 [ # # ]: 0 : if (fd < 0) {
329 [ # # # # ]: 0 : if (errno == ENOENT || errno == ENXIO ||
330 [ # # # # ]: 0 : errno == ENODEV || errno == EACCES) {
331 : 0 : PyErr_SetString(PyExc_NotImplementedError,
332 : : "/dev/urandom (or equivalent) not found");
333 : : }
334 : : /* otherwise, keep the OSError exception raised by _Py_open() */
335 : 0 : return -1;
336 : : }
337 [ # # ]: 0 : if (urandom_cache.fd >= 0) {
338 : : /* urandom_fd was initialized by another thread while we were
339 : : not holding the GIL, keep it. */
340 : 0 : close(fd);
341 : 0 : fd = urandom_cache.fd;
342 : : }
343 : : else {
344 [ # # ]: 0 : if (_Py_fstat(fd, &st)) {
345 : 0 : close(fd);
346 : 0 : return -1;
347 : : }
348 : : else {
349 : 0 : urandom_cache.fd = fd;
350 : 0 : urandom_cache.st_dev = st.st_dev;
351 : 0 : urandom_cache.st_ino = st.st_ino;
352 : : }
353 : : }
354 : : }
355 : :
356 : : do {
357 : 0 : n = _Py_read(fd, buffer, (size_t)size);
358 [ # # ]: 0 : if (n == -1)
359 : 0 : return -1;
360 [ # # ]: 0 : if (n == 0) {
361 : 0 : PyErr_Format(PyExc_RuntimeError,
362 : : "Failed to read %zi bytes from /dev/urandom",
363 : : size);
364 : 0 : return -1;
365 : : }
366 : :
367 : 0 : buffer += n;
368 : 0 : size -= n;
369 [ # # ]: 0 : } while (0 < size);
370 : : }
371 : : else {
372 : 0 : fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
373 [ # # ]: 0 : if (fd < 0) {
374 : 0 : return -1;
375 : : }
376 : :
377 [ # # ]: 0 : while (0 < size)
378 : : {
379 : : do {
380 : 0 : n = read(fd, buffer, (size_t)size);
381 [ # # # # ]: 0 : } while (n < 0 && errno == EINTR);
382 : :
383 [ # # ]: 0 : if (n <= 0) {
384 : : /* stop on error or if read(size) returned 0 */
385 : 0 : close(fd);
386 : 0 : return -1;
387 : : }
388 : :
389 : 0 : buffer += n;
390 : 0 : size -= n;
391 : : }
392 : 0 : close(fd);
393 : : }
394 : 0 : return 0;
395 : : }
396 : :
397 : : static void
398 : 2956 : dev_urandom_close(void)
399 : : {
400 [ - + ]: 2956 : if (urandom_cache.fd >= 0) {
401 : 0 : close(urandom_cache.fd);
402 : 0 : urandom_cache.fd = -1;
403 : : }
404 : 2956 : }
405 : : #endif /* !MS_WINDOWS */
406 : :
407 : :
408 : : /* Fill buffer with pseudo-random bytes generated by a linear congruent
409 : : generator (LCG):
410 : :
411 : : x(n+1) = (x(n) * 214013 + 2531011) % 2^32
412 : :
413 : : Use bits 23..16 of x(n) to generate a byte. */
414 : : static void
415 : 16 : lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
416 : : {
417 : : size_t index;
418 : : unsigned int x;
419 : :
420 : 16 : x = x0;
421 [ + + ]: 400 : for (index=0; index < size; index++) {
422 : 384 : x *= 214013;
423 : 384 : x += 2531011;
424 : : /* modulo 2 ^ (8 * sizeof(int)) */
425 : 384 : buffer[index] = (x >> 16) & 0xff;
426 : : }
427 : 16 : }
428 : :
429 : : /* Read random bytes:
430 : :
431 : : - Return 0 on success
432 : : - Raise an exception (if raise is non-zero) and return -1 on error
433 : :
434 : : Used sources of entropy ordered by preference, preferred source first:
435 : :
436 : : - BCryptGenRandom() on Windows
437 : : - getrandom() function (ex: Linux and Solaris): call py_getrandom()
438 : : - getentropy() function (ex: OpenBSD): call py_getentropy()
439 : : - /dev/urandom device
440 : :
441 : : Read from the /dev/urandom device if getrandom() or getentropy() function
442 : : is not available or does not work.
443 : :
444 : : Prefer getrandom() over getentropy() because getrandom() supports blocking
445 : : and non-blocking mode: see the PEP 524. Python requires non-blocking RNG at
446 : : startup to initialize its hash secret, but os.urandom() must block until the
447 : : system urandom is initialized (at least on Linux 3.17 and newer).
448 : :
449 : : Prefer getrandom() and getentropy() over reading directly /dev/urandom
450 : : because these functions don't need file descriptors and so avoid ENFILE or
451 : : EMFILE errors (too many open files): see the issue #18756.
452 : :
453 : : Only the getrandom() function supports non-blocking mode.
454 : :
455 : : Only use RNG running in the kernel. They are more secure because it is
456 : : harder to get the internal state of a RNG running in the kernel land than a
457 : : RNG running in the user land. The kernel has a direct access to the hardware
458 : : and has access to hardware RNG, they are used as entropy sources.
459 : :
460 : : Note: the OpenSSL RAND_pseudo_bytes() function does not automatically reseed
461 : : its RNG on fork(), two child processes (with the same pid) generate the same
462 : : random numbers: see issue #18747. Kernel RNGs don't have this issue,
463 : : they have access to good quality entropy sources.
464 : :
465 : : If raise is zero:
466 : :
467 : : - Don't raise an exception on error
468 : : - Don't call the Python signal handler (don't call PyErr_CheckSignals()) if
469 : : a function fails with EINTR: retry directly the interrupted function
470 : : - Don't release the GIL to call functions.
471 : : */
472 : : static int
473 : 173564 : pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise)
474 : : {
475 : : #if defined(PY_GETRANDOM) || defined(PY_GETENTROPY)
476 : : int res;
477 : : #endif
478 : :
479 [ - + ]: 173564 : if (size < 0) {
480 [ # # ]: 0 : if (raise) {
481 : 0 : PyErr_Format(PyExc_ValueError,
482 : : "negative argument not allowed");
483 : : }
484 : 0 : return -1;
485 : : }
486 : :
487 [ + + ]: 173564 : if (size == 0) {
488 : 3 : return 0;
489 : : }
490 : :
491 : : #ifdef MS_WINDOWS
492 : : return win32_urandom((unsigned char *)buffer, size, raise);
493 : : #else
494 : :
495 : : #if defined(PY_GETRANDOM) || defined(PY_GETENTROPY)
496 : : if (HAVE_GETENTRYPY_GETRANDOM_RUNTIME) {
497 : : #ifdef PY_GETRANDOM
498 : 173561 : res = py_getrandom(buffer, size, blocking, raise);
499 : : #else
500 : : res = py_getentropy(buffer, size, raise);
501 : : #endif
502 [ - + ]: 173561 : if (res < 0) {
503 : 0 : return -1;
504 : : }
505 [ + - ]: 173561 : if (res == 1) {
506 : 173561 : return 0;
507 : : }
508 : : /* getrandom() or getentropy() function is not available: failed with
509 : : ENOSYS or EPERM. Fall back on reading from /dev/urandom. */
510 : : } /* end of availability block */
511 : : #endif
512 : :
513 : 0 : return dev_urandom(buffer, size, raise);
514 : : #endif
515 : : }
516 : :
517 : : /* Fill buffer with size pseudo-random bytes from the operating system random
518 : : number generator (RNG). It is suitable for most cryptographic purposes
519 : : except long living private keys for asymmetric encryption.
520 : :
521 : : On Linux 3.17 and newer, the getrandom() syscall is used in blocking mode:
522 : : block until the system urandom entropy pool is initialized (128 bits are
523 : : collected by the kernel).
524 : :
525 : : Return 0 on success. Raise an exception and return -1 on error. */
526 : : int
527 : 168899 : _PyOS_URandom(void *buffer, Py_ssize_t size)
528 : : {
529 : 168899 : return pyurandom(buffer, size, 1, 1);
530 : : }
531 : :
532 : : /* Fill buffer with size pseudo-random bytes from the operating system random
533 : : number generator (RNG). It is not suitable for cryptographic purpose.
534 : :
535 : : On Linux 3.17 and newer (when getrandom() syscall is used), if the system
536 : : urandom is not initialized yet, the function returns "weak" entropy read
537 : : from /dev/urandom.
538 : :
539 : : Return 0 on success. Raise an exception and return -1 on error. */
540 : : int
541 : 1757 : _PyOS_URandomNonblock(void *buffer, Py_ssize_t size)
542 : : {
543 : 1757 : return pyurandom(buffer, size, 0, 1);
544 : : }
545 : :
546 : :
547 : : PyStatus
548 : 2967 : _Py_HashRandomization_Init(const PyConfig *config)
549 : : {
550 : 2967 : void *secret = &_Py_HashSecret;
551 : 2967 : Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
552 : :
553 [ + + ]: 2967 : if (_Py_HashSecret_Initialized) {
554 : 34 : return _PyStatus_OK();
555 : : }
556 : 2933 : _Py_HashSecret_Initialized = 1;
557 : :
558 [ + + ]: 2933 : if (config->use_hash_seed) {
559 [ + + ]: 25 : if (config->hash_seed == 0) {
560 : : /* disable the randomized hash */
561 : 9 : memset(secret, 0, secret_size);
562 : : }
563 : : else {
564 : : /* use the specified hash seed */
565 : 16 : lcg_urandom(config->hash_seed, secret, secret_size);
566 : : }
567 : : }
568 : : else {
569 : : /* use a random hash seed */
570 : : int res;
571 : :
572 : : /* _PyRandom_Init() is called very early in the Python initialization
573 : : and so exceptions cannot be used (use raise=0).
574 : :
575 : : _PyRandom_Init() must not block Python initialization: call
576 : : pyurandom() is non-blocking mode (blocking=0): see the PEP 524. */
577 : 2908 : res = pyurandom(secret, secret_size, 0, 0);
578 [ - + ]: 2908 : if (res < 0) {
579 : 0 : return _PyStatus_ERR("failed to get random numbers "
580 : : "to initialize Python");
581 : : }
582 : : }
583 : 2933 : return _PyStatus_OK();
584 : : }
585 : :
586 : :
587 : : void
588 : 2956 : _Py_HashRandomization_Fini(void)
589 : : {
590 : : #ifndef MS_WINDOWS
591 : 2956 : dev_urandom_close();
592 : : #endif
593 : 2956 : }
|