Re: [Christoph Martin: idle-timeout]
"Aaron M. Ucko" <ucko@debian.org> writes:
> Ah, well, it never hurts to ask. :-) In that case, I'll see what I
> can do about updating Jani's patch this weekend.
OK, please try this patch relative to the latest Debian version
(3.0.2p1-8.3). I have verified that it looks sane and compiles
happily, but have not yet actually run it, so you may wish to exercise
some caution.
diff -u ../openssh-3.0.2p1/CREDITS openssh-3.0.2p1/CREDITS
--- ../openssh-3.0.2p1/CREDITS Sun Nov 11 18:34:22 2001
+++ openssh-3.0.2p1/CREDITS Fri Apr 5 21:50:00 2002
@@ -42,6 +42,7 @@
IWAMURO Motonori <iwa@mmp.fujitsu.co.jp> - bugfixes
Jani Hakala <jahakala@cc.jyu.fi> - Patches
Jarno Huuskonen <jhuuskon@hytti.uku.fi> - Bugfixes
+Jani Jaakkola <jjaakkol@cs.helsinki.fi> - IdleTimeOut
Jim Knoble <jmknoble@jmknoble.cx> - Many patches
Jonchen (email unknown) - the original author of PAM support of SSH
Juergen Keil <jk@tools.de> - scp bugfixing
diff -u ../openssh-3.0.2p1/packet.c openssh-3.0.2p1/packet.c
--- ../openssh-3.0.2p1/packet.c Sat Mar 23 21:15:29 2002
+++ openssh-3.0.2p1/packet.c Fri Apr 5 23:18:39 2002
@@ -113,12 +113,110 @@
/* Set to true if the connection is interactive. */
static int interactive_mode = 0;
+static time_t idletime_last=0; /* The last time something happened
+ * for idletimeout. */
+static int idletimeout=0; /* The current idletimeout */
+
/* Session key information for Encryption and MAC */
Newkeys *newkeys[MODE_MAX];
/* roundup current message to extra_pad bytes */
static u_char extra_pad = 0;
+
+/* This sets the maximum idle time before packet_select() automatically
+ * disconnects with packet_disconnect("Idletimeout").
+ * Never autodisconnects if set to zero. zero is the default */
+void
+packet_set_idletimeout(int max_idle_seconds)
+{
+ idletimeout=max_idle_seconds;
+ if (max_idle_seconds>0) {
+ /* Initialize */
+ time(&idletime_last);
+ }
+}
+
+/* Called by whenever packets are sent or received.
+ * This function decides on which packets idletimeout should
+ * be reset */
+void
+idletimeout_check(int type)
+{
+ /* No-op, if idletimeouts are not configured */
+ if (idletimeout==0) return;
+
+ /* The following packets reset idletimeout on input or output.
+ * Note that only actual data resets idletimeout, control packets
+ * do not. */
+ if (compat20) {
+ switch(type) {
+ case SSH2_MSG_CHANNEL_DATA:
+ case SSH2_MSG_CHANNEL_EXTENDED_DATA:
+ time(&idletime_last);
+ }
+ } else {
+ switch(type) {
+ case SSH_MSG_CHANNEL_DATA:
+ case SSH_CMSG_STDIN_DATA:
+ case SSH_SMSG_STDOUT_DATA:
+ case SSH_SMSG_STDERR_DATA:
+ time(&idletime_last);
+ }
+ }
+}
+
+/* This is an quite normal select, except that it implements idletimeouts
+ * set with packet_set_idletimeout().
+ * It also returns exits, if select() returns any other error than AGAIN
+ * or EINTR. So if packet_select returns -1, you can safely reinit fd_sets
+ * and call packet_select again, without checking errno.
+ */
+int
+packet_select(int maxfds,
+ fd_set *readset, fd_set *writeset, fd_set *exceptset,
+ int max_time_milliseconds)
+{
+ struct timeval tv, *tvp=NULL;
+ int ret;
+
+ if (idletimeout>0) {
+ /* Count the time to idletimeout */
+ time_t diff=time(NULL)-idletime_last;
+ if (diff>=idletimeout)
+ tv.tv_sec=1;
+ else
+ tv.tv_sec=idletimeout-diff+1;
+ tv.tv_usec=0;
+ tvp = &tv;
+ debug("idletimeout after %ld seconds",tv.tv_sec);
+ }
+ /* If a timeout value was given and the timeout happens before
+ * idletimeout, use it */
+ if (max_time_milliseconds>0 &&
+ (tvp==NULL || max_time_milliseconds/1000<tv.tv_sec)) {
+ tv.tv_sec = max_time_milliseconds / 1000;
+ tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
+ tvp = &tv;
+ debug("timeout after %d milliseconds",max_time_milliseconds);
+ }
+ if (tvp==NULL) debug("no timeout");
+
+ ret = select( maxfds, readset, writeset, exceptset, tvp );
+ if (ret<0 && errno!=EAGAIN && errno!=EINTR) {
+ fatal("select: %.100s", strerror(errno));
+ }
+
+ /* Disconnect on idletimeout */
+ if (idletimeout>0 && ret==0 &&
+ time(NULL)-idletime_last>idletimeout) {
+ packet_disconnect("Idletimeout.");
+ idletimeout=0;
+ }
+ return ret;
+}
+
+
/*
* Sets the descriptors used for communication. Disables encryption until
* packet_set_encryption_key is called.
@@ -316,6 +414,7 @@
buf[len - 1] = type;
buffer_clear(&outgoing_packet);
buffer_append(&outgoing_packet, buf, len);
+ idletimeout_check(type);
}
/* Append payload. */
@@ -617,7 +716,6 @@
int type, len;
fd_set *setp;
char buf[8192];
- struct timeval tv, *tvp;
DBG(debug("packet_read()"));
setp = (fd_set *)xmalloc(howmany(connection_in+1, NFDBITS) *
@@ -639,27 +737,21 @@
/* If we got a packet, return it. */
if (type != SSH_MSG_NONE) {
xfree(setp);
+ idletimeout_check(type);
return type;
}
/*
* Otherwise, wait for some data to arrive, add it to the
* buffer, and try again.
*/
- memset(setp, 0, howmany(connection_in + 1, NFDBITS) *
- sizeof(fd_mask));
- FD_SET(connection_in, setp);
-
- if (setup_timeout > 0) {
- tvp = &tv;
- tv.tv_sec = setup_timeout;
- tv.tv_usec = 0;
- } else
- tvp = 0;
-
- /* Wait for some data to arrive. */
- while (select(connection_in + 1, setp, NULL, NULL, tvp) == -1 &&
- (errno == EAGAIN || errno == EINTR))
- ;
+ do {
+ memset(setp, 0, howmany(connection_in + 1, NFDBITS) *
+ sizeof(fd_mask));
+ FD_SET(connection_in, setp);
+
+ /* Wait for some data to arrive. */
+ } while (packet_select(connection_in + 1, setp, NULL, NULL,
+ setup_timeout * 1000 * 1000) == -1);
if (!FD_ISSET(connection_in, setp))
fatal("packet_read: Setup timeout expired, giving up");
@@ -908,6 +1000,11 @@
char *msg;
for (;;) {
+ int type = compat20 ?
+ packet_read_poll2(payload_len_ptr):
+ packet_read_poll1(payload_len_ptr);
+
+ idletimeout_check(type);
if (compat20) {
type = packet_read_poll2(payload_len_ptr);
if (type)
@@ -1152,12 +1249,12 @@
sizeof(fd_mask));
packet_write_poll();
while (packet_have_data_to_write()) {
- memset(setp, 0, howmany(connection_out + 1, NFDBITS) *
- sizeof(fd_mask));
- FD_SET(connection_out, setp);
- while (select(connection_out + 1, NULL, setp, NULL, NULL) == -1 &&
- (errno == EAGAIN || errno == EINTR))
- ;
+ do {
+ memset(setp, 0, howmany(connection_out + 1, NFDBITS) *
+ sizeof(fd_mask));
+ FD_SET(connection_out, setp);
+ } while (packet_select(connection_out + 1,
+ NULL, setp, NULL, 0) == -1);
packet_write_poll();
}
xfree(setp);
diff -u ../openssh-3.0.2p1/packet.h openssh-3.0.2p1/packet.h
--- ../openssh-3.0.2p1/packet.h Sat Mar 23 21:15:29 2002
+++ openssh-3.0.2p1/packet.h Fri Apr 5 21:56:52 2002
@@ -65,6 +65,11 @@
void packet_send_ignore(int);
void packet_add_padding(u_char);
+void packet_set_idletimeout(int max_idle_seconds);
+int packet_select(int maxfds,
+ fd_set *readset, fd_set *writeset, fd_set *exceptset,
+ int max_time_milliseconds);
+
void tty_make_modes(int, struct termios *);
void tty_parse_modes(int, int *);
diff -u ../openssh-3.0.2p1/servconf.c openssh-3.0.2p1/servconf.c
--- ../openssh-3.0.2p1/servconf.c Tue Nov 13 08:03:15 2001
+++ openssh-3.0.2p1/servconf.c Fri Apr 5 22:12:39 2002
@@ -109,6 +109,7 @@
options->client_alive_count_max = -1;
options->authorized_keys_file = NULL;
options->authorized_keys_file2 = NULL;
+ options->idletimeout = -1;
}
void
@@ -229,6 +230,8 @@
}
if (options->authorized_keys_file == NULL)
options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS;
+ if (options->idletimeout == -1)
+ options->idletimeout=0;
}
/* Keyword tokens. */
@@ -261,7 +264,7 @@
sBanner, sReverseMappingCheck, sHostbasedAuthentication,
sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
- sDeprecated
+ sIdleTimeout, sDeprecated
} ServerOpCodes;
/* Textual representation of the tokens. */
@@ -334,6 +337,7 @@
{ "clientalivecountmax", sClientAliveCountMax },
{ "authorizedkeysfile", sAuthorizedKeysFile },
{ "authorizedkeysfile2", sAuthorizedKeysFile2 },
+ { "idletimeout", sIdleTimeout },
{ NULL, 0 }
};
@@ -859,6 +863,28 @@
intptr = &options->client_alive_count_max;
goto parse_int;
+ case sIdleTimeout:
+ arg = strdelim(&cp);
+ if (!arg || *arg == '\0')
+ fatal("%s line %d: Missing IdleTimeout argument",
+ filename,linenum);
+ options->idletimeout=atoi(arg);
+ switch(arg[strlen(arg)-1]) {
+ case 'w': options->idletimeout*=7;
+ case 'd': options->idletimeout*=24;
+ case 'h': options->idletimeout*=60;
+ case 'm': options->idletimeout*=60;
+ case 's':
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9':
+ break;
+ default:
+ fatal("%s line %d: Invalid IdleTimeout argument",
+ filename,linenum);
+ }
+ break;
+
case sDeprecated:
log("%s line %d: Deprecated option %s",
filename, linenum, arg);
diff -u ../openssh-3.0.2p1/servconf.h openssh-3.0.2p1/servconf.h
--- ../openssh-3.0.2p1/servconf.h Wed Sep 12 12:40:06 2001
+++ openssh-3.0.2p1/servconf.h Fri Apr 5 22:53:09 2002
@@ -129,6 +129,10 @@
char *authorized_keys_file; /* File containing public keys */
char *authorized_keys_file2;
int pam_authentication_via_kbd_int;
+ int idletimeout; /*
+ * If nonzero, the number of second
+ * after which idle connections
+ * will be terminated */
} ServerOptions;
diff -u ../openssh-3.0.2p1/serverloop.c openssh-3.0.2p1/serverloop.c
--- ../openssh-3.0.2p1/serverloop.c Sat Mar 23 21:15:29 2002
+++ openssh-3.0.2p1/serverloop.c Fri Apr 5 23:21:41 2002
@@ -190,7 +190,6 @@
wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
int *nallocp, u_int max_time_milliseconds)
{
- struct timeval tv, *tvp;
int ret;
int client_alive_scheduled = 0;
@@ -208,6 +207,9 @@
max_time_milliseconds = options.client_alive_interval * 1000;
}
+ /* When select fails we restart from here. */
+retry_select:
+
/* Allocate and update select() masks for channel descriptors. */
channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 0);
@@ -258,25 +260,16 @@
if (max_time_milliseconds == 0 || client_alive_scheduled)
max_time_milliseconds = 100;
- if (max_time_milliseconds == 0)
- tvp = NULL;
- else {
- tv.tv_sec = max_time_milliseconds / 1000;
- tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
- tvp = &tv;
- }
- if (tvp!=NULL)
- debug3("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds);
- /* Wait for something to happen, or the timeout to expire. */
- ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
+ /* Wait for something to happen, or the timeout to expire.
+ * packet select also implements server idletimeouts for us. */
+ ret = packet_select((*maxfdp)+1, *readsetp, *writesetp, NULL,
+ max_time_milliseconds);
+
+ if (ret == -1)
+ goto retry_select;
- if (ret == -1) {
- memset(*readsetp, 0, *nallocp);
- memset(*writesetp, 0, *nallocp);
- if (errno != EINTR)
- error("select: %.100s", strerror(errno));
- } else if (ret == 0 && client_alive_scheduled)
+ if (ret == 0 && client_alive_scheduled)
client_alive_check();
}
diff -u ../openssh-3.0.2p1/session.c openssh-3.0.2p1/session.c
--- ../openssh-3.0.2p1/session.c Sat Dec 1 18:37:08 2001
+++ openssh-3.0.2p1/session.c Fri Apr 5 23:05:36 2002
@@ -174,6 +174,11 @@
* authentication.
*/
alarm(0);
+ /*
+ * Now that the login grace alarm is cleared it is time to apply
+ * idletimeout */
+ packet_set_idletimeout(options.idletimeout);
+
if (startup_pipe != -1) {
close(startup_pipe);
startup_pipe = -1;
diff -u ../openssh-3.0.2p1/sshd.8 openssh-3.0.2p1/sshd.8
--- ../openssh-3.0.2p1/sshd.8 Sat Mar 23 21:15:29 2002
+++ openssh-3.0.2p1/sshd.8 Fri Apr 5 23:16:28 2002
@@ -483,6 +483,8 @@
or
.Dq rsa
are used for version 2 of the SSH protocol.
+.It Cm IdleTimeout
+Specifies ...
.It Cm IgnoreRhosts
Specifies that
.Pa .rhosts
--
Aaron M. Ucko, KB1CJC (amu at alum.mit.edu, ucko at debian.org)
Finger amu@monk.mit.edu (NOT a valid e-mail address) for more info.
--
To UNSUBSCRIBE, email to debian-ssh-request@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmaster@lists.debian.org
Reply to: