]> git.pond.sub.org Git - empserver/commitdiff
Fix PRNG seeding to resist guessing
authorMarkus Armbruster <armbru@pond.sub.org>
Sat, 29 Dec 2012 14:06:29 +0000 (15:06 +0100)
committerMarkus Armbruster <armbru@pond.sub.org>
Wed, 8 May 2013 04:55:21 +0000 (06:55 +0200)
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
src/lib/gen/chance.c
src/server/main.c
src/util/fairland.c

index fc56ea02e9e8706f18354d5265b49feccf596eca..759d89c9ea6933292f32b7006009d02ea508b08a 100644 (file)
@@ -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
index 2a00789fd0b2e4a1094271fc5c52e7970736e1b5..4c38b4ae75db6d542c57dcace4c13548105d6022 100644 (file)
 
 #include <config.h>
 
+#include <fcntl.h>
 #include <math.h>
+#include <stdint.h>
 #include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
 #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;
+}
index 9cb2761ddac185fb69b5c33460a9e3c05a799402..52c4c8cc3fb2eb72494b8cf155e96979aaf9683c 100644 (file)
@@ -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)
index 078a23f035561b011a4128b02d1fe18ebf0ac17b..d95282f2c60e82ee872f093ba680e1dbe47ee670 100644 (file)
@@ -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)