Python-2.7.3/Python/random.c

No issues found

  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 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     fd = open("/dev/urandom", O_RDONLY);
132     if (fd < 0)
133         Py_FatalError("Failed to open /dev/urandom");
134 
135     while (0 < size)
136     {
137         do {
138             n = read(fd, buffer, (size_t)size);
139         } while (n < 0 && errno == EINTR);
140         if (n <= 0)
141         {
142             /* stop on error or if read(size) returned 0 */
143             Py_FatalError("Failed to read bytes from /dev/urandom");
144             break;
145         }
146         buffer += n;
147         size -= (Py_ssize_t)n;
148     }
149     close(fd);
150 }
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 dev_urandom_python(char *buffer, Py_ssize_t size)
156 {
157     int fd;
158     Py_ssize_t n;
159 
160     if (size <= 0)
161         return 0;
162 
163     Py_BEGIN_ALLOW_THREADS
164     fd = open("/dev/urandom", O_RDONLY);
165     Py_END_ALLOW_THREADS
166     if (fd < 0)
167     {
168         PyErr_SetFromErrnoWithFilename(PyExc_OSError, "/dev/urandom");
169         return -1;
170     }
171 
172     Py_BEGIN_ALLOW_THREADS
173     do {
174         do {
175             n = read(fd, buffer, (size_t)size);
176         } while (n < 0 && errno == EINTR);
177         if (n <= 0)
178             break;
179         buffer += n;
180         size -= (Py_ssize_t)n;
181     } while (0 < size);
182     Py_END_ALLOW_THREADS
183 
184     if (n <= 0)
185     {
186         /* stop on error or if read(size) returned 0 */
187         if (n < 0)
188             PyErr_SetFromErrno(PyExc_OSError);
189         else
190             PyErr_Format(PyExc_RuntimeError,
191                          "Failed to read %zi bytes from /dev/urandom",
192                          size);
193         close(fd);
194         return -1;
195     }
196     close(fd);
197     return 0;
198 }
199 #endif /* !defined(MS_WINDOWS) && !defined(__VMS) */
200 
201 /* Fill buffer with pseudo-random bytes generated by a linear congruent
202    generator (LCG):
203 
204        x(n+1) = (x(n) * 214013 + 2531011) % 2^32
205 
206    Use bits 23..16 of x(n) to generate a byte. */
207 static void
208 lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
209 {
210     size_t index;
211     unsigned int x;
212 
213     x = x0;
214     for (index=0; index < size; index++) {
215         x *= 214013;
216         x += 2531011;
217         /* modulo 2 ^ (8 * sizeof(int)) */
218         buffer[index] = (x >> 16) & 0xff;
219     }
220 }
221 
222 /* Fill buffer with size pseudo-random bytes, not suitable for cryptographic
223    use, from the operating random number generator (RNG).
224 
225    Return 0 on success, raise an exception and return -1 on error. */
226 int
227 _PyOS_URandom(void *buffer, Py_ssize_t size)
228 {
229     if (size < 0) {
230         PyErr_Format(PyExc_ValueError,
231                      "negative argument not allowed");
232         return -1;
233     }
234     if (size == 0)
235         return 0;
236 
237 #ifdef MS_WINDOWS
238     return win32_urandom((unsigned char *)buffer, size, 1);
239 #else
240 # ifdef __VMS
241     return vms_urandom((unsigned char *)buffer, size, 1);
242 # else
243     return dev_urandom_python((char*)buffer, size);
244 # endif
245 #endif
246 }
247 
248 void
249 _PyRandom_Init(void)
250 {
251     char *env;
252     void *secret = &_Py_HashSecret;
253     Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
254 
255     if (_Py_HashSecret_Initialized)
256         return;
257     _Py_HashSecret_Initialized = 1;
258 
259     /*
260       By default, hash randomization is disabled, and only
261       enabled if PYTHONHASHSEED is set to non-empty or if
262       "-R" is provided at the command line:
263     */
264     if (!Py_HashRandomizationFlag) {
265         /* Disable the randomized hash: */
266         memset(secret, 0, secret_size);
267         return;
268     }
269 
270     /*
271       Hash randomization is enabled.  Generate a per-process secret,
272       using PYTHONHASHSEED if provided.
273     */
274 
275     env = Py_GETENV("PYTHONHASHSEED");
276     if (env && *env != '\0' && strcmp(env, "random") != 0) {
277         char *endptr = env;
278         unsigned long seed;
279         seed = strtoul(env, &endptr, 10);
280         if (*endptr != '\0'
281             || seed > 4294967295UL
282             || (errno == ERANGE && seed == ULONG_MAX))
283         {
284             Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
285                           "in range [0; 4294967295]");
286         }
287         if (seed == 0) {
288             /* disable the randomized hash */
289             memset(secret, 0, secret_size);
290         }
291         else {
292             lcg_urandom(seed, (unsigned char*)secret, secret_size);
293         }
294     }
295     else {
296 #ifdef MS_WINDOWS
297         (void)win32_urandom((unsigned char *)secret, secret_size, 0);
298 #else /* #ifdef MS_WINDOWS */
299 # ifdef __VMS
300         vms_urandom((unsigned char *)secret, secret_size, 0);
301 # else
302         dev_urandom_noraise((char*)secret, secret_size);
303 # endif
304 #endif
305     }
306 }