- (djm) Big OpenBSD sync:
   - markus@cvs.openbsd.org  2000/09/30 10:27:44
     [log.c]
     allow loglevel debug
   - markus@cvs.openbsd.org  2000/10/03 11:59:57
     [packet.c]
     hmac->mac
   - markus@cvs.openbsd.org  2000/10/03 12:03:03
     [auth-krb4.c auth-passwd.c auth-rh-rsa.c auth-rhosts.c auth-rsa.c auth1.c]
     move fake-auth from auth1.c to individual auth methods, disables s/key in
     debug-msg
   - markus@cvs.openbsd.org  2000/10/03 12:16:48
     ssh.c
     do not resolve canonname, i have no idea why this was added oin ossh
   - markus@cvs.openbsd.org  2000/10/09 15:30:44
     ssh-keygen.1 ssh-keygen.c
     -X now reads private ssh.com DSA keys, too.
   - markus@cvs.openbsd.org  2000/10/09 15:32:34
     auth-options.c
     clear options on every call.
   - markus@cvs.openbsd.org  2000/10/09 15:51:00
     authfd.c authfd.h
     interop with ssh-agent2, from <res@shore.net>
   - markus@cvs.openbsd.org  2000/10/10 14:20:45
     compat.c
     use rexexp for version string matching
   - provos@cvs.openbsd.org  2000/10/10 22:02:18
     [kex.c kex.h myproposal.h ssh.h ssh2.h sshconnect2.c sshd.c dh.c dh.h]
     First rough implementation of the diffie-hellman group exchange.  The
     client can ask the server for bigger groups to perform the diffie-hellman
     in, thus increasing the attack complexity when using ciphers with longer
     keys.  University of Windsor provided network, T the company.
   - markus@cvs.openbsd.org  2000/10/11 13:59:52
     [auth-rsa.c auth2.c]
     clear auth options unless auth sucessfull
   - markus@cvs.openbsd.org  2000/10/11 14:00:27
     [auth-options.h]
     clear auth options unless auth sucessfull
   - markus@cvs.openbsd.org  2000/10/11 14:03:27
     [scp.1 scp.c]
     support 'scp -o' with help from mouring@pconline.com
   - markus@cvs.openbsd.org  2000/10/11 14:11:35
     [dh.c]
     Wall
   - markus@cvs.openbsd.org  2000/10/11 14:14:40
     [auth.h auth2.c readconf.c readconf.h readpass.c servconf.c servconf.h]
     [ssh.h sshconnect2.c sshd_config auth2-skey.c cli.c cli.h]
     add support for s/key (kbd-interactive) to ssh2, based on work by
     mkiernan@avantgo.com and me
   - markus@cvs.openbsd.org  2000/10/11 14:27:24
     [auth.c auth1.c auth2.c authfile.c cipher.c cipher.h kex.c kex.h]
     [myproposal.h packet.c readconf.c session.c ssh.c ssh.h sshconnect1.c]
     [sshconnect2.c sshd.c]
     new cipher framework
   - markus@cvs.openbsd.org  2000/10/11 14:45:21
     [cipher.c]
     remove DES
   - markus@cvs.openbsd.org  2000/10/12 03:59:20
     [cipher.c cipher.h sshconnect1.c sshconnect2.c sshd.c]
     enable DES in SSH-1 clients only
   - markus@cvs.openbsd.org  2000/10/12 08:21:13
     [kex.h packet.c]
     remove unused
   - markus@cvs.openbsd.org  2000/10/13 12:34:46
     [sshd.c]
     Kludge for F-Secure Macintosh < 1.0.2; appro@fy.chalmers.se
   - markus@cvs.openbsd.org  2000/10/13 12:59:15
     [cipher.c cipher.h myproposal.h  rijndael.c rijndael.h]
     rijndael/aes support
   - markus@cvs.openbsd.org  2000/10/13 13:10:54
     [sshd.8]
     more info about -V
   - markus@cvs.openbsd.org  2000/10/13 13:12:02
     [myproposal.h]
     prefer no compression
diff --git a/sshconnect2.c b/sshconnect2.c
index eee09a1..ca459f6 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect2.c,v 1.21 2000/09/27 21:41:34 markus Exp $");
+RCSID("$OpenBSD: sshconnect2.c,v 1.25 2000/10/12 09:59:19 markus Exp $");
 
 #include <openssl/bn.h>
 #include <openssl/rsa.h>
@@ -37,7 +37,6 @@
 #include "rsa.h"
 #include "buffer.h"
 #include "packet.h"
-#include "cipher.h"
 #include "uidswap.h"
 #include "compat.h"
 #include "readconf.h"
@@ -49,9 +48,13 @@
 #include "dsa.h"
 #include "sshconnect.h"
 #include "authfile.h"
+#include "cli.h"
 #include "dispatch.h"
 #include "authfd.h"
 
+void ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
+void ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
+
 /* import */
 extern char *client_version_string;
 extern char *server_version_string;
@@ -65,8 +68,90 @@
 int session_id2_len = 0;
 
 void
-ssh_kex_dh(Kex *kex, char *host, struct sockaddr *hostaddr,
-    Buffer *client_kexinit, Buffer *server_kexinit)
+ssh_kex2(char *host, struct sockaddr *hostaddr)
+{
+	int i, plen;
+	Kex *kex;
+	Buffer *client_kexinit, *server_kexinit;
+	char *sprop[PROPOSAL_MAX];
+
+	if (options.ciphers == NULL) {
+		if (options.cipher == SSH_CIPHER_3DES) {
+			options.ciphers = "3des-cbc";
+		} else if (options.cipher == SSH_CIPHER_BLOWFISH) {
+			options.ciphers = "blowfish-cbc";
+		} else if (options.cipher == SSH_CIPHER_DES) {
+			fatal("cipher DES not supported for protocol version 2");
+		}
+	}
+	if (options.ciphers != NULL) {
+		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
+		myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
+	}
+	if (options.compression) {
+		myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib";
+		myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib";
+	} else {
+		myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none";
+		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
+	}
+
+	/* buffers with raw kexinit messages */
+	server_kexinit = xmalloc(sizeof(*server_kexinit));
+	buffer_init(server_kexinit);
+	client_kexinit = kex_init(myproposal);
+
+	/* algorithm negotiation */
+	kex_exchange_kexinit(client_kexinit, server_kexinit, sprop);
+	kex = kex_choose_conf(myproposal, sprop, 0);
+	for (i = 0; i < PROPOSAL_MAX; i++)
+		xfree(sprop[i]);
+
+	/* server authentication and session key agreement */
+	switch(kex->kex_type) {
+	case DH_GRP1_SHA1:
+		ssh_dh1_client(kex, host, hostaddr,
+			       client_kexinit, server_kexinit);
+		break;
+	case DH_GEX_SHA1:
+		ssh_dhgex_client(kex, host, hostaddr, client_kexinit,
+				 server_kexinit);
+		break;
+	default:
+		fatal("Unsupported key exchange %d", kex->kex_type);
+	}
+
+	buffer_free(client_kexinit);
+	buffer_free(server_kexinit);
+	xfree(client_kexinit);
+	xfree(server_kexinit);
+
+	debug("Wait SSH2_MSG_NEWKEYS.");
+	packet_read_expect(&plen, SSH2_MSG_NEWKEYS);
+	packet_done();
+	debug("GOT SSH2_MSG_NEWKEYS.");
+
+	debug("send SSH2_MSG_NEWKEYS.");
+	packet_start(SSH2_MSG_NEWKEYS);
+	packet_send();
+	packet_write_wait();
+	debug("done: send SSH2_MSG_NEWKEYS.");
+
+#ifdef DEBUG_KEXDH
+	/* send 1st encrypted/maced/compressed message */
+	packet_start(SSH2_MSG_IGNORE);
+	packet_put_cstring("markus");
+	packet_send();
+	packet_write_wait();
+#endif
+	debug("done: KEX2.");
+}
+
+/* diffie-hellman-group1-sha1 */
+
+void
+ssh_dh1_client(Kex *kex, char *host, struct sockaddr *hostaddr, 
+	       Buffer *client_kexinit, Buffer *server_kexinit)
 {
 #ifdef DEBUG_KEXDH
 	int i;
@@ -116,7 +201,7 @@
 		fatal("cannot decode server_host_key_blob");
 
 	check_host_key(host, hostaddr, server_host_key,
-	    options.user_hostfile2, options.system_hostfile2);
+		       options.user_hostfile2, options.system_hostfile2);
 
 	/* DH paramter f, server public DH key */
 	dh_server_pub = BN_new();
@@ -186,72 +271,175 @@
 	memcpy(session_id2, hash, session_id2_len);
 }
 
-void
-ssh_kex2(char *host, struct sockaddr *hostaddr)
+/* diffie-hellman-group-exchange-sha1 */
+
+/*
+ * Estimates the group order for a Diffie-Hellman group that has an
+ * attack complexity approximately the same as O(2**bits).  Estimate
+ * with:  O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3)))
+ */
+
+int
+dh_estimate(int bits)
 {
-	int i, plen;
-	Kex *kex;
-	Buffer *client_kexinit, *server_kexinit;
-	char *sprop[PROPOSAL_MAX];
+	
+	if (bits < 64)
+		return (512);	/* O(2**63) */
+	if (bits < 128)
+		return (1024);	/* O(2**86) */
+	if (bits < 192)
+		return (2048);	/* O(2**116) */
+	return (4096);		/* O(2**156) */
+}
 
-	if (options.ciphers != NULL) {
-		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
-		myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
-	} else if (options.cipher == SSH_CIPHER_3DES) {
-		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
-		myproposal[PROPOSAL_ENC_ALGS_STOC] =
-		    (char *) cipher_name(SSH_CIPHER_3DES_CBC);
-	} else if (options.cipher == SSH_CIPHER_BLOWFISH) {
-		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
-		myproposal[PROPOSAL_ENC_ALGS_STOC] =
-		    (char *) cipher_name(SSH_CIPHER_BLOWFISH_CBC);
-	}
-	if (options.compression) {
-		myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib";
-		myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib";
-	} else {
-		myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none";
-		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
-	}
+void
+ssh_dhgex_client(Kex *kex, char *host, struct sockaddr *hostaddr,
+		 Buffer *client_kexinit, Buffer *server_kexinit)
+{
+#ifdef DEBUG_KEXDH
+	int i;
+#endif
+	int plen, dlen;
+	unsigned int klen, kout;
+	char *signature = NULL;
+	unsigned int slen, nbits;
+	char *server_host_key_blob = NULL;
+	Key *server_host_key;
+	unsigned int sbloblen;
+	DH *dh;
+	BIGNUM *dh_server_pub = 0;
+	BIGNUM *shared_secret = 0;
+	BIGNUM *p = 0, *g = 0;
+	unsigned char *kbuf;
+	unsigned char *hash;
 
-	/* buffers with raw kexinit messages */
-	server_kexinit = xmalloc(sizeof(*server_kexinit));
-	buffer_init(server_kexinit);
-	client_kexinit = kex_init(myproposal);
+	nbits = dh_estimate(kex->enc[MODE_OUT].cipher->key_len * 8);
 
-	/* algorithm negotiation */
-	kex_exchange_kexinit(client_kexinit, server_kexinit, sprop);
-	kex = kex_choose_conf(myproposal, sprop, 0);
-	for (i = 0; i < PROPOSAL_MAX; i++)
-		xfree(sprop[i]);
-
-	/* server authentication and session key agreement */
-	ssh_kex_dh(kex, host, hostaddr, client_kexinit, server_kexinit);
-
-	buffer_free(client_kexinit);
-	buffer_free(server_kexinit);
-	xfree(client_kexinit);
-	xfree(server_kexinit);
-
-	debug("Wait SSH2_MSG_NEWKEYS.");
-	packet_read_expect(&plen, SSH2_MSG_NEWKEYS);
-	packet_done();
-	debug("GOT SSH2_MSG_NEWKEYS.");
-
-	debug("send SSH2_MSG_NEWKEYS.");
-	packet_start(SSH2_MSG_NEWKEYS);
+	debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST.");
+	packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST);
+	packet_put_int(nbits);
 	packet_send();
 	packet_write_wait();
-	debug("done: send SSH2_MSG_NEWKEYS.");
 
 #ifdef DEBUG_KEXDH
-	/* send 1st encrypted/maced/compressed message */
-	packet_start(SSH2_MSG_IGNORE);
-	packet_put_cstring("markus");
+	fprintf(stderr, "\nnbits = %d", nbits);
+#endif
+
+	debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP.");
+
+	packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP);
+
+	debug("Got SSH2_MSG_KEX_DH_GEX_GROUP.");
+
+	if ((p = BN_new()) == NULL)
+		fatal("BN_new");
+	packet_get_bignum2(p, &dlen);
+	if ((g = BN_new()) == NULL)
+		fatal("BN_new");
+	packet_get_bignum2(g, &dlen);
+	if ((dh = dh_new_group(g, p)) == NULL)
+		fatal("dh_new_group");
+
+#ifdef DEBUG_KEXDH
+	fprintf(stderr, "\np= ");
+	BN_print_fp(stderr, dh->p);
+	fprintf(stderr, "\ng= ");
+	BN_print_fp(stderr, dh->g);
+	fprintf(stderr, "\npub= ");
+	BN_print_fp(stderr, dh->pub_key);
+	fprintf(stderr, "\n");
+	DHparams_print_fp(stderr, dh);
+#endif
+
+	debug("Sending SSH2_MSG_KEX_DH_GEX_INIT.");
+	/* generate and send 'e', client DH public key */
+	packet_start(SSH2_MSG_KEX_DH_GEX_INIT);
+	packet_put_bignum2(dh->pub_key);
 	packet_send();
 	packet_write_wait();
+
+	debug("Wait SSH2_MSG_KEX_DH_GEX_REPLY.");
+
+	packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_REPLY);
+
+	debug("Got SSH2_MSG_KEXDH_REPLY.");
+
+	/* key, cert */
+	server_host_key_blob = packet_get_string(&sbloblen);
+	server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen);
+	if (server_host_key == NULL)
+		fatal("cannot decode server_host_key_blob");
+
+	check_host_key(host, hostaddr, server_host_key,
+		       options.user_hostfile2, options.system_hostfile2);
+
+	/* DH paramter f, server public DH key */
+	dh_server_pub = BN_new();
+	if (dh_server_pub == NULL)
+		fatal("dh_server_pub == NULL");
+	packet_get_bignum2(dh_server_pub, &dlen);
+
+#ifdef DEBUG_KEXDH
+	fprintf(stderr, "\ndh_server_pub= ");
+	BN_print_fp(stderr, dh_server_pub);
+	fprintf(stderr, "\n");
+	debug("bits %d", BN_num_bits(dh_server_pub));
 #endif
-	debug("done: KEX2.");
+
+	/* signed H */
+	signature = packet_get_string(&slen);
+	packet_done();
+
+	if (!dh_pub_is_valid(dh, dh_server_pub))
+		packet_disconnect("bad server public DH value");
+
+	klen = DH_size(dh);
+	kbuf = xmalloc(klen);
+	kout = DH_compute_key(kbuf, dh_server_pub, dh);
+#ifdef DEBUG_KEXDH
+	debug("shared secret: len %d/%d", klen, kout);
+	fprintf(stderr, "shared secret == ");
+	for (i = 0; i< kout; i++)
+		fprintf(stderr, "%02x", (kbuf[i])&0xff);
+	fprintf(stderr, "\n");
+#endif
+	shared_secret = BN_new();
+
+	BN_bin2bn(kbuf, kout, shared_secret);
+	memset(kbuf, 0, klen);
+	xfree(kbuf);
+
+	/* calc and verify H */
+	hash = kex_hash_gex(
+	    client_version_string,
+	    server_version_string,
+	    buffer_ptr(client_kexinit), buffer_len(client_kexinit),
+	    buffer_ptr(server_kexinit), buffer_len(server_kexinit),
+	    server_host_key_blob, sbloblen,
+	    nbits, dh->p, dh->g, 
+	    dh->pub_key,
+	    dh_server_pub,
+	    shared_secret
+	);
+	xfree(server_host_key_blob);
+	DH_free(dh);
+#ifdef DEBUG_KEXDH
+	fprintf(stderr, "hash == ");
+	for (i = 0; i< 20; i++)
+		fprintf(stderr, "%02x", (hash[i])&0xff);
+	fprintf(stderr, "\n");
+#endif
+	if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1)
+		fatal("dsa_verify failed for server_host_key");
+	key_free(server_host_key);
+
+	kex_derive_keys(kex, hash, shared_secret);
+	packet_set_kex(kex);
+
+	/* save session id */
+	session_id2_len = 20;
+	session_id2 = xmalloc(session_id2_len);
+	memcpy(session_id2, hash, session_id2_len);
 }
 
 /*
@@ -270,8 +458,8 @@
 	const char *host;
 	const char *service;
 	AuthenticationConnection *agent;
-	int success;
 	Authmethod *method;
+	int success;
 };
 struct Authmethod {
 	char	*name;		/* string to compare against server's list */
@@ -283,11 +471,16 @@
 void	input_userauth_success(int type, int plen, void *ctxt);
 void	input_userauth_failure(int type, int plen, void *ctxt);
 void	input_userauth_error(int type, int plen, void *ctxt);
+void	input_userauth_info_req(int type, int plen, void *ctxt);
+
+int	userauth_none(Authctxt *authctxt);
 int	userauth_pubkey(Authctxt *authctxt);
 int	userauth_passwd(Authctxt *authctxt);
+int	userauth_kbdint(Authctxt *authctxt);
 
 void	authmethod_clear();
-Authmethod *authmethod_get(char *auth_list);
+Authmethod *authmethod_get(char *authlist);
+Authmethod *authmethod_lookup(const char *name);
 
 Authmethod authmethods[] = {
 	{"publickey",
@@ -298,6 +491,14 @@
 		userauth_passwd,
 		&options.password_authentication,
 		&options.batch_mode},
+	{"keyboard-interactive",
+		userauth_kbdint,
+		&options.kbd_interactive_authentication,
+		&options.batch_mode},
+	{"none",
+		userauth_none,
+		NULL,
+		NULL},
 	{NULL, NULL, NULL, NULL}
 };
 
@@ -334,17 +535,13 @@
 	authctxt.host = host;
 	authctxt.service = "ssh-connection";		/* service name */
 	authctxt.success = 0;
-	authctxt.method = NULL;
+	authctxt.method = authmethod_lookup("none");
+	if (authctxt.method == NULL)
+		fatal("ssh_userauth2: internal error: cannot send userauth none request");
+	authmethod_clear();
 
 	/* initial userauth request */
-	packet_start(SSH2_MSG_USERAUTH_REQUEST);
-	packet_put_cstring(authctxt.server_user);
-	packet_put_cstring(authctxt.service);
-	packet_put_cstring("none");
-	packet_send();
-	packet_write_wait();
-
-	authmethod_clear();
+	userauth_none(&authctxt);
 
 	dispatch_init(&input_userauth_error);
 	dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
@@ -354,7 +551,7 @@
 	if (authctxt.agent != NULL)
 		ssh_close_authentication_connection(authctxt.agent);
 
-	debug("ssh-userauth2 successfull");
+	debug("ssh-userauth2 successfull: method %s", authctxt.method->name);
 }
 void
 input_userauth_error(int type, int plen, void *ctxt)
@@ -376,12 +573,11 @@
 	Authctxt *authctxt = ctxt;
 	char *authlist = NULL;
 	int partial;
-	int dlen;
 
 	if (authctxt == NULL)
 		fatal("input_userauth_failure: no authentication context");
 
-	authlist = packet_get_string(&dlen);
+	authlist = packet_get_string(NULL);
 	partial = packet_get_char();
 	packet_done();
 
@@ -390,12 +586,12 @@
 	debug("authentications that can continue: %s", authlist);
 
 	for (;;) {
-		/* try old method or get next method */
 		method = authmethod_get(authlist);
 		if (method == NULL)
                         fatal("Unable to find an authentication method");
+		authctxt->method = method;
 		if (method->userauth(authctxt) != 0) {
-			debug2("we sent a packet, wait for reply");
+			debug2("we sent a %s packet, wait for reply", method->name);
 			break;
 		} else {
 			debug2("we did not send a packet, disable method");
@@ -406,6 +602,19 @@
 }
 
 int
+userauth_none(Authctxt *authctxt)
+{
+	/* initial userauth request */
+	packet_start(SSH2_MSG_USERAUTH_REQUEST);
+	packet_put_cstring(authctxt->server_user);
+	packet_put_cstring(authctxt->service);
+	packet_put_cstring(authctxt->method->name);
+	packet_send();
+	packet_write_wait();
+	return 1;
+}
+
+int
 userauth_passwd(Authctxt *authctxt)
 {
 	static int attempt = 0;
@@ -424,7 +633,7 @@
 	packet_start(SSH2_MSG_USERAUTH_REQUEST);
 	packet_put_cstring(authctxt->server_user);
 	packet_put_cstring(authctxt->service);
-	packet_put_cstring("password");
+	packet_put_cstring(authctxt->method->name);
 	packet_put_char(0);
 	packet_put_cstring(password);
 	memset(password, 0, strlen(password));
@@ -442,6 +651,7 @@
 	int bloblen, slen;
 	int skip = 0;
 	int ret = -1;
+	int have_sig = 1;
 
 	dsa_make_key_blob(k, &blob, &bloblen);
 
@@ -460,8 +670,8 @@
 	    datafellows & SSH_BUG_PUBKEYAUTH ?
 	    "ssh-userauth" :
 	    authctxt->service);
-	buffer_put_cstring(&b, "publickey");
-	buffer_put_char(&b, 1);
+	buffer_put_cstring(&b, authctxt->method->name);
+	buffer_put_char(&b, have_sig);
 	buffer_put_cstring(&b, KEX_DSS); 
 	buffer_put_string(&b, blob, bloblen);
 
@@ -481,8 +691,8 @@
 		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
 		buffer_put_cstring(&b, authctxt->server_user);
 		buffer_put_cstring(&b, authctxt->service);
-		buffer_put_cstring(&b, "publickey");
-		buffer_put_char(&b, 1);
+		buffer_put_cstring(&b, authctxt->method->name);
+		buffer_put_char(&b, have_sig);
 		buffer_put_cstring(&b, KEX_DSS); 
 		buffer_put_string(&b, blob, bloblen);
 	}
@@ -606,6 +816,92 @@
 	return sent;
 }
 
+/*
+ * Send userauth request message specifying keyboard-interactive method.
+ */
+int
+userauth_kbdint(Authctxt *authctxt)
+{
+	static int attempt = 0;
+
+	if (attempt++ >= options.number_of_password_prompts)
+		return 0;
+
+	debug2("userauth_kbdint");
+	packet_start(SSH2_MSG_USERAUTH_REQUEST);
+	packet_put_cstring(authctxt->server_user);
+	packet_put_cstring(authctxt->service);
+	packet_put_cstring(authctxt->method->name);
+	packet_put_cstring("");					/* lang */
+	packet_put_cstring(options.kbd_interactive_devices ?
+	    options.kbd_interactive_devices : "");
+	packet_send();
+	packet_write_wait();
+
+	dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
+	return 1;
+}
+
+/*
+ * parse SSH2_MSG_USERAUTH_INFO_REQUEST, prompt user and send
+ * SSH2_MSG_USERAUTH_INFO_RESPONSE
+ */
+void
+input_userauth_info_req(int type, int plen, void *ctxt)
+{
+	Authctxt *authctxt = ctxt;
+	char *name = NULL;
+	char *inst = NULL;
+	char *lang = NULL;
+	char *prompt = NULL;
+	char *response = NULL;
+	unsigned int num_prompts, i;
+	int echo = 0;
+
+	debug2("input_userauth_info_req");
+
+	if (authctxt == NULL)
+		fatal("input_userauth_info_req: no authentication context");
+
+	name = packet_get_string(NULL);
+	inst = packet_get_string(NULL);
+	lang = packet_get_string(NULL);
+
+	if (strlen(name) > 0)
+		cli_mesg(name);
+	xfree(name);
+
+	if (strlen(inst) > 0)
+		cli_mesg(inst);
+	xfree(inst);
+	xfree(lang); 				/* unused */
+
+	num_prompts = packet_get_int();
+	/*
+	 * Begin to build info response packet based on prompts requested.
+	 * We commit to providing the correct number of responses, so if
+	 * further on we run into a problem that prevents this, we have to
+	 * be sure and clean this up and send a correct error response.
+	 */
+	packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE);
+	packet_put_int(num_prompts);
+
+	for (i = 0; i < num_prompts; i++) {
+		prompt = packet_get_string(NULL);
+		echo = packet_get_char();
+
+		response = cli_prompt(prompt, echo);
+
+		packet_put_cstring(response);
+		memset(response, 0, strlen(response));
+		xfree(response);
+		xfree(prompt);
+	}
+	packet_done(); /* done with parsing incoming message. */
+
+	packet_send();
+	packet_write_wait();
+}
 
 /* find auth method */
 
@@ -692,6 +988,7 @@
 
 	if (authlist_current == NULL || strcmp(authlist, authlist_current) != 0) {
 		/* start over if passed a different list */
+		debug3("start over, passed a different list");
 		authmethod_clear();
 		authlist_current = xstrdup(authlist);
 		authlist_working = xstrdup(authlist);
@@ -706,16 +1003,20 @@
 	}
 
 	while (name != NULL) {
+		debug3("authmethod_lookup %s", name);
 		method = authmethod_lookup(name);
-		if (method != NULL && authmethod_is_enabled(method))
+		if (method != NULL && authmethod_is_enabled(method)) {
+			debug3("authmethod_is_enabled %s", name);
 			break;
+		}
 		name = strtok_r(NULL, DELIM, &authlist_state);
+		method = NULL;
 	}
 
 	if (authname_current != NULL)
 		xfree(authname_current);
 
-	if (name != NULL) {
+	if (method != NULL) {
 		debug("next auth method to try is %s", name);
 		authname_current = xstrdup(name);
 		return method;