- dtucker@cvs.openbsd.org 2013/05/16 02:00:34
     [ssh_config sshconnect2.c packet.c readconf.h readconf.c clientloop.c
     ssh_config.5 packet.h]
     Add an optional second argument to RekeyLimit in the client to allow
     rekeying based on elapsed time in addition to amount of traffic.
     with djm@ jmc@, ok djm
diff --git a/ChangeLog b/ChangeLog
index 7f42492..85a5edc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -22,6 +22,12 @@
    - dtucker@cvs.openbsd.org 2013/05/10 10:13:50
      [ssh-pkcs11-helper.c]
      remove unused extern optarg.  ok markus@
+   - dtucker@cvs.openbsd.org 2013/05/16 02:00:34
+     [ssh_config sshconnect2.c packet.c readconf.h readconf.c clientloop.c
+     ssh_config.5 packet.h]
+     Add an optional second argument to RekeyLimit in the client to allow
+     rekeying based on elapsed time in addition to amount of traffic.
+     with djm@ jmc@, ok djm
 
 20130510
  - (dtucker) [configure.ac] Enable -Wsizeof-pointer-memaccess if the compiler
diff --git a/clientloop.c b/clientloop.c
index c1d1d44..f1b108f 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.248 2013/01/02 00:32:07 djm Exp $ */
+/* $OpenBSD: clientloop.c,v 1.249 2013/05/16 02:00:34 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -583,7 +583,7 @@
 {
 	struct timeval tv, *tvp;
 	int timeout_secs;
-	time_t minwait_secs = 0;
+	time_t minwait_secs = 0, server_alive_time = 0, now = time(NULL);
 	int ret;
 
 	/* Add any selections by the channel mechanism. */
@@ -632,12 +632,16 @@
 	 */
 
 	timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */
-	if (options.server_alive_interval > 0 && compat20)
+	if (options.server_alive_interval > 0 && compat20) {
 		timeout_secs = options.server_alive_interval;
+		server_alive_time = now + options.server_alive_interval;
+	}
+	if (options.rekey_interval > 0 && compat20 && !rekeying)
+		timeout_secs = MIN(timeout_secs, packet_get_rekey_timeout());
 	set_control_persist_exit_time();
 	if (control_persist_exit_time > 0) {
 		timeout_secs = MIN(timeout_secs,
-			control_persist_exit_time - time(NULL));
+			control_persist_exit_time - now);
 		if (timeout_secs < 0)
 			timeout_secs = 0;
 	}
@@ -669,8 +673,15 @@
 		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
 		buffer_append(&stderr_buffer, buf, strlen(buf));
 		quit_pending = 1;
-	} else if (ret == 0)
-		server_alive_check();
+	} else if (ret == 0) {
+		/*
+		 * Timeout.  Could have been either keepalive or rekeying.
+		 * Keepalive we check here, rekeying is checked in clientloop.
+		 */
+		if (server_alive_time != 0 && server_alive_time <= time(NULL))
+			server_alive_check();
+	}
+
 }
 
 static void
diff --git a/packet.c b/packet.c
index 3c97383..dd9d26f 100644
--- a/packet.c
+++ b/packet.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.183 2013/04/19 01:06:50 djm Exp $ */
+/* $OpenBSD: packet.c,v 1.184 2013/05/16 02:00:34 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -58,6 +58,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <signal.h>
+#include <time.h>
 
 #include "xmalloc.h"
 #include "buffer.h"
@@ -165,9 +166,14 @@
 	Newkeys *newkeys[MODE_MAX];
 	struct packet_state p_read, p_send;
 
+	/* Volume-based rekeying */
 	u_int64_t max_blocks_in, max_blocks_out;
 	u_int32_t rekey_limit;
 
+	/* Time-based rekeying */
+	time_t rekey_interval;	/* how often in seconds */
+	time_t rekey_time;	/* time of last rekeying */
+
 	/* Session key for protocol v1 */
 	u_char ssh1_key[SSH_SESSION_KEY_LENGTH];
 	u_int ssh1_keylen;
@@ -1009,6 +1015,7 @@
 	/* after a NEWKEYS message we can send the complete queue */
 	if (type == SSH2_MSG_NEWKEYS) {
 		active_state->rekeying = 0;
+		active_state->rekey_time = time(NULL);
 		while ((p = TAILQ_FIRST(&active_state->outgoing))) {
 			type = p->type;
 			debug("dequeue packet: %u", type);
@@ -1933,13 +1940,33 @@
 	    (active_state->max_blocks_out &&
 	        (active_state->p_send.blocks > active_state->max_blocks_out)) ||
 	    (active_state->max_blocks_in &&
-	        (active_state->p_read.blocks > active_state->max_blocks_in));
+	        (active_state->p_read.blocks > active_state->max_blocks_in)) ||
+	    (active_state->rekey_interval != 0 && active_state->rekey_time +
+		 active_state->rekey_interval <= time(NULL));
 }
 
 void
-packet_set_rekey_limit(u_int32_t bytes)
+packet_set_rekey_limits(u_int32_t bytes, time_t seconds)
 {
+	debug3("rekey after %lld bytes, %d seconds", (long long)bytes,
+	    (int)seconds);
 	active_state->rekey_limit = bytes;
+	active_state->rekey_interval = seconds;
+	/*
+	 * We set the time here so that in post-auth privsep slave we count
+	 * from the completion of the authentication.
+	 */
+	active_state->rekey_time = time(NULL);
+}
+
+time_t
+packet_get_rekey_timeout(void)
+{
+	time_t seconds;
+
+	seconds = active_state->rekey_time + active_state->rekey_interval -
+	    time(NULL);
+	return (seconds < 0 ? 0 : seconds);
 }
 
 void
diff --git a/packet.h b/packet.h
index 09ba079..bc548f2 100644
--- a/packet.h
+++ b/packet.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.h,v 1.57 2012/01/25 19:40:09 markus Exp $ */
+/* $OpenBSD: packet.h,v 1.58 2013/05/16 02:00:34 dtucker Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -115,7 +115,8 @@
 } while (0)
 
 int	 packet_need_rekeying(void);
-void	 packet_set_rekey_limit(u_int32_t);
+void	 packet_set_rekey_limits(u_int32_t, time_t);
+time_t	 packet_get_rekey_timeout(void);
 
 void	 packet_backup_state(void);
 void	 packet_restore_state(void);
diff --git a/readconf.c b/readconf.c
index 6e708e0..d8898a0 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.197 2013/03/06 23:36:53 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.198 2013/05/16 02:00:34 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -562,39 +562,54 @@
 	case oRekeyLimit:
 		arg = strdelim(&s);
 		if (!arg || *arg == '\0')
-			fatal("%.200s line %d: Missing argument.", filename, linenum);
-		if (arg[0] < '0' || arg[0] > '9')
-			fatal("%.200s line %d: Bad number.", filename, linenum);
-		orig = val64 = strtoll(arg, &endofnumber, 10);
-		if (arg == endofnumber)
-			fatal("%.200s line %d: Bad number.", filename, linenum);
-		switch (toupper(*endofnumber)) {
-		case '\0':
-			scale = 1;
-			break;
-		case 'K':
-			scale = 1<<10;
-			break;
-		case 'M':
-			scale = 1<<20;
-			break;
-		case 'G':
-			scale = 1<<30;
-			break;
-		default:
-			fatal("%.200s line %d: Invalid RekeyLimit suffix",
-			    filename, linenum);
+			fatal("%.200s line %d: Missing argument.", filename,
+			    linenum);
+		if (strcmp(arg, "default") == 0) {
+			val64 = 0;
+		} else {
+			if (arg[0] < '0' || arg[0] > '9')
+				fatal("%.200s line %d: Bad number.", filename,
+				    linenum);
+			orig = val64 = strtoll(arg, &endofnumber, 10);
+			if (arg == endofnumber)
+				fatal("%.200s line %d: Bad number.", filename,
+			 linenum);
+	 		switch (toupper(*endofnumber)) {
+			case '\0':
+				scale = 1;
+				break;
+			case 'K':
+				scale = 1<<10;
+				break;
+			case 'M':
+				scale = 1<<20;
+				break;
+			case 'G':
+				scale = 1<<30;
+				break;
+			default:
+				fatal("%.200s line %d: Invalid RekeyLimit "
+				    "suffix", filename, linenum);
+			}
+			val64 *= scale;
+			/* detect integer wrap and too-large limits */
+			if ((val64 / scale) != orig || val64 > UINT_MAX)
+				fatal("%.200s line %d: RekeyLimit too large",
+				    filename, linenum);
+			if (val64 != 0 && val64 < 16)
+				fatal("%.200s line %d: RekeyLimit too small",
+				    filename, linenum);
 		}
-		val64 *= scale;
-		/* detect integer wrap and too-large limits */
-		if ((val64 / scale) != orig || val64 > UINT_MAX)
-			fatal("%.200s line %d: RekeyLimit too large",
-			    filename, linenum);
-		if (val64 < 16)
-			fatal("%.200s line %d: RekeyLimit too small",
-			    filename, linenum);
 		if (*activep && options->rekey_limit == -1)
 			options->rekey_limit = (u_int32_t)val64;
+		if (s != NULL) { /* optional rekey interval present */
+			if (strcmp(s, "none") == 0) {
+				(void)strdelim(&s);	/* discard */
+				break;
+			}
+			intptr = &options->rekey_interval;
+			goto parse_time;
+		}
 		break;
 
 	case oIdentityFile:
@@ -1202,6 +1217,7 @@
 	options->no_host_authentication_for_localhost = - 1;
 	options->identities_only = - 1;
 	options->rekey_limit = - 1;
+	options->rekey_interval = -1;
 	options->verify_host_key_dns = -1;
 	options->server_alive_interval = -1;
 	options->server_alive_count_max = -1;
@@ -1337,6 +1353,8 @@
 		options->enable_ssh_keysign = 0;
 	if (options->rekey_limit == -1)
 		options->rekey_limit = 0;
+	if (options->rekey_interval == -1)
+		options->rekey_interval = 0;
 	if (options->verify_host_key_dns == -1)
 		options->verify_host_key_dns = 0;
 	if (options->server_alive_interval == -1)
diff --git a/readconf.h b/readconf.h
index 8416489..e205730 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.93 2013/02/22 04:45:09 dtucker Exp $ */
+/* $OpenBSD: readconf.h,v 1.94 2013/05/16 02:00:34 dtucker Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -110,6 +110,7 @@
 
 	int	enable_ssh_keysign;
 	int64_t rekey_limit;
+	int	rekey_interval;
 	int	no_host_authentication_for_localhost;
 	int	identities_only;
 	int	server_alive_interval;
diff --git a/ssh_config b/ssh_config
index 1893674..bb40819 100644
--- a/ssh_config
+++ b/ssh_config
@@ -1,4 +1,4 @@
-#	$OpenBSD: ssh_config,v 1.26 2010/01/11 01:39:46 dtucker Exp $
+#	$OpenBSD: ssh_config,v 1.27 2013/05/16 02:00:34 dtucker Exp $
 
 # This is the ssh client system-wide configuration file.  See
 # ssh_config(5) for more information.  This file provides defaults for
@@ -45,3 +45,4 @@
 #   PermitLocalCommand no
 #   VisualHostKey no
 #   ProxyCommand ssh -q -W %h:%p gateway.example.com
+#   RekeyLimit 1G 1h
diff --git a/ssh_config.5 b/ssh_config.5
index 269529c..97897e0 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -33,8 +33,8 @@
 .\" (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: ssh_config.5,v 1.161 2013/01/08 18:49:04 markus Exp $
-.Dd $Mdocdate: January 8 2013 $
+.\" $OpenBSD: ssh_config.5,v 1.162 2013/05/16 02:00:34 dtucker Exp $
+.Dd $Mdocdate: May 16 2013 $
 .Dt SSH_CONFIG 5
 .Os
 .Sh NAME
@@ -931,8 +931,9 @@
 This option applies to protocol version 2 only.
 .It Cm RekeyLimit
 Specifies the maximum amount of data that may be transmitted before the
-session key is renegotiated.
-The argument is the number of bytes, with an optional suffix of
+session key is renegotiated, optionally followed a maximum amount of
+time that may pass before the session key is renegotiated.
+The first argument is specified in bytes and may have a suffix of
 .Sq K ,
 .Sq M ,
 or
@@ -943,6 +944,17 @@
 and
 .Sq 4G ,
 depending on the cipher.
+The optional second value is specified in seconds and may use any of the
+units documented in the
+.Sx TIME FORMATS
+section of
+.Xr sshd_config 5 .
+The default value for
+.Cm RekeyLimit
+is
+.Dq default none ,
+which means that rekeying is performed after the cipher's default amount
+of data has been sent or received and no time based rekeying is done.
 This option applies to protocol version 2 only.
 .It Cm RemoteForward
 Specifies that a TCP port on the remote machine be forwarded over
diff --git a/sshconnect2.c b/sshconnect2.c
index c01efe3..7b83c59 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect2.c,v 1.195 2013/05/10 03:40:07 djm Exp $ */
+/* $OpenBSD: sshconnect2.c,v 1.196 2013/05/16 02:00:34 dtucker Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2008 Damien Miller.  All rights reserved.
@@ -197,8 +197,9 @@
 	if (options.kex_algorithms != NULL)
 		myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
 
-	if (options.rekey_limit)
-		packet_set_rekey_limit((u_int32_t)options.rekey_limit);
+	if (options.rekey_limit || options.rekey_interval)
+		packet_set_rekey_limits((u_int32_t)options.rekey_limit,
+		    (time_t)options.rekey_interval);
 
 	/* start key exchange */
 	kex = kex_setup(myproposal);