blob: 08b2a998dd67b30f9635ffae2dd9a123aef20299 [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 Peterson6b1c9092016-12-19 23:54:25 -08006#ifdef HAVE_SYS_RANDOM_H
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 Stinnera87633e2015-10-01 09:57:26 +0200100 * Solaris 11.3), it is blocking whereas os.urandom() should not block. */
101#elif defined(HAVE_GETENTROPY) && !defined(sun)
102#define PY_GETENTROPY 1
103
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600104/* Fill buffer with size pseudo-random bytes generated by getentropy().
105 Return 0 on success, or raise an exception and return -1 on error.
106 If fatal is nonzero, call Py_FatalError() instead of raising an exception
107 on error. */
108static int
109py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
110{
111 while (size > 0) {
Serhiy Storchakae8d750c2015-02-16 08:05:10 +0200112 Py_ssize_t len = size < 256 ? size : 256;
Victor Stinnere9932452015-03-30 11:22:13 +0200113 int res;
114
115 if (!fatal) {
116 Py_BEGIN_ALLOW_THREADS
117 res = getentropy(buffer, len);
118 Py_END_ALLOW_THREADS
119
120 if (res < 0) {
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600121 PyErr_SetFromErrno(PyExc_OSError);
122 return -1;
123 }
124 }
Victor Stinnere9932452015-03-30 11:22:13 +0200125 else {
126 res = getentropy(buffer, len);
127 if (res < 0)
128 Py_FatalError("getentropy() failed");
129 }
130
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600131 buffer += len;
132 size -= len;
133 }
134 return 0;
135}
136#endif
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500137
138#ifdef __VMS
139/* Use openssl random routine */
140#include <openssl/rand.h>
141static int
142vms_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
143{
144 if (RAND_pseudo_bytes(buffer, size) < 0) {
145 if (raise) {
146 PyErr_Format(PyExc_ValueError,
147 "RAND_pseudo_bytes");
148 } else {
149 Py_FatalError("Failed to initialize the randomized hash "
150 "secret using RAND_pseudo_bytes");
151 }
152 return -1;
153 }
154 return 0;
155}
156#endif /* __VMS */
157
158
159#if !defined(MS_WINDOWS) && !defined(__VMS)
160
Benjamin Peterson57057a62014-08-28 12:30:00 -0400161static struct {
162 int fd;
163 dev_t st_dev;
164 ino_t st_ino;
165} urandom_cache = { -1 };
166
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500167/* Read size bytes from /dev/urandom into buffer.
168 Call Py_FatalError() on error. */
169static void
Benjamin Peterson57057a62014-08-28 12:30:00 -0400170dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500171{
172 int fd;
173 Py_ssize_t n;
174
175 assert (0 < size);
176
177 fd = open("/dev/urandom", O_RDONLY);
178 if (fd < 0)
179 Py_FatalError("Failed to open /dev/urandom");
180
181 while (0 < size)
182 {
183 do {
184 n = read(fd, buffer, (size_t)size);
185 } while (n < 0 && errno == EINTR);
186 if (n <= 0)
187 {
188 /* stop on error or if read(size) returned 0 */
189 Py_FatalError("Failed to read bytes from /dev/urandom");
190 break;
191 }
192 buffer += n;
193 size -= (Py_ssize_t)n;
194 }
195 close(fd);
196}
197
198/* Read size bytes from /dev/urandom into buffer.
199 Return 0 on success, raise an exception and return -1 on error. */
200static int
201dev_urandom_python(char *buffer, Py_ssize_t size)
202{
203 int fd;
204 Py_ssize_t n;
Benjamin Peterson57057a62014-08-28 12:30:00 -0400205 struct stat st;
Victor Stinnere0a0bd62015-02-24 14:30:43 +0100206 int attr;
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500207
208 if (size <= 0)
209 return 0;
210
Benjamin Peterson57057a62014-08-28 12:30:00 -0400211 if (urandom_cache.fd >= 0) {
212 /* Does the fd point to the same thing as before? (issue #21207) */
213 if (fstat(urandom_cache.fd, &st)
214 || st.st_dev != urandom_cache.st_dev
215 || st.st_ino != urandom_cache.st_ino) {
216 /* Something changed: forget the cached fd (but don't close it,
217 since it probably points to something important for some
218 third-party code). */
219 urandom_cache.fd = -1;
220 }
221 }
222 if (urandom_cache.fd >= 0)
223 fd = urandom_cache.fd;
224 else {
225 Py_BEGIN_ALLOW_THREADS
226 fd = open("/dev/urandom", O_RDONLY);
227 Py_END_ALLOW_THREADS
228 if (fd < 0)
229 {
230 if (errno == ENOENT || errno == ENXIO ||
231 errno == ENODEV || errno == EACCES)
232 PyErr_SetString(PyExc_NotImplementedError,
233 "/dev/urandom (or equivalent) not found");
234 else
235 PyErr_SetFromErrno(PyExc_OSError);
236 return -1;
237 }
Victor Stinnere0a0bd62015-02-24 14:30:43 +0100238
239 /* try to make the file descriptor non-inheritable, ignore errors */
240 attr = fcntl(fd, F_GETFD);
241 if (attr >= 0) {
242 attr |= FD_CLOEXEC;
243 (void)fcntl(fd, F_SETFD, attr);
244 }
245
Benjamin Peterson57057a62014-08-28 12:30:00 -0400246 if (urandom_cache.fd >= 0) {
247 /* urandom_fd was initialized by another thread while we were
248 not holding the GIL, keep it. */
249 close(fd);
250 fd = urandom_cache.fd;
251 }
252 else {
253 if (fstat(fd, &st)) {
254 PyErr_SetFromErrno(PyExc_OSError);
255 close(fd);
256 return -1;
257 }
258 else {
259 urandom_cache.fd = fd;
260 urandom_cache.st_dev = st.st_dev;
261 urandom_cache.st_ino = st.st_ino;
262 }
263 }
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500264 }
265
266 Py_BEGIN_ALLOW_THREADS
267 do {
268 do {
269 n = read(fd, buffer, (size_t)size);
270 } while (n < 0 && errno == EINTR);
271 if (n <= 0)
272 break;
273 buffer += n;
274 size -= (Py_ssize_t)n;
275 } while (0 < size);
276 Py_END_ALLOW_THREADS
277
278 if (n <= 0)
279 {
280 /* stop on error or if read(size) returned 0 */
281 if (n < 0)
282 PyErr_SetFromErrno(PyExc_OSError);
283 else
284 PyErr_Format(PyExc_RuntimeError,
285 "Failed to read %zi bytes from /dev/urandom",
286 size);
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500287 return -1;
288 }
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500289 return 0;
290}
Benjamin Peterson57057a62014-08-28 12:30:00 -0400291
292static void
293dev_urandom_close(void)
294{
295 if (urandom_cache.fd >= 0) {
296 close(urandom_cache.fd);
297 urandom_cache.fd = -1;
298 }
299}
300
301
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500302#endif /* !defined(MS_WINDOWS) && !defined(__VMS) */
303
304/* Fill buffer with pseudo-random bytes generated by a linear congruent
305 generator (LCG):
306
307 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
308
309 Use bits 23..16 of x(n) to generate a byte. */
310static void
311lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
312{
313 size_t index;
314 unsigned int x;
315
316 x = x0;
317 for (index=0; index < size; index++) {
318 x *= 214013;
319 x += 2531011;
320 /* modulo 2 ^ (8 * sizeof(int)) */
321 buffer[index] = (x >> 16) & 0xff;
322 }
323}
324
Georg Brandlc0edade2013-10-06 18:43:19 +0200325/* Fill buffer with size pseudo-random bytes from the operating system random
Serhiy Storchaka0f8f7842014-12-01 18:16:30 +0200326 number generator (RNG). It is suitable for most cryptographic purposes
Georg Brandlc0edade2013-10-06 18:43:19 +0200327 except long living private keys for asymmetric encryption.
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500328
329 Return 0 on success, raise an exception and return -1 on error. */
330int
331_PyOS_URandom(void *buffer, Py_ssize_t size)
332{
333 if (size < 0) {
334 PyErr_Format(PyExc_ValueError,
335 "negative argument not allowed");
336 return -1;
337 }
338 if (size == 0)
339 return 0;
340
341#ifdef MS_WINDOWS
342 return win32_urandom((unsigned char *)buffer, size, 1);
Victor Stinnera87633e2015-10-01 09:57:26 +0200343#elif defined(PY_GETENTROPY)
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600344 return py_getentropy(buffer, size, 0);
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500345#else
346# ifdef __VMS
347 return vms_urandom((unsigned char *)buffer, size, 1);
348# else
349 return dev_urandom_python((char*)buffer, size);
350# endif
351#endif
352}
353
354void
355_PyRandom_Init(void)
356{
357 char *env;
358 void *secret = &_Py_HashSecret;
Benjamin Peterson26da9202012-02-21 11:08:50 -0500359 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500360
Benjamin Peterson26da9202012-02-21 11:08:50 -0500361 if (_Py_HashSecret_Initialized)
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500362 return;
Benjamin Peterson26da9202012-02-21 11:08:50 -0500363 _Py_HashSecret_Initialized = 1;
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500364
365 /*
366 By default, hash randomization is disabled, and only
367 enabled if PYTHONHASHSEED is set to non-empty or if
368 "-R" is provided at the command line:
369 */
370 if (!Py_HashRandomizationFlag) {
371 /* Disable the randomized hash: */
372 memset(secret, 0, secret_size);
373 return;
374 }
375
376 /*
377 Hash randomization is enabled. Generate a per-process secret,
378 using PYTHONHASHSEED if provided.
379 */
380
381 env = Py_GETENV("PYTHONHASHSEED");
382 if (env && *env != '\0' && strcmp(env, "random") != 0) {
383 char *endptr = env;
384 unsigned long seed;
385 seed = strtoul(env, &endptr, 10);
386 if (*endptr != '\0'
387 || seed > 4294967295UL
388 || (errno == ERANGE && seed == ULONG_MAX))
389 {
390 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
391 "in range [0; 4294967295]");
392 }
393 if (seed == 0) {
394 /* disable the randomized hash */
395 memset(secret, 0, secret_size);
396 }
397 else {
398 lcg_urandom(seed, (unsigned char*)secret, secret_size);
399 }
400 }
401 else {
402#ifdef MS_WINDOWS
403 (void)win32_urandom((unsigned char *)secret, secret_size, 0);
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600404#elif __VMS
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500405 vms_urandom((unsigned char *)secret, secret_size, 0);
Victor Stinnera87633e2015-10-01 09:57:26 +0200406#elif defined(PY_GETENTROPY)
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600407 (void)py_getentropy(secret, secret_size, 1);
408#else
409 dev_urandom_noraise(secret, secret_size);
Barry Warsaw1e13eb02012-02-20 20:42:21 -0500410#endif
411 }
412}
Benjamin Peterson57057a62014-08-28 12:30:00 -0400413
414void
415_PyRandom_Fini(void)
416{
417#ifdef MS_WINDOWS
418 if (hCryptProv) {
419 CryptReleaseContext(hCryptProv, 0);
420 hCryptProv = 0;
421 }
Victor Stinnera87633e2015-10-01 09:57:26 +0200422#elif defined(PY_GETENTROPY)
Benjamin Peterson27c269a2014-12-26 11:09:00 -0600423 /* nothing to clean */
Benjamin Peterson57057a62014-08-28 12:30:00 -0400424#else
425 dev_urandom_close();
426#endif
427}