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.

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