# HG changeset patch # User Matt Johnston # Date 1448291088 -28800 # Node ID d734fe76b72f70f83a99f701eba3a9bb76760bd5 # Parent 624fc24cfae5fa6642855c0c25097128c2434960# Parent a44747e5a05a405fc4bd5dc21496779ce2496489 merge diff -r 624fc24cfae5 -r d734fe76b72f .hgsigs --- a/.hgsigs Fri Aug 07 21:26:03 2015 +0800 +++ b/.hgsigs Mon Nov 23 23:04:48 2015 +0800 @@ -14,3 +14,5 @@ caac692b366c153cea0e9cd59aa2d79a7d843d4e 0 iEYEABECAAYFAlPk1mcACgkQjPn4sExkf7wLpgCeOqMYqpkf4lYUuyrn9VYThNpc7PkAn3JOSNgIqkKUcmSy6FstrI8jwJzq 2d421bc0545d1be6d59a4ebfe61606d94b124b0c 0 iEYEABECAAYFAlRJDCQACgkQjPn4sExkf7xUYACcCwVJkYWXJn5x/D5A+qMupy778lEAn0rg1oNiq96YU/4jOPsS5IMItihu 1d2d81b1b7c1b100e9c369e40b9fa5b2d491eea9 0 iEYEABECAAYFAlTKOKUACgkQjPn4sExkf7xWMACfYFozyHiRk5GaocTa5z6Ws1uyB4kAoLubxoxcnM3E7AA9mHAzc3OB5M0Y +a687f835236c7025b5cb2968fe9c4ebc4a49f0ea 0 iQIcBAABCgAGBQJVxg62AAoJEPSYMBLCC7qsC+EQAKw8YWogrVHhIFct2fx/nqybSPVrhFyKFKHhq7K/lZeVm0MGIWdSyVcQgP+Hs2jWNBWzG4AJ1BtifHWQH6IDh7W5RuwOXu5KobgPW9BsN3EVE9KIR+xe9jCAmFl9rIw0tNpy1q6R0TpYXx/sWlMilxecyEGyr2Ias2Sm19aY2mOEv8PLfh9BLfrJEKtt2NxL7TX8ScPwJXJMmVIQjN9WK4Ptx3tjcGNRivEVR/dftP5sJx2DBJx9avyDqrfloMW7Q7sPgJ88MPruCDxedOkbzH7JdHe3Humr2G4LsI0KPU7pNN6EBDjhJ+SVXuOyAgu5j/C0R+0ggGfjSrjDu8WjHyclFlwwu2MSGuHf111I1qkLtaRY3H1FZO5Y2gbLwBLQ82svA4klcBIxtP5jKAZDTh1jQMYsfKotvZdawOWrPDkNmKoUg2JXLHAtj9Dd0uGIhqfspZY3qlpzxw9uCkljWclUBD097ygotwAb2XdLoAWZ3KdvoPM+k448vIAQ7Q/aqcnm/dLQJr3Le029gpkOKoWKaQTlk0itrRGpgETHAhE2LnmWxYSKp6NYSKMgEONbfDiVNLyDTOlvpPiEb20RsOP64xA4wVDGmPenCURmMYoepQK6oJdtkNtCdth2S49KxPQAC+Dem4YZ7b+5b+cXrK5Nz7elBxZzRQWdjmZ4JDQK +ef4b26364b0cdda1084751d7de3d76c589e2d9cb 0 iQIcBAABCgAGBQJVxg7BAAoJEESTFJTynGdz9Q4P/A0Kq4H52rQqxq42PoEMFbVQIUfkFzyWjAz8eEGLmP5x5/sdpyxZDEyBSUG55uyNvOPTHE+Sd3t2h2Iieq749qwYgqggXC0P+C0zGzW3hB5Rv6dTUrKN1yCyaWE2tY488RsyVlcAs4vrp1Cum5Gv8/BUVKjzZmkZ1iq/3RyrvbLEiLoMrcLnQ+sUdaYHvfEwxDbzpOEvepg8iDJBitTrfG9xHp9otX6ucahwn1EumFvC5mvUxbiQ9jv76t4FJztjMoB24hPCH9T1FjB8uNsoM+j2Z67r81eJrGgNpJzjX0S3lY/AADZGhfGnfybTM9gFuQayIJuCJqduQibVwYkAAnPi17NmbdwPu0Rdz55oU+ft09XLVm/qkQcD1EP5bxYWnLIEMkkZQnFx7WdMpjKK9oGxZHeFYAKEgPgePCkk4TQ4PxNa+3854H19AUssQlaueGcbDLyPIRiSyqhleXawGfaJi+1jBt0DM7CNbAHAUWUE07VhQzNGWjabdEk4eXKTmDL+mZJFdHGBhyCve8sPmZBYJvM2PRgcXe8fwFh+R7gVj6kFbZJvgM9kG7EeF+4ZMEXG4yKpV/SKfMMeEPBCZjFxZhlJJ0fsZbB1Y/iLw8LXnJ0fa/5xFYv6k+iytfom/rqS4iUD7NWTjcEYHjd4EO4QlPD2Ef/AWOO8YBUBv8kA diff -r 624fc24cfae5 -r d734fe76b72f .hgtags --- a/.hgtags Fri Aug 07 21:26:03 2015 +0800 +++ b/.hgtags Mon Nov 23 23:04:48 2015 +0800 @@ -47,3 +47,4 @@ e9579816f20ea85affc6135e87f8477992808948 DROPBEAR_2014.65 735511a4c761141416ad0e6728989d2dafa55bc2 DROPBEAR_2014.66 cbd674d63cd4f3781464a8d4056a5506c8ae926f DROPBEAR_2015.67 +809feaa9408f036734129c77f2b3c7e779d4f099 DROPBEAR_2015.68 diff -r 624fc24cfae5 -r d734fe76b72f CHANGES --- a/CHANGES Fri Aug 07 21:26:03 2015 +0800 +++ b/CHANGES Mon Nov 23 23:04:48 2015 +0800 @@ -1,23 +1,31 @@ -- Improve efficiency of writing data to local program/pipes, measured 30% increase +2015.68 - Saturday 8 August 2015 + +- Reduce local data copying for improved efficiency. Measured 30% increase in throughput for connections to localhost -- Use TCP Fast Open on Linux if available. saves a round trip at connection +- Forwarded TCP ports connect asynchronously and try all available addresses + (IPv4, IPv6, round robin DNS) + +- Fix all compile warnings, many patches from Gaël Portay + Note that configure with -Werror may not be successful on some platforms (OS X) + and some configuration options may still result in unused variable + warnings. + +- Use TCP Fast Open on Linux if available. Saves a round trip at connection to hosts that have previously been connected. Needs a recent Linux kernel and possibly "sysctl -w net.ipv4.tcp_fastopen=3" Client side is disabled by default pending further compatibility testing with networks and systems. -- Forwarded TCP ports connect asynchronously and retry with other available - addresses (IPv4 versus IPv6, round robin IPs) +- Increase maximum command length to 9000 bytes - Free memory before exiting, patch from Thorsten Horstmann. Useful for Dropbear ports to embedded systems and for checking memory leaks - with valgrind. Only partially implemented for client side. + with valgrind. Only partially implemented for dbclient. + This is disabled by default, enable with DROPBEAR_CLEANUP in sysoptions.h -- Fix all compile warnings, patch from Gaël Portay - (note that configure with -Werror may not be successful on some platforms - such as OS X and some configuration options may result in unused variable - warnings) +- DROPBEAR_DEFAULT_CLI_AUTHKEY setting now always prepends home directory unless + there is a leading slash (~ isn't treated specially) - Fix small ECC memory leaks @@ -29,10 +37,13 @@ - Fix pre-authentication timeout when waiting for client SSH-2.0 banner, thanks to CL Ouyang -- Increase maximum command size to 9000 bytes +- Fix null pointer crash with restrictions in authorized_keys without a command, patch from + Guilhem Moulin -- DROPBEAR_DEFAULT_CLI_AUTHKEY setting now always prepends home directory unless - there is a leading slash (~ isn't treated specially) +- Ensure authentication timeout is handled while reading the initial banner, + thanks to CL Ouyang for finding it. + +- Fix null pointer crash when handling bad ECC keys. Found by afl-fuzz 2015.67 - Wednesday 28 January 2015 diff -r 624fc24cfae5 -r d734fe76b72f circbuffer.c --- a/circbuffer.c Fri Aug 07 21:26:03 2015 +0800 +++ b/circbuffer.c Mon Nov 23 23:04:48 2015 +0800 @@ -37,9 +37,8 @@ } cbuf = (circbuffer*)m_malloc(sizeof(circbuffer)); - if (size > 0) { - cbuf->data = (unsigned char*)m_malloc(size); - } + /* data is malloced on first write */ + cbuf->data = NULL; cbuf->used = 0; cbuf->readpos = 0; cbuf->writepos = 0; @@ -50,8 +49,10 @@ void cbuf_free(circbuffer * cbuf) { - m_burn(cbuf->data, cbuf->size); - m_free(cbuf->data); + if (cbuf->data) { + m_burn(cbuf->data, cbuf->size); + m_free(cbuf->data); + } m_free(cbuf); } @@ -106,6 +107,11 @@ dropbear_exit("Bad cbuf write"); } + if (!cbuf->data) { + /* lazy allocation */ + cbuf->data = (unsigned char*)m_malloc(cbuf->size); + } + return &cbuf->data[cbuf->writepos]; } diff -r 624fc24cfae5 -r d734fe76b72f cli-auth.c --- a/cli-auth.c Fri Aug 07 21:26:03 2015 +0800 +++ b/cli-auth.c Mon Nov 23 23:04:48 2015 +0800 @@ -324,6 +324,7 @@ return DROPBEAR_FAILURE; } +#if defined(ENABLE_CLI_PASSWORD_AUTH) || defined(ENABLE_CLI_INTERACT_AUTH) /* A helper for getpass() that exits if the user cancels. The returned * password is statically allocated by getpass() */ char* getpass_or_cancel(char* prompt) @@ -347,3 +348,4 @@ } return password; } +#endif diff -r 624fc24cfae5 -r d734fe76b72f cli-runopts.c --- a/cli-runopts.c Fri Aug 07 21:26:03 2015 +0800 +++ b/cli-runopts.c Mon Nov 23 23:04:48 2015 +0800 @@ -105,25 +105,30 @@ void cli_getopts(int argc, char ** argv) { unsigned int i, j; char ** next = 0; - unsigned int cmdlen; + enum { #ifdef ENABLE_CLI_PUBKEY_AUTH - int nextiskey = 0; /* A flag if the next argument is a keyfile */ + OPT_AUTHKEY, #endif #ifdef ENABLE_CLI_LOCALTCPFWD - int nextislocal = 0; + OPT_LOCALTCPFWD, #endif #ifdef ENABLE_CLI_REMOTETCPFWD - int nextisremote = 0; + OPT_REMOTETCPFWD, #endif #ifdef ENABLE_CLI_NETCAT - int nextisnetcat = 0; + OPT_NETCAT, #endif + /* a flag (no arg) if 'next' is NULL, a string-valued option otherwise */ + OPT_OTHER + } opt; + unsigned int cmdlen; char* dummy = NULL; /* Not used for anything real */ char* recv_window_arg = NULL; char* keepalive_arg = NULL; char* idle_timeout_arg = NULL; char *host_arg = NULL; + char c; /* see printhelp() for options */ cli_opts.progname = argv[0]; @@ -172,54 +177,23 @@ fill_own_user(); - /* Iterate all the arguments */ for (i = 1; i < (unsigned int)argc; i++) { -#ifdef ENABLE_CLI_PUBKEY_AUTH - if (nextiskey) { - /* Load a hostkey since the previous argument was "-i" */ - loadidentityfile(argv[i], 1); - nextiskey = 0; - continue; - } -#endif -#ifdef ENABLE_CLI_REMOTETCPFWD - if (nextisremote) { - TRACE(("nextisremote true")) - addforward(argv[i], cli_opts.remotefwds); - nextisremote = 0; - continue; - } -#endif -#ifdef ENABLE_CLI_LOCALTCPFWD - if (nextislocal) { - TRACE(("nextislocal true")) - addforward(argv[i], cli_opts.localfwds); - nextislocal = 0; - continue; - } -#endif -#ifdef ENABLE_CLI_NETCAT - if (nextisnetcat) { - TRACE(("nextisnetcat true")) - add_netcat(argv[i]); - nextisnetcat = 0; - continue; - } -#endif - if (next) { - /* The previous flag set a value to assign */ - *next = argv[i]; - if (*next == NULL) { - dropbear_exit("Invalid null argument"); + /* Handle non-flag arguments such as hostname or commands for the remote host */ + if (argv[i][0] != '-') + { + if (host_arg == NULL) { + host_arg = argv[i]; + continue; } - next = NULL; - continue; + /* Commands to pass to the remote host. No more flag handling, + commands are consumed below */ + break; } - if (argv[i][0] == '-') { - /* A flag *waves* */ - - switch (argv[i][1]) { + /* Begins with '-' */ + opt = OPT_OTHER; + for (j = 1; (c = argv[i][j]) != '\0' && !next && opt == OPT_OTHER; j++) { + switch (c) { case 'y': /* always accept the remote hostkey */ if (cli_opts.always_accept_key) { /* twice means no checking at all */ @@ -232,12 +206,7 @@ break; #ifdef ENABLE_CLI_PUBKEY_AUTH case 'i': /* an identityfile */ - /* Keep scp happy when it changes "-i file" to "-ifile" */ - if (strlen(argv[i]) > 2) { - loadidentityfile(&argv[i][2], 1); - } else { - nextiskey = 1; - } + opt = OPT_AUTHKEY; break; #endif case 't': /* we want a pty */ @@ -257,7 +226,7 @@ break; #ifdef ENABLE_CLI_LOCALTCPFWD case 'L': - nextislocal = 1; + opt = OPT_LOCALTCPFWD; break; case 'g': opts.listen_fwd_all = 1; @@ -265,12 +234,12 @@ #endif #ifdef ENABLE_CLI_REMOTETCPFWD case 'R': - nextisremote = 1; + opt = OPT_REMOTETCPFWD; break; #endif #ifdef ENABLE_CLI_NETCAT case 'B': - nextisnetcat = 1; + opt = OPT_NETCAT; break; #endif #ifdef ENABLE_CLI_PROXYCMD @@ -336,50 +305,85 @@ case 'b': next = &dummy; default: - fprintf(stderr, - "WARNING: Ignoring unknown argument '%s'\n", argv[i]); + fprintf(stderr, + "WARNING: Ignoring unknown option -%c\n", c); break; } /* Switch */ - - /* Now we handle args where they might be "-luser" (no spaces)*/ - if (next && strlen(argv[i]) > 2) { - *next = &argv[i][2]; - next = NULL; - } + } - continue; /* next argument */ + if (!next && opt == OPT_OTHER) /* got a flag */ + continue; - } else { - TRACE(("non-flag arg: '%s'", argv[i])) - - /* Either the hostname or commands */ - - if (host_arg == NULL) { - host_arg = argv[i]; - } else { + if (c == '\0') { + i++; + j = 0; + if (!argv[i]) + dropbear_exit("Missing argument"); + } - /* this is part of the commands to send - after this we - * don't parse any more options, and flags are sent as the - * command */ - cmdlen = 0; - for (j = i; j < (unsigned int)argc; j++) { - cmdlen += strlen(argv[j]) + 1; /* +1 for spaces */ - } - /* Allocate the space */ - cli_opts.cmd = (char*)m_malloc(cmdlen); - cli_opts.cmd[0] = '\0'; +#ifdef ENABLE_CLI_PUBKEY_AUTH + if (opt == OPT_AUTHKEY) { + TRACE(("opt authkey")) + loadidentityfile(&argv[i][j], 1); + } + else +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + if (opt == OPT_REMOTETCPFWD) { + TRACE(("opt remotetcpfwd")) + addforward(&argv[i][j], cli_opts.remotefwds); + } + else +#endif +#ifdef ENABLE_CLI_LOCALTCPFWD + if (opt == OPT_LOCALTCPFWD) { + TRACE(("opt localtcpfwd")) + addforward(&argv[i][j], cli_opts.localfwds); + } + else +#endif +#ifdef ENABLE_CLI_NETCAT + if (opt == OPT_NETCAT) { + TRACE(("opt netcat")) + add_netcat(&argv[i][j]); + } + else +#endif + if (next) { + /* The previous flag set a value to assign */ + *next = &argv[i][j]; + if (*next == NULL) + dropbear_exit("Invalid null argument"); + next = NULL; + } + } - /* Append all the bits */ - for (j = i; j < (unsigned int)argc; j++) { - strlcat(cli_opts.cmd, argv[j], cmdlen); - strlcat(cli_opts.cmd, " ", cmdlen); - } - /* It'll be null-terminated here */ + /* Done with options/flags; now handle the hostname (which may not + * start with a hyphen) and optional command */ + + if (host_arg == NULL) { /* missing hostname */ + printhelp(); + exit(EXIT_FAILURE); + } + TRACE(("host is: %s", host_arg)) - /* We've eaten all the options and flags */ - break; - } + if (i < (unsigned int)argc) { + /* Build the command to send */ + cmdlen = 0; + for (j = i; j < (unsigned int)argc; j++) + cmdlen += strlen(argv[j]) + 1; /* +1 for spaces */ + + /* Allocate the space */ + cli_opts.cmd = (char*)m_malloc(cmdlen); + cli_opts.cmd[0] = '\0'; + + /* Append all the bits */ + for (j = i; j < (unsigned int)argc; j++) { + strlcat(cli_opts.cmd, argv[j], cmdlen); + strlcat(cli_opts.cmd, " ", cmdlen); } + /* It'll be null-terminated here */ + TRACE(("cmd is: %s", cli_opts.cmd)) } /* And now a few sanity checks and setup */ @@ -388,11 +392,6 @@ parse_ciphers_macs(); #endif - if (host_arg == NULL) { - printhelp(); - exit(EXIT_FAILURE); - } - #ifdef ENABLE_CLI_PROXYCMD if (cli_opts.proxycmd) { /* To match the common path of m_freeing it */ diff -r 624fc24cfae5 -r d734fe76b72f common-channel.c --- a/common-channel.c Fri Aug 07 21:26:03 2015 +0800 +++ b/common-channel.c Mon Nov 23 23:04:48 2015 +0800 @@ -42,7 +42,7 @@ static void send_msg_channel_open_confirmation(struct Channel* channel, unsigned int recvwindow, unsigned int recvmaxpacket); -static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf, +static int writechannel(struct Channel* channel, int fd, circbuffer *cbuf, const unsigned char *moredata, unsigned int *morelen); static void send_msg_channel_window_adjust(struct Channel *channel, unsigned int incr); @@ -100,15 +100,6 @@ 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 @@ -167,8 +158,8 @@ newchan->await_open = 0; newchan->flushing = 0; - newchan->writebuf = cbuf_new(0); /* resized later by chan_initwritebuf */ - newchan->recvwindow = 0; + newchan->writebuf = cbuf_new(opts.recv_window); + newchan->recvwindow = opts.recv_window; newchan->extrabuf = NULL; /* The user code can set it up */ newchan->recvdonelen = 0; @@ -256,7 +247,6 @@ if (ses.channel_signal_pending) { /* SIGCHLD can change channel state for server sessions */ do_check_close = 1; - ses.channel_signal_pending = 0; } /* handle any channel closing etc */ @@ -265,6 +255,8 @@ } } + ses.channel_signal_pending = 0; + #ifdef USING_LISTENERS handle_listeners(readfds); #endif @@ -378,7 +370,6 @@ { channel->readfd = channel->writefd = sock; channel->conn_pending = NULL; - chan_initwritebuf(channel); send_msg_channel_open_confirmation(channel, channel->recvwindow, channel->recvmaxpacket); TRACE(("leave channel_connect_done: success")) @@ -435,7 +426,7 @@ } #ifndef HAVE_WRITEV -static void writechannel_fallback(struct Channel* channel, int fd, circbuffer *cbuf, +static int writechannel_fallback(struct Channel* channel, int fd, circbuffer *cbuf, const unsigned char *UNUSED(moredata), unsigned int *morelen) { unsigned char *circ_p1, *circ_p2; @@ -454,23 +445,24 @@ if (errno != EINTR && errno != EAGAIN) { TRACE(("channel IO write error fd %d %s", fd, strerror(errno))) close_chan_fd(channel, fd, SHUT_WR); + return DROPBEAR_FAILURE; } - } else { - cbuf_incrread(cbuf, written); - channel->recvdonelen += written; } + cbuf_incrread(cbuf, written); + channel->recvdonelen += written; + return DROPBEAR_SUCCESS; } #endif /* !HAVE_WRITEV */ #ifdef HAVE_WRITEV -static void writechannel_writev(struct Channel* channel, int fd, circbuffer *cbuf, +static int writechannel_writev(struct Channel* channel, int fd, circbuffer *cbuf, const unsigned char *moredata, unsigned int *morelen) { struct iovec iov[3]; unsigned char *circ_p1, *circ_p2; unsigned int circ_len1, circ_len2; int io_count = 0; - + int cbuf_written; ssize_t written; cbuf_readptrs(cbuf, &circ_p1, &circ_len1, &circ_p2, &circ_len2); @@ -502,7 +494,7 @@ From common_recv_msg_channel_data() then channelio(). The second call may not have any data to write, so we just return. */ TRACE(("leave writechannel, no data")) - return; + return DROPBEAR_SUCCESS; } if (morelen) { @@ -516,29 +508,32 @@ if (errno != EINTR && errno != EAGAIN) { TRACE(("channel IO write error fd %d %s", fd, strerror(errno))) close_chan_fd(channel, fd, SHUT_WR); + return DROPBEAR_FAILURE; } - } else { - int cbuf_written = MIN(circ_len1+circ_len2, (unsigned int)written); - cbuf_incrread(cbuf, cbuf_written); - if (morelen) { - *morelen = written - cbuf_written; - } - channel->recvdonelen += written; + } + + cbuf_written = MIN(circ_len1+circ_len2, (unsigned int)written); + cbuf_incrread(cbuf, cbuf_written); + if (morelen) { + *morelen = written - cbuf_written; } - + channel->recvdonelen += written; + return DROPBEAR_SUCCESS; } #endif /* HAVE_WRITEV */ /* Called to write data out to the local side of the channel. Writes the circular buffer contents and also the "moredata" buffer - if not null. Will ignore EAGAIN */ -static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf, + if not null. Will ignore EAGAIN. + Returns DROPBEAR_FAILURE if writing to fd had an error and the channel is being closed, DROPBEAR_SUCCESS otherwise */ +static int writechannel(struct Channel* channel, int fd, circbuffer *cbuf, const unsigned char *moredata, unsigned int *morelen) { + int ret = DROPBEAR_SUCCESS; TRACE(("enter writechannel fd %d", fd)) #ifdef HAVE_WRITEV - writechannel_writev(channel, fd, cbuf, moredata, morelen); + ret = writechannel_writev(channel, fd, cbuf, moredata, morelen); #else - writechannel_fallback(channel, fd, cbuf, moredata, morelen); + ret = writechannel_fallback(channel, fd, cbuf, moredata, morelen); #endif /* Window adjust handling */ @@ -554,6 +549,7 @@ channel->recvwindow <= cbuf_getavail(channel->extrabuf)); TRACE(("leave writechannel")) + return ret; } @@ -828,6 +824,7 @@ unsigned int buflen; unsigned int len; unsigned int consumed; + int res; TRACE(("enter recv_msg_channel_data")) @@ -860,7 +857,7 @@ /* Attempt to write the data immediately without having to put it in the circular buffer */ consumed = datalen; - writechannel(channel, fd, cbuf, buf_getptr(ses.payload, datalen), &consumed); + res = writechannel(channel, fd, cbuf, buf_getptr(ses.payload, datalen), &consumed); datalen -= consumed; buf_incrpos(ses.payload, consumed); @@ -868,17 +865,20 @@ /* We may have to run throught twice, if the buffer wraps around. Can't * just "leave it for next time" like with writechannel, since this - * is payload data */ - len = datalen; - while (len > 0) { - buflen = cbuf_writelen(cbuf); - buflen = MIN(buflen, len); + * is payload data. + * If the writechannel() failed then remaining data is discarded */ + if (res == DROPBEAR_SUCCESS) { + len = datalen; + while (len > 0) { + buflen = cbuf_writelen(cbuf); + buflen = MIN(buflen, len); - memcpy(cbuf_writeptr(cbuf, buflen), - buf_getptr(ses.payload, buflen), buflen); - cbuf_incrwrite(cbuf, buflen); - buf_incrpos(ses.payload, buflen); - len -= buflen; + memcpy(cbuf_writeptr(cbuf, buflen), + buf_getptr(ses.payload, buflen), buflen); + cbuf_incrwrite(cbuf, buflen); + buf_incrpos(ses.payload, buflen); + len -= buflen; + } } TRACE(("leave recv_msg_channel_data")) @@ -970,6 +970,7 @@ if (channel == NULL) { TRACE(("newchannel returned NULL")) + errtype = SSH_OPEN_RESOURCE_SHORTAGE; goto failure; } @@ -991,8 +992,6 @@ channel->prio = DROPBEAR_CHANNEL_PRIO_BULK; } - chan_initwritebuf(channel); - /* success */ send_msg_channel_open_confirmation(channel, channel->recvwindow, channel->recvmaxpacket); @@ -1135,7 +1134,6 @@ /* 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 624fc24cfae5 -r d734fe76b72f common-session.c --- a/common-session.c Fri Aug 07 21:26:03 2015 +0800 +++ b/common-session.c Mon Nov 23 23:04:48 2015 +0800 @@ -159,6 +159,16 @@ FD_ZERO(&readfd); dropbear_assert(ses.payload == NULL); + /* We get woken up when signal handlers write to this pipe. + SIGCHLD in svr-chansession is the only one currently. */ + FD_SET(ses.signal_pipe[0], &readfd); + + /* set up for channels which can be read/written */ + setchannelfds(&readfd, &writefd, writequeue_has_space); + + /* Pending connections to test */ + set_connect_fds(&writefd); + /* We delay reading from the input socket during initial setup until after we have written out our initial KEXINIT packet (empty writequeue). This means our initial packet can be in-flight while we're doing a blocking @@ -170,19 +180,12 @@ && writequeue_has_space) { FD_SET(ses.sock_in, &readfd); } + + /* Ordering is important, this test must occur after any other function + might have queued packets (such as connection handlers) */ if (ses.sock_out != -1 && !isempty(&ses.writequeue)) { FD_SET(ses.sock_out, &writefd); } - - /* We get woken up when signal handlers write to this pipe. - SIGCHLD in svr-chansession is the only one currently. */ - FD_SET(ses.signal_pipe[0], &readfd); - - /* set up for channels which can be read/written */ - setchannelfds(&readfd, &writefd, writequeue_has_space); - - /* Pending connections to test */ - set_connect_fds(&writefd); val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); diff -r 624fc24cfae5 -r d734fe76b72f configure.ac --- a/configure.ac Fri Aug 07 21:26:03 2015 +0800 +++ b/configure.ac Mon Nov 23 23:04:48 2015 +0800 @@ -632,7 +632,7 @@ AC_PROG_GCC_TRADITIONAL AC_FUNC_MEMCMP AC_FUNC_SELECT_ARGTYPES -AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork writev]) +AC_CHECK_FUNCS([dup2 getpass getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork writev crypt]) AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME)) @@ -719,10 +719,21 @@ AC_MSG_NOTICE() if test $BUNDLED_LIBTOM = 1 ; then -AC_MSG_NOTICE(Using bundled libtomcrypt and libtommath) +AC_MSG_NOTICE([Using bundled libtomcrypt and libtommath]) else -AC_MSG_NOTICE(Using system libtomcrypt and libtommath) +AC_MSG_NOTICE([Using system libtomcrypt and libtommath]) +fi + + +if test "x$ac_cv_func_getpass" != xyes; then +AC_MSG_NOTICE() +AC_MSG_NOTICE([getpass() not available, dbclient will only have public-key authentication]) +fi + +if test "x$ac_cv_func_crypt" != xyes; then +AC_MSG_NOTICE() +AC_MSG_NOTICE([crypt() not available, dropbear server will not have password authentication]) fi AC_MSG_NOTICE() -AC_MSG_NOTICE(Now edit options.h to choose features.) +AC_MSG_NOTICE([Now edit options.h to choose features.]) diff -r 624fc24cfae5 -r d734fe76b72f dbclient.1 --- a/dbclient.1 Fri Aug 07 21:26:03 2015 +0800 +++ b/dbclient.1 Mon Nov 23 23:04:48 2015 +0800 @@ -3,13 +3,14 @@ dbclient \- lightweight SSH client .SH SYNOPSIS .B dbclient -[\-Tt] [\-p +[flag arguments] [\-p .I port\fR] [\-i .I id\fR] [\-L -.I l\fR:\fIh\fR:\fIr\fR] [\-R -.I l\fR:\fIh\fR:\fIr\fR] [\-l +.I l\fR:\fIh\fR:\fIp\fR] [\-R +.I l\fR:\fIh\fR:\fIp\fR] [\-l .IR user ] .I host +.RI [ more\ flags ] .RI [ command ] .B dbclient @@ -22,6 +23,13 @@ is a small SSH client .SH OPTIONS .TP +.TP +.B command +A command to run on the remote host. This will normally be run by the remote host +using the user's shell. The command begins at the first hyphen argument after the +host argument. If no command is specified an interactive terminal will be opened +(see -t and -T). +.TP .B \-p \fIport Connect to .I port @@ -60,10 +68,12 @@ on the remote host. .TP .B \-t -Allocate a PTY. +Allocate a PTY. This is the default when no command is given, it gives a full +interactive remote session. The main effect is that keystrokes are sent remotely +immediately as opposed to local line-based editing. .TP .B \-T -Don't allocate a PTY. +Don't allocate a PTY. This is the default a command is given. See -t. .TP .B \-N Don't request a remote shell or run any commands. Any command arguments are ignored. @@ -129,7 +139,7 @@ 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 the final destination will not see anything other than the encrypted SSH stream. -A port for a host can be specified with a hash (eg matt@martello^44 ). +A port for a host can be specified with a caret (eg matt@martello^44 ). This syntax can also be used with scp or rsync (specifying dbclient as the ssh/rsh command). A file can be "bounced" through multiple SSH hops, eg @@ -157,6 +167,10 @@ on standard output. This program will only be used if either DISPLAY is set and standard input is not a TTY, or the environment variable SSH_ASKPASS_ALWAYS is set. +.SH NOTES +If compiled with zlib support and if the server supports it, dbclient will +always use compression. + .SH AUTHOR Matt Johnston (matt@ucc.asn.au). .br diff -r 624fc24cfae5 -r d734fe76b72f debian/changelog --- a/debian/changelog Fri Aug 07 21:26:03 2015 +0800 +++ b/debian/changelog Mon Nov 23 23:04:48 2015 +0800 @@ -1,3 +1,9 @@ +dropbear (2015.68-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston Sat, 8 Aug 2015 22:52:58 +0800 + dropbear (2015.67-0.1) unstable; urgency=low * New upstream release. diff -r 624fc24cfae5 -r d734fe76b72f dropbear.8 --- a/dropbear.8 Fri Aug 07 21:26:03 2015 +0800 +++ b/dropbear.8 Mon Nov 23 23:04:48 2015 +0800 @@ -3,7 +3,7 @@ dropbear \- lightweight SSH server .SH SYNOPSIS .B dropbear -[\-RFEmwsgjki] [\-b +[flag arguments] [\-b .I banner\fR] [\-r .I hostkeyfile\fR] [\-p @@ -100,7 +100,8 @@ .TP Authorized Keys -~/.ssh/authorized_keys can be set up to allow remote login with a RSA or DSS +~/.ssh/authorized_keys can be set up to allow remote login with a RSA, +ECDSA, or DSS key. Each line is of the form .TP [restrictions] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIgAsp... [comment] @@ -139,7 +140,7 @@ Host key files are read at startup from a standard location, by default /etc/dropbear/dropbear_dss_host_key, /etc/dropbear/dropbear_rsa_host_key, and -/etc/dropbear/dropbear-ecdsa_host_key +/etc/dropbear/dropbear_ecdsa_host_key or specified on the commandline with -r. These are of the form generated by dropbearkey. The -R option can be used to automatically generate keys in the default location - keys will be generated after startup when the first diff -r 624fc24cfae5 -r d734fe76b72f dropbearkey.1 --- a/dropbearkey.1 Fri Aug 07 21:26:03 2015 +0800 +++ b/dropbearkey.1 Mon Nov 23 23:04:48 2015 +0800 @@ -9,6 +9,7 @@ .I file [\-s .IR bits ] +[\-y] .SH DESCRIPTION .B dropbearkey generates a @@ -39,12 +40,19 @@ Set the key size to .I bits bits, should be multiple of 8 (optional). +.TP +.B \-y +Just print the publickey and fingerprint for the private key in \fIfile\fR. .SH NOTES The program dropbearconvert(1) can be used to convert between Dropbear and OpenSSH key formats. .P Dropbear does not support encrypted keys. .SH EXAMPLE +generate a host-key: # dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key + +extract a public key suitable for authorized_keys from private key: + # dropbearkey -y -f id_rsa | grep "^ssh-rsa " >> authorized_keys .SH AUTHOR Matt Johnston (matt@ucc.asn.au). .br diff -r 624fc24cfae5 -r d734fe76b72f netio.c --- a/netio.c Fri Aug 07 21:26:03 2015 +0800 +++ b/netio.c Mon Nov 23 23:04:48 2015 +0800 @@ -174,28 +174,26 @@ void set_connect_fds(fd_set *writefd) { m_list_elem *iter; - TRACE(("enter handle_connect_fds")) - for (iter = ses.conn_pending.first; iter; iter = iter->next) { + TRACE(("enter set_connect_fds")) + iter = ses.conn_pending.first; + while (iter) { + m_list_elem *next_iter = iter->next; struct dropbear_progress_connection *c = iter->item; /* Set one going */ - while (c->res_iter && c->sock < 0) - { + while (c->res_iter && c->sock < 0) { connect_try_next(c); } if (c->sock >= 0) { FD_SET(c->sock, writefd); } else { - m_list_elem *remove_iter; /* Final failure */ if (!c->errstring) { c->errstring = m_strdup("unexpected failure"); } c->cb(DROPBEAR_FAILURE, -1, c->cb_data, c->errstring); - /* Safely remove without invalidating iter */ - remove_iter = iter; - iter = iter->prev; - remove_connect(c, remove_iter); + remove_connect(c, iter); } + iter = next_iter; } } diff -r 624fc24cfae5 -r d734fe76b72f options.h --- a/options.h Fri Aug 07 21:26:03 2015 +0800 +++ b/options.h Mon Nov 23 23:04:48 2015 +0800 @@ -206,7 +206,10 @@ * PAM challenge/response. * You can't enable both PASSWORD and PAM. */ +/* This requires crypt() */ +#ifdef HAVE_CRYPT #define ENABLE_SVR_PASSWORD_AUTH +#endif /* PAM requires ./configure --enable-pam */ /*#define ENABLE_SVR_PAM_AUTH */ #define ENABLE_SVR_PUBKEY_AUTH @@ -217,9 +220,12 @@ #define ENABLE_SVR_PUBKEY_OPTIONS #endif +/* This requires getpass. */ +#ifdef HAVE_GETPASS #define ENABLE_CLI_PASSWORD_AUTH +#define ENABLE_CLI_INTERACT_AUTH +#endif #define ENABLE_CLI_PUBKEY_AUTH -#define ENABLE_CLI_INTERACT_AUTH /* A default argument for dbclient -i . Homedir is prepended unless path begins with / */ diff -r 624fc24cfae5 -r d734fe76b72f scp.c --- a/scp.c Fri Aug 07 21:26:03 2015 +0800 +++ b/scp.c Mon Nov 23 23:04:48 2015 +0800 @@ -992,7 +992,7 @@ continue; } omode = mode; - mode |= S_IWRITE; + mode |= S_IWUSR; if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { bad: run_err("%s: %s", np, strerror(errno)); continue; diff -r 624fc24cfae5 -r d734fe76b72f session.h --- a/session.h Fri Aug 07 21:26:03 2015 +0800 +++ b/session.h Mon Nov 23 23:04:48 2015 +0800 @@ -293,10 +293,9 @@ int interact_request_received; /* flag whether we've received an info request from the server for interactive auth.*/ - +#endif int cipher_none_after_auth; /* Set to 1 if the user requested "none" auth */ -#endif sign_key *lastprivkey; int retval; /* What the command exit status was - we emulate it */ diff -r 624fc24cfae5 -r d734fe76b72f svr-authpubkeyoptions.c --- a/svr-authpubkeyoptions.c Fri Aug 07 21:26:03 2015 +0800 +++ b/svr-authpubkeyoptions.c Mon Nov 23 23:04:48 2015 +0800 @@ -91,7 +91,7 @@ /* Set chansession command to the one forced * by any 'command' public key option. */ void svr_pubkey_set_forced_command(struct ChanSess *chansess) { - if (ses.authstate.pubkey_options) { + if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->forced_command) { if (chansess->cmd) { /* original_command takes ownership */ chansess->original_command = chansess->cmd; diff -r 624fc24cfae5 -r d734fe76b72f svr-chansession.c --- a/svr-chansession.c Fri Aug 07 21:26:03 2015 +0800 +++ b/svr-chansession.c Mon Nov 23 23:04:48 2015 +0800 @@ -814,7 +814,7 @@ login_free_entry(li); #ifdef DO_MOTD - if (svr_opts.domotd) { + if (svr_opts.domotd && !chansess->cmd) { /* don't show the motd if ~/.hushlogin exists */ /* 12 == strlen("/.hushlogin\0") */ diff -r 624fc24cfae5 -r d734fe76b72f svr-runopts.c --- a/svr-runopts.c Fri Aug 07 21:26:03 2015 +0800 +++ b/svr-runopts.c Mon Nov 23 23:04:48 2015 +0800 @@ -112,13 +112,14 @@ void svr_getopts(int argc, char ** argv) { - unsigned int i; + unsigned int i, j; char ** next = 0; int nextisport = 0; char* recv_window_arg = NULL; char* keepalive_arg = NULL; char* idle_timeout_arg = NULL; char* keyfile = NULL; + char c; /* see printhelp() for options */ @@ -168,28 +169,11 @@ #endif for (i = 1; i < (unsigned int)argc; i++) { - if (nextisport) { - addportandaddress(argv[i]); - nextisport = 0; - continue; - } - - if (next) { - *next = argv[i]; - if (*next == NULL) { - dropbear_exit("Invalid null argument"); - } - next = 0x00; + if (argv[i][0] != '-' || argv[i][1] == '\0') + dropbear_exit("Invalid argument: %s", argv[i]); - if (keyfile) { - addhostkey(keyfile); - keyfile = NULL; - } - continue; - } - - if (argv[i][0] == '-') { - switch (argv[i][1]) { + for (j = 1; (c = argv[i][j]) != '\0' && !next && !nextisport; j++) { + switch (c) { case 'b': next = &svr_opts.bannerfile; break; @@ -278,12 +262,39 @@ exit(EXIT_SUCCESS); break; default: - fprintf(stderr, "Unknown argument %s\n", argv[i]); + fprintf(stderr, "Invalid option -%c\n", c); printhelp(argv[0]); exit(EXIT_FAILURE); break; } } + + if (!next && !nextisport) + continue; + + if (c == '\0') { + i++; + j = 0; + if (!argv[i]) { + dropbear_exit("Missing argument"); + } + } + + if (nextisport) { + addportandaddress(&argv[i][j]); + nextisport = 0; + } else if (next) { + *next = &argv[i][j]; + if (*next == NULL) { + dropbear_exit("Invalid null argument"); + } + next = 0x00; + + if (keyfile) { + addhostkey(keyfile); + keyfile = NULL; + } + } } /* Set up listening ports */ diff -r 624fc24cfae5 -r d734fe76b72f sysoptions.h --- a/sysoptions.h Fri Aug 07 21:26:03 2015 +0800 +++ b/sysoptions.h Mon Nov 23 23:04:48 2015 +0800 @@ -4,7 +4,7 @@ *******************************************************************/ #ifndef DROPBEAR_VERSION -#define DROPBEAR_VERSION "2015.67" +#define DROPBEAR_VERSION "2015.68" #endif #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION @@ -150,7 +150,7 @@ RECV_WINDOWEXTEND bytes */ #define MAX_RECV_WINDOW (1024*1024) /* 1 MB should be enough */ -#define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11 +#define MAX_CHANNELS 1000 /* simple mem restriction, includes each tcp/x11 connection, so can't be _too_ small */ #define MAX_STRING_LEN (MAX(MAX_CMD_LEN, 2400)) /* Sun SSH needs 2400 for algos,