diff --git a/LICENSE b/LICENSE index be9d5d8..828b9af 100644 --- a/LICENSE +++ b/LICENSE @@ -8,7 +8,7 @@ The majority of code is written by Matt Johnston, under the license below. 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 --git a/auth.h b/auth.h index 3aed57b..66f5b6a 100644 --- a/auth.h +++ b/auth.h @@ -106,7 +106,7 @@ struct AuthState { 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 --git a/channel.h b/channel.h index 7008eff..a310d44 100644 --- a/channel.h +++ b/channel.h @@ -105,6 +105,9 @@ void chancleanup(); 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,8 +131,10 @@ int send_msg_channel_open_init(int fd, const struct ChanType *type); 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 --git a/chansession.h b/chansession.h index ef252ea..1a01d04 100644 --- a/chansession.h +++ b/chansession.h @@ -89,7 +89,6 @@ void cli_chansess_winchange(); #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 --git a/cli-agentfwd.c b/cli-agentfwd.c index 4ec555b..535024f 100644 --- a/cli-agentfwd.c +++ b/cli-agentfwd.c @@ -234,7 +234,7 @@ void cli_setup_agent(struct Channel *channel) { 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 --git a/cli-chansession.c b/cli-chansession.c index c16443e..57457d2 100644 --- a/cli-chansession.c +++ b/cli-chansession.c @@ -92,17 +92,6 @@ static void cli_closechansess(struct Channel *UNUSED(channel)) { } } -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 @@ static void send_chansess_pty_req(struct Channel *channel) { 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 @@ static void send_chansess_shell_req(struct Channel *channel) { 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 */ diff --git a/cli-session.c b/cli-session.c index ecefe46..a484bf7 100644 --- a/cli-session.c +++ b/cli-session.c @@ -70,11 +70,15 @@ static const packettype cli_packettypes[] = { {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 - {SSH_MSG_REQUEST_FAILURE, ignore_recv_msg_request_failure}, /* for keepalive */ + /* For keepalive */ + {SSH_MSG_REQUEST_SUCCESS, ignore_recv_response}, + {SSH_MSG_REQUEST_FAILURE, ignore_recv_response}, #endif {0, 0} /* End */ }; diff --git a/common-channel.c b/common-channel.c index 63a42fb..049658d 100644 --- a/common-channel.c +++ b/common-channel.c @@ -627,7 +627,12 @@ void recv_msg_channel_request() { && !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")) @@ -1134,3 +1139,30 @@ void send_msg_request_failure() { 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 --git a/common-session.c b/common-session.c index ee5f1e9..83fb7f4 100644 --- a/common-session.c +++ b/common-session.c @@ -394,19 +394,30 @@ static int ident_readln(int fd, char* buf, int count) { return pos+1; } -void ignore_recv_msg_request_failure() { +void ignore_recv_response() { // Do nothing - TRACE(("Ignored msg_request_failure")) + TRACE(("Ignored msg_request_response")) } static void send_msg_keepalive() { CHECKCLEARTOWRITE(); 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); + + 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(); @@ -435,7 +446,10 @@ static void checktimeouts() { send_msg_kexinit(); } - if (opts.keepalive_secs > 0) { + 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(); diff --git a/session.h b/session.h index 5d555fa..ed0f5be 100644 --- a/session.h +++ b/session.h @@ -47,7 +47,7 @@ void session_loop(void(*loophandler)()); void session_cleanup(); void send_session_identification(); void send_msg_ignore(); -void ignore_recv_msg_request_failure(); +void ignore_recv_response(); void update_channel_prio(); diff --git a/svr-chansession.c b/svr-chansession.c index 2a99362..6c73778 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -53,6 +53,7 @@ static void sesssigchild_handler(int val); 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 @@ static void send_msg_chansess_exitsignal(struct Channel * channel, 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; @@ -968,16 +977,6 @@ static void execchild(void *user_data) { 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 --git a/svr-main.c b/svr-main.c index 4b38594..cf92d42 100644 --- a/svr-main.c +++ b/svr-main.c @@ -409,7 +409,7 @@ static size_t listensockets(int *sock, size_t sockcount, int *maxfd) { 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 --git a/svr-session.c b/svr-session.c index 74a7b16..343cb30 100644 --- a/svr-session.c +++ b/svr-session.c @@ -58,7 +58,10 @@ static const packettype svr_packettypes[] = { {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_REQUEST_FAILURE, ignore_recv_msg_request_failure}, /* for keepalive */ + {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}, diff --git a/sysoptions.h b/sysoptions.h index 7999d95..d01b4ea 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -257,4 +257,7 @@ #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 */