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/opensslconf.h>
  28 #include <openssl/rand.h>
  29 #include <openssl/crypto.h>
  30 
  31 #include "ssh.h"
  32 #include "misc.h"
  33 #include "xmalloc.h"
  34 #include "atomicio.h"
  35 #include "pathnames.h"
  36 #include "log.h"
  37 
  38 /*
  39  * Portable OpenSSH PRNG seeding:
  40  * If OpenSSL has not "internally seeded" itself (e.g. pulled data from 
  41  * /dev/random), then we execute a "ssh-rand-helper" program which 
  42  * collects entropy and writes it to stdout. The child program must 
  43  * write at least RANDOM_SEED_SIZE bytes. The child is run with stderr
  44  * attached, so error/debugging output should be visible.
  45  *
  46  * XXX: we should tell the child how many bytes we need.
  47  */
  48 
  49 RCSID("$Id: entropy.c,v 1.44 2002/06/09 19:41:48 mouring Exp $");
  50 
  51 #pragma ident   "%Z%%M% %I%     %E% SMI"
  52 
  53 #ifndef OPENSSL_PRNG_ONLY
  54 #define RANDOM_SEED_SIZE 48
  55 static uid_t original_uid, original_euid;
  56 #endif
  57 
  58 void
  59 seed_rng(void)
  60 {
  61 #ifndef OPENSSL_PRNG_ONLY
  62         int devnull;
  63         int p[2];
  64         pid_t pid;
  65         int ret;
  66         unsigned char buf[RANDOM_SEED_SIZE];
  67         mysig_t old_sigchld;
  68 
  69         if (RAND_status() == 1) {
  70                 debug3("RNG is ready, skipping seeding");
  71                 return;
  72         }
  73 
  74         debug3("Seeding PRNG from %s", SSH_RAND_HELPER);
  75 
  76         if ((devnull = open("/dev/null", O_RDWR)) == -1)
  77                 fatal("Couldn't open /dev/null: %s", strerror(errno));
  78         if (pipe(p) == -1)
  79                 fatal("pipe: %s", strerror(errno));
  80 
  81         old_sigchld = mysignal(SIGCHLD, SIG_DFL);
  82         if ((pid = fork()) == -1)
  83                 fatal("Couldn't fork: %s", strerror(errno));
  84         if (pid == 0) {
  85                 dup2(devnull, STDIN_FILENO);
  86                 dup2(p[1], STDOUT_FILENO);
  87                 /* Keep stderr open for errors */
  88                 close(p[0]);
  89                 close(p[1]);
  90                 close(devnull);
  91 
  92                 if (original_uid != original_euid && 
  93                     ( seteuid(getuid()) == -1 || 
  94                       setuid(original_uid) == -1) ) {
  95                         fprintf(stderr, "(rand child) setuid(%d): %s\n", 
  96                             original_uid, strerror(errno));
  97                         _exit(1);
  98                 }
  99                 
 100                 execl(SSH_RAND_HELPER, "ssh-rand-helper", NULL);
 101                 fprintf(stderr, "(rand child) Couldn't exec '%s': %s\n", 
 102                     SSH_RAND_HELPER, strerror(errno));
 103                 _exit(1);
 104         }
 105 
 106         close(devnull);
 107         close(p[1]);
 108 
 109         memset(buf, '\0', sizeof(buf));
 110         ret = atomicio(read, p[0], buf, sizeof(buf));
 111         if (ret == -1)
 112                 fatal("Couldn't read from ssh-rand-helper: %s",
 113                     strerror(errno));
 114         if (ret != sizeof(buf))
 115                 fatal("ssh-rand-helper child produced insufficient data");
 116 
 117         close(p[0]);
 118 
 119         if (waitpid(pid, &ret, 0) == -1)
 120                fatal("Couldn't wait for ssh-rand-helper completion: %s", 
 121                    strerror(errno));
 122         mysignal(SIGCHLD, old_sigchld);
 123 
 124         /* We don't mind if the child exits upon a SIGPIPE */
 125         if (!WIFEXITED(ret) && 
 126             (!WIFSIGNALED(ret) || WTERMSIG(ret) != SIGPIPE))
 127                 fatal("ssh-rand-helper terminated abnormally");
 128         if (WEXITSTATUS(ret) != 0)
 129                 fatal("ssh-rand-helper exit with exit status %d", ret);
 130 
 131         RAND_add(buf, sizeof(buf), sizeof(buf));
 132         memset(buf, '\0', sizeof(buf));
 133 
 134 #endif /* OPENSSL_PRNG_ONLY */
 135         if (RAND_status() != 1)
 136                 fatal("PRNG is not seeded");
 137 }
 138 
 139 void
 140 init_rng(void) 
 141 {
 142         /*
 143          * OpenSSL version numbers: MNNFFPPS: major minor fix patch status
 144          * We match major, minor, fix and status (not patch)
 145          */
 146         if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L)
 147                 fatal("OpenSSL version mismatch. Built against %lx, you "
 148                     "have %lx", OPENSSL_VERSION_NUMBER, SSLeay());
 149 
 150 #ifndef OPENSSL_PRNG_ONLY
 151         if ((original_uid = getuid()) == -1)
 152                 fatal("getuid: %s", strerror(errno));
 153         if ((original_euid = geteuid()) == -1)
 154                 fatal("geteuid: %s", strerror(errno));
 155 #endif
 156 }
 157