Add single-DES support in SSH2
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index 119cd4f..3a02b12 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -236,15 +236,13 @@ extern void pfd_override_throttle(Socket s, int enable);
 #define OUR_V2_WINSIZE 16384
 
 /*
- * Ciphers for SSH2. We miss out single-DES because it isn't
- * supported; also 3DES and Blowfish are both done differently from
- * SSH1. (3DES uses outer chaining; Blowfish has the opposite
- * endianness and different-sized keys.)
+ * Ciphers for SSH2.
  */
 const static struct ssh2_ciphers *ciphers[] = {
     &ssh2_aes,
     &ssh2_blowfish,
     &ssh2_3des,
+    &ssh2_des,
 };
 
 const static struct ssh_kex *kex_algs[] = {
@@ -2912,6 +2910,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                if (c && c->type == CHAN_SOCKDATA_DORMANT) {
                    c->remoteid = localid;
                    c->type = CHAN_SOCKDATA;
+                   c->v.v1.throttling = 0;
                    pfd_confirm(c->u.pfd.s);
                }
 
@@ -3171,7 +3170,8 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
            n_preferred_ciphers++;
            break;
          case CIPHER_DES:
-           /* Not supported in SSH2; silently drop */
+           preferred_ciphers[n_preferred_ciphers] = &ssh2_des;
+           n_preferred_ciphers++;
            break;
          case CIPHER_3DES:
            preferred_ciphers[n_preferred_ciphers] = &ssh2_3des;
@@ -3352,6 +3352,11 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
            break;
        }
     }
+    if (!cscipher_tobe) {
+       bombout(("Couldn't agree a client-to-server cipher (available: %s)", str));
+       crReturn(0);
+    }
+
     ssh2_pkt_getstring(&str, &len);    /* server->client cipher */
     warn = 0;
     for (i = 0; i < n_preferred_ciphers; i++) {
@@ -3372,6 +3377,11 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
            break;
        }
     }
+    if (!sccipher_tobe) {
+       bombout(("Couldn't agree a server-to-client cipher (available: %s)", str));
+       crReturn(0);
+    }
+
     ssh2_pkt_getstring(&str, &len);    /* client->server mac */
     for (i = 0; i < nmacs; i++) {
        if (in_commasep_string(maclist[i]->name, str, len)) {
@@ -3499,15 +3509,6 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     }
 
     /*
-     * Expect SSH2_MSG_NEWKEYS from server.
-     */
-    crWaitUntil(ispkt);
-    if (pktin.type != SSH2_MSG_NEWKEYS) {
-       bombout(("expected new-keys packet from server"));
-       crReturn(0);
-    }
-
-    /*
      * Authenticate remote host: verify host key. (We've already
      * checked the signature of the exchange hash.)
      */
@@ -3530,6 +3531,15 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     ssh2_pkt_send();
 
     /*
+     * Expect SSH2_MSG_NEWKEYS from server.
+     */
+    crWaitUntil(ispkt);
+    if (pktin.type != SSH2_MSG_NEWKEYS) {
+       bombout(("expected new-keys packet from server"));
+       crReturn(0);
+    }
+
+    /*
      * Create and initialise session keys.
      */
     cscipher = cscipher_tobe;
@@ -4796,8 +4806,6 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                ssh_state = SSH_STATE_CLOSED;
                logevent("Received disconnect message");
                crReturnV;
-           } else if (pktin.type == SSH2_MSG_CHANNEL_REQUEST) {
-               continue;              /* exit status et al; ignore (FIXME?) */
            } else if (pktin.type == SSH2_MSG_CHANNEL_EOF) {
                unsigned i = ssh2_pkt_getuint32();
                struct ssh_channel *c;
@@ -4916,6 +4924,46 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 
                del234(ssh_channels, c);
                sfree(c);
+           } else if (pktin.type == SSH2_MSG_CHANNEL_REQUEST) {
+               unsigned localid;
+               char *type;
+               int typelen, want_reply;
+               struct ssh_channel *c;
+
+               localid = ssh2_pkt_getuint32();
+               ssh2_pkt_getstring(&type, &typelen);
+               want_reply = ssh2_pkt_getbool();
+
+               /*
+                * First, check that the channel exists. Otherwise,
+                * we can instantly disconnect with a rude message.
+                */
+               c = find234(ssh_channels, &localid, ssh_channelfind);
+               if (!c) {
+                   char buf[80];
+                   sprintf(buf, "Received channel request for nonexistent"
+                           " channel %d", localid);
+                   logevent(buf);
+                   ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+                   ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
+                   ssh2_pkt_addstring(buf);
+                   ssh2_pkt_addstring("en");   /* language tag */
+                   ssh2_pkt_send();
+                   connection_fatal(buf);
+                   ssh_state = SSH_STATE_CLOSED;
+                   crReturnV;
+               }
+
+               /*
+                * 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.
+                */
+               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;
                int typelen;