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.

437 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. errno = 0;
  116. #ifdef HAVE_GETRANDOM
  117. if (raise) {
  118. Py_BEGIN_ALLOW_THREADS
  119. n = getrandom(buffer, size, flags);
  120. Py_END_ALLOW_THREADS
  121. }
  122. else {
  123. n = getrandom(buffer, size, flags);
  124. }
  125. #else
  126. /* On Linux, use the syscall() function because the GNU libc doesn't
  127. * expose the Linux getrandom() syscall yet. See:
  128. * https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */
  129. if (raise) {
  130. Py_BEGIN_ALLOW_THREADS
  131. n = syscall(SYS_getrandom, buffer, size, flags);
  132. Py_END_ALLOW_THREADS
  133. }
  134. else {
  135. n = syscall(SYS_getrandom, buffer, size, flags);
  136. }
  137. #endif
  138. if (n < 0) {
  139. if (errno == ENOSYS) {
  140. getrandom_works = 0;
  141. return 0;
  142. }
  143. if (errno == EINTR) {
  144. if (PyErr_CheckSignals()) {
  145. if (!raise)
  146. Py_FatalError("getrandom() interrupted by a signal");
  147. return -1;
  148. }
  149. /* retry getrandom() */
  150. continue;
  151. }
  152. if (raise)
  153. PyErr_SetFromErrno(PyExc_OSError);
  154. else
  155. Py_FatalError("getrandom() failed");
  156. return -1;
  157. }
  158. buffer += n;
  159. size -= n;
  160. }
  161. return 1;
  162. }
  163. #endif
  164. static struct {
  165. int fd;
  166. dev_t st_dev;
  167. ino_t st_ino;
  168. } urandom_cache = { -1 };
  169. /* Read size bytes from /dev/urandom into buffer.
  170. Call Py_FatalError() on error. */
  171. static void
  172. dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
  173. {
  174. int fd;
  175. Py_ssize_t n;
  176. assert (0 < size);
  177. #ifdef PY_GETRANDOM
  178. if (py_getrandom(buffer, size, 0) == 1)
  179. return;
  180. /* getrandom() is not supported by the running kernel, fall back
  181. * on reading /dev/urandom */
  182. #endif
  183. fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
  184. if (fd < 0)
  185. Py_FatalError("Failed to open /dev/urandom");
  186. while (0 < size)
  187. {
  188. do {
  189. n = read(fd, buffer, (size_t)size);
  190. } while (n < 0 && errno == EINTR);
  191. if (n <= 0)
  192. {
  193. /* stop on error or if read(size) returned 0 */
  194. Py_FatalError("Failed to read bytes from /dev/urandom");
  195. break;
  196. }
  197. buffer += n;
  198. size -= (Py_ssize_t)n;
  199. }
  200. close(fd);
  201. }
  202. /* Read size bytes from /dev/urandom into buffer.
  203. Return 0 on success, raise an exception and return -1 on error. */
  204. static int
  205. dev_urandom_python(char *buffer, Py_ssize_t size)
  206. {
  207. int fd;
  208. Py_ssize_t n;
  209. struct _Py_stat_struct st;
  210. #ifdef PY_GETRANDOM
  211. int res;
  212. #endif
  213. if (size <= 0)
  214. return 0;
  215. #ifdef PY_GETRANDOM
  216. res = py_getrandom(buffer, size, 1);
  217. if (res < 0)
  218. return -1;
  219. if (res == 1)
  220. return 0;
  221. /* getrandom() is not supported by the running kernel, fall back
  222. * on reading /dev/urandom */
  223. #endif
  224. if (urandom_cache.fd >= 0) {
  225. /* Does the fd point to the same thing as before? (issue #21207) */
  226. if (_Py_fstat_noraise(urandom_cache.fd, &st)
  227. || st.st_dev != urandom_cache.st_dev
  228. || st.st_ino != urandom_cache.st_ino) {
  229. /* Something changed: forget the cached fd (but don't close it,
  230. since it probably points to something important for some
  231. third-party code). */
  232. urandom_cache.fd = -1;
  233. }
  234. }
  235. if (urandom_cache.fd >= 0)
  236. fd = urandom_cache.fd;
  237. else {
  238. fd = _Py_open("/dev/urandom", O_RDONLY);
  239. if (fd < 0) {
  240. if (errno == ENOENT || errno == ENXIO ||
  241. errno == ENODEV || errno == EACCES)
  242. PyErr_SetString(PyExc_NotImplementedError,
  243. "/dev/urandom (or equivalent) not found");
  244. /* otherwise, keep the OSError exception raised by _Py_open() */
  245. return -1;
  246. }
  247. if (urandom_cache.fd >= 0) {
  248. /* urandom_fd was initialized by another thread while we were
  249. not holding the GIL, keep it. */
  250. close(fd);
  251. fd = urandom_cache.fd;
  252. }
  253. else {
  254. if (_Py_fstat(fd, &st)) {
  255. close(fd);
  256. return -1;
  257. }
  258. else {
  259. urandom_cache.fd = fd;
  260. urandom_cache.st_dev = st.st_dev;
  261. urandom_cache.st_ino = st.st_ino;
  262. }
  263. }
  264. }
  265. do {
  266. n = _Py_read(fd, buffer, (size_t)size);
  267. if (n == -1)
  268. return -1;
  269. if (n == 0) {
  270. PyErr_Format(PyExc_RuntimeError,
  271. "Failed to read %zi bytes from /dev/urandom",
  272. size);
  273. return -1;
  274. }
  275. buffer += n;
  276. size -= n;
  277. } while (0 < size);
  278. return 0;
  279. }
  280. static void
  281. dev_urandom_close(void)
  282. {
  283. if (urandom_cache.fd >= 0) {
  284. close(urandom_cache.fd);
  285. urandom_cache.fd = -1;
  286. }
  287. }
  288. #endif
  289. /* Fill buffer with pseudo-random bytes generated by a linear congruent
  290. generator (LCG):
  291. x(n+1) = (x(n) * 214013 + 2531011) % 2^32
  292. Use bits 23..16 of x(n) to generate a byte. */
  293. static void
  294. lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
  295. {
  296. size_t index;
  297. unsigned int x;
  298. x = x0;
  299. for (index=0; index < size; index++) {
  300. x *= 214013;
  301. x += 2531011;
  302. /* modulo 2 ^ (8 * sizeof(int)) */
  303. buffer[index] = (x >> 16) & 0xff;
  304. }
  305. }
  306. /* Fill buffer with size pseudo-random bytes from the operating system random
  307. number generator (RNG). It is suitable for most cryptographic purposes
  308. except long living private keys for asymmetric encryption.
  309. Return 0 on success, raise an exception and return -1 on error. */
  310. int
  311. _PyOS_URandom(void *buffer, Py_ssize_t size)
  312. {
  313. if (size < 0) {
  314. PyErr_Format(PyExc_ValueError,
  315. "negative argument not allowed");
  316. return -1;
  317. }
  318. if (size == 0)
  319. return 0;
  320. #ifdef MS_WINDOWS
  321. return win32_urandom((unsigned char *)buffer, size, 1);
  322. #elif defined(PY_GETENTROPY)
  323. return py_getentropy(buffer, size, 0);
  324. #else
  325. return dev_urandom_python((char*)buffer, size);
  326. #endif
  327. }
  328. void
  329. _PyRandom_Init(void)
  330. {
  331. char *env;
  332. unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc;
  333. Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
  334. assert(secret_size == sizeof(_Py_HashSecret.uc));
  335. if (_Py_HashSecret_Initialized)
  336. return;
  337. _Py_HashSecret_Initialized = 1;
  338. /*
  339. Hash randomization is enabled. Generate a per-process secret,
  340. using PYTHONHASHSEED if provided.
  341. */
  342. env = Py_GETENV("PYTHONHASHSEED");
  343. if (env && *env != '\0' && strcmp(env, "random") != 0) {
  344. char *endptr = env;
  345. unsigned long seed;
  346. seed = strtoul(env, &endptr, 10);
  347. if (*endptr != '\0'
  348. || seed > 4294967295UL
  349. || (errno == ERANGE && seed == ULONG_MAX))
  350. {
  351. Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
  352. "in range [0; 4294967295]");
  353. }
  354. if (seed == 0) {
  355. /* disable the randomized hash */
  356. memset(secret, 0, secret_size);
  357. }
  358. else {
  359. lcg_urandom(seed, secret, secret_size);
  360. }
  361. }
  362. else {
  363. #ifdef MS_WINDOWS
  364. (void)win32_urandom(secret, secret_size, 0);
  365. #elif defined(PY_GETENTROPY)
  366. (void)py_getentropy(secret, secret_size, 1);
  367. #else
  368. dev_urandom_noraise(secret, secret_size);
  369. #endif
  370. }
  371. }
  372. void
  373. _PyRandom_Fini(void)
  374. {
  375. #ifdef MS_WINDOWS
  376. if (hCryptProv) {
  377. CryptReleaseContext(hCryptProv, 0);
  378. hCryptProv = 0;
  379. }
  380. #elif defined(PY_GETENTROPY)
  381. /* nothing to clean */
  382. #else
  383. dev_urandom_close();
  384. #endif
  385. }