New SSH bug flag, for 'can't handle SSH2_MSG_IGNORE'. Another user
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Fri, 23 Apr 2010 18:32:15 +0000 (18:32 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Fri, 23 Apr 2010 18:32:15 +0000 (18:32 +0000)
today reported an SSH2_MSG_UNIMPLEMENTED from a Cisco router which
looks as if it was triggered by SSH2_MSG_IGNORE, so I'm
experimentally putting this flag in. Currently must be manually
enabled, though if it turns out to solve the user's problem then
I'll probably add at least one version string...

[Edited commit message: actually, I also committed in error a piece
of experimental code as part of this checkin. Serve me right for not
running 'svn diff' first.]

git-svn-id: svn://svn.tartarus.org/sgt/putty@8926 cda61777-01e9-0310-a592-d414129be87e

config.c
doc/config.but
putty.h
settings.c
ssh.c
terminal.c
windows/window.c

index 464a241..bc48e27 100644 (file)
--- a/config.c
+++ b/config.c
@@ -2284,6 +2284,9 @@ void setup_config_box(struct controlbox *b, int midsession,
            ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
                          HELPCTX(ssh_bugs_rsa1),
                          sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
            ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
                          HELPCTX(ssh_bugs_rsa1),
                          sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
+           ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
+                         HELPCTX(ssh_bugs_ignore2),
+                         sshbug_handler, I(offsetof(Config,sshbug_ignore2)));
            ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
                          HELPCTX(ssh_bugs_hmac2),
                          sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
            ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
                          HELPCTX(ssh_bugs_hmac2),
                          sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
index 27704ee..c36e818 100644 (file)
@@ -2948,9 +2948,6 @@ enabled when talking to a correct server, the session will succeed,
 but keepalives will not work and the session might be more
 vulnerable to eavesdroppers than it could be.
 
 but keepalives will not work and the session might be more
 vulnerable to eavesdroppers than it could be.
 
-This is an SSH-1-specific bug. No known SSH-2 server fails to deal
-with SSH-2 ignore messages.
-
 \S{config-ssh-bug-plainpw1} \q{Refuses all SSH-1 \i{password camouflage}}
 
 \cfg{winhelp-topic}{ssh.bugs.plainpw1}
 \S{config-ssh-bug-plainpw1} \q{Refuses all SSH-1 \i{password camouflage}}
 
 \cfg{winhelp-topic}{ssh.bugs.plainpw1}
@@ -2992,6 +2989,23 @@ will be impossible.
 
 This is an SSH-1-specific bug.
 
 
 This is an SSH-1-specific bug.
 
+\S{config-ssh-bug-ignore2} \q{Chokes on SSH-2 \i{ignore message}s}
+
+\cfg{winhelp-topic}{ssh.bugs.ignore2}
+
+An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol
+which can be sent from the client to the server, or from the server
+to the client, at any time. Either side is required to ignore the
+message whenever it receives it. PuTTY uses ignore messages in SSH-2
+to confuse the encrypted data stream and make it harder to
+cryptanalyse. It also uses ignore messages for connection
+\i{keepalives} (see \k{config-keepalive}).
+
+If it believes the server to have this bug, PuTTY will stop using
+ignore messages. If this bug is enabled when talking to a correct
+server, the session will succeed, but keepalives will not work and
+the session might be less cryptographically secure than it could be.
+
 \S{config-ssh-bug-hmac2} \q{Miscomputes SSH-2 HMAC keys}
 
 \cfg{winhelp-topic}{ssh.bugs.hmac2}
 \S{config-ssh-bug-hmac2} \q{Miscomputes SSH-2 HMAC keys}
 
 \cfg{winhelp-topic}{ssh.bugs.hmac2}
diff --git a/putty.h b/putty.h
index 6303968..ca232bc 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -592,7 +592,8 @@ struct config_tag {
     /* SSH bug compatibility modes */
     int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,
        sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,
     /* SSH bug compatibility modes */
     int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,
        sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,
-       sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2;
+       sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2,
+       sshbug_ignore2;
     /*
      * ssh_simple means that we promise never to open any channel other
      * than the main one, which means it can safely use a very large
     /*
      * ssh_simple means that we promise never to open any channel other
      * than the main one, which means it can safely use a very large
@@ -825,6 +826,7 @@ void term_free(Terminal *);
 void term_size(Terminal *, int, int, int);
 void term_paint(Terminal *, Context, int, int, int, int, int);
 void term_scroll(Terminal *, int, int);
 void term_size(Terminal *, int, int, int);
 void term_paint(Terminal *, Context, int, int, int, int, int);
 void term_scroll(Terminal *, int, int);
+void term_scroll_to_selection(Terminal *, int);
 void term_pwron(Terminal *, int);
 void term_clrsb(Terminal *);
 void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action,
 void term_pwron(Terminal *, int);
 void term_clrsb(Terminal *);
 void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action,
index 9985e2d..38c4188 100644 (file)
@@ -477,6 +477,7 @@ void save_open_settings(void *sesskey, Config *cfg)
     write_setting_i(sesskey, "BugIgnore1", 2-cfg->sshbug_ignore1);
     write_setting_i(sesskey, "BugPlainPW1", 2-cfg->sshbug_plainpw1);
     write_setting_i(sesskey, "BugRSA1", 2-cfg->sshbug_rsa1);
     write_setting_i(sesskey, "BugIgnore1", 2-cfg->sshbug_ignore1);
     write_setting_i(sesskey, "BugPlainPW1", 2-cfg->sshbug_plainpw1);
     write_setting_i(sesskey, "BugRSA1", 2-cfg->sshbug_rsa1);
+    write_setting_i(sesskey, "BugIgnore2", 2-cfg->sshbug_ignore2);
     write_setting_i(sesskey, "BugHMAC2", 2-cfg->sshbug_hmac2);
     write_setting_i(sesskey, "BugDeriveKey2", 2-cfg->sshbug_derivekey2);
     write_setting_i(sesskey, "BugRSAPad2", 2-cfg->sshbug_rsapad2);
     write_setting_i(sesskey, "BugHMAC2", 2-cfg->sshbug_hmac2);
     write_setting_i(sesskey, "BugDeriveKey2", 2-cfg->sshbug_derivekey2);
     write_setting_i(sesskey, "BugRSAPad2", 2-cfg->sshbug_rsapad2);
@@ -817,6 +818,7 @@ void load_open_settings(void *sesskey, Config *cfg)
     gppi(sesskey, "BugIgnore1", 0, &i); cfg->sshbug_ignore1 = 2-i;
     gppi(sesskey, "BugPlainPW1", 0, &i); cfg->sshbug_plainpw1 = 2-i;
     gppi(sesskey, "BugRSA1", 0, &i); cfg->sshbug_rsa1 = 2-i;
     gppi(sesskey, "BugIgnore1", 0, &i); cfg->sshbug_ignore1 = 2-i;
     gppi(sesskey, "BugPlainPW1", 0, &i); cfg->sshbug_plainpw1 = 2-i;
     gppi(sesskey, "BugRSA1", 0, &i); cfg->sshbug_rsa1 = 2-i;
+    gppi(sesskey, "BugIgnore2", 0, &i); cfg->sshbug_ignore2 = 2-i;
     {
        int i;
        gppi(sesskey, "BugHMAC2", 0, &i); cfg->sshbug_hmac2 = 2-i;
     {
        int i;
        gppi(sesskey, "BugHMAC2", 0, &i); cfg->sshbug_hmac2 = 2-i;
diff --git a/ssh.c b/ssh.c
index 8887d9b..029c78a 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -194,6 +194,7 @@ static const char *const ssh2_disconnect_reasons[] = {
 #define BUG_SSH2_REKEY                           64
 #define BUG_SSH2_PK_SESSIONID                   128
 #define BUG_SSH2_MAXPKT                                256
 #define BUG_SSH2_REKEY                           64
 #define BUG_SSH2_PK_SESSIONID                   128
 #define BUG_SSH2_MAXPKT                                256
+#define BUG_CHOKES_ON_SSH2_IGNORE               512
 
 /*
  * Codes for terminal modes.
 
 /*
  * Codes for terminal modes.
@@ -2011,7 +2012,8 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)
 {
     int len;
     if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
 {
     int len;
     if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
-       ssh->deferred_len == 0 && !noignore) {
+       ssh->deferred_len == 0 && !noignore &&
+       !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
        /*
         * Interpose an SSH_MSG_IGNORE to ensure that user data don't
         * get encrypted with a known IV.
        /*
         * Interpose an SSH_MSG_IGNORE to ensure that user data don't
         * get encrypted with a known IV.
@@ -2141,7 +2143,8 @@ static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt,
         * unavailable, we don't do this trick at all, because we
         * gain nothing by it.)
         */
         * unavailable, we don't do this trick at all, because we
         * gain nothing by it.)
         */
-       if (ssh->cscipher) {
+       if (ssh->cscipher &&
+           !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
            int stringlen, i;
 
            stringlen = (256 - ssh->deferred_len);
            int stringlen, i;
 
            stringlen = (256 - ssh->deferred_len);
@@ -2508,6 +2511,15 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        ssh->remote_bugs |= BUG_SSH2_MAXPKT;
        logevent("We believe remote version ignores SSH-2 maximum packet size");
     }
        ssh->remote_bugs |= BUG_SSH2_MAXPKT;
        logevent("We believe remote version ignores SSH-2 maximum packet size");
     }
+
+    if (ssh->cfg.sshbug_ignore2 == FORCE_ON) {
+       /*
+        * Servers that don't support SSH2_MSG_IGNORE. Currently,
+        * none detected automatically.
+        */
+       ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;
+       logevent("We believe remote version has SSH-2 ignore bug");
+    }
 }
 
 /*
 }
 
 /*
@@ -9426,8 +9438,10 @@ static const struct telnet_special *ssh_get_specials(void *handle)
     static const struct telnet_special ssh1_ignore_special[] = {
        {"IGNORE message", TS_NOP}
     };
     static const struct telnet_special ssh1_ignore_special[] = {
        {"IGNORE message", TS_NOP}
     };
-    static const struct telnet_special ssh2_transport_specials[] = {
+    static const struct telnet_special ssh2_ignore_special[] = {
        {"IGNORE message", TS_NOP},
        {"IGNORE message", TS_NOP},
+    };
+    static const struct telnet_special ssh2_rekey_special[] = {
        {"Repeat key exchange", TS_REKEY},
     };
     static const struct telnet_special ssh2_session_specials[] = {
        {"Repeat key exchange", TS_REKEY},
     };
     static const struct telnet_special ssh2_session_specials[] = {
@@ -9452,7 +9466,8 @@ static const struct telnet_special *ssh_get_specials(void *handle)
        {NULL, TS_EXITMENU}
     };
     /* XXX review this length for any changes: */
        {NULL, TS_EXITMENU}
     };
     /* XXX review this length for any changes: */
-    static struct telnet_special ssh_specials[lenof(ssh2_transport_specials) +
+    static struct telnet_special ssh_specials[lenof(ssh2_ignore_special) +
+                                             lenof(ssh2_rekey_special) +
                                              lenof(ssh2_session_specials) +
                                              lenof(specials_end)];
     Ssh ssh = (Ssh) handle;
                                              lenof(ssh2_session_specials) +
                                              lenof(specials_end)];
     Ssh ssh = (Ssh) handle;
@@ -9471,7 +9486,10 @@ static const struct telnet_special *ssh_get_specials(void *handle)
        if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
            ADD_SPECIALS(ssh1_ignore_special);
     } else if (ssh->version == 2) {
        if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
            ADD_SPECIALS(ssh1_ignore_special);
     } else if (ssh->version == 2) {
-       ADD_SPECIALS(ssh2_transport_specials);
+       if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE))
+           ADD_SPECIALS(ssh2_ignore_special);
+       if (!(ssh->remote_bugs & BUG_SSH2_REKEY))
+           ADD_SPECIALS(ssh2_rekey_special);
        if (ssh->mainchan)
            ADD_SPECIALS(ssh2_session_specials);
     } /* else we're not ready yet */
        if (ssh->mainchan)
            ADD_SPECIALS(ssh2_session_specials);
     } /* else we're not ready yet */
@@ -9521,9 +9539,11 @@ static void ssh_special(void *handle, Telnet_Special code)
            if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
                send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
        } else {
            if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
                send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
        } else {
-           pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
-           ssh2_pkt_addstring_start(pktout);
-           ssh2_pkt_send_noqueue(ssh, pktout);
+           if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
+               pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
+               ssh2_pkt_addstring_start(pktout);
+               ssh2_pkt_send_noqueue(ssh, pktout);
+           }
        }
     } else if (code == TS_REKEY) {
        if (!ssh->kex_in_progress && ssh->version == 2) {
        }
     } else if (code == TS_REKEY) {
        if (!ssh->kex_in_progress && ssh->version == 2) {
index aaf8705..c0c809c 100644 (file)
@@ -5137,6 +5137,31 @@ void term_scroll(Terminal *term, int rel, int where)
 }
 
 /*
 }
 
 /*
+ * Scroll the scrollback to centre it on the beginning or end of the
+ * current selection, if any.
+ */
+void term_scroll_to_selection(Terminal *term, int which_end)
+{
+    pos target;
+    int y;
+    int sbtop = -sblines(term);
+
+    if (term->selstate != SELECTED)
+       return;
+    if (which_end)
+       target = term->selend;
+    else
+       target = term->selstart;
+
+    y = target.y - term->rows/2;
+    if (y < sbtop)
+       y = sbtop;
+    else if (y > 0)
+       y = 0;
+    term_scroll(term, -1, y);
+}
+
+/*
  * Helper routine for clipme(): growing buffer.
  */
 typedef struct {
  * Helper routine for clipme(): growing buffer.
  */
 typedef struct {
index 3f092ae..96f6499 100644 (file)
@@ -3811,6 +3811,10 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
            return 0;
        }
            SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
            return 0;
        }
+       if ((wParam == VK_PRIOR || wParam == VK_NEXT) && shift_state == 3) {
+           term_scroll_to_selection(term, (wParam == VK_PRIOR ? 0 : 1));
+           return 0;
+       }
        if (wParam == VK_INSERT && shift_state == 1) {
            request_paste(NULL);
            return 0;
        if (wParam == VK_INSERT && shift_state == 1) {
            request_paste(NULL);
            return 0;