diff -r 5c091acc799f Modules/_ssl.c --- a/Modules/_ssl.c Sat Aug 17 17:25:27 2013 +0200 +++ b/Modules/_ssl.c Sat Aug 17 17:58:47 2013 +0200 @@ -51,6 +51,11 @@ #include #endif +#ifdef HAVE_PTHREAD_ATFORK +#include +#include +#endif + /* Include OpenSSL header files */ #include "openssl/rsa.h" #include "openssl/crypto.h" @@ -2936,8 +2941,84 @@ 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. + */ + +#ifdef HAVE_PTHREAD_ATFORK +static void +PySSL_RAND_atfork_child(void) +{ + /* OpenSSL's PRNG processes seeds in blocks of 20 bytes. */ + struct { + char stack[64]; /* uninitialized (!) stack data, 64 because OpenSSL + * processes seed in blocks of 20 bytes. */ + pid_t pid; /* current pid */ + long time; /* current nano, micro or full seconds */ + } seed; +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; +#elif defined(HAVE_GETTIMEOFDAY) + struct timeval tv; #endif +#ifdef WITH_VALGRIND + VALGRIND_MAKE_MEM_DEFINED(seed.stack, sizeof(seed.stack)); +#endif + + seed.pid = getpid(); + seed.time = 0; +#ifdef HAVE_CLOCK_GETTIME + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) + seed.time = ts.tv_nsec; +#elif defined(HAVE_GETTIMEOFDAY) + if (gettimeofday(&tv, NULL) == 0) { + seed.time = tv.tv_usec; +#endif + if (seed.time == 0) + seed.time = time(NULL); + +#if 1 + fprintf(stderr, "PySSL_RAND_atfork_child() seeds %i bytes in %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\ \n\ @@ -3533,5 +3614,10 @@ if (r == NULL || PyModule_AddObject(m, "_OPENSSL_API_VERSION", r)) return NULL; +#if defined(HAVE_OPENSSL_RAND) && defined(HAVE_PTHREAD_ATFORK) + if (PySSL_RAND_atfork() == -1) + return NULL; +#endif + return m; } diff -r 5c091acc799f configure --- a/configure Sat Aug 17 17:25:27 2013 +0200 +++ b/configure Sat Aug 17 17:58:47 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 5c091acc799f configure.ac --- a/configure.ac Sat Aug 17 17:25:27 2013 +0200 +++ b/configure.ac Sat Aug 17 17:58:47 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 5c091acc799f pyconfig.h.in --- a/pyconfig.h.in Sat Aug 17 17:25:27 2013 +0200 +++ b/pyconfig.h.in Sat Aug 17 17:58:47 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