blob: 9b40e8ac9ffed3c9fbb75b009f1d4043c1676c9d [file] [log] [blame]
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001From 035ba5da3e53e45c712b39fe1f6fb743e697c032 Mon Sep 17 00:00:00 2001
2From: Victor Stinner <victor.stinner@gmail.com>
3Date: Mon, 9 Jan 2017 11:18:53 +0100
4Subject: [PATCH] Issue #29157: Prefer getrandom() over getentropy()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05005
6Copy and then adapt Python/random.c from default branch. Difference between 3.5
7and default branches:
8
9* Python 3.5 only uses getrandom() in non-blocking mode: flags=GRND_NONBLOCK
10* If getrandom() fails with EAGAIN: py_getrandom() immediately fails and
11 remembers that getrandom() doesn't work.
12* Python 3.5 has no _PyOS_URandomNonblock() function: _PyOS_URandom()
13 works in non-blocking mode on Python 3.5
14
Brad Bishopd7bf8c12018-02-25 22:55:05 -050015Upstream-Status: Backport [https://github.com/python/cpython/commit/035ba5da3e53e45c712b39fe1f6fb743e697c032]
16Signed-off-by: Alexander Kanavin <alexander.kanavin@intel.com>
Brad Bishop6e60e8b2018-02-01 10:27:11 -050017
Brad Bishopd7bf8c12018-02-25 22:55:05 -050018---
19 Python/random.c | 494 +++++++++++++++++++++++++++++++++-----------------------
20 1 file changed, 294 insertions(+), 200 deletions(-)
21
22diff --git a/Python/random.c b/Python/random.c
23index d203939..31f61d0 100644
24--- a/Python/random.c
25+++ b/Python/random.c
Brad Bishop6e60e8b2018-02-01 10:27:11 -050026@@ -1,6 +1,9 @@
27 #include "Python.h"
28 #ifdef MS_WINDOWS
29 # include <windows.h>
30+/* All sample MSDN wincrypt programs include the header below. It is at least
31+ * required with Min GW. */
32+# include <wincrypt.h>
33 #else
34 # include <fcntl.h>
35 # ifdef HAVE_SYS_STAT_H
Brad Bishopd7bf8c12018-02-25 22:55:05 -050036@@ -37,10 +40,9 @@ win32_urandom_init(int raise)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050037 return 0;
38
39 error:
40- if (raise)
41+ if (raise) {
42 PyErr_SetFromWindowsErr(0);
43- else
44- Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
45+ }
46 return -1;
47 }
48
Brad Bishopd7bf8c12018-02-25 22:55:05 -050049@@ -53,8 +55,9 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050050
51 if (hCryptProv == 0)
52 {
53- if (win32_urandom_init(raise) == -1)
54+ if (win32_urandom_init(raise) == -1) {
55 return -1;
56+ }
57 }
58
59 while (size > 0)
Brad Bishopd7bf8c12018-02-25 22:55:05 -050060@@ -63,11 +66,9 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050061 if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
62 {
63 /* CryptGenRandom() failed */
64- if (raise)
65+ if (raise) {
66 PyErr_SetFromWindowsErr(0);
67- else
68- Py_FatalError("Failed to initialized the randomized hash "
69- "secret using CryptoGen)");
70+ }
71 return -1;
72 }
73 buffer += chunk;
Brad Bishopd7bf8c12018-02-25 22:55:05 -050074@@ -76,58 +77,23 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050075 return 0;
76 }
77
78-/* Issue #25003: Don't use getentropy() on Solaris (available since
79- * Solaris 11.3), it is blocking whereas os.urandom() should not block. */
80-#elif defined(HAVE_GETENTROPY) && !defined(sun)
81-#define PY_GETENTROPY 1
82-
83-/* Fill buffer with size pseudo-random bytes generated by getentropy().
84- Return 0 on success, or raise an exception and return -1 on error.
85-
86- If fatal is nonzero, call Py_FatalError() instead of raising an exception
87- on error. */
88-static int
89-py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
90-{
91- while (size > 0) {
92- Py_ssize_t len = Py_MIN(size, 256);
93- int res;
94-
95- if (!fatal) {
96- Py_BEGIN_ALLOW_THREADS
97- res = getentropy(buffer, len);
98- Py_END_ALLOW_THREADS
99-
100- if (res < 0) {
101- PyErr_SetFromErrno(PyExc_OSError);
102- return -1;
103- }
104- }
105- else {
106- res = getentropy(buffer, len);
107- if (res < 0)
108- Py_FatalError("getentropy() failed");
109- }
110-
111- buffer += len;
112- size -= len;
113- }
114- return 0;
115-}
116-
117-#else
118+#else /* !MS_WINDOWS */
119
120 #if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL)
121 #define PY_GETRANDOM 1
122
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500123-/* Call getrandom()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500124+/* Call getrandom() to get random bytes:
125+
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500126 - Return 1 on success
127- - Return 0 if getrandom() syscall is not available (failed with ENOSYS or
128- EPERM) or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom
129- not initialized yet) and raise=0.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500130+ - Return 0 if getrandom() is not available (failed with ENOSYS or EPERM),
131+ or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom not
132+ initialized yet).
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500133 - Raise an exception (if raise is non-zero) and return -1 on error:
134- getrandom() failed with EINTR and the Python signal handler raised an
135- exception, or getrandom() failed with a different error. */
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500136+ if getrandom() failed with EINTR, raise is non-zero and the Python signal
137+ handler raised an exception, or if getrandom() failed with a different
138+ error.
139+
140+ getrandom() is retried if it failed with EINTR: interrupted by a signal. */
141 static int
142 py_getrandom(void *buffer, Py_ssize_t size, int raise)
143 {
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500144@@ -142,16 +108,19 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500145 * see https://bugs.python.org/issue26839. To avoid this, use the
146 * GRND_NONBLOCK flag. */
147 const int flags = GRND_NONBLOCK;
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500148+ char *dest;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500149 long n;
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500150
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500151 if (!getrandom_works) {
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500152 return 0;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500153 }
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500154
155+ dest = buffer;
156 while (0 < size) {
157 #ifdef sun
158 /* Issue #26735: On Solaris, getrandom() is limited to returning up
159- to 1024 bytes */
160+ to 1024 bytes. Call it multiple times if more bytes are
161+ requested. */
162 n = Py_MIN(size, 1024);
163 #else
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500164 n = Py_MIN(size, LONG_MAX);
165@@ -161,34 +130,35 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500166 #ifdef HAVE_GETRANDOM
167 if (raise) {
168 Py_BEGIN_ALLOW_THREADS
169- n = getrandom(buffer, n, flags);
170+ n = getrandom(dest, n, flags);
171 Py_END_ALLOW_THREADS
172 }
173 else {
174- n = getrandom(buffer, n, flags);
175+ n = getrandom(dest, n, flags);
176 }
177 #else
178 /* On Linux, use the syscall() function because the GNU libc doesn't
179- * expose the Linux getrandom() syscall yet. See:
180- * https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */
181+ expose the Linux getrandom() syscall yet. See:
182+ https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */
183 if (raise) {
184 Py_BEGIN_ALLOW_THREADS
185- n = syscall(SYS_getrandom, buffer, n, flags);
186+ n = syscall(SYS_getrandom, dest, n, flags);
187 Py_END_ALLOW_THREADS
188 }
189 else {
190- n = syscall(SYS_getrandom, buffer, n, flags);
191+ n = syscall(SYS_getrandom, dest, n, flags);
192 }
193 #endif
194
195 if (n < 0) {
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500196- /* ENOSYS: getrandom() syscall not supported by the kernel (but
197- * maybe supported by the host which built Python). EPERM:
198- * getrandom() syscall blocked by SECCOMP or something else. */
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500199+ /* ENOSYS: the syscall is not supported by the kernel.
200+ EPERM: the syscall is blocked by a security policy (ex: SECCOMP)
201+ or something else. */
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500202 if (errno == ENOSYS || errno == EPERM) {
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500203 getrandom_works = 0;
204 return 0;
205 }
206+
207 if (errno == EAGAIN) {
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500208 /* getrandom(GRND_NONBLOCK) fails with EAGAIN if the system
209 urandom is not initialiazed yet. In this case, fall back on
210@@ -202,169 +172,225 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500211 }
212
213 if (errno == EINTR) {
214- if (PyErr_CheckSignals()) {
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500215- if (!raise) {
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500216- Py_FatalError("getrandom() interrupted by a signal");
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500217+ if (raise) {
218+ if (PyErr_CheckSignals()) {
219+ return -1;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500220 }
221- return -1;
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500222 }
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500223
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500224- /* retry getrandom() */
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500225+ /* retry getrandom() if it was interrupted by a signal */
226 continue;
227 }
228
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500229 if (raise) {
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500230 PyErr_SetFromErrno(PyExc_OSError);
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500231 }
232- else {
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500233- Py_FatalError("getrandom() failed");
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500234- }
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500235 return -1;
236 }
237
238- buffer += n;
239+ dest += n;
240 size -= n;
241 }
242 return 1;
243 }
244-#endif
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500245
246-static struct {
247- int fd;
248- dev_t st_dev;
249- ino_t st_ino;
250-} urandom_cache = { -1 };
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500251+#elif defined(HAVE_GETENTROPY)
252+#define PY_GETENTROPY 1
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500253
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500254+/* Fill buffer with size pseudo-random bytes generated by getentropy():
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500255
256-/* Read 'size' random bytes from py_getrandom(). Fall back on reading from
257- /dev/urandom if getrandom() is not available.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500258+ - Return 1 on success
259+ - Return 0 if getentropy() syscall is not available (failed with ENOSYS or
260+ EPERM).
261+ - Raise an exception (if raise is non-zero) and return -1 on error:
262+ if getentropy() failed with EINTR, raise is non-zero and the Python signal
263+ handler raised an exception, or if getentropy() failed with a different
264+ error.
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500265
266- Call Py_FatalError() on error. */
267-static void
268-dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500269+ getentropy() is retried if it failed with EINTR: interrupted by a signal. */
270+static int
271+py_getentropy(char *buffer, Py_ssize_t size, int raise)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500272 {
273- int fd;
274- Py_ssize_t n;
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500275+ /* Is getentropy() supported by the running kernel? Set to 0 if
276+ getentropy() failed with ENOSYS or EPERM. */
277+ static int getentropy_works = 1;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500278
279- assert (0 < size);
280-
281-#ifdef PY_GETRANDOM
282- if (py_getrandom(buffer, size, 0) == 1) {
283- return;
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500284+ if (!getentropy_works) {
285+ return 0;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500286 }
287- /* getrandom() failed with ENOSYS or EPERM,
288- fall back on reading /dev/urandom */
289-#endif
290
291- fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
292- if (fd < 0) {
293- Py_FatalError("Failed to open /dev/urandom");
294- }
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500295+ while (size > 0) {
296+ /* getentropy() is limited to returning up to 256 bytes. Call it
297+ multiple times if more bytes are requested. */
298+ Py_ssize_t len = Py_MIN(size, 256);
299+ int res;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500300
301- while (0 < size)
302- {
303- do {
304- n = read(fd, buffer, (size_t)size);
305- } while (n < 0 && errno == EINTR);
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500306+ if (raise) {
307+ Py_BEGIN_ALLOW_THREADS
308+ res = getentropy(buffer, len);
309+ Py_END_ALLOW_THREADS
310+ }
311+ else {
312+ res = getentropy(buffer, len);
313+ }
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500314
315- if (n <= 0) {
316- /* read() failed or returned 0 bytes */
317- Py_FatalError("Failed to read bytes from /dev/urandom");
318- break;
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500319+ if (res < 0) {
320+ /* ENOSYS: the syscall is not supported by the running kernel.
321+ EPERM: the syscall is blocked by a security policy (ex: SECCOMP)
322+ or something else. */
323+ if (errno == ENOSYS || errno == EPERM) {
324+ getentropy_works = 0;
325+ return 0;
326+ }
327+
328+ if (errno == EINTR) {
329+ if (raise) {
330+ if (PyErr_CheckSignals()) {
331+ return -1;
332+ }
333+ }
334+
335+ /* retry getentropy() if it was interrupted by a signal */
336+ continue;
337+ }
338+
339+ if (raise) {
340+ PyErr_SetFromErrno(PyExc_OSError);
341+ }
342+ return -1;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500343 }
344- buffer += n;
345- size -= n;
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500346+
347+ buffer += len;
348+ size -= len;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500349 }
350- close(fd);
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500351+ return 1;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500352 }
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500353+#endif /* defined(HAVE_GETENTROPY) && !defined(sun) */
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500354
355-/* Read 'size' random bytes from py_getrandom(). Fall back on reading from
356- /dev/urandom if getrandom() is not available.
357
358- Return 0 on success. Raise an exception and return -1 on error. */
359+static struct {
360+ int fd;
361+ dev_t st_dev;
362+ ino_t st_ino;
363+} urandom_cache = { -1 };
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500364+
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500365+/* Read random bytes from the /dev/urandom device:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500366+
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500367+ - Return 0 on success
368+ - Raise an exception (if raise is non-zero) and return -1 on error
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500369+
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500370+ Possible causes of errors:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500371+
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500372+ - open() failed with ENOENT, ENXIO, ENODEV, EACCES: the /dev/urandom device
373+ was not found. For example, it was removed manually or not exposed in a
374+ chroot or container.
375+ - open() failed with a different error
376+ - fstat() failed
377+ - read() failed or returned 0
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500378+
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500379+ read() is retried if it failed with EINTR: interrupted by a signal.
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500380+
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500381+ The file descriptor of the device is kept open between calls to avoid using
382+ many file descriptors when run in parallel from multiple threads:
383+ see the issue #18756.
384+
385+ st_dev and st_ino fields of the file descriptor (from fstat()) are cached to
386+ check if the file descriptor was replaced by a different file (which is
387+ likely a bug in the application): see the issue #21207.
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500388+
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500389+ If the file descriptor was closed or replaced, open a new file descriptor
390+ but don't close the old file descriptor: it probably points to something
391+ important for some third-party code. */
392 static int
393-dev_urandom_python(char *buffer, Py_ssize_t size)
394+dev_urandom(char *buffer, Py_ssize_t size, int raise)
395 {
396 int fd;
397 Py_ssize_t n;
398- struct _Py_stat_struct st;
399-#ifdef PY_GETRANDOM
400- int res;
401-#endif
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500402-
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500403- if (size <= 0)
404- return 0;
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500405
406-#ifdef PY_GETRANDOM
407- res = py_getrandom(buffer, size, 1);
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500408- if (res < 0) {
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500409- return -1;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500410- }
411- if (res == 1) {
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500412- return 0;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500413- }
414- /* getrandom() failed with ENOSYS or EPERM,
415- fall back on reading /dev/urandom */
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500416-#endif
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500417+ if (raise) {
418+ struct _Py_stat_struct st;
419
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500420- if (urandom_cache.fd >= 0) {
421- /* Does the fd point to the same thing as before? (issue #21207) */
422- if (_Py_fstat_noraise(urandom_cache.fd, &st)
423- || st.st_dev != urandom_cache.st_dev
424- || st.st_ino != urandom_cache.st_ino) {
425- /* Something changed: forget the cached fd (but don't close it,
426- since it probably points to something important for some
427- third-party code). */
428- urandom_cache.fd = -1;
429- }
430- }
431- if (urandom_cache.fd >= 0)
432- fd = urandom_cache.fd;
433- else {
434- fd = _Py_open("/dev/urandom", O_RDONLY);
435- if (fd < 0) {
436- if (errno == ENOENT || errno == ENXIO ||
437- errno == ENODEV || errno == EACCES)
438- PyErr_SetString(PyExc_NotImplementedError,
439- "/dev/urandom (or equivalent) not found");
440- /* otherwise, keep the OSError exception raised by _Py_open() */
441- return -1;
442- }
443 if (urandom_cache.fd >= 0) {
444- /* urandom_fd was initialized by another thread while we were
445- not holding the GIL, keep it. */
446- close(fd);
447- fd = urandom_cache.fd;
448+ /* Does the fd point to the same thing as before? (issue #21207) */
449+ if (_Py_fstat_noraise(urandom_cache.fd, &st)
450+ || st.st_dev != urandom_cache.st_dev
451+ || st.st_ino != urandom_cache.st_ino) {
452+ /* Something changed: forget the cached fd (but don't close it,
453+ since it probably points to something important for some
454+ third-party code). */
455+ urandom_cache.fd = -1;
456+ }
457 }
458+ if (urandom_cache.fd >= 0)
459+ fd = urandom_cache.fd;
460 else {
461- if (_Py_fstat(fd, &st)) {
462- close(fd);
463+ fd = _Py_open("/dev/urandom", O_RDONLY);
464+ if (fd < 0) {
465+ if (errno == ENOENT || errno == ENXIO ||
466+ errno == ENODEV || errno == EACCES) {
467+ PyErr_SetString(PyExc_NotImplementedError,
468+ "/dev/urandom (or equivalent) not found");
469+ }
470+ /* otherwise, keep the OSError exception raised by _Py_open() */
471 return -1;
472 }
473+ if (urandom_cache.fd >= 0) {
474+ /* urandom_fd was initialized by another thread while we were
475+ not holding the GIL, keep it. */
476+ close(fd);
477+ fd = urandom_cache.fd;
478+ }
479 else {
480- urandom_cache.fd = fd;
481- urandom_cache.st_dev = st.st_dev;
482- urandom_cache.st_ino = st.st_ino;
483+ if (_Py_fstat(fd, &st)) {
484+ close(fd);
485+ return -1;
486+ }
487+ else {
488+ urandom_cache.fd = fd;
489+ urandom_cache.st_dev = st.st_dev;
490+ urandom_cache.st_ino = st.st_ino;
491+ }
492 }
493 }
494- }
495
496- do {
497- n = _Py_read(fd, buffer, (size_t)size);
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500498- if (n == -1) {
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500499- return -1;
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500500- }
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500501- if (n == 0) {
502- PyErr_Format(PyExc_RuntimeError,
503- "Failed to read %zi bytes from /dev/urandom",
504- size);
505+ do {
506+ n = _Py_read(fd, buffer, (size_t)size);
507+ if (n == -1)
508+ return -1;
509+ if (n == 0) {
510+ PyErr_Format(PyExc_RuntimeError,
511+ "Failed to read %zi bytes from /dev/urandom",
512+ size);
513+ return -1;
514+ }
515+
516+ buffer += n;
517+ size -= n;
518+ } while (0 < size);
519+ }
520+ else {
521+ fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
522+ if (fd < 0) {
523 return -1;
524 }
525
526- buffer += n;
527- size -= n;
528- } while (0 < size);
529+ while (0 < size)
530+ {
531+ do {
532+ n = read(fd, buffer, (size_t)size);
533+ } while (n < 0 && errno == EINTR);
534
535+ if (n <= 0) {
536+ /* stop on error or if read(size) returned 0 */
537+ close(fd);
538+ return -1;
539+ }
540+
541+ buffer += n;
542+ size -= n;
543+ }
544+ close(fd);
545+ }
546 return 0;
547 }
548
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500549@@ -376,8 +402,8 @@ dev_urandom_close(void)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500550 urandom_cache.fd = -1;
551 }
552 }
553+#endif /* !MS_WINDOWS */
554
555-#endif
556
557 /* Fill buffer with pseudo-random bytes generated by a linear congruent
558 generator (LCG):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500559@@ -400,31 +426,100 @@ lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500560 }
561 }
562
563-/* Fill buffer with size pseudo-random bytes from the operating system random
564- number generator (RNG). It is suitable for most cryptographic purposes
565- except long living private keys for asymmetric encryption.
566+/* Read random bytes:
567
568- Return 0 on success, raise an exception and return -1 on error. */
569-int
570-_PyOS_URandom(void *buffer, Py_ssize_t size)
571+ - Return 0 on success
572+ - Raise an exception (if raise is non-zero) and return -1 on error
573+
574+ Used sources of entropy ordered by preference, preferred source first:
575+
576+ - CryptGenRandom() on Windows
577+ - getrandom() function (ex: Linux and Solaris): call py_getrandom()
578+ - getentropy() function (ex: OpenBSD): call py_getentropy()
579+ - /dev/urandom device
580+
581+ Read from the /dev/urandom device if getrandom() or getentropy() function
582+ is not available or does not work.
583+
584+ Prefer getrandom() over getentropy() because getrandom() supports blocking
585+ and non-blocking mode and Python requires non-blocking RNG at startup to
586+ initialize its hash secret: see the PEP 524.
587+
588+ Prefer getrandom() and getentropy() over reading directly /dev/urandom
589+ because these functions don't need file descriptors and so avoid ENFILE or
590+ EMFILE errors (too many open files): see the issue #18756.
591+
592+ Only use RNG running in the kernel. They are more secure because it is
593+ harder to get the internal state of a RNG running in the kernel land than a
594+ RNG running in the user land. The kernel has a direct access to the hardware
595+ and has access to hardware RNG, they are used as entropy sources.
596+
597+ Note: the OpenSSL RAND_pseudo_bytes() function does not automatically reseed
598+ its RNG on fork(), two child processes (with the same pid) generate the same
599+ random numbers: see issue #18747. Kernel RNGs don't have this issue,
600+ they have access to good quality entropy sources.
601+
602+ If raise is zero:
603+
604+ - Don't raise an exception on error
605+ - Don't call the Python signal handler (don't call PyErr_CheckSignals()) if
606+ a function fails with EINTR: retry directly the interrupted function
607+ - Don't release the GIL to call functions.
608+*/
609+static int
610+pyurandom(void *buffer, Py_ssize_t size, int raise)
611 {
612+#if defined(PY_GETRANDOM) || defined(PY_GETENTROPY)
613+ int res;
614+#endif
615+
616 if (size < 0) {
617- PyErr_Format(PyExc_ValueError,
618- "negative argument not allowed");
619+ if (raise) {
620+ PyErr_Format(PyExc_ValueError,
621+ "negative argument not allowed");
622+ }
623 return -1;
624 }
625- if (size == 0)
626+
627+ if (size == 0) {
628 return 0;
629+ }
630
631 #ifdef MS_WINDOWS
632- return win32_urandom((unsigned char *)buffer, size, 1);
633-#elif defined(PY_GETENTROPY)
634- return py_getentropy(buffer, size, 0);
635+ return win32_urandom((unsigned char *)buffer, size, raise);
636+#else
637+
638+#if defined(PY_GETRANDOM) || defined(PY_GETENTROPY)
639+#ifdef PY_GETRANDOM
640+ res = py_getrandom(buffer, size, raise);
641 #else
642- return dev_urandom_python((char*)buffer, size);
643+ res = py_getentropy(buffer, size, raise);
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500644+#endif
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500645+ if (res < 0) {
646+ return -1;
647+ }
648+ if (res == 1) {
649+ return 0;
650+ }
651+ /* getrandom() or getentropy() function is not available: failed with
652+ ENOSYS, EPERM or EAGAIN. Fall back on reading from /dev/urandom. */
653+#endif
654+
655+ return dev_urandom(buffer, size, raise);
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500656 #endif
657 }
658
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500659+/* Fill buffer with size pseudo-random bytes from the operating system random
660+ number generator (RNG). It is suitable for most cryptographic purposes
661+ except long living private keys for asymmetric encryption.
662+
663+ Return 0 on success. Raise an exception and return -1 on error. */
664+int
665+_PyOS_URandom(void *buffer, Py_ssize_t size)
666+{
667+ return pyurandom(buffer, size, 1);
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500668+}
669+
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500670 void
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500671 _PyRandom_Init(void)
672 {
673@@ -463,13 +558,14 @@ _PyRandom_Init(void)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500674 }
675 }
676 else {
677-#ifdef MS_WINDOWS
678- (void)win32_urandom(secret, secret_size, 0);
679-#elif defined(PY_GETENTROPY)
680- (void)py_getentropy(secret, secret_size, 1);
681-#else
682- dev_urandom_noraise(secret, secret_size);
683-#endif
684+ int res;
685+
686+ /* _PyRandom_Init() is called very early in the Python initialization
687+ and so exceptions cannot be used (use raise=0). */
688+ res = pyurandom(secret, secret_size, 0);
689+ if (res < 0) {
690+ Py_FatalError("failed to get random numbers to initialize Python");
691+ }
692 }
693 }
694
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500695@@ -481,8 +577,6 @@ _PyRandom_Fini(void)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500696 CryptReleaseContext(hCryptProv, 0);
697 hCryptProv = 0;
698 }
699-#elif defined(PY_GETENTROPY)
700- /* nothing to clean */
701 #else
702 dev_urandom_close();
703 #endif