blob: b04d205a46fe566d8b2911ec2074ee5d576e9f9d [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 +010018static HCRYPTPROV hCryptProv = 0;
19
20static int
21win32_urandom_init(int raise)
22{
Georg Brandl2daf6ae2012-02-20 19:54:16 +010023 /* Acquire context */
Martin v. Löwis3f50bf62013-01-25 14:06:18 +010024 if (!CryptAcquireContext(&hCryptProv, NULL, NULL,
25 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010026 goto error;
27
28 return 0;
29
30error:
31 if (raise)
32 PyErr_SetFromWindowsErr(0);
33 else
34 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
35 return -1;
36}
37
38/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
39 API. Return 0 on success, or -1 on error. */
40static int
41win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
42{
43 Py_ssize_t chunk;
44
45 if (hCryptProv == 0)
46 {
47 if (win32_urandom_init(raise) == -1)
48 return -1;
49 }
50
51 while (size > 0)
52 {
53 chunk = size > INT_MAX ? INT_MAX : size;
Victor Stinner0c083462013-11-15 23:26:25 +010054 if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010055 {
56 /* CryptGenRandom() failed */
57 if (raise)
58 PyErr_SetFromWindowsErr(0);
59 else
60 Py_FatalError("Failed to initialized the randomized hash "
61 "secret using CryptoGen)");
62 return -1;
63 }
64 buffer += chunk;
65 size -= chunk;
66 }
67 return 0;
68}
69#endif /* MS_WINDOWS */
70
71
Christian Heimesaf01f662013-12-21 16:19:10 +010072#ifndef MS_WINDOWS
Antoine Pitroue472aea2014-04-26 14:33:03 +020073static struct {
74 int fd;
75 dev_t st_dev;
76 ino_t st_ino;
77} urandom_cache = { -1 };
Georg Brandl2daf6ae2012-02-20 19:54:16 +010078
79/* Read size bytes from /dev/urandom into buffer.
80 Call Py_FatalError() on error. */
81static void
Christian Heimes985ecdc2013-11-20 11:46:18 +010082dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Georg Brandl2daf6ae2012-02-20 19:54:16 +010083{
84 int fd;
85 Py_ssize_t n;
86
87 assert (0 < size);
88
Victor Stinnerdaf45552013-08-28 00:53:59 +020089 fd = _Py_open("/dev/urandom", O_RDONLY);
Georg Brandl2daf6ae2012-02-20 19:54:16 +010090 if (fd < 0)
91 Py_FatalError("Failed to open /dev/urandom");
92
93 while (0 < size)
94 {
95 do {
96 n = read(fd, buffer, (size_t)size);
97 } while (n < 0 && errno == EINTR);
98 if (n <= 0)
99 {
100 /* stop on error or if read(size) returned 0 */
101 Py_FatalError("Failed to read bytes from /dev/urandom");
102 break;
103 }
104 buffer += n;
105 size -= (Py_ssize_t)n;
106 }
107 close(fd);
108}
109
110/* Read size bytes from /dev/urandom into buffer.
111 Return 0 on success, raise an exception and return -1 on error. */
112static int
113dev_urandom_python(char *buffer, Py_ssize_t size)
114{
115 int fd;
116 Py_ssize_t n;
Antoine Pitroue472aea2014-04-26 14:33:03 +0200117 struct stat st;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100118
119 if (size <= 0)
120 return 0;
121
Antoine Pitroue472aea2014-04-26 14:33:03 +0200122 if (urandom_cache.fd >= 0) {
123 /* Does the fd point to the same thing as before? (issue #21207) */
124 if (fstat(urandom_cache.fd, &st)
125 || st.st_dev != urandom_cache.st_dev
126 || st.st_ino != urandom_cache.st_ino) {
127 /* Something changed: forget the cached fd (but don't close it,
128 since it probably points to something important for some
129 third-party code). */
130 urandom_cache.fd = -1;
131 }
132 }
133 if (urandom_cache.fd >= 0)
134 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200135 else {
136 Py_BEGIN_ALLOW_THREADS
137 fd = _Py_open("/dev/urandom", O_RDONLY);
138 Py_END_ALLOW_THREADS
139 if (fd < 0)
140 {
141 if (errno == ENOENT || errno == ENXIO ||
142 errno == ENODEV || errno == EACCES)
143 PyErr_SetString(PyExc_NotImplementedError,
144 "/dev/urandom (or equivalent) not found");
145 else
146 PyErr_SetFromErrno(PyExc_OSError);
147 return -1;
148 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200149 if (urandom_cache.fd >= 0) {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200150 /* urandom_fd was initialized by another thread while we were
151 not holding the GIL, keep it. */
152 close(fd);
Antoine Pitroue472aea2014-04-26 14:33:03 +0200153 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200154 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200155 else {
156 if (fstat(fd, &st)) {
157 PyErr_SetFromErrno(PyExc_OSError);
158 close(fd);
159 return -1;
160 }
161 else {
162 urandom_cache.fd = fd;
163 urandom_cache.st_dev = st.st_dev;
164 urandom_cache.st_ino = st.st_ino;
165 }
166 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100167 }
168
169 Py_BEGIN_ALLOW_THREADS
170 do {
171 do {
172 n = read(fd, buffer, (size_t)size);
173 } while (n < 0 && errno == EINTR);
174 if (n <= 0)
175 break;
176 buffer += n;
177 size -= (Py_ssize_t)n;
178 } while (0 < size);
179 Py_END_ALLOW_THREADS
180
181 if (n <= 0)
182 {
183 /* stop on error or if read(size) returned 0 */
184 if (n < 0)
185 PyErr_SetFromErrno(PyExc_OSError);
186 else
187 PyErr_Format(PyExc_RuntimeError,
188 "Failed to read %zi bytes from /dev/urandom",
189 size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100190 return -1;
191 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100192 return 0;
193}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200194
195static void
196dev_urandom_close(void)
197{
Antoine Pitroue472aea2014-04-26 14:33:03 +0200198 if (urandom_cache.fd >= 0) {
199 close(urandom_cache.fd);
200 urandom_cache.fd = -1;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200201 }
202}
203
Christian Heimesaf01f662013-12-21 16:19:10 +0100204#endif /* MS_WINDOWS */
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100205
206/* Fill buffer with pseudo-random bytes generated by a linear congruent
207 generator (LCG):
208
209 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
210
211 Use bits 23..16 of x(n) to generate a byte. */
212static void
213lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
214{
215 size_t index;
216 unsigned int x;
217
218 x = x0;
219 for (index=0; index < size; index++) {
220 x *= 214013;
221 x += 2531011;
222 /* modulo 2 ^ (8 * sizeof(int)) */
223 buffer[index] = (x >> 16) & 0xff;
224 }
225}
226
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200227/* Fill buffer with size pseudo-random bytes from the operating system random
228 number generator (RNG). It is suitable for for most cryptographic purposes
229 except long living private keys for asymmetric encryption.
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100230
231 Return 0 on success, raise an exception and return -1 on error. */
232int
233_PyOS_URandom(void *buffer, Py_ssize_t size)
234{
235 if (size < 0) {
236 PyErr_Format(PyExc_ValueError,
237 "negative argument not allowed");
238 return -1;
239 }
240 if (size == 0)
241 return 0;
242
243#ifdef MS_WINDOWS
244 return win32_urandom((unsigned char *)buffer, size, 1);
245#else
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100246 return dev_urandom_python((char*)buffer, size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100247#endif
248}
249
250void
251_PyRandom_Init(void)
252{
253 char *env;
Christian Heimes985ecdc2013-11-20 11:46:18 +0100254 unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500255 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
Christian Heimes985ecdc2013-11-20 11:46:18 +0100256 assert(secret_size == sizeof(_Py_HashSecret.uc));
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100257
Benjamin Peterson69e97272012-02-21 11:08:50 -0500258 if (_Py_HashSecret_Initialized)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100259 return;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500260 _Py_HashSecret_Initialized = 1;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100261
262 /*
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100263 Hash randomization is enabled. Generate a per-process secret,
264 using PYTHONHASHSEED if provided.
265 */
266
267 env = Py_GETENV("PYTHONHASHSEED");
Georg Brandl12897d72012-02-20 23:49:29 +0100268 if (env && *env != '\0' && strcmp(env, "random") != 0) {
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100269 char *endptr = env;
270 unsigned long seed;
271 seed = strtoul(env, &endptr, 10);
272 if (*endptr != '\0'
273 || seed > 4294967295UL
274 || (errno == ERANGE && seed == ULONG_MAX))
275 {
276 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
277 "in range [0; 4294967295]");
278 }
279 if (seed == 0) {
280 /* disable the randomized hash */
281 memset(secret, 0, secret_size);
282 }
283 else {
Christian Heimes985ecdc2013-11-20 11:46:18 +0100284 lcg_urandom(seed, secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100285 }
286 }
287 else {
288#ifdef MS_WINDOWS
Christian Heimes985ecdc2013-11-20 11:46:18 +0100289 (void)win32_urandom(secret, secret_size, 0);
Christian Heimesaf01f662013-12-21 16:19:10 +0100290#else
Christian Heimes985ecdc2013-11-20 11:46:18 +0100291 dev_urandom_noraise(secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100292#endif
293 }
294}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200295
296void
297_PyRandom_Fini(void)
298{
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200299#ifdef MS_WINDOWS
300 if (hCryptProv) {
301 CloseHandle(hCryptProv);
302 hCryptProv = 0;
303 }
304#else
Antoine Pitrou4879a962013-08-31 00:26:02 +0200305 dev_urandom_close();
306#endif
307}