blob: ea09e84a7b0d73e5160ee865c8831c9fdc7e9c1e [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) {
Victor Stinner59f7fb22015-03-18 14:39:33 +0100145 if (PyErr_CheckSignals()) {
146 if (!raise)
147 Py_FatalError("getrandom() interrupted by a signal");
148 return -1;
149 }
150 /* retry getrandom() */
151 continue;
152 }
153
154 if (raise)
155 PyErr_SetFromErrno(PyExc_OSError);
156 else
157 Py_FatalError("getrandom() failed");
158 return -1;
159 }
160
161 buffer += n;
162 size -= n;
163 }
164 return 1;
165}
166#endif
167
Antoine Pitroue472aea2014-04-26 14:33:03 +0200168static struct {
169 int fd;
170 dev_t st_dev;
171 ino_t st_ino;
172} urandom_cache = { -1 };
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100173
Victor Stinner59f7fb22015-03-18 14:39:33 +0100174
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100175/* Read size bytes from /dev/urandom into buffer.
176 Call Py_FatalError() on error. */
177static void
Christian Heimes985ecdc2013-11-20 11:46:18 +0100178dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100179{
180 int fd;
181 Py_ssize_t n;
182
183 assert (0 < size);
184
Victor Stinner9eb57c52015-03-19 22:21:49 +0100185#ifdef HAVE_GETRANDOM_SYSCALL
Victor Stinner59f7fb22015-03-18 14:39:33 +0100186 if (py_getrandom(buffer, size, 0) == 1)
187 return;
188 /* getrandom() is not supported by the running kernel, fall back
189 * on reading /dev/urandom */
190#endif
191
Victor Stinnerc7cd12d2015-03-19 23:24:45 +0100192 fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
193 if (fd < 0)
194 Py_FatalError("Failed to open /dev/urandom");
195
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100196 while (0 < size)
197 {
198 do {
199 n = read(fd, buffer, (size_t)size);
200 } while (n < 0 && errno == EINTR);
201 if (n <= 0)
202 {
203 /* stop on error or if read(size) returned 0 */
204 Py_FatalError("Failed to read bytes from /dev/urandom");
205 break;
206 }
207 buffer += n;
208 size -= (Py_ssize_t)n;
209 }
210 close(fd);
211}
212
213/* Read size bytes from /dev/urandom into buffer.
214 Return 0 on success, raise an exception and return -1 on error. */
215static int
216dev_urandom_python(char *buffer, Py_ssize_t size)
217{
218 int fd;
219 Py_ssize_t n;
Steve Dowerf2f373f2015-02-21 08:44:05 -0800220 struct _Py_stat_struct st;
Victor Stinner9eb57c52015-03-19 22:21:49 +0100221#ifdef HAVE_GETRANDOM_SYSCALL
Victor Stinner59f7fb22015-03-18 14:39:33 +0100222 int res;
223#endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100224
225 if (size <= 0)
226 return 0;
227
Victor Stinner9eb57c52015-03-19 22:21:49 +0100228#ifdef HAVE_GETRANDOM_SYSCALL
Victor Stinner59f7fb22015-03-18 14:39:33 +0100229 res = py_getrandom(buffer, size, 1);
230 if (res < 0)
231 return -1;
232 if (res == 1)
233 return 0;
234 /* getrandom() is not supported by the running kernel, fall back
235 * on reading /dev/urandom */
236#endif
237
Antoine Pitroue472aea2014-04-26 14:33:03 +0200238 if (urandom_cache.fd >= 0) {
239 /* Does the fd point to the same thing as before? (issue #21207) */
Victor Stinnere134a7f2015-03-30 10:09:31 +0200240 if (_Py_fstat_noraise(urandom_cache.fd, &st)
Antoine Pitroue472aea2014-04-26 14:33:03 +0200241 || st.st_dev != urandom_cache.st_dev
242 || st.st_ino != urandom_cache.st_ino) {
243 /* Something changed: forget the cached fd (but don't close it,
244 since it probably points to something important for some
245 third-party code). */
246 urandom_cache.fd = -1;
247 }
248 }
249 if (urandom_cache.fd >= 0)
250 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200251 else {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200252 fd = _Py_open("/dev/urandom", O_RDONLY);
Victor Stinnera555cfc2015-03-18 00:22:14 +0100253 if (fd < 0) {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200254 if (errno == ENOENT || errno == ENXIO ||
255 errno == ENODEV || errno == EACCES)
256 PyErr_SetString(PyExc_NotImplementedError,
257 "/dev/urandom (or equivalent) not found");
Victor Stinnera555cfc2015-03-18 00:22:14 +0100258 /* otherwise, keep the OSError exception raised by _Py_open() */
Antoine Pitrou4879a962013-08-31 00:26:02 +0200259 return -1;
260 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200261 if (urandom_cache.fd >= 0) {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200262 /* urandom_fd was initialized by another thread while we were
263 not holding the GIL, keep it. */
264 close(fd);
Antoine Pitroue472aea2014-04-26 14:33:03 +0200265 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200266 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200267 else {
Steve Dowerf2f373f2015-02-21 08:44:05 -0800268 if (_Py_fstat(fd, &st)) {
Antoine Pitroue472aea2014-04-26 14:33:03 +0200269 close(fd);
270 return -1;
271 }
272 else {
273 urandom_cache.fd = fd;
274 urandom_cache.st_dev = st.st_dev;
275 urandom_cache.st_ino = st.st_ino;
276 }
277 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100278 }
279
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100280 do {
Victor Stinnerc9382eb2015-03-19 23:36:33 +0100281 n = _Py_read(fd, buffer, (size_t)size);
282 if (n == -1)
283 return -1;
284 if (n == 0) {
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100285 PyErr_Format(PyExc_RuntimeError,
Victor Stinnerc9382eb2015-03-19 23:36:33 +0100286 "Failed to read %zi bytes from /dev/urandom",
287 size);
288 return -1;
289 }
290
291 buffer += n;
292 size -= n;
293 } while (0 < size);
294
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100295 return 0;
296}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200297
298static void
299dev_urandom_close(void)
300{
Antoine Pitroue472aea2014-04-26 14:33:03 +0200301 if (urandom_cache.fd >= 0) {
302 close(urandom_cache.fd);
303 urandom_cache.fd = -1;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200304 }
305}
306
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100307#endif /* HAVE_GETENTROPY */
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100308
309/* Fill buffer with pseudo-random bytes generated by a linear congruent
310 generator (LCG):
311
312 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
313
314 Use bits 23..16 of x(n) to generate a byte. */
315static void
316lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
317{
318 size_t index;
319 unsigned int x;
320
321 x = x0;
322 for (index=0; index < size; index++) {
323 x *= 214013;
324 x += 2531011;
325 /* modulo 2 ^ (8 * sizeof(int)) */
326 buffer[index] = (x >> 16) & 0xff;
327 }
328}
329
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200330/* Fill buffer with size pseudo-random bytes from the operating system random
Serhiy Storchaka56a6d852014-12-01 18:28:43 +0200331 number generator (RNG). It is suitable for most cryptographic purposes
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200332 except long living private keys for asymmetric encryption.
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100333
334 Return 0 on success, raise an exception and return -1 on error. */
335int
336_PyOS_URandom(void *buffer, Py_ssize_t size)
337{
338 if (size < 0) {
339 PyErr_Format(PyExc_ValueError,
340 "negative argument not allowed");
341 return -1;
342 }
343 if (size == 0)
344 return 0;
345
346#ifdef MS_WINDOWS
347 return win32_urandom((unsigned char *)buffer, size, 1);
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100348#elif HAVE_GETENTROPY
349 return py_getentropy(buffer, size, 0);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100350#else
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100351 return dev_urandom_python((char*)buffer, size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100352#endif
353}
354
355void
356_PyRandom_Init(void)
357{
358 char *env;
Christian Heimes985ecdc2013-11-20 11:46:18 +0100359 unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500360 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
Christian Heimes985ecdc2013-11-20 11:46:18 +0100361 assert(secret_size == sizeof(_Py_HashSecret.uc));
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100362
Benjamin Peterson69e97272012-02-21 11:08:50 -0500363 if (_Py_HashSecret_Initialized)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100364 return;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500365 _Py_HashSecret_Initialized = 1;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100366
367 /*
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100368 Hash randomization is enabled. Generate a per-process secret,
369 using PYTHONHASHSEED if provided.
370 */
371
372 env = Py_GETENV("PYTHONHASHSEED");
Georg Brandl12897d72012-02-20 23:49:29 +0100373 if (env && *env != '\0' && strcmp(env, "random") != 0) {
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100374 char *endptr = env;
375 unsigned long seed;
376 seed = strtoul(env, &endptr, 10);
377 if (*endptr != '\0'
378 || seed > 4294967295UL
379 || (errno == ERANGE && seed == ULONG_MAX))
380 {
381 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
382 "in range [0; 4294967295]");
383 }
384 if (seed == 0) {
385 /* disable the randomized hash */
386 memset(secret, 0, secret_size);
387 }
388 else {
Christian Heimes985ecdc2013-11-20 11:46:18 +0100389 lcg_urandom(seed, secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100390 }
391 }
392 else {
393#ifdef MS_WINDOWS
Christian Heimes985ecdc2013-11-20 11:46:18 +0100394 (void)win32_urandom(secret, secret_size, 0);
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100395#elif HAVE_GETENTROPY
396 (void)py_getentropy(secret, secret_size, 1);
Christian Heimesaf01f662013-12-21 16:19:10 +0100397#else
Christian Heimes985ecdc2013-11-20 11:46:18 +0100398 dev_urandom_noraise(secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100399#endif
400 }
401}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200402
403void
404_PyRandom_Fini(void)
405{
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200406#ifdef MS_WINDOWS
407 if (hCryptProv) {
Tim Goldenb8ac3e12014-05-06 13:29:45 +0100408 CryptReleaseContext(hCryptProv, 0);
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200409 hCryptProv = 0;
410 }
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100411#elif HAVE_GETENTROPY
412 /* nothing to clean */
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200413#else
Antoine Pitrou4879a962013-08-31 00:26:02 +0200414 dev_urandom_close();
415#endif
416}