From: jacob Date: Fri, 24 Dec 2004 13:39:32 +0000 (+0000) Subject: Basic configurability for client-initiated rekeys. X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/commitdiff_plain/d57f70afa40c24426e5f936c86f7640801d43f7a Basic configurability for client-initiated rekeys. git-svn-id: svn://svn.tartarus.org/sgt/putty@5027 cda61777-01e9-0310-a592-d414129be87e --- diff --git a/config.c b/config.c index 51d0f735..6511502e 100644 --- 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. diff --git a/doc/config.but b/doc/config.but index badb0d99..f1258176 100644 --- a/doc/config.but +++ b/doc/config.but @@ -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 diff --git a/doc/using.but b/doc/using.but index 59109b6f..8ab8b40e 100644 --- a/doc/using.but +++ b/doc/using.but @@ -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 474bf024..59e91f2f 100644 --- a/misc.c +++ b/misc.c @@ -9,6 +9,39 @@ #include #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 cd144b53..77394469 100644 --- 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 5ffd73ce..4b0e3fef 100644 --- 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]; diff --git a/settings.c b/settings.c index 711ef88f..5143bef9 100644 --- a/settings.c +++ b/settings.c @@ -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 e59afc26..63f200be 100644 --- 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); diff --git a/windows/winhelp.h b/windows/winhelp.h index 63f25adf..cb0afed9 100644 --- a/windows/winhelp.h +++ b/windows/winhelp.h @@ -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"