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.

296 lines
7.5 KiB

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