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