[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: