From 9102ecce547c357802578ef437aae466ea88dc1d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 29 Dec 2012 15:06:29 +0100 Subject: [PATCH] Fix PRNG seeding to resist guessing We seed it with value of time(). It's the traditional way, but it provides only a few bits of effective entropy when an attacker has a rough idea when the program started. Instead, seed with a kernel random number. If we can't get one, fall back to a hash of gettimeofday() and getpid(). This should happen only on old systems or Windows. Far worse than a kernel random number, but far better than using time(). Note that fairland used to seed with time() + getpid() until commit 331aac2a (v4.2.20) dropped the getpid(), claiming it didn't improve the randomness. Perhaps it didn't under Windows then, but it certainly did elsewhere, so it was a regression. --- include/chance.h | 1 + src/lib/gen/chance.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ src/server/main.c | 6 ++++- src/util/fairland.c | 6 ++++- 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/include/chance.h b/include/chance.h index fc56ea02e..759d89c9e 100644 --- a/include/chance.h +++ b/include/chance.h @@ -39,5 +39,6 @@ extern int roll0(int); extern int roll(int); extern int roundavg(double); extern void seed_prng(unsigned); +extern unsigned pick_seed(void); #endif diff --git a/src/lib/gen/chance.c b/src/lib/gen/chance.c index 2a00789fd..4c38b4ae7 100644 --- a/src/lib/gen/chance.c +++ b/src/lib/gen/chance.c @@ -32,8 +32,12 @@ #include +#include #include +#include #include +#include +#include #include "chance.h" #include "mt19937ar.h" @@ -115,3 +119,56 @@ seed_prng(unsigned seed) { init_genrand(seed); } + +static uint32_t +djb_hash(uint32_t hash, void *buf, size_t sz) +{ + unsigned char *bp; + + for (bp = buf; bp < (unsigned char *)buf + sz; bp++) + hash = hash * 33 ^ *bp; + + return hash; +} + +/* + * Pick a reasonably random seed for the pseudo-random number generator. + */ +unsigned +pick_seed(void) +{ + int fd; + uint32_t seed; + int got_seed = 0; + struct timeval tv; + pid_t pid; + + /* + * Modern systems provide random number devices, but the details + * vary. On many systems, /dev/random blocks when the kernel + * entropy pool has been depleted, while /dev/urandom doesn't. + * The former should only be used for generating long-lived + * cryptographic keys. On other systems, both devices behave + * exactly the same, or only /dev/random exists. + * + * Try /dev/urandom first, and if it can't be opened, blindly try + * /dev/random. + */ + fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK); + if (fd < 0) + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd >= 0) { + got_seed = read(fd, &seed, sizeof(seed)) == sizeof(seed); + close(fd); + } + + if (!got_seed) { + /* Kernel didn't provide, fall back to hashing time and PID */ + gettimeofday(&tv, NULL); + seed = djb_hash(5381, &tv, sizeof(tv)); + pid = getpid(); + seed = djb_hash(seed, &pid, sizeof(pid)); + } + + return seed; +} diff --git a/src/server/main.c b/src/server/main.c index 9cb2761dd..52c4c8cc3 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -143,7 +143,8 @@ main(int argc, char **argv) char *config_file = NULL; int force_bad_state = 0; int op, idx, sig; - unsigned seed = time(NULL); + unsigned seed = 0; + int seed_set = 0; oops_handler = ignore; @@ -194,6 +195,7 @@ main(int argc, char **argv) break; case 'R': seed = strtoul(optarg, NULL, 10); + seed_set = 1; break; case 'v': printf("%s\n\n%s", version, legal); @@ -254,6 +256,8 @@ main(int argc, char **argv) return install_service(program_name, service_name, config_file); #endif /* _WIN32 */ + if (!seed_set) + seed = pick_seed(); init_server(seed, force_bad_state); #if defined(_WIN32) diff --git a/src/util/fairland.c b/src/util/fairland.c index 078a23f03..d95282f2c 100644 --- a/src/util/fairland.c +++ b/src/util/fairland.c @@ -179,9 +179,10 @@ main(int argc, char *argv[]) int opt; char *config_file = NULL; int i = 0; + int seed_set = 0; program_name = argv[0]; - rnd_seed = time(NULL); + rnd_seed = 0; while ((opt = getopt(argc, argv, "ae:hioqR:s:v")) != EOF) { switch (opt) { @@ -202,6 +203,7 @@ main(int argc, char *argv[]) break; case 'R': rnd_seed = strtoul(optarg, NULL, 10); + seed_set = 1; break; case 's': outfile = optarg; @@ -219,6 +221,8 @@ main(int argc, char *argv[]) } parse_args(argc - optind, argv + optind); + if (!seed_set) + rnd_seed = pick_seed(); seed_prng(rnd_seed); empfile_init(); if (emp_config(config_file) < 0) -- 2.43.0