Add the CRC32 compensation attack detector that all other SSH
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index dcb791d..b8abc5b 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -499,6 +499,7 @@ static int ssh_echoing, ssh_editing;
 
 static tree234 *ssh_channels;         /* indexed by local id */
 static struct ssh_channel *mainchan;   /* primary session channel */
+static int ssh_exitcode = -1;
 
 static tree234 *ssh_rportfwds;
 
@@ -723,6 +724,11 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen)
        st->to_read -= st->chunk;
     }
 
+    if (cipher && detect_attack(pktin.data, st->biglen, NULL)) {
+        bombout(("Network attack (CRC compensation) detected!"));
+        crReturn(0);
+    }
+
     if (cipher)
        cipher->decrypt(pktin.data, st->biglen);
 
@@ -1853,11 +1859,13 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
     struct RSAKey servkey, hostkey;
     struct MD5Context md5c;
     static unsigned long supported_ciphers_mask, supported_auths_mask;
-    static int tried_publickey;
+    static int tried_publickey, tried_agent;
     static int tis_auth_refused, ccard_auth_refused;
     static unsigned char session_id[16];
     static int cipher_type;
     static char username[100];
+    static void *publickey_blob;
+    int publickey_bloblen;
 
     crBegin;
 
@@ -2102,8 +2110,14 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
 
     crWaitUntil(ispkt);
 
-    tried_publickey = 0;
+    tried_publickey = tried_agent = 0;
     tis_auth_refused = ccard_auth_refused = 0;
+    /* Load the public half of cfg.keyfile so we notice if it's in Pageant */
+    if (*cfg.keyfile) {
+       if (!rsakey_pubblob(cfg.keyfile, &publickey_blob, &publickey_bloblen))
+           publickey_blob = NULL;
+    } else
+       publickey_blob = NULL;
 
     while (pktin.type == SSH1_SMSG_FAILURE) {
        static char password[100];
@@ -2113,7 +2127,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        static int pwpkt_type;
        pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;
 
-       if (agent_exists()) {
+       if (agent_exists() && !tried_agent) {
            /*
             * Attempt RSA authentication using Pageant.
             */
@@ -2123,6 +2137,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
            static int authed = FALSE;
            void *r;
 
+           tried_agent = 1;
            logevent("Pageant is running. Requesting keys.");
 
            /* Request the keys held by the agent. */
@@ -2151,6 +2166,11 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                        sprintf(buf, "Trying Pageant key #%d", i);
                        logevent(buf);
                    }
+                   if (publickey_blob &&
+                       !memcmp(p, publickey_blob, publickey_bloblen)) {
+                       logevent("This key matches configured key file");
+                       tried_publickey = 1;
+                   }
                    p += 4;
                    p += ssh1_read_bignum(p, &key.exponent);
                    p += ssh1_read_bignum(p, &key.modulus);
@@ -3110,6 +3130,11 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                /* may be from EXEC_SHELL on some servers
                 * if no pty is available or in other odd cases. Ignore */
            } else if (pktin.type == SSH1_SMSG_EXIT_STATUS) {
+               char buf[100];
+               ssh_exitcode = GET_32BIT(pktin.body);
+               sprintf(buf, "Server sent command exit status %d",
+                       ssh_exitcode);
+               logevent(buf);
                send_packet(SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
                 /*
                  * In case `helpful' firewalls or proxies tack
@@ -3745,6 +3770,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     static char username[100];
     static char pwprompt[200];
     static char password[100];
+    static void *publickey_blob;
+    static int publickey_bloblen;
 
     crBegin;
 
@@ -3885,6 +3912,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
        tried_agent = FALSE;
        tried_keyb_inter = FALSE;
        kbd_inter_running = FALSE;
+       /* Load the pub half of cfg.keyfile so we notice if it's in Pageant */
+       if (*cfg.keyfile) {
+           publickey_blob = ssh2_userkey_loadpub(cfg.keyfile, NULL,
+                                                 &publickey_bloblen);
+       } else
+           publickey_blob = NULL;
 
        while (1) {
            /*
@@ -4040,6 +4073,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                        }
                        pklen = GET_32BIT(p);
                        p += 4;
+                       if (publickey_blob &&
+                           pklen == publickey_bloblen &&
+                           !memcmp(p, publickey_blob, publickey_bloblen)) {
+                           logevent("This key matches configured key file");
+                           tried_pubkey_config = 1;
+                       }
                        pkblob = p;
                        p += pklen;
                        alglen = GET_32BIT(pkblob);
@@ -5061,14 +5100,35 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                }
 
                /*
-                * We don't recognise any form of channel request,
-                * so we now either ignore the request or respond
-                * with CHANNEL_FAILURE, depending on want_reply.
+                * Having got the channel number, we now look at
+                * the request type string to see if it's something
+                * we recognise.
                 */
-               if (want_reply) {
-                   ssh2_pkt_init(SSH2_MSG_CHANNEL_FAILURE);
-                   ssh2_pkt_adduint32(c->remoteid);
-                   ssh2_pkt_send();
+               if (typelen == 11 && !memcmp(type, "exit-status", 11) &&
+                   c == mainchan) {
+                   /* We recognise "exit-status" on the primary channel. */
+                   char buf[100];
+                   ssh_exitcode = ssh2_pkt_getuint32();
+                   sprintf(buf, "Server sent command exit status %d",
+                           ssh_exitcode);
+                   logevent(buf);
+                   if (want_reply) {
+                       ssh2_pkt_init(SSH2_MSG_CHANNEL_SUCCESS);
+                       ssh2_pkt_adduint32(c->remoteid);
+                       ssh2_pkt_send();
+                   }
+               } else {
+                   /*
+                    * This is a channel request we don't know
+                    * about, so we now either ignore the request
+                    * or respond with CHANNEL_FAILURE, depending
+                    * on want_reply.
+                    */
+                   if (want_reply) {
+                       ssh2_pkt_init(SSH2_MSG_CHANNEL_FAILURE);
+                       ssh2_pkt_adduint32(c->remoteid);
+                       ssh2_pkt_send();
+                   }
                }
            } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) {
                char *type;
@@ -5443,6 +5503,11 @@ static int ssh_ldisc(int option)
     return FALSE;
 }
 
+static int ssh_return_exitcode(void)
+{
+    return ssh_exitcode;
+}
+
 Backend ssh_backend = {
     ssh_init,
     ssh_send,
@@ -5450,6 +5515,7 @@ Backend ssh_backend = {
     ssh_size,
     ssh_special,
     ssh_socket,
+    ssh_return_exitcode,
     ssh_sendok,
     ssh_ldisc,
     ssh_unthrottle,