Line data Source code
1 : #include "Python.h"
2 : #ifdef MS_WINDOWS
3 : #include <windows.h>
4 : #else
5 : #include <fcntl.h>
6 : #endif
7 :
8 : #ifdef Py_DEBUG
9 : int _Py_HashSecret_Initialized = 0;
10 : #else
11 : static int _Py_HashSecret_Initialized = 0;
12 : #endif
13 :
14 : #ifdef MS_WINDOWS
15 : typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\
16 : LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\
17 : DWORD dwFlags );
18 : typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\
19 : BYTE *pbBuffer );
20 :
21 : static CRYPTGENRANDOM pCryptGenRandom = NULL;
22 : /* This handle is never explicitly released. Instead, the operating
23 : system will release it when the process terminates. */
24 : static HCRYPTPROV hCryptProv = 0;
25 :
26 : static int
27 : win32_urandom_init(int raise)
28 : {
29 : HINSTANCE hAdvAPI32 = NULL;
30 : CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
31 :
32 : /* Obtain handle to the DLL containing CryptoAPI. This should not fail. */
33 : hAdvAPI32 = GetModuleHandle("advapi32.dll");
34 : if(hAdvAPI32 == NULL)
35 : goto error;
36 :
37 : /* Obtain pointers to the CryptoAPI functions. This will fail on some early
38 : versions of Win95. */
39 : pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(
40 : hAdvAPI32, "CryptAcquireContextA");
41 : if (pCryptAcquireContext == NULL)
42 : goto error;
43 :
44 : pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,
45 : "CryptGenRandom");
46 : if (pCryptGenRandom == NULL)
47 : goto error;
48 :
49 : /* Acquire context */
50 : if (! pCryptAcquireContext(&hCryptProv, NULL, NULL,
51 : PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
52 : goto error;
53 :
54 : return 0;
55 :
56 : error:
57 : if (raise)
58 : PyErr_SetFromWindowsErr(0);
59 : else
60 : Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
61 : return -1;
62 : }
63 :
64 : /* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
65 : API. Return 0 on success, or -1 on error. */
66 : static int
67 : win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
68 : {
69 : Py_ssize_t chunk;
70 :
71 : if (hCryptProv == 0)
72 : {
73 : if (win32_urandom_init(raise) == -1)
74 : return -1;
75 : }
76 :
77 : while (size > 0)
78 : {
79 : chunk = size > INT_MAX ? INT_MAX : size;
80 : if (!pCryptGenRandom(hCryptProv, chunk, buffer))
81 : {
82 : /* CryptGenRandom() failed */
83 : if (raise)
84 : PyErr_SetFromWindowsErr(0);
85 : else
86 : Py_FatalError("Failed to initialized the randomized hash "
87 : "secret using CryptoGen)");
88 : return -1;
89 : }
90 : buffer += chunk;
91 : size -= chunk;
92 : }
93 : return 0;
94 : }
95 : #endif /* MS_WINDOWS */
96 :
97 :
98 : #ifdef __VMS
99 : /* Use openssl random routine */
100 : #include <openssl/rand.h>
101 : static int
102 : vms_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
103 : {
104 : if (RAND_pseudo_bytes(buffer, size) < 0) {
105 : if (raise) {
106 : PyErr_Format(PyExc_ValueError,
107 : "RAND_pseudo_bytes");
108 : } else {
109 : Py_FatalError("Failed to initialize the randomized hash "
110 : "secret using RAND_pseudo_bytes");
111 : }
112 : return -1;
113 : }
114 : return 0;
115 : }
116 : #endif /* __VMS */
117 :
118 :
119 : #if !defined(MS_WINDOWS) && !defined(__VMS)
120 :
121 : /* Read size bytes from /dev/urandom into buffer.
122 : Call Py_FatalError() on error. */
123 : static void
124 1 : dev_urandom_noraise(char *buffer, Py_ssize_t size)
125 : {
126 : int fd;
127 : Py_ssize_t n;
128 :
129 : assert (0 < size);
130 :
131 1 : fd = open("/dev/urandom", O_RDONLY);
132 1 : if (fd < 0)
133 0 : Py_FatalError("Failed to open /dev/urandom");
134 :
135 3 : while (0 < size)
136 : {
137 : do {
138 1 : n = read(fd, buffer, (size_t)size);
139 1 : } while (n < 0 && errno == EINTR);
140 1 : if (n <= 0)
141 : {
142 : /* stop on error or if read(size) returned 0 */
143 0 : Py_FatalError("Failed to read bytes from /dev/urandom");
144 0 : break;
145 : }
146 1 : buffer += n;
147 1 : size -= (Py_ssize_t)n;
148 : }
149 1 : close(fd);
150 1 : }
151 :
152 : /* Read size bytes from /dev/urandom into buffer.
153 : Return 0 on success, raise an exception and return -1 on error. */
154 : static int
155 0 : dev_urandom_python(char *buffer, Py_ssize_t size)
156 : {
157 : int fd;
158 : Py_ssize_t n;
159 :
160 0 : if (size <= 0)
161 0 : return 0;
162 :
163 0 : Py_BEGIN_ALLOW_THREADS
164 0 : fd = open("/dev/urandom", O_RDONLY);
165 0 : Py_END_ALLOW_THREADS
166 0 : if (fd < 0)
167 : {
168 0 : PyErr_SetString(PyExc_NotImplementedError,
169 : "/dev/urandom (or equivalent) not found");
170 0 : return -1;
171 : }
172 :
173 0 : Py_BEGIN_ALLOW_THREADS
174 : do {
175 : do {
176 0 : n = read(fd, buffer, (size_t)size);
177 0 : } while (n < 0 && errno == EINTR);
178 0 : if (n <= 0)
179 0 : break;
180 0 : buffer += n;
181 0 : size -= (Py_ssize_t)n;
182 0 : } while (0 < size);
183 0 : Py_END_ALLOW_THREADS
184 :
185 0 : if (n <= 0)
186 : {
187 : /* stop on error or if read(size) returned 0 */
188 0 : if (n < 0)
189 0 : PyErr_SetFromErrno(PyExc_OSError);
190 : else
191 0 : PyErr_Format(PyExc_RuntimeError,
192 : "Failed to read %zi bytes from /dev/urandom",
193 : size);
194 0 : close(fd);
195 0 : return -1;
196 : }
197 0 : close(fd);
198 0 : return 0;
199 : }
200 : #endif /* !defined(MS_WINDOWS) && !defined(__VMS) */
201 :
202 : /* Fill buffer with pseudo-random bytes generated by a linear congruent
203 : generator (LCG):
204 :
205 : x(n+1) = (x(n) * 214013 + 2531011) % 2^32
206 :
207 : Use bits 23..16 of x(n) to generate a byte. */
208 : static void
209 0 : lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
210 : {
211 : size_t index;
212 : unsigned int x;
213 :
214 0 : x = x0;
215 0 : for (index=0; index < size; index++) {
216 0 : x *= 214013;
217 0 : x += 2531011;
218 : /* modulo 2 ^ (8 * sizeof(int)) */
219 0 : buffer[index] = (x >> 16) & 0xff;
220 : }
221 0 : }
222 :
223 : /* Fill buffer with size pseudo-random bytes, not suitable for cryptographic
224 : use, from the operating random number generator (RNG).
225 :
226 : Return 0 on success, raise an exception and return -1 on error. */
227 : int
228 0 : _PyOS_URandom(void *buffer, Py_ssize_t size)
229 : {
230 0 : if (size < 0) {
231 0 : PyErr_Format(PyExc_ValueError,
232 : "negative argument not allowed");
233 0 : return -1;
234 : }
235 0 : if (size == 0)
236 0 : return 0;
237 :
238 : #ifdef MS_WINDOWS
239 : return win32_urandom((unsigned char *)buffer, size, 1);
240 : #else
241 : # ifdef __VMS
242 : return vms_urandom((unsigned char *)buffer, size, 1);
243 : # else
244 0 : return dev_urandom_python((char*)buffer, size);
245 : # endif
246 : #endif
247 : }
248 :
249 : void
250 1 : _PyRandom_Init(void)
251 : {
252 : char *env;
253 1 : void *secret = &_Py_HashSecret;
254 1 : Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
255 :
256 1 : if (_Py_HashSecret_Initialized)
257 1 : return;
258 1 : _Py_HashSecret_Initialized = 1;
259 :
260 : /*
261 : Hash randomization is enabled. Generate a per-process secret,
262 : using PYTHONHASHSEED if provided.
263 : */
264 :
265 1 : env = Py_GETENV("PYTHONHASHSEED");
266 1 : if (env && *env != '\0' && strcmp(env, "random") != 0) {
267 0 : char *endptr = env;
268 : unsigned long seed;
269 0 : seed = strtoul(env, &endptr, 10);
270 0 : if (*endptr != '\0'
271 0 : || seed > 4294967295UL
272 0 : || (errno == ERANGE && seed == ULONG_MAX))
273 : {
274 0 : Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
275 : "in range [0; 4294967295]");
276 : }
277 0 : if (seed == 0) {
278 : /* disable the randomized hash */
279 0 : memset(secret, 0, secret_size);
280 : }
281 : else {
282 0 : lcg_urandom(seed, (unsigned char*)secret, secret_size);
283 : }
284 : }
285 : else {
286 : #ifdef MS_WINDOWS
287 : (void)win32_urandom((unsigned char *)secret, secret_size, 0);
288 : #else /* #ifdef MS_WINDOWS */
289 : # ifdef __VMS
290 : vms_urandom((unsigned char *)secret, secret_size, 0);
291 : # else
292 1 : dev_urandom_noraise((char*)secret, secret_size);
293 : # endif
294 : #endif
295 : }
296 : }
|