diff -r afb1b4797419 Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py Tue Aug 20 22:09:41 2013 +0200 +++ b/Lib/test/test_ssl.py Wed Aug 21 00:55:22 2013 +0200 @@ -143,6 +143,34 @@ self.assertRaises(TypeError, ssl.RAND_egd, 'foo', 1) ssl.RAND_add("this is a random string", 75.0) + @unittest.skipUnless(os.name == 'posix', 'requires posix') + def test_random_fork(self): + status = ssl.RAND_status() + if not status: + self.fail("OpenSSL's PRNG has insufficient randomness") + + rfd, wfd = os.pipe() + pid = os.fork() + if pid == 0: + os.close(rfd) + child_random = ssl.RAND_bytes(16) + self.assertEqual(len(child_random), 16) + os.write(wfd, child_random) + os.close(wfd) + os._exit(0) + else: + os.close(wfd) + self.addCleanup(os.close, rfd) + _, status = os.waitpid(pid, 0) + self.assertEqual(status, 0) + + child_random = os.read(rfd, 32) + self.assertEqual(len(child_random), 16) + parent_random = ssl.RAND_bytes(16) + self.assertEqual(len(parent_random), 16) + + self.assertNotEqual(child_random, parent_random) + def test_parse_cert(self): # note that this uses an 'unofficial' function in _ssl.c, # provided solely for this test, to exercise the certificate diff -r afb1b4797419 Misc/NEWS --- a/Misc/NEWS Tue Aug 20 22:09:41 2013 +0200 +++ b/Misc/NEWS Wed Aug 21 00:55:22 2013 +0200 @@ -38,11 +38,15 @@ Library ------- +- Issue #18777: The ssl module now uses the new CRYPTO_THREADID API of + OpenSSL 1.0.0+ instead of the deprecated CRYPTO id callback function. + - Issue #8865: Concurrent invocation of select.poll.poll() now raises a RuntimeError exception. Patch by Christian Schubert. -- Issue #18777: The ssl module now uses the new CRYPTO_THREADID API of - OpenSSL 1.0.0+ instead of the deprecated CRYPTO id callback function. +- Issue #18747: Re-seed OpenSSL's pseudo-random number generator after fork. + A pthread_atfork() child handler is used to seeded the PRNG with pid, time + and some stack data. - Issue #18768: Correct doc string of RAND_edg(). Patch by Vajrasky Kok. diff -r afb1b4797419 Modules/_ssl.c --- a/Modules/_ssl.c Tue Aug 20 22:09:41 2013 +0200 +++ b/Modules/_ssl.c Wed Aug 21 00:55:22 2013 +0200 @@ -18,6 +18,11 @@ #ifdef WITH_THREAD #include "pythread.h" + +#ifdef HAVE_PTHREAD_ATFORK +# include +#endif + #define PySSL_BEGIN_ALLOW_THREADS_S(save) \ do { if (_ssl_locks_count>0) { (save) = PyEval_SaveThread(); } } while (0) #define PySSL_END_ALLOW_THREADS_S(save) \ @@ -2936,7 +2941,68 @@ Returns number of bytes read. Raises SSLError if connection to EGD\n\ fails or if it does not provide enough data to seed PRNG."); +/* Seed OpenSSL's PRNG at fork(), http://bugs.python.org/issue18747 + * + * The child handler seeds the PRNG from pseudo-random data like pid, the + * current time (nanoseconds, miliseconds or seconds) and an uninitialized + * array. The array contains stack variables that are impossible to predict + * on most systems, e.g. function return address (subject to ASLR), the + * stack protection canary and automatic variables. + * The code is inspired by Apache's ssl_rand_seed() function. + * + * Note: + * The code uses pthread_atfork() until Python has a proper atfork API. The + * handlers are not removed from the child process. + */ + +#if defined(HAVE_PTHREAD_ATFORK) && defined(WITH_THREAD) +#define PYSSL_RAND_ATFORK 1 + +static void +PySSL_RAND_atfork_child(void) +{ + struct { + char stack[72]; /* uninitialized (!) stack data */ + pid_t pid; /* current pid */ + _PyTime_timeval tp; /* current time */ + } seed; + +#ifdef WITH_VALGRIND + VALGRIND_MAKE_MEM_DEFINED(seed.stack, sizeof(seed.stack)); #endif + seed.pid = getpid(); + _PyTime_gettimeofday(&(seed.tp)); + +#if 0 + fprintf(stderr, "PySSL_RAND_atfork_child() seeds %i bytes in pid %i\n", + (int)sizeof(seed), seed.pid); +#endif + RAND_add((unsigned char *)&seed, sizeof(seed), 0.0); +} + +static int +PySSL_RAND_atfork(void) +{ + static int registered = 0; + int retval; + + if (registered) + return 0; + + retval = pthread_atfork(NULL, /* prepare */ + NULL, /* parent */ + PySSL_RAND_atfork_child); /* child */ + if (retval != 0) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + registered = 1; + return 0; +} +#endif /* HAVE_PTHREAD_ATFORK */ + +#endif /* HAVE_OPENSSL_RAND */ + PyDoc_STRVAR(PySSL_get_default_verify_paths_doc, "get_default_verify_paths() -> tuple\n\ @@ -3549,5 +3615,10 @@ if (r == NULL || PyModule_AddObject(m, "_OPENSSL_API_VERSION", r)) return NULL; +#ifdef PYSSL_RAND_ATFORK + if (PySSL_RAND_atfork() == -1) + return NULL; +#endif + return m; } diff -r afb1b4797419 configure --- a/configure Tue Aug 20 22:09:41 2013 +0200 +++ b/configure Wed Aug 21 00:55:22 2013 +0200 @@ -9809,6 +9809,17 @@ fi done + for ac_func in pthread_atfork +do : + ac_fn_c_check_func "$LINENO" "pthread_atfork" "ac_cv_func_pthread_atfork" +if test "x$ac_cv_func_pthread_atfork" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PTHREAD_ATFORK 1 +_ACEOF + +fi +done + fi diff -r afb1b4797419 configure.ac --- a/configure.ac Tue Aug 20 22:09:41 2013 +0200 +++ b/configure.ac Wed Aug 21 00:55:22 2013 +0200 @@ -2512,6 +2512,7 @@ [Define if pthread_sigmask() does not work on your system.]) ;; esac]) + AC_CHECK_FUNCS(pthread_atfork) fi diff -r afb1b4797419 pyconfig.h.in --- a/pyconfig.h.in Tue Aug 20 22:09:41 2013 +0200 +++ b/pyconfig.h.in Wed Aug 21 00:55:22 2013 +0200 @@ -633,6 +633,9 @@ /* Define if your compiler supports function prototype */ #undef HAVE_PROTOTYPES +/* Define to 1 if you have the `pthread_atfork' function. */ +#undef HAVE_PTHREAD_ATFORK + /* Defined for Solaris 2.6 bug in pthread header. */ #undef HAVE_PTHREAD_DESTRUCTOR