]> git.proxmox.com Git - mirror_edk2.git/blame - AppPkg/Applications/Python/Python-2.7.10/PyMod-2.7.10/Python/random.c
AppPkg/.../Python-2.7.10: AppPkg.dsc, pyconfig.h, PyMod-2.7.10
[mirror_edk2.git] / AppPkg / Applications / Python / Python-2.7.10 / PyMod-2.7.10 / Python / random.c
CommitLineData
d11973f1
DM
1/** @file\r
2\r
3 Copyright (c) 2015, Daryl McDaniel. All rights reserved.<BR>\r
4 This program and the accompanying materials are licensed and made available under\r
5 the terms and conditions of the BSD License that accompanies this distribution.\r
6 The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.\r
8\r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11**/\r
12\r
3ec97ca4
DM
13#include "Python.h"\r
14#ifdef MS_WINDOWS\r
15#include <windows.h>\r
16#else\r
17#include <fcntl.h>\r
18#endif\r
19\r
20#ifdef Py_DEBUG\r
21int _Py_HashSecret_Initialized = 0;\r
22#else\r
23static int _Py_HashSecret_Initialized = 0;\r
24#endif\r
25\r
26#ifdef MS_WINDOWS\r
27typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\\r
28 LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\\r
29 DWORD dwFlags );\r
30typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\\r
31 BYTE *pbBuffer );\r
32\r
33static CRYPTGENRANDOM pCryptGenRandom = NULL;\r
34/* This handle is never explicitly released. Instead, the operating\r
35 system will release it when the process terminates. */\r
36static HCRYPTPROV hCryptProv = 0;\r
37\r
38static int\r
39win32_urandom_init(int raise)\r
40{\r
41 HINSTANCE hAdvAPI32 = NULL;\r
42 CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;\r
43\r
44 /* Obtain handle to the DLL containing CryptoAPI. This should not fail. */\r
45 hAdvAPI32 = GetModuleHandle("advapi32.dll");\r
46 if(hAdvAPI32 == NULL)\r
47 goto error;\r
48\r
49 /* Obtain pointers to the CryptoAPI functions. This will fail on some early\r
50 versions of Win95. */\r
51 pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(\r
52 hAdvAPI32, "CryptAcquireContextA");\r
53 if (pCryptAcquireContext == NULL)\r
54 goto error;\r
55\r
56 pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,\r
57 "CryptGenRandom");\r
58 if (pCryptGenRandom == NULL)\r
59 goto error;\r
60\r
61 /* Acquire context */\r
62 if (! pCryptAcquireContext(&hCryptProv, NULL, NULL,\r
63 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))\r
64 goto error;\r
65\r
66 return 0;\r
67\r
68error:\r
69 if (raise)\r
70 PyErr_SetFromWindowsErr(0);\r
71 else\r
72 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");\r
73 return -1;\r
74}\r
75\r
76/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen\r
77 API. Return 0 on success, or -1 on error. */\r
78static int\r
79win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)\r
80{\r
81 Py_ssize_t chunk;\r
82\r
83 if (hCryptProv == 0)\r
84 {\r
85 if (win32_urandom_init(raise) == -1)\r
86 return -1;\r
87 }\r
88\r
89 while (size > 0)\r
90 {\r
91 chunk = size > INT_MAX ? INT_MAX : size;\r
92 if (!pCryptGenRandom(hCryptProv, chunk, buffer))\r
93 {\r
94 /* CryptGenRandom() failed */\r
95 if (raise)\r
96 PyErr_SetFromWindowsErr(0);\r
97 else\r
98 Py_FatalError("Failed to initialized the randomized hash "\r
99 "secret using CryptoGen)");\r
100 return -1;\r
101 }\r
102 buffer += chunk;\r
103 size -= chunk;\r
104 }\r
105 return 0;\r
106}\r
107\r
108#elif HAVE_GETENTROPY\r
109/* Fill buffer with size pseudo-random bytes generated by getentropy().\r
110 Return 0 on success, or raise an exception and return -1 on error.\r
111 If fatal is nonzero, call Py_FatalError() instead of raising an exception\r
112 on error. */\r
113static int\r
114py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)\r
115{\r
116 while (size > 0) {\r
117 Py_ssize_t len = size < 256 ? size : 256;\r
118 int res;\r
119\r
120 if (!fatal) {\r
121 Py_BEGIN_ALLOW_THREADS\r
122 res = getentropy(buffer, len);\r
123 Py_END_ALLOW_THREADS\r
124\r
125 if (res < 0) {\r
126 PyErr_SetFromErrno(PyExc_OSError);\r
127 return -1;\r
128 }\r
129 }\r
130 else {\r
131 res = getentropy(buffer, len);\r
132 if (res < 0)\r
133 Py_FatalError("getentropy() failed");\r
134 }\r
135\r
136 buffer += len;\r
137 size -= len;\r
138 }\r
139 return 0;\r
140}\r
141#endif\r
142\r
143#ifdef __VMS\r
144/* Use openssl random routine */\r
145#include <openssl/rand.h>\r
146static int\r
147vms_urandom(unsigned char *buffer, Py_ssize_t size, int raise)\r
148{\r
149 if (RAND_pseudo_bytes(buffer, size) < 0) {\r
150 if (raise) {\r
151 PyErr_Format(PyExc_ValueError,\r
152 "RAND_pseudo_bytes");\r
153 } else {\r
154 Py_FatalError("Failed to initialize the randomized hash "\r
155 "secret using RAND_pseudo_bytes");\r
156 }\r
157 return -1;\r
158 }\r
159 return 0;\r
160}\r
161#endif /* __VMS */\r
162\r
163\r
164#if !defined(MS_WINDOWS) && !defined(__VMS)\r
165\r
166static struct {\r
167 int fd;\r
d11973f1 168#ifdef HAVE_STRUCT_STAT_ST_DEV\r
3ec97ca4 169 dev_t st_dev;\r
d11973f1
DM
170#endif\r
171#ifdef HAVE_STRUCT_STAT_ST_INO\r
3ec97ca4 172 ino_t st_ino;\r
d11973f1 173#endif\r
3ec97ca4
DM
174} urandom_cache = { -1 };\r
175\r
176/* Read size bytes from /dev/urandom into buffer.\r
177 Call Py_FatalError() on error. */\r
178static void\r
179dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)\r
180{\r
181 int fd;\r
182 Py_ssize_t n;\r
183\r
184 assert (0 < size);\r
185\r
d11973f1 186 fd = open("/dev/urandom", O_RDONLY, 0);\r
3ec97ca4
DM
187 if (fd < 0)\r
188 Py_FatalError("Failed to open /dev/urandom");\r
189\r
190 while (0 < size)\r
191 {\r
192 do {\r
193 n = read(fd, buffer, (size_t)size);\r
194 } while (n < 0 && errno == EINTR);\r
195 if (n <= 0)\r
196 {\r
197 /* stop on error or if read(size) returned 0 */\r
198 Py_FatalError("Failed to read bytes from /dev/urandom");\r
199 break;\r
200 }\r
201 buffer += n;\r
202 size -= (Py_ssize_t)n;\r
203 }\r
204 close(fd);\r
205}\r
206\r
207/* Read size bytes from /dev/urandom into buffer.\r
208 Return 0 on success, raise an exception and return -1 on error. */\r
209static int\r
210dev_urandom_python(char *buffer, Py_ssize_t size)\r
211{\r
212 int fd;\r
213 Py_ssize_t n;\r
214 struct stat st;\r
215 int attr;\r
216\r
217 if (size <= 0)\r
218 return 0;\r
219\r
220 if (urandom_cache.fd >= 0) {\r
221 /* Does the fd point to the same thing as before? (issue #21207) */\r
222 if (fstat(urandom_cache.fd, &st)\r
d11973f1 223#ifdef HAVE_STRUCT_STAT_ST_DEV\r
3ec97ca4 224 || st.st_dev != urandom_cache.st_dev\r
d11973f1
DM
225#endif\r
226#ifdef HAVE_STRUCT_STAT_ST_INO\r
227 || st.st_ino != urandom_cache.st_ino\r
228#endif\r
229 )\r
230 {\r
3ec97ca4
DM
231 /* Something changed: forget the cached fd (but don't close it,\r
232 since it probably points to something important for some\r
233 third-party code). */\r
234 urandom_cache.fd = -1;\r
235 }\r
236 }\r
237 if (urandom_cache.fd >= 0)\r
238 fd = urandom_cache.fd;\r
239 else {\r
240 Py_BEGIN_ALLOW_THREADS\r
d11973f1 241 fd = open("/dev/urandom", O_RDONLY, 0);\r
3ec97ca4
DM
242 Py_END_ALLOW_THREADS\r
243 if (fd < 0)\r
244 {\r
245 if (errno == ENOENT || errno == ENXIO ||\r
246 errno == ENODEV || errno == EACCES)\r
247 PyErr_SetString(PyExc_NotImplementedError,\r
248 "/dev/urandom (or equivalent) not found");\r
249 else\r
250 PyErr_SetFromErrno(PyExc_OSError);\r
251 return -1;\r
252 }\r
253\r
254 /* try to make the file descriptor non-inheritable, ignore errors */\r
255 attr = fcntl(fd, F_GETFD);\r
256 if (attr >= 0) {\r
257 attr |= FD_CLOEXEC;\r
258 (void)fcntl(fd, F_SETFD, attr);\r
259 }\r
260\r
261 if (urandom_cache.fd >= 0) {\r
262 /* urandom_fd was initialized by another thread while we were\r
263 not holding the GIL, keep it. */\r
264 close(fd);\r
265 fd = urandom_cache.fd;\r
266 }\r
267 else {\r
268 if (fstat(fd, &st)) {\r
269 PyErr_SetFromErrno(PyExc_OSError);\r
270 close(fd);\r
271 return -1;\r
272 }\r
273 else {\r
274 urandom_cache.fd = fd;\r
d11973f1 275#ifdef HAVE_STRUCT_STAT_ST_DEV\r
3ec97ca4 276 urandom_cache.st_dev = st.st_dev;\r
d11973f1
DM
277#endif\r
278#ifdef HAVE_STRUCT_STAT_ST_INO\r
3ec97ca4 279 urandom_cache.st_ino = st.st_ino;\r
d11973f1 280#endif\r
3ec97ca4
DM
281 }\r
282 }\r
283 }\r
284\r
285 Py_BEGIN_ALLOW_THREADS\r
286 do {\r
287 do {\r
288 n = read(fd, buffer, (size_t)size);\r
289 } while (n < 0 && errno == EINTR);\r
290 if (n <= 0)\r
291 break;\r
292 buffer += n;\r
293 size -= (Py_ssize_t)n;\r
294 } while (0 < size);\r
295 Py_END_ALLOW_THREADS\r
296\r
297 if (n <= 0)\r
298 {\r
299 /* stop on error or if read(size) returned 0 */\r
300 if (n < 0)\r
301 PyErr_SetFromErrno(PyExc_OSError);\r
302 else\r
303 PyErr_Format(PyExc_RuntimeError,\r
304 "Failed to read %zi bytes from /dev/urandom",\r
305 size);\r
306 return -1;\r
307 }\r
308 return 0;\r
309}\r
310\r
311static void\r
312dev_urandom_close(void)\r
313{\r
314 if (urandom_cache.fd >= 0) {\r
315 close(urandom_cache.fd);\r
316 urandom_cache.fd = -1;\r
317 }\r
318}\r
319\r
320\r
321#endif /* !defined(MS_WINDOWS) && !defined(__VMS) */\r
322\r
323/* Fill buffer with pseudo-random bytes generated by a linear congruent\r
324 generator (LCG):\r
325\r
326 x(n+1) = (x(n) * 214013 + 2531011) % 2^32\r
327\r
328 Use bits 23..16 of x(n) to generate a byte. */\r
329static void\r
330lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)\r
331{\r
332 size_t index;\r
333 unsigned int x;\r
334\r
335 x = x0;\r
336 for (index=0; index < size; index++) {\r
337 x *= 214013;\r
338 x += 2531011;\r
339 /* modulo 2 ^ (8 * sizeof(int)) */\r
340 buffer[index] = (x >> 16) & 0xff;\r
341 }\r
342}\r
343\r
344/* Fill buffer with size pseudo-random bytes from the operating system random\r
345 number generator (RNG). It is suitable for most cryptographic purposes\r
346 except long living private keys for asymmetric encryption.\r
347\r
348 Return 0 on success, raise an exception and return -1 on error. */\r
349int\r
350_PyOS_URandom(void *buffer, Py_ssize_t size)\r
351{\r
352 if (size < 0) {\r
353 PyErr_Format(PyExc_ValueError,\r
354 "negative argument not allowed");\r
355 return -1;\r
356 }\r
357 if (size == 0)\r
358 return 0;\r
359\r
360#ifdef MS_WINDOWS\r
361 return win32_urandom((unsigned char *)buffer, size, 1);\r
362#elif HAVE_GETENTROPY\r
363 return py_getentropy(buffer, size, 0);\r
364#else\r
365# ifdef __VMS\r
366 return vms_urandom((unsigned char *)buffer, size, 1);\r
367# else\r
368 return dev_urandom_python((char*)buffer, size);\r
369# endif\r
370#endif\r
371}\r
372\r
373void\r
374_PyRandom_Init(void)\r
375{\r
376 char *env;\r
377 void *secret = &_Py_HashSecret;\r
378 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);\r
379\r
380 if (_Py_HashSecret_Initialized)\r
381 return;\r
382 _Py_HashSecret_Initialized = 1;\r
383\r
384 /*\r
385 By default, hash randomization is disabled, and only\r
386 enabled if PYTHONHASHSEED is set to non-empty or if\r
387 "-R" is provided at the command line:\r
388 */\r
389 if (!Py_HashRandomizationFlag) {\r
390 /* Disable the randomized hash: */\r
391 memset(secret, 0, secret_size);\r
392 return;\r
393 }\r
394\r
395 /*\r
396 Hash randomization is enabled. Generate a per-process secret,\r
397 using PYTHONHASHSEED if provided.\r
398 */\r
399\r
400 env = Py_GETENV("PYTHONHASHSEED");\r
401 if (env && *env != '\0' && strcmp(env, "random") != 0) {\r
402 char *endptr = env;\r
403 unsigned long seed;\r
404 seed = strtoul(env, &endptr, 10);\r
405 if (*endptr != '\0'\r
406 || seed > 4294967295UL\r
407 || (errno == ERANGE && seed == ULONG_MAX))\r
408 {\r
409 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "\r
410 "in range [0; 4294967295]");\r
411 }\r
412 if (seed == 0) {\r
413 /* disable the randomized hash */\r
414 memset(secret, 0, secret_size);\r
415 }\r
416 else {\r
417 lcg_urandom(seed, (unsigned char*)secret, secret_size);\r
418 }\r
419 }\r
420 else {\r
421#ifdef MS_WINDOWS\r
422 (void)win32_urandom((unsigned char *)secret, secret_size, 0);\r
423#elif __VMS\r
424 vms_urandom((unsigned char *)secret, secret_size, 0);\r
425#elif HAVE_GETENTROPY\r
426 (void)py_getentropy(secret, secret_size, 1);\r
427#else\r
428 dev_urandom_noraise(secret, secret_size);\r
429#endif\r
430 }\r
431}\r
432\r
433void\r
434_PyRandom_Fini(void)\r
435{\r
436#ifdef MS_WINDOWS\r
437 if (hCryptProv) {\r
438 CryptReleaseContext(hCryptProv, 0);\r
439 hCryptProv = 0;\r
440 }\r
441#elif HAVE_GETENTROPY\r
442 /* nothing to clean */\r
443#else\r
444 dev_urandom_close();\r
445#endif\r
446}\r