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.
This commit is contained in:
parent
39c26f4238
commit
9102ecce54
4 changed files with 68 additions and 2 deletions
|
@ -39,5 +39,6 @@ extern int roll0(int);
|
||||||
extern int roll(int);
|
extern int roll(int);
|
||||||
extern int roundavg(double);
|
extern int roundavg(double);
|
||||||
extern void seed_prng(unsigned);
|
extern void seed_prng(unsigned);
|
||||||
|
extern unsigned pick_seed(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,8 +32,12 @@
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include "chance.h"
|
#include "chance.h"
|
||||||
#include "mt19937ar.h"
|
#include "mt19937ar.h"
|
||||||
|
|
||||||
|
@ -115,3 +119,56 @@ seed_prng(unsigned seed)
|
||||||
{
|
{
|
||||||
init_genrand(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;
|
||||||
|
}
|
||||||
|
|
|
@ -143,7 +143,8 @@ main(int argc, char **argv)
|
||||||
char *config_file = NULL;
|
char *config_file = NULL;
|
||||||
int force_bad_state = 0;
|
int force_bad_state = 0;
|
||||||
int op, idx, sig;
|
int op, idx, sig;
|
||||||
unsigned seed = time(NULL);
|
unsigned seed = 0;
|
||||||
|
int seed_set = 0;
|
||||||
|
|
||||||
oops_handler = ignore;
|
oops_handler = ignore;
|
||||||
|
|
||||||
|
@ -194,6 +195,7 @@ main(int argc, char **argv)
|
||||||
break;
|
break;
|
||||||
case 'R':
|
case 'R':
|
||||||
seed = strtoul(optarg, NULL, 10);
|
seed = strtoul(optarg, NULL, 10);
|
||||||
|
seed_set = 1;
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
printf("%s\n\n%s", version, legal);
|
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);
|
return install_service(program_name, service_name, config_file);
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
if (!seed_set)
|
||||||
|
seed = pick_seed();
|
||||||
init_server(seed, force_bad_state);
|
init_server(seed, force_bad_state);
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
|
@ -179,9 +179,10 @@ main(int argc, char *argv[])
|
||||||
int opt;
|
int opt;
|
||||||
char *config_file = NULL;
|
char *config_file = NULL;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
int seed_set = 0;
|
||||||
|
|
||||||
program_name = argv[0];
|
program_name = argv[0];
|
||||||
rnd_seed = time(NULL);
|
rnd_seed = 0;
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv, "ae:hioqR:s:v")) != EOF) {
|
while ((opt = getopt(argc, argv, "ae:hioqR:s:v")) != EOF) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
|
@ -202,6 +203,7 @@ main(int argc, char *argv[])
|
||||||
break;
|
break;
|
||||||
case 'R':
|
case 'R':
|
||||||
rnd_seed = strtoul(optarg, NULL, 10);
|
rnd_seed = strtoul(optarg, NULL, 10);
|
||||||
|
seed_set = 1;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
outfile = optarg;
|
outfile = optarg;
|
||||||
|
@ -219,6 +221,8 @@ main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
parse_args(argc - optind, argv + optind);
|
parse_args(argc - optind, argv + optind);
|
||||||
|
|
||||||
|
if (!seed_set)
|
||||||
|
rnd_seed = pick_seed();
|
||||||
seed_prng(rnd_seed);
|
seed_prng(rnd_seed);
|
||||||
empfile_init();
|
empfile_init();
|
||||||
if (emp_config(config_file) < 0)
|
if (emp_config(config_file) < 0)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue