Home | History | Annotate | Download | only in common
      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 
    157