Home | History | Annotate | Download | only in dropbear
      1 /*
      2  * Dropbear - a SSH2 server
      3  *
      4  * Copyright (c) 2002,2003 Matt Johnston
      5  * All rights reserved.
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a copy
      8  * of this software and associated documentation files (the "Software"), to deal
      9  * in the Software without restriction, including without limitation the rights
     10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11  * copies of the Software, and to permit persons to whom the Software is
     12  * furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be included in
     15  * all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23  * SOFTWARE. */
     24 
     25 #include "includes.h"
     26 #include "buffer.h"
     27 #include "dbutil.h"
     28 #include "bignum.h"
     29 
     30 static int donerandinit = 0;
     31 
     32 /* this is used to generate unique output from the same hashpool */
     33 static uint32_t counter = 0;
     34 /* the max value for the counter, so it won't integer overflow */
     35 #define MAX_COUNTER 1<<30
     36 
     37 static unsigned char hashpool[SHA1_HASH_SIZE];
     38 
     39 #define INIT_SEED_SIZE 32 /* 256 bits */
     40 
     41 static void readrand(unsigned char* buf, unsigned int buflen);
     42 
     43 /* The basic setup is we read some data from /dev/(u)random or prngd and hash it
     44  * into hashpool. To read data, we hash together current hashpool contents,
     45  * and a counter. We feed more data in by hashing the current pool and new
     46  * data into the pool.
     47  *
     48  * It is important to ensure that counter doesn't wrap around before we
     49  * feed in new entropy.
     50  *
     51  */
     52 
     53 static void readrand(unsigned char* buf, unsigned int buflen) {
     54 
     55 	static int already_blocked = 0;
     56 	int readfd;
     57 	unsigned int readpos;
     58 	int readlen;
     59 #ifdef DROPBEAR_PRNGD_SOCKET
     60 	struct sockaddr_un egdsock;
     61 	char egdcmd[2];
     62 #endif
     63 
     64 #ifdef DROPBEAR_RANDOM_DEV
     65 	readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY);
     66 	if (readfd < 0) {
     67 		dropbear_exit("couldn't open random device");
     68 	}
     69 #endif
     70 
     71 #ifdef DROPBEAR_PRNGD_SOCKET
     72 	memset((void*)&egdsock, 0x0, sizeof(egdsock));
     73 	egdsock.sun_family = AF_UNIX;
     74 	strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET,
     75 			sizeof(egdsock.sun_path));
     76 
     77 	readfd = socket(PF_UNIX, SOCK_STREAM, 0);
     78 	if (readfd < 0) {
     79 		dropbear_exit("couldn't open random device");
     80 	}
     81 	/* todo - try various common locations */
     82 	if (connect(readfd, (struct sockaddr*)&egdsock,
     83 			sizeof(struct sockaddr_un)) < 0) {
     84 		dropbear_exit("couldn't open random device");
     85 	}
     86 
     87 	if (buflen > 255)
     88 		dropbear_exit("can't request more than 255 bytes from egd");
     89 	egdcmd[0] = 0x02;	/* blocking read */
     90 	egdcmd[1] = (unsigned char)buflen;
     91 	if (write(readfd, egdcmd, 2) < 0)
     92 		dropbear_exit("can't send command to egd");
     93 #endif
     94 
     95 	/* read the actual random data */
     96 	readpos = 0;
     97 	do {
     98 		if (!already_blocked)
     99 		{
    100 			int ret;
    101 			struct timeval timeout;
    102 			fd_set read_fds;
    103 
    104 			timeout.tv_sec = 2; /* two seconds should be enough */
    105 			timeout.tv_usec = 0;
    106 
    107 			FD_ZERO(&read_fds);
    108 			FD_SET(readfd, &read_fds);
    109 			ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout);
    110 			if (ret == 0)
    111 			{
    112 				dropbear_log(LOG_INFO, "Warning: Reading the random source seems to have blocked.\nIf you experience problems, you probably need to find a better entropy source.");
    113 				already_blocked = 1;
    114 			}
    115 		}
    116 		readlen = read(readfd, &buf[readpos], buflen - readpos);
    117 		if (readlen <= 0) {
    118 			if (readlen < 0 && errno == EINTR) {
    119 				continue;
    120 			}
    121 			dropbear_exit("error reading random source");
    122 		}
    123 		readpos += readlen;
    124 	} while (readpos < buflen);
    125 
    126 	close (readfd);
    127 }
    128 
    129 /* initialise the prng from /dev/(u)random or prngd */
    130 void seedrandom() {
    131 
    132 	unsigned char readbuf[INIT_SEED_SIZE];
    133 
    134 	hash_state hs;
    135 
    136 	/* initialise so that things won't warn about
    137 	 * hashing an undefined buffer */
    138 	if (!donerandinit) {
    139 		m_burn(hashpool, sizeof(hashpool));
    140 	}
    141 
    142 	/* get the seed data */
    143 	readrand(readbuf, sizeof(readbuf));
    144 
    145 	/* hash in the new seed data */
    146 	sha1_init(&hs);
    147 	sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
    148 	sha1_process(&hs, (void*)readbuf, sizeof(readbuf));
    149 	sha1_done(&hs, hashpool);
    150 
    151 	counter = 0;
    152 	donerandinit = 1;
    153 }
    154 
    155 /* hash the current random pool with some unique identifiers
    156  * for this process and point-in-time. this is used to separate
    157  * the random pools for fork()ed processes. */
    158 void reseedrandom() {
    159 
    160 	pid_t pid;
    161 	hash_state hs;
    162 	struct timeval tv;
    163 
    164 	if (!donerandinit) {
    165 		dropbear_exit("seedrandom not done");
    166 	}
    167 
    168 	pid = getpid();
    169 	gettimeofday(&tv, NULL);
    170 
    171 	sha1_init(&hs);
    172 	sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
    173 	sha1_process(&hs, (void*)&pid, sizeof(pid));
    174 	sha1_process(&hs, (void*)&tv, sizeof(tv));
    175 	sha1_done(&hs, hashpool);
    176 }
    177 
    178 /* return len bytes of pseudo-random data */
    179 void genrandom(unsigned char* buf, unsigned int len) {
    180 
    181 	hash_state hs;
    182 	unsigned char hash[SHA1_HASH_SIZE];
    183 	unsigned int copylen;
    184 
    185 	if (!donerandinit) {
    186 		dropbear_exit("seedrandom not done");
    187 	}
    188 
    189 	while (len > 0) {
    190 		sha1_init(&hs);
    191 		sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
    192 		sha1_process(&hs, (void*)&counter, sizeof(counter));
    193 		sha1_done(&hs, hash);
    194 
    195 		counter++;
    196 		if (counter > MAX_COUNTER) {
    197 			seedrandom();
    198 		}
    199 
    200 		copylen = MIN(len, SHA1_HASH_SIZE);
    201 		memcpy(buf, hash, copylen);
    202 		len -= copylen;
    203 		buf += copylen;
    204 	}
    205 	m_burn(hash, sizeof(hash));
    206 }
    207 
    208 /* Generates a random mp_int.
    209  * max is a *mp_int specifying an upper bound.
    210  * rand must be an initialised *mp_int for the result.
    211  * the result rand satisfies:  0 < rand < max
    212  * */
    213 void gen_random_mpint(mp_int *max, mp_int *rand) {
    214 
    215 	unsigned char *randbuf = NULL;
    216 	unsigned int len = 0;
    217 	const unsigned char masks[] = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
    218 
    219 	const int size_bits = mp_count_bits(max);
    220 
    221 	len = size_bits / 8;
    222 	if ((size_bits % 8) != 0) {
    223 		len += 1;
    224 	}
    225 
    226 	randbuf = (unsigned char*)m_malloc(len);
    227 	do {
    228 		genrandom(randbuf, len);
    229 		/* Mask out the unrequired bits - mp_read_unsigned_bin expects
    230 		 * MSB first.*/
    231 		randbuf[0] &= masks[size_bits % 8];
    232 
    233 		bytes_to_mp(rand, randbuf, len);
    234 
    235 		/* keep regenerating until we get one satisfying
    236 		 * 0 < rand < max    */
    237 	} while (mp_cmp(rand, max) != MP_LT);
    238 	m_burn(randbuf, len);
    239 	m_free(randbuf);
    240 }
    241