1323136Sdes/* $OpenBSD: sshconnect2.c,v 1.255 2017/03/11 23:40:26 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"
64294328Sdes#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"
73294332Sdes#include "ssherr.h"
74323129Sdes#include "utf8.h"
7560573Skris
76124211Sdes#ifdef GSSAPI
77124211Sdes#include "ssh-gss.h"
78124211Sdes#endif
79124211Sdes
8060573Skris/* import */
8160573Skrisextern char *client_version_string;
8260573Skrisextern char *server_version_string;
8360573Skrisextern Options options;
8460573Skris
8560573Skris/*
8660573Skris * SSH2 key exchange
8760573Skris */
8860573Skris
8976262Sgreenu_char *session_id2 = NULL;
90124211Sdesu_int session_id2_len = 0;
9160573Skris
9276262Sgreenchar *xxx_host;
9376262Sgreenstruct sockaddr *xxx_hostaddr;
9476262Sgreen
9592559Sdesstatic int
96294332Sdesverify_host_key_callback(Key *hostkey, struct ssh *ssh)
9776262Sgreen{
9892559Sdes	if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) == -1)
9992559Sdes		fatal("Host key verification failed.");
10076262Sgreen	return 0;
10176262Sgreen}
10276262Sgreen
103221420Sdesstatic char *
104221420Sdesorder_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port)
105221420Sdes{
106221420Sdes	char *oavail, *avail, *first, *last, *alg, *hostname, *ret;
107221420Sdes	size_t maxlen;
108221420Sdes	struct hostkeys *hostkeys;
109221420Sdes	int ktype;
110226046Sdes	u_int i;
111221420Sdes
112221420Sdes	/* Find all hostkeys for this hostname */
113221420Sdes	get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL);
114221420Sdes	hostkeys = init_hostkeys();
115226046Sdes	for (i = 0; i < options.num_user_hostfiles; i++)
116226046Sdes		load_hostkeys(hostkeys, hostname, options.user_hostfiles[i]);
117226046Sdes	for (i = 0; i < options.num_system_hostfiles; i++)
118226046Sdes		load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]);
119221420Sdes
120221420Sdes	oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG);
121221420Sdes	maxlen = strlen(avail) + 1;
122221420Sdes	first = xmalloc(maxlen);
123221420Sdes	last = xmalloc(maxlen);
124221420Sdes	*first = *last = '\0';
125221420Sdes
126221420Sdes#define ALG_APPEND(to, from) \
127221420Sdes	do { \
128221420Sdes		if (*to != '\0') \
129221420Sdes			strlcat(to, ",", maxlen); \
130221420Sdes		strlcat(to, from, maxlen); \
131221420Sdes	} while (0)
132221420Sdes
133221420Sdes	while ((alg = strsep(&avail, ",")) && *alg != '\0') {
134294332Sdes		if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC)
135221420Sdes			fatal("%s: unknown alg %s", __func__, alg);
136221420Sdes		if (lookup_key_in_hostkeys_by_type(hostkeys,
137294332Sdes		    sshkey_type_plain(ktype), NULL))
138221420Sdes			ALG_APPEND(first, alg);
139221420Sdes		else
140221420Sdes			ALG_APPEND(last, alg);
141221420Sdes	}
142221420Sdes#undef ALG_APPEND
143294332Sdes	xasprintf(&ret, "%s%s%s", first,
144294332Sdes	    (*first == '\0' || *last == '\0') ? "" : ",", last);
145221420Sdes	if (*first != '\0')
146221420Sdes		debug3("%s: prefer hostkeyalgs: %s", __func__, first);
147221420Sdes
148255767Sdes	free(first);
149255767Sdes	free(last);
150255767Sdes	free(hostname);
151255767Sdes	free(oavail);
152221420Sdes	free_hostkeys(hostkeys);
153221420Sdes
154221420Sdes	return ret;
155221420Sdes}
156221420Sdes
15760573Skrisvoid
158221420Sdesssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
15960573Skris{
160294328Sdes	char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
161296633Sdes	char *s;
162294332Sdes	struct kex *kex;
163294332Sdes	int r;
16469587Sgreen
16576262Sgreen	xxx_host = host;
16676262Sgreen	xxx_hostaddr = hostaddr;
16776262Sgreen
168296633Sdes	if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL)
169296633Sdes		fatal("%s: kex_names_cat", __func__);
170296633Sdes	myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s);
17176262Sgreen	myproposal[PROPOSAL_ENC_ALGS_CTOS] =
172294464Sdes	    compat_cipher_proposal(options.ciphers);
17376262Sgreen	myproposal[PROPOSAL_ENC_ALGS_STOC] =
174294464Sdes	    compat_cipher_proposal(options.ciphers);
175323129Sdes	myproposal[PROPOSAL_COMP_ALGS_CTOS] =
176323129Sdes	    myproposal[PROPOSAL_COMP_ALGS_STOC] = options.compression ?
177323129Sdes	    "zlib@openssh.com,zlib,none" : "none,zlib@openssh.com,zlib";
178294464Sdes	myproposal[PROPOSAL_MAC_ALGS_CTOS] =
179294464Sdes	    myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
180294464Sdes	if (options.hostkeyalgorithms != NULL) {
181294464Sdes		if (kex_assemble_names(KEX_DEFAULT_PK_ALG,
182294464Sdes		    &options.hostkeyalgorithms) != 0)
183294464Sdes			fatal("%s: kex_assemble_namelist", __func__);
18492559Sdes		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
185261320Sdes		    compat_pkalg_proposal(options.hostkeyalgorithms);
186294464Sdes	} else {
187294464Sdes		/* Enforce default */
188294464Sdes		options.hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG);
189221420Sdes		/* Prefer algorithms that we already have keys for */
190221420Sdes		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
191261320Sdes		    compat_pkalg_proposal(
192261320Sdes		    order_hostkeyalgs(host, hostaddr, port));
193221420Sdes	}
19469587Sgreen
195255767Sdes	if (options.rekey_limit || options.rekey_interval)
196323136Sdes		packet_set_rekey_limits(options.rekey_limit,
197323136Sdes		    options.rekey_interval);
198124211Sdes
19976262Sgreen	/* start key exchange */
200294332Sdes	if ((r = kex_setup(active_state, myproposal)) != 0)
201294332Sdes		fatal("kex_setup: %s", ssh_err(r));
202294332Sdes	kex = active_state->kex;
203294328Sdes#ifdef WITH_OPENSSL
204113911Sdes	kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
205137019Sdes	kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
206323129Sdes	kex->kex[KEX_DH_GRP14_SHA256] = kexdh_client;
207323129Sdes	kex->kex[KEX_DH_GRP16_SHA512] = kexdh_client;
208323129Sdes	kex->kex[KEX_DH_GRP18_SHA512] = kexdh_client;
209113911Sdes	kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
210162856Sdes	kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
211294332Sdes# ifdef OPENSSL_HAS_ECC
212221420Sdes	kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
213294332Sdes# endif
214294328Sdes#endif
215261320Sdes	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
220294332Sdes	dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
22169587Sgreen
222296633Sdes	/* remove ext-info from the KEX proposals for rekeying */
223296633Sdes	myproposal[PROPOSAL_KEX_ALGS] =
224296633Sdes	    compat_kex_proposal(options.kex_algorithms);
225296633Sdes	if ((r = kex_prop2buf(kex->my, myproposal)) != 0)
226296633Sdes		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
244294332Sdestypedef struct cauthctxt Authctxt;
245294332Sdestypedef struct cauthmethod Authmethod;
246124211Sdestypedef struct identity Identity;
247124211Sdestypedef struct idlist Idlist;
24869587Sgreen
249124211Sdesstruct identity {
250124211Sdes	TAILQ_ENTRY(identity) next;
251294332Sdes	int	agent_fd;		/* >=0 if agent supports key */
252294332Sdes	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
260294332Sdesstruct cauthctxt {
26169587Sgreen	const char *server_user;
26276262Sgreen	const char *local_user;
26369587Sgreen	const char *host;
26469587Sgreen	const char *service;
265294332Sdes	struct cauthmethod *method;
266215116Sdes	sig_atomic_t success;
26776262Sgreen	char *authlist;
268294332Sdes	int attempt;
26976262Sgreen	/* pubkey */
270294332Sdes	struct idlist keys;
271294332Sdes	int agent_fd;
27276262Sgreen	/* hostbased */
27398684Sdes	Sensitive *sensitive;
274294332Sdes	char *oktypes, *ktypes;
275294332Sdes	const char *active_ktype;
27692559Sdes	/* kbd-interactive */
27792559Sdes	int info_req_seen;
278124211Sdes	/* generic */
279124211Sdes	void *methoddata;
28069587Sgreen};
281294332Sdes
282294332Sdesstruct 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
290296633Sdesint	input_userauth_service_accept(int, u_int32_t, void *);
291296633Sdesint	input_userauth_ext_info(int, u_int32_t, void *);
292294332Sdesint	input_userauth_success(int, u_int32_t, void *);
293294332Sdesint	input_userauth_success_unexpected(int, u_int32_t, void *);
294294332Sdesint	input_userauth_failure(int, u_int32_t, void *);
295294332Sdesint	input_userauth_banner(int, u_int32_t, void *);
296294332Sdesint	input_userauth_error(int, u_int32_t, void *);
297294332Sdesint	input_userauth_info_req(int, u_int32_t, void *);
298294332Sdesint	input_userauth_pk_ok(int, u_int32_t, void *);
299294332Sdesint	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);
309294332Sdesint	input_gssapi_response(int type, u_int32_t, void *);
310294332Sdesint	input_gssapi_token(int type, u_int32_t, void *);
311294332Sdesint	input_gssapi_hash(int type, u_int32_t, void *);
312294332Sdesint	input_gssapi_error(int, u_int32_t, void *);
313294332Sdesint	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 *);
321323134Sdesstatic void pubkey_reset(Authctxt *);
322296633Sdesstatic Key *load_identity_file(Identity *);
32376262Sgreen
32492559Sdesstatic Authmethod *authmethod_get(char *authlist);
32592559Sdesstatic Authmethod *authmethod_lookup(const char *name);
32692559Sdesstatic char *authmethods_get(void);
32769587Sgreen
32869587SgreenAuthmethod authmethods[] = {
329124211Sdes#ifdef GSSAPI
330126277Sdes	{"gssapi-with-mic",
331124211Sdes		userauth_gssapi,
332192595Sdes		NULL,
333124211Sdes		&options.gss_authentication,
334124211Sdes		NULL},
335124211Sdes#endif
33692559Sdes	{"hostbased",
33792559Sdes		userauth_hostbased,
338192595Sdes		NULL,
33992559Sdes		&options.hostbased_authentication,
34092559Sdes		NULL},
34169587Sgreen	{"publickey",
34269587Sgreen		userauth_pubkey,
343192595Sdes		NULL,
34476262Sgreen		&options.pubkey_authentication,
34569587Sgreen		NULL},
34692559Sdes	{"keyboard-interactive",
34792559Sdes		userauth_kbdint,
348192595Sdes		NULL,
34992559Sdes		&options.kbd_interactive_authentication,
35092559Sdes		&options.batch_mode},
35169587Sgreen	{"password",
35269587Sgreen		userauth_passwd,
353192595Sdes		NULL,
35469587Sgreen		&options.password_authentication,
35569587Sgreen		&options.batch_mode},
35669587Sgreen	{"none",
35769587Sgreen		userauth_none,
35869587Sgreen		NULL,
359192595Sdes		NULL,
36069587Sgreen		NULL},
361192595Sdes	{NULL, NULL, NULL, NULL, NULL}
36269587Sgreen};
36369587Sgreen
36469587Sgreenvoid
36576262Sgreenssh_userauth2(const char *local_user, const char *server_user, char *host,
36698684Sdes    Sensitive *sensitive)
36769587Sgreen{
368296633Sdes	struct ssh *ssh = active_state;
36969587Sgreen	Authctxt authctxt;
370296633Sdes	int r;
37169587Sgreen
37292559Sdes	if (options.challenge_response_authentication)
37376262Sgreen		options.kbd_interactive_authentication = 1;
37476262Sgreen	if (options.preferred_authentications == NULL)
37576262Sgreen		options.preferred_authentications = authmethods_get();
37676262Sgreen
37769587Sgreen	/* setup authentication context */
37892559Sdes	memset(&authctxt, 0, sizeof(authctxt));
379124211Sdes	pubkey_prepare(&authctxt);
38069587Sgreen	authctxt.server_user = server_user;
38176262Sgreen	authctxt.local_user = local_user;
38269587Sgreen	authctxt.host = host;
38369587Sgreen	authctxt.service = "ssh-connection";		/* service name */
38469587Sgreen	authctxt.success = 0;
38569587Sgreen	authctxt.method = authmethod_lookup("none");
38676262Sgreen	authctxt.authlist = NULL;
387124211Sdes	authctxt.methoddata = NULL;
38898684Sdes	authctxt.sensitive = sensitive;
389294332Sdes	authctxt.active_ktype = authctxt.oktypes = authctxt.ktypes = NULL;
39092559Sdes	authctxt.info_req_seen = 0;
391294332Sdes	authctxt.agent_fd = -1;
39269587Sgreen	if (authctxt.method == NULL)
39369587Sgreen		fatal("ssh_userauth2: internal error: cannot send userauth none request");
39469587Sgreen
395296633Sdes	if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 ||
396296633Sdes	    (r = sshpkt_put_cstring(ssh, "ssh-userauth")) != 0 ||
397296633Sdes	    (r = sshpkt_send(ssh)) != 0)
398296633Sdes		fatal("%s: %s", __func__, ssh_err(r));
39969587Sgreen
400296633Sdes	ssh_dispatch_init(ssh, &input_userauth_error);
401296633Sdes	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info);
402296633Sdes	ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept);
403296633Sdes	ssh_dispatch_run(ssh, DISPATCH_BLOCK, &authctxt.success, &authctxt);	/* loop until success */
40469587Sgreen
405124211Sdes	pubkey_cleanup(&authctxt);
406296633Sdes	ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL);
40769587Sgreen
408323134Sdes	if (!authctxt.success)
409323134Sdes		fatal("Authentication failed.");
410113911Sdes	debug("Authentication succeeded (%s).", authctxt.method->name);
41169587Sgreen}
412124211Sdes
413296633Sdes/* ARGSUSED */
414296633Sdesint
415296633Sdesinput_userauth_service_accept(int type, u_int32_t seqnr, void *ctxt)
416296633Sdes{
417296633Sdes	Authctxt *authctxt = ctxt;
418296633Sdes	struct ssh *ssh = active_state;
419296633Sdes	int r;
420296633Sdes
421296633Sdes	if (ssh_packet_remaining(ssh) > 0) {
422296633Sdes		char *reply;
423296633Sdes
424296633Sdes		if ((r = sshpkt_get_cstring(ssh, &reply, NULL)) != 0)
425296633Sdes			goto out;
426296633Sdes		debug2("service_accept: %s", reply);
427296633Sdes		free(reply);
428296633Sdes	} else {
429296633Sdes		debug2("buggy server: service_accept w/o service");
430296633Sdes	}
431296633Sdes	if ((r = sshpkt_get_end(ssh)) != 0)
432296633Sdes		goto out;
433296633Sdes	debug("SSH2_MSG_SERVICE_ACCEPT received");
434296633Sdes
435296633Sdes	/* initial userauth request */
436296633Sdes	userauth_none(authctxt);
437296633Sdes
438296633Sdes	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_error);
439296633Sdes	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
440296633Sdes	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
441296633Sdes	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner);
442296633Sdes	r = 0;
443296633Sdes out:
444296633Sdes	return r;
445296633Sdes}
446296633Sdes
447296633Sdes/* ARGSUSED */
448296633Sdesint
449296633Sdesinput_userauth_ext_info(int type, u_int32_t seqnr, void *ctxt)
450296633Sdes{
451296633Sdes	return kex_input_ext_info(type, seqnr, active_state);
452296633Sdes}
453296633Sdes
45469587Sgreenvoid
45576262Sgreenuserauth(Authctxt *authctxt, char *authlist)
45676262Sgreen{
457192595Sdes	if (authctxt->method != NULL && authctxt->method->cleanup != NULL)
458192595Sdes		authctxt->method->cleanup(authctxt);
459192595Sdes
460255767Sdes	free(authctxt->methoddata);
461255767Sdes	authctxt->methoddata = NULL;
46276262Sgreen	if (authlist == NULL) {
46376262Sgreen		authlist = authctxt->authlist;
46476262Sgreen	} else {
465255767Sdes		free(authctxt->authlist);
46676262Sgreen		authctxt->authlist = authlist;
46776262Sgreen	}
46876262Sgreen	for (;;) {
46976262Sgreen		Authmethod *method = authmethod_get(authlist);
47076262Sgreen		if (method == NULL)
47176262Sgreen			fatal("Permission denied (%s).", authlist);
47276262Sgreen		authctxt->method = method;
473124211Sdes
474124211Sdes		/* reset the per method handler */
475124211Sdes		dispatch_range(SSH2_MSG_USERAUTH_PER_METHOD_MIN,
476124211Sdes		    SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL);
477124211Sdes
478124211Sdes		/* and try new method */
47976262Sgreen		if (method->userauth(authctxt) != 0) {
48076262Sgreen			debug2("we sent a %s packet, wait for reply", method->name);
48176262Sgreen			break;
48276262Sgreen		} else {
48376262Sgreen			debug2("we did not send a packet, disable method");
48476262Sgreen			method->enabled = NULL;
48576262Sgreen		}
48676262Sgreen	}
48776262Sgreen}
48899063Sdes
489192595Sdes/* ARGSUSED */
490294332Sdesint
49192559Sdesinput_userauth_error(int type, u_int32_t seq, void *ctxt)
49269587Sgreen{
49376262Sgreen	fatal("input_userauth_error: bad message during authentication: "
494149753Sdes	    "type %d", type);
495294332Sdes	return 0;
49669587Sgreen}
49799063Sdes
498192595Sdes/* ARGSUSED */
499294332Sdesint
50092559Sdesinput_userauth_banner(int type, u_int32_t seq, void *ctxt)
50176262Sgreen{
502323129Sdes	char *msg, *lang;
503181111Sdes	u_int len;
504126277Sdes
505323129Sdes	debug3("%s", __func__);
506323129Sdes	msg = packet_get_string(&len);
50776262Sgreen	lang = packet_get_string(NULL);
508323129Sdes	if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO)
509323129Sdes		fmprintf(stderr, "%s", msg);
510323129Sdes	free(msg);
511255767Sdes	free(lang);
512294332Sdes	return 0;
51376262Sgreen}
51499063Sdes
515192595Sdes/* ARGSUSED */
516294332Sdesint
51792559Sdesinput_userauth_success(int type, u_int32_t seq, void *ctxt)
51869587Sgreen{
51969587Sgreen	Authctxt *authctxt = ctxt;
520204917Sdes
52169587Sgreen	if (authctxt == NULL)
52269587Sgreen		fatal("input_userauth_success: no authentication context");
523255767Sdes	free(authctxt->authlist);
524255767Sdes	authctxt->authlist = NULL;
525204917Sdes	if (authctxt->method != NULL && authctxt->method->cleanup != NULL)
526204917Sdes		authctxt->method->cleanup(authctxt);
527255767Sdes	free(authctxt->methoddata);
528255767Sdes	authctxt->methoddata = NULL;
52969587Sgreen	authctxt->success = 1;			/* break out */
530294332Sdes	return 0;
53169587Sgreen}
53299063Sdes
533294332Sdesint
534204917Sdesinput_userauth_success_unexpected(int type, u_int32_t seq, void *ctxt)
535204917Sdes{
536204917Sdes	Authctxt *authctxt = ctxt;
537204917Sdes
538204917Sdes	if (authctxt == NULL)
539204917Sdes		fatal("%s: no authentication context", __func__);
540204917Sdes
541204917Sdes	fatal("Unexpected authentication success during %s.",
542204917Sdes	    authctxt->method->name);
543294332Sdes	return 0;
544204917Sdes}
545204917Sdes
546192595Sdes/* ARGSUSED */
547294332Sdesint
54892559Sdesinput_userauth_failure(int type, u_int32_t seq, void *ctxt)
54969587Sgreen{
55069587Sgreen	Authctxt *authctxt = ctxt;
55169587Sgreen	char *authlist = NULL;
55269587Sgreen	int partial;
55369587Sgreen
55469587Sgreen	if (authctxt == NULL)
55569587Sgreen		fatal("input_userauth_failure: no authentication context");
55669587Sgreen
55769587Sgreen	authlist = packet_get_string(NULL);
55869587Sgreen	partial = packet_get_char();
55992559Sdes	packet_check_eom();
56069587Sgreen
561255767Sdes	if (partial != 0) {
562323129Sdes		verbose("Authenticated with partial success.");
563255767Sdes		/* reset state */
564323134Sdes		pubkey_reset(authctxt);
565255767Sdes	}
566113911Sdes	debug("Authentications that can continue: %s", authlist);
56769587Sgreen
56876262Sgreen	userauth(authctxt, authlist);
569294332Sdes	return 0;
57076262Sgreen}
571192595Sdes
572192595Sdes/* ARGSUSED */
573294332Sdesint
57492559Sdesinput_userauth_pk_ok(int type, u_int32_t seq, void *ctxt)
57576262Sgreen{
57676262Sgreen	Authctxt *authctxt = ctxt;
57776262Sgreen	Key *key = NULL;
578124211Sdes	Identity *id = NULL;
57976262Sgreen	Buffer b;
58092559Sdes	int pktype, sent = 0;
58192559Sdes	u_int alen, blen;
58292559Sdes	char *pkalg, *fp;
58392559Sdes	u_char *pkblob;
58476262Sgreen
58576262Sgreen	if (authctxt == NULL)
58676262Sgreen		fatal("input_userauth_pk_ok: no authentication context");
58776262Sgreen	if (datafellows & SSH_BUG_PKOK) {
58876262Sgreen		/* this is similar to SSH_BUG_PKAUTH */
58976262Sgreen		debug2("input_userauth_pk_ok: SSH_BUG_PKOK");
59076262Sgreen		pkblob = packet_get_string(&blen);
59176262Sgreen		buffer_init(&b);
59276262Sgreen		buffer_append(&b, pkblob, blen);
59376262Sgreen		pkalg = buffer_get_string(&b, &alen);
59476262Sgreen		buffer_free(&b);
59576262Sgreen	} else {
59676262Sgreen		pkalg = packet_get_string(&alen);
59776262Sgreen		pkblob = packet_get_string(&blen);
59876262Sgreen	}
59992559Sdes	packet_check_eom();
60076262Sgreen
601124211Sdes	debug("Server accepts key: pkalg %s blen %u", pkalg, blen);
60276262Sgreen
603124211Sdes	if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) {
604124211Sdes		debug("unknown pkalg %s", pkalg);
605124211Sdes		goto done;
606124211Sdes	}
607124211Sdes	if ((key = key_from_blob(pkblob, blen)) == NULL) {
608124211Sdes		debug("no key from blob. pkalg %s", pkalg);
609124211Sdes		goto done;
610124211Sdes	}
611124211Sdes	if (key->type != pktype) {
612124211Sdes		error("input_userauth_pk_ok: type mismatch "
613124211Sdes		    "for decoded key (received %d, expected %d)",
614124211Sdes		    key->type, pktype);
615124211Sdes		goto done;
616124211Sdes	}
617294332Sdes	if ((fp = sshkey_fingerprint(key, options.fingerprint_hash,
618294332Sdes	    SSH_FP_DEFAULT)) == NULL)
619294332Sdes		goto done;
620124211Sdes	debug2("input_userauth_pk_ok: fp %s", fp);
621255767Sdes	free(fp);
622124211Sdes
623126277Sdes	/*
624126277Sdes	 * search keys in the reverse order, because last candidate has been
625126277Sdes	 * moved to the end of the queue.  this also avoids confusion by
626126277Sdes	 * duplicate keys
627126277Sdes	 */
628137019Sdes	TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) {
629124211Sdes		if (key_equal(key, id->key)) {
630124211Sdes			sent = sign_and_send_pubkey(authctxt, id);
63169587Sgreen			break;
63269587Sgreen		}
633124211Sdes	}
634124211Sdesdone:
63576262Sgreen	if (key != NULL)
63676262Sgreen		key_free(key);
637255767Sdes	free(pkalg);
638255767Sdes	free(pkblob);
63976262Sgreen
640106130Sdes	/* try another method if we did not send a packet */
64176262Sgreen	if (sent == 0)
64276262Sgreen		userauth(authctxt, NULL);
643294332Sdes	return 0;
644124211Sdes}
64576262Sgreen
646124211Sdes#ifdef GSSAPI
647126277Sdesint
648124211Sdesuserauth_gssapi(Authctxt *authctxt)
649124211Sdes{
650124211Sdes	Gssctxt *gssctxt = NULL;
651126277Sdes	static gss_OID_set gss_supported = NULL;
652149753Sdes	static u_int mech = 0;
653124211Sdes	OM_uint32 min;
654124211Sdes	int ok = 0;
655124211Sdes
656124211Sdes	/* Try one GSSAPI method at a time, rather than sending them all at
657124211Sdes	 * once. */
658124211Sdes
659126277Sdes	if (gss_supported == NULL)
660126277Sdes		gss_indicate_mechs(&min, &gss_supported);
661124211Sdes
662124211Sdes	/* Check to see if the mechanism is usable before we offer it */
663126277Sdes	while (mech < gss_supported->count && !ok) {
664124211Sdes		/* My DER encoding requires length<128 */
665126277Sdes		if (gss_supported->elements[mech].length < 128 &&
666162856Sdes		    ssh_gssapi_check_mechanism(&gssctxt,
667162856Sdes		    &gss_supported->elements[mech], authctxt->host)) {
668124211Sdes			ok = 1; /* Mechanism works */
669124211Sdes		} else {
670124211Sdes			mech++;
671124211Sdes		}
672124211Sdes	}
673124211Sdes
674149753Sdes	if (!ok)
675149753Sdes		return 0;
676124211Sdes
677124211Sdes	authctxt->methoddata=(void *)gssctxt;
678124211Sdes
679124211Sdes	packet_start(SSH2_MSG_USERAUTH_REQUEST);
680124211Sdes	packet_put_cstring(authctxt->server_user);
681124211Sdes	packet_put_cstring(authctxt->service);
682124211Sdes	packet_put_cstring(authctxt->method->name);
683124211Sdes
684124211Sdes	packet_put_int(1);
685124211Sdes
686126277Sdes	packet_put_int((gss_supported->elements[mech].length) + 2);
687126277Sdes	packet_put_char(SSH_GSS_OIDTYPE);
688126277Sdes	packet_put_char(gss_supported->elements[mech].length);
689126277Sdes	packet_put_raw(gss_supported->elements[mech].elements,
690126277Sdes	    gss_supported->elements[mech].length);
691124211Sdes
692124211Sdes	packet_send();
693124211Sdes
694124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response);
695124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
696124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error);
697124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
698124211Sdes
699124211Sdes	mech++; /* Move along to next candidate */
700124211Sdes
701124211Sdes	return 1;
70269587Sgreen}
70369587Sgreen
704126277Sdesstatic OM_uint32
705126277Sdesprocess_gssapi_token(void *ctxt, gss_buffer_t recv_tok)
706126277Sdes{
707126277Sdes	Authctxt *authctxt = ctxt;
708126277Sdes	Gssctxt *gssctxt = authctxt->methoddata;
709126277Sdes	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
710149753Sdes	gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
711149753Sdes	gss_buffer_desc gssbuf;
712126277Sdes	OM_uint32 status, ms, flags;
713126277Sdes	Buffer b;
714126277Sdes
715126277Sdes	status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
716126277Sdes	    recv_tok, &send_tok, &flags);
717126277Sdes
718126277Sdes	if (send_tok.length > 0) {
719126277Sdes		if (GSS_ERROR(status))
720126277Sdes			packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
721126277Sdes		else
722126277Sdes			packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
723126277Sdes
724126277Sdes		packet_put_string(send_tok.value, send_tok.length);
725126277Sdes		packet_send();
726126277Sdes		gss_release_buffer(&ms, &send_tok);
727126277Sdes	}
728126277Sdes
729126277Sdes	if (status == GSS_S_COMPLETE) {
730126277Sdes		/* send either complete or MIC, depending on mechanism */
731126277Sdes		if (!(flags & GSS_C_INTEG_FLAG)) {
732126277Sdes			packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE);
733126277Sdes			packet_send();
734126277Sdes		} else {
735126277Sdes			ssh_gssapi_buildmic(&b, authctxt->server_user,
736126277Sdes			    authctxt->service, "gssapi-with-mic");
737126277Sdes
738126277Sdes			gssbuf.value = buffer_ptr(&b);
739126277Sdes			gssbuf.length = buffer_len(&b);
740126277Sdes
741126277Sdes			status = ssh_gssapi_sign(gssctxt, &gssbuf, &mic);
742126277Sdes
743126277Sdes			if (!GSS_ERROR(status)) {
744126277Sdes				packet_start(SSH2_MSG_USERAUTH_GSSAPI_MIC);
745126277Sdes				packet_put_string(mic.value, mic.length);
746126277Sdes
747126277Sdes				packet_send();
748126277Sdes			}
749126277Sdes
750126277Sdes			buffer_free(&b);
751126277Sdes			gss_release_buffer(&ms, &mic);
752126277Sdes		}
753126277Sdes	}
754126277Sdes
755126277Sdes	return status;
756126277Sdes}
757126277Sdes
758192595Sdes/* ARGSUSED */
759294332Sdesint
760124211Sdesinput_gssapi_response(int type, u_int32_t plen, void *ctxt)
761124211Sdes{
762124211Sdes	Authctxt *authctxt = ctxt;
763124211Sdes	Gssctxt *gssctxt;
764124211Sdes	int oidlen;
765124211Sdes	char *oidv;
766124211Sdes
767124211Sdes	if (authctxt == NULL)
768124211Sdes		fatal("input_gssapi_response: no authentication context");
769124211Sdes	gssctxt = authctxt->methoddata;
770124211Sdes
771124211Sdes	/* Setup our OID */
772124211Sdes	oidv = packet_get_string(&oidlen);
773124211Sdes
774126277Sdes	if (oidlen <= 2 ||
775126277Sdes	    oidv[0] != SSH_GSS_OIDTYPE ||
776126277Sdes	    oidv[1] != oidlen - 2) {
777255767Sdes		free(oidv);
778126277Sdes		debug("Badly encoded mechanism OID received");
779126277Sdes		userauth(authctxt, NULL);
780294332Sdes		return 0;
781124211Sdes	}
782124211Sdes
783126277Sdes	if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2))
784126277Sdes		fatal("Server returned different OID than expected");
785126277Sdes
786124211Sdes	packet_check_eom();
787124211Sdes
788255767Sdes	free(oidv);
789124211Sdes
790126277Sdes	if (GSS_ERROR(process_gssapi_token(ctxt, GSS_C_NO_BUFFER))) {
791124211Sdes		/* Start again with next method on list */
792124211Sdes		debug("Trying to start again");
793124211Sdes		userauth(authctxt, NULL);
794294332Sdes		return 0;
795124211Sdes	}
796294332Sdes	return 0;
797124211Sdes}
798124211Sdes
799192595Sdes/* ARGSUSED */
800294332Sdesint
801124211Sdesinput_gssapi_token(int type, u_int32_t plen, void *ctxt)
802124211Sdes{
803124211Sdes	Authctxt *authctxt = ctxt;
804124211Sdes	gss_buffer_desc recv_tok;
805126277Sdes	OM_uint32 status;
806124211Sdes	u_int slen;
807124211Sdes
808124211Sdes	if (authctxt == NULL)
809124211Sdes		fatal("input_gssapi_response: no authentication context");
810124211Sdes
811124211Sdes	recv_tok.value = packet_get_string(&slen);
812124211Sdes	recv_tok.length = slen;	/* safe typecast */
813124211Sdes
814124211Sdes	packet_check_eom();
815124211Sdes
816126277Sdes	status = process_gssapi_token(ctxt, &recv_tok);
817124211Sdes
818255767Sdes	free(recv_tok.value);
819124211Sdes
820124211Sdes	if (GSS_ERROR(status)) {
821124211Sdes		/* Start again with the next method in the list */
822124211Sdes		userauth(authctxt, NULL);
823294332Sdes		return 0;
824124211Sdes	}
825294332Sdes	return 0;
826124211Sdes}
827124211Sdes
828192595Sdes/* ARGSUSED */
829294332Sdesint
830124211Sdesinput_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
831124211Sdes{
832124211Sdes	Authctxt *authctxt = ctxt;
833124211Sdes	Gssctxt *gssctxt;
834124211Sdes	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
835124211Sdes	gss_buffer_desc recv_tok;
836255767Sdes	OM_uint32 ms;
837124211Sdes	u_int len;
838124211Sdes
839124211Sdes	if (authctxt == NULL)
840124211Sdes		fatal("input_gssapi_response: no authentication context");
841124211Sdes	gssctxt = authctxt->methoddata;
842124211Sdes
843124211Sdes	recv_tok.value = packet_get_string(&len);
844124211Sdes	recv_tok.length = len;
845124211Sdes
846124211Sdes	packet_check_eom();
847124211Sdes
848124211Sdes	/* Stick it into GSSAPI and see what it says */
849255767Sdes	(void)ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
850149753Sdes	    &recv_tok, &send_tok, NULL);
851124211Sdes
852255767Sdes	free(recv_tok.value);
853124211Sdes	gss_release_buffer(&ms, &send_tok);
854124211Sdes
855124211Sdes	/* Server will be returning a failed packet after this one */
856294332Sdes	return 0;
857124211Sdes}
858124211Sdes
859192595Sdes/* ARGSUSED */
860294332Sdesint
861124211Sdesinput_gssapi_error(int type, u_int32_t plen, void *ctxt)
862124211Sdes{
863124211Sdes	char *msg;
864124211Sdes	char *lang;
865124211Sdes
866255767Sdes	/* maj */(void)packet_get_int();
867255767Sdes	/* min */(void)packet_get_int();
868124211Sdes	msg=packet_get_string(NULL);
869124211Sdes	lang=packet_get_string(NULL);
870124211Sdes
871124211Sdes	packet_check_eom();
872124211Sdes
873157019Sdes	debug("Server GSSAPI Error:\n%s", msg);
874255767Sdes	free(msg);
875255767Sdes	free(lang);
876294332Sdes	return 0;
877124211Sdes}
878124211Sdes#endif /* GSSAPI */
879124211Sdes
88060573Skrisint
88169587Sgreenuserauth_none(Authctxt *authctxt)
88260573Skris{
88369587Sgreen	/* initial userauth request */
88469587Sgreen	packet_start(SSH2_MSG_USERAUTH_REQUEST);
88569587Sgreen	packet_put_cstring(authctxt->server_user);
88669587Sgreen	packet_put_cstring(authctxt->service);
88769587Sgreen	packet_put_cstring(authctxt->method->name);
88869587Sgreen	packet_send();
88969587Sgreen	return 1;
89069587Sgreen}
89169587Sgreen
89269587Sgreenint
89369587Sgreenuserauth_passwd(Authctxt *authctxt)
89469587Sgreen{
89560573Skris	static int attempt = 0;
89698684Sdes	char prompt[150];
89760573Skris	char *password;
898204917Sdes	const char *host = options.host_key_alias ?  options.host_key_alias :
899204917Sdes	    authctxt->host;
90060573Skris
90165668Skris	if (attempt++ >= options.number_of_password_prompts)
90260573Skris		return 0;
90360573Skris
90492559Sdes	if (attempt != 1)
90565668Skris		error("Permission denied, please try again.");
90665668Skris
90776262Sgreen	snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
908204917Sdes	    authctxt->server_user, host);
90960573Skris	password = read_passphrase(prompt, 0);
91060573Skris	packet_start(SSH2_MSG_USERAUTH_REQUEST);
91169587Sgreen	packet_put_cstring(authctxt->server_user);
91269587Sgreen	packet_put_cstring(authctxt->service);
91369587Sgreen	packet_put_cstring(authctxt->method->name);
91460573Skris	packet_put_char(0);
91593704Sdes	packet_put_cstring(password);
916263712Sdes	explicit_bzero(password, strlen(password));
917255767Sdes	free(password);
91892559Sdes	packet_add_padding(64);
91960573Skris	packet_send();
92098684Sdes
92198684Sdes	dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
92298684Sdes	    &input_userauth_passwd_changereq);
92398684Sdes
92460573Skris	return 1;
92560573Skris}
926192595Sdes
92798684Sdes/*
92898684Sdes * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST
92998684Sdes */
930192595Sdes/* ARGSUSED */
931294332Sdesint
93298941Sdesinput_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt)
93398684Sdes{
93498684Sdes	Authctxt *authctxt = ctxt;
93598684Sdes	char *info, *lang, *password = NULL, *retype = NULL;
93698684Sdes	char prompt[150];
937323136Sdes	const char *host;
93860573Skris
93998684Sdes	debug2("input_userauth_passwd_changereq");
94098684Sdes
94198684Sdes	if (authctxt == NULL)
94298684Sdes		fatal("input_userauth_passwd_changereq: "
94398684Sdes		    "no authentication context");
944323136Sdes	host = options.host_key_alias ? options.host_key_alias : authctxt->host;
94598684Sdes
94698684Sdes	info = packet_get_string(NULL);
94798684Sdes	lang = packet_get_string(NULL);
94898684Sdes	if (strlen(info) > 0)
949124211Sdes		logit("%s", info);
950255767Sdes	free(info);
951255767Sdes	free(lang);
95298684Sdes	packet_start(SSH2_MSG_USERAUTH_REQUEST);
95398684Sdes	packet_put_cstring(authctxt->server_user);
95498684Sdes	packet_put_cstring(authctxt->service);
95598684Sdes	packet_put_cstring(authctxt->method->name);
95698684Sdes	packet_put_char(1);			/* additional info */
95798684Sdes	snprintf(prompt, sizeof(prompt),
95898684Sdes	    "Enter %.30s@%.128s's old password: ",
959204917Sdes	    authctxt->server_user, host);
96098684Sdes	password = read_passphrase(prompt, 0);
96198684Sdes	packet_put_cstring(password);
962263712Sdes	explicit_bzero(password, strlen(password));
963255767Sdes	free(password);
96498684Sdes	password = NULL;
96598684Sdes	while (password == NULL) {
96698684Sdes		snprintf(prompt, sizeof(prompt),
96798684Sdes		    "Enter %.30s@%.128s's new password: ",
968204917Sdes		    authctxt->server_user, host);
96998684Sdes		password = read_passphrase(prompt, RP_ALLOW_EOF);
97098684Sdes		if (password == NULL) {
97198684Sdes			/* bail out */
972294332Sdes			return 0;
97398684Sdes		}
97498684Sdes		snprintf(prompt, sizeof(prompt),
97598684Sdes		    "Retype %.30s@%.128s's new password: ",
976204917Sdes		    authctxt->server_user, host);
97798684Sdes		retype = read_passphrase(prompt, 0);
97898684Sdes		if (strcmp(password, retype) != 0) {
979263712Sdes			explicit_bzero(password, strlen(password));
980255767Sdes			free(password);
981124211Sdes			logit("Mismatch; try again, EOF to quit.");
98298684Sdes			password = NULL;
98398684Sdes		}
984263712Sdes		explicit_bzero(retype, strlen(retype));
985255767Sdes		free(retype);
98698684Sdes	}
98798684Sdes	packet_put_cstring(password);
988263712Sdes	explicit_bzero(password, strlen(password));
989255767Sdes	free(password);
99098684Sdes	packet_add_padding(64);
99198684Sdes	packet_send();
99298684Sdes
99398684Sdes	dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
99498684Sdes	    &input_userauth_passwd_changereq);
995294332Sdes	return 0;
99698684Sdes}
99798684Sdes
998296633Sdesstatic const char *
999323136Sdeskey_sign_encode(const struct sshkey *key)
1000296633Sdes{
1001296633Sdes	struct ssh *ssh = active_state;
1002296633Sdes
1003323136Sdes	if (key->type == KEY_RSA) {
1004296633Sdes		switch (ssh->kex->rsa_sha2) {
1005296633Sdes		case 256:
1006296633Sdes			return "rsa-sha2-256";
1007296633Sdes		case 512:
1008296633Sdes			return "rsa-sha2-512";
1009296633Sdes		}
1010296633Sdes	}
1011323136Sdes	return key_ssh_name(key);
1012296633Sdes}
1013296633Sdes
1014124211Sdesstatic int
1015294332Sdesidentity_sign(struct identity *id, u_char **sigp, size_t *lenp,
1016294332Sdes    const u_char *data, size_t datalen, u_int compat)
101776262Sgreen{
1018124211Sdes	Key *prv;
1019124211Sdes	int ret;
102098684Sdes
1021124211Sdes	/* the agent supports this key */
1022323136Sdes	if (id->key != NULL && id->agent_fd != -1)
1023294332Sdes		return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp,
1024323136Sdes		    data, datalen, key_sign_encode(id->key), compat);
1025294332Sdes
1026124211Sdes	/*
1027124211Sdes	 * we have already loaded the private key or
1028124211Sdes	 * the private key is stored in external hardware
1029124211Sdes	 */
1030323136Sdes	if (id->key != NULL &&
1031323136Sdes	    (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)))
1032323136Sdes		return (sshkey_sign(id->key, sigp, lenp, data, datalen,
1033323136Sdes		    key_sign_encode(id->key), compat));
1034323136Sdes
1035124211Sdes	/* load the private key from the file */
1036296633Sdes	if ((prv = load_identity_file(id)) == NULL)
1037296633Sdes		return SSH_ERR_KEY_NOT_FOUND;
1038323136Sdes	ret = sshkey_sign(prv, sigp, lenp, data, datalen,
1039323136Sdes	    key_sign_encode(prv), compat);
1040294332Sdes	sshkey_free(prv);
1041124211Sdes	return (ret);
104276262Sgreen}
104376262Sgreen
104492559Sdesstatic int
1045323136Sdesid_filename_matches(Identity *id, Identity *private_id)
1046323136Sdes{
1047323136Sdes	const char *suffixes[] = { ".pub", "-cert.pub", NULL };
1048323136Sdes	size_t len = strlen(id->filename), plen = strlen(private_id->filename);
1049323136Sdes	size_t i, slen;
1050323136Sdes
1051323136Sdes	if (strcmp(id->filename, private_id->filename) == 0)
1052323136Sdes		return 1;
1053323136Sdes	for (i = 0; suffixes[i]; i++) {
1054323136Sdes		slen = strlen(suffixes[i]);
1055323136Sdes		if (len > slen && plen == len - slen &&
1056323136Sdes		    strcmp(id->filename + (len - slen), suffixes[i]) == 0 &&
1057323136Sdes		    memcmp(id->filename, private_id->filename, plen) == 0)
1058323136Sdes			return 1;
1059323136Sdes	}
1060323136Sdes	return 0;
1061323136Sdes}
1062323136Sdes
1063323136Sdesstatic int
1064124211Sdessign_and_send_pubkey(Authctxt *authctxt, Identity *id)
106560573Skris{
106660573Skris	Buffer b;
1067296633Sdes	Identity *private_id;
106876262Sgreen	u_char *blob, *signature;
1069294332Sdes	size_t slen;
1070296633Sdes	u_int bloblen, skip = 0;
1071296633Sdes	int matched, ret = -1, have_sig = 1;
1072215116Sdes	char *fp;
107360573Skris
1074294332Sdes	if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
1075294332Sdes	    SSH_FP_DEFAULT)) == NULL)
1076294332Sdes		return 0;
1077296633Sdes	debug3("%s: %s %s", __func__, key_type(id->key), fp);
1078255767Sdes	free(fp);
107960573Skris
1080124211Sdes	if (key_to_blob(id->key, &blob, &bloblen) == 0) {
108176262Sgreen		/* we cannot handle this key */
108276262Sgreen		debug3("sign_and_send_pubkey: cannot handle key");
108376262Sgreen		return 0;
108476262Sgreen	}
108560573Skris	/* data to be signed */
108660573Skris	buffer_init(&b);
108769587Sgreen	if (datafellows & SSH_OLD_SESSIONID) {
108869587Sgreen		buffer_append(&b, session_id2, session_id2_len);
108976262Sgreen		skip = session_id2_len;
109069587Sgreen	} else {
109165668Skris		buffer_put_string(&b, session_id2, session_id2_len);
109265668Skris		skip = buffer_len(&b);
109365668Skris	}
109460573Skris	buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
109569587Sgreen	buffer_put_cstring(&b, authctxt->server_user);
109660573Skris	buffer_put_cstring(&b,
109776262Sgreen	    datafellows & SSH_BUG_PKSERVICE ?
109860573Skris	    "ssh-userauth" :
109969587Sgreen	    authctxt->service);
110076262Sgreen	if (datafellows & SSH_BUG_PKAUTH) {
110176262Sgreen		buffer_put_char(&b, have_sig);
110276262Sgreen	} else {
110376262Sgreen		buffer_put_cstring(&b, authctxt->method->name);
110476262Sgreen		buffer_put_char(&b, have_sig);
1105323136Sdes		buffer_put_cstring(&b, key_sign_encode(id->key));
110676262Sgreen	}
110760573Skris	buffer_put_string(&b, blob, bloblen);
110860573Skris
1109296633Sdes	/*
1110296633Sdes	 * If the key is an certificate, try to find a matching private key
1111296633Sdes	 * and use it to complete the signature.
1112323129Sdes	 * If no such private key exists, fall back to trying the certificate
1113323129Sdes	 * key itself in case it has a private half already loaded.
1114296633Sdes	 */
1115296633Sdes	if (key_is_cert(id->key)) {
1116296633Sdes		matched = 0;
1117296633Sdes		TAILQ_FOREACH(private_id, &authctxt->keys, next) {
1118296633Sdes			if (sshkey_equal_public(id->key, private_id->key) &&
1119296633Sdes			    id->key->type != private_id->key->type) {
1120296633Sdes				id = private_id;
1121296633Sdes				matched = 1;
1122296633Sdes				break;
1123296633Sdes			}
1124296633Sdes		}
1125323136Sdes		/*
1126323136Sdes		 * Exact key matches are preferred, but also allow
1127323136Sdes		 * filename matches for non-PKCS#11/agent keys that
1128323136Sdes		 * didn't load public keys. This supports the case
1129323136Sdes		 * of keeping just a private key file and public
1130323136Sdes		 * certificate on disk.
1131323136Sdes		 */
1132323136Sdes		if (!matched && !id->isprivate && id->agent_fd == -1 &&
1133323136Sdes		    (id->key->flags & SSHKEY_FLAG_EXT) == 0) {
1134323136Sdes			TAILQ_FOREACH(private_id, &authctxt->keys, next) {
1135323136Sdes				if (private_id->key == NULL &&
1136323136Sdes				    id_filename_matches(id, private_id)) {
1137323136Sdes					id = private_id;
1138323136Sdes					matched = 1;
1139323136Sdes					break;
1140323136Sdes				}
1141323136Sdes			}
1142323136Sdes		}
1143296633Sdes		if (matched) {
1144296633Sdes			debug2("%s: using private key \"%s\"%s for "
1145296633Sdes			    "certificate", __func__, id->filename,
1146296633Sdes			    id->agent_fd != -1 ? " from agent" : "");
1147296633Sdes		} else {
1148323129Sdes			debug("%s: no separate private key for certificate "
1149296633Sdes			    "\"%s\"", __func__, id->filename);
1150296633Sdes		}
1151296633Sdes	}
1152296633Sdes
115360573Skris	/* generate signature */
1154124211Sdes	ret = identity_sign(id, &signature, &slen,
1155294332Sdes	    buffer_ptr(&b), buffer_len(&b), datafellows);
1156294332Sdes	if (ret != 0) {
1157296633Sdes		if (ret != SSH_ERR_KEY_NOT_FOUND)
1158296633Sdes			error("%s: signing failed: %s", __func__, ssh_err(ret));
1159255767Sdes		free(blob);
116065668Skris		buffer_free(&b);
116165668Skris		return 0;
116265668Skris	}
116376262Sgreen#ifdef DEBUG_PK
116460573Skris	buffer_dump(&b);
116560573Skris#endif
116676262Sgreen	if (datafellows & SSH_BUG_PKSERVICE) {
116760573Skris		buffer_clear(&b);
116860573Skris		buffer_append(&b, session_id2, session_id2_len);
116976262Sgreen		skip = session_id2_len;
117060573Skris		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
117169587Sgreen		buffer_put_cstring(&b, authctxt->server_user);
117269587Sgreen		buffer_put_cstring(&b, authctxt->service);
117369587Sgreen		buffer_put_cstring(&b, authctxt->method->name);
117469587Sgreen		buffer_put_char(&b, have_sig);
117576262Sgreen		if (!(datafellows & SSH_BUG_PKAUTH))
1176124211Sdes			buffer_put_cstring(&b, key_ssh_name(id->key));
117760573Skris		buffer_put_string(&b, blob, bloblen);
117860573Skris	}
1179255767Sdes	free(blob);
118076262Sgreen
118160573Skris	/* append signature */
118260573Skris	buffer_put_string(&b, signature, slen);
1183255767Sdes	free(signature);
118460573Skris
118560573Skris	/* skip session id and packet type */
118665668Skris	if (buffer_len(&b) < skip + 1)
118769587Sgreen		fatal("userauth_pubkey: internal error");
118865668Skris	buffer_consume(&b, skip + 1);
118960573Skris
119060573Skris	/* put remaining data from buffer into packet */
119160573Skris	packet_start(SSH2_MSG_USERAUTH_REQUEST);
119260573Skris	packet_put_raw(buffer_ptr(&b), buffer_len(&b));
119360573Skris	buffer_free(&b);
119460573Skris	packet_send();
119565668Skris
119660573Skris	return 1;
119760573Skris}
119860573Skris
119992559Sdesstatic int
1200124211Sdessend_pubkey_test(Authctxt *authctxt, Identity *id)
120169587Sgreen{
120276262Sgreen	u_char *blob;
120392559Sdes	u_int bloblen, have_sig = 0;
120476262Sgreen
120576262Sgreen	debug3("send_pubkey_test");
120676262Sgreen
1207124211Sdes	if (key_to_blob(id->key, &blob, &bloblen) == 0) {
120876262Sgreen		/* we cannot handle this key */
120976262Sgreen		debug3("send_pubkey_test: cannot handle key");
121076262Sgreen		return 0;
121176262Sgreen	}
121276262Sgreen	/* register callback for USERAUTH_PK_OK message */
121376262Sgreen	dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok);
121476262Sgreen
121576262Sgreen	packet_start(SSH2_MSG_USERAUTH_REQUEST);
121676262Sgreen	packet_put_cstring(authctxt->server_user);
121776262Sgreen	packet_put_cstring(authctxt->service);
121876262Sgreen	packet_put_cstring(authctxt->method->name);
121976262Sgreen	packet_put_char(have_sig);
122076262Sgreen	if (!(datafellows & SSH_BUG_PKAUTH))
1221323136Sdes		packet_put_cstring(key_sign_encode(id->key));
122276262Sgreen	packet_put_string(blob, bloblen);
1223255767Sdes	free(blob);
122476262Sgreen	packet_send();
122576262Sgreen	return 1;
122669587Sgreen}
122769587Sgreen
122892559Sdesstatic Key *
1229296633Sdesload_identity_file(Identity *id)
123065668Skris{
1231296633Sdes	Key *private = NULL;
1232296633Sdes	char prompt[300], *passphrase, *comment;
1233294332Sdes	int r, perm_ok = 0, quit = 0, i;
123465668Skris	struct stat st;
123565668Skris
1236296633Sdes	if (stat(id->filename, &st) < 0) {
1237296633Sdes		(id->userprovided ? logit : debug3)("no such identity: %s: %s",
1238296633Sdes		    id->filename, strerror(errno));
123976262Sgreen		return NULL;
124065668Skris	}
1241294332Sdes	snprintf(prompt, sizeof prompt,
1242296633Sdes	    "Enter passphrase for key '%.100s': ", id->filename);
1243294332Sdes	for (i = 0; i <= options.number_of_password_prompts; i++) {
1244294332Sdes		if (i == 0)
1245294332Sdes			passphrase = "";
1246294332Sdes		else {
124769587Sgreen			passphrase = read_passphrase(prompt, 0);
1248294332Sdes			if (*passphrase == '\0') {
124969587Sgreen				debug2("no passphrase given, try next key");
1250294332Sdes				free(passphrase);
1251294332Sdes				break;
1252294332Sdes			}
1253294332Sdes		}
1254296633Sdes		switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename,
1255296633Sdes		    passphrase, &private, &comment, &perm_ok))) {
1256294332Sdes		case 0:
1257294332Sdes			break;
1258294332Sdes		case SSH_ERR_KEY_WRONG_PASSPHRASE:
1259294332Sdes			if (options.batch_mode) {
126076262Sgreen				quit = 1;
1261294332Sdes				break;
126269587Sgreen			}
1263294332Sdes			if (i != 0)
1264294332Sdes				debug2("bad passphrase given, try again...");
1265294332Sdes			break;
1266294332Sdes		case SSH_ERR_SYSTEM_ERROR:
1267294332Sdes			if (errno == ENOENT) {
1268294332Sdes				debug2("Load key \"%s\": %s",
1269296633Sdes				    id->filename, ssh_err(r));
1270294332Sdes				quit = 1;
1271294332Sdes				break;
1272294332Sdes			}
1273294332Sdes			/* FALLTHROUGH */
1274294332Sdes		default:
1275296633Sdes			error("Load key \"%s\": %s", id->filename, ssh_err(r));
1276294332Sdes			quit = 1;
1277294332Sdes			break;
1278294332Sdes		}
1279296633Sdes		if (!quit && private != NULL && id->agent_fd == -1 &&
1280296633Sdes		    !(id->key && id->isprivate))
1281296633Sdes			maybe_add_key_to_agent(id->filename, private, comment,
1282296633Sdes			    passphrase);
1283294332Sdes		if (i > 0) {
1284263712Sdes			explicit_bzero(passphrase, strlen(passphrase));
1285255767Sdes			free(passphrase);
128669587Sgreen		}
1287296633Sdes		free(comment);
1288294332Sdes		if (private != NULL || quit)
1289294332Sdes			break;
129065668Skris	}
129176262Sgreen	return private;
129276262Sgreen}
129376262Sgreen
1294124211Sdes/*
1295124211Sdes * try keys in the following order:
1296296633Sdes * 	1. certificates listed in the config file
1297296633Sdes * 	2. other input certificates
1298296633Sdes *	3. agent keys that are found in the config file
1299296633Sdes *	4. other agent keys
1300296633Sdes *	5. keys that are only listed in the config file
1301124211Sdes */
1302124211Sdesstatic void
1303124211Sdespubkey_prepare(Authctxt *authctxt)
130476262Sgreen{
1305294332Sdes	struct identity *id, *id2, *tmp;
1306294332Sdes	struct idlist agent, files, *preferred;
1307294332Sdes	struct sshkey *key;
1308296633Sdes	int agent_fd = -1, i, r, found;
1309294332Sdes	size_t j;
1310294332Sdes	struct ssh_identitylist *idlist;
131176262Sgreen
1312124211Sdes	TAILQ_INIT(&agent);	/* keys from the agent */
1313124211Sdes	TAILQ_INIT(&files);	/* keys from the config file */
1314124211Sdes	preferred = &authctxt->keys;
1315124211Sdes	TAILQ_INIT(preferred);	/* preferred order of keys */
131692559Sdes
1317248619Sdes	/* list of keys stored in the filesystem and PKCS#11 */
1318124211Sdes	for (i = 0; i < options.num_identity_files; i++) {
1319124211Sdes		key = options.identity_keys[i];
1320124211Sdes		if (key && key->type == KEY_RSA1)
1321124211Sdes			continue;
1322204917Sdes		if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER)
1323204917Sdes			continue;
1324124211Sdes		options.identity_keys[i] = NULL;
1325162856Sdes		id = xcalloc(1, sizeof(*id));
1326296633Sdes		id->agent_fd = -1;
1327124211Sdes		id->key = key;
1328124211Sdes		id->filename = xstrdup(options.identity_files[i]);
1329249016Sdes		id->userprovided = options.identity_file_userprovided[i];
1330124211Sdes		TAILQ_INSERT_TAIL(&files, id, next);
1331124211Sdes	}
1332296633Sdes	/* list of certificates specified by user */
1333296633Sdes	for (i = 0; i < options.num_certificate_files; i++) {
1334296633Sdes		key = options.certificates[i];
1335296633Sdes		if (!key_is_cert(key) || key->cert == NULL ||
1336296633Sdes		    key->cert->type != SSH2_CERT_TYPE_USER)
1337296633Sdes			continue;
1338296633Sdes		id = xcalloc(1, sizeof(*id));
1339296633Sdes		id->agent_fd = -1;
1340296633Sdes		id->key = key;
1341296633Sdes		id->filename = xstrdup(options.certificate_files[i]);
1342296633Sdes		id->userprovided = options.certificate_file_userprovided[i];
1343296633Sdes		TAILQ_INSERT_TAIL(preferred, id, next);
1344296633Sdes	}
1345124211Sdes	/* list of keys supported by the agent */
1346294332Sdes	if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
1347294332Sdes		if (r != SSH_ERR_AGENT_NOT_PRESENT)
1348294332Sdes			debug("%s: ssh_get_authentication_socket: %s",
1349294332Sdes			    __func__, ssh_err(r));
1350294332Sdes	} else if ((r = ssh_fetch_identitylist(agent_fd, 2, &idlist)) != 0) {
1351294332Sdes		if (r != SSH_ERR_AGENT_NO_IDENTITIES)
1352294332Sdes			debug("%s: ssh_fetch_identitylist: %s",
1353294332Sdes			    __func__, ssh_err(r));
1354296633Sdes		close(agent_fd);
1355294332Sdes	} else {
1356294332Sdes		for (j = 0; j < idlist->nkeys; j++) {
1357124211Sdes			found = 0;
1358124211Sdes			TAILQ_FOREACH(id, &files, next) {
1359294332Sdes				/*
1360294332Sdes				 * agent keys from the config file are
1361294332Sdes				 * preferred
1362294332Sdes				 */
1363294332Sdes				if (sshkey_equal(idlist->keys[j], id->key)) {
1364124211Sdes					TAILQ_REMOVE(&files, id, next);
1365124211Sdes					TAILQ_INSERT_TAIL(preferred, id, next);
1366294332Sdes					id->agent_fd = agent_fd;
1367124211Sdes					found = 1;
1368124211Sdes					break;
1369124211Sdes				}
1370124211Sdes			}
1371128460Sdes			if (!found && !options.identities_only) {
1372162856Sdes				id = xcalloc(1, sizeof(*id));
1373294332Sdes				/* XXX "steals" key/comment from idlist */
1374294332Sdes				id->key = idlist->keys[j];
1375294332Sdes				id->filename = idlist->comments[j];
1376294332Sdes				idlist->keys[j] = NULL;
1377294332Sdes				idlist->comments[j] = NULL;
1378294332Sdes				id->agent_fd = agent_fd;
1379124211Sdes				TAILQ_INSERT_TAIL(&agent, id, next);
1380124211Sdes			}
1381124211Sdes		}
1382294332Sdes		ssh_free_identitylist(idlist);
1383124211Sdes		/* append remaining agent keys */
1384124211Sdes		for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) {
1385124211Sdes			TAILQ_REMOVE(&agent, id, next);
1386124211Sdes			TAILQ_INSERT_TAIL(preferred, id, next);
1387124211Sdes		}
1388294332Sdes		authctxt->agent_fd = agent_fd;
1389124211Sdes	}
1390323129Sdes	/* Prefer PKCS11 keys that are explicitly listed */
1391323129Sdes	TAILQ_FOREACH_SAFE(id, &files, next, tmp) {
1392323129Sdes		if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0)
1393323129Sdes			continue;
1394323129Sdes		found = 0;
1395323129Sdes		TAILQ_FOREACH(id2, &files, next) {
1396323129Sdes			if (id2->key == NULL ||
1397323129Sdes			    (id2->key->flags & SSHKEY_FLAG_EXT) == 0)
1398323129Sdes				continue;
1399323129Sdes			if (sshkey_equal(id->key, id2->key)) {
1400323129Sdes				TAILQ_REMOVE(&files, id, next);
1401323129Sdes				TAILQ_INSERT_TAIL(preferred, id, next);
1402323129Sdes				found = 1;
1403323129Sdes				break;
1404323129Sdes			}
1405323129Sdes		}
1406323129Sdes		/* If IdentitiesOnly set and key not found then don't use it */
1407323129Sdes		if (!found && options.identities_only) {
1408323129Sdes			TAILQ_REMOVE(&files, id, next);
1409323129Sdes			explicit_bzero(id, sizeof(*id));
1410323129Sdes			free(id);
1411323129Sdes		}
1412323129Sdes	}
1413124211Sdes	/* append remaining keys from the config file */
1414124211Sdes	for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) {
1415124211Sdes		TAILQ_REMOVE(&files, id, next);
1416124211Sdes		TAILQ_INSERT_TAIL(preferred, id, next);
1417124211Sdes	}
1418296633Sdes	/* finally, filter by PubkeyAcceptedKeyTypes */
1419296633Sdes	TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
1420296633Sdes		if (id->key != NULL &&
1421296633Sdes		    match_pattern_list(sshkey_ssh_name(id->key),
1422296633Sdes		    options.pubkey_key_types, 0) != 1) {
1423296633Sdes			debug("Skipping %s key %s - "
1424296633Sdes			    "not in PubkeyAcceptedKeyTypes",
1425296633Sdes			    sshkey_ssh_name(id->key), id->filename);
1426296633Sdes			TAILQ_REMOVE(preferred, id, next);
1427296633Sdes			sshkey_free(id->key);
1428296633Sdes			free(id->filename);
1429296633Sdes			memset(id, 0, sizeof(*id));
1430296633Sdes			continue;
1431296633Sdes		}
1432296633Sdes		debug2("key: %s (%p)%s%s", id->filename, id->key,
1433296633Sdes		    id->userprovided ? ", explicit" : "",
1434296633Sdes		    id->agent_fd != -1 ? ", agent" : "");
1435124211Sdes	}
143665668Skris}
143765668Skris
1438124211Sdesstatic void
1439124211Sdespubkey_cleanup(Authctxt *authctxt)
144065668Skris{
1441124211Sdes	Identity *id;
144265668Skris
1443294332Sdes	if (authctxt->agent_fd != -1)
1444294332Sdes		ssh_close_authentication_socket(authctxt->agent_fd);
1445124211Sdes	for (id = TAILQ_FIRST(&authctxt->keys); id;
1446124211Sdes	    id = TAILQ_FIRST(&authctxt->keys)) {
1447124211Sdes		TAILQ_REMOVE(&authctxt->keys, id, next);
1448296633Sdes		sshkey_free(id->key);
1449255767Sdes		free(id->filename);
1450255767Sdes		free(id);
145165668Skris	}
145265668Skris}
145365668Skris
1454323134Sdesstatic void
1455323134Sdespubkey_reset(Authctxt *authctxt)
1456323134Sdes{
1457323134Sdes	Identity *id;
1458323134Sdes
1459323134Sdes	TAILQ_FOREACH(id, &authctxt->keys, next)
1460323134Sdes		id->tried = 0;
1461323134Sdes}
1462323134Sdes
1463294464Sdesstatic int
1464294464Sdestry_identity(Identity *id)
1465294464Sdes{
1466294464Sdes	if (!id->key)
1467294464Sdes		return (0);
1468294464Sdes	if (key_type_plain(id->key->type) == KEY_RSA &&
1469294464Sdes	    (datafellows & SSH_BUG_RSASIGMD5) != 0) {
1470294464Sdes		debug("Skipped %s key %s for RSA/MD5 server",
1471294464Sdes		    key_type(id->key), id->filename);
1472294464Sdes		return (0);
1473294464Sdes	}
1474294464Sdes	return (id->key->type != KEY_RSA1);
1475294464Sdes}
1476294464Sdes
147769587Sgreenint
147869587Sgreenuserauth_pubkey(Authctxt *authctxt)
147969587Sgreen{
1480124211Sdes	Identity *id;
148169587Sgreen	int sent = 0;
148269587Sgreen
1483124211Sdes	while ((id = TAILQ_FIRST(&authctxt->keys))) {
1484124211Sdes		if (id->tried++)
1485124211Sdes			return (0);
1486126277Sdes		/* move key to the end of the queue */
1487124211Sdes		TAILQ_REMOVE(&authctxt->keys, id, next);
1488124211Sdes		TAILQ_INSERT_TAIL(&authctxt->keys, id, next);
1489124211Sdes		/*
1490124211Sdes		 * send a test message if we have the public key. for
1491124211Sdes		 * encrypted keys we cannot do this and have to load the
1492124211Sdes		 * private key instead
1493124211Sdes		 */
1494261320Sdes		if (id->key != NULL) {
1495294464Sdes			if (try_identity(id)) {
1496261320Sdes				debug("Offering %s public key: %s",
1497261320Sdes				    key_type(id->key), id->filename);
1498261320Sdes				sent = send_pubkey_test(authctxt, id);
1499261320Sdes			}
1500261320Sdes		} else {
1501124211Sdes			debug("Trying private key: %s", id->filename);
1502296633Sdes			id->key = load_identity_file(id);
1503124211Sdes			if (id->key != NULL) {
1504294464Sdes				if (try_identity(id)) {
1505294464Sdes					id->isprivate = 1;
1506261320Sdes					sent = sign_and_send_pubkey(
1507261320Sdes					    authctxt, id);
1508261320Sdes				}
1509124211Sdes				key_free(id->key);
1510124211Sdes				id->key = NULL;
1511323134Sdes				id->isprivate = 0;
151276262Sgreen			}
151376262Sgreen		}
1514124211Sdes		if (sent)
1515124211Sdes			return (sent);
151676262Sgreen	}
1517124211Sdes	return (0);
151869587Sgreen}
151969587Sgreen
152069587Sgreen/*
152169587Sgreen * Send userauth request message specifying keyboard-interactive method.
152269587Sgreen */
152369587Sgreenint
152469587Sgreenuserauth_kbdint(Authctxt *authctxt)
152569587Sgreen{
152669587Sgreen	static int attempt = 0;
152769587Sgreen
152869587Sgreen	if (attempt++ >= options.number_of_password_prompts)
152969587Sgreen		return 0;
153092559Sdes	/* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */
153192559Sdes	if (attempt > 1 && !authctxt->info_req_seen) {
153292559Sdes		debug3("userauth_kbdint: disable: no info_req_seen");
153392559Sdes		dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL);
153492559Sdes		return 0;
153592559Sdes	}
153669587Sgreen
153769587Sgreen	debug2("userauth_kbdint");
153869587Sgreen	packet_start(SSH2_MSG_USERAUTH_REQUEST);
153969587Sgreen	packet_put_cstring(authctxt->server_user);
154069587Sgreen	packet_put_cstring(authctxt->service);
154169587Sgreen	packet_put_cstring(authctxt->method->name);
154269587Sgreen	packet_put_cstring("");					/* lang */
154369587Sgreen	packet_put_cstring(options.kbd_interactive_devices ?
154469587Sgreen	    options.kbd_interactive_devices : "");
154569587Sgreen	packet_send();
154669587Sgreen
154769587Sgreen	dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
154869587Sgreen	return 1;
154969587Sgreen}
155069587Sgreen
155169587Sgreen/*
155276262Sgreen * parse INFO_REQUEST, prompt user and send INFO_RESPONSE
155369587Sgreen */
1554294332Sdesint
155592559Sdesinput_userauth_info_req(int type, u_int32_t seq, void *ctxt)
155660573Skris{
155769587Sgreen	Authctxt *authctxt = ctxt;
155876262Sgreen	char *name, *inst, *lang, *prompt, *response;
155976262Sgreen	u_int num_prompts, i;
156069587Sgreen	int echo = 0;
156160573Skris
156269587Sgreen	debug2("input_userauth_info_req");
156369587Sgreen
156469587Sgreen	if (authctxt == NULL)
156569587Sgreen		fatal("input_userauth_info_req: no authentication context");
156669587Sgreen
156792559Sdes	authctxt->info_req_seen = 1;
156892559Sdes
156969587Sgreen	name = packet_get_string(NULL);
157069587Sgreen	inst = packet_get_string(NULL);
157169587Sgreen	lang = packet_get_string(NULL);
157269587Sgreen	if (strlen(name) > 0)
1573124211Sdes		logit("%s", name);
157469587Sgreen	if (strlen(inst) > 0)
1575124211Sdes		logit("%s", inst);
1576255767Sdes	free(name);
1577255767Sdes	free(inst);
1578255767Sdes	free(lang);
157969587Sgreen
158069587Sgreen	num_prompts = packet_get_int();
158169587Sgreen	/*
158269587Sgreen	 * Begin to build info response packet based on prompts requested.
158369587Sgreen	 * We commit to providing the correct number of responses, so if
158469587Sgreen	 * further on we run into a problem that prevents this, we have to
158569587Sgreen	 * be sure and clean this up and send a correct error response.
158669587Sgreen	 */
158769587Sgreen	packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE);
158869587Sgreen	packet_put_int(num_prompts);
158969587Sgreen
159092559Sdes	debug2("input_userauth_info_req: num_prompts %d", num_prompts);
159169587Sgreen	for (i = 0; i < num_prompts; i++) {
159269587Sgreen		prompt = packet_get_string(NULL);
159369587Sgreen		echo = packet_get_char();
159469587Sgreen
159592559Sdes		response = read_passphrase(prompt, echo ? RP_ECHO : 0);
159669587Sgreen
159793704Sdes		packet_put_cstring(response);
1598263712Sdes		explicit_bzero(response, strlen(response));
1599255767Sdes		free(response);
1600255767Sdes		free(prompt);
160169587Sgreen	}
160292559Sdes	packet_check_eom(); /* done with parsing incoming message. */
160369587Sgreen
160492559Sdes	packet_add_padding(64);
160560573Skris	packet_send();
1606294332Sdes	return 0;
160769587Sgreen}
160860573Skris
160998684Sdesstatic int
1610294332Sdesssh_keysign(struct sshkey *key, u_char **sigp, size_t *lenp,
1611294332Sdes    const u_char *data, size_t datalen)
161298684Sdes{
1613294332Sdes	struct sshbuf *b;
161498684Sdes	struct stat st;
161598684Sdes	pid_t pid;
1616294332Sdes	int i, r, to[2], from[2], status, sock = packet_get_connection_in();
1617294332Sdes	u_char rversion = 0, version = 2;
1618294332Sdes	void (*osigchld)(int);
161998684Sdes
1620294332Sdes	*sigp = NULL;
1621294332Sdes	*lenp = 0;
162298684Sdes
162398684Sdes	if (stat(_PATH_SSH_KEY_SIGN, &st) < 0) {
1624294332Sdes		error("%s: not installed: %s", __func__, strerror(errno));
162598684Sdes		return -1;
162698684Sdes	}
1627294332Sdes	if (fflush(stdout) != 0) {
1628294332Sdes		error("%s: fflush: %s", __func__, strerror(errno));
1629294332Sdes		return -1;
1630294332Sdes	}
163198684Sdes	if (pipe(to) < 0) {
1632294332Sdes		error("%s: pipe: %s", __func__, strerror(errno));
163398684Sdes		return -1;
163498684Sdes	}
163598684Sdes	if (pipe(from) < 0) {
1636294332Sdes		error("%s: pipe: %s", __func__, strerror(errno));
163798684Sdes		return -1;
163898684Sdes	}
163998684Sdes	if ((pid = fork()) < 0) {
1640294332Sdes		error("%s: fork: %s", __func__, strerror(errno));
164198684Sdes		return -1;
164298684Sdes	}
1643294332Sdes	osigchld = signal(SIGCHLD, SIG_DFL);
164498684Sdes	if (pid == 0) {
1645204917Sdes		/* keep the socket on exec */
1646294332Sdes		fcntl(sock, F_SETFD, 0);
1647162856Sdes		permanently_drop_suid(getuid());
164898684Sdes		close(from[0]);
164998684Sdes		if (dup2(from[1], STDOUT_FILENO) < 0)
1650294332Sdes			fatal("%s: dup2: %s", __func__, strerror(errno));
165198684Sdes		close(to[1]);
165298684Sdes		if (dup2(to[0], STDIN_FILENO) < 0)
1653294332Sdes			fatal("%s: dup2: %s", __func__, strerror(errno));
165498684Sdes		close(from[1]);
165598684Sdes		close(to[0]);
1656294332Sdes		/* Close everything but stdio and the socket */
1657294332Sdes		for (i = STDERR_FILENO + 1; i < sock; i++)
1658294332Sdes			close(i);
1659294332Sdes		closefrom(sock + 1);
1660294332Sdes		debug3("%s: [child] pid=%ld, exec %s",
1661294332Sdes		    __func__, (long)getpid(), _PATH_SSH_KEY_SIGN);
1662296633Sdes		execl(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *)NULL);
1663294332Sdes		fatal("%s: exec(%s): %s", __func__, _PATH_SSH_KEY_SIGN,
166498684Sdes		    strerror(errno));
166598684Sdes	}
166698684Sdes	close(from[1]);
166798684Sdes	close(to[0]);
166898684Sdes
1669294332Sdes	if ((b = sshbuf_new()) == NULL)
1670294332Sdes		fatal("%s: sshbuf_new failed", __func__);
1671294332Sdes	/* send # of sock, data to be signed */
1672323136Sdes	if ((r = sshbuf_put_u32(b, sock)) != 0 ||
1673294332Sdes	    (r = sshbuf_put_string(b, data, datalen)) != 0)
1674294332Sdes		fatal("%s: buffer error: %s", __func__, ssh_err(r));
1675294332Sdes	if (ssh_msg_send(to[1], version, b) == -1)
1676294332Sdes		fatal("%s: couldn't send request", __func__);
1677294332Sdes	sshbuf_reset(b);
1678294332Sdes	r = ssh_msg_recv(from[0], b);
167998684Sdes	close(from[0]);
168098684Sdes	close(to[1]);
1681294332Sdes	if (r < 0) {
1682294332Sdes		error("%s: no reply", __func__);
1683294332Sdes		goto fail;
1684294332Sdes	}
168598684Sdes
1686294332Sdes	errno = 0;
1687294332Sdes	while (waitpid(pid, &status, 0) < 0) {
1688294332Sdes		if (errno != EINTR) {
1689294332Sdes			error("%s: waitpid %ld: %s",
1690294332Sdes			    __func__, (long)pid, strerror(errno));
1691294332Sdes			goto fail;
1692294332Sdes		}
1693294332Sdes	}
1694294332Sdes	if (!WIFEXITED(status)) {
1695294332Sdes		error("%s: exited abnormally", __func__);
1696294332Sdes		goto fail;
1697294332Sdes	}
1698294332Sdes	if (WEXITSTATUS(status) != 0) {
1699294332Sdes		error("%s: exited with status %d",
1700294332Sdes		    __func__, WEXITSTATUS(status));
1701294332Sdes		goto fail;
1702294332Sdes	}
1703294332Sdes	if ((r = sshbuf_get_u8(b, &rversion)) != 0) {
1704294332Sdes		error("%s: buffer error: %s", __func__, ssh_err(r));
1705294332Sdes		goto fail;
1706294332Sdes	}
1707294332Sdes	if (rversion != version) {
1708294332Sdes		error("%s: bad version", __func__);
1709294332Sdes		goto fail;
1710294332Sdes	}
1711294332Sdes	if ((r = sshbuf_get_string(b, sigp, lenp)) != 0) {
1712294332Sdes		error("%s: buffer error: %s", __func__, ssh_err(r));
1713294332Sdes fail:
1714294332Sdes		signal(SIGCHLD, osigchld);
1715294332Sdes		sshbuf_free(b);
171698684Sdes		return -1;
171798684Sdes	}
1718294332Sdes	signal(SIGCHLD, osigchld);
1719294332Sdes	sshbuf_free(b);
172098684Sdes
172198684Sdes	return 0;
172298684Sdes}
172398684Sdes
172476262Sgreenint
172576262Sgreenuserauth_hostbased(Authctxt *authctxt)
172669587Sgreen{
1727294332Sdes	struct ssh *ssh = active_state;
1728294332Sdes	struct sshkey *private = NULL;
1729294332Sdes	struct sshbuf *b = NULL;
173076262Sgreen	const char *service;
1731294332Sdes	u_char *sig = NULL, *keyblob = NULL;
1732294332Sdes	char *fp = NULL, *chost = NULL, *lname = NULL;
1733294332Sdes	size_t siglen = 0, keylen = 0;
1734294332Sdes	int i, r, success = 0;
173576262Sgreen
1736294332Sdes	if (authctxt->ktypes == NULL) {
1737294332Sdes		authctxt->oktypes = xstrdup(options.hostbased_key_types);
1738294332Sdes		authctxt->ktypes = authctxt->oktypes;
1739294332Sdes	}
1740294332Sdes
1741294332Sdes	/*
1742294332Sdes	 * Work through each listed type pattern in HostbasedKeyTypes,
1743294332Sdes	 * trying each hostkey that matches the type in turn.
1744294332Sdes	 */
1745294332Sdes	for (;;) {
1746294332Sdes		if (authctxt->active_ktype == NULL)
1747294332Sdes			authctxt->active_ktype = strsep(&authctxt->ktypes, ",");
1748294332Sdes		if (authctxt->active_ktype == NULL ||
1749294332Sdes		    *authctxt->active_ktype == '\0')
1750294332Sdes			break;
1751294332Sdes		debug3("%s: trying key type %s", __func__,
1752294332Sdes		    authctxt->active_ktype);
1753294332Sdes
1754294332Sdes		/* check for a useful key */
1755294332Sdes		private = NULL;
1756294332Sdes		for (i = 0; i < authctxt->sensitive->nkeys; i++) {
1757294332Sdes			if (authctxt->sensitive->keys[i] == NULL ||
1758294332Sdes			    authctxt->sensitive->keys[i]->type == KEY_RSA1 ||
1759294332Sdes			    authctxt->sensitive->keys[i]->type == KEY_UNSPEC)
1760294332Sdes				continue;
1761294332Sdes			if (match_pattern_list(
1762294332Sdes			    sshkey_ssh_name(authctxt->sensitive->keys[i]),
1763294336Sdes			    authctxt->active_ktype, 0) != 1)
1764294332Sdes				continue;
176576262Sgreen			/* we take and free the key */
1766294332Sdes			private = authctxt->sensitive->keys[i];
1767294332Sdes			authctxt->sensitive->keys[i] = NULL;
176876262Sgreen			break;
176976262Sgreen		}
1770294332Sdes		/* Found one */
1771294332Sdes		if (private != NULL)
1772294332Sdes			break;
1773294332Sdes		/* No more keys of this type; advance */
1774294332Sdes		authctxt->active_ktype = NULL;
177569587Sgreen	}
1776294332Sdes	if (private == NULL) {
1777294332Sdes		free(authctxt->oktypes);
1778294332Sdes		authctxt->oktypes = authctxt->ktypes = NULL;
1779294332Sdes		authctxt->active_ktype = NULL;
1780113911Sdes		debug("No more client hostkeys for hostbased authentication.");
1781294332Sdes		goto out;
178269587Sgreen	}
1783294332Sdes
1784294332Sdes	if ((fp = sshkey_fingerprint(private, options.fingerprint_hash,
1785294332Sdes	    SSH_FP_DEFAULT)) == NULL) {
1786294332Sdes		error("%s: sshkey_fingerprint failed", __func__);
1787294332Sdes		goto out;
178876262Sgreen	}
1789294332Sdes	debug("%s: trying hostkey %s %s",
1790294332Sdes	    __func__, sshkey_ssh_name(private), fp);
1791294332Sdes
179292559Sdes	/* figure out a name for the client host */
1793294332Sdes	if ((lname = get_local_name(packet_get_connection_in())) == NULL) {
1794294332Sdes		error("%s: cannot get local ipaddr/name", __func__);
1795294332Sdes		goto out;
179692559Sdes	}
179792559Sdes
1798294332Sdes	/* XXX sshbuf_put_stringf? */
1799294332Sdes	xasprintf(&chost, "%s.", lname);
1800294332Sdes	debug2("%s: chost %s", __func__, chost);
1801294332Sdes
180276262Sgreen	service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" :
180376262Sgreen	    authctxt->service;
1804294332Sdes
180576262Sgreen	/* construct data */
1806294332Sdes	if ((b = sshbuf_new()) == NULL) {
1807294332Sdes		error("%s: sshbuf_new failed", __func__);
1808294332Sdes		goto out;
1809294332Sdes	}
1810294332Sdes	if ((r = sshkey_to_blob(private, &keyblob, &keylen)) != 0) {
1811294332Sdes		error("%s: sshkey_to_blob: %s", __func__, ssh_err(r));
1812294332Sdes		goto out;
1813294332Sdes	}
1814294332Sdes	if ((r = sshbuf_put_string(b, session_id2, session_id2_len)) != 0 ||
1815294332Sdes	    (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1816294332Sdes	    (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 ||
1817294332Sdes	    (r = sshbuf_put_cstring(b, service)) != 0 ||
1818294332Sdes	    (r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 ||
1819294332Sdes	    (r = sshbuf_put_cstring(b, key_ssh_name(private))) != 0 ||
1820294332Sdes	    (r = sshbuf_put_string(b, keyblob, keylen)) != 0 ||
1821294332Sdes	    (r = sshbuf_put_cstring(b, chost)) != 0 ||
1822294332Sdes	    (r = sshbuf_put_cstring(b, authctxt->local_user)) != 0) {
1823294332Sdes		error("%s: buffer error: %s", __func__, ssh_err(r));
1824294332Sdes		goto out;
1825294332Sdes	}
1826294332Sdes
182776262Sgreen#ifdef DEBUG_PK
1828294332Sdes	sshbuf_dump(b, stderr);
182976262Sgreen#endif
1830294332Sdes	if (authctxt->sensitive->external_keysign)
1831294332Sdes		r = ssh_keysign(private, &sig, &siglen,
1832294332Sdes		    sshbuf_ptr(b), sshbuf_len(b));
1833294332Sdes	else if ((r = sshkey_sign(private, &sig, &siglen,
1834296633Sdes	    sshbuf_ptr(b), sshbuf_len(b), NULL, datafellows)) != 0)
1835294332Sdes		debug("%s: sshkey_sign: %s", __func__, ssh_err(r));
1836294332Sdes	if (r != 0) {
1837294332Sdes		error("sign using hostkey %s %s failed",
1838294332Sdes		    sshkey_ssh_name(private), fp);
1839294332Sdes		goto out;
184076262Sgreen	}
1841294332Sdes	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1842294332Sdes	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1843294332Sdes	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1844294332Sdes	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1845294332Sdes	    (r = sshpkt_put_cstring(ssh, key_ssh_name(private))) != 0 ||
1846294332Sdes	    (r = sshpkt_put_string(ssh, keyblob, keylen)) != 0 ||
1847294332Sdes	    (r = sshpkt_put_cstring(ssh, chost)) != 0 ||
1848294332Sdes	    (r = sshpkt_put_cstring(ssh, authctxt->local_user)) != 0 ||
1849294332Sdes	    (r = sshpkt_put_string(ssh, sig, siglen)) != 0 ||
1850294332Sdes	    (r = sshpkt_send(ssh)) != 0) {
1851294332Sdes		error("%s: packet error: %s", __func__, ssh_err(r));
1852294332Sdes		goto out;
1853294332Sdes	}
1854294332Sdes	success = 1;
1855294332Sdes
1856294332Sdes out:
1857294332Sdes	if (sig != NULL) {
1858294332Sdes		explicit_bzero(sig, siglen);
1859294332Sdes		free(sig);
1860294332Sdes	}
1861294332Sdes	free(keyblob);
1862294332Sdes	free(lname);
1863294332Sdes	free(fp);
1864255767Sdes	free(chost);
1865294332Sdes	sshkey_free(private);
1866294332Sdes	sshbuf_free(b);
186776262Sgreen
1868294332Sdes	return success;
186969587Sgreen}
187069587Sgreen
187176262Sgreen/* find auth method */
187276262Sgreen
187369587Sgreen/*
187469587Sgreen * given auth method name, if configurable options permit this method fill
187569587Sgreen * in auth_ident field and return true, otherwise return false.
187669587Sgreen */
187792559Sdesstatic int
187869587Sgreenauthmethod_is_enabled(Authmethod *method)
187969587Sgreen{
188069587Sgreen	if (method == NULL)
188169587Sgreen		return 0;
188269587Sgreen	/* return false if options indicate this method is disabled */
188369587Sgreen	if  (method->enabled == NULL || *method->enabled == 0)
188469587Sgreen		return 0;
188569587Sgreen	/* return false if batch mode is enabled but method needs interactive mode */
188669587Sgreen	if  (method->batch_flag != NULL && *method->batch_flag != 0)
188769587Sgreen		return 0;
188869587Sgreen	return 1;
188969587Sgreen}
189069587Sgreen
189192559Sdesstatic Authmethod *
189269587Sgreenauthmethod_lookup(const char *name)
189369587Sgreen{
189469587Sgreen	Authmethod *method = NULL;
189569587Sgreen	if (name != NULL)
189669587Sgreen		for (method = authmethods; method->name != NULL; method++)
189769587Sgreen			if (strcmp(name, method->name) == 0)
189869587Sgreen				return method;
189969587Sgreen	debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
190069587Sgreen	return NULL;
190169587Sgreen}
190269587Sgreen
190376262Sgreen/* XXX internal state */
190476262Sgreenstatic Authmethod *current = NULL;
190576262Sgreenstatic char *supported = NULL;
190676262Sgreenstatic char *preferred = NULL;
190799063Sdes
190869587Sgreen/*
190969587Sgreen * Given the authentication method list sent by the server, return the
191069587Sgreen * next method we should try.  If the server initially sends a nil list,
191176262Sgreen * use a built-in default list.
191276262Sgreen */
191392559Sdesstatic Authmethod *
191469587Sgreenauthmethod_get(char *authlist)
191569587Sgreen{
191676262Sgreen	char *name = NULL;
191792559Sdes	u_int next;
191876262Sgreen
191969587Sgreen	/* Use a suitable default if we're passed a nil list.  */
192069587Sgreen	if (authlist == NULL || strlen(authlist) == 0)
192176262Sgreen		authlist = options.preferred_authentications;
192269587Sgreen
192376262Sgreen	if (supported == NULL || strcmp(authlist, supported) != 0) {
192476262Sgreen		debug3("start over, passed a different list %s", authlist);
1925255767Sdes		free(supported);
192676262Sgreen		supported = xstrdup(authlist);
192776262Sgreen		preferred = options.preferred_authentications;
192876262Sgreen		debug3("preferred %s", preferred);
192976262Sgreen		current = NULL;
193076262Sgreen	} else if (current != NULL && authmethod_is_enabled(current))
193176262Sgreen		return current;
193260573Skris
193376262Sgreen	for (;;) {
193476262Sgreen		if ((name = match_list(preferred, supported, &next)) == NULL) {
1935113911Sdes			debug("No more authentication methods to try.");
193676262Sgreen			current = NULL;
193776262Sgreen			return NULL;
193876262Sgreen		}
193976262Sgreen		preferred += next;
194069587Sgreen		debug3("authmethod_lookup %s", name);
194176262Sgreen		debug3("remaining preferred: %s", preferred);
194276262Sgreen		if ((current = authmethod_lookup(name)) != NULL &&
194376262Sgreen		    authmethod_is_enabled(current)) {
194469587Sgreen			debug3("authmethod_is_enabled %s", name);
1945113911Sdes			debug("Next authentication method: %s", name);
1946255767Sdes			free(name);
194776262Sgreen			return current;
194860573Skris		}
1949255767Sdes		free(name);
195060573Skris	}
195176262Sgreen}
195269587Sgreen
195392559Sdesstatic char *
195476262Sgreenauthmethods_get(void)
195576262Sgreen{
195676262Sgreen	Authmethod *method = NULL;
195792559Sdes	Buffer b;
195892559Sdes	char *list;
195969587Sgreen
196092559Sdes	buffer_init(&b);
196176262Sgreen	for (method = authmethods; method->name != NULL; method++) {
196276262Sgreen		if (authmethod_is_enabled(method)) {
196392559Sdes			if (buffer_len(&b) > 0)
196492559Sdes				buffer_append(&b, ",", 1);
196592559Sdes			buffer_append(&b, method->name, strlen(method->name));
196676262Sgreen		}
196776262Sgreen	}
1968323129Sdes	if ((list = sshbuf_dup_string(&b)) == NULL)
1969323129Sdes		fatal("%s: sshbuf_dup_string failed", __func__);
197092559Sdes	buffer_free(&b);
197192559Sdes	return list;
197260573Skris}
1973192595Sdes
1974