# HG changeset patch # User Matt Johnston # Date 1422020327 -28800 # Node ID 73ea0dce9a57d40f3a59304774288f7ddca54ac7 # Parent bae0b34bc059c12d14ea37756b5fa05af9317192# Parent 6c0fb5428aaa0f0156029f24c24984e72199fa5a Merge up to date diff -r bae0b34bc059 -r 73ea0dce9a57 .hgsigs --- a/.hgsigs Wed Mar 12 23:40:02 2014 +0800 +++ b/.hgsigs Fri Jan 23 21:38:47 2015 +0800 @@ -10,3 +10,6 @@ 9ec083a21adfcb099f21eb03704b66d14a4ba800 0 iEYEABECAAYFAlKE4JoACgkQjPn4sExkf7wLDgCghkVGwMjI138bEv+ORVzN7zIH7cEAoLckaxZc1k1aXlmlSCRlP8cuKH3o 3d1d7d151c0ce3a79da62e86463f5632fa2b144a 0 iEYEABECAAYFAlKd5AEACgkQjPn4sExkf7wzWgCfdvPEEIdlMPqcbOQMJ7b+eAyy164An2ip1lPh1eS5g26/gSfruvWBVym4 277429102f1337bd10c89107d3e01de509cc1a7e 0 iEYEABECAAYFAlMEvF4ACgkQjPn4sExkf7xeVQCgtbxJ4G3hsFwUOM0K1WGr1J2vsbEAoMM8dEyr1mdrbgO1tzNLfD1nxbyn +96584b934d04ebab443f603e78d38fe692d36313 0 iEYEABECAAYFAlPVFrQACgkQjPn4sExkf7xr6ACglRiLE21vRrS1rJ809o2yMADIKtwAn1f5SyZUngSde8eE55JxCMwtMC5m +caac692b366c153cea0e9cd59aa2d79a7d843d4e 0 iEYEABECAAYFAlPk1mcACgkQjPn4sExkf7wLpgCeOqMYqpkf4lYUuyrn9VYThNpc7PkAn3JOSNgIqkKUcmSy6FstrI8jwJzq +2d421bc0545d1be6d59a4ebfe61606d94b124b0c 0 iEYEABECAAYFAlRJDCQACgkQjPn4sExkf7xUYACcCwVJkYWXJn5x/D5A+qMupy778lEAn0rg1oNiq96YU/4jOPsS5IMItihu diff -r bae0b34bc059 -r 73ea0dce9a57 .hgtags --- a/.hgtags Wed Mar 12 23:40:02 2014 +0800 +++ b/.hgtags Fri Jan 23 21:38:47 2015 +0800 @@ -43,3 +43,6 @@ e894dbc015ba7ff4c3bf897ee20e28ca90c55a16 DROPBEAR_2013.61test 3d1d7d151c0ce3a79da62e86463f5632fa2b144a DROPBEAR_2013.62 2351b2da8e0d08dcc6e64fcc328b53b9630bda68 DROPBEAR_2014.63 +0d2d39957c029adb7f4327d37fe6b4900f0736d9 DROPBEAR_2014.64 +e9579816f20ea85affc6135e87f8477992808948 DROPBEAR_2014.65 +735511a4c761141416ad0e6728989d2dafa55bc2 DROPBEAR_2014.66 diff -r bae0b34bc059 -r 73ea0dce9a57 CHANGES --- a/CHANGES Wed Mar 12 23:40:02 2014 +0800 +++ b/CHANGES Fri Jan 23 21:38:47 2015 +0800 @@ -1,3 +1,61 @@ +2014.66 - Thursday 23 October 2014 + +- Use the same keepalive handling behaviour as OpenSSH. This will work better + with some SSH implementations that have different behaviour with unknown + message types. + +- Don't reply with SSH_MSG_UNIMPLEMENTED when we receive a reply to our own + keepalive message + +- Set $SSH_CLIENT to keep bash happy, patch from Ryan Cleere + +- Fix wtmp which broke since 2013.62, patch from Whoopie + +2014.65 - Friday 8 August 2014 + +- Fix 2014.64 regression, server session hang on exit with scp (and probably + others), thanks to NiLuJe for tracking it down + +- Fix 2014.64 regression, clock_gettime() error handling which broke on older + Linux kernels, reported by NiLuJe + +- Fix 2014.64 regression, writev() could occassionally fail with EAGAIN which + wasn't caught + +- Avoid error message when trying to set QoS on proxycommand or multihop pipes + +- Use /usr/bin/xauth, thanks to Mike Frysinger + +- Don't exit the client if the local user entry can't be found, thanks to iquaba + +2014.64 - Sunday 27 July 2014 + +- 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 + +- Add -V for version + 2014.63 - Wednesday 19 February 2014 - Fix ~. to terminate a client interactive session after waking a laptop diff -r bae0b34bc059 -r 73ea0dce9a57 LICENSE --- a/LICENSE Wed Mar 12 23:40:02 2014 +0800 +++ b/LICENSE Fri Jan 23 21:38:47 2015 +0800 @@ -8,7 +8,7 @@ Portions of the client-mode work are (c) 2004 Mihnea Stoenescu, under the same license: -Copyright (c) 2002-2013 Matt Johnston +Copyright (c) 2002-2014 Matt Johnston Portions copyright (c) 2004 Mihnea Stoenescu All rights reserved. diff -r bae0b34bc059 -r 73ea0dce9a57 auth.h --- a/auth.h Wed Mar 12 23:40:02 2014 +0800 +++ b/auth.h Fri Jan 23 21:38:47 2015 +0800 @@ -108,7 +108,7 @@ valid */ unsigned int failcount; /* Number of (failed) authentication attempts.*/ unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have. Applies for - client and server (though has differing [obvious] + client and server (though has differing meanings). */ unsigned perm_warn : 1; /* Server only, set if bad permissions on ~/.ssh/authorized_keys have already been diff -r bae0b34bc059 -r 73ea0dce9a57 channel.h --- a/channel.h Wed Mar 12 23:40:02 2014 +0800 +++ b/channel.h Fri Jan 23 21:38:47 2015 +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[]); @@ -105,6 +105,9 @@ void setchannelfds(fd_set *readfd, fd_set *writefd); void channelio(fd_set *readfd, fd_set *writefd); struct Channel* getchannel(); +/* Returns an arbitrary channel that is in a ready state - not +being initialised and no EOF in either direction. NULL if none. */ +struct Channel* get_any_ready_channel(); void recv_msg_channel_open(); void recv_msg_channel_request(); @@ -128,5 +131,10 @@ void recv_msg_channel_open_confirmation(); void recv_msg_channel_open_failure(); #endif +void start_send_channel_request(struct Channel *channel, unsigned char *type); + +void send_msg_request_success(); +void send_msg_request_failure(); + #endif /* _CHANNEL_H_ */ diff -r bae0b34bc059 -r 73ea0dce9a57 chansession.h --- a/chansession.h Wed Mar 12 23:40:02 2014 +0800 +++ b/chansession.h Fri Jan 23 21:38:47 2015 +0800 @@ -51,9 +51,12 @@ /* exit details */ struct exitinfo exit; - /* Used to set $SSH_CONNECTION in the child session. - Is only set temporarily before forking */ + + /* These are only set temporarily before forking */ + /* Used to set $SSH_CONNECTION in the child session. */ char *connection_string; + /* Used to set $SSH_CLIENT in the child session. */ + char *client_string; #ifndef DISABLE_X11FWD struct Listener * x11listener; @@ -89,7 +92,6 @@ #ifdef ENABLE_CLI_NETCAT void cli_send_netcat_request(); #endif -void cli_start_send_channel_request(struct Channel *channel, unsigned char *type); void svr_chansessinitialise(); extern const struct ChanType svrchansess; diff -r bae0b34bc059 -r 73ea0dce9a57 cli-agentfwd.c --- a/cli-agentfwd.c Wed Mar 12 23:40:02 2014 +0800 +++ b/cli-agentfwd.c Fri Jan 23 21:38:47 2015 +0800 @@ -210,13 +210,14 @@ ret = buf_get_pub_key(key_buf, pubkey, &key_type); buf_free(key_buf); if (ret != DROPBEAR_SUCCESS) { - /* This is slack, properly would cleanup vars etc */ - dropbear_exit("Bad pubkey received from agent"); + TRACE(("Skipping bad/unknown type pubkey from agent")); + sign_key_free(pubkey); + } else { + pubkey->type = key_type; + pubkey->source = SIGNKEY_SOURCE_AGENT; + + list_append(ret_list, pubkey); } - pubkey->type = key_type; - pubkey->source = SIGNKEY_SOURCE_AGENT; - - list_append(ret_list, pubkey); /* We'll ignore the comment for now. might want it later.*/ buf_eatstring(inbuf); @@ -234,7 +235,7 @@ return; } - cli_start_send_channel_request(channel, "auth-agent-req@openssh.com"); + start_send_channel_request(channel, "auth-agent-req@openssh.com"); /* Don't want replies */ buf_putbyte(ses.writepayload, 0); encrypt_packet(); diff -r bae0b34bc059 -r 73ea0dce9a57 cli-auth.c --- a/cli-auth.c Wed Mar 12 23:40:02 2014 +0800 +++ b/cli-auth.c Fri Jan 23 21:38:47 2015 +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 bae0b34bc059 -r 73ea0dce9a57 cli-chansession.c --- a/cli-chansession.c Wed Mar 12 23:40:02 2014 +0800 +++ b/cli-chansession.c Fri Jan 23 21:38:47 2015 +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(); @@ -92,17 +92,6 @@ } } -void cli_start_send_channel_request(struct Channel *channel, - unsigned char *type) { - - CHECKCLEARTOWRITE(); - buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); - buf_putint(ses.writepayload, channel->remotechan); - - buf_putstring(ses.writepayload, type, strlen(type)); - -} - /* Taken from OpenSSH's sshtty.c: * RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */ static void cli_tty_setup() { @@ -287,7 +276,7 @@ TRACE(("enter send_chansess_pty_req")) - cli_start_send_channel_request(channel, "pty-req"); + start_send_channel_request(channel, "pty-req"); /* Don't want replies */ buf_putbyte(ses.writepayload, 0); @@ -330,7 +319,7 @@ reqtype = "shell"; } - cli_start_send_channel_request(channel, reqtype); + start_send_channel_request(channel, reqtype); /* XXX TODO */ buf_putbyte(ses.writepayload, 0); /* Don't want replies */ @@ -357,6 +346,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 +363,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 +384,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 bae0b34bc059 -r 73ea0dce9a57 cli-main.c --- a/cli-main.c Wed Mar 12 23:40:02 2014 +0800 +++ b/cli-main.c Fri Jan 23 21:38:47 2015 +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 bae0b34bc059 -r 73ea0dce9a57 cli-runopts.c --- a/cli-runopts.c Wed Mar 12 23:40:02 2014 +0800 +++ b/cli-runopts.c Fri Jan 23 21:38:47 2015 +0800 @@ -90,6 +90,7 @@ "-c Specify preferred ciphers ('-c help' to list options)\n" "-m Specify preferred MACs for packet verification (or '-m help')\n" #endif + "-V Version\n" #ifdef DEBUG_TRACE "-v verbose (compiled with DEBUG_TRACE)\n" #endif @@ -163,6 +164,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(); @@ -322,6 +325,10 @@ #ifndef ENABLE_CLI_LOCALTCPFWD case 'L': #endif + case 'V': + print_version(); + exit(EXIT_SUCCESS); + break; case 'o': case 'b': next = &dummy; @@ -676,11 +683,13 @@ uid = getuid(); pw = getpwuid(uid); - if (pw == NULL || pw->pw_name == NULL) { - dropbear_exit("Unknown own user"); + if (pw && pw->pw_name != NULL) { + cli_opts.own_user = m_strdup(pw->pw_name); + } else { + dropbear_log(LOG_INFO, "Warning: failed to identify current user. Trying anyway."); + cli_opts.own_user = m_strdup("unknown"); } - cli_opts.own_user = m_strdup(pw->pw_name); } #ifdef ENABLE_CLI_ANYTCPFWD diff -r bae0b34bc059 -r 73ea0dce9a57 cli-session.c --- a/cli-session.c Wed Mar 12 23:40:02 2014 +0800 +++ b/cli-session.c Fri Jan 23 21:38:47 2015 +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,9 +69,16 @@ {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}, + {SSH_MSG_CHANNEL_SUCCESS, ignore_recv_response}, + {SSH_MSG_CHANNEL_FAILURE, ignore_recv_response}, #ifdef ENABLE_CLI_REMOTETCPFWD {SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */ {SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */ +#else + /* For keepalive */ + {SSH_MSG_REQUEST_SUCCESS, ignore_recv_response}, + {SSH_MSG_REQUEST_FAILURE, ignore_recv_response}, #endif {0, 0} /* End */ }; @@ -367,3 +375,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 bae0b34bc059 -r 73ea0dce9a57 cli-tcpfwd.c --- a/cli-tcpfwd.c Wed Mar 12 23:40:02 2014 +0800 +++ b/cli-tcpfwd.c Fri Jan 23 21:38:47 2015 +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 bae0b34bc059 -r 73ea0dce9a57 common-channel.c --- a/common-channel.c Wed Mar 12 23:40:02 2014 +0800 +++ b/common-channel.c Fri Jan 23 21:38:47 2015 +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,28 @@ 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; + } + + 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 */ - 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 +582,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 +603,8 @@ m_free(channel); ses.chancount--; + update_channel_prio(); + TRACE(("leave remove_channel")) } @@ -608,7 +627,12 @@ && !channel->close_handler_done) { channel->type->reqhandler(channel); } else { - send_msg_channel_failure(channel); + int wantreply; + buf_eatstring(ses.payload); + wantreply = buf_getbool(ses.payload); + if (wantreply) { + send_msg_channel_failure(channel); + } } TRACE(("leave recv_msg_channel_request")) @@ -876,6 +900,10 @@ } } + if (channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) { + channel->prio = DROPBEAR_CHANNEL_PRIO_BULK; + } + chan_initwritebuf(channel); /* success */ @@ -889,6 +917,8 @@ cleanup: m_free(type); + + update_channel_prio(); TRACE(("leave recv_msg_channel_open")) } @@ -1004,7 +1034,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; @@ -1070,9 +1100,14 @@ if (ret > 0) { remove_channel(channel); TRACE(("inithandler returned failure %d", ret)) + return; } } + 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 +1127,42 @@ 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(); +} + +struct Channel* get_any_ready_channel() { + if (ses.chancount == 0) { + return NULL; + } + size_t i; + for (i = 0; i < ses.chansize; i++) { + struct Channel *chan = ses.channels[i]; + if (chan + && !(chan->sent_eof || chan->recv_eof) + && !(chan->await_open || chan->initconn)) { + return chan; + } + } + return NULL; +} + +void start_send_channel_request(struct Channel *channel, + unsigned char *type) { + + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); + buf_putint(ses.writepayload, channel->remotechan); + + buf_putstring(ses.writepayload, type, strlen(type)); + +} diff -r bae0b34bc059 -r 73ea0dce9a57 common-kex.c --- a/common-kex.c Wed Mar 12 23:40:02 2014 +0800 +++ b/common-kex.c Fri Jan 23 21:38:47 2015 +0800 @@ -270,7 +270,7 @@ ses.kexstate.our_first_follows_matches = 0; - ses.kexstate.lastkextime = time(NULL); + ses.kexstate.lastkextime = monotonic_now(); } @@ -303,7 +303,7 @@ hash_desc->done(&hs2, tmpout); memcpy(&out[offset], tmpout, MIN(outlen - offset, hash_desc->hashsize)); } - + m_burn(&hs2, sizeof(hash_state)); } /* Generate the actual encryption/integrity keys, using the results of the @@ -403,6 +403,7 @@ m_burn(C2S_key, sizeof(C2S_key)); m_burn(S2C_IV, sizeof(S2C_IV)); m_burn(S2C_key, sizeof(S2C_key)); + m_burn(&hs, sizeof(hash_state)); TRACE(("leave gen_new_keys")) } @@ -798,6 +799,7 @@ buf_burn(ses.kexhashbuf); buf_free(ses.kexhashbuf); + m_burn(&hs, sizeof(hash_state)); ses.kexhashbuf = NULL; /* first time around, we set the session_id to H */ @@ -805,7 +807,6 @@ /* create the session_id, this never needs freeing */ ses.session_id = buf_newcopy(ses.hash); } - } /* read the other side's algo list. buf_match_algo is a callback to match diff -r bae0b34bc059 -r 73ea0dce9a57 common-runopts.c --- a/common-runopts.c Wed Mar 12 23:40:02 2014 +0800 +++ b/common-runopts.c Fri Jan 23 21:38:47 2015 +0800 @@ -106,3 +106,8 @@ } #endif +void print_version() { + fprintf(stderr, "Dropbear v%s\n", DROPBEAR_VERSION); +} + + diff -r bae0b34bc059 -r 73ea0dce9a57 common-session.c --- a/common-session.c Wed Mar 12 23:40:02 2014 +0800 +++ b/common-session.c Fri Jan 23 21:38:47 2015 +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"); @@ -189,13 +196,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) { @@ -221,8 +222,21 @@ * during rekeying ) */ channelio(&readfd, &writefd); +<<<<<<< local if (ses.loop_handler) { ses.loop_handler(); +======= + /* process session socket's outgoing data */ + if (ses.sock_out != -1) { + if (!isempty(&ses.writequeue)) { + write_packet(); + } + } + + + if (loophandler) { + loophandler(); +>>>>>>> other } } /* for(;;) */ @@ -388,11 +402,37 @@ return pos+1; } -void send_msg_ignore() { +void ignore_recv_response() { + // Do nothing + TRACE(("Ignored msg_request_response")) +} + +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; + + struct Channel *chan = get_any_ready_channel(); + + if (chan) { + /* Channel requests are preferable, more implementations + handle them than SSH_MSG_GLOBAL_REQUEST */ + TRACE(("keepalive channel request %d", chan->index)) + start_send_channel_request(chan, DROPBEAR_KEEPALIVE_STRING); + } else { + TRACE(("keepalive global request")) + /* Some peers will reply with SSH_MSG_REQUEST_FAILURE, + some will reply with SSH_MSG_UNIMPLEMENTED, some will exit. */ + buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST); + buf_putstring(ses.writepayload, DROPBEAR_KEEPALIVE_STRING, + strlen(DROPBEAR_KEEPALIVE_STRING)); + } + 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 @@ -400,13 +440,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; @@ -419,13 +454,30 @@ 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 && ses.authstate.authdone) { + /* Avoid sending keepalives prior to auth - those are + not valid pre-auth packet types */ + + /* 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"); } } @@ -436,12 +488,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; } @@ -490,3 +543,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 bae0b34bc059 -r 73ea0dce9a57 configure.ac --- a/configure.ac Wed Mar 12 23:40:02 2014 +0800 +++ b/configure.ac Fri Jan 23 21:38:47 2015 +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 bae0b34bc059 -r 73ea0dce9a57 dbclient.1 --- a/dbclient.1 Wed Mar 12 23:40:02 2014 +0800 +++ b/dbclient.1 Fri Jan 23 21:38:47 2015 +0800 @@ -19,8 +19,7 @@ .SH DESCRIPTION .B dbclient -is a SSH client designed to be small enough to be used in small memory -environments, while still being functional and secure enough for general use. +is a small SSH client .SH OPTIONS .TP .B \-p \fIport @@ -98,7 +97,7 @@ useful for working around firewalls or routers that drop connections after a certain period of inactivity. The trade-off is that a session may be closed if there is a temporary lapse of network connectivity. A setting -if 0 disables keepalives. +if 0 disables keepalives. If no response is received for 3 consecutive keepalives the connection will be closed. .TP .B \-I \fIidle_timeout Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds. @@ -121,6 +120,9 @@ .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 +.TP +.B \-V +Print the version .SH MULTI-HOP Dropbear will also allow multiple "hops" to be specified, separated by commas. In diff -r bae0b34bc059 -r 73ea0dce9a57 dbutil.c --- a/dbutil.c Wed Mar 12 23:40:02 2014 +0800 +++ b/dbutil.c Fri Jan 23 21:38:47 2015 +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" @@ -148,7 +161,7 @@ gettimeofday(&tv, NULL); va_start(param, format); - fprintf(stderr, "TRACE (%d) %d.%d: ", getpid(), tv.tv_sec, tv.tv_usec); + fprintf(stderr, "TRACE (%d) %d.%d: ", getpid(), (int)tv.tv_sec, (int)tv.tv_usec); vfprintf(stderr, format, param); fprintf(stderr, "\n"); va_end(param); @@ -170,7 +183,7 @@ gettimeofday(&tv, NULL); va_start(param, format); - fprintf(stderr, "TRACE2 (%d) %d.%d: ", getpid(), tv.tv_sec, tv.tv_usec); + fprintf(stderr, "TRACE2 (%d) %d.%d: ", getpid(), (int)tv.tv_sec, (int)tv.tv_usec); vfprintf(stderr, format, param); fprintf(stderr, "\n"); va_end(param); @@ -189,6 +202,9 @@ int iptos_val = 0, so_prio_val = 0, rc; + /* Don't log ENOTSOCK errors so that this can harmlessly be called + * on a client '-J' proxy pipe */ + /* set the TOS bit for either ipv4 or ipv6 */ #ifdef IPTOS_LOWDELAY if (prio == DROPBEAR_PRIO_LOWDELAY) { @@ -198,12 +214,12 @@ } #if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS) rc = setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (void*)&iptos_val, sizeof(iptos_val)); - if (rc < 0) { + if (rc < 0 && errno != ENOTSOCK) { TRACE(("Couldn't set IPV6_TCLASS (%s)", strerror(errno))); } #endif rc = setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&iptos_val, sizeof(iptos_val)); - if (rc < 0) { + if (rc < 0 && errno != ENOTSOCK) { TRACE(("Couldn't set IP_TOS (%s)", strerror(errno))); } #endif @@ -216,7 +232,7 @@ } /* linux specific, sets QoS class. see tc-prio(8) */ rc = setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &so_prio_val, sizeof(so_prio_val)); - if (rc < 0) + if (rc < 0 && errno != ENOTSOCK) dropbear_log(LOG_WARNING, "Couldn't set SO_PRIORITY (%s)", strerror(errno)); #endif @@ -319,7 +335,7 @@ continue; } - if (listen(sock, 20) < 0) { + if (listen(sock, DROPBEAR_LISTEN_BACKLOG) < 0) { err = errno; close(sock); TRACE(("listen() failed")) @@ -932,3 +948,56 @@ return c; } +#if defined(__linux__) && defined(SYS_clock_gettime) +/* CLOCK_MONOTONIC_COARSE was added in Linux 2.6.32 but took a while to +reach userspace include headers */ +#ifndef CLOCK_MONOTONIC_COARSE +#define CLOCK_MONOTONIC_COARSE 6 +#endif +static clockid_t get_linux_clock_source() { + struct timespec ts; + if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC_COARSE, &ts) == 0) { + return CLOCK_MONOTONIC_COARSE; + } + + if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts) == 0) { + return CLOCK_MONOTONIC; + } + return -1; +} +#endif + +time_t monotonic_now() { +#if defined(__linux__) && defined(SYS_clock_gettime) + static clockid_t clock_source = -2; + + if (clock_source == -2) { + /* First run, find out which one works. + -1 will fall back to time() */ + clock_source = get_linux_clock_source(); + } + + if (clock_source >= 0) { + struct timespec ts; + if (syscall(SYS_clock_gettime, clock_source, &ts) != 0) { + /* Intermittent clock failures should not happen */ + dropbear_exit("Clock broke"); + } + return ts.tv_sec; + } +#endif /* linux clock_gettime */ + +#if 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; +#endif /* osx mach_absolute_time */ + + /* Fallback for everything else - this will sometimes go backwards */ + return time(NULL); +} + diff -r bae0b34bc059 -r 73ea0dce9a57 dbutil.h --- a/dbutil.h Wed Mar 12 23:40:02 2014 +0800 +++ b/dbutil.h Fri Jan 23 21:38:47 2015 +0800 @@ -62,9 +62,9 @@ #endif enum dropbear_prio { - DROPBEAR_PRIO_DEFAULT, - DROPBEAR_PRIO_LOWDELAY, - DROPBEAR_PRIO_BULK, + DROPBEAR_PRIO_DEFAULT = 10, + DROPBEAR_PRIO_LOWDELAY = 11, + DROPBEAR_PRIO_BULK = 12, }; 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 bae0b34bc059 -r 73ea0dce9a57 debian/changelog --- a/debian/changelog Wed Mar 12 23:40:02 2014 +0800 +++ b/debian/changelog Fri Jan 23 21:38:47 2015 +0800 @@ -1,3 +1,21 @@ +dropbear (2014.66-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston Thu, 23 Oct 2014 22:54:00 +0800 + +dropbear (2014.65-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston Fri, 8 Aug 2014 22:54:00 +0800 + +dropbear (2014.64-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston Sun, 27 Jul 2014 22:54:00 +0800 + dropbear (2014.63-0.1) unstable; urgency=low * New upstream release. diff -r bae0b34bc059 -r 73ea0dce9a57 dropbear.8 --- a/dropbear.8 Wed Mar 12 23:40:02 2014 +0800 +++ b/dropbear.8 Fri Jan 23 21:38:47 2015 +0800 @@ -10,8 +10,7 @@ .IR [address:]port ] .SH DESCRIPTION .B dropbear -is a SSH server designed to be small enough to be used in small memory -environments, while still being functional and secure enough for general use. +is a small SSH server .SH OPTIONS .TP .B \-b \fIbanner @@ -88,10 +87,14 @@ useful for working around firewalls or routers that drop connections after a certain period of inactivity. The trade-off is that a session may be closed if there is a temporary lapse of network connectivity. A setting -if 0 disables keepalives. +if 0 disables keepalives. If no response is received for 3 consecutive keepalives the connection will be closed. .TP .B \-I \fIidle_timeout Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds. +.TP +.B \-V +Print the version + .SH FILES .TP diff -r bae0b34bc059 -r 73ea0dce9a57 gensignkey.c --- a/gensignkey.c Wed Mar 12 23:40:02 2014 +0800 +++ b/gensignkey.c Fri Jan 23 21:38:47 2015 +0800 @@ -41,6 +41,9 @@ out: if (fd >= 0) { + if (fsync(fd) != 0) { + dropbear_log(LOG_ERR, "fsync of %s failed: %s", filename, strerror(errno)); + } m_close(fd); } return ret; diff -r bae0b34bc059 -r 73ea0dce9a57 keyimport.c --- a/keyimport.c Wed Mar 12 23:40:02 2014 +0800 +++ b/keyimport.c Fri Jan 23 21:38:47 2015 +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 bae0b34bc059 -r 73ea0dce9a57 loginrec.h --- a/loginrec.h Wed Mar 12 23:40:02 2014 +0800 +++ b/loginrec.h Fri Jan 23 21:38:47 2015 +0800 @@ -79,10 +79,10 @@ # if defined(HAVE_UTMP_H) && defined(UTMP_FILE) && !defined(DISABLE_UTMP) # define USE_UTMP # endif -# if defined(HAVE_WTMPX_H) && defined(WTMPX_FILE) && !defined(DISABLE_WTMPX) +# if defined(WTMPX_FILE) && !defined(DISABLE_WTMPX) # define USE_WTMPX # endif -# if defined(HAVE_WTMP_H) && defined(WTMP_FILE) && !defined(DISABLE_WTMP) +# if defined(WTMP_FILE) && !defined(DISABLE_WTMP) # define USE_WTMP # endif diff -r bae0b34bc059 -r 73ea0dce9a57 options.h --- a/options.h Wed Mar 12 23:40:02 2014 +0800 +++ b/options.h Fri Jan 23 21:38:47 2015 +0800 @@ -123,8 +123,8 @@ * which are not the standard form. */ #define DROPBEAR_SHA1_HMAC #define DROPBEAR_SHA1_96_HMAC -/*#define DROPBEAR_SHA2_256_HMAC*/ -/*#define DROPBEAR_SHA2_512_HMAC*/ +#define DROPBEAR_SHA2_256_HMAC +#define DROPBEAR_SHA2_512_HMAC #define DROPBEAR_MD5_HMAC /* You can also disable integrity. Don't bother disabling this if you're @@ -257,7 +257,7 @@ /* The command to invoke for xauth when using X11 forwarding. * "-q" for quiet */ #ifndef XAUTH_COMMAND -#define XAUTH_COMMAND "/usr/bin/X11/xauth -q" +#define XAUTH_COMMAND "/usr/bin/xauth -q" #endif /* if you want to enable running an sftp server (such as the one included with @@ -301,6 +301,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 bae0b34bc059 -r 73ea0dce9a57 packet.c --- a/packet.c Wed Mar 12 23:40:02 2014 +0800 +++ b/packet.c Fri Jan 23 21:38:47 2015 +0800 @@ -57,42 +57,55 @@ 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); + /* This may return EAGAIN. The main loop sometimes + calls write_packet() without bothering to test with select() since + it's likely to be necessary */ + written = writev(ses.sock_out, iov, iov_max_count); if (written < 0) { - if (errno == EINTR) { + if (errno == EINTR || errno == EAGAIN) { 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 +126,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); @@ -127,14 +139,13 @@ written = write(ses.sock_out, buf_getptr(writebuf, len), len); if (written < 0) { - if (errno == EINTR) { + if (errno == EINTR || errno == EAGAIN) { 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 +160,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")) } @@ -254,7 +258,7 @@ ses.remoteclosed(); } if (slen < 0) { - if (errno == EINTR) { + if (errno == EINTR || errno == EAGAIN) { TRACE2(("leave read_packet_init: EINTR")) return DROPBEAR_FAILURE; } @@ -503,6 +507,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 +616,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 bae0b34bc059 -r 73ea0dce9a57 process-packet.c --- a/process-packet.c Wed Mar 12 23:40:02 2014 +0800 +++ b/process-packet.c Fri Jan 23 21:38:47 2015 +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 bae0b34bc059 -r 73ea0dce9a57 release.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/release.sh Fri Jan 23 21:38:47 2015 +0800 @@ -0,0 +1,40 @@ +#!/bin/sh +VERSION=$(echo '#include "sysoptions.h"\necho DROPBEAR_VERSION' | cpp - | sh) +echo Releasing version "$VERSION" ... +if ! head -n1 CHANGES | grep -q $VERSION ; then + echo "CHANGES needs updating" + exit 1 +fi + +if ! head -n1 debian/changelog | grep -q $VERSION ; then + echo "CHANGES needs updating" + exit 1 +fi + +head -n1 CHANGES + +#sleep 3 + +RELDIR=$PWD/../dropbear-$VERSION +ARCHIVE=${RELDIR}.tar.bz2 +if test -e $RELDIR; then + echo "$RELDIR exists" + exit 1 +fi + +if test -e $ARCHIVE; then + echo "$ARCHIVE exists" + exit 1 +fi + +hg archive "$RELDIR" || exit 2 + +(cd "$RELDIR" && autoconf && autoheader) || exit 2 + +rm -r "$RELDIR/autom4te.cache" || exit 2 + +(cd $RELDIR/.. && tar cjf $ARCHIVE `basename "$RELDIR"`) || exit 2 + +ls -l $ARCHIVE +openssl sha1 $ARCHIVE +echo "Done to $ARCHIVE" diff -r bae0b34bc059 -r 73ea0dce9a57 runopts.h --- a/runopts.h Wed Mar 12 23:40:02 2014 +0800 +++ b/runopts.h Fri Jan 23 21:38:47 2015 +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 @@ -164,4 +164,6 @@ void parse_ciphers_macs(); #endif +void print_version(void); + #endif /* _RUNOPTS_H_ */ diff -r bae0b34bc059 -r 73ea0dce9a57 scp.c --- a/scp.c Wed Mar 12 23:40:02 2014 +0800 +++ b/scp.c Fri Jan 23 21:38:47 2015 +0800 @@ -1146,7 +1146,7 @@ { (void) fprintf(stderr, "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" - " [-l limit] [-o ssh_option] [-P port] [-S program]\n" + " [-l limit] [-P port] [-S program]\n" " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); exit(1); } diff -r bae0b34bc059 -r 73ea0dce9a57 session.h --- a/session.h Wed Mar 12 23:40:02 2014 +0800 +++ b/session.h Fri Jan 23 21:38:47 2015 +0800 @@ -47,6 +47,9 @@ void session_cleanup(); void send_session_identification(); void send_msg_ignore(); +void ignore_recv_response(); + +void update_channel_prio(); const char* get_user_shell(); void fill_passwd(const char* username); @@ -104,10 +107,6 @@ /* Is it a client or server? */ unsigned int 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; @@ -150,11 +149,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 */ @@ -191,8 +193,11 @@ unsigned int chansize; /* the number of Channel*s allocated for channels */ unsigned int chancount; /* the number of Channel*s in use */ const struct ChanType **chantypes; /* The valid channel types */ + int channel_signal_pending; /* Flag set by sigchld handler */ - + /* 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; @@ -222,6 +227,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 bae0b34bc059 -r 73ea0dce9a57 signkey.c --- a/signkey.c Wed Mar 12 23:40:02 2014 +0800 +++ b/signkey.c Fri Jan 23 21:38:47 2015 +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 bae0b34bc059 -r 73ea0dce9a57 svr-auth.c --- a/svr-auth.c Wed Mar 12 23:40:02 2014 +0800 +++ b/svr-auth.c Fri Jan 23 21:38:47 2015 +0800 @@ -401,8 +401,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 bae0b34bc059 -r 73ea0dce9a57 svr-chansession.c --- a/svr-chansession.c Wed Mar 12 23:40:02 2014 +0800 +++ b/svr-chansession.c Fri Jan 23 21:38:47 2015 +0800 @@ -53,6 +53,7 @@ static void closechansess(struct Channel *channel); static int newchansess(struct Channel *channel); static void chansessionrequest(struct Channel *channel); +static int sesscheckclose(struct Channel *channel); static void send_exitsignalstatus(struct Channel *channel); static void send_msg_chansess_exitstatus(struct Channel * channel, @@ -61,6 +62,14 @@ struct ChanSess * chansess); static void get_termmodes(struct ChanSess *chansess); +const struct ChanType svrchansess = { + 0, /* sepfds */ + "session", /* name */ + newchansess, /* inithandler */ + sesscheckclose, /* checkclosehandler */ + chansessionrequest, /* reqhandler */ + closechansess, /* closehandler */ +}; /* required to clear environment */ extern char** environ; @@ -89,6 +98,9 @@ const int saved_errno = errno; + /* Make channel handling code look for closed channels */ + ses.channel_signal_pending = 1; + TRACE(("enter sigchld handler")) while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { TRACE(("sigchld handler: pid %d", pid)) @@ -229,6 +241,7 @@ chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess)); chansess->cmd = NULL; chansess->connection_string = NULL; + chansess->client_string = NULL; chansess->pid = 0; /* pty details */ @@ -253,6 +266,8 @@ chansess->agentdir = NULL; #endif + channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE; + return 0; } @@ -588,19 +603,26 @@ return DROPBEAR_SUCCESS; } -static char* make_connection_string() { +static void make_connection_string(struct ChanSess *chansess) { char *local_ip, *local_port, *remote_ip, *remote_port; size_t len; - char *ret; get_socket_address(ses.sock_in, &local_ip, &local_port, &remote_ip, &remote_port, 0); - len = strlen(local_ip) + strlen(local_port) + strlen(remote_ip) + strlen(remote_port) + 4; - ret = m_malloc(len); - snprintf(ret, len, "%s %s %s %s", remote_ip, remote_port, local_ip, local_port); + + /* "remoteip remoteport localip localport" */ + len = strlen(local_ip) + strlen(remote_ip) + 20; + chansess->connection_string = m_malloc(len); + snprintf(chansess->connection_string, len, "%s %s %s %s", remote_ip, remote_port, local_ip, local_port); + + /* deprecated but bash only loads .bashrc if SSH_CLIENT is set */ + /* "remoteip remoteport localport" */ + len = strlen(remote_ip) + 20; + chansess->client_string = m_malloc(len); + snprintf(chansess->client_string, len, "%s %s %s", remote_ip, remote_port, local_port); + m_free(local_ip); m_free(local_port); m_free(remote_ip); m_free(remote_port); - return ret; } /* Handle a command request from the client. This is used for both shell @@ -663,13 +685,16 @@ /* uClinux will vfork(), so there'll be a race as connection_string is freed below. */ #ifndef USE_VFORK - chansess->connection_string = make_connection_string(); + make_connection_string(chansess); #endif 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); @@ -677,6 +702,7 @@ #ifndef USE_VFORK m_free(chansess->connection_string); + m_free(chansess->client_string); #endif if (ret == DROPBEAR_FAILURE) { @@ -932,6 +958,10 @@ if (chansess->connection_string) { addnewvar("SSH_CONNECTION", chansess->connection_string); } + + if (chansess->client_string) { + addnewvar("SSH_CLIENT", chansess->client_string); + } #ifdef ENABLE_SVR_PUBKEY_OPTIONS if (chansess->original_command) { @@ -960,16 +990,6 @@ dropbear_exit("Child failed"); } -const struct ChanType svrchansess = { - 0, /* sepfds */ - "session", /* name */ - newchansess, /* inithandler */ - sesscheckclose, /* checkclosehandler */ - chansessionrequest, /* reqhandler */ - closechansess, /* closehandler */ -}; - - /* Set up the general chansession environment, in particular child-exit * handling */ void svr_chansessinitialise() { diff -r bae0b34bc059 -r 73ea0dce9a57 svr-kex.c --- a/svr-kex.c Wed Mar 12 23:40:02 2014 +0800 +++ b/svr-kex.c Fri Jan 23 21:38:47 2015 +0800 @@ -84,7 +84,28 @@ TRACE(("leave recv_msg_kexdh_init")) } + #ifdef DROPBEAR_DELAY_HOSTKEY + +static void fsync_parent_dir(const char* fn) { +#ifdef HAVE_LIBGEN_H + char *fn_dir = m_strdup(fn); + char *dir = dirname(fn_dir); + int dirfd = open(dir, O_RDONLY); + + if (dirfd != -1) { + if (fsync(dirfd) != 0) { + TRACE(("fsync of directory %s failed: %s", dir, strerror(errno))) + } + m_close(dirfd); + } else { + TRACE(("error opening directory %s for fsync: %s", dir, strerror(errno))) + } + + free(fn_dir); +#endif +} + static void svr_ensure_hostkey() { const char* fn = NULL; @@ -142,6 +163,10 @@ } } + /* ensure directory update is flushed to disk, otherwise we can end up + with zero-byte hostkey files if the power goes off */ + fsync_parent_dir(fn); + ret = readhostkey(fn, svr_opts.hostkey, &type); if (ret == DROPBEAR_SUCCESS) { diff -r bae0b34bc059 -r 73ea0dce9a57 svr-main.c --- a/svr-main.c Wed Mar 12 23:40:02 2014 +0800 +++ b/svr-main.c Fri Jan 23 21:38:47 2015 +0800 @@ -409,7 +409,7 @@ size_t sockpos = 0; int nsock; - TRACE(("listensockets: %d to try\n", svr_opts.portcount)) + TRACE(("listensockets: %d to try", svr_opts.portcount)) for (i = 0; i < svr_opts.portcount; i++) { diff -r bae0b34bc059 -r 73ea0dce9a57 svr-runopts.c --- a/svr-runopts.c Wed Mar 12 23:40:02 2014 +0800 +++ b/svr-runopts.c Fri Jan 23 21:38:47 2015 +0800 @@ -92,6 +92,7 @@ "-W (default %d, larger may be faster, max 1MB)\n" "-K (0 is never, default %d, in seconds)\n" "-I (0 is never, default %d, in seconds)\n" + "-V Version\n" #ifdef DEBUG_TRACE "-v verbose (compiled with DEBUG_TRACE)\n" #endif @@ -256,7 +257,7 @@ #endif case 'h': printhelp(argv[0]); - exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); break; case 'u': /* backwards compatibility with old urandom option */ @@ -266,6 +267,10 @@ debug_trace = 1; break; #endif + case 'V': + print_version(); + exit(EXIT_SUCCESS); + break; default: fprintf(stderr, "Unknown argument %s\n", argv[i]); printhelp(argv[0]); @@ -405,7 +410,9 @@ sign_key * read_key = new_sign_key(); enum signkey_type type = DROPBEAR_SIGNKEY_ANY; if (readhostkey(keyfile, read_key, &type) == DROPBEAR_FAILURE) { - dropbear_log(LOG_WARNING, "Failed loading %s", keyfile); + if (!svr_opts.delay_hostkey) { + dropbear_log(LOG_WARNING, "Failed loading %s", keyfile); + } } #ifdef DROPBEAR_RSA diff -r bae0b34bc059 -r 73ea0dce9a57 svr-session.c --- a/svr-session.c Wed Mar 12 23:40:02 2014 +0800 +++ b/svr-session.c Fri Jan 23 21:38:47 2015 +0800 @@ -58,6 +58,10 @@ {SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open}, {SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof}, {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close}, + {SSH_MSG_CHANNEL_SUCCESS, ignore_recv_response}, + {SSH_MSG_CHANNEL_FAILURE, ignore_recv_response}, + {SSH_MSG_REQUEST_FAILURE, ignore_recv_response}, /* for keepalive */ + {SSH_MSG_REQUEST_SUCCESS, ignore_recv_response}, /* client */ #ifdef USING_LISTENERS {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation}, {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, @@ -83,12 +87,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 @@ -98,8 +112,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; @@ -131,7 +143,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 bae0b34bc059 -r 73ea0dce9a57 svr-tcpfwd.c --- a/svr-tcpfwd.c Wed Mar 12 23:40:02 2014 +0800 +++ b/svr-tcpfwd.c Fri Jan 23 21:38:47 2015 +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 bae0b34bc059 -r 73ea0dce9a57 svr-x11fwd.c --- a/svr-x11fwd.c Wed Mar 12 23:40:02 2014 +0800 +++ b/svr-x11fwd.c Fri Jan 23 21:38:47 2015 +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 bae0b34bc059 -r 73ea0dce9a57 sysoptions.h --- a/sysoptions.h Wed Mar 12 23:40:02 2014 +0800 +++ b/sysoptions.h Fri Jan 23 21:38:47 2015 +0800 @@ -4,7 +4,7 @@ *******************************************************************/ #ifndef DROPBEAR_VERSION -#define DROPBEAR_VERSION "2014.63" +#define DROPBEAR_VERSION "2014.66" #endif #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION @@ -153,8 +153,7 @@ #define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11 connection, so can't be _too_ small */ -#define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also - is the max length for a password etc */ +#define MAX_STRING_LEN 2400 /* Sun SSH needs this long for algos */ /* For a 4096 bit DSS key, empirically determined */ #define MAX_PUBKEY_SIZE 1700 @@ -249,4 +248,13 @@ #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 + +/* Use this string since some implementations might special-case it */ +#define DROPBEAR_KEEPALIVE_STRING "keepalive@openssh.com" + /* no include guard for this file */ diff -r bae0b34bc059 -r 73ea0dce9a57 tcp-accept.c --- a/tcp-accept.c Wed Mar 12 23:40:02 2014 +0800 +++ b/tcp-accept.c Fri Jan 23 21:38:47 2015 +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 bae0b34bc059 -r 73ea0dce9a57 tcpfwd.h --- a/tcpfwd.h Wed Mar 12 23:40:02 2014 +0800 +++ b/tcpfwd.h Fri Jan 23 21:38:47 2015 +0800 @@ -70,5 +70,9 @@ /* Common */ int listen_tcpfwd(struct TCPListener* tcpinfo); +int tcp_prio_inithandler(struct Channel* chan); + +/* A random identifier */ +#define CHANNEL_ID_TCPFORWARDED 0x43612c67 #endif