You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

302 lines
7.7 KiB

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