blob: dcc3aab79f4f2c543f9f55267dbaceefd33bbb36 [file] [log] [blame]
Georg Brandl2daf6ae2012-02-20 19:54:16 +01001#include "Python.h"
2#ifdef MS_WINDOWS
Victor Stinner59f7fb22015-03-18 14:39:33 +01003# include <windows.h>
Georg Brandl2daf6ae2012-02-20 19:54:16 +01004#else
Victor Stinner59f7fb22015-03-18 14:39:33 +01005# include <fcntl.h>
6# ifdef HAVE_SYS_STAT_H
7# include <sys/stat.h>
8# endif
9# ifdef HAVE_SYS_SYSCALL_H
10# include <sys/syscall.h>
11# if defined(__linux__) && defined(SYS_getrandom)
12# define HAVE_GETRANDOM
13# endif
14# endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +010015#endif
16
Benjamin Peterson69e97272012-02-21 11:08:50 -050017#ifdef Py_DEBUG
18int _Py_HashSecret_Initialized = 0;
19#else
20static int _Py_HashSecret_Initialized = 0;
21#endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +010022
23#ifdef MS_WINDOWS
Georg Brandl2daf6ae2012-02-20 19:54:16 +010024static HCRYPTPROV hCryptProv = 0;
25
26static int
27win32_urandom_init(int raise)
28{
Georg Brandl2daf6ae2012-02-20 19:54:16 +010029 /* Acquire context */
Martin v. Löwis3f50bf62013-01-25 14:06:18 +010030 if (!CryptAcquireContext(&hCryptProv, NULL, NULL,
31 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010032 goto error;
33
34 return 0;
35
36error:
37 if (raise)
38 PyErr_SetFromWindowsErr(0);
39 else
40 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
41 return -1;
42}
43
44/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
Victor Stinner4d6a3d62014-12-21 01:16:38 +010045 API. Return 0 on success, or raise an exception and return -1 on error. */
Georg Brandl2daf6ae2012-02-20 19:54:16 +010046static int
47win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
48{
49 Py_ssize_t chunk;
50
51 if (hCryptProv == 0)
52 {
53 if (win32_urandom_init(raise) == -1)
54 return -1;
55 }
56
57 while (size > 0)
58 {
59 chunk = size > INT_MAX ? INT_MAX : size;
Victor Stinner0c083462013-11-15 23:26:25 +010060 if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010061 {
62 /* CryptGenRandom() failed */
63 if (raise)
64 PyErr_SetFromWindowsErr(0);
65 else
66 Py_FatalError("Failed to initialized the randomized hash "
67 "secret using CryptoGen)");
68 return -1;
69 }
70 buffer += chunk;
71 size -= chunk;
72 }
73 return 0;
74}
Georg Brandl2daf6ae2012-02-20 19:54:16 +010075
Victor Stinner4d6a3d62014-12-21 01:16:38 +010076#elif HAVE_GETENTROPY
77/* Fill buffer with size pseudo-random bytes generated by getentropy().
78 Return 0 on success, or raise an exception and return -1 on error.
Georg Brandl2daf6ae2012-02-20 19:54:16 +010079
Victor Stinner4d6a3d62014-12-21 01:16:38 +010080 If fatal is nonzero, call Py_FatalError() instead of raising an exception
81 on error. */
82static int
83py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
84{
85 while (size > 0) {
86 Py_ssize_t len = Py_MIN(size, 256);
87 int res = getentropy(buffer, len);
88 if (res < 0) {
89 if (fatal) {
90 Py_FatalError("getentropy() failed");
91 }
92 else {
93 PyErr_SetFromErrno(PyExc_OSError);
94 return -1;
95 }
96 }
97 buffer += len;
98 size -= len;
99 }
100 return 0;
101}
102
Victor Stinner59f7fb22015-03-18 14:39:33 +0100103#else /* !HAVE_GETENTROPY */
104
105#ifdef HAVE_GETRANDOM
106static int
107py_getrandom(void *buffer, Py_ssize_t size, int raise)
108{
109 /* is getrandom() supported by the running kernel?
110 * need Linux kernel 3.17 or later */
111 static int getrandom_works = 1;
112 /* Use /dev/urandom, block if the kernel has no entropy */
113 const int flags = 0;
114 int n;
115
116 if (!getrandom_works)
117 return 0;
118
119 while (0 < size) {
120 errno = 0;
121 /* the libc doesn't expose getrandom() yet, see:
122 * https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */
123 n = syscall(SYS_getrandom, buffer, size, flags);
124 if (n < 0) {
125 if (errno == ENOSYS) {
126 getrandom_works = 0;
127 return 0;
128 }
129
130 if (errno == EINTR) {
131 /* Note: EINTR should not occur with flags=0 */
132 if (PyErr_CheckSignals()) {
133 if (!raise)
134 Py_FatalError("getrandom() interrupted by a signal");
135 return -1;
136 }
137 /* retry getrandom() */
138 continue;
139 }
140
141 if (raise)
142 PyErr_SetFromErrno(PyExc_OSError);
143 else
144 Py_FatalError("getrandom() failed");
145 return -1;
146 }
147
148 buffer += n;
149 size -= n;
150 }
151 return 1;
152}
153#endif
154
Antoine Pitroue472aea2014-04-26 14:33:03 +0200155static struct {
156 int fd;
157 dev_t st_dev;
158 ino_t st_ino;
159} urandom_cache = { -1 };
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100160
Victor Stinner59f7fb22015-03-18 14:39:33 +0100161
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100162/* Read size bytes from /dev/urandom into buffer.
163 Call Py_FatalError() on error. */
164static void
Christian Heimes985ecdc2013-11-20 11:46:18 +0100165dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100166{
167 int fd;
168 Py_ssize_t n;
169
170 assert (0 < size);
171
Victor Stinnera555cfc2015-03-18 00:22:14 +0100172 fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100173 if (fd < 0)
174 Py_FatalError("Failed to open /dev/urandom");
175
Victor Stinner59f7fb22015-03-18 14:39:33 +0100176#ifdef HAVE_GETRANDOM
177 if (py_getrandom(buffer, size, 0) == 1)
178 return;
179 /* getrandom() is not supported by the running kernel, fall back
180 * on reading /dev/urandom */
181#endif
182
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100183 while (0 < size)
184 {
185 do {
186 n = read(fd, buffer, (size_t)size);
187 } while (n < 0 && errno == EINTR);
188 if (n <= 0)
189 {
190 /* stop on error or if read(size) returned 0 */
191 Py_FatalError("Failed to read bytes from /dev/urandom");
192 break;
193 }
194 buffer += n;
195 size -= (Py_ssize_t)n;
196 }
197 close(fd);
198}
199
200/* Read size bytes from /dev/urandom into buffer.
201 Return 0 on success, raise an exception and return -1 on error. */
202static int
203dev_urandom_python(char *buffer, Py_ssize_t size)
204{
205 int fd;
206 Py_ssize_t n;
Steve Dowerf2f373f2015-02-21 08:44:05 -0800207 struct _Py_stat_struct st;
Victor Stinner59f7fb22015-03-18 14:39:33 +0100208#ifdef HAVE_GETRANDOM
209 int res;
210#endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100211
212 if (size <= 0)
213 return 0;
214
Victor Stinner59f7fb22015-03-18 14:39:33 +0100215#ifdef HAVE_GETRANDOM
216 res = py_getrandom(buffer, size, 1);
217 if (res < 0)
218 return -1;
219 if (res == 1)
220 return 0;
221 /* getrandom() is not supported by the running kernel, fall back
222 * on reading /dev/urandom */
223#endif
224
Antoine Pitroue472aea2014-04-26 14:33:03 +0200225 if (urandom_cache.fd >= 0) {
226 /* Does the fd point to the same thing as before? (issue #21207) */
Steve Dowerf2f373f2015-02-21 08:44:05 -0800227 if (_Py_fstat(urandom_cache.fd, &st)
Antoine Pitroue472aea2014-04-26 14:33:03 +0200228 || st.st_dev != urandom_cache.st_dev
229 || st.st_ino != urandom_cache.st_ino) {
230 /* Something changed: forget the cached fd (but don't close it,
231 since it probably points to something important for some
232 third-party code). */
233 urandom_cache.fd = -1;
234 }
235 }
236 if (urandom_cache.fd >= 0)
237 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200238 else {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200239 fd = _Py_open("/dev/urandom", O_RDONLY);
Victor Stinnera555cfc2015-03-18 00:22:14 +0100240 if (fd < 0) {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200241 if (errno == ENOENT || errno == ENXIO ||
242 errno == ENODEV || errno == EACCES)
243 PyErr_SetString(PyExc_NotImplementedError,
244 "/dev/urandom (or equivalent) not found");
Victor Stinnera555cfc2015-03-18 00:22:14 +0100245 /* otherwise, keep the OSError exception raised by _Py_open() */
Antoine Pitrou4879a962013-08-31 00:26:02 +0200246 return -1;
247 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200248 if (urandom_cache.fd >= 0) {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200249 /* urandom_fd was initialized by another thread while we were
250 not holding the GIL, keep it. */
251 close(fd);
Antoine Pitroue472aea2014-04-26 14:33:03 +0200252 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200253 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200254 else {
Steve Dowerf2f373f2015-02-21 08:44:05 -0800255 if (_Py_fstat(fd, &st)) {
Antoine Pitroue472aea2014-04-26 14:33:03 +0200256 PyErr_SetFromErrno(PyExc_OSError);
257 close(fd);
258 return -1;
259 }
260 else {
261 urandom_cache.fd = fd;
262 urandom_cache.st_dev = st.st_dev;
263 urandom_cache.st_ino = st.st_ino;
264 }
265 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100266 }
267
268 Py_BEGIN_ALLOW_THREADS
269 do {
270 do {
271 n = read(fd, buffer, (size_t)size);
272 } while (n < 0 && errno == EINTR);
273 if (n <= 0)
274 break;
275 buffer += n;
276 size -= (Py_ssize_t)n;
277 } while (0 < size);
278 Py_END_ALLOW_THREADS
279
280 if (n <= 0)
281 {
282 /* stop on error or if read(size) returned 0 */
283 if (n < 0)
284 PyErr_SetFromErrno(PyExc_OSError);
285 else
286 PyErr_Format(PyExc_RuntimeError,
287 "Failed to read %zi bytes from /dev/urandom",
288 size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100289 return -1;
290 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100291 return 0;
292}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200293
294static void
295dev_urandom_close(void)
296{
Antoine Pitroue472aea2014-04-26 14:33:03 +0200297 if (urandom_cache.fd >= 0) {
298 close(urandom_cache.fd);
299 urandom_cache.fd = -1;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200300 }
301}
302
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100303#endif /* HAVE_GETENTROPY */
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100304
305/* Fill buffer with pseudo-random bytes generated by a linear congruent
306 generator (LCG):
307
308 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
309
310 Use bits 23..16 of x(n) to generate a byte. */
311static void
312lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
313{
314 size_t index;
315 unsigned int x;
316
317 x = x0;
318 for (index=0; index < size; index++) {
319 x *= 214013;
320 x += 2531011;
321 /* modulo 2 ^ (8 * sizeof(int)) */
322 buffer[index] = (x >> 16) & 0xff;
323 }
324}
325
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200326/* Fill buffer with size pseudo-random bytes from the operating system random
Serhiy Storchaka56a6d852014-12-01 18:28:43 +0200327 number generator (RNG). It is suitable for most cryptographic purposes
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200328 except long living private keys for asymmetric encryption.
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100329
330 Return 0 on success, raise an exception and return -1 on error. */
331int
332_PyOS_URandom(void *buffer, Py_ssize_t size)
333{
334 if (size < 0) {
335 PyErr_Format(PyExc_ValueError,
336 "negative argument not allowed");
337 return -1;
338 }
339 if (size == 0)
340 return 0;
341
342#ifdef MS_WINDOWS
343 return win32_urandom((unsigned char *)buffer, size, 1);
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100344#elif HAVE_GETENTROPY
345 return py_getentropy(buffer, size, 0);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100346#else
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100347 return dev_urandom_python((char*)buffer, size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100348#endif
349}
350
351void
352_PyRandom_Init(void)
353{
354 char *env;
Christian Heimes985ecdc2013-11-20 11:46:18 +0100355 unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500356 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
Christian Heimes985ecdc2013-11-20 11:46:18 +0100357 assert(secret_size == sizeof(_Py_HashSecret.uc));
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100358
Benjamin Peterson69e97272012-02-21 11:08:50 -0500359 if (_Py_HashSecret_Initialized)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100360 return;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500361 _Py_HashSecret_Initialized = 1;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100362
363 /*
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100364 Hash randomization is enabled. Generate a per-process secret,
365 using PYTHONHASHSEED if provided.
366 */
367
368 env = Py_GETENV("PYTHONHASHSEED");
Georg Brandl12897d72012-02-20 23:49:29 +0100369 if (env && *env != '\0' && strcmp(env, "random") != 0) {
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100370 char *endptr = env;
371 unsigned long seed;
372 seed = strtoul(env, &endptr, 10);
373 if (*endptr != '\0'
374 || seed > 4294967295UL
375 || (errno == ERANGE && seed == ULONG_MAX))
376 {
377 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
378 "in range [0; 4294967295]");
379 }
380 if (seed == 0) {
381 /* disable the randomized hash */
382 memset(secret, 0, secret_size);
383 }
384 else {
Christian Heimes985ecdc2013-11-20 11:46:18 +0100385 lcg_urandom(seed, secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100386 }
387 }
388 else {
389#ifdef MS_WINDOWS
Christian Heimes985ecdc2013-11-20 11:46:18 +0100390 (void)win32_urandom(secret, secret_size, 0);
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100391#elif HAVE_GETENTROPY
392 (void)py_getentropy(secret, secret_size, 1);
Christian Heimesaf01f662013-12-21 16:19:10 +0100393#else
Christian Heimes985ecdc2013-11-20 11:46:18 +0100394 dev_urandom_noraise(secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100395#endif
396 }
397}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200398
399void
400_PyRandom_Fini(void)
401{
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200402#ifdef MS_WINDOWS
403 if (hCryptProv) {
Tim Goldenb8ac3e12014-05-06 13:29:45 +0100404 CryptReleaseContext(hCryptProv, 0);
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200405 hCryptProv = 0;
406 }
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100407#elif HAVE_GETENTROPY
408 /* nothing to clean */
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200409#else
Antoine Pitrou4879a962013-08-31 00:26:02 +0200410 dev_urandom_close();
411#endif
412}