Anecdotal evidence suggests that a single EnumPrinters() call
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index 339b98e..7cc9d20 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -515,8 +515,6 @@ static const struct ssh_compress *sccomp = NULL;
 static const struct ssh_kex *kex = NULL;
 static const struct ssh_signkey *hostkey = NULL;
 static unsigned char ssh2_session_id[20];
-int (*ssh_get_line) (const char *prompt, char *str, int maxlen,
-                    int is_pw) = NULL;
 
 static char *savedhost;
 static int savedport;
@@ -1541,6 +1539,7 @@ static int ssh2_pkt_getbool(void)
 static void ssh2_pkt_getstring(char **p, int *length)
 {
     *p = NULL;
+    *length = 0;
     if (pktin.length - pktin.savedpos < 4)
        return;
     *length = GET_32BIT(pktin.data + pktin.savedpos);
@@ -1711,6 +1710,7 @@ static int do_ssh_init(unsigned char c)
     static int vstrsize;
     static char *vlog;
     static int i;
+    static int proto1, proto2;
 
     crBegin;
 
@@ -1767,12 +1767,26 @@ static int do_ssh_init(unsigned char c)
     sfree(vlog);
 
     /*
-     * Server version "1.99" means we can choose whether we use v1
-     * or v2 protocol. Choice is based on cfg.sshprot.
+     * Decide which SSH protocol version to support.
      */
-    if (ssh_versioncmp(version, cfg.sshprot == 1 ? "2.0" : "1.99") >= 0) {
+
+    /* Anything strictly below "2.0" means protocol 1 is supported. */
+    proto1 = ssh_versioncmp(version, "2.0") < 0;
+    /* Anything greater or equal to "1.99" means protocol 2 is supported. */
+    proto2 = ssh_versioncmp(version, "1.99") >= 0;
+
+    if (cfg.sshprot == 0 && !proto1) {
+       bombout(("SSH protocol version 1 required by user but not provided by server"));
+       crReturn(0);
+    }
+    if (cfg.sshprot == 3 && !proto2) {
+       bombout(("SSH protocol version 2 required by user but not provided by server"));
+       crReturn(0);
+    }
+
+    if (proto2 && (cfg.sshprot >= 2 || !proto1)) {
        /*
-        * This is a v2 server. Begin v2 protocol.
+        * Use v2 protocol.
         */
        char verstring[80], vlog[100];
        sprintf(verstring, "SSH-2.0-%s", sshver);
@@ -1792,7 +1806,7 @@ static int do_ssh_init(unsigned char c)
        s_rdpkt = ssh2_rdpkt;
     } else {
        /*
-        * This is a v1 server. Begin v1 protocol.
+        * Use v1 protocol.
         */
        char verstring[80], vlog[100];
        sprintf(verstring, "SSH-%s-%s",
@@ -1801,11 +1815,6 @@ static int do_ssh_init(unsigned char c)
        sprintf(vlog, "We claim version: %s", verstring);
        logevent(vlog);
        strcat(verstring, "\n");
-       
-       if (cfg.sshprot == 3) {
-           bombout(("SSH protocol version 2 required by user but not provided by server"));
-           crReturn(0);
-       }
 
        logevent("Using SSH protocol version 1");
        sk_write(s, verstring, strlen(verstring));
@@ -2227,7 +2236,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        static int pos = 0;
        static char c;
        if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
-           if (ssh_get_line) {
+           if (ssh_get_line && !ssh_getline_pw_only) {
                if (!ssh_get_line("login as: ",
                                  username, sizeof(username), FALSE)) {
                    /*
@@ -4034,7 +4043,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     static int tried_pubkey_config, tried_agent, tried_keyb_inter;
     static int kbd_inter_running;
     static int we_are_in;
-    static int num_prompts, echo;
+    static int num_prompts, curr_prompt, echo;
     static char username[100];
     static int got_username;
     static char pwprompt[200];
@@ -4097,7 +4106,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
             * it again.
             */
        } else if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
-           if (ssh_get_line) {
+           if (ssh_get_line && !ssh_getline_pw_only) {
                if (!ssh_get_line("login as: ",
                                  username, sizeof(username), FALSE)) {
                    /*
@@ -4235,9 +4244,14 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            if (kbd_inter_running &&
                pktin.type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
                /*
-                * This is a further prompt in keyboard-interactive
-                * authentication. Do nothing.
+                * This is either a further set-of-prompts packet
+                * in keyboard-interactive authentication, or it's
+                * the same one and we came back here with `gotit'
+                * set. In the former case, we must reset the
+                * curr_prompt variable.
                 */
+               if (!gotit)
+                   curr_prompt = 0;
            } else if (pktin.type != SSH2_MSG_USERAUTH_FAILURE) {
                bombout(("Strange packet received during authentication: type %d",
                         pktin.type));
@@ -4321,6 +4335,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                static int authed = FALSE;
                void *r;
 
+               ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
                ssh_pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
 
                tried_agent = TRUE;
@@ -4463,6 +4478,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 
                tried_pubkey_config = TRUE;
 
+               ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
                ssh_pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
 
                /*
@@ -4517,6 +4533,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
                tried_keyb_inter = TRUE;
 
+               ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
                ssh_pkt_ctx |= SSH2_PKTCTX_KBDINTER;
 
                ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
@@ -4537,6 +4554,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                }
 
                kbd_inter_running = TRUE;
+               curr_prompt = 0;
            }
 
            if (kbd_inter_running) {
@@ -4544,34 +4562,58 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
                tried_keyb_inter = TRUE;
 
+               ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
                ssh_pkt_ctx |= SSH2_PKTCTX_KBDINTER;
 
-               /* We've got packet with that "interactive" info
-                  dump banners, and set its prompt as ours */
-               {
-                   char *name, *inst, *lang, *prompt;
-                   int name_len, inst_len, lang_len, prompt_len;
+               if (curr_prompt == 0) {
+                   /*
+                    * We've got a fresh USERAUTH_INFO_REQUEST.
+                    * Display header data, and start going through
+                    * the prompts.
+                    */
+                   char *name, *inst, *lang;
+                   int name_len, inst_len, lang_len;
+
                    ssh2_pkt_getstring(&name, &name_len);
                    ssh2_pkt_getstring(&inst, &inst_len);
                    ssh2_pkt_getstring(&lang, &lang_len);
-                   if (name_len > 0)
+                   if (name_len > 0) {
                        c_write_untrusted(name, name_len);
-                   if (inst_len > 0)
+                       c_write_str("\n");
+                   }
+                   if (inst_len > 0) {
                        c_write_untrusted(inst, inst_len);
+                       c_write_str("\n");
+                   }
                    num_prompts = ssh2_pkt_getuint32();
+               }
 
-                   ssh2_pkt_getstring(&prompt, &prompt_len);
-                   strncpy(pwprompt, prompt, sizeof(pwprompt));
-                   pwprompt[prompt_len < sizeof(pwprompt) ?
-                            prompt_len : sizeof(pwprompt)-1] = '\0';
-                   need_pw = TRUE;
+               /*
+                * If there are prompts remaining in the packet,
+                * display one and get a response.
+                */
+               if (curr_prompt < num_prompts) {
+                   char *prompt;
+                   int prompt_len;
 
+                   ssh2_pkt_getstring(&prompt, &prompt_len);
+                   if (prompt_len > 0) {
+                       strncpy(pwprompt, prompt, sizeof(pwprompt));
+                       pwprompt[prompt_len < sizeof(pwprompt) ?
+                                prompt_len : sizeof(pwprompt)-1] = '\0';
+                   } else {
+                       strcpy(pwprompt,
+                              "<server failed to send prompt>: ");
+                   }
                    echo = ssh2_pkt_getbool();
-               }
+                   need_pw = TRUE;
+               } else
+                   need_pw = FALSE;
            }
 
            if (!method && can_passwd) {
                method = AUTH_PASSWORD;
+               ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
                ssh_pkt_ctx |= SSH2_PKTCTX_PASSWORD;
                sprintf(pwprompt, "%.90s@%.90s's password: ", username,
                        savedhost);
@@ -4763,11 +4805,28 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                logevent("Sent password");
                type = AUTH_TYPE_PASSWORD;
            } else if (method == AUTH_KEYBOARD_INTERACTIVE) {
-                ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
-                ssh2_pkt_adduint32(num_prompts);
-                ssh2_pkt_addstring(password);
-                memset(password, 0, sizeof(password));
-                ssh2_pkt_send();
+               if (curr_prompt == 0) {
+                   ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
+                   ssh2_pkt_adduint32(num_prompts);
+               }
+               if (need_pw) {         /* only add pw if we just got one! */
+                   ssh2_pkt_addstring(password);
+                   memset(password, 0, sizeof(password));
+                   curr_prompt++;
+               }
+               if (curr_prompt >= num_prompts) {
+                   ssh2_pkt_send();
+               } else {
+                   /*
+                    * If there are prompts remaining, we set
+                    * `gotit' so that we won't attempt to get
+                    * another packet. Then we go back round the
+                    * loop and will end up retrieving another
+                    * prompt out of the existing packet. Funky or
+                    * what?
+                    */
+                   gotit = TRUE;
+               }
                type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
            } else {
                c_write_str
@@ -5357,7 +5416,6 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                c->type = CHAN_SOCKDATA;
                c->v.v2.remwindow = ssh2_pkt_getuint32();
                c->v.v2.remmaxpkt = ssh2_pkt_getuint32();
-               bufchain_init(&c->v.v2.outbuffer);
                if (c->u.pfd.s)
                    pfd_confirm(c->u.pfd.s);
                if (c->closes) {
@@ -5759,6 +5817,7 @@ void *new_sock_channel(Socket s)
        c->closes = 0;
        c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
        c->u.pfd.s = s;
+       bufchain_init(&c->v.v2.outbuffer);
        add234(ssh_channels, c);
     }
     return c;