]> andersk Git - openssh.git/blobdiff - entropy.c
- djm@cvs.openbsd.org 2010/01/30 02:54:53
[openssh.git] / entropy.c
index 2a632502850550e4582e671462fec6bde1e36392..8b705397ff9a1870b743583b69913808fc92b3b9 100644 (file)
--- a/entropy.c
+++ b/entropy.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000 Damien Miller.  All rights reserved.
+ * Copyright (c) 2001 Damien Miller.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -9,11 +9,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *      This product includes software developed by Markus Friedl.
- * 4. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
 #include "includes.h"
 
-#include "ssh.h"
-#include "xmalloc.h"
+#include <sys/types.h>
+#include <sys/wait.h>
 
-#ifdef HAVE_OPENSSL
-# include <openssl/rand.h>
-# include <openssl/sha.h>
-#endif
-#ifdef HAVE_SSL
-# include <ssl/rand.h>
-# include <ssl/sha.h>
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
 #endif
 
-RCSID("$Id$");
-
-#ifdef EGD_SOCKET
-#ifndef offsetof
-# define offsetof(type, member) ((size_t) &((type *)0)->member)
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
 #endif
-/* Collect entropy from EGD */
-void get_random_bytes(unsigned char *buf, int len)
-{
-       static int egd_socket = -1;
-       int c;
-       char egd_message[2] = { 0x02, 0x00 };
-       struct sockaddr_un addr;
-       int addr_len;
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
 
-       memset(&addr, '\0', sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       
-       /* FIXME: compile time check? */
-       if (sizeof(EGD_SOCKET) > sizeof(addr.sun_path))
-               fatal("Random pool path is too long");
-       
-       strcpy(addr.sun_path, EGD_SOCKET);
-       
-       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));
-       }       
+#include <openssl/rand.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
 
-       if (len > 255)
-               fatal("Too many bytes to read from EGD");
-       
-       /* Send blocking read request to EGD */
-       egd_message[1] = len;
+#include "ssh.h"
+#include "misc.h"
+#include "xmalloc.h"
+#include "atomicio.h"
+#include "pathnames.h"
+#include "log.h"
+#include "buffer.h"
 
-       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));
+/*
+ * Portable OpenSSH PRNG seeding:
+ * If OpenSSL has not "internally seeded" itself (e.g. pulled data from
+ * /dev/random), then we execute a "ssh-rand-helper" program which
+ * collects entropy and writes it to stdout. The child program must
+ * write at least RANDOM_SEED_SIZE bytes. The child is run with stderr
+ * attached, so error/debugging output should be visible.
+ *
+ * XXX: we should tell the child how many bytes we need.
+ */
 
-       c = atomicio(read, egd_socket, buf, len);
-       if (c <= 0)
-               fatal("Couldn't read from EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
-       
-       close(EGD_SOCKET);
-}
-#else /* !EGD_SOCKET */
-#ifdef RANDOM_POOL
-/* Collect entropy from /dev/urandom or pipe */
-void get_random_bytes(unsigned char *buf, int len)
+#ifndef OPENSSL_PRNG_ONLY
+#define RANDOM_SEED_SIZE 48
+static uid_t original_uid, original_euid;
+#endif
+
+void
+seed_rng(void)
 {
-       static int random_pool = -1;
-       int c;
+#ifndef OPENSSL_PRNG_ONLY
+       int devnull;
+       int p[2];
+       pid_t pid;
+       int ret;
+       unsigned char buf[RANDOM_SEED_SIZE];
+       mysig_t old_sigchld;
 
-       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));
+       if (RAND_status() == 1) {
+               debug3("RNG is ready, skipping seeding");
+               return;
        }
-       
-       c = atomicio(read, random_pool, buf, len);
-       if (c <= 0)
-               fatal("Couldn't read from random pool \"%s\": %s", RANDOM_POOL, strerror(errno));
-}
-#endif /* RANDOM_POOL */
-#endif /* EGD_SOCKET */
 
-#if !defined(EGD_SOCKET) && !defined(RANDOM_POOL)
-/* 
- * FIXME: proper entropy estimations. All current values are guesses
- * FIXME: Need timeout for slow moving programs
- * FIXME: More entropy sources
- */
+       debug3("Seeding PRNG from %s", SSH_RAND_HELPER);
 
-double stir_from_system(void);
-double stir_from_programs(void);
-double stir_gettimeofday(double entropy_estimate);
-double stir_clock(double entropy_estimate);
-double stir_rusage(int who, double entropy_estimate);
-double hash_output_from_command(const char *path, const char **args, char *hash);
+       if ((devnull = open("/dev/null", O_RDWR)) == -1)
+               fatal("Couldn't open /dev/null: %s", strerror(errno));
+       if (pipe(p) == -1)
+               fatal("pipe: %s", strerror(errno));
+
+       old_sigchld = signal(SIGCHLD, SIG_DFL);
+       if ((pid = fork()) == -1)
+               fatal("Couldn't fork: %s", strerror(errno));
+       if (pid == 0) {
+               dup2(devnull, STDIN_FILENO);
+               dup2(p[1], STDOUT_FILENO);
+               /* Keep stderr open for errors */
+               close(p[0]);
+               close(p[1]);
+               close(devnull);
+
+               if (original_uid != original_euid &&
+                   ( seteuid(getuid()) == -1 ||
+                     setuid(original_uid) == -1) ) {
+                       fprintf(stderr, "(rand child) setuid(%li): %s\n",
+                           (long int)original_uid, strerror(errno));
+                       _exit(1);
+               }
 
-typedef struct
-{
-       /* Proportion of data that is entropy */
-       double rate;
-       /* Path to executable */
-       const char *path;
-       /* argv to pass to executable */
-       const char *args[5];
-} entropy_source_t;
+               execl(SSH_RAND_HELPER, "ssh-rand-helper", NULL);
+               fprintf(stderr, "(rand child) Couldn't exec '%s': %s\n",
+                   SSH_RAND_HELPER, strerror(errno));
+               _exit(1);
+       }
 
-entropy_source_t entropy_sources[] = {
-#ifdef PROG_LS
-       { 0.002, PROG_LS,       { "ls", "-alni", "/var/log", NULL } },
-       { 0.002, PROG_LS,       { "ls", "-alni", "/var/adm", NULL } },
-       { 0.002, PROG_LS,       { "ls", "-alni", "/var/mail", NULL } },
-       { 0.002, PROG_LS,       { "ls", "-alni", "/var/spool/mail", NULL } },
-       { 0.002, PROG_LS,       { "ls", "-alni", "/proc", NULL } },
-       { 0.002, PROG_LS,       { "ls", "-alni", "/tmp", NULL } },
-#endif
-#ifdef PROG_NETSTAT
-       { 0.005, PROG_NETSTAT,  { "netstat","-an", NULL, NULL } },
-       { 0.010, PROG_NETSTAT,  { "netstat","-in", NULL, NULL } },
-       { 0.002, PROG_NETSTAT,  { "netstat","-rn", NULL, NULL } },
-       { 0.002, PROG_NETSTAT,  { "netstat","-s", NULL, NULL } },
-#endif
-#ifdef PROG_ARP
-       { 0.002, PROG_ARP,      { "arp","-a","-n", NULL } },
-#endif
-#ifdef PROG_IFCONFIG
-       { 0.002, PROG_IFCONFIG, { "ifconfig", "-a", NULL, NULL } },
-#endif
-#ifdef PROG_PS
-       { 0.003, PROG_PS,       { "ps", "laxww", NULL, NULL } },
-       { 0.003, PROG_PS,       { "ps", "-al", NULL, NULL } },
-       { 0.003, PROG_PS,       { "ps", "-efl", NULL, NULL } },
-#endif
-#ifdef PROG_W
-       { 0.005, PROG_W,        { "w", NULL, NULL, NULL } },
-#endif
-#ifdef PROG_WHO
-       { 0.001, PROG_WHO,      { "who","-i", NULL, NULL } },
-#endif
-#ifdef PROG_LAST
-       { 0.001, PROG_LAST,     { "last", NULL, NULL, NULL } },
-#endif
-#ifdef PROG_LASTLOG
-       { 0.001, PROG_LASTLOG,  { "lastlog", NULL, NULL, NULL } },
-#endif
-#ifdef PROG_DF
-       { 0.010, PROG_DF,       { "df", NULL, NULL, NULL } },
-       { 0.010, PROG_DF,       { "df", "-i", NULL, NULL } },
-#endif
-#ifdef PROG_VMSTAT
-       { 0.010, PROG_VMSTAT,   { "vmstat", NULL, NULL, NULL } },
-#endif
-#ifdef PROG_UPTIME
-       { 0.001, PROG_UPTIME,   { "uptime", NULL, NULL, NULL } },
-#endif
-#ifdef PROG_IPCS
-       { 0.001, PROG_IPCS,     { "-a", NULL, NULL, NULL } },
-#endif
-#ifdef PROG_TAIL
-       { 0.001, PROG_TAIL,     { "tail", "-200", "/var/log/messages", NULL, NULL } },
-       { 0.001, PROG_TAIL,     { "tail", "-200", "/var/log/syslog", NULL, NULL } },
-       { 0.001, PROG_TAIL,     { "tail", "-200", "/var/adm/messages", NULL, NULL } },
-       { 0.001, PROG_TAIL,     { "tail", "-200", "/var/adm/syslog", NULL, NULL } },
-       { 0.001, PROG_TAIL,     { "tail", "-200", "/var/log/maillog", NULL, NULL } },
-       { 0.001, PROG_TAIL,     { "tail", "-200", "/var/adm/maillog", NULL, NULL } },
-#endif
-       { 0.000, NULL,          { NULL, NULL, NULL, NULL, NULL } },
-};
+       close(devnull);
+       close(p[1]);
 
+       memset(buf, '\0', sizeof(buf));
+       ret = atomicio(read, p[0], buf, sizeof(buf));
+       if (ret == -1)
+               fatal("Couldn't read from ssh-rand-helper: %s",
+                   strerror(errno));
+       if (ret != sizeof(buf))
+               fatal("ssh-rand-helper child produced insufficient data");
 
-double 
-stir_from_system(void)
-{
-       double total_entropy_estimate;
-       long int i;
-       
-       total_entropy_estimate = 0;
-       
-       i = getpid();
-       RAND_add(&i, sizeof(i), 0.1);
-       total_entropy_estimate += 0.1;
-       
-       i = getppid();
-       RAND_add(&i, sizeof(i), 0.1);
-       total_entropy_estimate += 0.1;
+       close(p[0]);
 
-       i = getuid();
-       RAND_add(&i, sizeof(i), 0.0);
-       i = getgid();
-       RAND_add(&i, sizeof(i), 0.0);
+       if (waitpid(pid, &ret, 0) == -1)
+               fatal("Couldn't wait for ssh-rand-helper completion: %s",
+                   strerror(errno));
+       signal(SIGCHLD, old_sigchld);
 
-       total_entropy_estimate += stir_gettimeofday(1.0);
-       total_entropy_estimate += stir_clock(0.2);
-       total_entropy_estimate += stir_rusage(RUSAGE_SELF, 2.0);
+       /* We don't mind if the child exits upon a SIGPIPE */
+       if (!WIFEXITED(ret) &&
+           (!WIFSIGNALED(ret) || WTERMSIG(ret) != SIGPIPE))
+               fatal("ssh-rand-helper terminated abnormally");
+       if (WEXITSTATUS(ret) != 0)
+               fatal("ssh-rand-helper exit with exit status %d", ret);
 
-       return(total_entropy_estimate);
+       RAND_add(buf, sizeof(buf), sizeof(buf));
+       memset(buf, '\0', sizeof(buf));
+
+#endif /* OPENSSL_PRNG_ONLY */
+       if (RAND_status() != 1)
+               fatal("PRNG is not seeded");
 }
 
-double 
-stir_from_programs(void)
+void
+init_rng(void)
 {
-       int i;
-       int c;
-       double entropy_estimate;
-       double total_entropy_estimate;
-       char hash[SHA_DIGEST_LENGTH];
-
        /*
-        * Run through list of programs twice to catch differences
+        * OpenSSL version numbers: MNNFFPPS: major minor fix patch status
+        * We match major, minor, fix and status (not patch)
         */
-       total_entropy_estimate = 0;
-       for(i = 0; i < 2; i++) {
-               c = 0;
-               while (entropy_sources[c].path != NULL) {
-                       /* Hash output from command */
-                       entropy_estimate = hash_output_from_command(entropy_sources[c].path,
-                               entropy_sources[c].args, hash);
-
-                       /* Scale back entropy estimate according to command's rate */
-                       entropy_estimate *= entropy_sources[c].rate;
-                       /* Upper bound of entropy estimate is SHA_DIGEST_LENGTH */
-                       if (entropy_estimate > SHA_DIGEST_LENGTH)
-                               entropy_estimate = SHA_DIGEST_LENGTH;
+       if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L)
+               fatal("OpenSSL version mismatch. Built against %lx, you "
+                   "have %lx", OPENSSL_VERSION_NUMBER, SSLeay());
 
-                       /* * Scale back estimates for subsequent passes through list */
-                       entropy_estimate /= 10.0 * (i + 1.0);
-                       
-                       /* Stir it in */
-                       RAND_add(hash, sizeof(hash), entropy_estimate);
-
-/* FIXME: turn this off later */
-#if 1
-                       debug("Got %0.2f bytes of entropy from %s", entropy_estimate, 
-                               entropy_sources[c].path);
+#ifndef OPENSSL_PRNG_ONLY
+       original_uid = getuid();
+       original_euid = geteuid();
 #endif
-
-                       total_entropy_estimate += entropy_estimate;
-
-                       /* Execution times should be a little unpredictable */
-                       total_entropy_estimate += stir_gettimeofday(0.05);
-                       total_entropy_estimate += stir_clock(0.05);
-                       total_entropy_estimate += stir_rusage(RUSAGE_SELF, 0.1);
-                       total_entropy_estimate += stir_rusage(RUSAGE_CHILDREN, 0.1);
-                       
-                       c++;
-               }
-       }
-       
-       return(total_entropy_estimate);
-}
-
-double
-stir_gettimeofday(double entropy_estimate)
-{
-       struct timeval tv;
-       
-       if (gettimeofday(&tv, NULL) == -1)
-               fatal("Couldn't gettimeofday: %s", strerror(errno));
-
-       RAND_add(&tv, sizeof(tv), entropy_estimate);
-       
-       return(entropy_estimate);
-}
-
-double
-stir_clock(double entropy_estimate)
-{
-#ifdef HAVE_CLOCK
-       clock_t c;
-       
-       c = clock();
-       RAND_add(&c, sizeof(c), entropy_estimate);
-       
-       return(entropy_estimate);
-#else /* _HAVE_CLOCK */
-       return(0);
-#endif /* _HAVE_CLOCK */
 }
 
-double
-stir_rusage(int who, double entropy_estimate)
+#ifndef OPENSSL_PRNG_ONLY
+void
+rexec_send_rng_seed(Buffer *m)
 {
-#ifdef HAVE_GETRUSAGE
-       struct rusage ru;
-       
-   if (getrusage(who, &ru) == -1)
-               fatal("Couldn't getrusage: %s", strerror(errno));
-
-       RAND_add(&ru, sizeof(ru), 0.1);
-
-       return(entropy_estimate);
-#else /* _HAVE_GETRUSAGE */
-       return(0);
-#endif /* _HAVE_GETRUSAGE */
+       u_char buf[RANDOM_SEED_SIZE];
+
+       if (RAND_bytes(buf, sizeof(buf)) <= 0) {
+               error("Couldn't obtain random bytes (error %ld)",
+                   ERR_get_error());
+               buffer_put_string(m, "", 0);
+       } else 
+               buffer_put_string(m, buf, sizeof(buf));
 }
 
-double
-hash_output_from_command(const char *path, const char **args, char *hash)
+void
+rexec_recv_rng_seed(Buffer *m)
 {
-       static int devnull = -1;
-       int p[2];
-       pid_t pid;
-       int status;
-       char buf[2048];
-       int bytes_read;
-       int total_bytes_read;
-       SHA_CTX sha;
-       
-       if (devnull == -1) {
-               devnull = open("/dev/null", O_RDWR);
-               if (devnull == -1)
-                       fatal("Couldn't open /dev/null: %s", strerror(errno));
-       }
-       
-       if (pipe(p) == -1)
-               fatal("Couldn't open pipe: %s", strerror(errno));
-
-       switch (pid = fork()) {
-               case -1: /* Error */
-                       close(p[0]);
-                       close(p[1]);
-                       fatal("Couldn't fork: %s", strerror(errno));
-                       /* NOTREACHED */
-               case 0: /* Child */
-                       close(0);
-                       close(1);
-                       close(2);
-                       dup2(devnull, 0);
-                       dup2(p[1], 1);
-                       dup2(p[1], 2);
-                       close(p[0]);
-                       close(p[1]);
-                       close(devnull);
+       u_char *buf;
+       u_int len;
 
-                       execv(path, (char**)args);
-                       debug("(child) Couldn't exec '%s': %s", path, strerror(errno));
-                       _exit(-1);
-               default: /* Parent */
-                       break;
+       buf = buffer_get_string_ret(m, &len);
+       if (buf != NULL) {
+               debug3("rexec_recv_rng_seed: seeding rng with %u bytes", len);
+               RAND_add(buf, len, len);
        }
-
-       RAND_add(&pid, sizeof(&pid), 0.0);
-
-       close(p[1]);
-
-       /* Hash output from child */
-       SHA1_Init(&sha);
-       total_bytes_read = 0;
-       while ((bytes_read = read(p[0], buf, sizeof(buf)))      > 0) {
-               SHA1_Update(&sha, buf, bytes_read);
-               total_bytes_read += bytes_read;
-               RAND_add(&bytes_read, sizeof(&bytes_read), 0.0);
-       }
-       SHA1_Final(hash, &sha);
-
-       close(p[0]);
-       
-       if (waitpid(pid, &status, 0) == -1) {
-               error("Couldn't wait for child '%s' completion: %s", path, 
-                       strerror(errno));
-               return(-1);
-       }
-
-       RAND_add(&status, sizeof(&status), 0.0);
-
-       if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0))
-               return(0.0);
-       else
-               return(total_bytes_read);
 }
-#endif /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */
-
-#if defined(EGD_SOCKET) || defined(RANDOM_POOL)
-/*
- * Seed OpenSSL's random number pool from Kernel random number generator
- * or EGD
- */
-void
-seed_rng(void)
-{
-       char buf[32];
-       
-       debug("Seeding random number generator");
-       get_random_bytes(buf, sizeof(buf));
-       RAND_add(buf, sizeof(buf), sizeof(buf));
-       memset(buf, '\0', sizeof(buf));
-}
-#else /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */
-/*
- * Conditionally Seed OpenSSL's random number pool syscalls and program output
- */
-void
-seed_rng(void)
-{
-       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());
-}
-#endif /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */
+#endif
This page took 0.051727 seconds and 4 git commands to generate.