1 /*
   2  * Copyright (c) 2001 Damien Miller.  All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  * 1. Redistributions of source code must retain the above copyright
   8  *    notice, this list of conditions and the following disclaimer.
   9  * 2. Redistributions in binary form must reproduce the above copyright
  10  *    notice, this list of conditions and the following disclaimer in the
  11  *    documentation and/or other materials provided with the distribution.
  12  *
  13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23  */
  24 
  25 #include "includes.h"
  26 
  27 #include <openssl/rand.h>
  28 #include <openssl/crypto.h>
  29 
  30 #include "ssh.h"
  31 #include "misc.h"
  32 #include "xmalloc.h"
  33 #include "atomicio.h"
  34 #include "pathnames.h"
  35 #include "log.h"
  36 
  37 /*
  38  * Portable OpenSSH PRNG seeding:
  39  * If OpenSSL has not "internally seeded" itself (e.g. pulled data from 
  40  * /dev/random), then we execute a "ssh-rand-helper" program which 
  41  * collects entropy and writes it to stdout. The child program must 
  42  * write at least RANDOM_SEED_SIZE bytes. The child is run with stderr
  43  * attached, so error/debugging output should be visible.
  44  *
  45  * XXX: we should tell the child how many bytes we need.
  46  */
  47 
  48 RCSID("$Id: entropy.c,v 1.44 2002/06/09 19:41:48 mouring Exp $");
  49 
  50 #pragma ident   "%Z%%M% %I%     %E% SMI"
  51 
  52 #ifndef OPENSSL_PRNG_ONLY
  53 #define RANDOM_SEED_SIZE 48
  54 static uid_t original_uid, original_euid;
  55 #endif
  56 
  57 void
  58 seed_rng(void)
  59 {
  60 #ifndef OPENSSL_PRNG_ONLY
  61         int devnull;
  62         int p[2];
  63         pid_t pid;
  64         int ret;
  65         unsigned char buf[RANDOM_SEED_SIZE];
  66         mysig_t old_sigchld;
  67 
  68         if (RAND_status() == 1) {
  69                 debug3("RNG is ready, skipping seeding");
  70                 return;
  71         }
  72 
  73         debug3("Seeding PRNG from %s", SSH_RAND_HELPER);
  74 
  75         if ((devnull = open("/dev/null", O_RDWR)) == -1)
  76                 fatal("Couldn't open /dev/null: %s", strerror(errno));
  77         if (pipe(p) == -1)
  78                 fatal("pipe: %s", strerror(errno));
  79 
  80         old_sigchld = mysignal(SIGCHLD, SIG_DFL);
  81         if ((pid = fork()) == -1)
  82                 fatal("Couldn't fork: %s", strerror(errno));
  83         if (pid == 0) {
  84                 dup2(devnull, STDIN_FILENO);
  85                 dup2(p[1], STDOUT_FILENO);
  86                 /* Keep stderr open for errors */
  87                 close(p[0]);
  88                 close(p[1]);
  89                 close(devnull);
  90 
  91                 if (original_uid != original_euid && 
  92                     ( seteuid(getuid()) == -1 || 
  93                       setuid(original_uid) == -1) ) {
  94                         fprintf(stderr, "(rand child) setuid(%d): %s\n", 
  95                             original_uid, strerror(errno));
  96                         _exit(1);
  97                 }
  98                 
  99                 execl(SSH_RAND_HELPER, "ssh-rand-helper", NULL);
 100                 fprintf(stderr, "(rand child) Couldn't exec '%s': %s\n", 
 101                     SSH_RAND_HELPER, strerror(errno));
 102                 _exit(1);
 103         }
 104 
 105         close(devnull);
 106         close(p[1]);
 107 
 108         memset(buf, '\0', sizeof(buf));
 109         ret = atomicio(read, p[0], buf, sizeof(buf));
 110         if (ret == -1)
 111                 fatal("Couldn't read from ssh-rand-helper: %s",
 112                     strerror(errno));
 113         if (ret != sizeof(buf))
 114                 fatal("ssh-rand-helper child produced insufficient data");
 115 
 116         close(p[0]);
 117 
 118         if (waitpid(pid, &ret, 0) == -1)
 119                fatal("Couldn't wait for ssh-rand-helper completion: %s", 
 120                    strerror(errno));
 121         mysignal(SIGCHLD, old_sigchld);
 122 
 123         /* We don't mind if the child exits upon a SIGPIPE */
 124         if (!WIFEXITED(ret) && 
 125             (!WIFSIGNALED(ret) || WTERMSIG(ret) != SIGPIPE))
 126                 fatal("ssh-rand-helper terminated abnormally");
 127         if (WEXITSTATUS(ret) != 0)
 128                 fatal("ssh-rand-helper exit with exit status %d", ret);
 129 
 130         RAND_add(buf, sizeof(buf), sizeof(buf));
 131         memset(buf, '\0', sizeof(buf));
 132 
 133 #endif /* OPENSSL_PRNG_ONLY */
 134         if (RAND_status() != 1)
 135                 fatal("PRNG is not seeded");
 136 }
 137 
 138 void
 139 init_rng(void) 
 140 {
 141         /*
 142          * OpenSSL version numbers: MNNFFPPS: major minor fix patch status
 143          * We match major, minor, fix and status (not patch)
 144          */
 145         if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L)
 146                 fatal("OpenSSL version mismatch. Built against %lx, you "
 147                     "have %lx", OPENSSL_VERSION_NUMBER, SSLeay());
 148 
 149 #ifndef OPENSSL_PRNG_ONLY
 150         if ((original_uid = getuid()) == -1)
 151                 fatal("getuid: %s", strerror(errno));
 152         if ((original_euid = geteuid()) == -1)
 153                 fatal("geteuid: %s", strerror(errno));
 154 #endif
 155 }
 156