blob: 9c9d5057c9b88725f9a0adb2eecbf64ba753e78f [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
Victor Stinner9eb57c52015-03-19 22:21:49 +01009# ifdef HAVE_GETRANDOM_SYSCALL
Victor Stinner59f7fb22015-03-18 14:39:33 +010010# include <sys/syscall.h>
Victor Stinner59f7fb22015-03-18 14:39:33 +010011# endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +010012#endif
13
Benjamin Peterson69e97272012-02-21 11:08:50 -050014#ifdef Py_DEBUG
15int _Py_HashSecret_Initialized = 0;
16#else
17static int _Py_HashSecret_Initialized = 0;
18#endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +010019
20#ifdef MS_WINDOWS
Georg Brandl2daf6ae2012-02-20 19:54:16 +010021static HCRYPTPROV hCryptProv = 0;
22
23static int
24win32_urandom_init(int raise)
25{
Georg Brandl2daf6ae2012-02-20 19:54:16 +010026 /* Acquire context */
Martin v. Löwis3f50bf62013-01-25 14:06:18 +010027 if (!CryptAcquireContext(&hCryptProv, NULL, NULL,
28 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010029 goto error;
30
31 return 0;
32
33error:
34 if (raise)
35 PyErr_SetFromWindowsErr(0);
36 else
37 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
38 return -1;
39}
40
41/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
Victor Stinner4d6a3d62014-12-21 01:16:38 +010042 API. Return 0 on success, or raise an exception and return -1 on error. */
Georg Brandl2daf6ae2012-02-20 19:54:16 +010043static int
44win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
45{
46 Py_ssize_t chunk;
47
48 if (hCryptProv == 0)
49 {
50 if (win32_urandom_init(raise) == -1)
51 return -1;
52 }
53
54 while (size > 0)
55 {
56 chunk = size > INT_MAX ? INT_MAX : size;
Victor Stinner0c083462013-11-15 23:26:25 +010057 if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010058 {
59 /* CryptGenRandom() failed */
60 if (raise)
61 PyErr_SetFromWindowsErr(0);
62 else
63 Py_FatalError("Failed to initialized the randomized hash "
64 "secret using CryptoGen)");
65 return -1;
66 }
67 buffer += chunk;
68 size -= chunk;
69 }
70 return 0;
71}
Georg Brandl2daf6ae2012-02-20 19:54:16 +010072
Victor Stinner4d6a3d62014-12-21 01:16:38 +010073#elif HAVE_GETENTROPY
74/* Fill buffer with size pseudo-random bytes generated by getentropy().
75 Return 0 on success, or raise an exception and return -1 on error.
Georg Brandl2daf6ae2012-02-20 19:54:16 +010076
Victor Stinner4d6a3d62014-12-21 01:16:38 +010077 If fatal is nonzero, call Py_FatalError() instead of raising an exception
78 on error. */
79static int
80py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
81{
82 while (size > 0) {
83 Py_ssize_t len = Py_MIN(size, 256);
Victor Stinner9aa13312015-03-30 11:18:30 +020084 int res;
85
86 if (!fatal) {
87 Py_BEGIN_ALLOW_THREADS
88 res = getentropy(buffer, len);
89 Py_END_ALLOW_THREADS
90
91 if (res < 0) {
Victor Stinner4d6a3d62014-12-21 01:16:38 +010092 PyErr_SetFromErrno(PyExc_OSError);
93 return -1;
94 }
95 }
Victor Stinner9aa13312015-03-30 11:18:30 +020096 else {
97 res = getentropy(buffer, len);
98 if (res < 0)
99 Py_FatalError("getentropy() failed");
100 }
101
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100102 buffer += len;
103 size -= len;
104 }
105 return 0;
106}
107
Victor Stinner59f7fb22015-03-18 14:39:33 +0100108#else /* !HAVE_GETENTROPY */
109
Victor Stinner9eb57c52015-03-19 22:21:49 +0100110#ifdef HAVE_GETRANDOM_SYSCALL
Victor Stinner59f7fb22015-03-18 14:39:33 +0100111static int
112py_getrandom(void *buffer, Py_ssize_t size, int raise)
113{
114 /* is getrandom() supported by the running kernel?
115 * need Linux kernel 3.17 or later */
116 static int getrandom_works = 1;
117 /* Use /dev/urandom, block if the kernel has no entropy */
118 const int flags = 0;
119 int n;
120
121 if (!getrandom_works)
122 return 0;
123
124 while (0 < size) {
125 errno = 0;
Victor Stinner79b74ae2015-03-30 11:16:40 +0200126
127 /* Use syscall() because the libc doesn't expose getrandom() yet, see:
Victor Stinner59f7fb22015-03-18 14:39:33 +0100128 * https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */
Victor Stinner79b74ae2015-03-30 11:16:40 +0200129 if (raise) {
130 Py_BEGIN_ALLOW_THREADS
131 n = syscall(SYS_getrandom, buffer, size, flags);
132 Py_END_ALLOW_THREADS
133 }
134 else {
135 n = syscall(SYS_getrandom, buffer, size, flags);
136 }
137
Victor Stinner59f7fb22015-03-18 14:39:33 +0100138 if (n < 0) {
139 if (errno == ENOSYS) {
140 getrandom_works = 0;
141 return 0;
142 }
143
144 if (errno == EINTR) {
145 /* Note: EINTR should not occur with flags=0 */
146 if (PyErr_CheckSignals()) {
147 if (!raise)
148 Py_FatalError("getrandom() interrupted by a signal");
149 return -1;
150 }
151 /* retry getrandom() */
152 continue;
153 }
154
155 if (raise)
156 PyErr_SetFromErrno(PyExc_OSError);
157 else
158 Py_FatalError("getrandom() failed");
159 return -1;
160 }
161
162 buffer += n;
163 size -= n;
164 }
165 return 1;
166}
167#endif
168
Antoine Pitroue472aea2014-04-26 14:33:03 +0200169static struct {
170 int fd;
171 dev_t st_dev;
172 ino_t st_ino;
173} urandom_cache = { -1 };
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100174
Victor Stinner59f7fb22015-03-18 14:39:33 +0100175
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100176/* Read size bytes from /dev/urandom into buffer.
177 Call Py_FatalError() on error. */
178static void
Christian Heimes985ecdc2013-11-20 11:46:18 +0100179dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100180{
181 int fd;
182 Py_ssize_t n;
183
184 assert (0 < size);
185
Victor Stinner9eb57c52015-03-19 22:21:49 +0100186#ifdef HAVE_GETRANDOM_SYSCALL
Victor Stinner59f7fb22015-03-18 14:39:33 +0100187 if (py_getrandom(buffer, size, 0) == 1)
188 return;
189 /* getrandom() is not supported by the running kernel, fall back
190 * on reading /dev/urandom */
191#endif
192
Victor Stinnerc7cd12d2015-03-19 23:24:45 +0100193 fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
194 if (fd < 0)
195 Py_FatalError("Failed to open /dev/urandom");
196
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100197 while (0 < size)
198 {
199 do {
200 n = read(fd, buffer, (size_t)size);
201 } while (n < 0 && errno == EINTR);
202 if (n <= 0)
203 {
204 /* stop on error or if read(size) returned 0 */
205 Py_FatalError("Failed to read bytes from /dev/urandom");
206 break;
207 }
208 buffer += n;
209 size -= (Py_ssize_t)n;
210 }
211 close(fd);
212}
213
214/* Read size bytes from /dev/urandom into buffer.
215 Return 0 on success, raise an exception and return -1 on error. */
216static int
217dev_urandom_python(char *buffer, Py_ssize_t size)
218{
219 int fd;
220 Py_ssize_t n;
Steve Dowerf2f373f2015-02-21 08:44:05 -0800221 struct _Py_stat_struct st;
Victor Stinner9eb57c52015-03-19 22:21:49 +0100222#ifdef HAVE_GETRANDOM_SYSCALL
Victor Stinner59f7fb22015-03-18 14:39:33 +0100223 int res;
224#endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100225
226 if (size <= 0)
227 return 0;
228
Victor Stinner9eb57c52015-03-19 22:21:49 +0100229#ifdef HAVE_GETRANDOM_SYSCALL
Victor Stinner59f7fb22015-03-18 14:39:33 +0100230 res = py_getrandom(buffer, size, 1);
231 if (res < 0)
232 return -1;
233 if (res == 1)
234 return 0;
235 /* getrandom() is not supported by the running kernel, fall back
236 * on reading /dev/urandom */
237#endif
238
Antoine Pitroue472aea2014-04-26 14:33:03 +0200239 if (urandom_cache.fd >= 0) {
240 /* Does the fd point to the same thing as before? (issue #21207) */
Victor Stinnere134a7f2015-03-30 10:09:31 +0200241 if (_Py_fstat_noraise(urandom_cache.fd, &st)
Antoine Pitroue472aea2014-04-26 14:33:03 +0200242 || st.st_dev != urandom_cache.st_dev
243 || st.st_ino != urandom_cache.st_ino) {
244 /* Something changed: forget the cached fd (but don't close it,
245 since it probably points to something important for some
246 third-party code). */
247 urandom_cache.fd = -1;
248 }
249 }
250 if (urandom_cache.fd >= 0)
251 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200252 else {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200253 fd = _Py_open("/dev/urandom", O_RDONLY);
Victor Stinnera555cfc2015-03-18 00:22:14 +0100254 if (fd < 0) {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200255 if (errno == ENOENT || errno == ENXIO ||
256 errno == ENODEV || errno == EACCES)
257 PyErr_SetString(PyExc_NotImplementedError,
258 "/dev/urandom (or equivalent) not found");
Victor Stinnera555cfc2015-03-18 00:22:14 +0100259 /* otherwise, keep the OSError exception raised by _Py_open() */
Antoine Pitrou4879a962013-08-31 00:26:02 +0200260 return -1;
261 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200262 if (urandom_cache.fd >= 0) {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200263 /* urandom_fd was initialized by another thread while we were
264 not holding the GIL, keep it. */
265 close(fd);
Antoine Pitroue472aea2014-04-26 14:33:03 +0200266 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200267 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200268 else {
Steve Dowerf2f373f2015-02-21 08:44:05 -0800269 if (_Py_fstat(fd, &st)) {
Antoine Pitroue472aea2014-04-26 14:33:03 +0200270 close(fd);
271 return -1;
272 }
273 else {
274 urandom_cache.fd = fd;
275 urandom_cache.st_dev = st.st_dev;
276 urandom_cache.st_ino = st.st_ino;
277 }
278 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100279 }
280
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100281 do {
Victor Stinnerc9382eb2015-03-19 23:36:33 +0100282 n = _Py_read(fd, buffer, (size_t)size);
283 if (n == -1)
284 return -1;
285 if (n == 0) {
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100286 PyErr_Format(PyExc_RuntimeError,
Victor Stinnerc9382eb2015-03-19 23:36:33 +0100287 "Failed to read %zi bytes from /dev/urandom",
288 size);
289 return -1;
290 }
291
292 buffer += n;
293 size -= n;
294 } while (0 < size);
295
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100296 return 0;
297}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200298
299static void
300dev_urandom_close(void)
301{
Antoine Pitroue472aea2014-04-26 14:33:03 +0200302 if (urandom_cache.fd >= 0) {
303 close(urandom_cache.fd);
304 urandom_cache.fd = -1;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200305 }
306}
307
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100308#endif /* HAVE_GETENTROPY */
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100309
310/* Fill buffer with pseudo-random bytes generated by a linear congruent
311 generator (LCG):
312
313 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
314
315 Use bits 23..16 of x(n) to generate a byte. */
316static void
317lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
318{
319 size_t index;
320 unsigned int x;
321
322 x = x0;
323 for (index=0; index < size; index++) {
324 x *= 214013;
325 x += 2531011;
326 /* modulo 2 ^ (8 * sizeof(int)) */
327 buffer[index] = (x >> 16) & 0xff;
328 }
329}
330
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200331/* Fill buffer with size pseudo-random bytes from the operating system random
Serhiy Storchaka56a6d852014-12-01 18:28:43 +0200332 number generator (RNG). It is suitable for most cryptographic purposes
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200333 except long living private keys for asymmetric encryption.
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100334
335 Return 0 on success, raise an exception and return -1 on error. */
336int
337_PyOS_URandom(void *buffer, Py_ssize_t size)
338{
339 if (size < 0) {
340 PyErr_Format(PyExc_ValueError,
341 "negative argument not allowed");
342 return -1;
343 }
344 if (size == 0)
345 return 0;
346
347#ifdef MS_WINDOWS
348 return win32_urandom((unsigned char *)buffer, size, 1);
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100349#elif HAVE_GETENTROPY
350 return py_getentropy(buffer, size, 0);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100351#else
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100352 return dev_urandom_python((char*)buffer, size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100353#endif
354}
355
356void
357_PyRandom_Init(void)
358{
359 char *env;
Christian Heimes985ecdc2013-11-20 11:46:18 +0100360 unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500361 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
Christian Heimes985ecdc2013-11-20 11:46:18 +0100362 assert(secret_size == sizeof(_Py_HashSecret.uc));
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100363
Benjamin Peterson69e97272012-02-21 11:08:50 -0500364 if (_Py_HashSecret_Initialized)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100365 return;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500366 _Py_HashSecret_Initialized = 1;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100367
368 /*
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100369 Hash randomization is enabled. Generate a per-process secret,
370 using PYTHONHASHSEED if provided.
371 */
372
373 env = Py_GETENV("PYTHONHASHSEED");
Georg Brandl12897d72012-02-20 23:49:29 +0100374 if (env && *env != '\0' && strcmp(env, "random") != 0) {
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100375 char *endptr = env;
376 unsigned long seed;
377 seed = strtoul(env, &endptr, 10);
378 if (*endptr != '\0'
379 || seed > 4294967295UL
380 || (errno == ERANGE && seed == ULONG_MAX))
381 {
382 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
383 "in range [0; 4294967295]");
384 }
385 if (seed == 0) {
386 /* disable the randomized hash */
387 memset(secret, 0, secret_size);
388 }
389 else {
Christian Heimes985ecdc2013-11-20 11:46:18 +0100390 lcg_urandom(seed, secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100391 }
392 }
393 else {
394#ifdef MS_WINDOWS
Christian Heimes985ecdc2013-11-20 11:46:18 +0100395 (void)win32_urandom(secret, secret_size, 0);
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100396#elif HAVE_GETENTROPY
397 (void)py_getentropy(secret, secret_size, 1);
Christian Heimesaf01f662013-12-21 16:19:10 +0100398#else
Christian Heimes985ecdc2013-11-20 11:46:18 +0100399 dev_urandom_noraise(secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100400#endif
401 }
402}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200403
404void
405_PyRandom_Fini(void)
406{
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200407#ifdef MS_WINDOWS
408 if (hCryptProv) {
Tim Goldenb8ac3e12014-05-06 13:29:45 +0100409 CryptReleaseContext(hCryptProv, 0);
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200410 hCryptProv = 0;
411 }
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100412#elif HAVE_GETENTROPY
413 /* nothing to clean */
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200414#else
Antoine Pitrou4879a962013-08-31 00:26:02 +0200415 dev_urandom_close();
416#endif
417}