1296853Sdes/* $OpenBSD: sshconnect2.c,v 1.239 2016/02/23 01:34:14 djm Exp $ */
260573Skris/*
360573Skris * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4192595Sdes * Copyright (c) 2008 Damien Miller.  All rights reserved.
560573Skris *
660573Skris * Redistribution and use in source and binary forms, with or without
760573Skris * modification, are permitted provided that the following conditions
860573Skris * are met:
960573Skris * 1. Redistributions of source code must retain the above copyright
1060573Skris *    notice, this list of conditions and the following disclaimer.
1160573Skris * 2. Redistributions in binary form must reproduce the above copyright
1260573Skris *    notice, this list of conditions and the following disclaimer in the
1360573Skris *    documentation and/or other materials provided with the distribution.
1460573Skris *
1560573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1660573Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1760573Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1860573Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1960573Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2060573Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2160573Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2260573Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2360573Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2460573Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2560573Skris */
2660573Skris
2760573Skris#include "includes.h"
2860573Skris
29162856Sdes#include <sys/types.h>
30162856Sdes#include <sys/socket.h>
31162856Sdes#include <sys/wait.h>
32162856Sdes#include <sys/stat.h>
33162856Sdes
34162856Sdes#include <errno.h>
35204917Sdes#include <fcntl.h>
36181111Sdes#include <netdb.h>
37162856Sdes#include <pwd.h>
38162856Sdes#include <signal.h>
39162856Sdes#include <stdarg.h>
40162856Sdes#include <stdio.h>
41162856Sdes#include <string.h>
42162856Sdes#include <unistd.h>
43248619Sdes#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
44181111Sdes#include <vis.h>
45181111Sdes#endif
46162856Sdes
47124211Sdes#include "openbsd-compat/sys-queue.h"
48124211Sdes
49162856Sdes#include "xmalloc.h"
5060573Skris#include "ssh.h"
5176262Sgreen#include "ssh2.h"
5260573Skris#include "buffer.h"
5360573Skris#include "packet.h"
5460573Skris#include "compat.h"
5576262Sgreen#include "cipher.h"
56162856Sdes#include "key.h"
5760573Skris#include "kex.h"
5860573Skris#include "myproposal.h"
5960573Skris#include "sshconnect.h"
6060573Skris#include "authfile.h"
6176262Sgreen#include "dh.h"
6276262Sgreen#include "authfd.h"
6376262Sgreen#include "log.h"
64295367Sdes#include "misc.h"
6576262Sgreen#include "readconf.h"
6676262Sgreen#include "match.h"
6769587Sgreen#include "dispatch.h"
6876262Sgreen#include "canohost.h"
6998684Sdes#include "msg.h"
7098684Sdes#include "pathnames.h"
71162856Sdes#include "uidswap.h"
72221420Sdes#include "hostfile.h"
73295367Sdes#include "ssherr.h"
7460573Skris
75124211Sdes#ifdef GSSAPI
76124211Sdes#include "ssh-gss.h"
77124211Sdes#endif
78124211Sdes
7960573Skris/* import */
8060573Skrisextern char *client_version_string;
8160573Skrisextern char *server_version_string;
8260573Skrisextern Options options;
8360573Skris
8460573Skris/*
8560573Skris * SSH2 key exchange
8660573Skris */
8760573Skris
8876262Sgreenu_char *session_id2 = NULL;
89124211Sdesu_int session_id2_len = 0;
9060573Skris
9176262Sgreenchar *xxx_host;
9276262Sgreenstruct sockaddr *xxx_hostaddr;
9376262Sgreen
9492559Sdesstatic int
95295367Sdesverify_host_key_callback(Key *hostkey, struct ssh *ssh)
9676262Sgreen{
9792559Sdes	if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) == -1)
9892559Sdes		fatal("Host key verification failed.");
9976262Sgreen	return 0;
10076262Sgreen}
10176262Sgreen
102221420Sdesstatic char *
103221420Sdesorder_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port)
104221420Sdes{
105221420Sdes	char *oavail, *avail, *first, *last, *alg, *hostname, *ret;
106221420Sdes	size_t maxlen;
107221420Sdes	struct hostkeys *hostkeys;
108221420Sdes	int ktype;
109226046Sdes	u_int i;
110221420Sdes
111221420Sdes	/* Find all hostkeys for this hostname */
112221420Sdes	get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL);
113221420Sdes	hostkeys = init_hostkeys();
114226046Sdes	for (i = 0; i < options.num_user_hostfiles; i++)
115226046Sdes		load_hostkeys(hostkeys, hostname, options.user_hostfiles[i]);
116226046Sdes	for (i = 0; i < options.num_system_hostfiles; i++)
117226046Sdes		load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]);
118221420Sdes
119221420Sdes	oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG);
120221420Sdes	maxlen = strlen(avail) + 1;
121221420Sdes	first = xmalloc(maxlen);
122221420Sdes	last = xmalloc(maxlen);
123221420Sdes	*first = *last = '\0';
124221420Sdes
125221420Sdes#define ALG_APPEND(to, from) \
126221420Sdes	do { \
127221420Sdes		if (*to != '\0') \
128221420Sdes			strlcat(to, ",", maxlen); \
129221420Sdes		strlcat(to, from, maxlen); \
130221420Sdes	} while (0)
131221420Sdes
132221420Sdes	while ((alg = strsep(&avail, ",")) && *alg != '\0') {
133295367Sdes		if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC)
134221420Sdes			fatal("%s: unknown alg %s", __func__, alg);
135221420Sdes		if (lookup_key_in_hostkeys_by_type(hostkeys,
136295367Sdes		    sshkey_type_plain(ktype), NULL))
137221420Sdes			ALG_APPEND(first, alg);
138221420Sdes		else
139221420Sdes			ALG_APPEND(last, alg);
140221420Sdes	}
141221420Sdes#undef ALG_APPEND
142295367Sdes	xasprintf(&ret, "%s%s%s", first,
143295367Sdes	    (*first == '\0' || *last == '\0') ? "" : ",", last);
144221420Sdes	if (*first != '\0')
145221420Sdes		debug3("%s: prefer hostkeyalgs: %s", __func__, first);
146221420Sdes
147255767Sdes	free(first);
148255767Sdes	free(last);
149255767Sdes	free(hostname);
150255767Sdes	free(oavail);
151221420Sdes	free_hostkeys(hostkeys);
152221420Sdes
153221420Sdes	return ret;
154221420Sdes}
155221420Sdes
15660573Skrisvoid
157221420Sdesssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
15860573Skris{
159295367Sdes	char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
160296853Sdes	char *s;
161295367Sdes	struct kex *kex;
162295367Sdes	int r;
16369587Sgreen
16476262Sgreen	xxx_host = host;
16576262Sgreen	xxx_hostaddr = hostaddr;
16676262Sgreen
167296853Sdes	if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL)
168296853Sdes		fatal("%s: kex_names_cat", __func__);
169296853Sdes	myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s);
17076262Sgreen	myproposal[PROPOSAL_ENC_ALGS_CTOS] =
171295367Sdes	    compat_cipher_proposal(options.ciphers);
17276262Sgreen	myproposal[PROPOSAL_ENC_ALGS_STOC] =
173295367Sdes	    compat_cipher_proposal(options.ciphers);
17469587Sgreen	if (options.compression) {
17576262Sgreen		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
176149753Sdes		myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib@openssh.com,zlib,none";
17769587Sgreen	} else {
17876262Sgreen		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
179149753Sdes		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com,zlib";
18069587Sgreen	}
181295367Sdes	myproposal[PROPOSAL_MAC_ALGS_CTOS] =
182295367Sdes	    myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
183295367Sdes	if (options.hostkeyalgorithms != NULL) {
184295367Sdes		if (kex_assemble_names(KEX_DEFAULT_PK_ALG,
185295367Sdes		    &options.hostkeyalgorithms) != 0)
186295367Sdes			fatal("%s: kex_assemble_namelist", __func__);
18792559Sdes		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
188262566Sdes		    compat_pkalg_proposal(options.hostkeyalgorithms);
189295367Sdes	} else {
190295367Sdes		/* Enforce default */
191295367Sdes		options.hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG);
192221420Sdes		/* Prefer algorithms that we already have keys for */
193221420Sdes		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
194262566Sdes		    compat_pkalg_proposal(
195262566Sdes		    order_hostkeyalgs(host, hostaddr, port));
196221420Sdes	}
19769587Sgreen
198255767Sdes	if (options.rekey_limit || options.rekey_interval)
199255767Sdes		packet_set_rekey_limits((u_int32_t)options.rekey_limit,
200255767Sdes		    (time_t)options.rekey_interval);
201124211Sdes
20276262Sgreen	/* start key exchange */
203295367Sdes	if ((r = kex_setup(active_state, myproposal)) != 0)
204295367Sdes		fatal("kex_setup: %s", ssh_err(r));
205295367Sdes	kex = active_state->kex;
206295367Sdes#ifdef WITH_OPENSSL
207113911Sdes	kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
208137019Sdes	kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
209113911Sdes	kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
210162856Sdes	kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
211295367Sdes# ifdef OPENSSL_HAS_ECC
212221420Sdes	kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
213295367Sdes# endif
214295367Sdes#endif
215262566Sdes	kex->kex[KEX_C25519_SHA256] = kexc25519_client;
21676262Sgreen	kex->client_version_string=client_version_string;
21776262Sgreen	kex->server_version_string=server_version_string;
21892559Sdes	kex->verify_host_key=&verify_host_key_callback;
21969587Sgreen
220295367Sdes	dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
22169587Sgreen
222296853Sdes	/* remove ext-info from the KEX proposals for rekeying */
223296853Sdes	myproposal[PROPOSAL_KEX_ALGS] =
224296853Sdes	    compat_kex_proposal(options.kex_algorithms);
225296853Sdes	if ((r = kex_prop2buf(kex->my, myproposal)) != 0)
226296853Sdes		fatal("kex_prop2buf: %s", ssh_err(r));
227204917Sdes
22876262Sgreen	session_id2 = kex->session_id;
22976262Sgreen	session_id2_len = kex->session_id_len;
23069587Sgreen
23169587Sgreen#ifdef DEBUG_KEXDH
23269587Sgreen	/* send 1st encrypted/maced/compressed message */
23369587Sgreen	packet_start(SSH2_MSG_IGNORE);
23469587Sgreen	packet_put_cstring("markus");
23569587Sgreen	packet_send();
23669587Sgreen	packet_write_wait();
23769587Sgreen#endif
23869587Sgreen}
23969587Sgreen
24069587Sgreen/*
24160573Skris * Authenticate user
24260573Skris */
24369587Sgreen
244295367Sdestypedef struct cauthctxt Authctxt;
245295367Sdestypedef struct cauthmethod Authmethod;
246124211Sdestypedef struct identity Identity;
247124211Sdestypedef struct idlist Idlist;
24869587Sgreen
249124211Sdesstruct identity {
250124211Sdes	TAILQ_ENTRY(identity) next;
251295367Sdes	int	agent_fd;		/* >=0 if agent supports key */
252295367Sdes	struct sshkey	*key;		/* public/private key */
253124211Sdes	char	*filename;		/* comment for agent-only keys */
254124211Sdes	int	tried;
255124211Sdes	int	isprivate;		/* key points to the private key */
256248619Sdes	int	userprovided;
257124211Sdes};
258124211SdesTAILQ_HEAD(idlist, identity);
25969587Sgreen
260295367Sdesstruct cauthctxt {
26169587Sgreen	const char *server_user;
26276262Sgreen	const char *local_user;
26369587Sgreen	const char *host;
26469587Sgreen	const char *service;
265295367Sdes	struct cauthmethod *method;
266215116Sdes	sig_atomic_t success;
26776262Sgreen	char *authlist;
268295367Sdes	int attempt;
26976262Sgreen	/* pubkey */
270295367Sdes	struct idlist keys;
271295367Sdes	int agent_fd;
27276262Sgreen	/* hostbased */
27398684Sdes	Sensitive *sensitive;
274295367Sdes	char *oktypes, *ktypes;
275295367Sdes	const char *active_ktype;
27692559Sdes	/* kbd-interactive */
27792559Sdes	int info_req_seen;
278124211Sdes	/* generic */
279124211Sdes	void *methoddata;
28069587Sgreen};
281295367Sdes
282295367Sdesstruct cauthmethod {
28369587Sgreen	char	*name;		/* string to compare against server's list */
28469587Sgreen	int	(*userauth)(Authctxt *authctxt);
285192595Sdes	void	(*cleanup)(Authctxt *authctxt);
28669587Sgreen	int	*enabled;	/* flag in option struct that enables method */
28769587Sgreen	int	*batch_flag;	/* flag in option struct that disables method */
28869587Sgreen};
28969587Sgreen
290296853Sdesint	input_userauth_service_accept(int, u_int32_t, void *);
291296853Sdesint	input_userauth_ext_info(int, u_int32_t, void *);
292295367Sdesint	input_userauth_success(int, u_int32_t, void *);
293295367Sdesint	input_userauth_success_unexpected(int, u_int32_t, void *);
294295367Sdesint	input_userauth_failure(int, u_int32_t, void *);
295295367Sdesint	input_userauth_banner(int, u_int32_t, void *);
296295367Sdesint	input_userauth_error(int, u_int32_t, void *);
297295367Sdesint	input_userauth_info_req(int, u_int32_t, void *);
298295367Sdesint	input_userauth_pk_ok(int, u_int32_t, void *);
299295367Sdesint	input_userauth_passwd_changereq(int, u_int32_t, void *);
30069587Sgreen
30192559Sdesint	userauth_none(Authctxt *);
30292559Sdesint	userauth_pubkey(Authctxt *);
30392559Sdesint	userauth_passwd(Authctxt *);
30492559Sdesint	userauth_kbdint(Authctxt *);
30592559Sdesint	userauth_hostbased(Authctxt *);
30669587Sgreen
307124211Sdes#ifdef GSSAPI
308124211Sdesint	userauth_gssapi(Authctxt *authctxt);
309295367Sdesint	input_gssapi_response(int type, u_int32_t, void *);
310295367Sdesint	input_gssapi_token(int type, u_int32_t, void *);
311295367Sdesint	input_gssapi_hash(int type, u_int32_t, void *);
312295367Sdesint	input_gssapi_error(int, u_int32_t, void *);
313295367Sdesint	input_gssapi_errtok(int, u_int32_t, void *);
314124211Sdes#endif
315124211Sdes
31692559Sdesvoid	userauth(Authctxt *, char *);
31776262Sgreen
318124211Sdesstatic int sign_and_send_pubkey(Authctxt *, Identity *);
319124211Sdesstatic void pubkey_prepare(Authctxt *);
320124211Sdesstatic void pubkey_cleanup(Authctxt *);
321296853Sdesstatic Key *load_identity_file(Identity *);
32276262Sgreen
32392559Sdesstatic Authmethod *authmethod_get(char *authlist);
32492559Sdesstatic Authmethod *authmethod_lookup(const char *name);
32592559Sdesstatic char *authmethods_get(void);
32669587Sgreen
32769587SgreenAuthmethod authmethods[] = {
328124211Sdes#ifdef GSSAPI
329126277Sdes	{"gssapi-with-mic",
330124211Sdes		userauth_gssapi,
331192595Sdes		NULL,
332124211Sdes		&options.gss_authentication,
333124211Sdes		NULL},
334124211Sdes#endif
33592559Sdes	{"hostbased",
33692559Sdes		userauth_hostbased,
337192595Sdes		NULL,
33892559Sdes		&options.hostbased_authentication,
33992559Sdes		NULL},
34069587Sgreen	{"publickey",
34169587Sgreen		userauth_pubkey,
342192595Sdes		NULL,
34376262Sgreen		&options.pubkey_authentication,
34469587Sgreen		NULL},
34592559Sdes	{"keyboard-interactive",
34692559Sdes		userauth_kbdint,
347192595Sdes		NULL,
34892559Sdes		&options.kbd_interactive_authentication,
34992559Sdes		&options.batch_mode},
35069587Sgreen	{"password",
35169587Sgreen		userauth_passwd,
352192595Sdes		NULL,
35369587Sgreen		&options.password_authentication,
35469587Sgreen		&options.batch_mode},
35569587Sgreen	{"none",
35669587Sgreen		userauth_none,
35769587Sgreen		NULL,
358192595Sdes		NULL,
35969587Sgreen		NULL},
360192595Sdes	{NULL, NULL, NULL, NULL, NULL}
36169587Sgreen};
36269587Sgreen
36369587Sgreenvoid
36476262Sgreenssh_userauth2(const char *local_user, const char *server_user, char *host,
36598684Sdes    Sensitive *sensitive)
36669587Sgreen{
367296853Sdes	struct ssh *ssh = active_state;
36869587Sgreen	Authctxt authctxt;
369296853Sdes	int r;
37069587Sgreen
37192559Sdes	if (options.challenge_response_authentication)
37276262Sgreen		options.kbd_interactive_authentication = 1;
37376262Sgreen	if (options.preferred_authentications == NULL)
37476262Sgreen		options.preferred_authentications = authmethods_get();
37576262Sgreen
37669587Sgreen	/* setup authentication context */
37792559Sdes	memset(&authctxt, 0, sizeof(authctxt));
378124211Sdes	pubkey_prepare(&authctxt);
37969587Sgreen	authctxt.server_user = server_user;
38076262Sgreen	authctxt.local_user = local_user;
38169587Sgreen	authctxt.host = host;
38269587Sgreen	authctxt.service = "ssh-connection";		/* service name */
38369587Sgreen	authctxt.success = 0;
38469587Sgreen	authctxt.method = authmethod_lookup("none");
38576262Sgreen	authctxt.authlist = NULL;
386124211Sdes	authctxt.methoddata = NULL;
38798684Sdes	authctxt.sensitive = sensitive;
388295367Sdes	authctxt.active_ktype = authctxt.oktypes = authctxt.ktypes = NULL;
38992559Sdes	authctxt.info_req_seen = 0;
390295367Sdes	authctxt.agent_fd = -1;
39169587Sgreen	if (authctxt.method == NULL)
39269587Sgreen		fatal("ssh_userauth2: internal error: cannot send userauth none request");
39369587Sgreen
394296853Sdes	if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 ||
395296853Sdes	    (r = sshpkt_put_cstring(ssh, "ssh-userauth")) != 0 ||
396296853Sdes	    (r = sshpkt_send(ssh)) != 0)
397296853Sdes		fatal("%s: %s", __func__, ssh_err(r));
39869587Sgreen
399296853Sdes	ssh_dispatch_init(ssh, &input_userauth_error);
400296853Sdes	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info);
401296853Sdes	ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept);
402296853Sdes	ssh_dispatch_run(ssh, DISPATCH_BLOCK, &authctxt.success, &authctxt);	/* loop until success */
40369587Sgreen
404124211Sdes	pubkey_cleanup(&authctxt);
405296853Sdes	ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL);
40669587Sgreen
407113911Sdes	debug("Authentication succeeded (%s).", authctxt.method->name);
40869587Sgreen}
409124211Sdes
410296853Sdes/* ARGSUSED */
411296853Sdesint
412296853Sdesinput_userauth_service_accept(int type, u_int32_t seqnr, void *ctxt)
413296853Sdes{
414296853Sdes	Authctxt *authctxt = ctxt;
415296853Sdes	struct ssh *ssh = active_state;
416296853Sdes	int r;
417296853Sdes
418296853Sdes	if (ssh_packet_remaining(ssh) > 0) {
419296853Sdes		char *reply;
420296853Sdes
421296853Sdes		if ((r = sshpkt_get_cstring(ssh, &reply, NULL)) != 0)
422296853Sdes			goto out;
423296853Sdes		debug2("service_accept: %s", reply);
424296853Sdes		free(reply);
425296853Sdes	} else {
426296853Sdes		debug2("buggy server: service_accept w/o service");
427296853Sdes	}
428296853Sdes	if ((r = sshpkt_get_end(ssh)) != 0)
429296853Sdes		goto out;
430296853Sdes	debug("SSH2_MSG_SERVICE_ACCEPT received");
431296853Sdes
432296853Sdes	/* initial userauth request */
433296853Sdes	userauth_none(authctxt);
434296853Sdes
435296853Sdes	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_error);
436296853Sdes	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
437296853Sdes	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
438296853Sdes	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner);
439296853Sdes	r = 0;
440296853Sdes out:
441296853Sdes	return r;
442296853Sdes}
443296853Sdes
444296853Sdes/* ARGSUSED */
445296853Sdesint
446296853Sdesinput_userauth_ext_info(int type, u_int32_t seqnr, void *ctxt)
447296853Sdes{
448296853Sdes	return kex_input_ext_info(type, seqnr, active_state);
449296853Sdes}
450296853Sdes
45169587Sgreenvoid
45276262Sgreenuserauth(Authctxt *authctxt, char *authlist)
45376262Sgreen{
454192595Sdes	if (authctxt->method != NULL && authctxt->method->cleanup != NULL)
455192595Sdes		authctxt->method->cleanup(authctxt);
456192595Sdes
457255767Sdes	free(authctxt->methoddata);
458255767Sdes	authctxt->methoddata = NULL;
45976262Sgreen	if (authlist == NULL) {
46076262Sgreen		authlist = authctxt->authlist;
46176262Sgreen	} else {
462255767Sdes		free(authctxt->authlist);
46376262Sgreen		authctxt->authlist = authlist;
46476262Sgreen	}
46576262Sgreen	for (;;) {
46676262Sgreen		Authmethod *method = authmethod_get(authlist);
46776262Sgreen		if (method == NULL)
46876262Sgreen			fatal("Permission denied (%s).", authlist);
46976262Sgreen		authctxt->method = method;
470124211Sdes
471124211Sdes		/* reset the per method handler */
472124211Sdes		dispatch_range(SSH2_MSG_USERAUTH_PER_METHOD_MIN,
473124211Sdes		    SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL);
474124211Sdes
475124211Sdes		/* and try new method */
47676262Sgreen		if (method->userauth(authctxt) != 0) {
47776262Sgreen			debug2("we sent a %s packet, wait for reply", method->name);
47876262Sgreen			break;
47976262Sgreen		} else {
48076262Sgreen			debug2("we did not send a packet, disable method");
48176262Sgreen			method->enabled = NULL;
48276262Sgreen		}
48376262Sgreen	}
48476262Sgreen}
48599063Sdes
486192595Sdes/* ARGSUSED */
487295367Sdesint
48892559Sdesinput_userauth_error(int type, u_int32_t seq, void *ctxt)
48969587Sgreen{
49076262Sgreen	fatal("input_userauth_error: bad message during authentication: "
491149753Sdes	    "type %d", type);
492295367Sdes	return 0;
49369587Sgreen}
49499063Sdes
495192595Sdes/* ARGSUSED */
496295367Sdesint
49792559Sdesinput_userauth_banner(int type, u_int32_t seq, void *ctxt)
49876262Sgreen{
499181111Sdes	char *msg, *raw, *lang;
500181111Sdes	u_int len;
501126277Sdes
50276262Sgreen	debug3("input_userauth_banner");
503181111Sdes	raw = packet_get_string(&len);
50476262Sgreen	lang = packet_get_string(NULL);
505192595Sdes	if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO) {
506181111Sdes		if (len > 65536)
507181111Sdes			len = 65536;
508183336Sdes		msg = xmalloc(len * 4 + 1); /* max expansion from strnvis() */
509204917Sdes		strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL|VIS_NOSLASH);
510126277Sdes		fprintf(stderr, "%s", msg);
511255767Sdes		free(msg);
512181111Sdes	}
513255767Sdes	free(raw);
514255767Sdes	free(lang);
515295367Sdes	return 0;
51676262Sgreen}
51799063Sdes
518192595Sdes/* ARGSUSED */
519295367Sdesint
52092559Sdesinput_userauth_success(int type, u_int32_t seq, void *ctxt)
52169587Sgreen{
52269587Sgreen	Authctxt *authctxt = ctxt;
523204917Sdes
52469587Sgreen	if (authctxt == NULL)
52569587Sgreen		fatal("input_userauth_success: no authentication context");
526255767Sdes	free(authctxt->authlist);
527255767Sdes	authctxt->authlist = NULL;
528204917Sdes	if (authctxt->method != NULL && authctxt->method->cleanup != NULL)
529204917Sdes		authctxt->method->cleanup(authctxt);
530255767Sdes	free(authctxt->methoddata);
531255767Sdes	authctxt->methoddata = NULL;
53269587Sgreen	authctxt->success = 1;			/* break out */
533295367Sdes	return 0;
53469587Sgreen}
53599063Sdes
536295367Sdesint
537204917Sdesinput_userauth_success_unexpected(int type, u_int32_t seq, void *ctxt)
538204917Sdes{
539204917Sdes	Authctxt *authctxt = ctxt;
540204917Sdes
541204917Sdes	if (authctxt == NULL)
542204917Sdes		fatal("%s: no authentication context", __func__);
543204917Sdes
544204917Sdes	fatal("Unexpected authentication success during %s.",
545204917Sdes	    authctxt->method->name);
546295367Sdes	return 0;
547204917Sdes}
548204917Sdes
549192595Sdes/* ARGSUSED */
550295367Sdesint
55192559Sdesinput_userauth_failure(int type, u_int32_t seq, void *ctxt)
55269587Sgreen{
55369587Sgreen	Authctxt *authctxt = ctxt;
55469587Sgreen	char *authlist = NULL;
55569587Sgreen	int partial;
55669587Sgreen
55769587Sgreen	if (authctxt == NULL)
55869587Sgreen		fatal("input_userauth_failure: no authentication context");
55969587Sgreen
56069587Sgreen	authlist = packet_get_string(NULL);
56169587Sgreen	partial = packet_get_char();
56292559Sdes	packet_check_eom();
56369587Sgreen
564255767Sdes	if (partial != 0) {
565124211Sdes		logit("Authenticated with partial success.");
566255767Sdes		/* reset state */
567255767Sdes		pubkey_cleanup(authctxt);
568255767Sdes		pubkey_prepare(authctxt);
569255767Sdes	}
570113911Sdes	debug("Authentications that can continue: %s", authlist);
57169587Sgreen
57276262Sgreen	userauth(authctxt, authlist);
573295367Sdes	return 0;
57476262Sgreen}
575192595Sdes
576192595Sdes/* ARGSUSED */
577295367Sdesint
57892559Sdesinput_userauth_pk_ok(int type, u_int32_t seq, void *ctxt)
57976262Sgreen{
58076262Sgreen	Authctxt *authctxt = ctxt;
58176262Sgreen	Key *key = NULL;
582124211Sdes	Identity *id = NULL;
58376262Sgreen	Buffer b;
58492559Sdes	int pktype, sent = 0;
58592559Sdes	u_int alen, blen;
58692559Sdes	char *pkalg, *fp;
58792559Sdes	u_char *pkblob;
58876262Sgreen
58976262Sgreen	if (authctxt == NULL)
59076262Sgreen		fatal("input_userauth_pk_ok: no authentication context");
59176262Sgreen	if (datafellows & SSH_BUG_PKOK) {
59276262Sgreen		/* this is similar to SSH_BUG_PKAUTH */
59376262Sgreen		debug2("input_userauth_pk_ok: SSH_BUG_PKOK");
59476262Sgreen		pkblob = packet_get_string(&blen);
59576262Sgreen		buffer_init(&b);
59676262Sgreen		buffer_append(&b, pkblob, blen);
59776262Sgreen		pkalg = buffer_get_string(&b, &alen);
59876262Sgreen		buffer_free(&b);
59976262Sgreen	} else {
60076262Sgreen		pkalg = packet_get_string(&alen);
60176262Sgreen		pkblob = packet_get_string(&blen);
60276262Sgreen	}
60392559Sdes	packet_check_eom();
60476262Sgreen
605124211Sdes	debug("Server accepts key: pkalg %s blen %u", pkalg, blen);
60676262Sgreen
607124211Sdes	if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) {
608124211Sdes		debug("unknown pkalg %s", pkalg);
609124211Sdes		goto done;
610124211Sdes	}
611124211Sdes	if ((key = key_from_blob(pkblob, blen)) == NULL) {
612124211Sdes		debug("no key from blob. pkalg %s", pkalg);
613124211Sdes		goto done;
614124211Sdes	}
615124211Sdes	if (key->type != pktype) {
616124211Sdes		error("input_userauth_pk_ok: type mismatch "
617124211Sdes		    "for decoded key (received %d, expected %d)",
618124211Sdes		    key->type, pktype);
619124211Sdes		goto done;
620124211Sdes	}
621295367Sdes	if ((fp = sshkey_fingerprint(key, options.fingerprint_hash,
622295367Sdes	    SSH_FP_DEFAULT)) == NULL)
623295367Sdes		goto done;
624124211Sdes	debug2("input_userauth_pk_ok: fp %s", fp);
625255767Sdes	free(fp);
626124211Sdes
627126277Sdes	/*
628126277Sdes	 * search keys in the reverse order, because last candidate has been
629126277Sdes	 * moved to the end of the queue.  this also avoids confusion by
630126277Sdes	 * duplicate keys
631126277Sdes	 */
632137019Sdes	TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) {
633124211Sdes		if (key_equal(key, id->key)) {
634124211Sdes			sent = sign_and_send_pubkey(authctxt, id);
63569587Sgreen			break;
63669587Sgreen		}
637124211Sdes	}
638124211Sdesdone:
63976262Sgreen	if (key != NULL)
64076262Sgreen		key_free(key);
641255767Sdes	free(pkalg);
642255767Sdes	free(pkblob);
64376262Sgreen
644106130Sdes	/* try another method if we did not send a packet */
64576262Sgreen	if (sent == 0)
64676262Sgreen		userauth(authctxt, NULL);
647295367Sdes	return 0;
648124211Sdes}
64976262Sgreen
650124211Sdes#ifdef GSSAPI
651126277Sdesint
652124211Sdesuserauth_gssapi(Authctxt *authctxt)
653124211Sdes{
654124211Sdes	Gssctxt *gssctxt = NULL;
655126277Sdes	static gss_OID_set gss_supported = NULL;
656149753Sdes	static u_int mech = 0;
657124211Sdes	OM_uint32 min;
658124211Sdes	int ok = 0;
659124211Sdes
660124211Sdes	/* Try one GSSAPI method at a time, rather than sending them all at
661124211Sdes	 * once. */
662124211Sdes
663126277Sdes	if (gss_supported == NULL)
664126277Sdes		gss_indicate_mechs(&min, &gss_supported);
665124211Sdes
666124211Sdes	/* Check to see if the mechanism is usable before we offer it */
667126277Sdes	while (mech < gss_supported->count && !ok) {
668124211Sdes		/* My DER encoding requires length<128 */
669126277Sdes		if (gss_supported->elements[mech].length < 128 &&
670162856Sdes		    ssh_gssapi_check_mechanism(&gssctxt,
671162856Sdes		    &gss_supported->elements[mech], authctxt->host)) {
672124211Sdes			ok = 1; /* Mechanism works */
673124211Sdes		} else {
674124211Sdes			mech++;
675124211Sdes		}
676124211Sdes	}
677124211Sdes
678149753Sdes	if (!ok)
679149753Sdes		return 0;
680124211Sdes
681124211Sdes	authctxt->methoddata=(void *)gssctxt;
682124211Sdes
683124211Sdes	packet_start(SSH2_MSG_USERAUTH_REQUEST);
684124211Sdes	packet_put_cstring(authctxt->server_user);
685124211Sdes	packet_put_cstring(authctxt->service);
686124211Sdes	packet_put_cstring(authctxt->method->name);
687124211Sdes
688124211Sdes	packet_put_int(1);
689124211Sdes
690126277Sdes	packet_put_int((gss_supported->elements[mech].length) + 2);
691126277Sdes	packet_put_char(SSH_GSS_OIDTYPE);
692126277Sdes	packet_put_char(gss_supported->elements[mech].length);
693126277Sdes	packet_put_raw(gss_supported->elements[mech].elements,
694126277Sdes	    gss_supported->elements[mech].length);
695124211Sdes
696124211Sdes	packet_send();
697124211Sdes
698124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response);
699124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
700124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error);
701124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
702124211Sdes
703124211Sdes	mech++; /* Move along to next candidate */
704124211Sdes
705124211Sdes	return 1;
70669587Sgreen}
70769587Sgreen
708126277Sdesstatic OM_uint32
709126277Sdesprocess_gssapi_token(void *ctxt, gss_buffer_t recv_tok)
710126277Sdes{
711126277Sdes	Authctxt *authctxt = ctxt;
712126277Sdes	Gssctxt *gssctxt = authctxt->methoddata;
713126277Sdes	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
714149753Sdes	gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
715149753Sdes	gss_buffer_desc gssbuf;
716126277Sdes	OM_uint32 status, ms, flags;
717126277Sdes	Buffer b;
718126277Sdes
719126277Sdes	status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
720126277Sdes	    recv_tok, &send_tok, &flags);
721126277Sdes
722126277Sdes	if (send_tok.length > 0) {
723126277Sdes		if (GSS_ERROR(status))
724126277Sdes			packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
725126277Sdes		else
726126277Sdes			packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
727126277Sdes
728126277Sdes		packet_put_string(send_tok.value, send_tok.length);
729126277Sdes		packet_send();
730126277Sdes		gss_release_buffer(&ms, &send_tok);
731126277Sdes	}
732126277Sdes
733126277Sdes	if (status == GSS_S_COMPLETE) {
734126277Sdes		/* send either complete or MIC, depending on mechanism */
735126277Sdes		if (!(flags & GSS_C_INTEG_FLAG)) {
736126277Sdes			packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE);
737126277Sdes			packet_send();
738126277Sdes		} else {
739126277Sdes			ssh_gssapi_buildmic(&b, authctxt->server_user,
740126277Sdes			    authctxt->service, "gssapi-with-mic");
741126277Sdes
742126277Sdes			gssbuf.value = buffer_ptr(&b);
743126277Sdes			gssbuf.length = buffer_len(&b);
744126277Sdes
745126277Sdes			status = ssh_gssapi_sign(gssctxt, &gssbuf, &mic);
746126277Sdes
747126277Sdes			if (!GSS_ERROR(status)) {
748126277Sdes				packet_start(SSH2_MSG_USERAUTH_GSSAPI_MIC);
749126277Sdes				packet_put_string(mic.value, mic.length);
750126277Sdes
751126277Sdes				packet_send();
752126277Sdes			}
753126277Sdes
754126277Sdes			buffer_free(&b);
755126277Sdes			gss_release_buffer(&ms, &mic);
756126277Sdes		}
757126277Sdes	}
758126277Sdes
759126277Sdes	return status;
760126277Sdes}
761126277Sdes
762192595Sdes/* ARGSUSED */
763295367Sdesint
764124211Sdesinput_gssapi_response(int type, u_int32_t plen, void *ctxt)
765124211Sdes{
766124211Sdes	Authctxt *authctxt = ctxt;
767124211Sdes	Gssctxt *gssctxt;
768124211Sdes	int oidlen;
769124211Sdes	char *oidv;
770124211Sdes
771124211Sdes	if (authctxt == NULL)
772124211Sdes		fatal("input_gssapi_response: no authentication context");
773124211Sdes	gssctxt = authctxt->methoddata;
774124211Sdes
775124211Sdes	/* Setup our OID */
776124211Sdes	oidv = packet_get_string(&oidlen);
777124211Sdes
778126277Sdes	if (oidlen <= 2 ||
779126277Sdes	    oidv[0] != SSH_GSS_OIDTYPE ||
780126277Sdes	    oidv[1] != oidlen - 2) {
781255767Sdes		free(oidv);
782126277Sdes		debug("Badly encoded mechanism OID received");
783126277Sdes		userauth(authctxt, NULL);
784295367Sdes		return 0;
785124211Sdes	}
786124211Sdes
787126277Sdes	if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2))
788126277Sdes		fatal("Server returned different OID than expected");
789126277Sdes
790124211Sdes	packet_check_eom();
791124211Sdes
792255767Sdes	free(oidv);
793124211Sdes
794126277Sdes	if (GSS_ERROR(process_gssapi_token(ctxt, GSS_C_NO_BUFFER))) {
795124211Sdes		/* Start again with next method on list */
796124211Sdes		debug("Trying to start again");
797124211Sdes		userauth(authctxt, NULL);
798295367Sdes		return 0;
799124211Sdes	}
800295367Sdes	return 0;
801124211Sdes}
802124211Sdes
803192595Sdes/* ARGSUSED */
804295367Sdesint
805124211Sdesinput_gssapi_token(int type, u_int32_t plen, void *ctxt)
806124211Sdes{
807124211Sdes	Authctxt *authctxt = ctxt;
808124211Sdes	gss_buffer_desc recv_tok;
809126277Sdes	OM_uint32 status;
810124211Sdes	u_int slen;
811124211Sdes
812124211Sdes	if (authctxt == NULL)
813124211Sdes		fatal("input_gssapi_response: no authentication context");
814124211Sdes
815124211Sdes	recv_tok.value = packet_get_string(&slen);
816124211Sdes	recv_tok.length = slen;	/* safe typecast */
817124211Sdes
818124211Sdes	packet_check_eom();
819124211Sdes
820126277Sdes	status = process_gssapi_token(ctxt, &recv_tok);
821124211Sdes
822255767Sdes	free(recv_tok.value);
823124211Sdes
824124211Sdes	if (GSS_ERROR(status)) {
825124211Sdes		/* Start again with the next method in the list */
826124211Sdes		userauth(authctxt, NULL);
827295367Sdes		return 0;
828124211Sdes	}
829295367Sdes	return 0;
830124211Sdes}
831124211Sdes
832192595Sdes/* ARGSUSED */
833295367Sdesint
834124211Sdesinput_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
835124211Sdes{
836124211Sdes	Authctxt *authctxt = ctxt;
837124211Sdes	Gssctxt *gssctxt;
838124211Sdes	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
839124211Sdes	gss_buffer_desc recv_tok;
840255767Sdes	OM_uint32 ms;
841124211Sdes	u_int len;
842124211Sdes
843124211Sdes	if (authctxt == NULL)
844124211Sdes		fatal("input_gssapi_response: no authentication context");
845124211Sdes	gssctxt = authctxt->methoddata;
846124211Sdes
847124211Sdes	recv_tok.value = packet_get_string(&len);
848124211Sdes	recv_tok.length = len;
849124211Sdes
850124211Sdes	packet_check_eom();
851124211Sdes
852124211Sdes	/* Stick it into GSSAPI and see what it says */
853255767Sdes	(void)ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
854149753Sdes	    &recv_tok, &send_tok, NULL);
855124211Sdes
856255767Sdes	free(recv_tok.value);
857124211Sdes	gss_release_buffer(&ms, &send_tok);
858124211Sdes
859124211Sdes	/* Server will be returning a failed packet after this one */
860295367Sdes	return 0;
861124211Sdes}
862124211Sdes
863192595Sdes/* ARGSUSED */
864295367Sdesint
865124211Sdesinput_gssapi_error(int type, u_int32_t plen, void *ctxt)
866124211Sdes{
867124211Sdes	char *msg;
868124211Sdes	char *lang;
869124211Sdes
870255767Sdes	/* maj */(void)packet_get_int();
871255767Sdes	/* min */(void)packet_get_int();
872124211Sdes	msg=packet_get_string(NULL);
873124211Sdes	lang=packet_get_string(NULL);
874124211Sdes
875124211Sdes	packet_check_eom();
876124211Sdes
877157019Sdes	debug("Server GSSAPI Error:\n%s", msg);
878255767Sdes	free(msg);
879255767Sdes	free(lang);
880295367Sdes	return 0;
881124211Sdes}
882124211Sdes#endif /* GSSAPI */
883124211Sdes
88460573Skrisint
88569587Sgreenuserauth_none(Authctxt *authctxt)
88660573Skris{
88769587Sgreen	/* initial userauth request */
88869587Sgreen	packet_start(SSH2_MSG_USERAUTH_REQUEST);
88969587Sgreen	packet_put_cstring(authctxt->server_user);
89069587Sgreen	packet_put_cstring(authctxt->service);
89169587Sgreen	packet_put_cstring(authctxt->method->name);
89269587Sgreen	packet_send();
89369587Sgreen	return 1;
89469587Sgreen}
89569587Sgreen
89669587Sgreenint
89769587Sgreenuserauth_passwd(Authctxt *authctxt)
89869587Sgreen{
89960573Skris	static int attempt = 0;
90098684Sdes	char prompt[150];
90160573Skris	char *password;
902204917Sdes	const char *host = options.host_key_alias ?  options.host_key_alias :
903204917Sdes	    authctxt->host;
90460573Skris
90565668Skris	if (attempt++ >= options.number_of_password_prompts)
90660573Skris		return 0;
90760573Skris
90892559Sdes	if (attempt != 1)
90965668Skris		error("Permission denied, please try again.");
91065668Skris
91176262Sgreen	snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
912204917Sdes	    authctxt->server_user, host);
91360573Skris	password = read_passphrase(prompt, 0);
91460573Skris	packet_start(SSH2_MSG_USERAUTH_REQUEST);
91569587Sgreen	packet_put_cstring(authctxt->server_user);
91669587Sgreen	packet_put_cstring(authctxt->service);
91769587Sgreen	packet_put_cstring(authctxt->method->name);
91860573Skris	packet_put_char(0);
91993704Sdes	packet_put_cstring(password);
920264377Sdes	explicit_bzero(password, strlen(password));
921255767Sdes	free(password);
92292559Sdes	packet_add_padding(64);
92360573Skris	packet_send();
92498684Sdes
92598684Sdes	dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
92698684Sdes	    &input_userauth_passwd_changereq);
92798684Sdes
92860573Skris	return 1;
92960573Skris}
930192595Sdes
93198684Sdes/*
93298684Sdes * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST
93398684Sdes */
934192595Sdes/* ARGSUSED */
935295367Sdesint
93698941Sdesinput_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt)
93798684Sdes{
93898684Sdes	Authctxt *authctxt = ctxt;
93998684Sdes	char *info, *lang, *password = NULL, *retype = NULL;
94098684Sdes	char prompt[150];
941204917Sdes	const char *host = options.host_key_alias ? options.host_key_alias :
942204917Sdes	    authctxt->host;
94360573Skris
94498684Sdes	debug2("input_userauth_passwd_changereq");
94598684Sdes
94698684Sdes	if (authctxt == NULL)
94798684Sdes		fatal("input_userauth_passwd_changereq: "
94898684Sdes		    "no authentication context");
94998684Sdes
95098684Sdes	info = packet_get_string(NULL);
95198684Sdes	lang = packet_get_string(NULL);
95298684Sdes	if (strlen(info) > 0)
953124211Sdes		logit("%s", info);
954255767Sdes	free(info);
955255767Sdes	free(lang);
95698684Sdes	packet_start(SSH2_MSG_USERAUTH_REQUEST);
95798684Sdes	packet_put_cstring(authctxt->server_user);
95898684Sdes	packet_put_cstring(authctxt->service);
95998684Sdes	packet_put_cstring(authctxt->method->name);
96098684Sdes	packet_put_char(1);			/* additional info */
96198684Sdes	snprintf(prompt, sizeof(prompt),
96298684Sdes	    "Enter %.30s@%.128s's old password: ",
963204917Sdes	    authctxt->server_user, host);
96498684Sdes	password = read_passphrase(prompt, 0);
96598684Sdes	packet_put_cstring(password);
966264377Sdes	explicit_bzero(password, strlen(password));
967255767Sdes	free(password);
96898684Sdes	password = NULL;
96998684Sdes	while (password == NULL) {
97098684Sdes		snprintf(prompt, sizeof(prompt),
97198684Sdes		    "Enter %.30s@%.128s's new password: ",
972204917Sdes		    authctxt->server_user, host);
97398684Sdes		password = read_passphrase(prompt, RP_ALLOW_EOF);
97498684Sdes		if (password == NULL) {
97598684Sdes			/* bail out */
976295367Sdes			return 0;
97798684Sdes		}
97898684Sdes		snprintf(prompt, sizeof(prompt),
97998684Sdes		    "Retype %.30s@%.128s's new password: ",
980204917Sdes		    authctxt->server_user, host);
98198684Sdes		retype = read_passphrase(prompt, 0);
98298684Sdes		if (strcmp(password, retype) != 0) {
983264377Sdes			explicit_bzero(password, strlen(password));
984255767Sdes			free(password);
985124211Sdes			logit("Mismatch; try again, EOF to quit.");
98698684Sdes			password = NULL;
98798684Sdes		}
988264377Sdes		explicit_bzero(retype, strlen(retype));
989255767Sdes		free(retype);
99098684Sdes	}
99198684Sdes	packet_put_cstring(password);
992264377Sdes	explicit_bzero(password, strlen(password));
993255767Sdes	free(password);
99498684Sdes	packet_add_padding(64);
99598684Sdes	packet_send();
99698684Sdes
99798684Sdes	dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
99898684Sdes	    &input_userauth_passwd_changereq);
999295367Sdes	return 0;
100098684Sdes}
100198684Sdes
1002296853Sdesstatic const char *
1003296853Sdesidentity_sign_encode(struct identity *id)
1004296853Sdes{
1005296853Sdes	struct ssh *ssh = active_state;
1006296853Sdes
1007296853Sdes	if (id->key->type == KEY_RSA) {
1008296853Sdes		switch (ssh->kex->rsa_sha2) {
1009296853Sdes		case 256:
1010296853Sdes			return "rsa-sha2-256";
1011296853Sdes		case 512:
1012296853Sdes			return "rsa-sha2-512";
1013296853Sdes		}
1014296853Sdes	}
1015296853Sdes	return key_ssh_name(id->key);
1016296853Sdes}
1017296853Sdes
1018124211Sdesstatic int
1019295367Sdesidentity_sign(struct identity *id, u_char **sigp, size_t *lenp,
1020295367Sdes    const u_char *data, size_t datalen, u_int compat)
102176262Sgreen{
1022124211Sdes	Key *prv;
1023124211Sdes	int ret;
1024296853Sdes	const char *alg;
102598684Sdes
1026296853Sdes	alg = identity_sign_encode(id);
1027296853Sdes
1028124211Sdes	/* the agent supports this key */
1029296853Sdes	if (id->agent_fd != -1)
1030295367Sdes		return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp,
1031296853Sdes		    data, datalen, alg, compat);
1032295367Sdes
1033124211Sdes	/*
1034124211Sdes	 * we have already loaded the private key or
1035124211Sdes	 * the private key is stored in external hardware
1036124211Sdes	 */
1037295367Sdes	if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))
1038296853Sdes		return (sshkey_sign(id->key, sigp, lenp, data, datalen, alg,
1039295367Sdes		    compat));
1040124211Sdes	/* load the private key from the file */
1041296853Sdes	if ((prv = load_identity_file(id)) == NULL)
1042296853Sdes		return SSH_ERR_KEY_NOT_FOUND;
1043296853Sdes	ret = sshkey_sign(prv, sigp, lenp, data, datalen, alg, compat);
1044295367Sdes	sshkey_free(prv);
1045124211Sdes	return (ret);
104676262Sgreen}
104776262Sgreen
104892559Sdesstatic int
1049124211Sdessign_and_send_pubkey(Authctxt *authctxt, Identity *id)
105060573Skris{
105160573Skris	Buffer b;
1052296853Sdes	Identity *private_id;
105376262Sgreen	u_char *blob, *signature;
1054295367Sdes	size_t slen;
1055296853Sdes	u_int bloblen, skip = 0;
1056296853Sdes	int matched, ret = -1, have_sig = 1;
1057215116Sdes	char *fp;
105860573Skris
1059295367Sdes	if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
1060295367Sdes	    SSH_FP_DEFAULT)) == NULL)
1061295367Sdes		return 0;
1062296853Sdes	debug3("%s: %s %s", __func__, key_type(id->key), fp);
1063255767Sdes	free(fp);
106460573Skris
1065124211Sdes	if (key_to_blob(id->key, &blob, &bloblen) == 0) {
106676262Sgreen		/* we cannot handle this key */
106776262Sgreen		debug3("sign_and_send_pubkey: cannot handle key");
106876262Sgreen		return 0;
106976262Sgreen	}
107060573Skris	/* data to be signed */
107160573Skris	buffer_init(&b);
107269587Sgreen	if (datafellows & SSH_OLD_SESSIONID) {
107369587Sgreen		buffer_append(&b, session_id2, session_id2_len);
107476262Sgreen		skip = session_id2_len;
107569587Sgreen	} else {
107665668Skris		buffer_put_string(&b, session_id2, session_id2_len);
107765668Skris		skip = buffer_len(&b);
107865668Skris	}
107960573Skris	buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
108069587Sgreen	buffer_put_cstring(&b, authctxt->server_user);
108160573Skris	buffer_put_cstring(&b,
108276262Sgreen	    datafellows & SSH_BUG_PKSERVICE ?
108360573Skris	    "ssh-userauth" :
108469587Sgreen	    authctxt->service);
108576262Sgreen	if (datafellows & SSH_BUG_PKAUTH) {
108676262Sgreen		buffer_put_char(&b, have_sig);
108776262Sgreen	} else {
108876262Sgreen		buffer_put_cstring(&b, authctxt->method->name);
108976262Sgreen		buffer_put_char(&b, have_sig);
1090296853Sdes		buffer_put_cstring(&b, identity_sign_encode(id));
109176262Sgreen	}
109260573Skris	buffer_put_string(&b, blob, bloblen);
109360573Skris
1094296853Sdes	/*
1095296853Sdes	 * If the key is an certificate, try to find a matching private key
1096296853Sdes	 * and use it to complete the signature.
1097296853Sdes	 * If no such private key exists, return failure and continue with
1098296853Sdes	 * other methods of authentication.
1099296853Sdes	 */
1100296853Sdes	if (key_is_cert(id->key)) {
1101296853Sdes		matched = 0;
1102296853Sdes		TAILQ_FOREACH(private_id, &authctxt->keys, next) {
1103296853Sdes			if (sshkey_equal_public(id->key, private_id->key) &&
1104296853Sdes			    id->key->type != private_id->key->type) {
1105296853Sdes				id = private_id;
1106296853Sdes				matched = 1;
1107296853Sdes				break;
1108296853Sdes			}
1109296853Sdes		}
1110296853Sdes		if (matched) {
1111296853Sdes			debug2("%s: using private key \"%s\"%s for "
1112296853Sdes			    "certificate", __func__, id->filename,
1113296853Sdes			    id->agent_fd != -1 ? " from agent" : "");
1114296853Sdes		} else {
1115296853Sdes			/* XXX maybe verbose/error? */
1116296853Sdes			debug("%s: no private key for certificate "
1117296853Sdes			    "\"%s\"", __func__, id->filename);
1118296853Sdes			free(blob);
1119296853Sdes			buffer_free(&b);
1120296853Sdes			return 0;
1121296853Sdes		}
1122296853Sdes	}
1123296853Sdes
112460573Skris	/* generate signature */
1125124211Sdes	ret = identity_sign(id, &signature, &slen,
1126295367Sdes	    buffer_ptr(&b), buffer_len(&b), datafellows);
1127295367Sdes	if (ret != 0) {
1128296853Sdes		if (ret != SSH_ERR_KEY_NOT_FOUND)
1129296853Sdes			error("%s: signing failed: %s", __func__, ssh_err(ret));
1130255767Sdes		free(blob);
113165668Skris		buffer_free(&b);
113265668Skris		return 0;
113365668Skris	}
113476262Sgreen#ifdef DEBUG_PK
113560573Skris	buffer_dump(&b);
113660573Skris#endif
113776262Sgreen	if (datafellows & SSH_BUG_PKSERVICE) {
113860573Skris		buffer_clear(&b);
113960573Skris		buffer_append(&b, session_id2, session_id2_len);
114076262Sgreen		skip = session_id2_len;
114160573Skris		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
114269587Sgreen		buffer_put_cstring(&b, authctxt->server_user);
114369587Sgreen		buffer_put_cstring(&b, authctxt->service);
114469587Sgreen		buffer_put_cstring(&b, authctxt->method->name);
114569587Sgreen		buffer_put_char(&b, have_sig);
114676262Sgreen		if (!(datafellows & SSH_BUG_PKAUTH))
1147124211Sdes			buffer_put_cstring(&b, key_ssh_name(id->key));
114860573Skris		buffer_put_string(&b, blob, bloblen);
114960573Skris	}
1150255767Sdes	free(blob);
115176262Sgreen
115260573Skris	/* append signature */
115360573Skris	buffer_put_string(&b, signature, slen);
1154255767Sdes	free(signature);
115560573Skris
115660573Skris	/* skip session id and packet type */
115765668Skris	if (buffer_len(&b) < skip + 1)
115869587Sgreen		fatal("userauth_pubkey: internal error");
115965668Skris	buffer_consume(&b, skip + 1);
116060573Skris
116160573Skris	/* put remaining data from buffer into packet */
116260573Skris	packet_start(SSH2_MSG_USERAUTH_REQUEST);
116360573Skris	packet_put_raw(buffer_ptr(&b), buffer_len(&b));
116460573Skris	buffer_free(&b);
116560573Skris	packet_send();
116665668Skris
116760573Skris	return 1;
116860573Skris}
116960573Skris
117092559Sdesstatic int
1171124211Sdessend_pubkey_test(Authctxt *authctxt, Identity *id)
117269587Sgreen{
117376262Sgreen	u_char *blob;
117492559Sdes	u_int bloblen, have_sig = 0;
117576262Sgreen
117676262Sgreen	debug3("send_pubkey_test");
117776262Sgreen
1178124211Sdes	if (key_to_blob(id->key, &blob, &bloblen) == 0) {
117976262Sgreen		/* we cannot handle this key */
118076262Sgreen		debug3("send_pubkey_test: cannot handle key");
118176262Sgreen		return 0;
118276262Sgreen	}
118376262Sgreen	/* register callback for USERAUTH_PK_OK message */
118476262Sgreen	dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok);
118576262Sgreen
118676262Sgreen	packet_start(SSH2_MSG_USERAUTH_REQUEST);
118776262Sgreen	packet_put_cstring(authctxt->server_user);
118876262Sgreen	packet_put_cstring(authctxt->service);
118976262Sgreen	packet_put_cstring(authctxt->method->name);
119076262Sgreen	packet_put_char(have_sig);
119176262Sgreen	if (!(datafellows & SSH_BUG_PKAUTH))
1192296853Sdes		packet_put_cstring(identity_sign_encode(id));
119376262Sgreen	packet_put_string(blob, bloblen);
1194255767Sdes	free(blob);
119576262Sgreen	packet_send();
119676262Sgreen	return 1;
119769587Sgreen}
119869587Sgreen
119992559Sdesstatic Key *
1200296853Sdesload_identity_file(Identity *id)
120165668Skris{
1202296853Sdes	Key *private = NULL;
1203296853Sdes	char prompt[300], *passphrase, *comment;
1204295367Sdes	int r, perm_ok = 0, quit = 0, i;
120565668Skris	struct stat st;
120665668Skris
1207296853Sdes	if (stat(id->filename, &st) < 0) {
1208296853Sdes		(id->userprovided ? logit : debug3)("no such identity: %s: %s",
1209296853Sdes		    id->filename, strerror(errno));
121076262Sgreen		return NULL;
121165668Skris	}
1212295367Sdes	snprintf(prompt, sizeof prompt,
1213296853Sdes	    "Enter passphrase for key '%.100s': ", id->filename);
1214295367Sdes	for (i = 0; i <= options.number_of_password_prompts; i++) {
1215295367Sdes		if (i == 0)
1216295367Sdes			passphrase = "";
1217295367Sdes		else {
121869587Sgreen			passphrase = read_passphrase(prompt, 0);
1219295367Sdes			if (*passphrase == '\0') {
122069587Sgreen				debug2("no passphrase given, try next key");
1221295367Sdes				free(passphrase);
1222295367Sdes				break;
1223295367Sdes			}
1224295367Sdes		}
1225296853Sdes		switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename,
1226296853Sdes		    passphrase, &private, &comment, &perm_ok))) {
1227295367Sdes		case 0:
1228295367Sdes			break;
1229295367Sdes		case SSH_ERR_KEY_WRONG_PASSPHRASE:
1230295367Sdes			if (options.batch_mode) {
123176262Sgreen				quit = 1;
1232295367Sdes				break;
123369587Sgreen			}
1234295367Sdes			if (i != 0)
1235295367Sdes				debug2("bad passphrase given, try again...");
1236295367Sdes			break;
1237295367Sdes		case SSH_ERR_SYSTEM_ERROR:
1238295367Sdes			if (errno == ENOENT) {
1239295367Sdes				debug2("Load key \"%s\": %s",
1240296853Sdes				    id->filename, ssh_err(r));
1241295367Sdes				quit = 1;
1242295367Sdes				break;
1243295367Sdes			}
1244295367Sdes			/* FALLTHROUGH */
1245295367Sdes		default:
1246296853Sdes			error("Load key \"%s\": %s", id->filename, ssh_err(r));
1247295367Sdes			quit = 1;
1248295367Sdes			break;
1249295367Sdes		}
1250296853Sdes		if (!quit && private != NULL && id->agent_fd == -1 &&
1251296853Sdes		    !(id->key && id->isprivate))
1252296853Sdes			maybe_add_key_to_agent(id->filename, private, comment,
1253296853Sdes			    passphrase);
1254295367Sdes		if (i > 0) {
1255264377Sdes			explicit_bzero(passphrase, strlen(passphrase));
1256255767Sdes			free(passphrase);
125769587Sgreen		}
1258296853Sdes		free(comment);
1259295367Sdes		if (private != NULL || quit)
1260295367Sdes			break;
126165668Skris	}
126276262Sgreen	return private;
126376262Sgreen}
126476262Sgreen
1265124211Sdes/*
1266124211Sdes * try keys in the following order:
1267296853Sdes * 	1. certificates listed in the config file
1268296853Sdes * 	2. other input certificates
1269296853Sdes *	3. agent keys that are found in the config file
1270296853Sdes *	4. other agent keys
1271296853Sdes *	5. keys that are only listed in the config file
1272124211Sdes */
1273124211Sdesstatic void
1274124211Sdespubkey_prepare(Authctxt *authctxt)
127576262Sgreen{
1276295367Sdes	struct identity *id, *id2, *tmp;
1277295367Sdes	struct idlist agent, files, *preferred;
1278295367Sdes	struct sshkey *key;
1279296853Sdes	int agent_fd = -1, i, r, found;
1280295367Sdes	size_t j;
1281295367Sdes	struct ssh_identitylist *idlist;
128276262Sgreen
1283124211Sdes	TAILQ_INIT(&agent);	/* keys from the agent */
1284124211Sdes	TAILQ_INIT(&files);	/* keys from the config file */
1285124211Sdes	preferred = &authctxt->keys;
1286124211Sdes	TAILQ_INIT(preferred);	/* preferred order of keys */
128792559Sdes
1288248619Sdes	/* list of keys stored in the filesystem and PKCS#11 */
1289124211Sdes	for (i = 0; i < options.num_identity_files; i++) {
1290124211Sdes		key = options.identity_keys[i];
1291124211Sdes		if (key && key->type == KEY_RSA1)
1292124211Sdes			continue;
1293204917Sdes		if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER)
1294204917Sdes			continue;
1295124211Sdes		options.identity_keys[i] = NULL;
1296162856Sdes		id = xcalloc(1, sizeof(*id));
1297296853Sdes		id->agent_fd = -1;
1298124211Sdes		id->key = key;
1299124211Sdes		id->filename = xstrdup(options.identity_files[i]);
1300249016Sdes		id->userprovided = options.identity_file_userprovided[i];
1301124211Sdes		TAILQ_INSERT_TAIL(&files, id, next);
1302124211Sdes	}
1303248619Sdes	/* Prefer PKCS11 keys that are explicitly listed */
1304248619Sdes	TAILQ_FOREACH_SAFE(id, &files, next, tmp) {
1305295367Sdes		if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0)
1306248619Sdes			continue;
1307248619Sdes		found = 0;
1308248619Sdes		TAILQ_FOREACH(id2, &files, next) {
1309248619Sdes			if (id2->key == NULL ||
1310295367Sdes			    (id2->key->flags & SSHKEY_FLAG_EXT) == 0)
1311248619Sdes				continue;
1312295367Sdes			if (sshkey_equal(id->key, id2->key)) {
1313248619Sdes				TAILQ_REMOVE(&files, id, next);
1314248619Sdes				TAILQ_INSERT_TAIL(preferred, id, next);
1315248619Sdes				found = 1;
1316248619Sdes				break;
1317248619Sdes			}
1318248619Sdes		}
1319248619Sdes		/* If IdentitiesOnly set and key not found then don't use it */
1320248619Sdes		if (!found && options.identities_only) {
1321248619Sdes			TAILQ_REMOVE(&files, id, next);
1322264377Sdes			explicit_bzero(id, sizeof(*id));
1323248619Sdes			free(id);
1324248619Sdes		}
1325248619Sdes	}
1326296853Sdes	/* list of certificates specified by user */
1327296853Sdes	for (i = 0; i < options.num_certificate_files; i++) {
1328296853Sdes		key = options.certificates[i];
1329296853Sdes		if (!key_is_cert(key) || key->cert == NULL ||
1330296853Sdes		    key->cert->type != SSH2_CERT_TYPE_USER)
1331296853Sdes			continue;
1332296853Sdes		id = xcalloc(1, sizeof(*id));
1333296853Sdes		id->agent_fd = -1;
1334296853Sdes		id->key = key;
1335296853Sdes		id->filename = xstrdup(options.certificate_files[i]);
1336296853Sdes		id->userprovided = options.certificate_file_userprovided[i];
1337296853Sdes		TAILQ_INSERT_TAIL(preferred, id, next);
1338296853Sdes	}
1339124211Sdes	/* list of keys supported by the agent */
1340295367Sdes	if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
1341295367Sdes		if (r != SSH_ERR_AGENT_NOT_PRESENT)
1342295367Sdes			debug("%s: ssh_get_authentication_socket: %s",
1343295367Sdes			    __func__, ssh_err(r));
1344295367Sdes	} else if ((r = ssh_fetch_identitylist(agent_fd, 2, &idlist)) != 0) {
1345295367Sdes		if (r != SSH_ERR_AGENT_NO_IDENTITIES)
1346295367Sdes			debug("%s: ssh_fetch_identitylist: %s",
1347295367Sdes			    __func__, ssh_err(r));
1348296853Sdes		close(agent_fd);
1349295367Sdes	} else {
1350295367Sdes		for (j = 0; j < idlist->nkeys; j++) {
1351124211Sdes			found = 0;
1352124211Sdes			TAILQ_FOREACH(id, &files, next) {
1353295367Sdes				/*
1354295367Sdes				 * agent keys from the config file are
1355295367Sdes				 * preferred
1356295367Sdes				 */
1357295367Sdes				if (sshkey_equal(idlist->keys[j], id->key)) {
1358124211Sdes					TAILQ_REMOVE(&files, id, next);
1359124211Sdes					TAILQ_INSERT_TAIL(preferred, id, next);
1360295367Sdes					id->agent_fd = agent_fd;
1361124211Sdes					found = 1;
1362124211Sdes					break;
1363124211Sdes				}
1364124211Sdes			}
1365128460Sdes			if (!found && !options.identities_only) {
1366162856Sdes				id = xcalloc(1, sizeof(*id));
1367295367Sdes				/* XXX "steals" key/comment from idlist */
1368295367Sdes				id->key = idlist->keys[j];
1369295367Sdes				id->filename = idlist->comments[j];
1370295367Sdes				idlist->keys[j] = NULL;
1371295367Sdes				idlist->comments[j] = NULL;
1372295367Sdes				id->agent_fd = agent_fd;
1373124211Sdes				TAILQ_INSERT_TAIL(&agent, id, next);
1374124211Sdes			}
1375124211Sdes		}
1376295367Sdes		ssh_free_identitylist(idlist);
1377124211Sdes		/* append remaining agent keys */
1378124211Sdes		for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) {
1379124211Sdes			TAILQ_REMOVE(&agent, id, next);
1380124211Sdes			TAILQ_INSERT_TAIL(preferred, id, next);
1381124211Sdes		}
1382295367Sdes		authctxt->agent_fd = agent_fd;
1383124211Sdes	}
1384124211Sdes	/* append remaining keys from the config file */
1385124211Sdes	for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) {
1386124211Sdes		TAILQ_REMOVE(&files, id, next);
1387124211Sdes		TAILQ_INSERT_TAIL(preferred, id, next);
1388124211Sdes	}
1389296853Sdes	/* finally, filter by PubkeyAcceptedKeyTypes */
1390296853Sdes	TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
1391296853Sdes		if (id->key != NULL &&
1392296853Sdes		    match_pattern_list(sshkey_ssh_name(id->key),
1393296853Sdes		    options.pubkey_key_types, 0) != 1) {
1394296853Sdes			debug("Skipping %s key %s - "
1395296853Sdes			    "not in PubkeyAcceptedKeyTypes",
1396296853Sdes			    sshkey_ssh_name(id->key), id->filename);
1397296853Sdes			TAILQ_REMOVE(preferred, id, next);
1398296853Sdes			sshkey_free(id->key);
1399296853Sdes			free(id->filename);
1400296853Sdes			memset(id, 0, sizeof(*id));
1401296853Sdes			continue;
1402296853Sdes		}
1403296853Sdes		debug2("key: %s (%p)%s%s", id->filename, id->key,
1404296853Sdes		    id->userprovided ? ", explicit" : "",
1405296853Sdes		    id->agent_fd != -1 ? ", agent" : "");
1406124211Sdes	}
140765668Skris}
140865668Skris
1409124211Sdesstatic void
1410124211Sdespubkey_cleanup(Authctxt *authctxt)
141165668Skris{
1412124211Sdes	Identity *id;
141365668Skris
1414295367Sdes	if (authctxt->agent_fd != -1)
1415295367Sdes		ssh_close_authentication_socket(authctxt->agent_fd);
1416124211Sdes	for (id = TAILQ_FIRST(&authctxt->keys); id;
1417124211Sdes	    id = TAILQ_FIRST(&authctxt->keys)) {
1418124211Sdes		TAILQ_REMOVE(&authctxt->keys, id, next);
1419296853Sdes		sshkey_free(id->key);
1420255767Sdes		free(id->filename);
1421255767Sdes		free(id);
142265668Skris	}
142365668Skris}
142465668Skris
1425295367Sdesstatic int
1426295367Sdestry_identity(Identity *id)
1427295367Sdes{
1428295367Sdes	if (!id->key)
1429295367Sdes		return (0);
1430295367Sdes	if (key_type_plain(id->key->type) == KEY_RSA &&
1431295367Sdes	    (datafellows & SSH_BUG_RSASIGMD5) != 0) {
1432295367Sdes		debug("Skipped %s key %s for RSA/MD5 server",
1433295367Sdes		    key_type(id->key), id->filename);
1434295367Sdes		return (0);
1435295367Sdes	}
1436295367Sdes	return (id->key->type != KEY_RSA1);
1437295367Sdes}
1438295367Sdes
143969587Sgreenint
144069587Sgreenuserauth_pubkey(Authctxt *authctxt)
144169587Sgreen{
1442124211Sdes	Identity *id;
144369587Sgreen	int sent = 0;
144469587Sgreen
1445124211Sdes	while ((id = TAILQ_FIRST(&authctxt->keys))) {
1446124211Sdes		if (id->tried++)
1447124211Sdes			return (0);
1448126277Sdes		/* move key to the end of the queue */
1449124211Sdes		TAILQ_REMOVE(&authctxt->keys, id, next);
1450124211Sdes		TAILQ_INSERT_TAIL(&authctxt->keys, id, next);
1451124211Sdes		/*
1452124211Sdes		 * send a test message if we have the public key. for
1453124211Sdes		 * encrypted keys we cannot do this and have to load the
1454124211Sdes		 * private key instead
1455124211Sdes		 */
1456262566Sdes		if (id->key != NULL) {
1457295367Sdes			if (try_identity(id)) {
1458262566Sdes				debug("Offering %s public key: %s",
1459262566Sdes				    key_type(id->key), id->filename);
1460262566Sdes				sent = send_pubkey_test(authctxt, id);
1461262566Sdes			}
1462262566Sdes		} else {
1463124211Sdes			debug("Trying private key: %s", id->filename);
1464296853Sdes			id->key = load_identity_file(id);
1465124211Sdes			if (id->key != NULL) {
1466295367Sdes				if (try_identity(id)) {
1467295367Sdes					id->isprivate = 1;
1468262566Sdes					sent = sign_and_send_pubkey(
1469262566Sdes					    authctxt, id);
1470262566Sdes				}
1471124211Sdes				key_free(id->key);
1472124211Sdes				id->key = NULL;
147376262Sgreen			}
147476262Sgreen		}
1475124211Sdes		if (sent)
1476124211Sdes			return (sent);
147776262Sgreen	}
1478124211Sdes	return (0);
147969587Sgreen}
148069587Sgreen
148169587Sgreen/*
148269587Sgreen * Send userauth request message specifying keyboard-interactive method.
148369587Sgreen */
148469587Sgreenint
148569587Sgreenuserauth_kbdint(Authctxt *authctxt)
148669587Sgreen{
148769587Sgreen	static int attempt = 0;
148869587Sgreen
148969587Sgreen	if (attempt++ >= options.number_of_password_prompts)
149069587Sgreen		return 0;
149192559Sdes	/* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */
149292559Sdes	if (attempt > 1 && !authctxt->info_req_seen) {
149392559Sdes		debug3("userauth_kbdint: disable: no info_req_seen");
149492559Sdes		dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL);
149592559Sdes		return 0;
149692559Sdes	}
149769587Sgreen
149869587Sgreen	debug2("userauth_kbdint");
149969587Sgreen	packet_start(SSH2_MSG_USERAUTH_REQUEST);
150069587Sgreen	packet_put_cstring(authctxt->server_user);
150169587Sgreen	packet_put_cstring(authctxt->service);
150269587Sgreen	packet_put_cstring(authctxt->method->name);
150369587Sgreen	packet_put_cstring("");					/* lang */
150469587Sgreen	packet_put_cstring(options.kbd_interactive_devices ?
150569587Sgreen	    options.kbd_interactive_devices : "");
150669587Sgreen	packet_send();
150769587Sgreen
150869587Sgreen	dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
150969587Sgreen	return 1;
151069587Sgreen}
151169587Sgreen
151269587Sgreen/*
151376262Sgreen * parse INFO_REQUEST, prompt user and send INFO_RESPONSE
151469587Sgreen */
1515295367Sdesint
151692559Sdesinput_userauth_info_req(int type, u_int32_t seq, void *ctxt)
151760573Skris{
151869587Sgreen	Authctxt *authctxt = ctxt;
151976262Sgreen	char *name, *inst, *lang, *prompt, *response;
152076262Sgreen	u_int num_prompts, i;
152169587Sgreen	int echo = 0;
152260573Skris
152369587Sgreen	debug2("input_userauth_info_req");
152469587Sgreen
152569587Sgreen	if (authctxt == NULL)
152669587Sgreen		fatal("input_userauth_info_req: no authentication context");
152769587Sgreen
152892559Sdes	authctxt->info_req_seen = 1;
152992559Sdes
153069587Sgreen	name = packet_get_string(NULL);
153169587Sgreen	inst = packet_get_string(NULL);
153269587Sgreen	lang = packet_get_string(NULL);
153369587Sgreen	if (strlen(name) > 0)
1534124211Sdes		logit("%s", name);
153569587Sgreen	if (strlen(inst) > 0)
1536124211Sdes		logit("%s", inst);
1537255767Sdes	free(name);
1538255767Sdes	free(inst);
1539255767Sdes	free(lang);
154069587Sgreen
154169587Sgreen	num_prompts = packet_get_int();
154269587Sgreen	/*
154369587Sgreen	 * Begin to build info response packet based on prompts requested.
154469587Sgreen	 * We commit to providing the correct number of responses, so if
154569587Sgreen	 * further on we run into a problem that prevents this, we have to
154669587Sgreen	 * be sure and clean this up and send a correct error response.
154769587Sgreen	 */
154869587Sgreen	packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE);
154969587Sgreen	packet_put_int(num_prompts);
155069587Sgreen
155192559Sdes	debug2("input_userauth_info_req: num_prompts %d", num_prompts);
155269587Sgreen	for (i = 0; i < num_prompts; i++) {
155369587Sgreen		prompt = packet_get_string(NULL);
155469587Sgreen		echo = packet_get_char();
155569587Sgreen
155692559Sdes		response = read_passphrase(prompt, echo ? RP_ECHO : 0);
155769587Sgreen
155893704Sdes		packet_put_cstring(response);
1559264377Sdes		explicit_bzero(response, strlen(response));
1560255767Sdes		free(response);
1561255767Sdes		free(prompt);
156269587Sgreen	}
156392559Sdes	packet_check_eom(); /* done with parsing incoming message. */
156469587Sgreen
156592559Sdes	packet_add_padding(64);
156660573Skris	packet_send();
1567295367Sdes	return 0;
156869587Sgreen}
156960573Skris
157098684Sdesstatic int
1571295367Sdesssh_keysign(struct sshkey *key, u_char **sigp, size_t *lenp,
1572295367Sdes    const u_char *data, size_t datalen)
157398684Sdes{
1574295367Sdes	struct sshbuf *b;
157598684Sdes	struct stat st;
157698684Sdes	pid_t pid;
1577295367Sdes	int i, r, to[2], from[2], status, sock = packet_get_connection_in();
1578295367Sdes	u_char rversion = 0, version = 2;
1579295367Sdes	void (*osigchld)(int);
158098684Sdes
1581295367Sdes	*sigp = NULL;
1582295367Sdes	*lenp = 0;
158398684Sdes
158498684Sdes	if (stat(_PATH_SSH_KEY_SIGN, &st) < 0) {
1585295367Sdes		error("%s: not installed: %s", __func__, strerror(errno));
158698684Sdes		return -1;
158798684Sdes	}
1588295367Sdes	if (fflush(stdout) != 0) {
1589295367Sdes		error("%s: fflush: %s", __func__, strerror(errno));
1590295367Sdes		return -1;
1591295367Sdes	}
159298684Sdes	if (pipe(to) < 0) {
1593295367Sdes		error("%s: pipe: %s", __func__, strerror(errno));
159498684Sdes		return -1;
159598684Sdes	}
159698684Sdes	if (pipe(from) < 0) {
1597295367Sdes		error("%s: pipe: %s", __func__, strerror(errno));
159898684Sdes		return -1;
159998684Sdes	}
160098684Sdes	if ((pid = fork()) < 0) {
1601295367Sdes		error("%s: fork: %s", __func__, strerror(errno));
160298684Sdes		return -1;
160398684Sdes	}
1604295367Sdes	osigchld = signal(SIGCHLD, SIG_DFL);
160598684Sdes	if (pid == 0) {
1606204917Sdes		/* keep the socket on exec */
1607295367Sdes		fcntl(sock, F_SETFD, 0);
1608162856Sdes		permanently_drop_suid(getuid());
160998684Sdes		close(from[0]);
161098684Sdes		if (dup2(from[1], STDOUT_FILENO) < 0)
1611295367Sdes			fatal("%s: dup2: %s", __func__, strerror(errno));
161298684Sdes		close(to[1]);
161398684Sdes		if (dup2(to[0], STDIN_FILENO) < 0)
1614295367Sdes			fatal("%s: dup2: %s", __func__, strerror(errno));
161598684Sdes		close(from[1]);
161698684Sdes		close(to[0]);
1617295367Sdes		/* Close everything but stdio and the socket */
1618295367Sdes		for (i = STDERR_FILENO + 1; i < sock; i++)
1619295367Sdes			close(i);
1620295367Sdes		closefrom(sock + 1);
1621295367Sdes		debug3("%s: [child] pid=%ld, exec %s",
1622295367Sdes		    __func__, (long)getpid(), _PATH_SSH_KEY_SIGN);
1623296853Sdes		execl(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *)NULL);
1624295367Sdes		fatal("%s: exec(%s): %s", __func__, _PATH_SSH_KEY_SIGN,
162598684Sdes		    strerror(errno));
162698684Sdes	}
162798684Sdes	close(from[1]);
162898684Sdes	close(to[0]);
162998684Sdes
1630295367Sdes	if ((b = sshbuf_new()) == NULL)
1631295367Sdes		fatal("%s: sshbuf_new failed", __func__);
1632295367Sdes	/* send # of sock, data to be signed */
1633295367Sdes	if ((r = sshbuf_put_u32(b, sock) != 0) ||
1634295367Sdes	    (r = sshbuf_put_string(b, data, datalen)) != 0)
1635295367Sdes		fatal("%s: buffer error: %s", __func__, ssh_err(r));
1636295367Sdes	if (ssh_msg_send(to[1], version, b) == -1)
1637295367Sdes		fatal("%s: couldn't send request", __func__);
1638295367Sdes	sshbuf_reset(b);
1639295367Sdes	r = ssh_msg_recv(from[0], b);
164098684Sdes	close(from[0]);
164198684Sdes	close(to[1]);
1642295367Sdes	if (r < 0) {
1643295367Sdes		error("%s: no reply", __func__);
1644295367Sdes		goto fail;
1645295367Sdes	}
164698684Sdes
1647295367Sdes	errno = 0;
1648295367Sdes	while (waitpid(pid, &status, 0) < 0) {
1649295367Sdes		if (errno != EINTR) {
1650295367Sdes			error("%s: waitpid %ld: %s",
1651295367Sdes			    __func__, (long)pid, strerror(errno));
1652295367Sdes			goto fail;
1653295367Sdes		}
1654295367Sdes	}
1655295367Sdes	if (!WIFEXITED(status)) {
1656295367Sdes		error("%s: exited abnormally", __func__);
1657295367Sdes		goto fail;
1658295367Sdes	}
1659295367Sdes	if (WEXITSTATUS(status) != 0) {
1660295367Sdes		error("%s: exited with status %d",
1661295367Sdes		    __func__, WEXITSTATUS(status));
1662295367Sdes		goto fail;
1663295367Sdes	}
1664295367Sdes	if ((r = sshbuf_get_u8(b, &rversion)) != 0) {
1665295367Sdes		error("%s: buffer error: %s", __func__, ssh_err(r));
1666295367Sdes		goto fail;
1667295367Sdes	}
1668295367Sdes	if (rversion != version) {
1669295367Sdes		error("%s: bad version", __func__);
1670295367Sdes		goto fail;
1671295367Sdes	}
1672295367Sdes	if ((r = sshbuf_get_string(b, sigp, lenp)) != 0) {
1673295367Sdes		error("%s: buffer error: %s", __func__, ssh_err(r));
1674295367Sdes fail:
1675295367Sdes		signal(SIGCHLD, osigchld);
1676295367Sdes		sshbuf_free(b);
167798684Sdes		return -1;
167898684Sdes	}
1679295367Sdes	signal(SIGCHLD, osigchld);
1680295367Sdes	sshbuf_free(b);
168198684Sdes
168298684Sdes	return 0;
168398684Sdes}
168498684Sdes
168576262Sgreenint
168676262Sgreenuserauth_hostbased(Authctxt *authctxt)
168769587Sgreen{
1688295367Sdes	struct ssh *ssh = active_state;
1689295367Sdes	struct sshkey *private = NULL;
1690295367Sdes	struct sshbuf *b = NULL;
169176262Sgreen	const char *service;
1692295367Sdes	u_char *sig = NULL, *keyblob = NULL;
1693295367Sdes	char *fp = NULL, *chost = NULL, *lname = NULL;
1694295367Sdes	size_t siglen = 0, keylen = 0;
1695295367Sdes	int i, r, success = 0;
169676262Sgreen
1697295367Sdes	if (authctxt->ktypes == NULL) {
1698295367Sdes		authctxt->oktypes = xstrdup(options.hostbased_key_types);
1699295367Sdes		authctxt->ktypes = authctxt->oktypes;
1700295367Sdes	}
1701295367Sdes
1702295367Sdes	/*
1703295367Sdes	 * Work through each listed type pattern in HostbasedKeyTypes,
1704295367Sdes	 * trying each hostkey that matches the type in turn.
1705295367Sdes	 */
1706295367Sdes	for (;;) {
1707295367Sdes		if (authctxt->active_ktype == NULL)
1708295367Sdes			authctxt->active_ktype = strsep(&authctxt->ktypes, ",");
1709295367Sdes		if (authctxt->active_ktype == NULL ||
1710295367Sdes		    *authctxt->active_ktype == '\0')
1711295367Sdes			break;
1712295367Sdes		debug3("%s: trying key type %s", __func__,
1713295367Sdes		    authctxt->active_ktype);
1714295367Sdes
1715295367Sdes		/* check for a useful key */
1716295367Sdes		private = NULL;
1717295367Sdes		for (i = 0; i < authctxt->sensitive->nkeys; i++) {
1718295367Sdes			if (authctxt->sensitive->keys[i] == NULL ||
1719295367Sdes			    authctxt->sensitive->keys[i]->type == KEY_RSA1 ||
1720295367Sdes			    authctxt->sensitive->keys[i]->type == KEY_UNSPEC)
1721295367Sdes				continue;
1722295367Sdes			if (match_pattern_list(
1723295367Sdes			    sshkey_ssh_name(authctxt->sensitive->keys[i]),
1724295367Sdes			    authctxt->active_ktype, 0) != 1)
1725295367Sdes				continue;
172676262Sgreen			/* we take and free the key */
1727295367Sdes			private = authctxt->sensitive->keys[i];
1728295367Sdes			authctxt->sensitive->keys[i] = NULL;
172976262Sgreen			break;
173076262Sgreen		}
1731295367Sdes		/* Found one */
1732295367Sdes		if (private != NULL)
1733295367Sdes			break;
1734295367Sdes		/* No more keys of this type; advance */
1735295367Sdes		authctxt->active_ktype = NULL;
173669587Sgreen	}
1737295367Sdes	if (private == NULL) {
1738295367Sdes		free(authctxt->oktypes);
1739295367Sdes		authctxt->oktypes = authctxt->ktypes = NULL;
1740295367Sdes		authctxt->active_ktype = NULL;
1741113911Sdes		debug("No more client hostkeys for hostbased authentication.");
1742295367Sdes		goto out;
174369587Sgreen	}
1744295367Sdes
1745295367Sdes	if ((fp = sshkey_fingerprint(private, options.fingerprint_hash,
1746295367Sdes	    SSH_FP_DEFAULT)) == NULL) {
1747295367Sdes		error("%s: sshkey_fingerprint failed", __func__);
1748295367Sdes		goto out;
174976262Sgreen	}
1750295367Sdes	debug("%s: trying hostkey %s %s",
1751295367Sdes	    __func__, sshkey_ssh_name(private), fp);
1752295367Sdes
175392559Sdes	/* figure out a name for the client host */
1754295367Sdes	if ((lname = get_local_name(packet_get_connection_in())) == NULL) {
1755295367Sdes		error("%s: cannot get local ipaddr/name", __func__);
1756295367Sdes		goto out;
175792559Sdes	}
175892559Sdes
1759295367Sdes	/* XXX sshbuf_put_stringf? */
1760295367Sdes	xasprintf(&chost, "%s.", lname);
1761295367Sdes	debug2("%s: chost %s", __func__, chost);
1762295367Sdes
176376262Sgreen	service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" :
176476262Sgreen	    authctxt->service;
1765295367Sdes
176676262Sgreen	/* construct data */
1767295367Sdes	if ((b = sshbuf_new()) == NULL) {
1768295367Sdes		error("%s: sshbuf_new failed", __func__);
1769295367Sdes		goto out;
1770295367Sdes	}
1771295367Sdes	if ((r = sshkey_to_blob(private, &keyblob, &keylen)) != 0) {
1772295367Sdes		error("%s: sshkey_to_blob: %s", __func__, ssh_err(r));
1773295367Sdes		goto out;
1774295367Sdes	}
1775295367Sdes	if ((r = sshbuf_put_string(b, session_id2, session_id2_len)) != 0 ||
1776295367Sdes	    (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1777295367Sdes	    (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 ||
1778295367Sdes	    (r = sshbuf_put_cstring(b, service)) != 0 ||
1779295367Sdes	    (r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 ||
1780295367Sdes	    (r = sshbuf_put_cstring(b, key_ssh_name(private))) != 0 ||
1781295367Sdes	    (r = sshbuf_put_string(b, keyblob, keylen)) != 0 ||
1782295367Sdes	    (r = sshbuf_put_cstring(b, chost)) != 0 ||
1783295367Sdes	    (r = sshbuf_put_cstring(b, authctxt->local_user)) != 0) {
1784295367Sdes		error("%s: buffer error: %s", __func__, ssh_err(r));
1785295367Sdes		goto out;
1786295367Sdes	}
1787295367Sdes
178876262Sgreen#ifdef DEBUG_PK
1789295367Sdes	sshbuf_dump(b, stderr);
179076262Sgreen#endif
1791295367Sdes	if (authctxt->sensitive->external_keysign)
1792295367Sdes		r = ssh_keysign(private, &sig, &siglen,
1793295367Sdes		    sshbuf_ptr(b), sshbuf_len(b));
1794295367Sdes	else if ((r = sshkey_sign(private, &sig, &siglen,
1795296853Sdes	    sshbuf_ptr(b), sshbuf_len(b), NULL, datafellows)) != 0)
1796295367Sdes		debug("%s: sshkey_sign: %s", __func__, ssh_err(r));
1797295367Sdes	if (r != 0) {
1798295367Sdes		error("sign using hostkey %s %s failed",
1799295367Sdes		    sshkey_ssh_name(private), fp);
1800295367Sdes		goto out;
180176262Sgreen	}
1802295367Sdes	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1803295367Sdes	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1804295367Sdes	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1805295367Sdes	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1806295367Sdes	    (r = sshpkt_put_cstring(ssh, key_ssh_name(private))) != 0 ||
1807295367Sdes	    (r = sshpkt_put_string(ssh, keyblob, keylen)) != 0 ||
1808295367Sdes	    (r = sshpkt_put_cstring(ssh, chost)) != 0 ||
1809295367Sdes	    (r = sshpkt_put_cstring(ssh, authctxt->local_user)) != 0 ||
1810295367Sdes	    (r = sshpkt_put_string(ssh, sig, siglen)) != 0 ||
1811295367Sdes	    (r = sshpkt_send(ssh)) != 0) {
1812295367Sdes		error("%s: packet error: %s", __func__, ssh_err(r));
1813295367Sdes		goto out;
1814295367Sdes	}
1815295367Sdes	success = 1;
1816295367Sdes
1817295367Sdes out:
1818295367Sdes	if (sig != NULL) {
1819295367Sdes		explicit_bzero(sig, siglen);
1820295367Sdes		free(sig);
1821295367Sdes	}
1822295367Sdes	free(keyblob);
1823295367Sdes	free(lname);
1824295367Sdes	free(fp);
1825255767Sdes	free(chost);
1826295367Sdes	sshkey_free(private);
1827295367Sdes	sshbuf_free(b);
182876262Sgreen
1829295367Sdes	return success;
183069587Sgreen}
183169587Sgreen
183276262Sgreen/* find auth method */
183376262Sgreen
183469587Sgreen/*
183569587Sgreen * given auth method name, if configurable options permit this method fill
183669587Sgreen * in auth_ident field and return true, otherwise return false.
183769587Sgreen */
183892559Sdesstatic int
183969587Sgreenauthmethod_is_enabled(Authmethod *method)
184069587Sgreen{
184169587Sgreen	if (method == NULL)
184269587Sgreen		return 0;
184369587Sgreen	/* return false if options indicate this method is disabled */
184469587Sgreen	if  (method->enabled == NULL || *method->enabled == 0)
184569587Sgreen		return 0;
184669587Sgreen	/* return false if batch mode is enabled but method needs interactive mode */
184769587Sgreen	if  (method->batch_flag != NULL && *method->batch_flag != 0)
184869587Sgreen		return 0;
184969587Sgreen	return 1;
185069587Sgreen}
185169587Sgreen
185292559Sdesstatic Authmethod *
185369587Sgreenauthmethod_lookup(const char *name)
185469587Sgreen{
185569587Sgreen	Authmethod *method = NULL;
185669587Sgreen	if (name != NULL)
185769587Sgreen		for (method = authmethods; method->name != NULL; method++)
185869587Sgreen			if (strcmp(name, method->name) == 0)
185969587Sgreen				return method;
186069587Sgreen	debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
186169587Sgreen	return NULL;
186269587Sgreen}
186369587Sgreen
186476262Sgreen/* XXX internal state */
186576262Sgreenstatic Authmethod *current = NULL;
186676262Sgreenstatic char *supported = NULL;
186776262Sgreenstatic char *preferred = NULL;
186899063Sdes
186969587Sgreen/*
187069587Sgreen * Given the authentication method list sent by the server, return the
187169587Sgreen * next method we should try.  If the server initially sends a nil list,
187276262Sgreen * use a built-in default list.
187376262Sgreen */
187492559Sdesstatic Authmethod *
187569587Sgreenauthmethod_get(char *authlist)
187669587Sgreen{
187776262Sgreen	char *name = NULL;
187892559Sdes	u_int next;
187976262Sgreen
188069587Sgreen	/* Use a suitable default if we're passed a nil list.  */
188169587Sgreen	if (authlist == NULL || strlen(authlist) == 0)
188276262Sgreen		authlist = options.preferred_authentications;
188369587Sgreen
188476262Sgreen	if (supported == NULL || strcmp(authlist, supported) != 0) {
188576262Sgreen		debug3("start over, passed a different list %s", authlist);
1886255767Sdes		free(supported);
188776262Sgreen		supported = xstrdup(authlist);
188876262Sgreen		preferred = options.preferred_authentications;
188976262Sgreen		debug3("preferred %s", preferred);
189076262Sgreen		current = NULL;
189176262Sgreen	} else if (current != NULL && authmethod_is_enabled(current))
189276262Sgreen		return current;
189360573Skris
189476262Sgreen	for (;;) {
189576262Sgreen		if ((name = match_list(preferred, supported, &next)) == NULL) {
1896113911Sdes			debug("No more authentication methods to try.");
189776262Sgreen			current = NULL;
189876262Sgreen			return NULL;
189976262Sgreen		}
190076262Sgreen		preferred += next;
190169587Sgreen		debug3("authmethod_lookup %s", name);
190276262Sgreen		debug3("remaining preferred: %s", preferred);
190376262Sgreen		if ((current = authmethod_lookup(name)) != NULL &&
190476262Sgreen		    authmethod_is_enabled(current)) {
190569587Sgreen			debug3("authmethod_is_enabled %s", name);
1906113911Sdes			debug("Next authentication method: %s", name);
1907255767Sdes			free(name);
190876262Sgreen			return current;
190960573Skris		}
1910255767Sdes		free(name);
191160573Skris	}
191276262Sgreen}
191369587Sgreen
191492559Sdesstatic char *
191576262Sgreenauthmethods_get(void)
191676262Sgreen{
191776262Sgreen	Authmethod *method = NULL;
191892559Sdes	Buffer b;
191992559Sdes	char *list;
192069587Sgreen
192192559Sdes	buffer_init(&b);
192276262Sgreen	for (method = authmethods; method->name != NULL; method++) {
192376262Sgreen		if (authmethod_is_enabled(method)) {
192492559Sdes			if (buffer_len(&b) > 0)
192592559Sdes				buffer_append(&b, ",", 1);
192692559Sdes			buffer_append(&b, method->name, strlen(method->name));
192776262Sgreen		}
192876262Sgreen	}
192992559Sdes	buffer_append(&b, "\0", 1);
193092559Sdes	list = xstrdup(buffer_ptr(&b));
193192559Sdes	buffer_free(&b);
193292559Sdes	return list;
193360573Skris}
1934192595Sdes
1935