/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include #include "errf.h" #include "util.h" #include "config.h" #include "alg-util.h" #include "rnd.h" #define POOL_SIZE 512 #define POOL_RESERVE 128 extern const char *__progname; static const char *rnddev = "/dev/urandom"; #if POOL_SIZE % 16 #error "Code needs a rethink if POOL_SIZE isn't a multiple of MD5 hash size" #endif static int rndfd = -1; static int poolfd = -1; static unsigned char pool[POOL_SIZE]; static int poolhand; static int stirs = 0; static void stir_pool(void) { int o; void *h; unsigned char res[16]; if (rndfd >= 0) { read(rndfd,&res[0],1); pool[0] ^= res[0]; } for (o=0;o0;len--) { pool[poolhand++] ^= *dp++; if (poolhand >= POOL_SIZE) stir_pool(); } } static void mixin_command_output(const char *cmd) { int p[2]; pid_t kid; char obuf[512]; int n; if (pipe(p) < 0) { logmsg(LM_ERR,"pipe: %s",strerror(errno)); exit(1); } fflush(0); kid = moussh_fork(); if (kid < 0) { logmsg(LM_ERR,"fork: %s",strerror(errno)); exit(1); } if (kid == 0) { close(p[0]); if (p[1] != 1) { dup2(p[1],1); close(p[1]); } dup2(1,2); dup2(1,0); execl("/bin/sh","sh","-c",cmd,(char *)0); _exit(1); } close(p[1]); while (1) { n = read(p[0],&obuf[0],sizeof(obuf)); if (n < 0) { if (errno == EINTR) continue; logmsg(LM_ERR,"randomness read: %s",strerror(errno)); exit(1); } if (n == 0) break; mixin_data(&obuf[0],n); } close(p[0]); wait4(kid,0,0,0); } static void new_pool(void) { if (rndfd < 0) { struct timeval now; mixin_command_output("/bin/ps awwlx"); mixin_command_output("/bin/ls -la / /tmp"); gettimeofday(&now,0); mixin_data(&now,sizeof(struct timeval)); } else { read(rndfd,&pool[0],POOL_SIZE); } stir_pool(); } static void load_pool(void) { int n; n = pread(poolfd,&pool[0],POOL_SIZE,0); if (n != POOL_SIZE) { new_pool(); } else { if (rndfd >= 0) { char rbuf[4]; read(rndfd,&rbuf[0],4); mixin_data(&rbuf[0],4); } stir_pool(); } } static void save_pool(void) { char new[POOL_SIZE]; int o; void *h; for (o=0;o 512 #error "save_pool() needs work for POOL_SIZE > 512" #endif md5_process_bytes(h,"TheMagicWordInfibulatedGonkulator",(o>>4)+1); md5_process_bytes(h,&o,sizeof(int)); md5_result(h,&new[o]); } pwrite(poolfd,&new[0],POOL_SIZE,0); } static void init(void) { char *t; const char *path; if (rndfd != -1) return; rndfd = open(rnddev,O_RDONLY,0); if (rndfd < 0) rndfd = -2; else fcntl(rndfd,F_SETFD,1); bzero(&pool[0],POOL_SIZE); poolhand = 0; path = config_str("random-pool"); if (path) { t = 0; } else { asprintf(&t,"%s/random-pool",get_sshdir("random pool file")); path = t; } while (1) { poolfd = open(path,O_RDWR,0600); if (poolfd < 0) { if (errno == ENOENT) { poolfd = open(path,O_RDWR|O_CREAT|O_EXCL|O_EXLOCK,0600); if (poolfd < 0) { switch (errno) { case EEXIST: continue; break; default: logmsg(LM_ERR,"can't init random pool: can't create %s: %s",t,strerror(errno)); exit(1); break; } } new_pool(); break; } logmsg(LM_ERR,"can't init random pool: can't open %s: %s",t,strerror(errno)); exit(1); } fcntl(poolfd,F_SETFD,1); flock(poolfd,LOCK_EX); load_pool(); break; } save_pool(); stir_pool(); flock(poolfd,LOCK_UN); free(t); } void random_data(void *buf, int len) { unsigned char *bp; init(); bp = buf; for (;len>0;len--) { *bp++ = pool[poolhand++]; if (poolhand >= POOL_SIZE) stir_pool(); } }