blob: 2941ba16af0697c9d82b00d75a36c2c61a8e3ea8 [file] [log] [blame]
Georg Brandl2daf6ae2012-02-20 19:54:16 +01001#include "Python.h"
2#ifdef MS_WINDOWS
3#include <windows.h>
4#else
5#include <fcntl.h>
Antoine Pitroue472aea2014-04-26 14:33:03 +02006#ifdef HAVE_SYS_STAT_H
7#include <sys/stat.h>
8#endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +01009#endif
10
Benjamin Peterson69e97272012-02-21 11:08:50 -050011#ifdef Py_DEBUG
12int _Py_HashSecret_Initialized = 0;
13#else
14static int _Py_HashSecret_Initialized = 0;
15#endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +010016
17#ifdef MS_WINDOWS
Georg Brandl2daf6ae2012-02-20 19:54:16 +010018/* This handle is never explicitly released. Instead, the operating
19 system will release it when the process terminates. */
20static HCRYPTPROV hCryptProv = 0;
21
22static int
23win32_urandom_init(int raise)
24{
Georg Brandl2daf6ae2012-02-20 19:54:16 +010025 /* Acquire context */
Martin v. Löwis3f50bf62013-01-25 14:06:18 +010026 if (!CryptAcquireContext(&hCryptProv, NULL, NULL,
27 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010028 goto error;
29
30 return 0;
31
32error:
33 if (raise)
34 PyErr_SetFromWindowsErr(0);
35 else
36 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
37 return -1;
38}
39
40/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
41 API. Return 0 on success, or -1 on error. */
42static int
43win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
44{
45 Py_ssize_t chunk;
46
47 if (hCryptProv == 0)
48 {
49 if (win32_urandom_init(raise) == -1)
50 return -1;
51 }
52
53 while (size > 0)
54 {
55 chunk = size > INT_MAX ? INT_MAX : size;
Victor Stinner0c083462013-11-15 23:26:25 +010056 if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010057 {
58 /* CryptGenRandom() failed */
59 if (raise)
60 PyErr_SetFromWindowsErr(0);
61 else
62 Py_FatalError("Failed to initialized the randomized hash "
63 "secret using CryptoGen)");
64 return -1;
65 }
66 buffer += chunk;
67 size -= chunk;
68 }
69 return 0;
70}
71#endif /* MS_WINDOWS */
72
73
Christian Heimesaf01f662013-12-21 16:19:10 +010074#ifndef MS_WINDOWS
Antoine Pitroue472aea2014-04-26 14:33:03 +020075static struct {
76 int fd;
77 dev_t st_dev;
78 ino_t st_ino;
79} urandom_cache = { -1 };
Georg Brandl2daf6ae2012-02-20 19:54:16 +010080
81/* Read size bytes from /dev/urandom into buffer.
82 Call Py_FatalError() on error. */
83static void
Christian Heimes985ecdc2013-11-20 11:46:18 +010084dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Georg Brandl2daf6ae2012-02-20 19:54:16 +010085{
86 int fd;
87 Py_ssize_t n;
88
89 assert (0 < size);
90
Victor Stinnerdaf45552013-08-28 00:53:59 +020091 fd = _Py_open("/dev/urandom", O_RDONLY);
Georg Brandl2daf6ae2012-02-20 19:54:16 +010092 if (fd < 0)
93 Py_FatalError("Failed to open /dev/urandom");
94
95 while (0 < size)
96 {
97 do {
98 n = read(fd, buffer, (size_t)size);
99 } while (n < 0 && errno == EINTR);
100 if (n <= 0)
101 {
102 /* stop on error or if read(size) returned 0 */
103 Py_FatalError("Failed to read bytes from /dev/urandom");
104 break;
105 }
106 buffer += n;
107 size -= (Py_ssize_t)n;
108 }
109 close(fd);
110}
111
112/* Read size bytes from /dev/urandom into buffer.
113 Return 0 on success, raise an exception and return -1 on error. */
114static int
115dev_urandom_python(char *buffer, Py_ssize_t size)
116{
117 int fd;
118 Py_ssize_t n;
Antoine Pitroue472aea2014-04-26 14:33:03 +0200119 struct stat st;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100120
121 if (size <= 0)
122 return 0;
123
Antoine Pitroue472aea2014-04-26 14:33:03 +0200124 if (urandom_cache.fd >= 0) {
125 /* Does the fd point to the same thing as before? (issue #21207) */
126 if (fstat(urandom_cache.fd, &st)
127 || st.st_dev != urandom_cache.st_dev
128 || st.st_ino != urandom_cache.st_ino) {
129 /* Something changed: forget the cached fd (but don't close it,
130 since it probably points to something important for some
131 third-party code). */
132 urandom_cache.fd = -1;
133 }
134 }
135 if (urandom_cache.fd >= 0)
136 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200137 else {
138 Py_BEGIN_ALLOW_THREADS
139 fd = _Py_open("/dev/urandom", O_RDONLY);
140 Py_END_ALLOW_THREADS
141 if (fd < 0)
142 {
143 if (errno == ENOENT || errno == ENXIO ||
144 errno == ENODEV || errno == EACCES)
145 PyErr_SetString(PyExc_NotImplementedError,
146 "/dev/urandom (or equivalent) not found");
147 else
148 PyErr_SetFromErrno(PyExc_OSError);
149 return -1;
150 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200151 if (urandom_cache.fd >= 0) {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200152 /* urandom_fd was initialized by another thread while we were
153 not holding the GIL, keep it. */
154 close(fd);
Antoine Pitroue472aea2014-04-26 14:33:03 +0200155 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200156 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200157 else {
158 if (fstat(fd, &st)) {
159 PyErr_SetFromErrno(PyExc_OSError);
160 close(fd);
161 return -1;
162 }
163 else {
164 urandom_cache.fd = fd;
165 urandom_cache.st_dev = st.st_dev;
166 urandom_cache.st_ino = st.st_ino;
167 }
168 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100169 }
170
171 Py_BEGIN_ALLOW_THREADS
172 do {
173 do {
174 n = read(fd, buffer, (size_t)size);
175 } while (n < 0 && errno == EINTR);
176 if (n <= 0)
177 break;
178 buffer += n;
179 size -= (Py_ssize_t)n;
180 } while (0 < size);
181 Py_END_ALLOW_THREADS
182
183 if (n <= 0)
184 {
185 /* stop on error or if read(size) returned 0 */
186 if (n < 0)
187 PyErr_SetFromErrno(PyExc_OSError);
188 else
189 PyErr_Format(PyExc_RuntimeError,
190 "Failed to read %zi bytes from /dev/urandom",
191 size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100192 return -1;
193 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100194 return 0;
195}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200196
197static void
198dev_urandom_close(void)
199{
Antoine Pitroue472aea2014-04-26 14:33:03 +0200200 if (urandom_cache.fd >= 0) {
201 close(urandom_cache.fd);
202 urandom_cache.fd = -1;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200203 }
204}
205
Christian Heimesaf01f662013-12-21 16:19:10 +0100206#endif /* MS_WINDOWS */
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100207
208/* Fill buffer with pseudo-random bytes generated by a linear congruent
209 generator (LCG):
210
211 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
212
213 Use bits 23..16 of x(n) to generate a byte. */
214static void
215lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
216{
217 size_t index;
218 unsigned int x;
219
220 x = x0;
221 for (index=0; index < size; index++) {
222 x *= 214013;
223 x += 2531011;
224 /* modulo 2 ^ (8 * sizeof(int)) */
225 buffer[index] = (x >> 16) & 0xff;
226 }
227}
228
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200229/* Fill buffer with size pseudo-random bytes from the operating system random
230 number generator (RNG). It is suitable for for most cryptographic purposes
231 except long living private keys for asymmetric encryption.
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100232
233 Return 0 on success, raise an exception and return -1 on error. */
234int
235_PyOS_URandom(void *buffer, Py_ssize_t size)
236{
237 if (size < 0) {
238 PyErr_Format(PyExc_ValueError,
239 "negative argument not allowed");
240 return -1;
241 }
242 if (size == 0)
243 return 0;
244
245#ifdef MS_WINDOWS
246 return win32_urandom((unsigned char *)buffer, size, 1);
247#else
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100248 return dev_urandom_python((char*)buffer, size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100249#endif
250}
251
252void
253_PyRandom_Init(void)
254{
255 char *env;
Christian Heimes985ecdc2013-11-20 11:46:18 +0100256 unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500257 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
Christian Heimes985ecdc2013-11-20 11:46:18 +0100258 assert(secret_size == sizeof(_Py_HashSecret.uc));
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100259
Benjamin Peterson69e97272012-02-21 11:08:50 -0500260 if (_Py_HashSecret_Initialized)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100261 return;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500262 _Py_HashSecret_Initialized = 1;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100263
264 /*
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100265 Hash randomization is enabled. Generate a per-process secret,
266 using PYTHONHASHSEED if provided.
267 */
268
269 env = Py_GETENV("PYTHONHASHSEED");
Georg Brandl12897d72012-02-20 23:49:29 +0100270 if (env && *env != '\0' && strcmp(env, "random") != 0) {
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100271 char *endptr = env;
272 unsigned long seed;
273 seed = strtoul(env, &endptr, 10);
274 if (*endptr != '\0'
275 || seed > 4294967295UL
276 || (errno == ERANGE && seed == ULONG_MAX))
277 {
278 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
279 "in range [0; 4294967295]");
280 }
281 if (seed == 0) {
282 /* disable the randomized hash */
283 memset(secret, 0, secret_size);
284 }
285 else {
Christian Heimes985ecdc2013-11-20 11:46:18 +0100286 lcg_urandom(seed, secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100287 }
288 }
289 else {
290#ifdef MS_WINDOWS
Christian Heimes985ecdc2013-11-20 11:46:18 +0100291 (void)win32_urandom(secret, secret_size, 0);
Christian Heimesaf01f662013-12-21 16:19:10 +0100292#else
Christian Heimes985ecdc2013-11-20 11:46:18 +0100293 dev_urandom_noraise(secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100294#endif
295 }
296}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200297
298void
299_PyRandom_Fini(void)
300{
Christian Heimesaf01f662013-12-21 16:19:10 +0100301#ifndef MS_WINDOWS
Antoine Pitrou4879a962013-08-31 00:26:02 +0200302 dev_urandom_close();
303#endif
304}