diff options
-rw-r--r-- | usr.bin/ssh/channels.c | 37 | ||||
-rw-r--r-- | usr.bin/ssh/channels.h | 4 | ||||
-rw-r--r-- | usr.bin/ssh/servconf.c | 20 | ||||
-rw-r--r-- | usr.bin/ssh/servconf.h | 11 | ||||
-rw-r--r-- | usr.bin/ssh/serverloop.c | 64 | ||||
-rw-r--r-- | usr.bin/ssh/sshd.8 | 27 |
6 files changed, 152 insertions, 11 deletions
diff --git a/usr.bin/ssh/channels.c b/usr.bin/ssh/channels.c index 0ba7199d47b..006f395885b 100644 --- a/usr.bin/ssh/channels.c +++ b/usr.bin/ssh/channels.c @@ -40,7 +40,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.106 2001/04/11 13:56:13 markus Exp $"); +RCSID("$OpenBSD: channels.c,v 1.107 2001/04/13 22:46:52 beck Exp $"); #include <openssl/rsa.h> #include <openssl/dsa.h> @@ -1843,6 +1843,41 @@ channel_still_open() return 0; } +/* Returns the id of an open channel suitable for keepaliving */ + +int +channel_find_open() +{ + u_int i; + for (i = 0; i < channels_alloc; i++) + switch (channels[i].type) { + case SSH_CHANNEL_CLOSED: + continue; + case SSH_CHANNEL_LARVAL: + case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_CONNECTING: /* XXX ??? */ + case SSH_CHANNEL_FREE: + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + return i; + case SSH_CHANNEL_INPUT_DRAINING: + case SSH_CHANNEL_OUTPUT_DRAINING: + if (!compat13) + fatal("cannot happen: OUT_DRAIN"); + return i; + default: + fatal("channel_find_open: bad channel type %d", channels[i].type); + /* NOTREACHED */ + } + return -1; +} + + /* * Returns a message describing the currently open forwarded connections, * suitable for sending to the client. The message contains crlf pairs for diff --git a/usr.bin/ssh/channels.h b/usr.bin/ssh/channels.h index 23e6ece839d..bf70a8f215b 100644 --- a/usr.bin/ssh/channels.h +++ b/usr.bin/ssh/channels.h @@ -32,7 +32,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* RCSID("$OpenBSD: channels.h,v 1.30 2001/04/07 08:55:17 markus Exp $"); */ +/* RCSID("$OpenBSD: channels.h,v 1.31 2001/04/13 22:46:53 beck Exp $"); */ #ifndef CHANNELS_H #define CHANNELS_H @@ -307,4 +307,6 @@ int channel_connect_to(const char *host, u_short host_port); int channel_connect_by_listen_adress(u_short listen_port); int x11_connect_display(void); +int channel_find_open(void); + #endif diff --git a/usr.bin/ssh/servconf.c b/usr.bin/ssh/servconf.c index f3d5068c012..f978c632b30 100644 --- a/usr.bin/ssh/servconf.c +++ b/usr.bin/ssh/servconf.c @@ -10,7 +10,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: servconf.c,v 1.76 2001/04/12 20:09:37 stevesk Exp $"); +RCSID("$OpenBSD: servconf.c,v 1.77 2001/04/13 22:46:53 beck Exp $"); #ifdef KRB4 #include <krb.h> @@ -99,6 +99,8 @@ initialize_server_options(ServerOptions *options) options->max_startups = -1; options->banner = NULL; options->reverse_mapping_check = -1; + options->client_alive_interval = -1; + options->client_alive_count_max = -1; } void @@ -201,6 +203,10 @@ fill_default_server_options(ServerOptions *options) options->max_startups_begin = options->max_startups; if (options->reverse_mapping_check == -1) options->reverse_mapping_check = 0; + if (options->client_alive_interval == -1) + options->client_alive_interval = 0; + if (options->client_alive_count_max == -1) + options->client_alive_count_max = 3; } /* Keyword tokens. */ @@ -225,7 +231,8 @@ typedef enum { sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups, sBanner, sReverseMappingCheck, sHostbasedAuthentication, - sHostbasedUsesNameFromPacketOnly + sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, + sClientAliveCountMax } ServerOpCodes; /* Textual representation of the tokens. */ @@ -289,6 +296,8 @@ static struct { { "maxstartups", sMaxStartups }, { "banner", sBanner }, { "reversemappingcheck", sReverseMappingCheck }, + { "clientaliveinterval", sClientAliveInterval }, + { "clientalivecountmax", sClientAliveCountMax }, { NULL, 0 } }; @@ -792,7 +801,12 @@ parse_flag: case sBanner: charptr = &options->banner; goto parse_filename; - + case sClientAliveInterval: + intptr = &options->client_alive_interval; + goto parse_int; + case sClientAliveCountMax: + intptr = &options->client_alive_count_max; + goto parse_int; default: fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n", filename, linenum, arg, opcode); diff --git a/usr.bin/ssh/servconf.h b/usr.bin/ssh/servconf.h index 9b3a60f08f7..4c02c0f5218 100644 --- a/usr.bin/ssh/servconf.h +++ b/usr.bin/ssh/servconf.h @@ -11,7 +11,7 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: servconf.h,v 1.40 2001/04/12 19:15:25 markus Exp $"); */ +/* RCSID("$OpenBSD: servconf.h,v 1.41 2001/04/13 22:46:53 beck Exp $"); */ #ifndef SERVCONF_H #define SERVCONF_H @@ -115,6 +115,15 @@ typedef struct { int max_startups; char *banner; /* SSH-2 banner message */ int reverse_mapping_check; /* cross-check ip and dns */ + int client_alive_interval; /* + * poke the client this often to + * see if it's still there + */ + int client_alive_count_max; /* + *If the client is unresponsive + * for this many intervals, above + * diconnect the session + */ } ServerOptions; /* diff --git a/usr.bin/ssh/serverloop.c b/usr.bin/ssh/serverloop.c index d1a24252d9e..8bd2c000a06 100644 --- a/usr.bin/ssh/serverloop.c +++ b/usr.bin/ssh/serverloop.c @@ -35,7 +35,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: serverloop.c,v 1.60 2001/04/05 23:39:20 markus Exp $"); +RCSID("$OpenBSD: serverloop.c,v 1.61 2001/04/13 22:46:54 beck Exp $"); #include "xmalloc.h" #include "packet.h" @@ -91,6 +91,8 @@ static volatile int child_wait_status; /* Status from wait(). */ void server_init_dispatch(void); +int client_alive_timeouts = 0; + void sigchld_handler(int sig) { @@ -190,6 +192,21 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, { struct timeval tv, *tvp; int ret; + int client_alive_scheduled = 0; + + /* + * if using client_alive, set the max timeout accordingly, + * and indicate that this particular timeout was for client + * alive by setting the client_alive_scheduled flag. + * + * this could be randomized somewhat to make traffic + * analysis more difficult, but we're not doing it yet. + */ + if (max_time_milliseconds == 0 && options.client_alive_interval) { + client_alive_scheduled = 1; + max_time_milliseconds = options.client_alive_interval * 1000; + } else + client_alive_scheduled = 0; /* When select fails we restart from here. */ retry_select: @@ -239,7 +256,7 @@ retry_select: * from it, then read as much as is available and exit. */ if (child_terminated && packet_not_very_much_data_to_write()) - if (max_time_milliseconds == 0) + if (max_time_milliseconds == 0 || client_alive_scheduled) max_time_milliseconds = 100; if (max_time_milliseconds == 0) @@ -255,12 +272,36 @@ retry_select: /* Wait for something to happen, or the timeout to expire. */ ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); - if (ret < 0) { + if (ret == -1) { if (errno != EINTR) error("select: %.100s", strerror(errno)); else goto retry_select; } + if (ret == 0 && client_alive_scheduled) { + /* timeout, check to see how many we have had */ + client_alive_timeouts++; + + if (client_alive_timeouts > options.client_alive_count_max ) { + packet_disconnect( + "Timeout, your session not responding."); + } else { + /* + * send a bogus channel request with "wantreply" + * we should get back a failure + */ + int id; + + id = channel_find_open(); + if (id != -1) { + channel_request_start(id, + "keepalive@openssh.com", 1); + packet_send(); + } else + packet_disconnect( + "No open channels after timeout!"); + } + } } /* @@ -701,6 +742,19 @@ server_loop2(void) } void +server_input_channel_failure(int type, int plen, void *ctxt) +{ + debug("Got CHANNEL_FAILURE for keepalive"); + /* + * reset timeout, since we got a sane answer from the client. + * even if this was generated by something other than + * the bogus CHANNEL_REQUEST we send for keepalives. + */ + client_alive_timeouts = 0; +} + + +void server_input_stdin_data(int type, int plen, void *ctxt) { char *data; @@ -912,7 +966,8 @@ server_init_dispatch_20(void) dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); - + /* client_alive */ + dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_channel_failure); /* rekeying */ dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); } @@ -949,3 +1004,4 @@ server_init_dispatch(void) else server_init_dispatch_15(); } + diff --git a/usr.bin/ssh/sshd.8 b/usr.bin/ssh/sshd.8 index da95eaef717..887cc3ba37a 100644 --- a/usr.bin/ssh/sshd.8 +++ b/usr.bin/ssh/sshd.8 @@ -34,7 +34,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.114 2001/04/11 16:25:31 lebel Exp $ +.\" $OpenBSD: sshd.8,v 1.115 2001/04/13 22:46:54 beck Exp $ .Dd September 25, 1999 .Dt SSHD 8 .Os @@ -363,6 +363,31 @@ Specifies whether should check for new mail for interactive logins. The default is .Dq no . +.It Cm ClientAliveInterval +Sets a timeout interval in seconds after which if no data has been received +from the client, +.Nm +will send a message through the encrypted +channel to request a response from the client. This may only be +used on a server supporting only protocol version 2. The default +is 0, indicating that these messages will not be sent to the client. +.It Cm ClientAliveCountMax +Sets the number of client alive messages (see above) which may be +sent without +.Nm +receiving any messages back from the client. If this threshold is +reached while client alive messages are being sent, +.Nm +will disconnect the client, terminating the session. It is important +to note that the use of client alive messages is very different from +Keepalive (below). The client alive messages are sent through the +encrypted channel and therefore will not be spoofable. The TCP keepalive +option enable by Keepalive is spoofable. You want to use the client +alive mechanism when you are basing something important on +clients having an active connection to the server. + The default is value is 3. If you set ClientAliveInterval +(above) to 15, and leave this value at the default, unresponsive ssh clients +will be disconnected after approximately 45 seconds. .It Cm DenyGroups This keyword can be followed by a number of group names, separated by spaces. |