Get rid of all the MSVC warnings.
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index fc68bf9..953c69f 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -1052,7 +1052,7 @@ static void c_write_stderr(int trusted, const char *buf, int len)
 {
     int i;
     for (i = 0; i < len; i++)
-       if (buf[i] != '\r' && (trusted || buf[i] & 0x60))
+       if (buf[i] != '\r' && (trusted || buf[i] == '\n' || (buf[i] & 0x60)))
            fputc(buf[i], stderr);
 }
 
@@ -1489,6 +1489,7 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap)
 
     while ((argtype = va_arg(ap, int)) != PKT_END) {
        unsigned char *argp, argchar;
+       char *sargp;
        unsigned long argint;
        int arglen;
        switch (argtype) {
@@ -1507,8 +1508,8 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap)
            ssh_pkt_adddata(pkt, argp, arglen);
            break;
          case PKT_STR:
-           argp = va_arg(ap, unsigned char *);
-           ssh_pkt_addstring(pkt, argp);
+           sargp = va_arg(ap, char *);
+           ssh_pkt_addstring(pkt, sargp);
            break;
          case PKT_BIGNUM:
            bn = va_arg(ap, Bignum);
@@ -1654,7 +1655,7 @@ static void ssh_pkt_addstring(struct Packet *pkt, char *data)
 static void ssh1_pkt_addmp(struct Packet *pkt, Bignum b)
 {
     int len = ssh1_bignum_length(b);
-    unsigned char *data = snewn(len, char);
+    unsigned char *data = snewn(len, unsigned char);
     (void) ssh1_write_bignum(data, b);
     ssh_pkt_adddata(pkt, data, len);
     sfree(data);
@@ -1904,7 +1905,6 @@ static void ssh2_pkt_send(Ssh ssh, struct Packet *pkt)
        ssh2_pkt_send_noqueue(ssh, pkt);
 }
 
-#if 0 /* disused */
 /*
  * Either queue or defer a packet, depending on whether queueing is
  * set.
@@ -1916,7 +1916,6 @@ static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt)
     else
        ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);
 }
-#endif
 
 /*
  * Send the whole deferred data block constructed by
@@ -1950,6 +1949,74 @@ static void ssh_pkt_defersend(Ssh ssh)
 }
 
 /*
+ * Send a packet whose length needs to be disguised (typically
+ * passwords or keyboard-interactive responses).
+ */
+static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt,
+                                      int padsize)
+{
+#if 0
+    if (0) {
+       /*
+        * The simplest way to do this is to adjust the
+        * variable-length padding field in the outgoing packet.
+        * 
+        * Currently compiled out, because some Cisco SSH servers
+        * don't like excessively padded packets (bah, why's it
+        * always Cisco?)
+        */
+       pkt->forcepad = padsize;
+       ssh2_pkt_send(ssh, pkt);
+    } else
+#endif
+    {
+       /*
+        * If we can't do that, however, an alternative approach is
+        * to use the pkt_defer mechanism to bundle the packet
+        * tightly together with an SSH_MSG_IGNORE such that their
+        * combined length is a constant. So first we construct the
+        * final form of this packet and defer its sending.
+        */
+       ssh2_pkt_defer(ssh, pkt);
+
+       /*
+        * Now construct an SSH_MSG_IGNORE which includes a string
+        * that's an exact multiple of the cipher block size. (If
+        * the cipher is NULL so that the block size is
+        * unavailable, we don't do this trick at all, because we
+        * gain nothing by it.)
+        */
+       if (ssh->cscipher) {
+           int stringlen, i;
+
+           stringlen = (256 - ssh->deferred_len);
+           stringlen += ssh->cscipher->blksize - 1;
+           stringlen -= (stringlen % ssh->cscipher->blksize);
+           if (ssh->cscomp) {
+               /*
+                * Temporarily disable actual compression, so we
+                * can guarantee to get this string exactly the
+                * length we want it. The compression-disabling
+                * routine should return an integer indicating how
+                * many bytes we should adjust our string length
+                * by.
+                */
+               stringlen -=
+                   ssh->cscomp->disable_compression(ssh->cs_comp_ctx);
+           }
+           pkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+           ssh2_pkt_addstring_start(pkt);
+           for (i = 0; i < stringlen; i++) {
+               char c = (char) random_byte();
+               ssh2_pkt_addstring_data(pkt, &c, 1);
+           }
+           ssh2_pkt_defer(ssh, pkt);
+       }
+       ssh_pkt_defersend(ssh);
+    }
+}
+
+/*
  * Send all queued SSH-2 packets. We send them by means of
  * ssh2_pkt_defer_noqueue(), in case they included a pair of
  * packets that needed to be lumped together.
@@ -2166,6 +2233,13 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
 
     ssh->remote_bugs = 0;
 
+    /*
+     * General notes on server version strings:
+     *  - Not all servers reporting "Cisco-1.25" have all the bugs listed
+     *    here -- in particular, we've heard of one that's perfectly happy
+     *    with SSH1_MSG_IGNOREs -- but this string never seems to change,
+     *    so we can't distinguish them.
+     */
     if (ssh->cfg.sshbug_ignore1 == FORCE_ON ||
        (ssh->cfg.sshbug_ignore1 == AUTO &&
         (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
@@ -3703,19 +3777,19 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
             *    magnitude of the password length, but it will
             *    introduce a bit of extra uncertainty.
             * 
-            * A few servers (the old 1.2.18 through 1.2.22)
-            * can't deal with SSH1_MSG_IGNORE. For these
-            * servers, we need an alternative defence. We make
-            * use of the fact that the password is interpreted
-            * as a C string: so we can append a NUL, then some
-            * random data.
+            * A few servers can't deal with SSH1_MSG_IGNORE, at
+            * least in this context. For these servers, we need
+            * an alternative defence. We make use of the fact
+            * that the password is interpreted as a C string:
+            * so we can append a NUL, then some random data.
             * 
-            * One server (a Cisco one) can deal with neither
-            * SSH1_MSG_IGNORE _nor_ a padded password string.
-            * For this server we are left with no defences
+            * A few servers can deal with neither SSH1_MSG_IGNORE
+            * here _nor_ a padded password string.
+            * For these servers we are left with no defences
             * against password length sniffing.
             */
-           if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
+           if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) &&
+               !(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
                /*
                 * The server can deal with SSH1_MSG_IGNORE, so
                 * we can use the primary defence.
@@ -3784,10 +3858,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                            PKTT_OTHER, PKT_END);
            } else {
                /*
-                * The server has _both_
-                * BUG_CHOKES_ON_SSH1_IGNORE and
-                * BUG_NEEDS_SSH1_PLAIN_PASSWORD. There is
-                * therefore nothing we can do.
+                * The server is believed unable to cope with
+                * any of our password camouflage methods.
                 */
                int len;
                len = strlen(s->cur_prompt->prompts[0]->result);
@@ -4569,7 +4641,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
            /* Data for an agent message. Buffer it. */
            while (len > 0) {
                if (c->u.a.lensofar < 4) {
-                   unsigned int l = min(4 - c->u.a.lensofar, len);
+                   unsigned int l = min(4 - c->u.a.lensofar, (unsigned)len);
                    memcpy(c->u.a.msglen + c->u.a.lensofar, p,
                           l);
                    p += l;
@@ -4586,7 +4658,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
                if (c->u.a.lensofar >= 4 && len > 0) {
                    unsigned int l =
                        min(c->u.a.totallen - c->u.a.lensofar,
-                           len);
+                           (unsigned)len);
                    memcpy(c->u.a.message + c->u.a.lensofar, p,
                           l);
                    p += l;
@@ -6006,7 +6078,8 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
          case CHAN_AGENT:
            while (length > 0) {
                if (c->u.a.lensofar < 4) {
-                   unsigned int l = min(4 - c->u.a.lensofar, length);
+                   unsigned int l = min(4 - c->u.a.lensofar,
+                                        (unsigned)length);
                    memcpy(c->u.a.msglen + c->u.a.lensofar,
                           data, l);
                    data += l;
@@ -6023,7 +6096,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
                if (c->u.a.lensofar >= 4 && length > 0) {
                    unsigned int l =
                        min(c->u.a.totallen - c->u.a.lensofar,
-                           length);
+                           (unsigned)length);
                    memcpy(c->u.a.message + c->u.a.lensofar,
                           data, l);
                    data += l;
@@ -7368,7 +7441,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                     * Send the responses to the server.
                     */
                    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
-                   s->pktout->forcepad = 256;
                    ssh2_pkt_adduint32(s->pktout, s->num_prompts);
                    for (i=0; i < s->num_prompts; i++) {
                        dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
@@ -7376,7 +7448,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                                           s->cur_prompt->prompts[i]->result);
                        end_log_omission(ssh, s->pktout);
                    }
-                   ssh2_pkt_send(ssh, s->pktout);
+                   ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
 
                    /*
                     * Get the next packet in case it's another
@@ -7446,7 +7518,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                 * people who find out how long their password is!
                 */
                s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               s->pktout->forcepad = 256;
                ssh2_pkt_addstring(s->pktout, s->username);
                ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                        /* service requested */
@@ -7455,7 +7526,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
                ssh2_pkt_addstring(s->pktout, s->password);
                end_log_omission(ssh, s->pktout);
-               ssh2_pkt_send(ssh, s->pktout);
+               ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
                logevent("Sent password");
                s->type = AUTH_TYPE_PASSWORD;
 
@@ -7497,6 +7568,21 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    s->cur_prompt->instruction =
                        dupprintf("%.*s", prompt_len, prompt);
                    s->cur_prompt->instr_reqd = TRUE;
+                   /*
+                    * There's no explicit requirement in the protocol
+                    * for the "old" passwords in the original and
+                    * password-change messages to be the same, and
+                    * apparently some Cisco kit supports password change
+                    * by the user entering a blank password originally
+                    * and the real password subsequently, so,
+                    * reluctantly, we prompt for the old password again.
+                    *
+                    * (On the other hand, some servers don't even bother
+                    * to check this field.)
+                    */
+                   add_prompt(s->cur_prompt,
+                              dupstr("Current password (blank for previously entered password): "),
+                              FALSE, SSH_MAX_PASSWORD_LEN);
                    add_prompt(s->cur_prompt, dupstr("Enter new password: "),
                               FALSE, SSH_MAX_PASSWORD_LEN);
                    add_prompt(s->cur_prompt, dupstr("Confirm new password: "),
@@ -7530,10 +7616,25 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                        }
 
                        /*
-                        * Check the two passwords match.
+                        * If the user specified a new original password
+                        * (IYSWIM), overwrite any previously specified
+                        * one.
+                        * (A side effect is that the user doesn't have to
+                        * re-enter it if they louse up the new password.)
+                        */
+                       if (s->cur_prompt->prompts[0]->result[0]) {
+                           memset(s->password, 0, strlen(s->password));
+                               /* burn the evidence */
+                           sfree(s->password);
+                           s->password =
+                               dupstr(s->cur_prompt->prompts[0]->result);
+                       }
+
+                       /*
+                        * Check the two new passwords match.
                         */
-                       got_new = (strcmp(s->cur_prompt->prompts[0]->result,
-                                         s->cur_prompt->prompts[1]->result)
+                       got_new = (strcmp(s->cur_prompt->prompts[1]->result,
+                                         s->cur_prompt->prompts[2]->result)
                                   == 0);
                        if (!got_new)
                            /* They don't. Silly user. */
@@ -7546,7 +7647,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                     * (see above for padding rationale)
                     */
                    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-                   s->pktout->forcepad = 256;
                    ssh2_pkt_addstring(s->pktout, s->username);
                    ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                        /* service requested */
@@ -7555,10 +7655,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
                    ssh2_pkt_addstring(s->pktout, s->password);
                    ssh2_pkt_addstring(s->pktout,
-                                      s->cur_prompt->prompts[0]->result);
+                                      s->cur_prompt->prompts[1]->result);
                    free_prompts(s->cur_prompt);
                    end_log_omission(ssh, s->pktout);
-                   ssh2_pkt_send(ssh, s->pktout);
+                   ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
                    logevent("Sent new password");
                    
                    /*