]> andersk Git - openssh.git/blobdiff - entropy.c
- (djm) Fix pam sprintf fix
[openssh.git] / entropy.c
index cecf9e8d406e1d55d500ec993bcd8d7255580d83..84b1dd1d951d2fd908839db00e0e96c3855153d3 100644 (file)
--- a/entropy.c
+++ b/entropy.c
@@ -55,68 +55,94 @@ RCSID("$Id$");
 
 #define WHITESPACE " \t\n"
 
+#ifndef RUSAGE_SELF
+# define RUSAGE_SELF 0
+#endif
+#ifndef RUSAGE_CHILDREN
+# define RUSAGE_CHILDREN 0
+#endif
+
 #if defined(EGD_SOCKET) || defined(RANDOM_POOL)
 
 #ifdef EGD_SOCKET
 /* Collect entropy from EGD */
-void get_random_bytes(unsigned char *buf, int len)
+int get_random_bytes(unsigned char *buf, int len)
 {
-       static int egd_socket = -1;
-       int c;
-       char egd_message[2] = { 0x02, 0x00 };
+       int fd;
+       char msg[2];
        struct sockaddr_un addr;
        int addr_len;
 
-       memset(&addr, '\0', sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       
-       /* FIXME: compile time check? */
+       /* Sanity checks */
        if (sizeof(EGD_SOCKET) > sizeof(addr.sun_path))
                fatal("Random pool path is too long");
-       
+       if (len > 255)
+               fatal("Too many bytes to read from EGD");
+
+       memset(&addr, '\0', sizeof(addr));
+       addr.sun_family = AF_UNIX;
        strlcpy(addr.sun_path, EGD_SOCKET, sizeof(addr.sun_path));
-       
        addr_len = offsetof(struct sockaddr_un, sun_path) + sizeof(EGD_SOCKET);
        
-       if (egd_socket == -1) {
-               egd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
-               if (egd_socket == -1)
-                       fatal("Couldn't create AF_UNIX socket: %s", strerror(errno));
-               if (connect(egd_socket, (struct sockaddr*)&addr, addr_len) == -1)
-                       fatal("Couldn't connect to EGD socket \"%s\": %s", addr.sun_path, strerror(errno));
-       }       
+       fd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (fd == -1) {
+               error("Couldn't create AF_UNIX socket: %s", strerror(errno));
+               return(0);
+       }
+
+       if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) {
+               error("Couldn't connect to EGD socket \"%s\": %s", 
+                       addr.sun_path, strerror(errno));
+               close(fd);
+               return(0);
+       }
 
-       if (len > 255)
-               fatal("Too many bytes to read from EGD");
-       
        /* Send blocking read request to EGD */
-       egd_message[1] = len;
+       msg[0] = 0x02;
+       msg[1] = len;
 
-       c = atomicio(write, egd_socket, egd_message, sizeof(egd_message));
-       if (c == -1)
-               fatal("Couldn't write to EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
+       if (atomicio(write, fd, msg, sizeof(msg)) != sizeof(msg)) {
+               error("Couldn't write to EGD socket \"%s\": %s", 
+                       EGD_SOCKET, strerror(errno));
+               close(fd);
+               return(0);
+       }
 
-       c = atomicio(read, egd_socket, buf, len);
-       if (c <= 0)
-               fatal("Couldn't read from EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
+       if (atomicio(read, fd, buf, len) != len) {
+               error("Couldn't read from EGD socket \"%s\": %s", 
+                       EGD_SOCKET, strerror(errno));
+               close(fd);
+               return(0);
+       }
+       
+       close(fd);
+       
+       return(1);
 }
 #else /* !EGD_SOCKET */
 #ifdef RANDOM_POOL
 /* Collect entropy from /dev/urandom or pipe */
-void get_random_bytes(unsigned char *buf, int len)
+int get_random_bytes(unsigned char *buf, int len)
 {
-       static int random_pool = -1;
-       int c;
+       int random_pool;
 
+       random_pool = open(RANDOM_POOL, O_RDONLY);
        if (random_pool == -1) {
-               random_pool = open(RANDOM_POOL, O_RDONLY);
-               if (random_pool == -1)
-                       fatal("Couldn't open random pool \"%s\": %s", RANDOM_POOL, strerror(errno));
+               error("Couldn't open random pool \"%s\": %s", 
+                       RANDOM_POOL, strerror(errno));
+               return(0);
        }
        
-       c = atomicio(read, random_pool, buf, len);
-       if (c <= 0)
-               fatal("Couldn't read from random pool \"%s\": %s", RANDOM_POOL, strerror(errno));
+       if (atomicio(read, random_pool, buf, len) != len) {
+               error("Couldn't read from random pool \"%s\": %s", 
+                       RANDOM_POOL, strerror(errno));
+               close(random_pool);
+               return(0);
+       }
+       
+       close(random_pool);
+       
+       return(1);
 }
 #endif /* RANDOM_POOL */
 #endif /* EGD_SOCKET */
@@ -131,11 +157,20 @@ seed_rng(void)
        char buf[32];
        
        debug("Seeding random number generator");
-       get_random_bytes(buf, sizeof(buf));
-       RAND_add(buf, sizeof(buf), sizeof(buf));
+
+       if (!get_random_bytes(buf, sizeof(buf))) {
+               if (!RAND_status())
+                       fatal("Entropy collection failed and entropy exhausted");
+       } else {
+               RAND_add(buf, sizeof(buf), sizeof(buf));
+       }
+       
        memset(buf, '\0', sizeof(buf));
 }
 
+/* No-op */
+void init_rng(void) {}
+
 #else /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */
 
 /* 
@@ -148,9 +183,9 @@ seed_rng(void)
 /* static int entropy_timeout_default = ENTROPY_TIMEOUT_MSEC; */
 static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC;
 
-static int prng_seed_loaded = 0;
 static int prng_seed_saved = 0;
-static int prng_commands_loaded = 0;
+static int prng_initialised = 0;
+uid_t original_uid;
 
 typedef struct
 {
@@ -187,11 +222,11 @@ stir_from_system(void)
        total_entropy_estimate = 0;
        
        i = getpid();
-       RAND_add(&i, sizeof(i), 0.1);
+       RAND_add(&i, sizeof(i), 0.5);
        total_entropy_estimate += 0.1;
        
        i = getppid();
-       RAND_add(&i, sizeof(i), 0.1);
+       RAND_add(&i, sizeof(i), 0.5);
        total_entropy_estimate += 0.1;
 
        i = getuid();
@@ -200,7 +235,7 @@ stir_from_system(void)
        RAND_add(&i, sizeof(i), 0.0);
 
        total_entropy_estimate += stir_gettimeofday(1.0);
-       total_entropy_estimate += stir_clock(0.2);
+       total_entropy_estimate += stir_clock(0.5);
        total_entropy_estimate += stir_rusage(RUSAGE_SELF, 2.0);
 
        return(total_entropy_estimate);
@@ -301,9 +336,9 @@ stir_rusage(int who, double entropy_estimate)
        struct rusage ru;
        
        if (getrusage(who, &ru) == -1)
-               fatal("Couldn't getrusage: %s", strerror(errno));
+               return(0);
 
-       RAND_add(&ru, sizeof(ru), 0.1);
+       RAND_add(&ru, sizeof(ru), entropy_estimate);
 
        return(entropy_estimate);
 #else /* _HAVE_GETRUSAGE */
@@ -363,10 +398,10 @@ hash_output_from_command(entropy_source_t *src, char *hash)
                        close(p[1]);
                        close(devnull);
 
+                       setuid(original_uid);
                        execv(src->path, (char**)(src->args));
                        debug("(child) Couldn't exec '%s': %s", src->cmdstring,
                              strerror(errno));
-                       src->badness = src->sticky_badness = 128;
                        _exit(-1);
                default: /* Parent */
                        break;
@@ -400,38 +435,36 @@ hash_output_from_command(entropy_source_t *src, char *hash)
 
                ret = select(p[0]+1, &rdset, NULL, NULL, &tv);
 
+               RAND_add(&tv, sizeof(tv), 0.0);
+
                switch (ret) {
                case 0:
                        /* timer expired */
                        error_abort = 1;
                        break;
-                       
                case 1:
                        /* command input */
                        bytes_read = read(p[0], buf, sizeof(buf));
+                       RAND_add(&bytes_read, sizeof(&bytes_read), 0.0);
                        if (bytes_read == -1) {
                                error_abort = 1;
                                break;
-                       }
-                       if (bytes_read) {
+                       } else if (bytes_read) {
                                SHA1_Update(&sha, buf, bytes_read);
                                total_bytes_read += bytes_read;
-                               RAND_add(&bytes_read, sizeof(&bytes_read), 0.0);
-                       } else
+                       } else {
                                cmd_eof = 1;
-
+                       }
                        break;
-
                case -1:
                default:
+                       /* error */
                        debug("Command '%s': select() failed: %s", src->cmdstring,
                              strerror(errno));
                        error_abort = 1;
                        break;
-               } /* switch ret */
-
-               RAND_add(&tv, sizeof(&tv), 0.0);
-       } /* while !error_abort && !cmd_eof */
+               }
+       }
 
        SHA1_Final(hash, &sha);
 
@@ -501,7 +534,7 @@ prng_check_seedfile(char *filename) {
                fatal("PRNG seedfile %.100s is not a regular file", filename);
 
        /* mode 0600, owned by root or the current user? */
-       if (((st.st_mode & 0177) != 0) || !(st.st_uid == getuid()))
+       if (((st.st_mode & 0177) != 0) || !(st.st_uid == original_uid))
                fatal("PRNG seedfile %.100s must be mode 0600, owned by uid %d",
                         filename, getuid());
 
@@ -519,12 +552,14 @@ prng_write_seedfile(void) {
        if (prng_seed_saved)
                return;
        
+       setuid(original_uid);
+       
        prng_seed_saved = 1;
        
-       pw = getpwuid(getuid());
+       pw = getpwuid(original_uid);
        if (pw == NULL)
                fatal("Couldn't get password entry for current user (%i): %s", 
-                       getuid(), strerror(errno));
+                       original_uid, strerror(errno));
                                
        /* Try to ensure that the parent directory is there */
        snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, 
@@ -559,10 +594,10 @@ prng_read_seedfile(void) {
        char filename[1024];
        struct passwd *pw;
        
-       pw = getpwuid(getuid());
+       pw = getpwuid(original_uid);
        if (pw == NULL)
                fatal("Couldn't get password entry for current user (%i): %s", 
-                       getuid(), strerror(errno));
+                       original_uid, strerror(errno));
                        
        snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, 
                SSH_PRNG_SEED_FILE);
@@ -723,7 +758,7 @@ prng_read_commands(char *cmdfilename)
        /* trim to size */
        entropy_sources = xrealloc(entcmd, (cur_cmd+1) * sizeof(entropy_source_t));
 
-       debug("loaded %d entropy commands from %.100s", cur_cmd, cmdfilename);
+       debug("Loaded %d entropy commands from %.100s", cur_cmd, cmdfilename);
 
        return (cur_cmd >= MIN_ENTROPY_SOURCES);
 }
@@ -745,35 +780,41 @@ void
 seed_rng(void)
 {
        void *old_sigchld_handler;
-       
-       if (!prng_commands_loaded) {
-               if (!prng_read_commands(SSH_PRNG_COMMAND_FILE))
-                       fatal("PRNG initialisation failed -- exiting.");
-               prng_commands_loaded = 1;
-       }
 
+       if (!prng_initialised)
+               fatal("RNG not initialised");
+       
        /* Make sure some other sigchld handler doesn't reap our entropy */
        /* commands */
        old_sigchld_handler = signal(SIGCHLD, SIG_DFL);
 
-       debug("Seeding random number generator.");
-       debug("OpenSSL random status is now %i\n", RAND_status());
-       debug("%i bytes from system calls", (int)stir_from_system());
-       debug("%i bytes from programs", (int)stir_from_programs());
-       debug("OpenSSL random status is now %i\n", RAND_status());
+       debug("Seeded RNG with %i bytes from programs", (int)stir_from_programs());
+       debug("Seeded RNG with %i bytes from system calls", (int)stir_from_system());
+
+       if (!RAND_status())
+               fatal("Not enough entropy in RNG");
 
        signal(SIGCHLD, old_sigchld_handler);
 
        if (!RAND_status())
                fatal("Couldn't initialise builtin random number generator -- exiting.");
+}
 
-       if (!prng_seed_loaded)
-       {
-               prng_seed_loaded = 1;
-               prng_seed_saved = 0;            
-               prng_read_seedfile();
-               fatal_add_cleanup(prng_seed_cleanup, NULL);
-               atexit(prng_write_seedfile);
-       }
+void init_rng(void) 
+{
+       original_uid = getuid();
+
+       /* Read in collection commands */
+       if (!prng_read_commands(SSH_PRNG_COMMAND_FILE))
+               fatal("PRNG initialisation failed -- exiting.");
+
+       /* Set ourselves up to save a seed upon exit */
+       prng_seed_saved = 0;            
+       prng_read_seedfile();
+       fatal_add_cleanup(prng_seed_cleanup, NULL);
+       atexit(prng_write_seedfile);
+
+       prng_initialised = 1;
 }
+
 #endif /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */
This page took 0.046892 seconds and 4 git commands to generate.