mirror of
https://github.com/clearml/dropbear
synced 2025-05-04 12:11:56 +00:00
Make keepalive handling more robust, this should now match what OpenSSH does
This commit is contained in:
parent
1387654cc8
commit
6d2d3669f3
2
LICENSE
2
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
|
Portions of the client-mode work are (c) 2004 Mihnea Stoenescu, under the
|
||||||
same license:
|
same license:
|
||||||
|
|
||||||
Copyright (c) 2002-2013 Matt Johnston
|
Copyright (c) 2002-2014 Matt Johnston
|
||||||
Portions copyright (c) 2004 Mihnea Stoenescu
|
Portions copyright (c) 2004 Mihnea Stoenescu
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
2
auth.h
2
auth.h
@ -106,7 +106,7 @@ struct AuthState {
|
|||||||
valid */
|
valid */
|
||||||
unsigned int failcount; /* Number of (failed) authentication attempts.*/
|
unsigned int failcount; /* Number of (failed) authentication attempts.*/
|
||||||
unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have. Applies for
|
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). */
|
meanings). */
|
||||||
unsigned perm_warn : 1; /* Server only, set if bad permissions on
|
unsigned perm_warn : 1; /* Server only, set if bad permissions on
|
||||||
~/.ssh/authorized_keys have already been
|
~/.ssh/authorized_keys have already been
|
||||||
|
@ -105,6 +105,9 @@ void chancleanup();
|
|||||||
void setchannelfds(fd_set *readfd, fd_set *writefd);
|
void setchannelfds(fd_set *readfd, fd_set *writefd);
|
||||||
void channelio(fd_set *readfd, fd_set *writefd);
|
void channelio(fd_set *readfd, fd_set *writefd);
|
||||||
struct Channel* getchannel();
|
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_open();
|
||||||
void recv_msg_channel_request();
|
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_confirmation();
|
||||||
void recv_msg_channel_open_failure();
|
void recv_msg_channel_open_failure();
|
||||||
#endif
|
#endif
|
||||||
|
void start_send_channel_request(struct Channel *channel, unsigned char *type);
|
||||||
|
|
||||||
void send_msg_request_success();
|
void send_msg_request_success();
|
||||||
void send_msg_request_failure();
|
void send_msg_request_failure();
|
||||||
|
|
||||||
|
|
||||||
#endif /* _CHANNEL_H_ */
|
#endif /* _CHANNEL_H_ */
|
||||||
|
@ -89,7 +89,6 @@ void cli_chansess_winchange();
|
|||||||
#ifdef ENABLE_CLI_NETCAT
|
#ifdef ENABLE_CLI_NETCAT
|
||||||
void cli_send_netcat_request();
|
void cli_send_netcat_request();
|
||||||
#endif
|
#endif
|
||||||
void cli_start_send_channel_request(struct Channel *channel, unsigned char *type);
|
|
||||||
|
|
||||||
void svr_chansessinitialise();
|
void svr_chansessinitialise();
|
||||||
extern const struct ChanType svrchansess;
|
extern const struct ChanType svrchansess;
|
||||||
|
@ -234,7 +234,7 @@ void cli_setup_agent(struct Channel *channel) {
|
|||||||
return;
|
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 */
|
/* Don't want replies */
|
||||||
buf_putbyte(ses.writepayload, 0);
|
buf_putbyte(ses.writepayload, 0);
|
||||||
encrypt_packet();
|
encrypt_packet();
|
||||||
|
@ -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:
|
/* Taken from OpenSSH's sshtty.c:
|
||||||
* RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */
|
* RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */
|
||||||
static void cli_tty_setup() {
|
static void cli_tty_setup() {
|
||||||
@ -287,7 +276,7 @@ static void send_chansess_pty_req(struct Channel *channel) {
|
|||||||
|
|
||||||
TRACE(("enter send_chansess_pty_req"))
|
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 */
|
/* Don't want replies */
|
||||||
buf_putbyte(ses.writepayload, 0);
|
buf_putbyte(ses.writepayload, 0);
|
||||||
@ -330,7 +319,7 @@ static void send_chansess_shell_req(struct Channel *channel) {
|
|||||||
reqtype = "shell";
|
reqtype = "shell";
|
||||||
}
|
}
|
||||||
|
|
||||||
cli_start_send_channel_request(channel, reqtype);
|
start_send_channel_request(channel, reqtype);
|
||||||
|
|
||||||
/* XXX TODO */
|
/* XXX TODO */
|
||||||
buf_putbyte(ses.writepayload, 0); /* Don't want replies */
|
buf_putbyte(ses.writepayload, 0); /* Don't want replies */
|
||||||
|
@ -70,11 +70,15 @@ static const packettype cli_packettypes[] = {
|
|||||||
{SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */
|
{SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */
|
||||||
{SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */
|
{SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */
|
||||||
{SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_cli},
|
{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
|
#ifdef ENABLE_CLI_REMOTETCPFWD
|
||||||
{SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */
|
{SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */
|
||||||
{SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */
|
{SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */
|
||||||
#else
|
#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
|
#endif
|
||||||
{0, 0} /* End */
|
{0, 0} /* End */
|
||||||
};
|
};
|
||||||
|
@ -627,7 +627,12 @@ void recv_msg_channel_request() {
|
|||||||
&& !channel->close_handler_done) {
|
&& !channel->close_handler_done) {
|
||||||
channel->type->reqhandler(channel);
|
channel->type->reqhandler(channel);
|
||||||
} else {
|
} 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"))
|
TRACE(("leave recv_msg_channel_request"))
|
||||||
@ -1134,3 +1139,30 @@ void send_msg_request_failure() {
|
|||||||
buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
|
buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
|
||||||
encrypt_packet();
|
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));
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -394,19 +394,30 @@ static int ident_readln(int fd, char* buf, int count) {
|
|||||||
return pos+1;
|
return pos+1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ignore_recv_msg_request_failure() {
|
void ignore_recv_response() {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
TRACE(("Ignored msg_request_failure"))
|
TRACE(("Ignored msg_request_response"))
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_msg_keepalive() {
|
static void send_msg_keepalive() {
|
||||||
CHECKCLEARTOWRITE();
|
CHECKCLEARTOWRITE();
|
||||||
time_t old_time_idle = ses.last_packet_time_idle;
|
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 */
|
struct Channel *chan = get_any_ready_channel();
|
||||||
buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
|
|
||||||
/* A short string */
|
if (chan) {
|
||||||
buf_putstring(ses.writepayload, "k@dropbear.nl", 0);
|
/* 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 */
|
buf_putbyte(ses.writepayload, 1); /* want_reply */
|
||||||
encrypt_packet();
|
encrypt_packet();
|
||||||
|
|
||||||
@ -435,7 +446,10 @@ static void checktimeouts() {
|
|||||||
send_msg_kexinit();
|
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 */
|
/* Send keepalives if we've been idle */
|
||||||
if (now - ses.last_packet_time_any_sent >= opts.keepalive_secs) {
|
if (now - ses.last_packet_time_any_sent >= opts.keepalive_secs) {
|
||||||
send_msg_keepalive();
|
send_msg_keepalive();
|
||||||
|
@ -47,7 +47,7 @@ void session_loop(void(*loophandler)());
|
|||||||
void session_cleanup();
|
void session_cleanup();
|
||||||
void send_session_identification();
|
void send_session_identification();
|
||||||
void send_msg_ignore();
|
void send_msg_ignore();
|
||||||
void ignore_recv_msg_request_failure();
|
void ignore_recv_response();
|
||||||
|
|
||||||
void update_channel_prio();
|
void update_channel_prio();
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ static void sesssigchild_handler(int val);
|
|||||||
static void closechansess(struct Channel *channel);
|
static void closechansess(struct Channel *channel);
|
||||||
static int newchansess(struct Channel *channel);
|
static int newchansess(struct Channel *channel);
|
||||||
static void chansessionrequest(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_exitsignalstatus(struct Channel *channel);
|
||||||
static void send_msg_chansess_exitstatus(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);
|
struct ChanSess * chansess);
|
||||||
static void get_termmodes(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 */
|
/* required to clear environment */
|
||||||
extern char** environ;
|
extern char** environ;
|
||||||
@ -968,16 +977,6 @@ static void execchild(void *user_data) {
|
|||||||
dropbear_exit("Child failed");
|
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
|
/* Set up the general chansession environment, in particular child-exit
|
||||||
* handling */
|
* handling */
|
||||||
void svr_chansessinitialise() {
|
void svr_chansessinitialise() {
|
||||||
|
@ -409,7 +409,7 @@ static size_t listensockets(int *sock, size_t sockcount, int *maxfd) {
|
|||||||
size_t sockpos = 0;
|
size_t sockpos = 0;
|
||||||
int nsock;
|
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++) {
|
for (i = 0; i < svr_opts.portcount; i++) {
|
||||||
|
|
||||||
|
@ -58,7 +58,10 @@ static const packettype svr_packettypes[] = {
|
|||||||
{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
|
{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
|
||||||
{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
|
{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
|
||||||
{SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
|
{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
|
#ifdef USING_LISTENERS
|
||||||
{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
|
{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
|
||||||
{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
|
{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
|
||||||
|
@ -257,4 +257,7 @@
|
|||||||
#define DROPBEAR_LISTEN_BACKLOG MAX_CHANNELS
|
#define DROPBEAR_LISTEN_BACKLOG MAX_CHANNELS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Use this string since some implementations might special-case it */
|
||||||
|
#define DROPBEAR_KEEPALIVE_STRING "keepalive@openssh.com"
|
||||||
|
|
||||||
/* no include guard for this file */
|
/* no include guard for this file */
|
||||||
|
Loading…
Reference in New Issue
Block a user