Improved error messages if you use the wrong key type: you should
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index e0fed88..d9981ed 100644 (file)
--- a/ssh.c
+++ b/ssh.c
                       if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \
                       { fprintf(stderr, "%s\n", s); fflush(stderr); } }
 
+/* logevent, only printf-formatted. */
+void logeventf(char *fmt, ...)
+{
+    va_list ap;
+    char stuff[200];
+
+    va_start(ap, fmt);
+    vsprintf(stuff, fmt, ap);
+    va_end(ap);
+    logevent(stuff);
+}
+
 #define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \
                           (s ? sk_close(s), s = NULL : 0), \
-                          connection_fatal msg )
+                          logeventf msg, connection_fatal msg )
 
 #define SSH1_MSG_DISCONNECT                       1    /* 0x1 */
 #define SSH1_SMSG_PUBLIC_KEY                      2    /* 0x2 */
@@ -168,6 +180,7 @@ static const char *const ssh2_disconnect_reasons[] = {
 #define BUG_CHOKES_ON_SSH1_IGNORE                 1
 #define BUG_SSH2_HMAC                             2
 #define BUG_NEEDS_SSH1_PLAIN_PASSWORD            4
+#define BUG_CHOKES_ON_RSA                        8
 
 static int ssh_pkt_ctx = 0;
 
@@ -309,6 +322,18 @@ extern void pfd_confirm(Socket s);
 extern void pfd_unthrottle(Socket s);
 extern void pfd_override_throttle(Socket s, int enable);
 
+static void ssh2_pkt_init(int pkt_type);
+static void ssh2_pkt_addbool(unsigned char value);
+static void ssh2_pkt_adduint32(unsigned long value);
+static void ssh2_pkt_addstring_start(void);
+static void ssh2_pkt_addstring_str(char *data);
+static void ssh2_pkt_addstring_data(char *data, int len);
+static void ssh2_pkt_addstring(char *data);
+static char *ssh2_mpint_fmt(Bignum b, int *len);
+static void ssh2_pkt_addmp(Bignum b);
+static int ssh2_pkt_construct(void);
+static void ssh2_pkt_send(void);
+
 /*
  * Buffer management constants. There are several of these for
  * various different purposes:
@@ -724,6 +749,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);
 
@@ -765,8 +795,8 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen)
        pktin.type == SSH1_MSG_DEBUG ||
        pktin.type == SSH1_SMSG_AUTH_TIS_CHALLENGE ||
        pktin.type == SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
-       long strlen = GET_32BIT(pktin.body);
-       if (strlen + 4 != pktin.length) {
+       long stringlen = GET_32BIT(pktin.body);
+       if (stringlen + 4 != pktin.length) {
            bombout(("Received data packet with bogus string length"));
            crReturn(0);
        }
@@ -775,12 +805,12 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen)
     if (pktin.type == SSH1_MSG_DEBUG) {
        /* log debug message */
        char buf[80];
-       int strlen = GET_32BIT(pktin.body);
+       int stringlen = GET_32BIT(pktin.body);
        strcpy(buf, "Remote: ");
-       if (strlen > 70)
-           strlen = 70;
-       memcpy(buf + 8, pktin.body + 4, strlen);
-       buf[8 + strlen] = '\0';
+       if (stringlen > 70)
+           stringlen = 70;
+       memcpy(buf + 8, pktin.body + 4, stringlen);
+       buf[8 + stringlen] = '\0';
        logevent(buf);
        goto next_packet;
     } else if (pktin.type == SSH1_MSG_IGNORE) {
@@ -799,7 +829,7 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen)
            msglen = sizeof(buf) - nowlen - 1;
        memcpy(buf + nowlen, pktin.body + 4, msglen);
        buf[nowlen + msglen] = '\0';
-       logevent(buf);
+       /* logevent(buf); (this is now done within the bombout macro) */
        bombout(("Server sent disconnect message:\n\"%s\"", buf+nowlen));
        crReturn(0);
     }
@@ -940,36 +970,105 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
     log_packet(PKT_INCOMING, pktin.type, ssh2_pkt_type(pktin.type),
               pktin.data+6, pktin.length-6);
 
-    if (pktin.type == SSH2_MSG_IGNORE || pktin.type == SSH2_MSG_DEBUG)
-       goto next_packet;              /* FIXME: print DEBUG message */
-
-    if (pktin.type == SSH2_MSG_DISCONNECT) {
-       /* log reason code in disconnect message */
-       char buf[256];
-       int reason = GET_32BIT(pktin.data + 6);
-       unsigned msglen = GET_32BIT(pktin.data + 10);
-       unsigned nowlen;
-       if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {
-           sprintf(buf, "Received disconnect message (%s)",
-                   ssh2_disconnect_reasons[reason]);
-       } else {
-           sprintf(buf, "Received disconnect message (unknown type %d)",
-                   reason);
+    switch (pktin.type) {
+        /*
+         * These packets we must handle instantly.
+         */
+      case SSH2_MSG_DISCONNECT:
+        {
+            /* log reason code in disconnect message */
+            char buf[256];
+            int reason = GET_32BIT(pktin.data + 6);
+            unsigned msglen = GET_32BIT(pktin.data + 10);
+            unsigned nowlen;
+            if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {
+                sprintf(buf, "Received disconnect message (%s)",
+                        ssh2_disconnect_reasons[reason]);
+            } else {
+                sprintf(buf, "Received disconnect message (unknown type %d)",
+                        reason);
+            }
+            logevent(buf);
+            strcpy(buf, "Disconnection message text: ");
+            nowlen = strlen(buf);
+            if (msglen > sizeof(buf) - nowlen - 1)
+                msglen = sizeof(buf) - nowlen - 1;
+            memcpy(buf + nowlen, pktin.data + 14, msglen);
+            buf[nowlen + msglen] = '\0';
+            logevent(buf);
+            bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"",
+                     reason,
+                     (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
+                     ssh2_disconnect_reasons[reason] : "unknown",
+                     buf+nowlen));
+            crReturn(0);
+        }
+        break;
+      case SSH2_MSG_IGNORE:
+       goto next_packet;
+      case SSH2_MSG_DEBUG:
+       {
+           /* log the debug message */
+           char buf[512];
+           /* int display = pktin.body[6]; */
+           int stringlen = GET_32BIT(pktin.data+7);
+           int prefix;
+           strcpy(buf, "Remote debug message: ");
+           prefix = strlen(buf);
+           if (stringlen > sizeof(buf)-prefix-1)
+               stringlen = sizeof(buf)-prefix-1;
+           memcpy(buf + prefix, pktin.data + 11, stringlen);
+           buf[prefix + stringlen] = '\0';
+           logevent(buf);
        }
-       logevent(buf);
-       strcpy(buf, "Disconnection message text: ");
-       nowlen = strlen(buf);
-       if (msglen > sizeof(buf) - nowlen - 1)
-           msglen = sizeof(buf) - nowlen - 1;
-       memcpy(buf + nowlen, pktin.data + 14, msglen);
-       buf[nowlen + msglen] = '\0';
-       logevent(buf);
-       bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"",
-                reason,
-                (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
-                ssh2_disconnect_reasons[reason] : "unknown",
-                buf+nowlen));
-       crReturn(0);
+        goto next_packet;              /* FIXME: print the debug message */
+
+        /*
+         * These packets we need do nothing about here.
+         */
+      case SSH2_MSG_UNIMPLEMENTED:
+      case SSH2_MSG_SERVICE_REQUEST:
+      case SSH2_MSG_SERVICE_ACCEPT:
+      case SSH2_MSG_KEXINIT:
+      case SSH2_MSG_NEWKEYS:
+      case SSH2_MSG_KEXDH_INIT:
+      case SSH2_MSG_KEXDH_REPLY:
+      /* case SSH2_MSG_KEX_DH_GEX_REQUEST: duplicate case value */
+      /* case SSH2_MSG_KEX_DH_GEX_GROUP: duplicate case value */
+      case SSH2_MSG_KEX_DH_GEX_INIT:
+      case SSH2_MSG_KEX_DH_GEX_REPLY:
+      case SSH2_MSG_USERAUTH_REQUEST:
+      case SSH2_MSG_USERAUTH_FAILURE:
+      case SSH2_MSG_USERAUTH_SUCCESS:
+      case SSH2_MSG_USERAUTH_BANNER:
+      case SSH2_MSG_USERAUTH_PK_OK:
+      /* case SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: duplicate case value */
+      /* case SSH2_MSG_USERAUTH_INFO_REQUEST: duplicate case value */
+      case SSH2_MSG_USERAUTH_INFO_RESPONSE:
+      case SSH2_MSG_GLOBAL_REQUEST:
+      case SSH2_MSG_REQUEST_SUCCESS:
+      case SSH2_MSG_REQUEST_FAILURE:
+      case SSH2_MSG_CHANNEL_OPEN:
+      case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
+      case SSH2_MSG_CHANNEL_OPEN_FAILURE:
+      case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
+      case SSH2_MSG_CHANNEL_DATA:
+      case SSH2_MSG_CHANNEL_EXTENDED_DATA:
+      case SSH2_MSG_CHANNEL_EOF:
+      case SSH2_MSG_CHANNEL_CLOSE:
+      case SSH2_MSG_CHANNEL_REQUEST:
+      case SSH2_MSG_CHANNEL_SUCCESS:
+      case SSH2_MSG_CHANNEL_FAILURE:
+        break;
+
+        /*
+         * For anything else we send SSH2_MSG_UNIMPLEMENTED.
+         */
+      default:
+       ssh2_pkt_init(SSH2_MSG_UNIMPLEMENTED);
+       ssh2_pkt_adduint32(st->incoming_sequence - 1);
+       ssh2_pkt_send();
+        break;
     }
 
     crFinish(0);
@@ -1504,6 +1603,16 @@ static void ssh_detect_bugs(char *vstring)
        logevent("We believe remote version needs a plain SSH1 password");
     }
 
+    if (!strcmp(imp, "Cisco-1.25")) {
+       /*
+        * These versions apparently have no clue whatever about
+        * RSA authentication and will panic and die if they see
+        * an AUTH_RSA message.
+        */
+       ssh_remote_bugs |= BUG_CHOKES_ON_RSA;
+       logevent("We believe remote version can't handle RSA authentication");
+    }
+
     if (!strncmp(imp, "2.1.0", 5) || !strncmp(imp, "2.0.", 4) ||
        !strncmp(imp, "2.2.0", 5) || !strncmp(imp, "2.3.0", 5) ||
        !strncmp(imp, "2.1 ", 4)) {
@@ -1682,6 +1791,7 @@ static int ssh_closing(Plug plug, char *error_msg, int error_code,
     }
     if (error_msg) {
        /* A socket error has occurred. */
+       logevent(error_msg);
        connection_fatal(error_msg);
     } else {
        /* Otherwise, the remote side closed the connection normally. */
@@ -1775,9 +1885,11 @@ static char *connect_to_host(char *host, int port, char **realhost, int nodelay)
        sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
        logevent(buf);
     }
-    s = sk_new(addr, port, 0, 1, nodelay, &fn_table_ptr);
-    if ((err = sk_socket_error(s)))
+    s = new_connection(addr, *realhost, port, 0, 1, nodelay, &fn_table_ptr);
+    if ((err = sk_socket_error(s))) {
+       s = NULL;
        return err;
+    }
 
 #ifdef FWHACK
     sk_write(s, "connect ", 8);
@@ -1854,11 +1966,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;
 
@@ -2067,8 +2181,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                            break;
                          case 3:
                          case 4:
-                           random_save_seed();
-                           exit(0);
+                           cleanup_exit(0);
                            break;
                          default:
                            if (((c >= ' ' && c <= '~') ||
@@ -2103,8 +2216,19 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
 
     crWaitUntil(ispkt);
 
-    tried_publickey = 0;
+    if ((ssh_remote_bugs & BUG_CHOKES_ON_RSA)) {
+       /* We must not attempt PK auth. Pretend we've already tried it. */
+       tried_publickey = tried_agent = 1;
+    } else {
+       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];
@@ -2114,7 +2238,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.
             */
@@ -2124,6 +2248,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. */
@@ -2152,6 +2277,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);
@@ -2300,8 +2430,22 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        }
        if (pwpkt_type == SSH1_CMSG_AUTH_RSA) {
            char *comment = NULL;
+           int type;
+           char msgbuf[256];
            if (flags & FLAG_VERBOSE)
                c_write_str("Trying public key authentication.\r\n");
+           sprintf(msgbuf, "Trying public key \"%.200s\"", cfg.keyfile);
+           logevent(msgbuf);
+           type = key_type(cfg.keyfile);
+           if (type != SSH_KEYTYPE_SSH1) {
+               sprintf(msgbuf, "Key is of wrong type (%s)",
+                       key_type_to_str(type));
+               logevent(msgbuf);
+               c_write_str(msgbuf);
+               c_write_str("\r\n");
+               tried_publickey = 1;
+               continue;
+           }
            if (!rsakey_encrypted(cfg.keyfile, &comment)) {
                if (flags & FLAG_VERBOSE)
                    c_write_str("No passphrase required.\r\n");
@@ -2326,6 +2470,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                send_packet(SSH1_MSG_DISCONNECT,
                            PKT_STR, "No more passwords available to try",
                            PKT_END);
+               logevent("Unable to authenticate");
                connection_fatal("Unable to authenticate");
                ssh_state = SSH_STATE_CLOSED;
                crReturn(1);
@@ -2356,8 +2501,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                        break;
                      case 3:
                      case 4:
-                       random_save_seed();
-                       exit(0);
+                       cleanup_exit(0);
                        break;
                      default:
                        if (pos < sizeof(password)-1)
@@ -2701,11 +2845,13 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
     }
 
     {
-       char type, *e;
+       char type;
+       static char *e;
        int n;
-       int sport,dport;
+       int sport,dport,sserv,dserv;
        char sports[256], dports[256], host[256];
        char buf[1024];
+       struct servent *se;
 
        ssh_rportfwds = newtree234(ssh_rportcmp_ssh1);
         /* Add port forwardings. */
@@ -2730,12 +2876,43 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
            dports[n] = 0;
            e++;
            dport = atoi(dports);
+           dserv = 0;
+           if (dport == 0) {
+               dserv = 1;
+               se = getservbyname(dports, NULL);
+               if (se != NULL) {
+                   dport = ntohs(se->s_port);
+               } else {
+                   sprintf(buf,
+                           "Service lookup failed for destination port \"%s\"",
+                           dports);
+                   logevent(buf);
+               }
+           }
            sport = atoi(sports);
+           sserv = 0;
+           if (sport == 0) {
+               sserv = 1;
+               se = getservbyname(sports, NULL);
+               if (se != NULL) {
+                   sport = ntohs(se->s_port);
+               } else {
+                   sprintf(buf,
+                           "Service lookup failed for source port \"%s\"",
+                           sports);
+                   logevent(buf);
+               }
+           }
            if (sport && dport) {
                if (type == 'L') {
                    pfd_addforward(host, dport, sport);
-                   sprintf(buf, "Local port %d forwarding to %s:%d",
-                           sport, host, dport);
+                   sprintf(buf, "Local port %.*s%.*s%d%.*s forwarding to"
+                           " %s:%.*s%.*s%d%.*s",
+                           sserv ? strlen(sports) : 0, sports,
+                           sserv, "(", sport, sserv, ")",
+                           host,
+                           dserv ? strlen(dports) : 0, dports,
+                           dserv, "(", dport, dserv, ")");
                    logevent(buf);
                } else {
                    struct ssh_rportfwd *pf;
@@ -2749,14 +2926,31 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                        logevent(buf);
                        sfree(pf);
                    } else {
-                       sprintf(buf, "Requesting remote port %d forward to %s:%d",
-                               sport, host, dport);
+                       sprintf(buf, "Requesting remote port %.*s%.*s%d%.*s"
+                               " forward to %s:%.*s%.*s%d%.*s",
+                           sserv ? strlen(sports) : 0, sports,
+                           sserv, "(", sport, sserv, ")",
+                           host,
+                           dserv ? strlen(dports) : 0, dports,
+                           dserv, "(", dport, dserv, ")");
                        logevent(buf);
                        send_packet(SSH1_CMSG_PORT_FORWARD_REQUEST,
                                    PKT_INT, sport,
                                    PKT_STR, host,
                                    PKT_INT, dport,
                                    PKT_END);
+                       do {
+                           crReturnV;
+                       } while (!ispkt);
+                       if (pktin.type != SSH1_SMSG_SUCCESS
+                           && pktin.type != SSH1_SMSG_FAILURE) {
+                           bombout(("Protocol confusion"));
+                           crReturnV;
+                       } else if (pktin.type == SSH1_SMSG_FAILURE) {
+                           c_write_str("Server refused port forwarding\r\n");
+                           ssh_editing = ssh_echoing = 1;
+                       }
+                       logevent("Remote port forwarding enabled");
                    }
                }
            }
@@ -3715,6 +3909,14 @@ static int ssh2_try_send(struct ssh_channel *c)
  */
 static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
 {
+    /*
+     * Never send WINDOW_ADJUST for a channel that the remote side
+     * already thinks it's closed; there's no point, since it won't
+     * be sending any more data anyway.
+     */
+    if (c->closes != 0)
+       return;
+
     if (newwin > c->v.v2.locwindow) {
        ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
        ssh2_pkt_adduint32(c->remoteid);
@@ -3749,8 +3951,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     static int we_are_in;
     static int num_prompts, echo;
     static char username[100];
+    static int got_username;
     static char pwprompt[200];
     static char password[100];
+    static void *publickey_blob;
+    static int publickey_bloblen;
 
     crBegin;
 
@@ -3791,6 +3996,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
      *    retype it!
      */
     username[0] = '\0';
+    got_username = FALSE;
     do {
        static int pos;
        static char c;
@@ -3799,7 +4005,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
         * Get a username.
         */
        pos = 0;
-       if (*username && !cfg.change_username) {
+       if (got_username && !cfg.change_username) {
            /*
             * We got a username last time round this loop, and
             * with change_username turned off we don't try to get
@@ -3845,8 +4051,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                            break;
                          case 3:
                          case 4:
-                           random_save_seed();
-                           exit(0);
+                           cleanup_exit(0);
                            break;
                          default:
                            if (((c >= ' ' && c <= '~') ||
@@ -3870,6 +4075,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                c_write_str(stuff);
            }
        }
+       got_username = TRUE;
 
        /*
         * Send an authentication request using method "none": (a)
@@ -3891,6 +4097,25 @@ 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) {
+           int keytype;
+           logeventf("Reading private key file \"%.150s\"", cfg.keyfile);
+           keytype = key_type(cfg.keyfile);
+           if (keytype == SSH_KEYTYPE_SSH2)
+               publickey_blob = ssh2_userkey_loadpub(cfg.keyfile, NULL,
+                                                     &publickey_bloblen);
+           else {
+               char msgbuf[256];
+               logeventf("Unable to use this key file (%s)",
+                       key_type_to_str(keytype));
+               sprintf(msgbuf, "Unable to use key file \"%.150s\" (%s)\r\n",
+                       cfg.keyfile, key_type_to_str(keytype));
+               c_write_str(msgbuf);
+               publickey_blob = NULL;
+           }
+       } else
+           publickey_blob = NULL;
 
        while (1) {
            /*
@@ -4046,6 +4271,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);
@@ -4141,7 +4372,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                }
            }
 
-           if (!method && can_pubkey && *cfg.keyfile
+           if (!method && can_pubkey && publickey_blob
                && !tried_pubkey_config) {
                unsigned char *pub_blob;
                char *algorithm, *comment;
@@ -4280,6 +4511,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                            ("No more passwords available to try");
                        ssh2_pkt_addstring("en");       /* language tag */
                        ssh2_pkt_send();
+                       logevent("Unable to authenticate");
                        connection_fatal("Unable to authenticate");
                        ssh_state = SSH_STATE_CLOSED;
                        crReturnV;
@@ -4312,8 +4544,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                                break;
                              case 3:
                              case 4:
-                               random_save_seed();
-                               exit(0);
+                               cleanup_exit(0);
                                break;
                              default:
                                if (pos < sizeof(password)-1)
@@ -4560,9 +4791,10 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
        static char *e;                /* preserve across crReturn */
        char type;
        int n;
-       int sport,dport;
+       int sport,dport,sserv,dserv;
        char sports[256], dports[256], host[256];
        char buf[1024];
+       struct servent *se;
 
        ssh_rportfwds = newtree234(ssh_rportcmp_ssh2);
         /* Add port forwardings. */
@@ -4587,12 +4819,43 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            dports[n] = 0;
            e++;
            dport = atoi(dports);
+           dserv = 0;
+           if (dport == 0) {
+               dserv = 1;
+               se = getservbyname(dports, NULL);
+               if (se != NULL) {
+                   dport = ntohs(se->s_port);
+               } else {
+                   sprintf(buf,
+                           "Service lookup failed for destination port \"%s\"",
+                           dports);
+                   logevent(buf);
+               }
+           }
            sport = atoi(sports);
+           sserv = 0;
+           if (sport == 0) {
+               sserv = 1;
+               se = getservbyname(sports, NULL);
+               if (se != NULL) {
+                   sport = ntohs(se->s_port);
+               } else {
+                   sprintf(buf,
+                           "Service lookup failed for source port \"%s\"",
+                           sports);
+                   logevent(buf);
+               }
+           }
            if (sport && dport) {
                if (type == 'L') {
                    pfd_addforward(host, dport, sport);
-                   sprintf(buf, "Local port %d forwarding to %s:%d",
-                           sport, host, dport);
+                   sprintf(buf, "Local port %.*s%.*s%d%.*s forwarding to"
+                           " %s:%.*s%.*s%d%.*s",
+                           sserv ? strlen(sports) : 0, sports,
+                           sserv, "(", sport, sserv, ")",
+                           host,
+                           dserv ? strlen(dports) : 0, dports,
+                           dserv, "(", dport, dserv, ")");
                    logevent(buf);
                } else {
                    struct ssh_rportfwd *pf;
@@ -4607,8 +4870,13 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                        logevent(buf);
                        sfree(pf);
                    } else {
-                       sprintf(buf, "Requesting remote port %d (forwarded to %s:%d)",
-                               sport, host, dport);
+                       sprintf(buf, "Requesting remote port %.*s%.*s%d%.*s"
+                               " forward to %s:%.*s%.*s%d%.*s",
+                           sserv ? strlen(sports) : 0, sports,
+                           sserv, "(", sport, sserv, ")",
+                           host,
+                           dserv ? strlen(dports) : 0, dports,
+                           dserv, "(", dport, dserv, ")");
                        logevent(buf);
                        ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
                        ssh2_pkt_addstring("tcpip-forward");
@@ -5061,7 +5329,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    ssh2_pkt_addstring(buf);
                    ssh2_pkt_addstring("en");   /* language tag */
                    ssh2_pkt_send();
-                   connection_fatal(buf);
+                   connection_fatal("%s", buf);
                    ssh_state = SSH_STATE_CLOSED;
                    crReturnV;
                }
@@ -5097,6 +5365,23 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                        ssh2_pkt_send();
                    }
                }
+           } else if (pktin.type == SSH2_MSG_GLOBAL_REQUEST) {
+               char *type;
+               int typelen, want_reply;
+
+               ssh2_pkt_getstring(&type, &typelen);
+               want_reply = ssh2_pkt_getbool();
+
+                /*
+                 * We currently don't support any global requests
+                 * at all, so we either ignore the request or
+                 * respond with REQUEST_FAILURE, depending on
+                 * want_reply.
+                 */
+                if (want_reply) {
+                    ssh2_pkt_init(SSH2_MSG_REQUEST_FAILURE);
+                    ssh2_pkt_send();
+               }
            } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) {
                char *type;
                int typelen;