sshconnect2.c revision 69587
160573Skris/*
260573Skris * Copyright (c) 2000 Markus Friedl.  All rights reserved.
360573Skris *
460573Skris * Redistribution and use in source and binary forms, with or without
560573Skris * modification, are permitted provided that the following conditions
660573Skris * are met:
760573Skris * 1. Redistributions of source code must retain the above copyright
860573Skris *    notice, this list of conditions and the following disclaimer.
960573Skris * 2. Redistributions in binary form must reproduce the above copyright
1060573Skris *    notice, this list of conditions and the following disclaimer in the
1160573Skris *    documentation and/or other materials provided with the distribution.
1260573Skris *
1360573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1460573Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1560573Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1660573Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1760573Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1860573Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1960573Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2060573Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2160573Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2260573Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2360573Skris */
2460573Skris
2560573Skris#include "includes.h"
2669587SgreenRCSID("$OpenBSD: sshconnect2.c,v 1.27 2000/10/19 16:45:16 provos Exp $");
2760573Skris
2860573Skris#include <openssl/bn.h>
2960573Skris#include <openssl/rsa.h>
3060573Skris#include <openssl/dsa.h>
3160573Skris#include <openssl/md5.h>
3260573Skris#include <openssl/dh.h>
3360573Skris#include <openssl/hmac.h>
3460573Skris
3560573Skris#include "ssh.h"
3660573Skris#include "xmalloc.h"
3760573Skris#include "rsa.h"
3860573Skris#include "buffer.h"
3960573Skris#include "packet.h"
4060573Skris#include "uidswap.h"
4160573Skris#include "compat.h"
4260573Skris#include "readconf.h"
4360573Skris#include "bufaux.h"
4460573Skris#include "ssh2.h"
4560573Skris#include "kex.h"
4660573Skris#include "myproposal.h"
4760573Skris#include "key.h"
4860573Skris#include "dsa.h"
4960573Skris#include "sshconnect.h"
5060573Skris#include "authfile.h"
5169587Sgreen#include "cli.h"
5269587Sgreen#include "dispatch.h"
5365668Skris#include "authfd.h"
5460573Skris
5569587Sgreenvoid ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
5669587Sgreenvoid ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
5769587Sgreen
5860573Skris/* import */
5960573Skrisextern char *client_version_string;
6060573Skrisextern char *server_version_string;
6160573Skrisextern Options options;
6260573Skris
6360573Skris/*
6460573Skris * SSH2 key exchange
6560573Skris */
6660573Skris
6760573Skrisunsigned char *session_id2 = NULL;
6860573Skrisint session_id2_len = 0;
6960573Skris
7060573Skrisvoid
7169587Sgreenssh_kex2(char *host, struct sockaddr *hostaddr)
7260573Skris{
7369587Sgreen	int i, plen;
7469587Sgreen	Kex *kex;
7569587Sgreen	Buffer *client_kexinit, *server_kexinit;
7669587Sgreen	char *sprop[PROPOSAL_MAX];
7769587Sgreen
7869587Sgreen	if (options.ciphers == NULL) {
7969587Sgreen		if (options.cipher == SSH_CIPHER_3DES) {
8069587Sgreen			options.ciphers = "3des-cbc";
8169587Sgreen		} else if (options.cipher == SSH_CIPHER_BLOWFISH) {
8269587Sgreen			options.ciphers = "blowfish-cbc";
8369587Sgreen		} else if (options.cipher == SSH_CIPHER_DES) {
8469587Sgreen			fatal("cipher DES not supported for protocol version 2");
8569587Sgreen		}
8669587Sgreen	}
8769587Sgreen	if (options.ciphers != NULL) {
8869587Sgreen		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
8969587Sgreen		myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
9069587Sgreen	}
9169587Sgreen	if (options.compression) {
9269587Sgreen		myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib";
9369587Sgreen		myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib";
9469587Sgreen	} else {
9569587Sgreen		myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none";
9669587Sgreen		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
9769587Sgreen	}
9869587Sgreen
9969587Sgreen	/* buffers with raw kexinit messages */
10069587Sgreen	server_kexinit = xmalloc(sizeof(*server_kexinit));
10169587Sgreen	buffer_init(server_kexinit);
10269587Sgreen	client_kexinit = kex_init(myproposal);
10369587Sgreen
10469587Sgreen	/* algorithm negotiation */
10569587Sgreen	kex_exchange_kexinit(client_kexinit, server_kexinit, sprop);
10669587Sgreen	kex = kex_choose_conf(myproposal, sprop, 0);
10769587Sgreen	for (i = 0; i < PROPOSAL_MAX; i++)
10869587Sgreen		xfree(sprop[i]);
10969587Sgreen
11069587Sgreen	/* server authentication and session key agreement */
11169587Sgreen	switch(kex->kex_type) {
11269587Sgreen	case DH_GRP1_SHA1:
11369587Sgreen		ssh_dh1_client(kex, host, hostaddr,
11469587Sgreen			       client_kexinit, server_kexinit);
11569587Sgreen		break;
11669587Sgreen	case DH_GEX_SHA1:
11769587Sgreen		ssh_dhgex_client(kex, host, hostaddr, client_kexinit,
11869587Sgreen				 server_kexinit);
11969587Sgreen		break;
12069587Sgreen	default:
12169587Sgreen		fatal("Unsupported key exchange %d", kex->kex_type);
12269587Sgreen	}
12369587Sgreen
12469587Sgreen	buffer_free(client_kexinit);
12569587Sgreen	buffer_free(server_kexinit);
12669587Sgreen	xfree(client_kexinit);
12769587Sgreen	xfree(server_kexinit);
12869587Sgreen
12969587Sgreen	debug("Wait SSH2_MSG_NEWKEYS.");
13069587Sgreen	packet_read_expect(&plen, SSH2_MSG_NEWKEYS);
13169587Sgreen	packet_done();
13269587Sgreen	debug("GOT SSH2_MSG_NEWKEYS.");
13369587Sgreen
13469587Sgreen	debug("send SSH2_MSG_NEWKEYS.");
13569587Sgreen	packet_start(SSH2_MSG_NEWKEYS);
13669587Sgreen	packet_send();
13769587Sgreen	packet_write_wait();
13869587Sgreen	debug("done: send SSH2_MSG_NEWKEYS.");
13969587Sgreen
14069587Sgreen#ifdef DEBUG_KEXDH
14169587Sgreen	/* send 1st encrypted/maced/compressed message */
14269587Sgreen	packet_start(SSH2_MSG_IGNORE);
14369587Sgreen	packet_put_cstring("markus");
14469587Sgreen	packet_send();
14569587Sgreen	packet_write_wait();
14669587Sgreen#endif
14769587Sgreen	debug("done: KEX2.");
14869587Sgreen}
14969587Sgreen
15069587Sgreen/* diffie-hellman-group1-sha1 */
15169587Sgreen
15269587Sgreenvoid
15369587Sgreenssh_dh1_client(Kex *kex, char *host, struct sockaddr *hostaddr,
15469587Sgreen	       Buffer *client_kexinit, Buffer *server_kexinit)
15569587Sgreen{
15669587Sgreen#ifdef DEBUG_KEXDH
15769587Sgreen	int i;
15869587Sgreen#endif
15961209Skris	int plen, dlen;
16060573Skris	unsigned int klen, kout;
16160573Skris	char *signature = NULL;
16260573Skris	unsigned int slen;
16360573Skris	char *server_host_key_blob = NULL;
16460573Skris	Key *server_host_key;
16560573Skris	unsigned int sbloblen;
16660573Skris	DH *dh;
16760573Skris	BIGNUM *dh_server_pub = 0;
16860573Skris	BIGNUM *shared_secret = 0;
16960573Skris	unsigned char *kbuf;
17060573Skris	unsigned char *hash;
17160573Skris
17260573Skris	debug("Sending SSH2_MSG_KEXDH_INIT.");
17360573Skris	/* generate and send 'e', client DH public key */
17460573Skris	dh = dh_new_group1();
17560573Skris	packet_start(SSH2_MSG_KEXDH_INIT);
17660573Skris	packet_put_bignum2(dh->pub_key);
17760573Skris	packet_send();
17860573Skris	packet_write_wait();
17960573Skris
18060573Skris#ifdef DEBUG_KEXDH
18160573Skris	fprintf(stderr, "\np= ");
18269587Sgreen	BN_print_fp(stderr, dh->p);
18360573Skris	fprintf(stderr, "\ng= ");
18469587Sgreen	BN_print_fp(stderr, dh->g);
18560573Skris	fprintf(stderr, "\npub= ");
18669587Sgreen	BN_print_fp(stderr, dh->pub_key);
18760573Skris	fprintf(stderr, "\n");
18860573Skris	DHparams_print_fp(stderr, dh);
18960573Skris#endif
19060573Skris
19160573Skris	debug("Wait SSH2_MSG_KEXDH_REPLY.");
19260573Skris
19361209Skris	packet_read_expect(&plen, SSH2_MSG_KEXDH_REPLY);
19460573Skris
19560573Skris	debug("Got SSH2_MSG_KEXDH_REPLY.");
19660573Skris
19760573Skris	/* key, cert */
19860573Skris	server_host_key_blob = packet_get_string(&sbloblen);
19960573Skris	server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen);
20060573Skris	if (server_host_key == NULL)
20160573Skris		fatal("cannot decode server_host_key_blob");
20260573Skris
20360573Skris	check_host_key(host, hostaddr, server_host_key,
20469587Sgreen		       options.user_hostfile2, options.system_hostfile2);
20560573Skris
20660573Skris	/* DH paramter f, server public DH key */
20760573Skris	dh_server_pub = BN_new();
20860573Skris	if (dh_server_pub == NULL)
20960573Skris		fatal("dh_server_pub == NULL");
21060573Skris	packet_get_bignum2(dh_server_pub, &dlen);
21160573Skris
21260573Skris#ifdef DEBUG_KEXDH
21360573Skris	fprintf(stderr, "\ndh_server_pub= ");
21469587Sgreen	BN_print_fp(stderr, dh_server_pub);
21560573Skris	fprintf(stderr, "\n");
21660573Skris	debug("bits %d", BN_num_bits(dh_server_pub));
21760573Skris#endif
21860573Skris
21960573Skris	/* signed H */
22060573Skris	signature = packet_get_string(&slen);
22160573Skris	packet_done();
22260573Skris
22360573Skris	if (!dh_pub_is_valid(dh, dh_server_pub))
22460573Skris		packet_disconnect("bad server public DH value");
22560573Skris
22660573Skris	klen = DH_size(dh);
22760573Skris	kbuf = xmalloc(klen);
22860573Skris	kout = DH_compute_key(kbuf, dh_server_pub, dh);
22960573Skris#ifdef DEBUG_KEXDH
23060573Skris	debug("shared secret: len %d/%d", klen, kout);
23160573Skris	fprintf(stderr, "shared secret == ");
23260573Skris	for (i = 0; i< kout; i++)
23360573Skris		fprintf(stderr, "%02x", (kbuf[i])&0xff);
23460573Skris	fprintf(stderr, "\n");
23560573Skris#endif
23660573Skris	shared_secret = BN_new();
23760573Skris
23860573Skris	BN_bin2bn(kbuf, kout, shared_secret);
23960573Skris	memset(kbuf, 0, klen);
24060573Skris	xfree(kbuf);
24160573Skris
24260573Skris	/* calc and verify H */
24360573Skris	hash = kex_hash(
24460573Skris	    client_version_string,
24560573Skris	    server_version_string,
24660573Skris	    buffer_ptr(client_kexinit), buffer_len(client_kexinit),
24760573Skris	    buffer_ptr(server_kexinit), buffer_len(server_kexinit),
24860573Skris	    server_host_key_blob, sbloblen,
24960573Skris	    dh->pub_key,
25060573Skris	    dh_server_pub,
25160573Skris	    shared_secret
25260573Skris	);
25360573Skris	xfree(server_host_key_blob);
25461209Skris	DH_free(dh);
25560573Skris#ifdef DEBUG_KEXDH
25660573Skris	fprintf(stderr, "hash == ");
25760573Skris	for (i = 0; i< 20; i++)
25860573Skris		fprintf(stderr, "%02x", (hash[i])&0xff);
25960573Skris	fprintf(stderr, "\n");
26060573Skris#endif
26160573Skris	if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1)
26260573Skris		fatal("dsa_verify failed for server_host_key");
26360573Skris	key_free(server_host_key);
26460573Skris
26560573Skris	kex_derive_keys(kex, hash, shared_secret);
26660573Skris	packet_set_kex(kex);
26760573Skris
26860573Skris	/* save session id */
26960573Skris	session_id2_len = 20;
27060573Skris	session_id2 = xmalloc(session_id2_len);
27160573Skris	memcpy(session_id2, hash, session_id2_len);
27261209Skris}
27360573Skris
27469587Sgreen/* diffie-hellman-group-exchange-sha1 */
27569587Sgreen
27669587Sgreen/*
27769587Sgreen * Estimates the group order for a Diffie-Hellman group that has an
27869587Sgreen * attack complexity approximately the same as O(2**bits).  Estimate
27969587Sgreen * with:  O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3)))
28069587Sgreen */
28169587Sgreen
28269587Sgreenint
28369587Sgreendh_estimate(int bits)
28469587Sgreen{
28569587Sgreen
28669587Sgreen	if (bits < 64)
28769587Sgreen		return (512);	/* O(2**63) */
28869587Sgreen	if (bits < 128)
28969587Sgreen		return (1024);	/* O(2**86) */
29069587Sgreen	if (bits < 192)
29169587Sgreen		return (2048);	/* O(2**116) */
29269587Sgreen	return (4096);		/* O(2**156) */
29369587Sgreen}
29469587Sgreen
29561209Skrisvoid
29669587Sgreenssh_dhgex_client(Kex *kex, char *host, struct sockaddr *hostaddr,
29769587Sgreen		 Buffer *client_kexinit, Buffer *server_kexinit)
29861209Skris{
29969587Sgreen#ifdef DEBUG_KEXDH
30069587Sgreen	int i;
30169587Sgreen#endif
30269587Sgreen	int plen, dlen;
30369587Sgreen	unsigned int klen, kout;
30469587Sgreen	char *signature = NULL;
30569587Sgreen	unsigned int slen, nbits;
30669587Sgreen	char *server_host_key_blob = NULL;
30769587Sgreen	Key *server_host_key;
30869587Sgreen	unsigned int sbloblen;
30969587Sgreen	DH *dh;
31069587Sgreen	BIGNUM *dh_server_pub = 0;
31169587Sgreen	BIGNUM *shared_secret = 0;
31269587Sgreen	BIGNUM *p = 0, *g = 0;
31369587Sgreen	unsigned char *kbuf;
31469587Sgreen	unsigned char *hash;
31561209Skris
31669587Sgreen	nbits = dh_estimate(kex->enc[MODE_OUT].cipher->key_len * 8);
31761209Skris
31869587Sgreen	debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST.");
31969587Sgreen	packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST);
32069587Sgreen	packet_put_int(nbits);
32169587Sgreen	packet_send();
32269587Sgreen	packet_write_wait();
32361209Skris
32469587Sgreen#ifdef DEBUG_KEXDH
32569587Sgreen	fprintf(stderr, "\nnbits = %d", nbits);
32669587Sgreen#endif
32761209Skris
32869587Sgreen	debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP.");
32961209Skris
33069587Sgreen	packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP);
33161209Skris
33269587Sgreen	debug("Got SSH2_MSG_KEX_DH_GEX_GROUP.");
33360573Skris
33469587Sgreen	if ((p = BN_new()) == NULL)
33569587Sgreen		fatal("BN_new");
33669587Sgreen	packet_get_bignum2(p, &dlen);
33769587Sgreen	if ((g = BN_new()) == NULL)
33869587Sgreen		fatal("BN_new");
33969587Sgreen	packet_get_bignum2(g, &dlen);
34069587Sgreen	if ((dh = dh_new_group(g, p)) == NULL)
34169587Sgreen		fatal("dh_new_group");
34269587Sgreen
34369587Sgreen#ifdef DEBUG_KEXDH
34469587Sgreen	fprintf(stderr, "\np= ");
34569587Sgreen	BN_print_fp(stderr, dh->p);
34669587Sgreen	fprintf(stderr, "\ng= ");
34769587Sgreen	BN_print_fp(stderr, dh->g);
34869587Sgreen	fprintf(stderr, "\npub= ");
34969587Sgreen	BN_print_fp(stderr, dh->pub_key);
35069587Sgreen	fprintf(stderr, "\n");
35169587Sgreen	DHparams_print_fp(stderr, dh);
35269587Sgreen#endif
35369587Sgreen
35469587Sgreen	debug("Sending SSH2_MSG_KEX_DH_GEX_INIT.");
35569587Sgreen	/* generate and send 'e', client DH public key */
35669587Sgreen	packet_start(SSH2_MSG_KEX_DH_GEX_INIT);
35769587Sgreen	packet_put_bignum2(dh->pub_key);
35860573Skris	packet_send();
35960573Skris	packet_write_wait();
36060573Skris
36169587Sgreen	debug("Wait SSH2_MSG_KEX_DH_GEX_REPLY.");
36269587Sgreen
36369587Sgreen	packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_REPLY);
36469587Sgreen
36569587Sgreen	debug("Got SSH2_MSG_KEXDH_REPLY.");
36669587Sgreen
36769587Sgreen	/* key, cert */
36869587Sgreen	server_host_key_blob = packet_get_string(&sbloblen);
36969587Sgreen	server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen);
37069587Sgreen	if (server_host_key == NULL)
37169587Sgreen		fatal("cannot decode server_host_key_blob");
37269587Sgreen
37369587Sgreen	check_host_key(host, hostaddr, server_host_key,
37469587Sgreen		       options.user_hostfile2, options.system_hostfile2);
37569587Sgreen
37669587Sgreen	/* DH paramter f, server public DH key */
37769587Sgreen	dh_server_pub = BN_new();
37869587Sgreen	if (dh_server_pub == NULL)
37969587Sgreen		fatal("dh_server_pub == NULL");
38069587Sgreen	packet_get_bignum2(dh_server_pub, &dlen);
38169587Sgreen
38260573Skris#ifdef DEBUG_KEXDH
38369587Sgreen	fprintf(stderr, "\ndh_server_pub= ");
38469587Sgreen	BN_print_fp(stderr, dh_server_pub);
38569587Sgreen	fprintf(stderr, "\n");
38669587Sgreen	debug("bits %d", BN_num_bits(dh_server_pub));
38760573Skris#endif
38869587Sgreen
38969587Sgreen	/* signed H */
39069587Sgreen	signature = packet_get_string(&slen);
39169587Sgreen	packet_done();
39269587Sgreen
39369587Sgreen	if (!dh_pub_is_valid(dh, dh_server_pub))
39469587Sgreen		packet_disconnect("bad server public DH value");
39569587Sgreen
39669587Sgreen	klen = DH_size(dh);
39769587Sgreen	kbuf = xmalloc(klen);
39869587Sgreen	kout = DH_compute_key(kbuf, dh_server_pub, dh);
39969587Sgreen#ifdef DEBUG_KEXDH
40069587Sgreen	debug("shared secret: len %d/%d", klen, kout);
40169587Sgreen	fprintf(stderr, "shared secret == ");
40269587Sgreen	for (i = 0; i< kout; i++)
40369587Sgreen		fprintf(stderr, "%02x", (kbuf[i])&0xff);
40469587Sgreen	fprintf(stderr, "\n");
40569587Sgreen#endif
40669587Sgreen	shared_secret = BN_new();
40769587Sgreen
40869587Sgreen	BN_bin2bn(kbuf, kout, shared_secret);
40969587Sgreen	memset(kbuf, 0, klen);
41069587Sgreen	xfree(kbuf);
41169587Sgreen
41269587Sgreen	/* calc and verify H */
41369587Sgreen	hash = kex_hash_gex(
41469587Sgreen	    client_version_string,
41569587Sgreen	    server_version_string,
41669587Sgreen	    buffer_ptr(client_kexinit), buffer_len(client_kexinit),
41769587Sgreen	    buffer_ptr(server_kexinit), buffer_len(server_kexinit),
41869587Sgreen	    server_host_key_blob, sbloblen,
41969587Sgreen	    nbits, dh->p, dh->g,
42069587Sgreen	    dh->pub_key,
42169587Sgreen	    dh_server_pub,
42269587Sgreen	    shared_secret
42369587Sgreen	);
42469587Sgreen	xfree(server_host_key_blob);
42569587Sgreen	DH_free(dh);
42669587Sgreen#ifdef DEBUG_KEXDH
42769587Sgreen	fprintf(stderr, "hash == ");
42869587Sgreen	for (i = 0; i< 20; i++)
42969587Sgreen		fprintf(stderr, "%02x", (hash[i])&0xff);
43069587Sgreen	fprintf(stderr, "\n");
43169587Sgreen#endif
43269587Sgreen	if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1)
43369587Sgreen		fatal("dsa_verify failed for server_host_key");
43469587Sgreen	key_free(server_host_key);
43569587Sgreen
43669587Sgreen	kex_derive_keys(kex, hash, shared_secret);
43769587Sgreen	packet_set_kex(kex);
43869587Sgreen
43969587Sgreen	/* save session id */
44069587Sgreen	session_id2_len = 20;
44169587Sgreen	session_id2 = xmalloc(session_id2_len);
44269587Sgreen	memcpy(session_id2, hash, session_id2_len);
44360573Skris}
44461209Skris
44560573Skris/*
44660573Skris * Authenticate user
44760573Skris */
44869587Sgreen
44969587Sgreentypedef struct Authctxt Authctxt;
45069587Sgreentypedef struct Authmethod Authmethod;
45169587Sgreen
45269587Sgreentypedef int sign_cb_fn(
45369587Sgreen    Authctxt *authctxt, Key *key,
45469587Sgreen    unsigned char **sigp, int *lenp, unsigned char *data, int datalen);
45569587Sgreen
45669587Sgreenstruct Authctxt {
45769587Sgreen	const char *server_user;
45869587Sgreen	const char *host;
45969587Sgreen	const char *service;
46069587Sgreen	AuthenticationConnection *agent;
46169587Sgreen	Authmethod *method;
46269587Sgreen	int success;
46369587Sgreen};
46469587Sgreenstruct Authmethod {
46569587Sgreen	char	*name;		/* string to compare against server's list */
46669587Sgreen	int	(*userauth)(Authctxt *authctxt);
46769587Sgreen	int	*enabled;	/* flag in option struct that enables method */
46869587Sgreen	int	*batch_flag;	/* flag in option struct that disables method */
46969587Sgreen};
47069587Sgreen
47169587Sgreenvoid	input_userauth_success(int type, int plen, void *ctxt);
47269587Sgreenvoid	input_userauth_failure(int type, int plen, void *ctxt);
47369587Sgreenvoid	input_userauth_error(int type, int plen, void *ctxt);
47469587Sgreenvoid	input_userauth_info_req(int type, int plen, void *ctxt);
47569587Sgreen
47669587Sgreenint	userauth_none(Authctxt *authctxt);
47769587Sgreenint	userauth_pubkey(Authctxt *authctxt);
47869587Sgreenint	userauth_passwd(Authctxt *authctxt);
47969587Sgreenint	userauth_kbdint(Authctxt *authctxt);
48069587Sgreen
48169587Sgreenvoid	authmethod_clear();
48269587SgreenAuthmethod *authmethod_get(char *authlist);
48369587SgreenAuthmethod *authmethod_lookup(const char *name);
48469587Sgreen
48569587SgreenAuthmethod authmethods[] = {
48669587Sgreen	{"publickey",
48769587Sgreen		userauth_pubkey,
48869587Sgreen		&options.dsa_authentication,
48969587Sgreen		NULL},
49069587Sgreen	{"password",
49169587Sgreen		userauth_passwd,
49269587Sgreen		&options.password_authentication,
49369587Sgreen		&options.batch_mode},
49469587Sgreen	{"keyboard-interactive",
49569587Sgreen		userauth_kbdint,
49669587Sgreen		&options.kbd_interactive_authentication,
49769587Sgreen		&options.batch_mode},
49869587Sgreen	{"none",
49969587Sgreen		userauth_none,
50069587Sgreen		NULL,
50169587Sgreen		NULL},
50269587Sgreen	{NULL, NULL, NULL, NULL}
50369587Sgreen};
50469587Sgreen
50569587Sgreenvoid
50669587Sgreenssh_userauth2(const char *server_user, char *host)
50769587Sgreen{
50869587Sgreen	Authctxt authctxt;
50969587Sgreen	int type;
51069587Sgreen	int plen;
51169587Sgreen
51269587Sgreen	debug("send SSH2_MSG_SERVICE_REQUEST");
51369587Sgreen	packet_start(SSH2_MSG_SERVICE_REQUEST);
51469587Sgreen	packet_put_cstring("ssh-userauth");
51569587Sgreen	packet_send();
51669587Sgreen	packet_write_wait();
51769587Sgreen	type = packet_read(&plen);
51869587Sgreen	if (type != SSH2_MSG_SERVICE_ACCEPT) {
51969587Sgreen		fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type);
52069587Sgreen	}
52169587Sgreen	if (packet_remaining() > 0) {
52269587Sgreen		char *reply = packet_get_string(&plen);
52369587Sgreen		debug("service_accept: %s", reply);
52469587Sgreen		xfree(reply);
52569587Sgreen		packet_done();
52669587Sgreen	} else {
52769587Sgreen		debug("buggy server: service_accept w/o service");
52869587Sgreen	}
52969587Sgreen	packet_done();
53069587Sgreen	debug("got SSH2_MSG_SERVICE_ACCEPT");
53169587Sgreen
53269587Sgreen	/* setup authentication context */
53369587Sgreen	authctxt.agent = ssh_get_authentication_connection();
53469587Sgreen	authctxt.server_user = server_user;
53569587Sgreen	authctxt.host = host;
53669587Sgreen	authctxt.service = "ssh-connection";		/* service name */
53769587Sgreen	authctxt.success = 0;
53869587Sgreen	authctxt.method = authmethod_lookup("none");
53969587Sgreen	if (authctxt.method == NULL)
54069587Sgreen		fatal("ssh_userauth2: internal error: cannot send userauth none request");
54169587Sgreen	authmethod_clear();
54269587Sgreen
54369587Sgreen	/* initial userauth request */
54469587Sgreen	userauth_none(&authctxt);
54569587Sgreen
54669587Sgreen	dispatch_init(&input_userauth_error);
54769587Sgreen	dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
54869587Sgreen	dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
54969587Sgreen	dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt);	/* loop until success */
55069587Sgreen
55169587Sgreen	if (authctxt.agent != NULL)
55269587Sgreen		ssh_close_authentication_connection(authctxt.agent);
55369587Sgreen
55469587Sgreen	debug("ssh-userauth2 successfull: method %s", authctxt.method->name);
55569587Sgreen}
55669587Sgreenvoid
55769587Sgreeninput_userauth_error(int type, int plen, void *ctxt)
55869587Sgreen{
55969587Sgreen	fatal("input_userauth_error: bad message during authentication");
56069587Sgreen}
56169587Sgreenvoid
56269587Sgreeninput_userauth_success(int type, int plen, void *ctxt)
56369587Sgreen{
56469587Sgreen	Authctxt *authctxt = ctxt;
56569587Sgreen	if (authctxt == NULL)
56669587Sgreen		fatal("input_userauth_success: no authentication context");
56769587Sgreen	authctxt->success = 1;			/* break out */
56869587Sgreen}
56969587Sgreenvoid
57069587Sgreeninput_userauth_failure(int type, int plen, void *ctxt)
57169587Sgreen{
57269587Sgreen	Authmethod *method = NULL;
57369587Sgreen	Authctxt *authctxt = ctxt;
57469587Sgreen	char *authlist = NULL;
57569587Sgreen	int partial;
57669587Sgreen
57769587Sgreen	if (authctxt == NULL)
57869587Sgreen		fatal("input_userauth_failure: no authentication context");
57969587Sgreen
58069587Sgreen	authlist = packet_get_string(NULL);
58169587Sgreen	partial = packet_get_char();
58269587Sgreen	packet_done();
58369587Sgreen
58469587Sgreen	if (partial != 0)
58569587Sgreen		debug("partial success");
58669587Sgreen	debug("authentications that can continue: %s", authlist);
58769587Sgreen
58869587Sgreen	for (;;) {
58969587Sgreen		method = authmethod_get(authlist);
59069587Sgreen		if (method == NULL)
59169587Sgreen                        fatal("Unable to find an authentication method");
59269587Sgreen		authctxt->method = method;
59369587Sgreen		if (method->userauth(authctxt) != 0) {
59469587Sgreen			debug2("we sent a %s packet, wait for reply", method->name);
59569587Sgreen			break;
59669587Sgreen		} else {
59769587Sgreen			debug2("we did not send a packet, disable method");
59869587Sgreen			method->enabled = NULL;
59969587Sgreen		}
60069587Sgreen	}
60169587Sgreen	xfree(authlist);
60269587Sgreen}
60369587Sgreen
60460573Skrisint
60569587Sgreenuserauth_none(Authctxt *authctxt)
60660573Skris{
60769587Sgreen	/* initial userauth request */
60869587Sgreen	packet_start(SSH2_MSG_USERAUTH_REQUEST);
60969587Sgreen	packet_put_cstring(authctxt->server_user);
61069587Sgreen	packet_put_cstring(authctxt->service);
61169587Sgreen	packet_put_cstring(authctxt->method->name);
61269587Sgreen	packet_send();
61369587Sgreen	packet_write_wait();
61469587Sgreen	return 1;
61569587Sgreen}
61669587Sgreen
61769587Sgreenint
61869587Sgreenuserauth_passwd(Authctxt *authctxt)
61969587Sgreen{
62060573Skris	static int attempt = 0;
62160573Skris	char prompt[80];
62260573Skris	char *password;
62360573Skris
62465668Skris	if (attempt++ >= options.number_of_password_prompts)
62560573Skris		return 0;
62660573Skris
62765668Skris	if(attempt != 1)
62865668Skris		error("Permission denied, please try again.");
62965668Skris
63060573Skris	snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ",
63169587Sgreen	    authctxt->server_user, authctxt->host);
63260573Skris	password = read_passphrase(prompt, 0);
63360573Skris	packet_start(SSH2_MSG_USERAUTH_REQUEST);
63469587Sgreen	packet_put_cstring(authctxt->server_user);
63569587Sgreen	packet_put_cstring(authctxt->service);
63669587Sgreen	packet_put_cstring(authctxt->method->name);
63760573Skris	packet_put_char(0);
63860573Skris	packet_put_cstring(password);
63960573Skris	memset(password, 0, strlen(password));
64060573Skris	xfree(password);
64160573Skris	packet_send();
64260573Skris	packet_write_wait();
64360573Skris	return 1;
64460573Skris}
64560573Skris
64660573Skrisint
64769587Sgreensign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback)
64860573Skris{
64960573Skris	Buffer b;
65060573Skris	unsigned char *blob, *signature;
65160573Skris	int bloblen, slen;
65265668Skris	int skip = 0;
65365668Skris	int ret = -1;
65469587Sgreen	int have_sig = 1;
65560573Skris
65660573Skris	dsa_make_key_blob(k, &blob, &bloblen);
65760573Skris
65860573Skris	/* data to be signed */
65960573Skris	buffer_init(&b);
66069587Sgreen	if (datafellows & SSH_OLD_SESSIONID) {
66169587Sgreen		buffer_append(&b, session_id2, session_id2_len);
66269587Sgreen		skip = session_id2_len;
66369587Sgreen	} else {
66465668Skris		buffer_put_string(&b, session_id2, session_id2_len);
66565668Skris		skip = buffer_len(&b);
66665668Skris	}
66760573Skris	buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
66869587Sgreen	buffer_put_cstring(&b, authctxt->server_user);
66960573Skris	buffer_put_cstring(&b,
67060573Skris	    datafellows & SSH_BUG_PUBKEYAUTH ?
67160573Skris	    "ssh-userauth" :
67269587Sgreen	    authctxt->service);
67369587Sgreen	buffer_put_cstring(&b, authctxt->method->name);
67469587Sgreen	buffer_put_char(&b, have_sig);
67560573Skris	buffer_put_cstring(&b, KEX_DSS);
67660573Skris	buffer_put_string(&b, blob, bloblen);
67760573Skris
67860573Skris	/* generate signature */
67969587Sgreen	ret = (*sign_callback)(authctxt, k, &signature, &slen, buffer_ptr(&b), buffer_len(&b));
68065668Skris	if (ret == -1) {
68165668Skris		xfree(blob);
68265668Skris		buffer_free(&b);
68365668Skris		return 0;
68465668Skris	}
68560573Skris#ifdef DEBUG_DSS
68660573Skris	buffer_dump(&b);
68760573Skris#endif
68860573Skris	if (datafellows & SSH_BUG_PUBKEYAUTH) {
68960573Skris		buffer_clear(&b);
69060573Skris		buffer_append(&b, session_id2, session_id2_len);
69160573Skris		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
69269587Sgreen		buffer_put_cstring(&b, authctxt->server_user);
69369587Sgreen		buffer_put_cstring(&b, authctxt->service);
69469587Sgreen		buffer_put_cstring(&b, authctxt->method->name);
69569587Sgreen		buffer_put_char(&b, have_sig);
69660573Skris		buffer_put_cstring(&b, KEX_DSS);
69760573Skris		buffer_put_string(&b, blob, bloblen);
69860573Skris	}
69960573Skris	xfree(blob);
70060573Skris	/* append signature */
70160573Skris	buffer_put_string(&b, signature, slen);
70260573Skris	xfree(signature);
70360573Skris
70460573Skris	/* skip session id and packet type */
70565668Skris	if (buffer_len(&b) < skip + 1)
70669587Sgreen		fatal("userauth_pubkey: internal error");
70765668Skris	buffer_consume(&b, skip + 1);
70860573Skris
70960573Skris	/* put remaining data from buffer into packet */
71060573Skris	packet_start(SSH2_MSG_USERAUTH_REQUEST);
71160573Skris	packet_put_raw(buffer_ptr(&b), buffer_len(&b));
71260573Skris	buffer_free(&b);
71360573Skris
71460573Skris	/* send */
71560573Skris	packet_send();
71660573Skris	packet_write_wait();
71765668Skris
71860573Skris	return 1;
71960573Skris}
72060573Skris
72169587Sgreen/* sign callback */
72269587Sgreenint dsa_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp,
72369587Sgreen    unsigned char *data, int datalen)
72469587Sgreen{
72569587Sgreen	return dsa_sign(key, sigp, lenp, data, datalen);
72669587Sgreen}
72769587Sgreen
72865668Skrisint
72969587Sgreenuserauth_pubkey_identity(Authctxt *authctxt, char *filename)
73065668Skris{
73165668Skris	Key *k;
73269587Sgreen	int i, ret, try_next;
73365668Skris	struct stat st;
73465668Skris
73565668Skris	if (stat(filename, &st) != 0) {
73665668Skris		debug("key does not exist: %s", filename);
73765668Skris		return 0;
73865668Skris	}
73965668Skris	debug("try pubkey: %s", filename);
74065668Skris
74165668Skris	k = key_new(KEY_DSA);
74265668Skris	if (!load_private_key(filename, "", k, NULL)) {
74365668Skris		int success = 0;
74465668Skris		char *passphrase;
74565668Skris		char prompt[300];
74665668Skris		snprintf(prompt, sizeof prompt,
74769587Sgreen		     "Enter passphrase for %s key '%.100s': ",
74869587Sgreen		     key_type(k), filename);
74969587Sgreen		for (i = 0; i < options.number_of_password_prompts; i++) {
75069587Sgreen			passphrase = read_passphrase(prompt, 0);
75169587Sgreen			if (strcmp(passphrase, "") != 0) {
75269587Sgreen				success = load_private_key(filename, passphrase, k, NULL);
75369587Sgreen				try_next = 0;
75469587Sgreen			} else {
75569587Sgreen				debug2("no passphrase given, try next key");
75669587Sgreen				try_next = 1;
75769587Sgreen			}
75869587Sgreen			memset(passphrase, 0, strlen(passphrase));
75969587Sgreen			xfree(passphrase);
76069587Sgreen			if (success || try_next)
76169587Sgreen				break;
76269587Sgreen			debug2("bad passphrase given, try again...");
76369587Sgreen		}
76465668Skris		if (!success) {
76565668Skris			key_free(k);
76665668Skris			return 0;
76765668Skris		}
76865668Skris	}
76969587Sgreen	ret = sign_and_send_pubkey(authctxt, k, dsa_sign_cb);
77065668Skris	key_free(k);
77165668Skris	return ret;
77265668Skris}
77365668Skris
77469587Sgreen/* sign callback */
77569587Sgreenint agent_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp,
77665668Skris    unsigned char *data, int datalen)
77765668Skris{
77869587Sgreen	return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen);
77965668Skris}
78065668Skris
78165668Skrisint
78269587Sgreenuserauth_pubkey_agent(Authctxt *authctxt)
78365668Skris{
78465668Skris	static int called = 0;
78565668Skris	char *comment;
78665668Skris	Key *k;
78765668Skris	int ret;
78865668Skris
78965668Skris	if (called == 0) {
79069587Sgreen		k = ssh_get_first_identity(authctxt->agent, &comment, 2);
79169587Sgreen		called = 1;
79265668Skris	} else {
79369587Sgreen		k = ssh_get_next_identity(authctxt->agent, &comment, 2);
79465668Skris	}
79569587Sgreen	if (k == NULL) {
79669587Sgreen		debug2("no more DSA keys from agent");
79765668Skris		return 0;
79869587Sgreen	}
79965668Skris	debug("trying DSA agent key %s", comment);
80065668Skris	xfree(comment);
80169587Sgreen	ret = sign_and_send_pubkey(authctxt, k, agent_sign_cb);
80265668Skris	key_free(k);
80365668Skris	return ret;
80465668Skris}
80565668Skris
80669587Sgreenint
80769587Sgreenuserauth_pubkey(Authctxt *authctxt)
80869587Sgreen{
80969587Sgreen	static int idx = 0;
81069587Sgreen	int sent = 0;
81169587Sgreen
81269587Sgreen	if (authctxt->agent != NULL)
81369587Sgreen		sent = userauth_pubkey_agent(authctxt);
81469587Sgreen	while (sent == 0 && idx < options.num_identity_files2)
81569587Sgreen		sent = userauth_pubkey_identity(authctxt, options.identity_files2[idx++]);
81669587Sgreen	return sent;
81769587Sgreen}
81869587Sgreen
81969587Sgreen/*
82069587Sgreen * Send userauth request message specifying keyboard-interactive method.
82169587Sgreen */
82269587Sgreenint
82369587Sgreenuserauth_kbdint(Authctxt *authctxt)
82469587Sgreen{
82569587Sgreen	static int attempt = 0;
82669587Sgreen
82769587Sgreen	if (attempt++ >= options.number_of_password_prompts)
82869587Sgreen		return 0;
82969587Sgreen
83069587Sgreen	debug2("userauth_kbdint");
83169587Sgreen	packet_start(SSH2_MSG_USERAUTH_REQUEST);
83269587Sgreen	packet_put_cstring(authctxt->server_user);
83369587Sgreen	packet_put_cstring(authctxt->service);
83469587Sgreen	packet_put_cstring(authctxt->method->name);
83569587Sgreen	packet_put_cstring("");					/* lang */
83669587Sgreen	packet_put_cstring(options.kbd_interactive_devices ?
83769587Sgreen	    options.kbd_interactive_devices : "");
83869587Sgreen	packet_send();
83969587Sgreen	packet_write_wait();
84069587Sgreen
84169587Sgreen	dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
84269587Sgreen	return 1;
84369587Sgreen}
84469587Sgreen
84569587Sgreen/*
84669587Sgreen * parse SSH2_MSG_USERAUTH_INFO_REQUEST, prompt user and send
84769587Sgreen * SSH2_MSG_USERAUTH_INFO_RESPONSE
84869587Sgreen */
84960573Skrisvoid
85069587Sgreeninput_userauth_info_req(int type, int plen, void *ctxt)
85160573Skris{
85269587Sgreen	Authctxt *authctxt = ctxt;
85369587Sgreen	char *name = NULL;
85469587Sgreen	char *inst = NULL;
85569587Sgreen	char *lang = NULL;
85669587Sgreen	char *prompt = NULL;
85769587Sgreen	char *response = NULL;
85869587Sgreen	unsigned int num_prompts, i;
85969587Sgreen	int echo = 0;
86060573Skris
86169587Sgreen	debug2("input_userauth_info_req");
86269587Sgreen
86369587Sgreen	if (authctxt == NULL)
86469587Sgreen		fatal("input_userauth_info_req: no authentication context");
86569587Sgreen
86669587Sgreen	name = packet_get_string(NULL);
86769587Sgreen	inst = packet_get_string(NULL);
86869587Sgreen	lang = packet_get_string(NULL);
86969587Sgreen
87069587Sgreen	if (strlen(name) > 0)
87169587Sgreen		cli_mesg(name);
87269587Sgreen	xfree(name);
87369587Sgreen
87469587Sgreen	if (strlen(inst) > 0)
87569587Sgreen		cli_mesg(inst);
87669587Sgreen	xfree(inst);
87769587Sgreen	xfree(lang); 				/* unused */
87869587Sgreen
87969587Sgreen	num_prompts = packet_get_int();
88069587Sgreen	/*
88169587Sgreen	 * Begin to build info response packet based on prompts requested.
88269587Sgreen	 * We commit to providing the correct number of responses, so if
88369587Sgreen	 * further on we run into a problem that prevents this, we have to
88469587Sgreen	 * be sure and clean this up and send a correct error response.
88569587Sgreen	 */
88669587Sgreen	packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE);
88769587Sgreen	packet_put_int(num_prompts);
88869587Sgreen
88969587Sgreen	for (i = 0; i < num_prompts; i++) {
89069587Sgreen		prompt = packet_get_string(NULL);
89169587Sgreen		echo = packet_get_char();
89269587Sgreen
89369587Sgreen		response = cli_prompt(prompt, echo);
89469587Sgreen
89569587Sgreen		packet_put_cstring(response);
89669587Sgreen		memset(response, 0, strlen(response));
89769587Sgreen		xfree(response);
89869587Sgreen		xfree(prompt);
89969587Sgreen	}
90069587Sgreen	packet_done(); /* done with parsing incoming message. */
90169587Sgreen
90260573Skris	packet_send();
90360573Skris	packet_write_wait();
90469587Sgreen}
90560573Skris
90669587Sgreen/* find auth method */
90769587Sgreen
90869587Sgreen#define	DELIM	","
90969587Sgreen
91069587Sgreenstatic char *def_authlist = "publickey,password";
91169587Sgreenstatic char *authlist_current = NULL;	 /* clean copy used for comparison */
91269587Sgreenstatic char *authname_current = NULL;	 /* last used auth method */
91369587Sgreenstatic char *authlist_working = NULL;	 /* copy that gets modified by strtok_r() */
91469587Sgreenstatic char *authlist_state = NULL;	 /* state variable for strtok_r() */
91569587Sgreen
91669587Sgreen/*
91769587Sgreen * Before starting to use a new authentication method list sent by the
91869587Sgreen * server, reset internal variables.  This should also be called when
91969587Sgreen * finished processing server list to free resources.
92069587Sgreen */
92169587Sgreenvoid
92269587Sgreenauthmethod_clear()
92369587Sgreen{
92469587Sgreen	if (authlist_current != NULL) {
92569587Sgreen		xfree(authlist_current);
92669587Sgreen		authlist_current = NULL;
92760573Skris	}
92869587Sgreen	if (authlist_working != NULL) {
92969587Sgreen		xfree(authlist_working);
93069587Sgreen		authlist_working = NULL;
93169587Sgreen	}
93269587Sgreen	if (authname_current != NULL) {
93369587Sgreen		xfree(authname_current);
93469587Sgreen		authlist_state = NULL;
93569587Sgreen	}
93669587Sgreen	if (authlist_state != NULL)
93769587Sgreen		authlist_state = NULL;
93869587Sgreen	return;
93969587Sgreen}
94069587Sgreen
94169587Sgreen/*
94269587Sgreen * given auth method name, if configurable options permit this method fill
94369587Sgreen * in auth_ident field and return true, otherwise return false.
94469587Sgreen */
94569587Sgreenint
94669587Sgreenauthmethod_is_enabled(Authmethod *method)
94769587Sgreen{
94869587Sgreen	if (method == NULL)
94969587Sgreen		return 0;
95069587Sgreen	/* return false if options indicate this method is disabled */
95169587Sgreen	if  (method->enabled == NULL || *method->enabled == 0)
95269587Sgreen		return 0;
95369587Sgreen	/* return false if batch mode is enabled but method needs interactive mode */
95469587Sgreen	if  (method->batch_flag != NULL && *method->batch_flag != 0)
95569587Sgreen		return 0;
95669587Sgreen	return 1;
95769587Sgreen}
95869587Sgreen
95969587SgreenAuthmethod *
96069587Sgreenauthmethod_lookup(const char *name)
96169587Sgreen{
96269587Sgreen	Authmethod *method = NULL;
96369587Sgreen	if (name != NULL)
96469587Sgreen		for (method = authmethods; method->name != NULL; method++)
96569587Sgreen			if (strcmp(name, method->name) == 0)
96669587Sgreen				return method;
96769587Sgreen	debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
96869587Sgreen	return NULL;
96969587Sgreen}
97069587Sgreen
97169587Sgreen/*
97269587Sgreen * Given the authentication method list sent by the server, return the
97369587Sgreen * next method we should try.  If the server initially sends a nil list,
97469587Sgreen * use a built-in default list.  If the server sends a nil list after
97569587Sgreen * previously sending a valid list, continue using the list originally
97669587Sgreen * sent.
97769587Sgreen */
97869587Sgreen
97969587SgreenAuthmethod *
98069587Sgreenauthmethod_get(char *authlist)
98169587Sgreen{
98269587Sgreen	char *name = NULL, *authname_old;
98369587Sgreen	Authmethod *method = NULL;
98469587Sgreen
98569587Sgreen	/* Use a suitable default if we're passed a nil list.  */
98669587Sgreen	if (authlist == NULL || strlen(authlist) == 0)
98769587Sgreen		authlist = def_authlist;
98869587Sgreen
98969587Sgreen	if (authlist_current == NULL || strcmp(authlist, authlist_current) != 0) {
99069587Sgreen		/* start over if passed a different list */
99169587Sgreen		debug3("start over, passed a different list");
99269587Sgreen		authmethod_clear();
99369587Sgreen		authlist_current = xstrdup(authlist);
99469587Sgreen		authlist_working = xstrdup(authlist);
99569587Sgreen		name = strtok_r(authlist_working, DELIM, &authlist_state);
99660573Skris	} else {
99769587Sgreen		/*
99869587Sgreen		 * try to use previously used authentication method
99969587Sgreen		 * or continue to use previously passed list
100069587Sgreen		 */
100169587Sgreen		name = (authname_current != NULL) ?
100269587Sgreen		    authname_current : strtok_r(NULL, DELIM, &authlist_state);
100360573Skris	}
100460573Skris
100569587Sgreen	while (name != NULL) {
100669587Sgreen		debug3("authmethod_lookup %s", name);
100769587Sgreen		method = authmethod_lookup(name);
100869587Sgreen		if (method != NULL && authmethod_is_enabled(method)) {
100969587Sgreen			debug3("authmethod_is_enabled %s", name);
101060573Skris			break;
101160573Skris		}
101269587Sgreen		name = strtok_r(NULL, DELIM, &authlist_state);
101369587Sgreen		method = NULL;
101460573Skris	}
101569587Sgreen
101669587Sgreen	authname_old = authname_current;
101769587Sgreen	if (method != NULL) {
101869587Sgreen		debug("next auth method to try is %s", name);
101969587Sgreen		authname_current = xstrdup(name);
102069587Sgreen	} else {
102169587Sgreen		debug("no more auth methods to try");
102269587Sgreen		authname_current = NULL;
102369587Sgreen	}
102469587Sgreen
102569587Sgreen	if (authname_old != NULL)
102669587Sgreen		xfree(authname_old);
102769587Sgreen
102869587Sgreen	return (method);
102960573Skris}
1030