# HG changeset patch # User Matt Johnston # Date 1406298230 -28800 # Node ID 185b241a0b4a4dd495abf2af30f294611565283f # Parent 204dc7bd62aa6c545945f950bb88df0144672123# Parent 8664fea5072f7ddc7a825072c2e1543b18ed1963 merge diff -r 204dc7bd62aa -r 185b241a0b4a CHANGES --- a/CHANGES Sat Mar 08 21:02:02 2014 +0800 +++ b/CHANGES Fri Jul 25 22:23:50 2014 +0800 @@ -1,3 +1,29 @@ +2014.64 pending + +- Fix compiling with ECDSA and DSS disabled + +- Don't exit abruptly if too many outgoing packets are queued for writev(). Patch + thanks to Ronny Meeus + +- The -K keepalive option now behaves more like OpenSSH's "ServerAliveInterval". + If no response is received after 3 keepalives then the session is terminated. This + will close connections faster than waiting for a TCP timeout. + +- Rework TCP priority setting. New settings are + if (connecting || ptys || x11) tos = LOWDELAY + else if (tcp_forwards) tos = 0 + else tos = BULK + Thanks to Catalin Patulea for the suggestion. + +- Improve handling of many concurrent new TCP forwarded connections, should now + be able to handle as many as MAX_CHANNELS. Thanks to Eduardo Silva for reporting + and investigating it. + +- Make sure that exit messages from the client are printed, regression in 2013.57 + +- Use monotonic clock where available, timeouts won't be affected by system time + changes + 2014.63 - Wednesday 19 February 2014 - Fix ~. to terminate a client interactive session after waking a laptop diff -r 204dc7bd62aa -r 185b241a0b4a channel.h --- a/channel.h Sat Mar 08 21:02:02 2014 +0800 +++ b/channel.h Fri Jul 25 22:23:50 2014 +0800 @@ -29,14 +29,6 @@ #include "buffer.h" #include "circbuffer.h" -/* channel->type values */ -#define CHANNEL_ID_NONE 0 -#define CHANNEL_ID_SESSION 1 -#define CHANNEL_ID_X11 2 -#define CHANNEL_ID_AGENT 3 -#define CHANNEL_ID_TCPDIRECT 4 -#define CHANNEL_ID_TCPFORWARDED 5 - #define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED 1 #define SSH_OPEN_CONNECT_FAILED 2 #define SSH_OPEN_UNKNOWN_CHANNEL_TYPE 3 @@ -49,6 +41,13 @@ struct ChanType; +enum dropbear_channel_prio { + DROPBEAR_CHANNEL_PRIO_INTERACTIVE, /* pty shell, x11 */ + DROPBEAR_CHANNEL_PRIO_UNKNOWABLE, /* tcp - can't know what's being forwarded */ + DROPBEAR_CHANNEL_PRIO_BULK, /* the rest - probably scp or something */ + DROPBEAR_CHANNEL_PRIO_EARLY, /* channel is still being set up */ +}; + struct Channel { unsigned int index; /* the local channel index */ @@ -87,6 +86,8 @@ void (*read_mangler)(struct Channel*, unsigned char* bytes, int *len); const struct ChanType* type; + + enum dropbear_channel_prio prio; }; struct ChanType { @@ -97,7 +98,6 @@ int (*check_close)(struct Channel*); void (*reqhandler)(struct Channel*); void (*closehandler)(struct Channel*); - }; void chaninitialise(const struct ChanType *chantypes[]); @@ -129,4 +129,7 @@ void recv_msg_channel_open_failure(); #endif +void send_msg_request_success(); +void send_msg_request_failure(); + #endif /* _CHANNEL_H_ */ diff -r 204dc7bd62aa -r 185b241a0b4a cli-auth.c --- a/cli-auth.c Sat Mar 08 21:02:02 2014 +0800 +++ b/cli-auth.c Fri Jul 25 22:23:50 2014 +0800 @@ -174,11 +174,11 @@ the "none" auth request, and then a response to the immediate auth request. We need to be careful handling them. */ if (cli_ses.ignore_next_auth_response) { - TRACE(("ignore next response, state set to USERAUTH_REQ_SENT")) cli_ses.state = USERAUTH_REQ_SENT; + cli_ses.ignore_next_auth_response = 0; + TRACE(("leave recv_msg_userauth_failure, ignored response, state set to USERAUTH_REQ_SENT")); + return; } else { - cli_ses.state = USERAUTH_FAIL_RCVD; - cli_ses.lastauthtype = AUTH_TYPE_NONE; #ifdef ENABLE_CLI_PUBKEY_AUTH /* If it was a pubkey auth request, we should cross that key * off the list. */ @@ -197,10 +197,10 @@ cli_ses.auth_interact_failed = 1; } #endif + cli_ses.state = USERAUTH_FAIL_RCVD; + cli_ses.lastauthtype = AUTH_TYPE_NONE; } - cli_ses.ignore_next_auth_response = 0; - methods = buf_getstring(ses.payload, &methlen); partial = buf_getbool(ses.payload); diff -r 204dc7bd62aa -r 185b241a0b4a cli-chansession.c --- a/cli-chansession.c Sat Mar 08 21:02:02 2014 +0800 +++ b/cli-chansession.c Fri Jul 25 22:23:50 2014 +0800 @@ -41,7 +41,7 @@ static void send_chansess_pty_req(struct Channel *channel); static void send_chansess_shell_req(struct Channel *channel); static void cli_escape_handler(struct Channel *channel, unsigned char* buf, int *len); - +static int cli_init_netcat(struct Channel *channel); static void cli_tty_setup(); @@ -357,6 +357,11 @@ return 0; } +static int cli_init_netcat(struct Channel *channel) { + channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE; + return cli_init_stdpipe_sess(channel); +} + static int cli_initchansess(struct Channel *channel) { cli_init_stdpipe_sess(channel); @@ -369,8 +374,9 @@ if (cli_opts.wantpty) { send_chansess_pty_req(channel); + channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE; } else { - set_sock_priority(ses.sock_out, DROPBEAR_PRIO_BULK); + channel->prio = DROPBEAR_CHANNEL_PRIO_BULK; } send_chansess_shell_req(channel); @@ -389,7 +395,7 @@ static const struct ChanType cli_chan_netcat = { 0, /* sepfds */ "direct-tcpip", - cli_init_stdpipe_sess, /* inithandler */ + cli_init_netcat, /* inithandler */ NULL, NULL, cli_closechansess diff -r 204dc7bd62aa -r 185b241a0b4a cli-main.c --- a/cli-main.c Sat Mar 08 21:02:02 2014 +0800 +++ b/cli-main.c Fri Jul 25 22:23:50 2014 +0800 @@ -75,9 +75,6 @@ int sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 0, &error); sock_in = sock_out = sock; - if (cli_opts.wantpty) { - set_sock_priority(sock, DROPBEAR_PRIO_LOWDELAY); - } } if (sock_in < 0) { @@ -107,9 +104,10 @@ /* Do the cleanup first, since then the terminal will be reset */ session_cleanup(); + /* Avoid printing onwards from terminal cruft */ + fprintf(stderr, "\n"); _dropbear_log(LOG_INFO, fmtbuf, param); - exit(exitcode); } @@ -121,7 +119,7 @@ vsnprintf(printbuf, sizeof(printbuf), format, param); fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf); - + fflush(stderr); } static void exec_proxy_cmd(void *user_data_cmd) { diff -r 204dc7bd62aa -r 185b241a0b4a cli-runopts.c --- a/cli-runopts.c Sat Mar 08 21:02:02 2014 +0800 +++ b/cli-runopts.c Fri Jul 25 22:23:50 2014 +0800 @@ -163,6 +163,8 @@ opts.ipv6 = 1; */ opts.recv_window = DEFAULT_RECV_WINDOW; + opts.keepalive_secs = DEFAULT_KEEPALIVE; + opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT; fill_own_user(); diff -r 204dc7bd62aa -r 185b241a0b4a cli-session.c --- a/cli-session.c Sat Mar 08 21:02:02 2014 +0800 +++ b/cli-session.c Fri Jul 25 22:23:50 2014 +0800 @@ -44,6 +44,7 @@ static void cli_finished(); static void recv_msg_service_accept(void); static void cli_session_cleanup(void); +static void recv_msg_global_request_cli(void); struct clientsession cli_ses; /* GLOBAL */ @@ -68,6 +69,7 @@ {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, {SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */ {SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */ + {SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_cli}, #ifdef ENABLE_CLI_REMOTETCPFWD {SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */ {SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */ @@ -366,3 +368,9 @@ /* Null terminate */ dirtytext[j] = '\0'; } + +static void recv_msg_global_request_cli(void) { + TRACE(("recv_msg_global_request_cli")) + /* Send a proper rejection */ + send_msg_request_failure(); +} diff -r 204dc7bd62aa -r 185b241a0b4a cli-tcpfwd.c --- a/cli-tcpfwd.c Sat Mar 08 21:02:02 2014 +0800 +++ b/cli-tcpfwd.c Fri Jul 25 22:23:50 2014 +0800 @@ -52,7 +52,7 @@ static const struct ChanType cli_chan_tcplocal = { 1, /* sepfds */ "direct-tcpip", - NULL, + tcp_prio_inithandler, NULL, NULL, NULL @@ -267,6 +267,8 @@ * progress succeeds */ channel->writefd = sock; channel->initconn = 1; + + channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE; err = SSH_OPEN_IN_PROGRESS; diff -r 204dc7bd62aa -r 185b241a0b4a common-channel.c --- a/common-channel.c Sat Mar 08 21:02:02 2014 +0800 +++ b/common-channel.c Fri Jul 25 22:23:50 2014 +0800 @@ -174,6 +174,8 @@ newchan->recvdonelen = 0; newchan->recvmaxpacket = RECV_MAX_CHANNEL_DATA_LEN; + newchan->prio = DROPBEAR_CHANNEL_PRIO_EARLY; /* inithandler sets it */ + ses.channels[i] = newchan; ses.chancount++; @@ -208,11 +210,14 @@ /* Iterate through the channels, performing IO if available */ void channelio(fd_set *readfds, fd_set *writefds) { + /* Listeners such as TCP, X11, agent-auth */ struct Channel *channel; unsigned int i; /* foreach channel */ for (i = 0; i < ses.chansize; i++) { + /* Close checking only needs to occur for channels that had IO events */ + int do_check_close = 0; channel = ses.channels[i]; if (channel == NULL) { @@ -224,6 +229,7 @@ if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) { TRACE(("send normal readfd")) send_msg_channel_data(channel, 0); + do_check_close = 1; } /* read stderr data and send it over the wire */ @@ -231,6 +237,7 @@ && FD_ISSET(channel->errfd, readfds)) { TRACE(("send normal errfd")) send_msg_channel_data(channel, 1); + do_check_close = 1; } /* write to program/pipe stdin */ @@ -242,20 +249,22 @@ check_in_progress(), as it may be NULL */ } writechannel(channel, channel->writefd, channel->writebuf); + do_check_close = 1; } /* stderr for client mode */ if (ERRFD_IS_WRITE(channel) && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) { writechannel(channel, channel->errfd, channel->extrabuf); + do_check_close = 1; } /* handle any channel closing etc */ - check_close(channel); - + if (do_check_close) { + check_close(channel); + } } - /* Listeners such as TCP, X11, agent-auth */ #ifdef USING_LISTENERS handle_listeners(readfds); #endif @@ -567,14 +576,16 @@ } - /* close the FDs in case they haven't been done - * yet (they might have been shutdown etc) */ - TRACE(("CLOSE writefd %d", channel->writefd)) - close(channel->writefd); - TRACE(("CLOSE readfd %d", channel->readfd)) - close(channel->readfd); - TRACE(("CLOSE errfd %d", channel->errfd)) - close(channel->errfd); + if (IS_DROPBEAR_SERVER || (channel->writefd != STDOUT_FILENO)) { + /* close the FDs in case they haven't been done + * yet (they might have been shutdown etc) */ + TRACE(("CLOSE writefd %d", channel->writefd)) + close(channel->writefd); + TRACE(("CLOSE readfd %d", channel->readfd)) + close(channel->readfd); + TRACE(("CLOSE errfd %d", channel->errfd)) + close(channel->errfd); + } if (!channel->close_handler_done && channel->type->closehandler) { @@ -586,6 +597,8 @@ m_free(channel); ses.chancount--; + update_channel_prio(); + TRACE(("leave remove_channel")) } @@ -876,6 +889,10 @@ } } + if (channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) { + channel->prio = DROPBEAR_CHANNEL_PRIO_BULK; + } + chan_initwritebuf(channel); /* success */ @@ -889,6 +906,8 @@ cleanup: m_free(type); + + update_channel_prio(); TRACE(("leave recv_msg_channel_open")) } @@ -1004,7 +1023,7 @@ * for X11, agent, tcp forwarding, and should be filled with channel-specific * options, with the calling function calling encrypt_packet() after * completion. It is mandatory for the caller to encrypt_packet() if - * DROPBEAR_SUCCESS is returned */ + * a channel is returned. NULL is returned on failure. */ int send_msg_channel_open_init(int fd, const struct ChanType *type) { struct Channel* chan; @@ -1073,6 +1092,10 @@ } } + if (channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) { + channel->prio = DROPBEAR_CHANNEL_PRIO_BULK; + } + update_channel_prio(); TRACE(("leave recv_msg_channel_open_confirmation")) } @@ -1092,3 +1115,15 @@ remove_channel(channel); } #endif /* USING_LISTENERS */ + +void send_msg_request_success() { + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS); + encrypt_packet(); +} + +void send_msg_request_failure() { + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE); + encrypt_packet(); +} diff -r 204dc7bd62aa -r 185b241a0b4a common-kex.c --- a/common-kex.c Sat Mar 08 21:02:02 2014 +0800 +++ b/common-kex.c Fri Jul 25 22:23:50 2014 +0800 @@ -270,7 +270,7 @@ ses.kexstate.our_first_follows_matches = 0; - ses.kexstate.lastkextime = time(NULL); + ses.kexstate.lastkextime = monotonic_now(); } diff -r 204dc7bd62aa -r 185b241a0b4a common-session.c --- a/common-session.c Sat Mar 08 21:02:02 2014 +0800 +++ b/common-session.c Fri Jul 25 22:23:50 2014 +0800 @@ -51,6 +51,7 @@ /* called only at the start of a session, set up initial state */ void common_session_init(int sock_in, int sock_out) { + time_t now; TRACE(("enter session_init")) @@ -58,9 +59,15 @@ ses.sock_out = sock_out; ses.maxfd = MAX(sock_in, sock_out); - ses.connect_time = 0; - ses.last_trx_packet_time = 0; - ses.last_packet_time = 0; + ses.socket_prio = DROPBEAR_PRIO_DEFAULT; + /* Sets it to lowdelay */ + update_channel_prio(); + + now = monotonic_now(); + ses.last_packet_time_keepalive_recv = now; + ses.last_packet_time_idle = now; + ses.last_packet_time_any_sent = 0; + ses.last_packet_time_keepalive_sent = 0; if (pipe(ses.signal_pipe) < 0) { dropbear_exit("Signal pipe failed"); @@ -186,13 +193,7 @@ /* check for auth timeout, rekeying required etc */ checktimeouts(); - /* process session socket's incoming/outgoing data */ - if (ses.sock_out != -1) { - if (FD_ISSET(ses.sock_out, &writefd) && !isempty(&ses.writequeue)) { - write_packet(); - } - } - + /* process session socket's incoming data */ if (ses.sock_in != -1) { if (FD_ISSET(ses.sock_in, &readfd)) { if (!ses.remoteident) { @@ -218,6 +219,14 @@ * during rekeying ) */ channelio(&readfd, &writefd); + /* process session socket's outgoing data */ + if (ses.sock_out != -1) { + if (!isempty(&ses.writequeue)) { + write_packet(); + } + } + + if (loophandler) { loophandler(); } @@ -385,11 +394,21 @@ return pos+1; } -void send_msg_ignore() { +static void send_msg_keepalive() { CHECKCLEARTOWRITE(); - buf_putbyte(ses.writepayload, SSH_MSG_IGNORE); - buf_putstring(ses.writepayload, "", 0); + time_t old_time_idle = ses.last_packet_time_idle; + /* Try to force a response from the other end. Some peers will + reply with SSH_MSG_REQUEST_FAILURE, some will reply with SSH_MSG_UNIMPLEMENTED */ + buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST); + /* A short string */ + buf_putstring(ses.writepayload, "k@dropbear.nl", 0); + buf_putbyte(ses.writepayload, 1); /* want_reply */ encrypt_packet(); + + ses.last_packet_time_keepalive_sent = monotonic_now(); + + /* keepalives shouldn't update idle timeout, reset it back */ + ses.last_packet_time_idle = old_time_idle; } /* Check all timeouts which are required. Currently these are the time for @@ -397,13 +416,8 @@ static void checktimeouts() { time_t now; - - now = time(NULL); + now = monotonic_now(); - if (ses.connect_time != 0 && now - ses.connect_time >= AUTH_TIMEOUT) { - dropbear_close("Timeout before auth"); - } - /* we can't rekey if we haven't done remote ident exchange yet */ if (ses.remoteident == NULL) { return; @@ -416,13 +430,27 @@ send_msg_kexinit(); } - if (opts.keepalive_secs > 0 - && now - ses.last_trx_packet_time >= opts.keepalive_secs) { - send_msg_ignore(); + if (opts.keepalive_secs > 0) { + /* Send keepalives if we've been idle */ + if (now - ses.last_packet_time_any_sent >= opts.keepalive_secs) { + send_msg_keepalive(); + } + + /* Also send an explicit keepalive message to trigger a response + if the remote end hasn't sent us anything */ + if (now - ses.last_packet_time_keepalive_recv >= opts.keepalive_secs + && now - ses.last_packet_time_keepalive_sent >= opts.keepalive_secs) { + send_msg_keepalive(); + } + + if (now - ses.last_packet_time_keepalive_recv + >= opts.keepalive_secs * DEFAULT_KEEPALIVE_LIMIT) { + dropbear_exit("Keepalive timeout"); + } } - if (opts.idle_timeout_secs > 0 && ses.last_packet_time > 0 - && now - ses.last_packet_time >= opts.idle_timeout_secs) { + if (opts.idle_timeout_secs > 0 + && now - ses.last_packet_time_idle >= opts.idle_timeout_secs) { dropbear_close("Idle timeout"); } } @@ -433,12 +461,13 @@ long ret = LONG_MAX; if (KEX_REKEY_TIMEOUT > 0) ret = MIN(KEX_REKEY_TIMEOUT, ret); - if (AUTH_TIMEOUT > 0) + /* AUTH_TIMEOUT is only relevant before authdone */ + if (ses.authstate.authdone != 1 && AUTH_TIMEOUT > 0) ret = MIN(AUTH_TIMEOUT, ret); if (opts.keepalive_secs > 0) ret = MIN(opts.keepalive_secs, ret); - if (opts.idle_timeout_secs > 0) - ret = MIN(opts.idle_timeout_secs, ret); + if (opts.idle_timeout_secs > 0) + ret = MIN(opts.idle_timeout_secs, ret); return ret; } @@ -487,3 +516,47 @@ } } +/* Called when channels are modified */ +void update_channel_prio() { + enum dropbear_prio new_prio; + int any = 0; + unsigned int i; + + TRACE(("update_channel_prio")) + + new_prio = DROPBEAR_PRIO_BULK; + for (i = 0; i < ses.chansize; i++) { + struct Channel *channel = ses.channels[i]; + if (!channel || channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) { + if (channel && channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) { + TRACE(("update_channel_prio: early %d", channel->index)) + } + continue; + } + any = 1; + if (channel->prio == DROPBEAR_CHANNEL_PRIO_INTERACTIVE) + { + TRACE(("update_channel_prio: lowdelay %d", channel->index)) + new_prio = DROPBEAR_PRIO_LOWDELAY; + break; + } else if (channel->prio == DROPBEAR_CHANNEL_PRIO_UNKNOWABLE + && new_prio == DROPBEAR_PRIO_BULK) + { + TRACE(("update_channel_prio: unknowable %d", channel->index)) + new_prio = DROPBEAR_PRIO_DEFAULT; + } + } + + if (any == 0) { + /* lowdelay during setup */ + TRACE(("update_channel_prio: not any")) + new_prio = DROPBEAR_PRIO_LOWDELAY; + } + + if (new_prio != ses.socket_prio) { + TRACE(("Dropbear priority transitioning %4.4s -> %4.4s", (char*)&ses.socket_prio, (char*)&new_prio)) + set_sock_priority(ses.sock_out, new_prio); + ses.socket_prio = new_prio; + } +} + diff -r 204dc7bd62aa -r 185b241a0b4a configure.ac --- a/configure.ac Sat Mar 08 21:02:02 2014 +0800 +++ b/configure.ac Fri Jul 25 22:23:50 2014 +0800 @@ -361,6 +361,10 @@ AC_CHECK_FUNCS(setutxent utmpxname) AC_CHECK_FUNCS(logout updwtmp logwtmp) +# OS X monotonic time +AC_CHECK_HEADERS([mach/mach_time.h]) +AC_CHECK_FUNCS(mach_absolute_time) + AC_ARG_ENABLE(bundled-libtom, [ --enable-bundled-libtom Force using bundled libtomcrypt/libtommath even if a system version exists. --disable-bundled-libtom Force using system libtomcrypt/libtommath, fail if it does not exist. diff -r 204dc7bd62aa -r 185b241a0b4a dbutil.c --- a/dbutil.c Sat Mar 08 21:02:02 2014 +0800 +++ b/dbutil.c Fri Jul 25 22:23:50 2014 +0800 @@ -48,6 +48,19 @@ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" + +#ifdef __linux__ +#define _GNU_SOURCE +/* To call clock_gettime() directly */ +#include +#endif /* __linux */ + +#ifdef HAVE_MACH_MACH_TIME_H +#include +#include +#endif + #include "includes.h" #include "dbutil.h" #include "buffer.h" @@ -319,7 +332,7 @@ continue; } - if (listen(sock, 20) < 0) { + if (listen(sock, DROPBEAR_LISTEN_BACKLOG) < 0) { err = errno; close(sock); TRACE(("listen() failed")) @@ -932,3 +945,33 @@ return c; } +time_t monotonic_now() { + +#if defined(__linux__) && defined(SYS_clock_gettime) + /* CLOCK_MONOTONIC_COARSE was added in Linux 2.6.32. Probably cheaper. */ +#ifndef CLOCK_MONOTONIC_COARSE +#define CLOCK_MONOTONIC_COARSE 6 +#endif + static clockid_t clock_source = CLOCK_MONOTONIC_COARSE; + struct timespec ts; + + if (syscall(SYS_clock_gettime, clock_source, &ts) == EINVAL) { + clock_source = CLOCK_MONOTONIC; + syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts); + } + return ts.tv_sec; +#elif defined(HAVE_MACH_ABSOLUTE_TIME) + /* OS X, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */ + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + mach_timebase_info(&timebase_info); + } + return mach_absolute_time() * timebase_info.numer / timebase_info.denom + / 1e9; +#else + /* Fallback for everything else - this will sometimes go backwards */ + return time(NULL); +#endif + +} + diff -r 204dc7bd62aa -r 185b241a0b4a dbutil.h --- a/dbutil.h Sat Mar 08 21:02:02 2014 +0800 +++ b/dbutil.h Fri Jul 25 22:23:50 2014 +0800 @@ -62,9 +62,9 @@ #endif enum dropbear_prio { - DROPBEAR_PRIO_DEFAULT, - DROPBEAR_PRIO_LOWDELAY, - DROPBEAR_PRIO_BULK, + DROPBEAR_PRIO_DEFAULT = 'dffd', + DROPBEAR_PRIO_LOWDELAY = 'lddl', + DROPBEAR_PRIO_BULK = 'bllb', }; char * stripcontrol(const char * text); @@ -106,4 +106,9 @@ /* Returns 0 if a and b have the same contents */ int constant_time_memcmp(const void* a, const void *b, size_t n); +/* Returns a time in seconds that doesn't go backwards - does not correspond to +a real-world clock */ +time_t monotonic_now(); + + #endif /* _DBUTIL_H_ */ diff -r 204dc7bd62aa -r 185b241a0b4a keyimport.c --- a/keyimport.c Sat Mar 08 21:02:02 2014 +0800 +++ b/keyimport.c Fri Jul 25 22:23:50 2014 +0800 @@ -602,13 +602,18 @@ */ blobbuf = buf_new(3000); +#ifdef DROPBEAR_DSS if (key->type == OSSH_DSA) { buf_putstring(blobbuf, "ssh-dss", 7); retkey->type = DROPBEAR_SIGNKEY_DSS; - } else if (key->type == OSSH_RSA) { + } +#endif +#ifdef DROPBEAR_RSA + if (key->type == OSSH_RSA) { buf_putstring(blobbuf, "ssh-rsa", 7); retkey->type = DROPBEAR_SIGNKEY_RSA; } +#endif for (i = 0; i < num_integers; i++) { ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, @@ -831,7 +836,14 @@ mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */ #endif - if (key->type == DROPBEAR_SIGNKEY_RSA || key->type == DROPBEAR_SIGNKEY_DSS) + if ( +#ifdef DROPBEAR_RSA + key->type == DROPBEAR_SIGNKEY_RSA || +#endif +#ifdef DROPBEAR_DSS + key->type == DROPBEAR_SIGNKEY_DSS || +#endif + 0) { /* * Fetch the key blobs. diff -r 204dc7bd62aa -r 185b241a0b4a options.h --- a/options.h Sat Mar 08 21:02:02 2014 +0800 +++ b/options.h Fri Jul 25 22:23:50 2014 +0800 @@ -308,6 +308,11 @@ be overridden at runtime with -K. 0 disables keepalives */ #define DEFAULT_KEEPALIVE 0 +/* If this many KEEPALIVES are sent with no packets received from the +other side, exit. Not run-time configurable - if you have a need +for runtime configuration please mail the Dropbear list */ +#define DEFAULT_KEEPALIVE_LIMIT 3 + /* Ensure that data is received within IDLE_TIMEOUT seconds. This can be overridden at runtime with -I. 0 disables idle timeouts */ #define DEFAULT_IDLE_TIMEOUT 0 diff -r 204dc7bd62aa -r 185b241a0b4a packet.c --- a/packet.c Sat Mar 08 21:02:02 2014 +0800 +++ b/packet.c Fri Jul 25 22:23:50 2014 +0800 @@ -57,42 +57,52 @@ int len, written; buffer * writebuf = NULL; - time_t now; unsigned packet_type; - int all_ignore = 1; #ifdef HAVE_WRITEV struct iovec *iov = NULL; int i; struct Link *l; + int iov_max_count; #endif TRACE2(("enter write_packet")) dropbear_assert(!isempty(&ses.writequeue)); -#ifdef HAVE_WRITEV - iov = m_malloc(sizeof(*iov) * ses.writequeue.count); +#if defined(HAVE_WRITEV) && (defined(IOV_MAX) || defined(UIO_MAXIOV)) + +#ifndef IOV_MAX +#define IOV_MAX UIO_MAXIOV +#endif + + /* Make sure the size of the iov is below the maximum allowed by the OS. */ + iov_max_count = ses.writequeue.count; + if (iov_max_count > IOV_MAX) + { + iov_max_count = IOV_MAX; + } + + iov = m_malloc(sizeof(*iov) * iov_max_count); for (l = ses.writequeue.head, i = 0; l; l = l->link, i++) { writebuf = (buffer*)l->item; packet_type = writebuf->data[writebuf->len-1]; len = writebuf->len - 1 - writebuf->pos; dropbear_assert(len > 0); - all_ignore &= (packet_type == SSH_MSG_IGNORE); TRACE2(("write_packet writev #%d type %d len %d/%d", i, packet_type, len, writebuf->len-1)) iov[i].iov_base = buf_getptr(writebuf, len); iov[i].iov_len = len; } - written = writev(ses.sock_out, iov, ses.writequeue.count); + written = writev(ses.sock_out, iov, iov_max_count); if (written < 0) { if (errno == EINTR) { m_free(iov); - TRACE2(("leave writepacket: EINTR")) + TRACE2(("leave write_packet: EINTR")) return; } else { - dropbear_exit("Error writing"); + dropbear_exit("Error writing: %s", strerror(errno)); } - } + } if (written == 0) { ses.remoteclosed(); @@ -113,8 +123,7 @@ } m_free(iov); - -#else +#else /* No writev () */ /* Get the next buffer in the queue of encrypted packets to write*/ writebuf = (buffer*)examine(&ses.writequeue); @@ -131,10 +140,9 @@ TRACE2(("leave writepacket: EINTR")) return; } else { - dropbear_exit("Error writing"); + dropbear_exit("Error writing: %s", strerror(errno)); } } - all_ignore = (packet_type == SSH_MSG_IGNORE); if (written == 0) { ses.remoteclosed(); @@ -149,14 +157,7 @@ /* More packet left to write, leave it in the queue for later */ buf_incrpos(writebuf, written); } - -#endif - now = time(NULL); - ses.last_trx_packet_time = now; - - if (!all_ignore) { - ses.last_packet_time = now; - } +#endif /* writev */ TRACE2(("leave write_packet")) } @@ -503,6 +504,8 @@ unsigned char packet_type; unsigned int len, encrypt_buf_size; unsigned char mac_bytes[MAX_MAC_LEN]; + + time_t now; TRACE2(("enter encrypt_packet()")) @@ -610,6 +613,18 @@ ses.kexstate.datatrans += writebuf->len; ses.transseq++; + now = monotonic_now(); + ses.last_packet_time_any_sent = now; + /* idle timeout shouldn't be affected by responses to keepalives. + send_msg_keepalive() itself also does tricks with + ses.last_packet_idle_time - read that if modifying this code */ + if (packet_type != SSH_MSG_REQUEST_FAILURE + && packet_type != SSH_MSG_UNIMPLEMENTED + && packet_type != SSH_MSG_IGNORE) { + ses.last_packet_time_idle = now; + + } + TRACE2(("leave encrypt_packet()")) } diff -r 204dc7bd62aa -r 185b241a0b4a process-packet.c --- a/process-packet.c Sat Mar 08 21:02:02 2014 +0800 +++ b/process-packet.c Fri Jul 25 22:23:50 2014 +0800 @@ -44,6 +44,7 @@ unsigned char type; unsigned int i; + time_t now; TRACE2(("enter process_packet")) @@ -52,7 +53,8 @@ ses.lastpacket = type; - ses.last_packet_time = time(NULL); + now = monotonic_now(); + ses.last_packet_time_keepalive_recv = now; /* These packets we can receive at any time */ switch(type) { @@ -65,13 +67,21 @@ case SSH_MSG_UNIMPLEMENTED: /* debugging XXX */ TRACE(("SSH_MSG_UNIMPLEMENTED")) - dropbear_exit("Received SSH_MSG_UNIMPLEMENTED"); + goto out; case SSH_MSG_DISCONNECT: /* TODO cleanup? */ dropbear_close("Disconnect received"); } + /* Ignore these packet types so that keepalives don't interfere with + idle detection. This is slightly incorrect since a tcp forwarded + global request with failure won't trigger the idle timeout, + but that's probably acceptable */ + if (!(type == SSH_MSG_GLOBAL_REQUEST || type == SSH_MSG_REQUEST_FAILURE)) { + ses.last_packet_time_idle = now; + } + /* This applies for KEX, where the spec says the next packet MUST be * NEWKEYS */ if (ses.requirenext != 0) { diff -r 204dc7bd62aa -r 185b241a0b4a runopts.h --- a/runopts.h Sat Mar 08 21:02:02 2014 +0800 +++ b/runopts.h Fri Jul 25 22:23:50 2014 +0800 @@ -37,8 +37,8 @@ int listen_fwd_all; #endif unsigned int recv_window; - time_t keepalive_secs; - time_t idle_timeout_secs; + time_t keepalive_secs; /* Time between sending keepalives. 0 is off */ + time_t idle_timeout_secs; /* Exit if no traffic is sent/received in this time */ #ifndef DISABLE_ZLIB /* TODO: add a commandline flag. Currently this is on by default if compression diff -r 204dc7bd62aa -r 185b241a0b4a session.h --- a/session.h Sat Mar 08 21:02:02 2014 +0800 +++ b/session.h Fri Jul 25 22:23:50 2014 +0800 @@ -48,6 +48,8 @@ void send_session_identification(); void send_msg_ignore(); +void update_channel_prio(); + const char* get_user_shell(); void fill_passwd(const char* username); @@ -104,10 +106,6 @@ /* Is it a client or server? */ unsigned char isserver; - time_t connect_time; /* time the connection was established - (cleared after auth once we're not - respecting AUTH_TIMEOUT any more) */ - int sock_in; int sock_out; @@ -146,11 +144,14 @@ int signal_pipe[2]; /* stores endpoints of a self-pipe used for race-free signal handling */ - time_t last_trx_packet_time; /* time of the last packet transmission, for - keepalive purposes */ + /* time of the last packet send/receive, for keepalive. Not real-world clock */ + time_t last_packet_time_keepalive_sent; + time_t last_packet_time_keepalive_recv; + time_t last_packet_time_any_sent; - time_t last_packet_time; /* time of the last packet transmission or receive, for - idle timeout purposes */ + time_t last_packet_time_idle; /* time of the last packet transmission or receive, for + idle timeout purposes so ignores SSH_MSG_IGNORE + or responses to keepalives. Not real-world clock */ /* KEX/encryption related */ @@ -187,7 +188,9 @@ unsigned int chancount; /* the number of Channel*s in use */ const struct ChanType **chantypes; /* The valid channel types */ - + /* TCP priority level for the main "port 22" tcp socket */ + enum dropbear_prio socket_prio; + /* TCP forwarding - where manage listeners */ struct Listener ** listeners; unsigned int listensize; @@ -217,6 +220,11 @@ /* The resolved remote address, used for lastlog etc */ char *remotehost; + time_t connect_time; /* time the connection was established + (cleared after auth once we're not + respecting AUTH_TIMEOUT any more). + A monotonic time, not realworld */ + #ifdef USE_VFORK pid_t server_pid; #endif diff -r 204dc7bd62aa -r 185b241a0b4a signkey.c --- a/signkey.c Sat Mar 08 21:02:02 2014 +0800 +++ b/signkey.c Fri Jul 25 22:23:50 2014 +0800 @@ -106,6 +106,7 @@ void ** signkey_key_ptr(sign_key *key, enum signkey_type type) { switch (type) { +#ifdef DROPBEAR_ECDSA #ifdef DROPBEAR_ECC_256 case DROPBEAR_SIGNKEY_ECDSA_NISTP256: return (void**)&key->ecckey256; @@ -118,6 +119,7 @@ case DROPBEAR_SIGNKEY_ECDSA_NISTP521: return (void**)&key->ecckey521; #endif +#endif /* DROPBEAR_ECDSA */ #ifdef DROPBEAR_RSA case DROPBEAR_SIGNKEY_RSA: return (void**)&key->rsakey; diff -r 204dc7bd62aa -r 185b241a0b4a svr-auth.c --- a/svr-auth.c Sat Mar 08 21:02:02 2014 +0800 +++ b/svr-auth.c Fri Jul 25 22:23:50 2014 +0800 @@ -392,8 +392,7 @@ /* authdone must be set after encrypt_packet() for * delayed-zlib mode */ ses.authstate.authdone = 1; - ses.connect_time = 0; - + svr_ses.connect_time = 0; if (ses.authstate.pw_uid == 0) { ses.allowprivport = 1; diff -r 204dc7bd62aa -r 185b241a0b4a svr-chansession.c --- a/svr-chansession.c Sat Mar 08 21:02:02 2014 +0800 +++ b/svr-chansession.c Fri Jul 25 22:23:50 2014 +0800 @@ -253,6 +253,8 @@ chansess->agentdir = NULL; #endif + channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE; + return 0; } @@ -668,8 +670,11 @@ if (chansess->term == NULL) { /* no pty */ - set_sock_priority(ses.sock_out, DROPBEAR_PRIO_BULK); ret = noptycommand(channel, chansess); + if (ret == DROPBEAR_SUCCESS) { + channel->prio = DROPBEAR_CHANNEL_PRIO_BULK; + update_channel_prio(); + } } else { /* want pty */ ret = ptycommand(channel, chansess); diff -r 204dc7bd62aa -r 185b241a0b4a svr-session.c --- a/svr-session.c Sat Mar 08 21:02:02 2014 +0800 +++ b/svr-session.c Fri Jul 25 22:23:50 2014 +0800 @@ -80,12 +80,22 @@ svr_pubkey_options_cleanup(); } +static void +svr_sessionloop() { + if (svr_ses.connect_time != 0 + && monotonic_now() - svr_ses.connect_time >= AUTH_TIMEOUT) { + dropbear_close("Timeout before auth"); + } +} + void svr_session(int sock, int childpipe) { char *host, *port; size_t len; common_session_init(sock, sock); + svr_ses.connect_time = monotonic_now();; + /* Initialise server specific parts of the session */ svr_ses.childpipe = childpipe; #ifdef USE_VFORK @@ -95,8 +105,6 @@ chaninitialise(svr_chantypes); svr_chansessinitialise(); - ses.connect_time = time(NULL); - /* for logging the remote address */ get_socket_address(ses.sock_in, NULL, NULL, &host, &port, 0); len = strlen(host) + strlen(port) + 2; @@ -128,7 +136,7 @@ /* Run the main for loop. NULL is for the dispatcher - only the client * code makes use of it */ - session_loop(NULL); + session_loop(svr_sessionloop); /* Not reached */ diff -r 204dc7bd62aa -r 185b241a0b4a svr-tcpfwd.c --- a/svr-tcpfwd.c Sat Mar 08 21:02:02 2014 +0800 +++ b/svr-tcpfwd.c Fri Jul 25 22:23:50 2014 +0800 @@ -34,14 +34,6 @@ #include "runopts.h" #include "auth.h" -static void send_msg_request_failure(); - -static void send_msg_request_failure() { - CHECKCLEARTOWRITE(); - buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE); - encrypt_packet(); -} - #ifndef ENABLE_SVR_REMOTETCPFWD /* This is better than SSH_MSG_UNIMPLEMENTED */ @@ -53,7 +45,6 @@ /* */ #endif /* !ENABLE_SVR_REMOTETCPFWD */ -static void send_msg_request_success(); static int svr_cancelremotetcp(); static int svr_remotetcpreq(); static int newtcpdirect(struct Channel * channel); @@ -62,7 +53,7 @@ static const struct ChanType svr_chan_tcpremote = { 1, /* sepfds */ "forwarded-tcpip", - NULL, + tcp_prio_inithandler, NULL, NULL, NULL @@ -115,15 +106,6 @@ TRACE(("leave recv_msg_global_request")) } - -static void send_msg_request_success() { - - CHECKCLEARTOWRITE(); - buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS); - encrypt_packet(); - -} - static int matchtcp(void* typedata1, void* typedata2) { const struct TCPListener *info1 = (struct TCPListener*)typedata1; @@ -258,6 +240,8 @@ int len; int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; + TRACE(("newtcpdirect channel %d", channel->index)) + if (svr_opts.nolocaltcp || !svr_pubkey_allows_tcpfwd()) { TRACE(("leave newtcpdirect: local tcp forwarding disabled")) goto out; @@ -299,6 +283,8 @@ * progress succeeds */ channel->writefd = sock; channel->initconn = 1; + + channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE; err = SSH_OPEN_IN_PROGRESS; diff -r 204dc7bd62aa -r 185b241a0b4a svr-x11fwd.c --- a/svr-x11fwd.c Sat Mar 08 21:02:02 2014 +0800 +++ b/svr-x11fwd.c Fri Jul 25 22:23:50 2014 +0800 @@ -182,10 +182,15 @@ } } +static int x11_inithandler(struct Channel *channel) { + channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE; + return 0; +} + static const struct ChanType chan_x11 = { 0, /* sepfds */ "x11", - NULL, /* inithandler */ + x11_inithandler, /* inithandler */ NULL, /* checkclose */ NULL, /* reqhandler */ NULL /* closehandler */ diff -r 204dc7bd62aa -r 185b241a0b4a sysoptions.h --- a/sysoptions.h Sat Mar 08 21:02:02 2014 +0800 +++ b/sysoptions.h Fri Jul 25 22:23:50 2014 +0800 @@ -251,4 +251,10 @@ #define USE_VFORK #endif /* don't HAVE_FORK */ +#if MAX_UNAUTH_CLIENTS > MAX_CHANNELS +#define DROPBEAR_LISTEN_BACKLOG MAX_UNAUTH_CLIENTS +#else +#define DROPBEAR_LISTEN_BACKLOG MAX_CHANNELS +#endif + /* no include guard for this file */ diff -r 204dc7bd62aa -r 185b241a0b4a tcp-accept.c --- a/tcp-accept.c Sat Mar 08 21:02:02 2014 +0800 +++ b/tcp-accept.c Fri Jul 25 22:23:50 2014 +0800 @@ -30,6 +30,7 @@ #include "buffer.h" #include "packet.h" #include "listener.h" +#include "listener.h" #include "runopts.h" #ifdef DROPBEAR_TCP_ACCEPT @@ -44,6 +45,13 @@ m_free(tcpinfo); } +int tcp_prio_inithandler(struct Channel* channel) +{ + TRACE(("tcp_prio_inithandler channel %d", channel->index)) + channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE; + return 0; +} + static void tcp_acceptor(struct Listener *listener, int sock) { int fd; diff -r 204dc7bd62aa -r 185b241a0b4a tcpfwd.h --- a/tcpfwd.h Sat Mar 08 21:02:02 2014 +0800 +++ b/tcpfwd.h Fri Jul 25 22:23:50 2014 +0800 @@ -70,5 +70,8 @@ /* Common */ int listen_tcpfwd(struct TCPListener* tcpinfo); +int tcp_prio_inithandler(struct Channel* chan); + +#define CHANNEL_ID_TCPFORWARDED 'tcpf' #endif