diff --git a/ChangeLog b/ChangeLog index 918c843bb..80140019f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,15 @@ - Sync with OpenBSD glob.c, strlcat.c and vis.c changes - Cygwin sftp/sftp-server binary mode patch from Corinna Vinschen + - OpenBSD CVS Sync + - beck@cvs.openbsd.org 2001/04/13 22:46:54 + [channels.c channels.h servconf.c servconf.h serverloop.c sshd.8] + Add options ClientAliveInterval and ClientAliveCountMax to sshd. + This gives the ability to do a "keepalive" via the encrypted channel + which can't be spoofed (unlike TCP keepalives). Useful for when you want + to use ssh connections to authenticate people for something, and know + relatively quickly when they are no longer authenticated. Disabled + by default (of course). ok markus@ 20010413 - OpenBSD CVS Sync @@ -5054,4 +5063,4 @@ - Wrote replacements for strlcpy and mkdtemp - Released 1.0pre1 -$Id: ChangeLog,v 1.1109 2001/04/13 14:28:42 djm Exp $ +$Id: ChangeLog,v 1.1110 2001/04/13 23:28:01 mouring Exp $ diff --git a/channels.c b/channels.c index a1aa937ae..f4f2c4942 100644 --- a/channels.c +++ b/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 #include @@ -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/channels.h b/channels.h index 23e6ece83..bf70a8f21 100644 --- a/channels.h +++ b/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/servconf.c b/servconf.c index f3d5068c0..f978c632b 100644 --- a/servconf.c +++ b/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 @@ -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/servconf.h b/servconf.h index 9b3a60f08..4c02c0f52 100644 --- a/servconf.h +++ b/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/serverloop.c b/serverloop.c index d6b360d9a..5a5b1e37f 100644 --- a/serverloop.c +++ b/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!"); + } + } } /* @@ -700,6 +741,19 @@ server_loop2(void) channel_stop_listening(); } +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) { @@ -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/sshd.8 b/sshd.8 index da95eaef7..887cc3ba3 100644 --- a/sshd.8 +++ b/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.