Basic configurability for client-initiated rekeys.
authorjacob <jacob@cda61777-01e9-0310-a592-d414129be87e>
Fri, 24 Dec 2004 13:39:32 +0000 (13:39 +0000)
committerjacob <jacob@cda61777-01e9-0310-a592-d414129be87e>
Fri, 24 Dec 2004 13:39:32 +0000 (13:39 +0000)
git-svn-id: svn://svn.tartarus.org/sgt/putty@5027 cda61777-01e9-0310-a592-d414129be87e

config.c
doc/config.but
doc/using.but
misc.c
misc.h
putty.h
settings.c
ssh.c
windows/winhelp.h

index 51d0f73..6511502 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1580,11 +1580,23 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                          kexlist_handler, P(NULL));
        c->listbox.height = 5;
 
-#if 0
        s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
                        "Options controlling key re-exchange");
-       /* FIXME: at least time and data size */
-#endif
+
+       /* FIXME: these could usefully be configured mid-session in SSH-2.
+        *        (So could cipher/compression/kex, now we have rekey.) */
+       ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
+                    HELPCTX(ssh_kex_repeat),
+                    dlg_stdeditbox_handler,
+                    I(offsetof(Config,ssh_rekey_time)),
+                    I(-1));
+       ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'd', 20,
+                    HELPCTX(ssh_kex_repeat),
+                    dlg_stdeditbox_handler,
+                    I(offsetof(Config,ssh_rekey_data)),
+                    I(16));
+       ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
+                 HELPCTX(ssh_kex_repeat));
 
        /*
         * The Connection/SSH/Auth panel.
index badb0d9..f125817 100644 (file)
@@ -2152,22 +2152,56 @@ If the first algorithm PuTTY finds is below the \q{warn below here}
 line, you will see a warning box when you make the connection, similar
 to that for cipher selection (see \k{config-ssh-encryption}).
 
-\# [Repeat key exchange bumph when config is added:] If the session
-key negotiated at connection startup is used too much or for too long,
-it may become feasible to mount attacks against the SSH connection.
-Therefore, the SSH protocol specifies that a new key exchange should
-take place every so often.
+\S{config-ssh-kex-rekey} Repeat key exchange
 
-\# While this renegotiation is taking place, no data can pass through
+\cfg{winhelp-topic}{ssh.kex.repeat}
+
+If the session key negotiated at connection startup is used too much
+or for too long, it may become feasible to mount attacks against the
+SSH connection. Therefore, the SSH-2 protocol specifies that a new key
+exchange should take place every so often; this can be initiated by
+either the client or the server.
+
+While this renegotiation is taking place, no data can pass through
 the SSH connection, so it may appear to \q{freeze}. (The occurrence of
 repeat key exchange is noted in the Event Log; see
 \k{using-eventlog}.) Usually the same algorithm is used as at the
 start of the connection, with a similar overhead.
 
-\# [When options are added to frob how often this happens, we should
-hardcode the values recommended by the drafts -- 1 hour, 1GB -- in
-this documentation, in case PuTTY's defaults are obscured by Default
-Settings etc. Assuming we think they're good advice, that is.]
+These options control how often PuTTY will initiate a repeat key
+exchange (\q{rekey}). You can also force a key exchange at any time
+from the Special Commands menu (see \k{using-specials}).
+
+\# FIXME: do we have any additions to the SSH-2 drafts' advice on
+these values? Do we want to enforce any limits?
+
+\b \q{Max minutes before rekey} specifies the amount of time that is
+allowed to elapse before a rekey is initiated. If this is set to zero,
+PuTTY will not rekey due to elapsed time. The SSH-2 protocol
+specification recommends a timeout of at most 60 minutes.
+
+\b \q{Max data before rekey} specifies the amount of data (in bytes)
+that is permitted to flow in either direction before a rekey is
+initiated. If this is set to zero, PuTTY will not rekey due to
+transferred data. The SSH-2 protocol specification recommends a limit
+of at most 1 gigabyte.
+
+\lcont{
+
+As well as specifying a value in bytes, the following shorthand can be
+used:
+
+\b \cq{1k} specifies 1 kilobyte (1024 bytes).
+
+\b \cq{1M} specifies 1 megabyte (1024 kilobytes).
+
+\b \cq{1G} specifies 1 gigabyte (1024 megabytes).
+
+}
+
+PuTTY can be prevented from initiating a rekey entirely by setting
+both of these values to zero. (Note, however, that the SSH server may
+still initiate rekeys.)
 
 \H{config-ssh-auth} The Auth panel
 
index 59109b6..8ab8b40 100644 (file)
@@ -185,8 +185,8 @@ Should have no effect.
 
 \lcont{
 Only available in SSH-2. Forces a repeat key exchange immediately (and
-resets associated timers and counters). \#{For more information about
-repeat key exchanges, see \k{FIXME}.}
+resets associated timers and counters). For more information about
+repeat key exchanges, see \k{config-ssh-kex-rekey}.
 }
 
 \b \I{Break, SSH special command}Break
diff --git a/misc.c b/misc.c
index 474bf02..59e91f2 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -9,6 +9,39 @@
 #include <assert.h>
 #include "putty.h"
 
+/*
+ * Parse a string block size specification. This is approximately a
+ * subset of the block size specs supported by GNU fileutils:
+ *  "nk" = n kilobytes
+ *  "nM" = n megabytes
+ *  "nG" = n gigabytes
+ * All numbers are decimal, and suffixes refer to powers of two.
+ * Case-insensitive.
+ */
+unsigned long parse_blocksize(const char *bs)
+{
+    char *suf;
+    unsigned long r = strtoul(bs, &suf, 10);
+    if (*suf != '\0') {
+       while (isspace(*suf)) suf++;
+       switch (*suf) {
+         case 'k': case 'K':
+           r *= 1024ul;
+           break;
+         case 'm': case 'M':
+           r *= 1024ul * 1024ul;
+           break;
+         case 'g': case 'G':
+           r *= 1024ul * 1024ul * 1024ul;
+           break;
+         case '\0':
+         default:
+           break;
+       }
+    }
+    return r;
+}
+
 /* ----------------------------------------------------------------------
  * String handling routines.
  */
diff --git a/misc.h b/misc.h
index cd144b5..7739446 100644 (file)
--- a/misc.h
+++ b/misc.h
@@ -16,6 +16,8 @@
 typedef struct Filename Filename;
 typedef struct FontSpec FontSpec;
 
+unsigned long parse_blocksize(const char *bs);
+
 char *dupstr(const char *s);
 char *dupcat(const char *s1, ...);
 char *dupprintf(const char *fmt, ...);
diff --git a/putty.h b/putty.h
index 5ffd73c..4b0e3fe 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -400,6 +400,8 @@ struct config_tag {
     int nopty;
     int compression;
     int ssh_kexlist[KEX_MAX];
+    int ssh_rekey_time;                       /* in minutes */
+    char ssh_rekey_data[16];
     int agentfwd;
     int change_username;              /* allow username switching in SSH2 */
     int ssh_cipherlist[CIPHER_MAX];
index 711ef88..5143bef 100644 (file)
@@ -236,6 +236,8 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg)
     wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX,
           cfg->ssh_cipherlist);
     wprefs(sesskey, "KEX", kexnames, KEX_MAX, cfg->ssh_kexlist);
+    write_setting_i(sesskey, "RekeyTime", cfg->ssh_rekey_time);
+    write_setting_s(sesskey, "RekeyBytes", cfg->ssh_rekey_data);
     write_setting_i(sesskey, "AuthTIS", cfg->try_tis_auth);
     write_setting_i(sesskey, "AuthKI", cfg->try_ki_auth);
     write_setting_i(sesskey, "SshNoShell", cfg->ssh_no_shell);
@@ -514,6 +516,9 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
        gprefs(sesskey, "KEX", default_kexes,
               kexnames, KEX_MAX, cfg->ssh_kexlist);
     }
+    gppi(sesskey, "RekeyTime", 60, &cfg->ssh_rekey_time);
+    gpps(sesskey, "RekeyBytes", "1G", cfg->ssh_rekey_data,
+        sizeof(cfg->ssh_rekey_data));
     gppi(sesskey, "SshProt", 2, &cfg->sshprot);
     gppi(sesskey, "SSH2DES", 0, &cfg->ssh2_des_cbc);
     gppi(sesskey, "AuthTIS", 0, &cfg->try_tis_auth);
diff --git a/ssh.c b/ssh.c
index e59afc2..63f200b 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -708,13 +708,11 @@ struct ssh_tag {
      * size-based rekeys.
      */
     unsigned long incoming_data_size, outgoing_data_size, deferred_data_size;
+    unsigned long max_data_size;
     int kex_in_progress;
     long next_rekey;
 };
 
-#define MAX_DATA_BEFORE_REKEY (0x40000000UL)
-#define REKEY_TIMEOUT (3600 * TICKSPERSEC)
-
 #define logevent(s) logevent(ssh->frontend, s)
 
 /* logevent, only printf-formatted. */
@@ -1653,7 +1651,8 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt)
 
     ssh->outgoing_data_size += pkt->encrypted_len;
     if (!ssh->kex_in_progress &&
-       ssh->outgoing_data_size > MAX_DATA_BEFORE_REKEY)
+       ssh->max_data_size != 0 &&
+       ssh->outgoing_data_size > ssh->max_data_size)
        do_ssh2_transport(ssh, "Initiating key re-exchange "
                          "(too much data sent)", -1, NULL);
 
@@ -1743,7 +1742,8 @@ static void ssh_pkt_defersend(Ssh ssh)
 
     ssh->outgoing_data_size += ssh->deferred_data_size;
     if (!ssh->kex_in_progress &&
-       ssh->outgoing_data_size > MAX_DATA_BEFORE_REKEY)
+       ssh->max_data_size != 0 &&
+       ssh->outgoing_data_size > ssh->max_data_size)
        do_ssh2_transport(ssh, "Initiating key re-exchange "
                          "(too much data sent)", -1, NULL);
     ssh->deferred_data_size = 0;
@@ -4915,8 +4915,10 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen,
      * Key exchange is over. Schedule a timer for our next rekey.
      */
     ssh->kex_in_progress = FALSE;
-    ssh->next_rekey = schedule_timer(REKEY_TIMEOUT, ssh2_timer, ssh);
-
+    if (ssh->cfg.ssh_rekey_time != 0)
+       ssh->next_rekey = schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
+                                        ssh2_timer, ssh);
+    
     /*
      * If this is the first key exchange phase, we must pass the
      * SSH2_MSG_NEWKEYS packet to the next layer, not because it
@@ -7087,7 +7089,8 @@ static void ssh2_protocol(Ssh ssh, unsigned char *in, int inlen,
     if (pktin) {
        ssh->incoming_data_size += pktin->encrypted_len;
        if (!ssh->kex_in_progress &&
-           ssh->incoming_data_size > MAX_DATA_BEFORE_REKEY)
+           ssh->max_data_size != 0 &&
+           ssh->incoming_data_size > ssh->max_data_size)
            do_ssh2_transport(ssh, "Initiating key re-exchange "
                              "(too much data received)", -1, NULL);
     }
@@ -7209,6 +7212,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
 
     ssh->incoming_data_size = ssh->outgoing_data_size =
        ssh->deferred_data_size = 0L;
+    ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data);
     ssh->kex_in_progress = FALSE;
 
     p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);
index 63f25ad..cb0afed 100644 (file)
@@ -87,6 +87,7 @@
 #define WINHELP_CTX_ssh_command "ssh.command"
 #define WINHELP_CTX_ssh_compress "ssh.compress"
 #define WINHELP_CTX_ssh_kexlist "ssh.kex.order"
+#define WINHELP_CTX_ssh_kex_repeat "ssh.kex.repeat"
 #define WINHELP_CTX_ssh_auth_privkey "ssh.auth.privkey"
 #define WINHELP_CTX_ssh_auth_agentfwd "ssh.auth.agentfwd"
 #define WINHELP_CTX_ssh_auth_changeuser "ssh.auth.changeuser"