blob: 000cb3693841c087564c9bd2faae9e42edbf64e7 [file] [log] [blame]
Barry Warsaw1e13eb02012-02-20 20:42:21 -05001#include "Python.h"
2#ifdef MS_WINDOWS
3#include <windows.h>
4#else
5#include <fcntl.h>
Benjamin Peterson2f7d13c2017-01-01 22:29:36 -06006#if defined(HAVE_SYS_RANDOM_H) && (defined(HAVE_GETRANDOM) || defined(HAVE_GETENTROPY))
Ned Deily09231e62016-11-12 16:34:25 -05007#include <sys/random.h>
8#endif
Barry Warsaw1e13eb02012-02-20 20:42:21 -05009#endif
10
Benjamin Peterson26da9202012-02-21 11:08:50 -050011#ifdef Py_DEBUG
12int _Py_HashSecret_Initialized = 0;
13#else
14static int _Py_HashSecret_Initialized = 0;
15#endif
Barry Warsaw1e13eb02012-02-20 20:42:21 -050016
17#ifdef MS_WINDOWS
18typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\
19 LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\
20 DWORD dwFlags );
21typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\
22 BYTE *pbBuffer );
23
24static CRYPTGENRANDOM pCryptGenRandom = NULL;
25/* This handle is never explicitly released. Instead, the operating
26 system will release it when the process terminates. */
27static HCRYPTPROV hCryptProv = 0;
28
29static int
30win32_urandom_init(int raise)
31{
32 HINSTANCE hAdvAPI32 = NULL;
33 CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
34
35 /* Obtain handle to the DLL containing CryptoAPI. This should not fail. */
36 hAdvAPI32 = GetModuleHandle("advapi32.dll");
37 if(hAdvAPI32 == NULL)
38 goto error;
39
40 /* Obtain pointers to the CryptoAPI functions. This will fail on some early
41 versions of Win95. */
42 pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(
43 hAdvAPI32, "CryptAcquireContextA");
44 if (pCryptAcquireContext == NULL)
45 goto error;
46
47 pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,
48 "CryptGenRandom");
49 if (pCryptGenRandom == NULL)
50 goto error;
51
52 /* Acquire context */
53 if (! pCryptAcquireContext(&hCryptProv, NULL, NULL,
54 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
55 goto error;
56
57 return 0;
58
59error:
60 if (raise)
61 PyErr_SetFromWindowsErr(0);
62 else
63 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
64 return -1;
65}
66
67/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
68 API. Return 0 on success, or -1 on error. */
69static int
70win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
71{
72 Py_ssize_t chunk;
73
74 if (hCryptProv == 0)
75 {
76 if (win32_urandom_init(raise) == -1)
77 return -1;
78 }
79
80 while (size > 0)
81 {
82 chunk = size > INT_MAX ? INT_MAX : size;
83 if (!pCryptGenRandom(hCryptProv, chunk, buffer))
84 {
85 /* CryptGenRandom() failed */
86 if (raise)
87 PyErr_SetFromWindowsErr(0);
88 else
89 Py_FatalError("Failed to initialized the randomized hash "
90 "secret using CryptoGen)");
91 return -1;
92 }
93 buffer += chunk;
94 size -= chunk;
95 }
96 return 0;
97}
Barry Warsaw1e13eb02012-02-20 20:42:21 -050098
Martin Panter7740c402016-06-10 08:07:11 +000099/* Issue #25003: Don't use getentropy() on Solaris (available since
Victor Stinner01bdbad2017-01-09 11:10:41 +0100100 Solaris 11.3), it is blocking whereas os.urandom() should not block.
101
102 Issue #29188: Don't use getentropy() on Linux since the glibc 2.24
103 implements it with the getrandom() syscall which can fail with ENOSYS,
104 and this error is not supported in py_getentropy() and getrandom() is called
105 with flags=0 which blocks until system urandom is initialized, which is not
106 the desired behaviour to seed the Python hash secret nor for os.urandom():
107 see the PEP 524 which was only implemented in Python 3.6. */
108#elif defined(HAVE_GETENTROPY) && !defined(sun) && !defined(linux)
Victor Stinnera87633e2015-10-01 09:57:26 +0200109#define PY_GETENTROPY 1
110
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600111/* Fill buffer with size pseudo-random bytes generated by getentropy().
112 Return 0 on success, or raise an exception and return -1 on error.
113 If fatal is nonzero, call Py_FatalError() instead of raising an exception
114 on error. */
115static int
116py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
117{
118 while (size > 0) {
Serhiy Storchakae8d750c2015-02-16 08:05:10 +0200119 Py_ssize_t len = size < 256 ? size : 256;
Victor Stinnere9932452015-03-30 11:22:13 +0200120 int res;
121
122 if (!fatal) {
123 Py_BEGIN_ALLOW_THREADS
124 res = getentropy(buffer, len);
125 Py_END_ALLOW_THREADS
126
127 if (res < 0) {
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600128 PyErr_SetFromErrno(PyExc_OSError);
129 return -1;
130 }
131 }
Victor Stinnere9932452015-03-30 11:22:13 +0200132 else {
133 res = getentropy(buffer, len);
134 if (res < 0)
135 Py_FatalError("getentropy() failed");
136 }
137
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600138 buffer += len;
139 size -= len;
140 }
141 return 0;
142}
143#endif
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500144
145#ifdef __VMS
146/* Use openssl random routine */
147#include <openssl/rand.h>
148static int
149vms_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
150{
151 if (RAND_pseudo_bytes(buffer, size) < 0) {
152 if (raise) {
153 PyErr_Format(PyExc_ValueError,
154 "RAND_pseudo_bytes");
155 } else {
156 Py_FatalError("Failed to initialize the randomized hash "
157 "secret using RAND_pseudo_bytes");
158 }
159 return -1;
160 }
161 return 0;
162}
163#endif /* __VMS */
164
165
166#if !defined(MS_WINDOWS) && !defined(__VMS)
167
Benjamin Peterson57057a62014-08-28 12:30:00 -0400168static struct {
169 int fd;
170 dev_t st_dev;
171 ino_t st_ino;
172} urandom_cache = { -1 };
173
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500174/* Read size bytes from /dev/urandom into buffer.
175 Call Py_FatalError() on error. */
176static void
Benjamin Peterson57057a62014-08-28 12:30:00 -0400177dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500178{
179 int fd;
180 Py_ssize_t n;
181
182 assert (0 < size);
183
184 fd = open("/dev/urandom", O_RDONLY);
185 if (fd < 0)
186 Py_FatalError("Failed to open /dev/urandom");
187
188 while (0 < size)
189 {
190 do {
191 n = read(fd, buffer, (size_t)size);
192 } while (n < 0 && errno == EINTR);
193 if (n <= 0)
194 {
195 /* stop on error or if read(size) returned 0 */
196 Py_FatalError("Failed to read bytes from /dev/urandom");
197 break;
198 }
199 buffer += n;
200 size -= (Py_ssize_t)n;
201 }
202 close(fd);
203}
204
205/* Read size bytes from /dev/urandom into buffer.
206 Return 0 on success, raise an exception and return -1 on error. */
207static int
208dev_urandom_python(char *buffer, Py_ssize_t size)
209{
210 int fd;
211 Py_ssize_t n;
Benjamin Peterson57057a62014-08-28 12:30:00 -0400212 struct stat st;
Victor Stinnere0a0bd62015-02-24 14:30:43 +0100213 int attr;
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500214
215 if (size <= 0)
216 return 0;
217
Benjamin Peterson57057a62014-08-28 12:30:00 -0400218 if (urandom_cache.fd >= 0) {
219 /* Does the fd point to the same thing as before? (issue #21207) */
220 if (fstat(urandom_cache.fd, &st)
221 || st.st_dev != urandom_cache.st_dev
222 || st.st_ino != urandom_cache.st_ino) {
223 /* Something changed: forget the cached fd (but don't close it,
224 since it probably points to something important for some
225 third-party code). */
226 urandom_cache.fd = -1;
227 }
228 }
229 if (urandom_cache.fd >= 0)
230 fd = urandom_cache.fd;
231 else {
232 Py_BEGIN_ALLOW_THREADS
233 fd = open("/dev/urandom", O_RDONLY);
234 Py_END_ALLOW_THREADS
235 if (fd < 0)
236 {
237 if (errno == ENOENT || errno == ENXIO ||
238 errno == ENODEV || errno == EACCES)
239 PyErr_SetString(PyExc_NotImplementedError,
240 "/dev/urandom (or equivalent) not found");
241 else
242 PyErr_SetFromErrno(PyExc_OSError);
243 return -1;
244 }
Victor Stinnere0a0bd62015-02-24 14:30:43 +0100245
246 /* try to make the file descriptor non-inheritable, ignore errors */
247 attr = fcntl(fd, F_GETFD);
248 if (attr >= 0) {
249 attr |= FD_CLOEXEC;
250 (void)fcntl(fd, F_SETFD, attr);
251 }
252
Benjamin Peterson57057a62014-08-28 12:30:00 -0400253 if (urandom_cache.fd >= 0) {
254 /* urandom_fd was initialized by another thread while we were
255 not holding the GIL, keep it. */
256 close(fd);
257 fd = urandom_cache.fd;
258 }
259 else {
260 if (fstat(fd, &st)) {
261 PyErr_SetFromErrno(PyExc_OSError);
262 close(fd);
263 return -1;
264 }
265 else {
266 urandom_cache.fd = fd;
267 urandom_cache.st_dev = st.st_dev;
268 urandom_cache.st_ino = st.st_ino;
269 }
270 }
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500271 }
272
273 Py_BEGIN_ALLOW_THREADS
274 do {
275 do {
276 n = read(fd, buffer, (size_t)size);
277 } while (n < 0 && errno == EINTR);
278 if (n <= 0)
279 break;
280 buffer += n;
281 size -= (Py_ssize_t)n;
282 } while (0 < size);
283 Py_END_ALLOW_THREADS
284
285 if (n <= 0)
286 {
287 /* stop on error or if read(size) returned 0 */
288 if (n < 0)
289 PyErr_SetFromErrno(PyExc_OSError);
290 else
291 PyErr_Format(PyExc_RuntimeError,
292 "Failed to read %zi bytes from /dev/urandom",
293 size);
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500294 return -1;
295 }
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500296 return 0;
297}
Benjamin Peterson57057a62014-08-28 12:30:00 -0400298
299static void
300dev_urandom_close(void)
301{
302 if (urandom_cache.fd >= 0) {
303 close(urandom_cache.fd);
304 urandom_cache.fd = -1;
305 }
306}
307
308
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500309#endif /* !defined(MS_WINDOWS) && !defined(__VMS) */
310
311/* Fill buffer with pseudo-random bytes generated by a linear congruent
312 generator (LCG):
313
314 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
315
316 Use bits 23..16 of x(n) to generate a byte. */
317static void
318lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
319{
320 size_t index;
321 unsigned int x;
322
323 x = x0;
324 for (index=0; index < size; index++) {
325 x *= 214013;
326 x += 2531011;
327 /* modulo 2 ^ (8 * sizeof(int)) */
328 buffer[index] = (x >> 16) & 0xff;
329 }
330}
331
Georg Brandlc0edade2013-10-06 18:43:19 +0200332/* Fill buffer with size pseudo-random bytes from the operating system random
Serhiy Storchaka0f8f7842014-12-01 18:16:30 +0200333 number generator (RNG). It is suitable for most cryptographic purposes
Georg Brandlc0edade2013-10-06 18:43:19 +0200334 except long living private keys for asymmetric encryption.
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500335
336 Return 0 on success, raise an exception and return -1 on error. */
337int
338_PyOS_URandom(void *buffer, Py_ssize_t size)
339{
340 if (size < 0) {
341 PyErr_Format(PyExc_ValueError,
342 "negative argument not allowed");
343 return -1;
344 }
345 if (size == 0)
346 return 0;
347
348#ifdef MS_WINDOWS
349 return win32_urandom((unsigned char *)buffer, size, 1);
Victor Stinnera87633e2015-10-01 09:57:26 +0200350#elif defined(PY_GETENTROPY)
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600351 return py_getentropy(buffer, size, 0);
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500352#else
353# ifdef __VMS
354 return vms_urandom((unsigned char *)buffer, size, 1);
355# else
356 return dev_urandom_python((char*)buffer, size);
357# endif
358#endif
359}
360
361void
362_PyRandom_Init(void)
363{
364 char *env;
365 void *secret = &_Py_HashSecret;
Benjamin Peterson26da9202012-02-21 11:08:50 -0500366 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500367
Benjamin Peterson26da9202012-02-21 11:08:50 -0500368 if (_Py_HashSecret_Initialized)
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500369 return;
Benjamin Peterson26da9202012-02-21 11:08:50 -0500370 _Py_HashSecret_Initialized = 1;
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500371
372 /*
373 By default, hash randomization is disabled, and only
374 enabled if PYTHONHASHSEED is set to non-empty or if
375 "-R" is provided at the command line:
376 */
377 if (!Py_HashRandomizationFlag) {
378 /* Disable the randomized hash: */
379 memset(secret, 0, secret_size);
380 return;
381 }
382
383 /*
384 Hash randomization is enabled. Generate a per-process secret,
385 using PYTHONHASHSEED if provided.
386 */
387
388 env = Py_GETENV("PYTHONHASHSEED");
389 if (env && *env != '\0' && strcmp(env, "random") != 0) {
390 char *endptr = env;
391 unsigned long seed;
392 seed = strtoul(env, &endptr, 10);
393 if (*endptr != '\0'
394 || seed > 4294967295UL
395 || (errno == ERANGE && seed == ULONG_MAX))
396 {
397 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
398 "in range [0; 4294967295]");
399 }
400 if (seed == 0) {
401 /* disable the randomized hash */
402 memset(secret, 0, secret_size);
403 }
404 else {
405 lcg_urandom(seed, (unsigned char*)secret, secret_size);
406 }
407 }
408 else {
409#ifdef MS_WINDOWS
410 (void)win32_urandom((unsigned char *)secret, secret_size, 0);
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600411#elif __VMS
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500412 vms_urandom((unsigned char *)secret, secret_size, 0);
Victor Stinnera87633e2015-10-01 09:57:26 +0200413#elif defined(PY_GETENTROPY)
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600414 (void)py_getentropy(secret, secret_size, 1);
415#else
416 dev_urandom_noraise(secret, secret_size);
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500417#endif
418 }
419}
Benjamin Peterson57057a62014-08-28 12:30:00 -0400420
421void
422_PyRandom_Fini(void)
423{
424#ifdef MS_WINDOWS
425 if (hCryptProv) {
426 CryptReleaseContext(hCryptProv, 0);
427 hCryptProv = 0;
428 }
Victor Stinnera87633e2015-10-01 09:57:26 +0200429#elif defined(PY_GETENTROPY)
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600430 /* nothing to clean */
Benjamin Peterson57057a62014-08-28 12:30:00 -0400431#else
432 dev_urandom_close();
433#endif
434}