Created
August 21, 2013 06:33
-
-
Save ppelleti/6290984 to your computer and use it in GitHub Desktop.
This is a simple program which demonstrates that OpenSSL's default random number generator is not fork-safe if the pids wrap.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Patrick Pelletier, public domain / cc0 | |
* | |
* OpenSSL's default random number generator (RAND_SSLeay) is not | |
* fork-safe in the case where the child pids wrap around without the | |
* parent having used or reseeded the RNG in the interim. Which makes | |
* this a rare problem, but one which recently cropped up on Android: | |
* https://plus.google.com/+AndroidDevelopers/posts/YxWzeNQMJS2 | |
* and Nikolay Elenkov wrote a program to demonstrate the bug on Android: | |
* https://gist.github.com/nelenkov/581f9be65dcc0b6b35b9 | |
* | |
* The program below demonstrates the same thing, but in plain C, | |
* using only OpenSSL, so it can be run on Linux or another POSIX-like | |
* operating system. | |
* | |
* compile me like this: | |
* gcc -Wall -O3 rand-trouble.c -o rand-trouble -lcrypto -pthread | |
* run me like this: | |
* ./rand-trouble > randies.txt | |
* and then view the results like this: | |
* sort randies.txt | uniq -c | sort | less | |
*/ | |
#include <openssl/rand.h> | |
#include <openssl/engine.h> | |
#include <openssl/conf.h> | |
#include <openssl/err.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <pthread.h> | |
typedef void (*voidfunc) (void); | |
static void print_random (void) | |
{ | |
unsigned char buf[16]; | |
RAND_bytes (buf, sizeof (buf)); | |
size_t i; | |
for (i = 0 ; i < sizeof (buf) ; i++) | |
printf ("%02x", buf[i]); | |
printf (" (pid %u)\n", getpid ()); | |
/* this is important, or we get duplicated stdio across fork() */ | |
fflush (stdout); | |
} | |
int main (int argc, char **argv) | |
{ | |
/* Load the human readable error strings for libcrypto */ | |
ERR_load_crypto_strings(); | |
/* Load all digest and cipher algorithms */ | |
OpenSSL_add_all_algorithms(); | |
/* Load config file, and other important initialisation and/or | |
* initialization. In particular, this is what, very indirectly, | |
* sets up the RdRand engine, as I describe here: | |
* http://wiki.opensslfoundation.com/index.php/Talk:Libcrypto_API#Initialization.2C_OPENSSL_conf_and_engines.3F | |
* So if you have RdRand and want to see how OpenSSL behaves without it, | |
* comment out the following line: */ | |
OPENSSL_config(NULL); | |
/* Print version of OpenSSL. */ | |
fprintf (stderr, "%s\n", SSLeay_version (SSLEAY_VERSION)); | |
/* Print whether we're using a rand ENGINE. If we're using RdRand, | |
* we don't expect any problems, but if we're using RAND_SSLeay (the | |
* default if there's no ENGINE), then we should get pid wraparound | |
* issues. */ | |
ENGINE *rnd = ENGINE_get_default_RAND (); | |
if (rnd) | |
fprintf (stderr, "rand engine: %s\n", ENGINE_get_name (rnd)); | |
else | |
fprintf (stderr, "no rand engine\n"); | |
/* Uncomment this to demonstrate a solution. We would only need | |
* to reseed the generator in either the parent or the child, but | |
* for the sake of overkill, we do both. */ | |
// pthread_atfork (NULL, (voidfunc) RAND_poll, (voidfunc) RAND_poll); | |
/* This is important, because we first use RAND_bytes (and thus | |
* provoke initialization) in the parent, which is what leads to | |
* the problem. If you comment this out, each child will seed | |
* itself from /dev/urandom, and there will be no duplicates. */ | |
print_random (); | |
int i; | |
for (i = 0 ; i < 65536 ; i++) | |
{ | |
pid_t pid = fork (); | |
if (pid < 0) | |
{ | |
perror ("fork"); | |
exit (EXIT_FAILURE); | |
} | |
else if (pid == 0) | |
{ | |
print_random (); | |
exit (EXIT_SUCCESS); | |
} | |
else | |
{ | |
int status; | |
if (waitpid (pid, &status, 0) < 0) | |
{ | |
perror ("waitpid"); | |
exit (EXIT_FAILURE); | |
} | |
} | |
} | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment