# HG changeset patch # User Matt Johnston # Date 1363790467 -28800 # Node ID 91dd8328a3ffe7933dd35b602276e5a12bf59306 # Parent abd99ecd7ec22aa07fb668cf3abad8be2b0430ac# Parent 983a817f8e41b26ecaea6b97e215cdc076bea2b6 Merge "none" cipher/MAC branch. Also adds sha256 and sha512 diff -r 983a817f8e41 -r 91dd8328a3ff .hgtags --- a/.hgtags Thu May 17 20:52:57 2012 +0800 +++ b/.hgtags Wed Mar 20 22:41:07 2013 +0800 @@ -31,3 +31,7 @@ e5d119ea4c63656bc54ecfd865d04591ac2ed225 LTC_DB_0.47 3f12086c2ef2b9ffe36a822fdb3ff647fcec1831 DROPBEAR_2011.54 d354464b2aa6f6ba0bf44d43bcae5aa798435393 DROPBEAR_2012.55 +7faae8f46238e23975430876547b8950b4e75481 t:ltc-0.95-orig +0000000000000000000000000000000000000000 t:ltc-0.95-orig +d7da3b1e15401eb234ec866d5eac992fc4cd5878 t:ltc-0.95-db-merge1 +0000000000000000000000000000000000000000 t:ltc-0.95-db-merge1 diff -r 983a817f8e41 -r 91dd8328a3ff CHANGES --- a/CHANGES Thu May 17 20:52:57 2012 +0800 +++ b/CHANGES Wed Mar 20 22:41:07 2013 +0800 @@ -1,3 +1,26 @@ +- Allow specifying cipher (-c) and MAC (-m) lists for dbclient + +- Allow using 'none' cipher or MAC + +- Allow a user in immediately if the account has a blank password and blank + passwords are enabled + +- Include a few extra sources of entropy from /proc on Linux, hash private keys + as well + +- Added sha2-256 and sha2-512 hashes + +- Don't sent "localhost" for -R forward connections, reported by Denis Bider + +- Add "-B" runtime option to allow blank passwords + +- Allow using IPv6 bracket notation for addresses in server "-p" option, from Ben Jencks + +- A few improvements for Android from Reimar Döffinger + +- Fix memory leak for TCP forwarded connections to hosts that timed out, + reported by Norbert Benczúr. Appears to be a very long-standing bug. + 2012.55 - Wednesday 22 February 2012 - Security: Fix use-after-free bug that could be triggered if command="..." diff -r 983a817f8e41 -r 91dd8328a3ff README --- a/README Thu May 17 20:52:57 2012 +0800 +++ b/README Wed Mar 20 22:41:07 2013 +0800 @@ -1,4 +1,5 @@ This is Dropbear, a smallish SSH 2 server and client. +https://matt.ucc.asn.au/dropbear/dropbear.html INSTALL has compilation instructions. diff -r 983a817f8e41 -r 91dd8328a3ff channel.h --- a/channel.h Thu May 17 20:52:57 2012 +0800 +++ b/channel.h Wed Mar 20 22:41:07 2013 +0800 @@ -61,7 +61,8 @@ int readfd; /* read from insecure side, written to wire */ int errfd; /* used like writefd or readfd, depending if it's client or server. Doesn't exactly belong here, but is cleaner here */ - circbuffer *writebuf; /* data from the wire, for local consumption */ + circbuffer *writebuf; /* data from the wire, for local consumption. Can be + initially NULL */ circbuffer *extrabuf; /* extended-data for the program - used like writebuf but for stderr */ @@ -102,9 +103,6 @@ void setchannelfds(fd_set *readfd, fd_set *writefd); void channelio(fd_set *readfd, fd_set *writefd); struct Channel* getchannel(); -struct Channel* newchannel(unsigned int remotechan, - const struct ChanType *type, - unsigned int transwindow, unsigned int transmaxpacket); void recv_msg_channel_open(); void recv_msg_channel_request(); diff -r 983a817f8e41 -r 91dd8328a3ff circbuffer.c --- a/circbuffer.c Thu May 17 20:52:57 2012 +0800 +++ b/circbuffer.c Wed Mar 20 22:41:07 2013 +0800 @@ -37,7 +37,9 @@ } cbuf = (circbuffer*)m_malloc(sizeof(circbuffer)); - cbuf->data = (unsigned char*)m_malloc(size); + if (size > 0) { + cbuf->data = (unsigned char*)m_malloc(size); + } cbuf->used = 0; cbuf->readpos = 0; cbuf->writepos = 0; diff -r 983a817f8e41 -r 91dd8328a3ff cli-runopts.c --- a/cli-runopts.c Thu May 17 20:52:57 2012 +0800 +++ b/cli-runopts.c Wed Mar 20 22:41:07 2013 +0800 @@ -63,7 +63,7 @@ "-N Don't run a remote command\n" "-f Run in background after auth\n" "-y Always accept remote host key if unknown\n" - "-s Request a subsystem (use for sftp)\n" + "-s Request a subsystem (use by external sftp)\n" #ifdef ENABLE_CLI_PUBKEY_AUTH "-i (multiple allowed)\n" #endif diff -r 983a817f8e41 -r 91dd8328a3ff common-channel.c --- a/common-channel.c Thu May 17 20:52:57 2012 +0800 +++ b/common-channel.c Wed Mar 20 22:41:07 2013 +0800 @@ -48,7 +48,6 @@ static void send_msg_channel_eof(struct Channel *channel); static void send_msg_channel_close(struct Channel *channel); static void remove_channel(struct Channel *channel); -static void delete_channel(struct Channel *channel); static void check_in_progress(struct Channel *channel); static unsigned int write_pending(struct Channel * channel); static void check_close(struct Channel *channel); @@ -93,11 +92,20 @@ TRACE(("leave chancleanup")) } +static void +chan_initwritebuf(struct Channel *channel) +{ + dropbear_assert(channel->writebuf->size == 0 && channel->recvwindow == 0); + cbuf_free(channel->writebuf); + channel->writebuf = cbuf_new(opts.recv_window); + channel->recvwindow = opts.recv_window; +} + /* Create a new channel entry, send a reply confirm or failure */ /* If remotechan, transwindow and transmaxpacket are not know (for a new * outgoing connection, with them to be filled on confirmation), they should * all be set to 0 */ -struct Channel* newchannel(unsigned int remotechan, +static struct Channel* newchannel(unsigned int remotechan, const struct ChanType *type, unsigned int transwindow, unsigned int transmaxpacket) { @@ -152,9 +160,10 @@ newchan->await_open = 0; newchan->flushing = 0; - newchan->writebuf = cbuf_new(opts.recv_window); + newchan->writebuf = cbuf_new(0); /* resized later by chan_initwritebuf */ + newchan->recvwindow = 0; + newchan->extrabuf = NULL; /* The user code can set it up */ - newchan->recvwindow = opts.recv_window; newchan->recvdonelen = 0; newchan->recvmaxpacket = RECV_MAX_PAYLOAD_LEN; @@ -268,7 +277,7 @@ channel->writefd, channel->readfd, channel->errfd, channel->sent_close, channel->recv_close)) TRACE(("writebuf size %d extrabuf size %d", - cbuf_getused(channel->writebuf), + channel->writebuf ? cbuf_getused(channel->writebuf) : 0, channel->extrabuf ? cbuf_getused(channel->extrabuf) : 0)) if (!channel->flushing @@ -352,9 +361,10 @@ send_msg_channel_open_failure(channel->remotechan, SSH_OPEN_CONNECT_FAILED, "", ""); close(channel->writefd); - delete_channel(channel); + remove_channel(channel); TRACE(("leave check_in_progress: fail")) } else { + chan_initwritebuf(channel); send_msg_channel_open_confirmation(channel, channel->recvwindow, channel->recvmaxpacket); channel->readfd = channel->writefd; @@ -474,13 +484,13 @@ } /* Stuff from the wire */ - if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 ) - || channel->initconn) { + if (channel->initconn + ||(channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0)) { FD_SET(channel->writefd, writefds); } if (ERRFD_IS_WRITE(channel) && channel->errfd >= 0 - && cbuf_getused(channel->extrabuf) > 0 ) { + && cbuf_getused(channel->extrabuf) > 0) { FD_SET(channel->errfd, writefds); } @@ -553,21 +563,13 @@ channel->typedata = NULL; - delete_channel(channel); + ses.channels[channel->index] = NULL; + m_free(channel); + ses.chancount--; TRACE(("leave remove_channel")) } -/* Remove a channel entry */ -static void delete_channel(struct Channel *channel) { - - ses.channels[channel->index] = NULL; - m_free(channel); - ses.chancount--; - -} - - /* Handle channel specific requests, passing off to corresponding handlers * such as chansession or x11fwd */ void recv_msg_channel_request() { @@ -700,7 +702,7 @@ dropbear_exit("Received data after eof"); } - if (fd < 0) { + if (fd < 0 || !cbuf) { /* If we have encountered failed write, the far side might still * be sending data without having yet received our close notification. * We just drop the data. */ @@ -838,12 +840,14 @@ } if (ret > 0) { errtype = ret; - delete_channel(channel); + remove_channel(channel); TRACE(("inithandler returned failure %d", ret)) goto failure; } } + chan_initwritebuf(channel); + /* success */ send_msg_channel_open_confirmation(channel, channel->recvwindow, channel->recvmaxpacket); @@ -982,6 +986,10 @@ return DROPBEAR_FAILURE; } + /* Outbound opened channels don't make use of in-progress connections, + * we can set it up straight away */ + chan_initwritebuf(chan); + /* set fd non-blocking */ setnonblocking(fd); diff -r 983a817f8e41 -r 91dd8328a3ff common-runopts.c --- a/common-runopts.c Thu May 17 20:52:57 2012 +0800 +++ b/common-runopts.c Wed Mar 20 22:41:07 2013 +0800 @@ -29,6 +29,7 @@ #include "dbutil.h" #include "auth.h" #include "algo.h" +#include "random.h" runopts opts; /* GLOBAL */ @@ -45,6 +46,9 @@ goto out; } buf_setpos(buf, 0); + + addrandom(buf_getptr(buf, buf->len), buf->len); + if (buf_get_priv_key(buf, hostkey, type) == DROPBEAR_FAILURE) { goto out; } diff -r 983a817f8e41 -r 91dd8328a3ff common-session.c --- a/common-session.c Thu May 17 20:52:57 2012 +0800 +++ b/common-session.c Wed Mar 20 22:41:07 2013 +0800 @@ -462,6 +462,10 @@ passwd_crypt = spasswd->sp_pwdp; } #endif + if (!passwd_crypt) { + /* android supposedly returns NULL */ + passwd_crypt = "!!"; + } ses.authstate.pw_passwd = m_strdup(passwd_crypt); } } diff -r 983a817f8e41 -r 91dd8328a3ff compat.c --- a/compat.c Thu May 17 20:52:57 2012 +0800 +++ b/compat.c Wed Mar 20 22:41:07 2013 +0800 @@ -193,6 +193,10 @@ char *basename(const char *path) { char *foo = strrchr(path, '/'); + if (!foo) + { + return path; + } return ++foo; } diff -r 983a817f8e41 -r 91dd8328a3ff dbclient.1 --- a/dbclient.1 Thu May 17 20:52:57 2012 +0800 +++ b/dbclient.1 Wed Mar 20 22:41:07 2013 +0800 @@ -111,7 +111,17 @@ "Netcat-alike" mode, where Dropbear will connect to the given host, then create a forwarded connection to \fIendhost\fR. This will then be presented as dbclient's standard input/output. +.TP +.B \-c \fIcipherlist +Specify a comma separated list of ciphers to enable. Use \fI-c help\fR to list possibilities. +.TP +.B \-m \fIMAClist +Specify a comma separated list of authentication MACs to enable. Use \fI-m help\fR to list possibilities. +.TP +.B \-s +The specified command will be requested as a subsystem, used for sftp. Dropbear doesn't implement sftp itself but the OpenSSH sftp client can be used eg \fIsftp -S dbclient user@host\fR +.SH MULTI-HOP Dropbear will also allow multiple "hops" to be specified, separated by commas. In this case a connection will be made to the first host, then a TCP forwarded connection will be made through that to the second host, and so on. Hosts other than @@ -148,4 +158,4 @@ .SH SEE ALSO dropbear(8), dropbearkey(8) .P -http://matt.ucc.asn.au/dropbear/dropbear.html +https://matt.ucc.asn.au/dropbear/dropbear.html diff -r 983a817f8e41 -r 91dd8328a3ff dropbear.8 --- a/dropbear.8 Thu May 17 20:52:57 2012 +0800 +++ b/dropbear.8 Wed Mar 20 22:41:07 2013 +0800 @@ -189,4 +189,4 @@ .SH SEE ALSO dropbearkey(8), dbclient(1) .P -http://matt.ucc.asn.au/dropbear/dropbear.html +https://matt.ucc.asn.au/dropbear/dropbear.html diff -r 983a817f8e41 -r 91dd8328a3ff dropbearkey.8 --- a/dropbearkey.8 Thu May 17 20:52:57 2012 +0800 +++ b/dropbearkey.8 Wed Mar 20 22:41:07 2013 +0800 @@ -47,4 +47,4 @@ .SH SEE ALSO dropbear(8), dbclient(1) .P -http://matt.ucc.asn.au/dropbear/dropbear.html +https://matt.ucc.asn.au/dropbear/dropbear.html diff -r 983a817f8e41 -r 91dd8328a3ff dss.c --- a/dss.c Thu May 17 20:52:57 2012 +0800 +++ b/dss.c Wed Mar 20 22:41:07 2013 +0800 @@ -258,52 +258,14 @@ } #endif /* DROPBEAR_SIGNKEY_VERIFY */ -#ifdef DSS_PROTOK -/* convert an unsigned mp into an array of bytes, malloced. - * This array must be freed after use, len contains the length of the array, - * if len != NULL */ -static unsigned char* mptobytes(mp_int *mp, int *len) { - - unsigned char* ret; - int size; - - size = mp_unsigned_bin_size(mp); - ret = m_malloc(size); - if (mp_to_unsigned_bin(mp, ret) != MP_OKAY) { - dropbear_exit("Mem alloc error"); - } - if (len != NULL) { - *len = size; - } - return ret; -} -#endif - /* Sign the data presented with key, writing the signature contents - * to the buffer - * - * When DSS_PROTOK is #defined: - * The alternate k generation method is based on the method used in PuTTY. - * In particular to avoid being vulnerable to attacks using flaws in random - * generation of k, we use the following: - * - * proto_k = SHA512 ( SHA512(x) || SHA160(message) ) - * k = proto_k mod q - * - * Now we aren't relying on the random number generation to protect the private - * key x, which is a long term secret */ + * to the buffer */ void buf_put_dss_sign(buffer* buf, dropbear_dss_key *key, const unsigned char* data, unsigned int len) { unsigned char msghash[SHA1_HASH_SIZE]; unsigned int writelen; unsigned int i; -#ifdef DSS_PROTOK - unsigned char privkeyhash[SHA512_HASH_SIZE]; - unsigned char *privkeytmp; - unsigned char proto_k[SHA512_HASH_SIZE]; - DEF_MP_INT(dss_protok); -#endif DEF_MP_INT(dss_k); DEF_MP_INT(dss_m); DEF_MP_INT(dss_temp1); @@ -322,33 +284,9 @@ m_mp_init_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s, &dss_m, NULL); -#ifdef DSS_PROTOK - /* hash the privkey */ - privkeytmp = mptobytes(key->x, &i); - sha512_init(&hs); - sha512_process(&hs, "the quick brown fox jumped over the lazy dog", 44); - sha512_process(&hs, privkeytmp, i); - sha512_done(&hs, privkeyhash); - m_burn(privkeytmp, i); - m_free(privkeytmp); - - /* calculate proto_k */ - sha512_init(&hs); - sha512_process(&hs, privkeyhash, SHA512_HASH_SIZE); - sha512_process(&hs, msghash, SHA1_HASH_SIZE); - sha512_done(&hs, proto_k); - - /* generate k */ - m_mp_init(&dss_protok); - bytes_to_mp(&dss_protok, proto_k, SHA512_HASH_SIZE); - if (mp_mod(&dss_protok, key->q, &dss_k) != MP_OKAY) { - dropbear_exit("DSS error"); - } - mp_clear(&dss_protok); - m_burn(proto_k, SHA512_HASH_SIZE); -#else /* DSS_PROTOK not defined*/ + /* the random number generator's input has included the private key which + * avoids DSS's problem of private key exposure due to low entropy */ gen_random_mpint(key->q, &dss_k); -#endif /* now generate the actual signature */ bytes_to_mp(&dss_m, msghash, SHA1_HASH_SIZE); diff -r 983a817f8e41 -r 91dd8328a3ff gendss.c diff -r 983a817f8e41 -r 91dd8328a3ff genrsa.c diff -r 983a817f8e41 -r 91dd8328a3ff options.h --- a/options.h Thu May 17 20:52:57 2012 +0800 +++ b/options.h Wed Mar 20 22:41:07 2013 +0800 @@ -143,13 +143,6 @@ * signing operations slightly slower. */ #define RSA_BLINDING -/* Define DSS_PROTOK to use PuTTY's method of generating the value k for dss, - * rather than just from the random byte source. Undefining this will save you - * ~4k in binary size with static uclibc, but your DSS hostkey could be exposed - * if the random number source isn't good. It happened to Sony. - * On systems with a decent random source this isn't required. */ -/* #define DSS_PROTOK */ - /* Control the memory/performance/compression tradeoff for zlib. * Set windowBits=8 for least memory usage, see your system's * zlib.h for full details. @@ -194,11 +187,6 @@ #define ENABLE_SVR_PUBKEY_OPTIONS #endif -/* Define this to allow logging in to accounts that have no password specified. - * Public key logins are allowed for blank-password accounts regardless of this - * setting. */ -/* #define ALLOW_BLANK_PASSWORD */ - #define ENABLE_CLI_PASSWORD_AUTH #define ENABLE_CLI_PUBKEY_AUTH #define ENABLE_CLI_INTERACT_AUTH @@ -218,20 +206,14 @@ * return the password on standard output */ /*#define ENABLE_CLI_ASKPASS_HELPER*/ -/* Random device to use - define either DROPBEAR_RANDOM_DEV or - * DROPBEAR_PRNGD_SOCKET. - * DROPBEAR_RANDOM_DEV is recommended on hosts with a good /dev/(u)random, - * otherwise use run prngd (or egd if you want), specifying the socket. - * The device will be queried for a few dozen bytes of seed a couple of times - * per session (or more for very long-lived sessions). */ +/* Source for randomness. This must be able to provide hundreds of bytes per SSH + * connection without blocking. In addition /dev/random is used for seeding + * rsa/dss key generation */ +#define DROPBEAR_URANDOM_DEV "/dev/urandom" -/* We'll use /dev/urandom by default, since /dev/random is too much hassle. - * If system developers aren't keeping seeds between boots nor getting - * any entropy from somewhere it's their own fault. */ -#define DROPBEAR_RANDOM_DEV "/dev/urandom" +/* Set this to use PRNGD or EGD instead of /dev/urandom or /dev/random */ +/*#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng"*/ -/* prngd must be manually set up to produce output */ -/*#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng"*/ /* Specify the number of clients we will allow to be connected but * not yet authenticated. After this limit, connections are rejected */ diff -r 983a817f8e41 -r 91dd8328a3ff random.c --- a/random.c Thu May 17 20:52:57 2012 +0800 +++ b/random.c Wed Mar 20 22:41:07 2013 +0800 @@ -26,20 +26,18 @@ #include "buffer.h" #include "dbutil.h" #include "bignum.h" - -static int donerandinit = 0; +#include "random.h" /* this is used to generate unique output from the same hashpool */ static uint32_t counter = 0; /* the max value for the counter, so it won't integer overflow */ #define MAX_COUNTER 1<<30 -static unsigned char hashpool[SHA1_HASH_SIZE]; +static unsigned char hashpool[SHA1_HASH_SIZE] = {0}; +static int donerandinit = 0; #define INIT_SEED_SIZE 32 /* 256 bits */ -static void readrand(unsigned char* buf, unsigned int buflen); - /* The basic setup is we read some data from /dev/(u)random or prngd and hash it * into hashpool. To read data, we hash together current hashpool contents, * and a counter. We feed more data in by hashing the current pool and new @@ -50,120 +48,192 @@ * */ -static void readrand(unsigned char* buf, unsigned int buflen) { - +/* Pass len=0 to hash an entire file */ +static int +process_file(hash_state *hs, const char *filename, + unsigned int len, int prngd) +{ static int already_blocked = 0; int readfd; - unsigned int readpos; - int readlen; -#ifdef DROPBEAR_PRNGD_SOCKET - struct sockaddr_un egdsock; - char egdcmd[2]; -#endif - -#ifdef DROPBEAR_RANDOM_DEV - readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY); - if (readfd < 0) { - dropbear_exit("Couldn't open random device"); - } -#endif + unsigned int readcount; + int ret = DROPBEAR_FAILURE; #ifdef DROPBEAR_PRNGD_SOCKET - readfd = connect_unix(DROPBEAR_PRNGD_SOCKET); - - if (readfd < 0) { - dropbear_exit("Couldn't open random device"); + if (prngd) + { + readfd = connect_unix(filename); + } + else +#endif + { + readfd = open(filename, O_RDONLY); } - if (buflen > 255) - dropbear_exit("Can't request more than 255 bytes from egd"); - egdcmd[0] = 0x02; /* blocking read */ - egdcmd[1] = (unsigned char)buflen; - if (write(readfd, egdcmd, 2) < 0) - dropbear_exit("Can't send command to egd"); -#endif + if (readfd < 0) { + goto out; + } - /* read the actual random data */ - readpos = 0; - do { + readcount = 0; + while (len == 0 || readcount < len) + { + int readlen, wantread; + unsigned char readbuf[2048]; if (!already_blocked) { int ret; - struct timeval timeout; + struct timeval timeout = { .tv_sec = 2, .tv_usec = 0}; fd_set read_fds; - timeout.tv_sec = 2; /* two seconds should be enough */ - timeout.tv_usec = 0; - FD_ZERO(&read_fds); FD_SET(readfd, &read_fds); ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout); if (ret == 0) { - 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."); + dropbear_log(LOG_WARNING, "Warning: Reading the randomness source '%s' seems to have blocked.\nYou may need to find a better entropy source.", filename); already_blocked = 1; } } - readlen = read(readfd, &buf[readpos], buflen - readpos); + + if (len == 0) + { + wantread = sizeof(readbuf); + } + else + { + wantread = MIN(sizeof(readbuf), len-readcount); + } + +#ifdef DROPBEAR_PRNGD_SOCKET + if (prngd) + { + char egdcmd[2]; + egdcmd[0] = 0x02; /* blocking read */ + egdcmd[1] = (unsigned char)wantread; + if (write(readfd, egdcmd, 2) < 0) + { + dropbear_exit("Can't send command to egd"); + } + } +#endif + + readlen = read(readfd, readbuf, wantread); if (readlen <= 0) { if (readlen < 0 && errno == EINTR) { continue; } - dropbear_exit("Error reading random source"); + if (readlen == 0 && len == 0) + { + /* whole file was read as requested */ + break; + } + goto out; } - readpos += readlen; - } while (readpos < buflen); - - close (readfd); + sha1_process(hs, readbuf, readlen); + readcount += readlen; + } + ret = DROPBEAR_SUCCESS; +out: + close(readfd); + return ret; } -/* initialise the prng from /dev/(u)random or prngd */ -void seedrandom() { - - unsigned char readbuf[INIT_SEED_SIZE]; - +void addrandom(char * buf, unsigned int len) +{ hash_state hs; - /* initialise so that things won't warn about - * hashing an undefined buffer */ - if (!donerandinit) { - m_burn(hashpool, sizeof(hashpool)); - } - - /* get the seed data */ - readrand(readbuf, sizeof(readbuf)); - /* hash in the new seed data */ sha1_init(&hs); + /* existing state (zeroes on startup) */ sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); - sha1_process(&hs, (void*)readbuf, sizeof(readbuf)); + + /* new */ + sha1_process(&hs, buf, len); + sha1_done(&hs, hashpool); +} + +static void write_urandom() +{ +#ifndef DROPBEAR_PRNGD_SOCKET + /* This is opportunistic, don't worry about failure */ + unsigned char buf[INIT_SEED_SIZE]; + FILE *f = fopen(DROPBEAR_URANDOM_DEV, "w"); + genrandom(buf, sizeof(buf)); + fwrite(buf, sizeof(buf), 1, f); + fclose(f); +#endif +} + +/* Initialise the prng from /dev/urandom or prngd. This function can + * be called multiple times */ +void seedrandom() { + + hash_state hs; + + pid_t pid; + struct timeval tv; + clock_t clockval; + + /* hash in the new seed data */ + sha1_init(&hs); + /* existing state */ + sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + +#ifdef DROPBEAR_PRNGD_SOCKET + if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1) + != DROPBEAR_SUCCESS) { + dropbear_exit("Failure reading random device %s", + DROPBEAR_PRNGD_SOCKET); + } +#else + /* non-blocking random source (probably /dev/urandom) */ + if (process_file(&hs, DROPBEAR_URANDOM_DEV, INIT_SEED_SIZE, 0) + != DROPBEAR_SUCCESS) { + dropbear_exit("Failure reading random device %s", + DROPBEAR_URANDOM_DEV); + } +#endif + + /* A few other sources to fall back on. + * Add more here for other platforms */ +#ifdef __linux__ + /* Seems to be a reasonable source of entropy from timers. Possibly hard + * for even local attackers to reproduce */ + process_file(&hs, "/proc/timer_list", 0, 0); + /* Might help on systems with wireless */ + process_file(&hs, "/proc/interrupts", 0, 0); + + process_file(&hs, "/proc/loadavg", 0, 0); + process_file(&hs, "/proc/sys/kernel/random/entropy_avail", 0, 0); + + /* Mostly network visible but useful in some situations */ + process_file(&hs, "/proc/net/netstat", 0, 0); + process_file(&hs, "/proc/net/dev", 0, 0); + process_file(&hs, "/proc/net/tcp", 0, 0); + /* Also includes interface lo */ + process_file(&hs, "/proc/net/rt_cache", 0, 0); + process_file(&hs, "/proc/vmstat", 0, 0); +#endif + + pid = getpid(); + sha1_process(&hs, (void*)&pid, sizeof(pid)); + + gettimeofday(&tv, NULL); + sha1_process(&hs, (void*)&tv, sizeof(tv)); + + clockval = clock(); + sha1_process(&hs, (void*)&clockval, sizeof(clockval)); + + /* When a private key is read by the client or server it will + * be added to the hashpool - see runopts.c */ + sha1_done(&hs, hashpool); counter = 0; donerandinit = 1; -} -/* hash the current random pool with some unique identifiers - * for this process and point-in-time. this is used to separate - * the random pools for fork()ed processes. */ -void reseedrandom() { - - pid_t pid; - hash_state hs; - struct timeval tv; - - if (!donerandinit) { - dropbear_exit("seedrandom not done"); - } - - pid = getpid(); - gettimeofday(&tv, NULL); - - sha1_init(&hs); - sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); - sha1_process(&hs, (void*)&pid, sizeof(pid)); - sha1_process(&hs, (void*)&tv, sizeof(tv)); - sha1_done(&hs, hashpool); + /* Feed it all back into /dev/urandom - this might help if Dropbear + * is running from inetd and gets new state each time */ + write_urandom(); } /* return len bytes of pseudo-random data */ diff -r 983a817f8e41 -r 91dd8328a3ff random.h --- a/random.h Thu May 17 20:52:57 2012 +0800 +++ b/random.h Wed Mar 20 22:41:07 2013 +0800 @@ -28,9 +28,8 @@ struct mp_int; void seedrandom(); -void reseedrandom(); -void genrandom(unsigned char* buf, int len); -void addrandom(unsigned char* buf, int len); +void genrandom(unsigned char* buf, unsigned int len); +void addrandom(char * buf, unsigned int len); void gen_random_mpint(mp_int *max, mp_int *rand); #endif /* _RANDOM_H_ */ diff -r 983a817f8e41 -r 91dd8328a3ff runopts.h --- a/runopts.h Thu May 17 20:52:57 2012 +0800 +++ b/runopts.h Wed Mar 20 22:41:07 2013 +0800 @@ -89,6 +89,7 @@ int noauthpass; int norootpass; + int allowblankpass; #ifdef ENABLE_SVR_REMOTETCPFWD int noremotetcp; diff -r 983a817f8e41 -r 91dd8328a3ff sshpty.c --- a/sshpty.c Thu May 17 20:52:57 2012 +0800 +++ b/sshpty.c Wed Mar 20 22:41:07 2013 +0800 @@ -133,7 +133,7 @@ close(*ptyfd); return 0; } -#ifndef HAVE_CYGWIN +#if !defined(HAVE_CYGWIN) && defined(I_PUSH) /* * Push the appropriate streams modules, as described in Solaris pts(7). * HP-UX pts(7) doesn't have ttcompat module. diff -r 983a817f8e41 -r 91dd8328a3ff svr-auth.c --- a/svr-auth.c Thu May 17 20:52:57 2012 +0800 +++ b/svr-auth.c Wed Mar 20 22:41:07 2013 +0800 @@ -154,8 +154,8 @@ strncmp(methodname, AUTH_METHOD_NONE, AUTH_METHOD_NONE_LEN) == 0) { TRACE(("recv_msg_userauth_request: 'none' request")) -#ifdef ALLOW_BLANK_PASSWORD - if (!svr_opts.noauthpass + if (svr_opts.allowblankpass + && !svr_opts.noauthpass && !(svr_opts.norootpass && ses.authstate.pw_uid == 0) && ses.authstate.pw_passwd[0] == '\0') { @@ -167,7 +167,6 @@ goto out; } else -#endif { send_msg_userauth_failure(0, 0); goto out; diff -r 983a817f8e41 -r 91dd8328a3ff svr-authpasswd.c --- a/svr-authpasswd.c Thu May 17 20:52:57 2012 +0800 +++ b/svr-authpasswd.c Wed Mar 20 22:41:07 2013 +0800 @@ -29,6 +29,7 @@ #include "buffer.h" #include "dbutil.h" #include "auth.h" +#include "runopts.h" #ifdef ENABLE_SVR_PASSWORD_AUTH diff -r 983a817f8e41 -r 91dd8328a3ff svr-chansession.c --- a/svr-chansession.c Thu May 17 20:52:57 2012 +0800 +++ b/svr-chansession.c Wed Mar 20 22:41:07 2013 +0800 @@ -871,7 +871,7 @@ svr_opts.hostkey = NULL; /* overwrite the prng state */ - reseedrandom(); + seedrandom(); #endif /* clear environment */ diff -r 983a817f8e41 -r 91dd8328a3ff svr-main.c --- a/svr-main.c Thu May 17 20:52:57 2012 +0800 +++ b/svr-main.c Wed Mar 20 22:41:07 2013 +0800 @@ -254,6 +254,8 @@ goto out; } + seedrandom(); + if (pipe(childpipe) < 0) { TRACE(("error creating child pipe")) goto out; @@ -267,8 +269,11 @@ if (fork_ret < 0) { dropbear_log(LOG_WARNING, "Error forking: %s", strerror(errno)); goto out; + } - } else if (fork_ret > 0) { + addrandom(&fork_ret, sizeof(fork_ret)); + + if (fork_ret > 0) { /* parent */ childpipes[conn_idx] = childpipe[0]; diff -r 983a817f8e41 -r 91dd8328a3ff svr-runopts.c --- a/svr-runopts.c Thu May 17 20:52:57 2012 +0800 +++ b/svr-runopts.c Wed Mar 20 22:41:07 2013 +0800 @@ -63,6 +63,7 @@ #if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH) "-s Disable password logins\n" "-g Disable password logins for root\n" + "-B Allow blank password logins\n" #endif #ifdef ENABLE_SVR_LOCALTCPFWD "-j Disable local port forwarding\n" @@ -115,6 +116,7 @@ svr_opts.norootlogin = 0; svr_opts.noauthpass = 0; svr_opts.norootpass = 0; + svr_opts.allowblankpass = 0; svr_opts.inetdmode = 0; svr_opts.portcount = 0; svr_opts.hostkey = NULL; @@ -234,6 +236,9 @@ case 'g': svr_opts.norootpass = 1; break; + case 'B': + svr_opts.allowblankpass = 1; + break; #endif case 'h': printhelp(argv[0]); @@ -324,8 +329,23 @@ /* We don't free it, it becomes part of the runopt state */ myspec = m_strdup(spec); - /* search for ':', that separates address and port */ - svr_opts.ports[svr_opts.portcount] = strrchr(myspec, ':'); + if (myspec[0] == '[') { + myspec++; + svr_opts.ports[svr_opts.portcount] = strchr(myspec, ']'); + if (svr_opts.ports[svr_opts.portcount] == NULL) { + /* Unmatched [ -> exit */ + dropbear_exit("Bad listen address"); + } + svr_opts.ports[svr_opts.portcount][0] = '\0'; + svr_opts.ports[svr_opts.portcount]++; + if (svr_opts.ports[svr_opts.portcount][0] != ':') { + /* Missing port -> exit */ + dropbear_exit("Missing port"); + } + } else { + /* search for ':', that separates address and port */ + svr_opts.ports[svr_opts.portcount] = strrchr(myspec, ':'); + } if (svr_opts.ports[svr_opts.portcount] == NULL) { /* no ':' -> the whole string specifies just a port */ diff -r 983a817f8e41 -r 91dd8328a3ff svr-session.c --- a/svr-session.c Thu May 17 20:52:57 2012 +0800 +++ b/svr-session.c Wed Mar 20 22:41:07 2013 +0800 @@ -75,7 +75,6 @@ void svr_session(int sock, int childpipe) { char *host, *port; size_t len; - reseedrandom(); crypto_init(); common_session_init(sock, sock); diff -r 983a817f8e41 -r 91dd8328a3ff sysoptions.h --- a/sysoptions.h Thu May 17 20:52:57 2012 +0800 +++ b/sysoptions.h Wed Mar 20 22:41:07 2013 +0800 @@ -201,14 +201,6 @@ #error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h" #endif -#if defined(DROPBEAR_RANDOM_DEV) && defined(DROPBEAR_PRNGD_SOCKET) -#error "You can't turn on DROPBEAR_PRNGD_SOCKET and DROPBEAR_RANDOM_DEV at once" -#endif - -#if !defined(DROPBEAR_RANDOM_DEV) && !defined(DROPBEAR_PRNGD_SOCKET) -#error "You must choose one of DROPBEAR_PRNGD_SOCKET or DROPBEAR_RANDOM_DEV in options.h" -#endif - /* We use dropbear_client and dropbear_server as shortcuts to avoid redundant * code, if we're just compiling as client or server */ #if defined(DROPBEAR_SERVER) && defined(DROPBEAR_CLIENT)