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.

401 lines
10 KiB

  1. #include "Python.h"
  2. #ifdef MS_WINDOWS
  3. # include <windows.h>
  4. #else
  5. # include <fcntl.h>
  6. # ifdef HAVE_SYS_STAT_H
  7. # include <sys/stat.h>
  8. # endif
  9. # ifdef HAVE_GETRANDOM_SYSCALL
  10. # include <sys/syscall.h>
  11. # endif
  12. #endif
  13. #ifdef Py_DEBUG
  14. int _Py_HashSecret_Initialized = 0;
  15. #else
  16. static int _Py_HashSecret_Initialized = 0;
  17. #endif
  18. #ifdef MS_WINDOWS
  19. static HCRYPTPROV hCryptProv = 0;
  20. static int
  21. win32_urandom_init(int raise)
  22. {
  23. /* Acquire context */
  24. if (!CryptAcquireContext(&hCryptProv, NULL, NULL,
  25. PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
  26. goto error;
  27. return 0;
  28. error:
  29. if (raise)
  30. PyErr_SetFromWindowsErr(0);
  31. else
  32. Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
  33. return -1;
  34. }
  35. /* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
  36. API. Return 0 on success, or raise an exception and return -1 on error. */
  37. static int
  38. win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
  39. {
  40. Py_ssize_t chunk;
  41. if (hCryptProv == 0)
  42. {
  43. if (win32_urandom_init(raise) == -1)
  44. return -1;
  45. }
  46. while (size > 0)
  47. {
  48. chunk = size > INT_MAX ? INT_MAX : size;
  49. if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
  50. {
  51. /* CryptGenRandom() failed */
  52. if (raise)
  53. PyErr_SetFromWindowsErr(0);
  54. else
  55. Py_FatalError("Failed to initialized the randomized hash "
  56. "secret using CryptoGen)");
  57. return -1;
  58. }
  59. buffer += chunk;
  60. size -= chunk;
  61. }
  62. return 0;
  63. }
  64. #elif HAVE_GETENTROPY
  65. /* Fill buffer with size pseudo-random bytes generated by getentropy().
  66. Return 0 on success, or raise an exception and return -1 on error.
  67. If fatal is nonzero, call Py_FatalError() instead of raising an exception
  68. on error. */
  69. static int
  70. py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
  71. {
  72. while (size > 0) {
  73. Py_ssize_t len = Py_MIN(size, 256);
  74. int res = getentropy(buffer, len);
  75. if (res < 0) {
  76. if (fatal) {
  77. Py_FatalError("getentropy() failed");
  78. }
  79. else {
  80. PyErr_SetFromErrno(PyExc_OSError);
  81. return -1;
  82. }
  83. }
  84. buffer += len;
  85. size -= len;
  86. }
  87. return 0;
  88. }
  89. #else /* !HAVE_GETENTROPY */
  90. #ifdef HAVE_GETRANDOM_SYSCALL
  91. static int
  92. py_getrandom(void *buffer, Py_ssize_t size, int raise)
  93. {
  94. /* is getrandom() supported by the running kernel?
  95. * need Linux kernel 3.17 or later */
  96. static int getrandom_works = 1;
  97. /* Use /dev/urandom, block if the kernel has no entropy */
  98. const int flags = 0;
  99. int n;
  100. if (!getrandom_works)
  101. return 0;
  102. while (0 < size) {
  103. errno = 0;
  104. /* the libc doesn't expose getrandom() yet, see:
  105. * https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */
  106. n = syscall(SYS_getrandom, buffer, size, flags);
  107. if (n < 0) {
  108. if (errno == ENOSYS) {
  109. getrandom_works = 0;
  110. return 0;
  111. }
  112. if (errno == EINTR) {
  113. /* Note: EINTR should not occur with flags=0 */
  114. if (PyErr_CheckSignals()) {
  115. if (!raise)
  116. Py_FatalError("getrandom() interrupted by a signal");
  117. return -1;
  118. }
  119. /* retry getrandom() */
  120. continue;
  121. }
  122. if (raise)
  123. PyErr_SetFromErrno(PyExc_OSError);
  124. else
  125. Py_FatalError("getrandom() failed");
  126. return -1;
  127. }
  128. buffer += n;
  129. size -= n;
  130. }
  131. return 1;
  132. }
  133. #endif
  134. static struct {
  135. int fd;
  136. dev_t st_dev;
  137. ino_t st_ino;
  138. } urandom_cache = { -1 };
  139. /* Read size bytes from /dev/urandom into buffer.
  140. Call Py_FatalError() on error. */
  141. static void
  142. dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
  143. {
  144. int fd;
  145. Py_ssize_t n;
  146. assert (0 < size);
  147. #ifdef HAVE_GETRANDOM_SYSCALL
  148. if (py_getrandom(buffer, size, 0) == 1)
  149. return;
  150. /* getrandom() is not supported by the running kernel, fall back
  151. * on reading /dev/urandom */
  152. #endif
  153. fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
  154. if (fd < 0)
  155. Py_FatalError("Failed to open /dev/urandom");
  156. while (0 < size)
  157. {
  158. do {
  159. n = read(fd, buffer, (size_t)size);
  160. } while (n < 0 && errno == EINTR);
  161. if (n <= 0)
  162. {
  163. /* stop on error or if read(size) returned 0 */
  164. Py_FatalError("Failed to read bytes from /dev/urandom");
  165. break;
  166. }
  167. buffer += n;
  168. size -= (Py_ssize_t)n;
  169. }
  170. close(fd);
  171. }
  172. /* Read size bytes from /dev/urandom into buffer.
  173. Return 0 on success, raise an exception and return -1 on error. */
  174. static int
  175. dev_urandom_python(char *buffer, Py_ssize_t size)
  176. {
  177. int fd;
  178. Py_ssize_t n;
  179. struct _Py_stat_struct st;
  180. #ifdef HAVE_GETRANDOM_SYSCALL
  181. int res;
  182. #endif
  183. if (size <= 0)
  184. return 0;
  185. #ifdef HAVE_GETRANDOM_SYSCALL
  186. res = py_getrandom(buffer, size, 1);
  187. if (res < 0)
  188. return -1;
  189. if (res == 1)
  190. return 0;
  191. /* getrandom() is not supported by the running kernel, fall back
  192. * on reading /dev/urandom */
  193. #endif
  194. if (urandom_cache.fd >= 0) {
  195. /* Does the fd point to the same thing as before? (issue #21207) */
  196. if (_Py_fstat(urandom_cache.fd, &st)
  197. || st.st_dev != urandom_cache.st_dev
  198. || st.st_ino != urandom_cache.st_ino) {
  199. /* Something changed: forget the cached fd (but don't close it,
  200. since it probably points to something important for some
  201. third-party code). */
  202. urandom_cache.fd = -1;
  203. }
  204. }
  205. if (urandom_cache.fd >= 0)
  206. fd = urandom_cache.fd;
  207. else {
  208. fd = _Py_open("/dev/urandom", O_RDONLY);
  209. if (fd < 0) {
  210. if (errno == ENOENT || errno == ENXIO ||
  211. errno == ENODEV || errno == EACCES)
  212. PyErr_SetString(PyExc_NotImplementedError,
  213. "/dev/urandom (or equivalent) not found");
  214. /* otherwise, keep the OSError exception raised by _Py_open() */
  215. return -1;
  216. }
  217. if (urandom_cache.fd >= 0) {
  218. /* urandom_fd was initialized by another thread while we were
  219. not holding the GIL, keep it. */
  220. close(fd);
  221. fd = urandom_cache.fd;
  222. }
  223. else {
  224. if (_Py_fstat(fd, &st)) {
  225. PyErr_SetFromErrno(PyExc_OSError);
  226. close(fd);
  227. return -1;
  228. }
  229. else {
  230. urandom_cache.fd = fd;
  231. urandom_cache.st_dev = st.st_dev;
  232. urandom_cache.st_ino = st.st_ino;
  233. }
  234. }
  235. }
  236. do {
  237. n = _Py_read(fd, buffer, (size_t)size);
  238. if (n == -1)
  239. return -1;
  240. if (n == 0) {
  241. PyErr_Format(PyExc_RuntimeError,
  242. "Failed to read %zi bytes from /dev/urandom",
  243. size);
  244. return -1;
  245. }
  246. buffer += n;
  247. size -= n;
  248. } while (0 < size);
  249. return 0;
  250. }
  251. static void
  252. dev_urandom_close(void)
  253. {
  254. if (urandom_cache.fd >= 0) {
  255. close(urandom_cache.fd);
  256. urandom_cache.fd = -1;
  257. }
  258. }
  259. #endif /* HAVE_GETENTROPY */
  260. /* Fill buffer with pseudo-random bytes generated by a linear congruent
  261. generator (LCG):
  262. x(n+1) = (x(n) * 214013 + 2531011) % 2^32
  263. Use bits 23..16 of x(n) to generate a byte. */
  264. static void
  265. lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
  266. {
  267. size_t index;
  268. unsigned int x;
  269. x = x0;
  270. for (index=0; index < size; index++) {
  271. x *= 214013;
  272. x += 2531011;
  273. /* modulo 2 ^ (8 * sizeof(int)) */
  274. buffer[index] = (x >> 16) & 0xff;
  275. }
  276. }
  277. /* Fill buffer with size pseudo-random bytes from the operating system random
  278. number generator (RNG). It is suitable for most cryptographic purposes
  279. except long living private keys for asymmetric encryption.
  280. Return 0 on success, raise an exception and return -1 on error. */
  281. int
  282. _PyOS_URandom(void *buffer, Py_ssize_t size)
  283. {
  284. if (size < 0) {
  285. PyErr_Format(PyExc_ValueError,
  286. "negative argument not allowed");
  287. return -1;
  288. }
  289. if (size == 0)
  290. return 0;
  291. #ifdef MS_WINDOWS
  292. return win32_urandom((unsigned char *)buffer, size, 1);
  293. #elif HAVE_GETENTROPY
  294. return py_getentropy(buffer, size, 0);
  295. #else
  296. return dev_urandom_python((char*)buffer, size);
  297. #endif
  298. }
  299. void
  300. _PyRandom_Init(void)
  301. {
  302. char *env;
  303. unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc;
  304. Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
  305. assert(secret_size == sizeof(_Py_HashSecret.uc));
  306. if (_Py_HashSecret_Initialized)
  307. return;
  308. _Py_HashSecret_Initialized = 1;
  309. /*
  310. Hash randomization is enabled. Generate a per-process secret,
  311. using PYTHONHASHSEED if provided.
  312. */
  313. env = Py_GETENV("PYTHONHASHSEED");
  314. if (env && *env != '\0' && strcmp(env, "random") != 0) {
  315. char *endptr = env;
  316. unsigned long seed;
  317. seed = strtoul(env, &endptr, 10);
  318. if (*endptr != '\0'
  319. || seed > 4294967295UL
  320. || (errno == ERANGE && seed == ULONG_MAX))
  321. {
  322. Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
  323. "in range [0; 4294967295]");
  324. }
  325. if (seed == 0) {
  326. /* disable the randomized hash */
  327. memset(secret, 0, secret_size);
  328. }
  329. else {
  330. lcg_urandom(seed, secret, secret_size);
  331. }
  332. }
  333. else {
  334. #ifdef MS_WINDOWS
  335. (void)win32_urandom(secret, secret_size, 0);
  336. #elif HAVE_GETENTROPY
  337. (void)py_getentropy(secret, secret_size, 1);
  338. #else
  339. dev_urandom_noraise(secret, secret_size);
  340. #endif
  341. }
  342. }
  343. void
  344. _PyRandom_Fini(void)
  345. {
  346. #ifdef MS_WINDOWS
  347. if (hCryptProv) {
  348. CryptReleaseContext(hCryptProv, 0);
  349. hCryptProv = 0;
  350. }
  351. #elif HAVE_GETENTROPY
  352. /* nothing to clean */
  353. #else
  354. dev_urandom_close();
  355. #endif
  356. }