"Aaron M. Ucko" <[EMAIL PROTECTED]> 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 <[EMAIL PROTECTED]> - bugfixes
 Jani Hakala <[EMAIL PROTECTED]> - Patches
 Jarno Huuskonen <[EMAIL PROTECTED]> - Bugfixes
+Jani Jaakkola <[EMAIL PROTECTED]> - IdleTimeOut
 Jim Knoble <[EMAIL PROTECTED]> - Many patches
 Jonchen (email unknown) - the original author of PAM support of SSH
 Juergen Keil <[EMAIL PROTECTED]> - 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 [EMAIL PROTECTED] (NOT a valid e-mail address) for more info.


-- 
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to