xref: /dflybsd-src/crypto/openssh/sshconnect2.c (revision ee11649955f6d79a768fb07c92b27e3ea6ebbd2a)
1*ee116499SAntonio Huete Jimenez /* $OpenBSD: sshconnect2.c,v 1.361 2022/09/17 10:33:18 djm Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4cb5eb4f1SPeter Avalos  * Copyright (c) 2008 Damien Miller.  All rights reserved.
518de8d7fSPeter Avalos  *
618de8d7fSPeter Avalos  * Redistribution and use in source and binary forms, with or without
718de8d7fSPeter Avalos  * modification, are permitted provided that the following conditions
818de8d7fSPeter Avalos  * are met:
918de8d7fSPeter Avalos  * 1. Redistributions of source code must retain the above copyright
1018de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer.
1118de8d7fSPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
1218de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
1318de8d7fSPeter Avalos  *    documentation and/or other materials provided with the distribution.
1418de8d7fSPeter Avalos  *
1518de8d7fSPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1618de8d7fSPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1718de8d7fSPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1818de8d7fSPeter Avalos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1918de8d7fSPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2018de8d7fSPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2118de8d7fSPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2218de8d7fSPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2318de8d7fSPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2418de8d7fSPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2518de8d7fSPeter Avalos  */
2618de8d7fSPeter Avalos 
2718de8d7fSPeter Avalos #include "includes.h"
2818de8d7fSPeter Avalos 
2918de8d7fSPeter Avalos #include <sys/types.h>
3018de8d7fSPeter Avalos #include <sys/socket.h>
3118de8d7fSPeter Avalos #include <sys/wait.h>
3218de8d7fSPeter Avalos #include <sys/stat.h>
3318de8d7fSPeter Avalos 
3418de8d7fSPeter Avalos #include <errno.h>
35856ea928SPeter Avalos #include <fcntl.h>
3650a69bb5SSascha Wildner #include <limits.h>
3718de8d7fSPeter Avalos #include <netdb.h>
3818de8d7fSPeter Avalos #include <pwd.h>
3918de8d7fSPeter Avalos #include <signal.h>
4018de8d7fSPeter Avalos #include <stdio.h>
4118de8d7fSPeter Avalos #include <string.h>
420cbfa66cSDaniel Fojt #include <stdarg.h>
4318de8d7fSPeter Avalos #include <unistd.h>
4436e94dc5SPeter Avalos #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
4518de8d7fSPeter Avalos #include <vis.h>
4618de8d7fSPeter Avalos #endif
4718de8d7fSPeter Avalos 
4818de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h"
4918de8d7fSPeter Avalos 
5018de8d7fSPeter Avalos #include "xmalloc.h"
5118de8d7fSPeter Avalos #include "ssh.h"
5218de8d7fSPeter Avalos #include "ssh2.h"
53664f4763Szrj #include "sshbuf.h"
5418de8d7fSPeter Avalos #include "packet.h"
5518de8d7fSPeter Avalos #include "compat.h"
5618de8d7fSPeter Avalos #include "cipher.h"
57664f4763Szrj #include "sshkey.h"
5818de8d7fSPeter Avalos #include "kex.h"
5918de8d7fSPeter Avalos #include "myproposal.h"
6018de8d7fSPeter Avalos #include "sshconnect.h"
6118de8d7fSPeter Avalos #include "authfile.h"
6218de8d7fSPeter Avalos #include "dh.h"
6318de8d7fSPeter Avalos #include "authfd.h"
6418de8d7fSPeter Avalos #include "log.h"
6518de8d7fSPeter Avalos #include "misc.h"
6636e94dc5SPeter Avalos #include "readconf.h"
6718de8d7fSPeter Avalos #include "match.h"
6818de8d7fSPeter Avalos #include "dispatch.h"
6918de8d7fSPeter Avalos #include "canohost.h"
7018de8d7fSPeter Avalos #include "msg.h"
7118de8d7fSPeter Avalos #include "pathnames.h"
7218de8d7fSPeter Avalos #include "uidswap.h"
739f304aafSPeter Avalos #include "hostfile.h"
74e9778795SPeter Avalos #include "ssherr.h"
75e9778795SPeter Avalos #include "utf8.h"
760cbfa66cSDaniel Fojt #include "ssh-sk.h"
770cbfa66cSDaniel Fojt #include "sk-api.h"
7818de8d7fSPeter Avalos 
7918de8d7fSPeter Avalos #ifdef GSSAPI
8018de8d7fSPeter Avalos #include "ssh-gss.h"
8118de8d7fSPeter Avalos #endif
8218de8d7fSPeter Avalos 
8318de8d7fSPeter Avalos /* import */
8418de8d7fSPeter Avalos extern char *client_version_string;
8518de8d7fSPeter Avalos extern char *server_version_string;
8618de8d7fSPeter Avalos extern Options options;
8718de8d7fSPeter Avalos 
8818de8d7fSPeter Avalos /*
8918de8d7fSPeter Avalos  * SSH2 key exchange
9018de8d7fSPeter Avalos  */
9118de8d7fSPeter Avalos 
9250a69bb5SSascha Wildner static char *xxx_host;
9350a69bb5SSascha Wildner static struct sockaddr *xxx_hostaddr;
9450a69bb5SSascha Wildner static const struct ssh_conn_info *xxx_conn_info;
9518de8d7fSPeter Avalos 
9618de8d7fSPeter Avalos static int
97ce74bacaSMatthew Dillon verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh)
9818de8d7fSPeter Avalos {
99*ee116499SAntonio Huete Jimenez 	int r;
100*ee116499SAntonio Huete Jimenez 
101*ee116499SAntonio Huete Jimenez 	if ((r = sshkey_check_rsa_length(hostkey,
102*ee116499SAntonio Huete Jimenez 	    options.required_rsa_size)) != 0)
103*ee116499SAntonio Huete Jimenez 		fatal_r(r, "Bad server host key");
10450a69bb5SSascha Wildner 	if (verify_host_key(xxx_host, xxx_hostaddr, hostkey,
10550a69bb5SSascha Wildner 	    xxx_conn_info) == -1)
10618de8d7fSPeter Avalos 		fatal("Host key verification failed.");
10718de8d7fSPeter Avalos 	return 0;
10818de8d7fSPeter Avalos }
10918de8d7fSPeter Avalos 
11050a69bb5SSascha Wildner /* Returns the first item from a comma-separated algorithm list */
1119f304aafSPeter Avalos static char *
11250a69bb5SSascha Wildner first_alg(const char *algs)
1139f304aafSPeter Avalos {
11450a69bb5SSascha Wildner 	char *ret, *cp;
11550a69bb5SSascha Wildner 
11650a69bb5SSascha Wildner 	ret = xstrdup(algs);
11750a69bb5SSascha Wildner 	if ((cp = strchr(ret, ',')) != NULL)
11850a69bb5SSascha Wildner 		*cp = '\0';
11950a69bb5SSascha Wildner 	return ret;
12050a69bb5SSascha Wildner }
12150a69bb5SSascha Wildner 
12250a69bb5SSascha Wildner static char *
12350a69bb5SSascha Wildner order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port,
12450a69bb5SSascha Wildner     const struct ssh_conn_info *cinfo)
12550a69bb5SSascha Wildner {
12650a69bb5SSascha Wildner 	char *oavail = NULL, *avail = NULL, *first = NULL, *last = NULL;
12750a69bb5SSascha Wildner 	char *alg = NULL, *hostname = NULL, *ret = NULL, *best = NULL;
1289f304aafSPeter Avalos 	size_t maxlen;
12950a69bb5SSascha Wildner 	struct hostkeys *hostkeys = NULL;
1309f304aafSPeter Avalos 	int ktype;
1311c188a7fSPeter Avalos 	u_int i;
1329f304aafSPeter Avalos 
1339f304aafSPeter Avalos 	/* Find all hostkeys for this hostname */
1349f304aafSPeter Avalos 	get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL);
1359f304aafSPeter Avalos 	hostkeys = init_hostkeys();
1361c188a7fSPeter Avalos 	for (i = 0; i < options.num_user_hostfiles; i++)
13750a69bb5SSascha Wildner 		load_hostkeys(hostkeys, hostname, options.user_hostfiles[i], 0);
13850a69bb5SSascha Wildner 	for (i = 0; i < options.num_system_hostfiles; i++) {
13950a69bb5SSascha Wildner 		load_hostkeys(hostkeys, hostname,
14050a69bb5SSascha Wildner 		    options.system_hostfiles[i], 0);
14150a69bb5SSascha Wildner 	}
14250a69bb5SSascha Wildner 	if (options.known_hosts_command != NULL) {
14350a69bb5SSascha Wildner 		load_hostkeys_command(hostkeys, options.known_hosts_command,
14450a69bb5SSascha Wildner 		    "ORDER", cinfo, NULL, host);
14550a69bb5SSascha Wildner 	}
14650a69bb5SSascha Wildner 	/*
14750a69bb5SSascha Wildner 	 * If a plain public key exists that matches the type of the best
14850a69bb5SSascha Wildner 	 * preference HostkeyAlgorithms, then use the whole list as is.
14950a69bb5SSascha Wildner 	 * Note that we ignore whether the best preference algorithm is a
15050a69bb5SSascha Wildner 	 * certificate type, as sshconnect.c will downgrade certs to
15150a69bb5SSascha Wildner 	 * plain keys if necessary.
15250a69bb5SSascha Wildner 	 */
15350a69bb5SSascha Wildner 	best = first_alg(options.hostkeyalgorithms);
15450a69bb5SSascha Wildner 	if (lookup_key_in_hostkeys_by_type(hostkeys,
15550a69bb5SSascha Wildner 	    sshkey_type_plain(sshkey_type_from_name(best)),
15650a69bb5SSascha Wildner 	    sshkey_ecdsa_nid_from_name(best), NULL)) {
15750a69bb5SSascha Wildner 		debug3_f("have matching best-preference key type %s, "
15850a69bb5SSascha Wildner 		    "using HostkeyAlgorithms verbatim", best);
15950a69bb5SSascha Wildner 		ret = xstrdup(options.hostkeyalgorithms);
16050a69bb5SSascha Wildner 		goto out;
16150a69bb5SSascha Wildner 	}
1629f304aafSPeter Avalos 
16350a69bb5SSascha Wildner 	/*
16450a69bb5SSascha Wildner 	 * Otherwise, prefer the host key algorithms that match known keys
16550a69bb5SSascha Wildner 	 * while keeping the ordering of HostkeyAlgorithms as much as possible.
16650a69bb5SSascha Wildner 	 */
1670cbfa66cSDaniel Fojt 	oavail = avail = xstrdup(options.hostkeyalgorithms);
1689f304aafSPeter Avalos 	maxlen = strlen(avail) + 1;
1699f304aafSPeter Avalos 	first = xmalloc(maxlen);
1709f304aafSPeter Avalos 	last = xmalloc(maxlen);
1719f304aafSPeter Avalos 	*first = *last = '\0';
1729f304aafSPeter Avalos 
1739f304aafSPeter Avalos #define ALG_APPEND(to, from) \
1749f304aafSPeter Avalos 	do { \
1759f304aafSPeter Avalos 		if (*to != '\0') \
1769f304aafSPeter Avalos 			strlcat(to, ",", maxlen); \
1779f304aafSPeter Avalos 		strlcat(to, from, maxlen); \
1789f304aafSPeter Avalos 	} while (0)
1799f304aafSPeter Avalos 
1809f304aafSPeter Avalos 	while ((alg = strsep(&avail, ",")) && *alg != '\0') {
181e9778795SPeter Avalos 		if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC)
18250a69bb5SSascha Wildner 			fatal_f("unknown alg %s", alg);
18350a69bb5SSascha Wildner 		/*
18450a69bb5SSascha Wildner 		 * If we have a @cert-authority marker in known_hosts then
18550a69bb5SSascha Wildner 		 * prefer all certificate algorithms.
18650a69bb5SSascha Wildner 		 */
18750a69bb5SSascha Wildner 		if (sshkey_type_is_cert(ktype) &&
18850a69bb5SSascha Wildner 		    lookup_marker_in_hostkeys(hostkeys, MRK_CA)) {
1899f304aafSPeter Avalos 			ALG_APPEND(first, alg);
19050a69bb5SSascha Wildner 			continue;
19150a69bb5SSascha Wildner 		}
19250a69bb5SSascha Wildner 		/* If the key appears in known_hosts then prefer it */
19350a69bb5SSascha Wildner 		if (lookup_key_in_hostkeys_by_type(hostkeys,
19450a69bb5SSascha Wildner 		    sshkey_type_plain(ktype),
19550a69bb5SSascha Wildner 		    sshkey_ecdsa_nid_from_name(alg), NULL)) {
19650a69bb5SSascha Wildner 			ALG_APPEND(first, alg);
19750a69bb5SSascha Wildner 			continue;
19850a69bb5SSascha Wildner 		}
19950a69bb5SSascha Wildner 		/* Otherwise, put it last */
2009f304aafSPeter Avalos 		ALG_APPEND(last, alg);
2019f304aafSPeter Avalos 	}
2029f304aafSPeter Avalos #undef ALG_APPEND
203e9778795SPeter Avalos 	xasprintf(&ret, "%s%s%s", first,
204e9778795SPeter Avalos 	    (*first == '\0' || *last == '\0') ? "" : ",", last);
2059f304aafSPeter Avalos 	if (*first != '\0')
20650a69bb5SSascha Wildner 		debug3_f("prefer hostkeyalgs: %s", first);
20750a69bb5SSascha Wildner 	else
20850a69bb5SSascha Wildner 		debug3_f("no algorithms matched; accept original");
20950a69bb5SSascha Wildner  out:
21050a69bb5SSascha Wildner 	free(best);
21136e94dc5SPeter Avalos 	free(first);
21236e94dc5SPeter Avalos 	free(last);
21336e94dc5SPeter Avalos 	free(hostname);
21436e94dc5SPeter Avalos 	free(oavail);
2159f304aafSPeter Avalos 	free_hostkeys(hostkeys);
2169f304aafSPeter Avalos 
2179f304aafSPeter Avalos 	return ret;
2189f304aafSPeter Avalos }
2199f304aafSPeter Avalos 
22018de8d7fSPeter Avalos void
22150a69bb5SSascha Wildner ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
22250a69bb5SSascha Wildner     const struct ssh_conn_info *cinfo)
22318de8d7fSPeter Avalos {
22436e94dc5SPeter Avalos 	char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
225664f4763Szrj 	char *s, *all_key;
226*ee116499SAntonio Huete Jimenez 	char *prop_kex = NULL, *prop_enc = NULL, *prop_hostkey = NULL;
2270cbfa66cSDaniel Fojt 	int r, use_known_hosts_order = 0;
22818de8d7fSPeter Avalos 
22918de8d7fSPeter Avalos 	xxx_host = host;
23018de8d7fSPeter Avalos 	xxx_hostaddr = hostaddr;
23150a69bb5SSascha Wildner 	xxx_conn_info = cinfo;
23218de8d7fSPeter Avalos 
2330cbfa66cSDaniel Fojt 	/*
2340cbfa66cSDaniel Fojt 	 * If the user has not specified HostkeyAlgorithms, or has only
2350cbfa66cSDaniel Fojt 	 * appended or removed algorithms from that list then prefer algorithms
2360cbfa66cSDaniel Fojt 	 * that are in the list that are supported by known_hosts keys.
2370cbfa66cSDaniel Fojt 	 */
2380cbfa66cSDaniel Fojt 	if (options.hostkeyalgorithms == NULL ||
2390cbfa66cSDaniel Fojt 	    options.hostkeyalgorithms[0] == '-' ||
2400cbfa66cSDaniel Fojt 	    options.hostkeyalgorithms[0] == '+')
2410cbfa66cSDaniel Fojt 		use_known_hosts_order = 1;
2420cbfa66cSDaniel Fojt 
2430cbfa66cSDaniel Fojt 	/* Expand or fill in HostkeyAlgorithms */
2440cbfa66cSDaniel Fojt 	all_key = sshkey_alg_list(0, 0, 1, ',');
24550a69bb5SSascha Wildner 	if ((r = kex_assemble_names(&options.hostkeyalgorithms,
24650a69bb5SSascha Wildner 	    kex_default_pk_alg(), all_key)) != 0)
24750a69bb5SSascha Wildner 		fatal_fr(r, "kex_assemble_namelist");
2480cbfa66cSDaniel Fojt 	free(all_key);
2490cbfa66cSDaniel Fojt 
250e9778795SPeter Avalos 	if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL)
25150a69bb5SSascha Wildner 		fatal_f("kex_names_cat");
252*ee116499SAntonio Huete Jimenez 	myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh, s);
25318de8d7fSPeter Avalos 	myproposal[PROPOSAL_ENC_ALGS_CTOS] =
254*ee116499SAntonio Huete Jimenez 	    myproposal[PROPOSAL_ENC_ALGS_STOC] = prop_enc =
25550a69bb5SSascha Wildner 	    compat_cipher_proposal(ssh, options.ciphers);
25618de8d7fSPeter Avalos 	myproposal[PROPOSAL_COMP_ALGS_CTOS] =
2570cbfa66cSDaniel Fojt 	    myproposal[PROPOSAL_COMP_ALGS_STOC] =
2580cbfa66cSDaniel Fojt 	    (char *)compression_alg_list(options.compression);
25918de8d7fSPeter Avalos 	myproposal[PROPOSAL_MAC_ALGS_CTOS] =
26018de8d7fSPeter Avalos 	    myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
2610cbfa66cSDaniel Fojt 	if (use_known_hosts_order) {
2620cbfa66cSDaniel Fojt 		/* Query known_hosts and prefer algorithms that appear there */
263*ee116499SAntonio Huete Jimenez 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = prop_hostkey =
26450a69bb5SSascha Wildner 		    compat_pkalg_proposal(ssh,
26550a69bb5SSascha Wildner 		    order_hostkeyalgs(host, hostaddr, port, cinfo));
2660cbfa66cSDaniel Fojt 	} else {
2670cbfa66cSDaniel Fojt 		/* Use specified HostkeyAlgorithms exactly */
268*ee116499SAntonio Huete Jimenez 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = prop_hostkey =
26950a69bb5SSascha Wildner 		    compat_pkalg_proposal(ssh, options.hostkeyalgorithms);
2709f304aafSPeter Avalos 	}
27118de8d7fSPeter Avalos 
27236e94dc5SPeter Avalos 	if (options.rekey_limit || options.rekey_interval)
273664f4763Szrj 		ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
274ce74bacaSMatthew Dillon 		    options.rekey_interval);
27518de8d7fSPeter Avalos 
27618de8d7fSPeter Avalos 	/* start key exchange */
277664f4763Szrj 	if ((r = kex_setup(ssh, myproposal)) != 0)
27850a69bb5SSascha Wildner 		fatal_r(r, "kex_setup");
27936e94dc5SPeter Avalos #ifdef WITH_OPENSSL
280664f4763Szrj 	ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client;
281664f4763Szrj 	ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client;
282664f4763Szrj 	ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client;
283664f4763Szrj 	ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client;
284664f4763Szrj 	ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client;
285664f4763Szrj 	ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
286664f4763Szrj 	ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
287e9778795SPeter Avalos # ifdef OPENSSL_HAS_ECC
288664f4763Szrj 	ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
28936e94dc5SPeter Avalos # endif
290e9778795SPeter Avalos #endif
291664f4763Szrj 	ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
29250a69bb5SSascha Wildner 	ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
293664f4763Szrj 	ssh->kex->verify_host_key=&verify_host_key_callback;
29418de8d7fSPeter Avalos 
295664f4763Szrj 	ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
29618de8d7fSPeter Avalos 
297e9778795SPeter Avalos 	/* remove ext-info from the KEX proposals for rekeying */
298e9778795SPeter Avalos 	myproposal[PROPOSAL_KEX_ALGS] =
29950a69bb5SSascha Wildner 	    compat_kex_proposal(ssh, options.kex_algorithms);
300664f4763Szrj 	if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0)
30150a69bb5SSascha Wildner 		fatal_r(r, "kex_prop2buf");
30218de8d7fSPeter Avalos 
30318de8d7fSPeter Avalos #ifdef DEBUG_KEXDH
30418de8d7fSPeter Avalos 	/* send 1st encrypted/maced/compressed message */
305664f4763Szrj 	if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 ||
306664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, "markus")) != 0 ||
307664f4763Szrj 	    (r = sshpkt_send(ssh)) != 0 ||
308664f4763Szrj 	    (r = ssh_packet_write_wait(ssh)) != 0)
30950a69bb5SSascha Wildner 		fatal_fr(r, "send packet");
31018de8d7fSPeter Avalos #endif
311*ee116499SAntonio Huete Jimenez 	/* Free only parts of proposal that were dynamically allocated here. */
312*ee116499SAntonio Huete Jimenez 	free(prop_kex);
313*ee116499SAntonio Huete Jimenez 	free(prop_enc);
314*ee116499SAntonio Huete Jimenez 	free(prop_hostkey);
31518de8d7fSPeter Avalos }
31618de8d7fSPeter Avalos 
31718de8d7fSPeter Avalos /*
31818de8d7fSPeter Avalos  * Authenticate user
31918de8d7fSPeter Avalos  */
32018de8d7fSPeter Avalos 
321e9778795SPeter Avalos typedef struct cauthctxt Authctxt;
322e9778795SPeter Avalos typedef struct cauthmethod Authmethod;
32318de8d7fSPeter Avalos typedef struct identity Identity;
32418de8d7fSPeter Avalos typedef struct idlist Idlist;
32518de8d7fSPeter Avalos 
32618de8d7fSPeter Avalos struct identity {
32718de8d7fSPeter Avalos 	TAILQ_ENTRY(identity) next;
328e9778795SPeter Avalos 	int	agent_fd;		/* >=0 if agent supports key */
329e9778795SPeter Avalos 	struct sshkey	*key;		/* public/private key */
33018de8d7fSPeter Avalos 	char	*filename;		/* comment for agent-only keys */
33118de8d7fSPeter Avalos 	int	tried;
33218de8d7fSPeter Avalos 	int	isprivate;		/* key points to the private key */
33336e94dc5SPeter Avalos 	int	userprovided;
33418de8d7fSPeter Avalos };
33518de8d7fSPeter Avalos TAILQ_HEAD(idlist, identity);
33618de8d7fSPeter Avalos 
337e9778795SPeter Avalos struct cauthctxt {
33818de8d7fSPeter Avalos 	const char *server_user;
33918de8d7fSPeter Avalos 	const char *local_user;
34018de8d7fSPeter Avalos 	const char *host;
34118de8d7fSPeter Avalos 	const char *service;
342e9778795SPeter Avalos 	struct cauthmethod *method;
343856ea928SPeter Avalos 	sig_atomic_t success;
34418de8d7fSPeter Avalos 	char *authlist;
345664f4763Szrj #ifdef GSSAPI
346664f4763Szrj 	/* gssapi */
347664f4763Szrj 	gss_OID_set gss_supported_mechs;
348664f4763Szrj 	u_int mech_tried;
349664f4763Szrj #endif
35018de8d7fSPeter Avalos 	/* pubkey */
351e9778795SPeter Avalos 	struct idlist keys;
352e9778795SPeter Avalos 	int agent_fd;
35318de8d7fSPeter Avalos 	/* hostbased */
35418de8d7fSPeter Avalos 	Sensitive *sensitive;
355e9778795SPeter Avalos 	char *oktypes, *ktypes;
356e9778795SPeter Avalos 	const char *active_ktype;
35718de8d7fSPeter Avalos 	/* kbd-interactive */
35818de8d7fSPeter Avalos 	int info_req_seen;
359664f4763Szrj 	int attempt_kbdint;
360664f4763Szrj 	/* password */
361664f4763Szrj 	int attempt_passwd;
36218de8d7fSPeter Avalos 	/* generic */
36318de8d7fSPeter Avalos 	void *methoddata;
36418de8d7fSPeter Avalos };
365e9778795SPeter Avalos 
366e9778795SPeter Avalos struct cauthmethod {
36718de8d7fSPeter Avalos 	char	*name;		/* string to compare against server's list */
368664f4763Szrj 	int	(*userauth)(struct ssh *ssh);
369664f4763Szrj 	void	(*cleanup)(struct ssh *ssh);
37018de8d7fSPeter Avalos 	int	*enabled;	/* flag in option struct that enables method */
37118de8d7fSPeter Avalos 	int	*batch_flag;	/* flag in option struct that disables method */
37218de8d7fSPeter Avalos };
37318de8d7fSPeter Avalos 
374664f4763Szrj static int input_userauth_service_accept(int, u_int32_t, struct ssh *);
375664f4763Szrj static int input_userauth_ext_info(int, u_int32_t, struct ssh *);
376664f4763Szrj static int input_userauth_success(int, u_int32_t, struct ssh *);
377664f4763Szrj static int input_userauth_failure(int, u_int32_t, struct ssh *);
378664f4763Szrj static int input_userauth_banner(int, u_int32_t, struct ssh *);
379664f4763Szrj static int input_userauth_error(int, u_int32_t, struct ssh *);
380664f4763Szrj static int input_userauth_info_req(int, u_int32_t, struct ssh *);
381664f4763Szrj static int input_userauth_pk_ok(int, u_int32_t, struct ssh *);
382664f4763Szrj static int input_userauth_passwd_changereq(int, u_int32_t, struct ssh *);
38318de8d7fSPeter Avalos 
384664f4763Szrj static int userauth_none(struct ssh *);
385664f4763Szrj static int userauth_pubkey(struct ssh *);
386664f4763Szrj static int userauth_passwd(struct ssh *);
387664f4763Szrj static int userauth_kbdint(struct ssh *);
388664f4763Szrj static int userauth_hostbased(struct ssh *);
38918de8d7fSPeter Avalos 
39018de8d7fSPeter Avalos #ifdef GSSAPI
391664f4763Szrj static int userauth_gssapi(struct ssh *);
392664f4763Szrj static void userauth_gssapi_cleanup(struct ssh *);
393664f4763Szrj static int input_gssapi_response(int type, u_int32_t, struct ssh *);
394664f4763Szrj static int input_gssapi_token(int type, u_int32_t, struct ssh *);
395664f4763Szrj static int input_gssapi_error(int, u_int32_t, struct ssh *);
396664f4763Szrj static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
39718de8d7fSPeter Avalos #endif
39818de8d7fSPeter Avalos 
399664f4763Szrj void	userauth(struct ssh *, char *);
40018de8d7fSPeter Avalos 
401664f4763Szrj static void pubkey_cleanup(struct ssh *);
402664f4763Szrj static int sign_and_send_pubkey(struct ssh *ssh, Identity *);
403*ee116499SAntonio Huete Jimenez static void pubkey_prepare(struct ssh *, Authctxt *);
404ce74bacaSMatthew Dillon static void pubkey_reset(Authctxt *);
405ce74bacaSMatthew Dillon static struct sshkey *load_identity_file(Identity *);
40618de8d7fSPeter Avalos 
40718de8d7fSPeter Avalos static Authmethod *authmethod_get(char *authlist);
40818de8d7fSPeter Avalos static Authmethod *authmethod_lookup(const char *name);
40918de8d7fSPeter Avalos static char *authmethods_get(void);
41018de8d7fSPeter Avalos 
41118de8d7fSPeter Avalos Authmethod authmethods[] = {
41218de8d7fSPeter Avalos #ifdef GSSAPI
41318de8d7fSPeter Avalos 	{"gssapi-with-mic",
41418de8d7fSPeter Avalos 		userauth_gssapi,
415664f4763Szrj 		userauth_gssapi_cleanup,
41618de8d7fSPeter Avalos 		&options.gss_authentication,
41718de8d7fSPeter Avalos 		NULL},
41818de8d7fSPeter Avalos #endif
41918de8d7fSPeter Avalos 	{"hostbased",
42018de8d7fSPeter Avalos 		userauth_hostbased,
421cb5eb4f1SPeter Avalos 		NULL,
42218de8d7fSPeter Avalos 		&options.hostbased_authentication,
42318de8d7fSPeter Avalos 		NULL},
42418de8d7fSPeter Avalos 	{"publickey",
42518de8d7fSPeter Avalos 		userauth_pubkey,
426cb5eb4f1SPeter Avalos 		NULL,
42718de8d7fSPeter Avalos 		&options.pubkey_authentication,
42818de8d7fSPeter Avalos 		NULL},
42918de8d7fSPeter Avalos 	{"keyboard-interactive",
43018de8d7fSPeter Avalos 		userauth_kbdint,
431cb5eb4f1SPeter Avalos 		NULL,
43218de8d7fSPeter Avalos 		&options.kbd_interactive_authentication,
43318de8d7fSPeter Avalos 		&options.batch_mode},
43418de8d7fSPeter Avalos 	{"password",
43518de8d7fSPeter Avalos 		userauth_passwd,
436cb5eb4f1SPeter Avalos 		NULL,
43718de8d7fSPeter Avalos 		&options.password_authentication,
43818de8d7fSPeter Avalos 		&options.batch_mode},
43918de8d7fSPeter Avalos 	{"none",
44018de8d7fSPeter Avalos 		userauth_none,
44118de8d7fSPeter Avalos 		NULL,
442cb5eb4f1SPeter Avalos 		NULL,
44318de8d7fSPeter Avalos 		NULL},
444cb5eb4f1SPeter Avalos 	{NULL, NULL, NULL, NULL, NULL}
44518de8d7fSPeter Avalos };
44618de8d7fSPeter Avalos 
44718de8d7fSPeter Avalos void
448664f4763Szrj ssh_userauth2(struct ssh *ssh, const char *local_user,
449664f4763Szrj     const char *server_user, char *host, Sensitive *sensitive)
45018de8d7fSPeter Avalos {
45118de8d7fSPeter Avalos 	Authctxt authctxt;
452e9778795SPeter Avalos 	int r;
45318de8d7fSPeter Avalos 
45418de8d7fSPeter Avalos 	if (options.preferred_authentications == NULL)
45518de8d7fSPeter Avalos 		options.preferred_authentications = authmethods_get();
45618de8d7fSPeter Avalos 
45718de8d7fSPeter Avalos 	/* setup authentication context */
45818de8d7fSPeter Avalos 	memset(&authctxt, 0, sizeof(authctxt));
45918de8d7fSPeter Avalos 	authctxt.server_user = server_user;
46018de8d7fSPeter Avalos 	authctxt.local_user = local_user;
46118de8d7fSPeter Avalos 	authctxt.host = host;
46218de8d7fSPeter Avalos 	authctxt.service = "ssh-connection";		/* service name */
46318de8d7fSPeter Avalos 	authctxt.success = 0;
46418de8d7fSPeter Avalos 	authctxt.method = authmethod_lookup("none");
46518de8d7fSPeter Avalos 	authctxt.authlist = NULL;
46618de8d7fSPeter Avalos 	authctxt.methoddata = NULL;
46718de8d7fSPeter Avalos 	authctxt.sensitive = sensitive;
468e9778795SPeter Avalos 	authctxt.active_ktype = authctxt.oktypes = authctxt.ktypes = NULL;
46918de8d7fSPeter Avalos 	authctxt.info_req_seen = 0;
470664f4763Szrj 	authctxt.attempt_kbdint = 0;
471664f4763Szrj 	authctxt.attempt_passwd = 0;
472664f4763Szrj #if GSSAPI
473664f4763Szrj 	authctxt.gss_supported_mechs = NULL;
474664f4763Szrj 	authctxt.mech_tried = 0;
475664f4763Szrj #endif
476e9778795SPeter Avalos 	authctxt.agent_fd = -1;
477*ee116499SAntonio Huete Jimenez 	pubkey_prepare(ssh, &authctxt);
478664f4763Szrj 	if (authctxt.method == NULL) {
47950a69bb5SSascha Wildner 		fatal_f("internal error: cannot send userauth none request");
480664f4763Szrj 	}
48118de8d7fSPeter Avalos 
482e9778795SPeter Avalos 	if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 ||
483e9778795SPeter Avalos 	    (r = sshpkt_put_cstring(ssh, "ssh-userauth")) != 0 ||
484e9778795SPeter Avalos 	    (r = sshpkt_send(ssh)) != 0)
48550a69bb5SSascha Wildner 		fatal_fr(r, "send packet");
48618de8d7fSPeter Avalos 
487ce74bacaSMatthew Dillon 	ssh->authctxt = &authctxt;
488e9778795SPeter Avalos 	ssh_dispatch_init(ssh, &input_userauth_error);
489e9778795SPeter Avalos 	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info);
490e9778795SPeter Avalos 	ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept);
491ce74bacaSMatthew Dillon 	ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success);	/* loop until success */
492664f4763Szrj 	pubkey_cleanup(ssh);
493ce74bacaSMatthew Dillon 	ssh->authctxt = NULL;
49418de8d7fSPeter Avalos 
495e9778795SPeter Avalos 	ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL);
49618de8d7fSPeter Avalos 
497ce74bacaSMatthew Dillon 	if (!authctxt.success)
498ce74bacaSMatthew Dillon 		fatal("Authentication failed.");
49950a69bb5SSascha Wildner 	if (ssh_packet_connection_is_on_socket(ssh)) {
50050a69bb5SSascha Wildner 		verbose("Authenticated to %s ([%s]:%d) using \"%s\".", host,
50150a69bb5SSascha Wildner 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
50250a69bb5SSascha Wildner 		    authctxt.method->name);
50350a69bb5SSascha Wildner 	} else {
50450a69bb5SSascha Wildner 		verbose("Authenticated to %s (via proxy) using \"%s\".", host,
50550a69bb5SSascha Wildner 		    authctxt.method->name);
50650a69bb5SSascha Wildner 	}
50718de8d7fSPeter Avalos }
50818de8d7fSPeter Avalos 
509e9778795SPeter Avalos /* ARGSUSED */
510664f4763Szrj static int
511ce74bacaSMatthew Dillon input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh)
512e9778795SPeter Avalos {
513e9778795SPeter Avalos 	int r;
514e9778795SPeter Avalos 
515e9778795SPeter Avalos 	if (ssh_packet_remaining(ssh) > 0) {
516e9778795SPeter Avalos 		char *reply;
517e9778795SPeter Avalos 
518e9778795SPeter Avalos 		if ((r = sshpkt_get_cstring(ssh, &reply, NULL)) != 0)
519e9778795SPeter Avalos 			goto out;
520e9778795SPeter Avalos 		debug2("service_accept: %s", reply);
521e9778795SPeter Avalos 		free(reply);
522e9778795SPeter Avalos 	} else {
523e9778795SPeter Avalos 		debug2("buggy server: service_accept w/o service");
524e9778795SPeter Avalos 	}
525e9778795SPeter Avalos 	if ((r = sshpkt_get_end(ssh)) != 0)
526e9778795SPeter Avalos 		goto out;
527e9778795SPeter Avalos 	debug("SSH2_MSG_SERVICE_ACCEPT received");
528e9778795SPeter Avalos 
529e9778795SPeter Avalos 	/* initial userauth request */
530664f4763Szrj 	userauth_none(ssh);
531e9778795SPeter Avalos 
532e9778795SPeter Avalos 	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_error);
533e9778795SPeter Avalos 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
534e9778795SPeter Avalos 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
535e9778795SPeter Avalos 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner);
536e9778795SPeter Avalos 	r = 0;
537e9778795SPeter Avalos  out:
538e9778795SPeter Avalos 	return r;
539e9778795SPeter Avalos }
540e9778795SPeter Avalos 
541e9778795SPeter Avalos /* ARGSUSED */
542664f4763Szrj static int
543ce74bacaSMatthew Dillon input_userauth_ext_info(int type, u_int32_t seqnr, struct ssh *ssh)
544e9778795SPeter Avalos {
545ce74bacaSMatthew Dillon 	return kex_input_ext_info(type, seqnr, ssh);
546e9778795SPeter Avalos }
547e9778795SPeter Avalos 
54818de8d7fSPeter Avalos void
549664f4763Szrj userauth(struct ssh *ssh, char *authlist)
55018de8d7fSPeter Avalos {
551664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
552664f4763Szrj 
553cb5eb4f1SPeter Avalos 	if (authctxt->method != NULL && authctxt->method->cleanup != NULL)
554664f4763Szrj 		authctxt->method->cleanup(ssh);
555cb5eb4f1SPeter Avalos 
55636e94dc5SPeter Avalos 	free(authctxt->methoddata);
55718de8d7fSPeter Avalos 	authctxt->methoddata = NULL;
55818de8d7fSPeter Avalos 	if (authlist == NULL) {
55918de8d7fSPeter Avalos 		authlist = authctxt->authlist;
56018de8d7fSPeter Avalos 	} else {
56136e94dc5SPeter Avalos 		free(authctxt->authlist);
56218de8d7fSPeter Avalos 		authctxt->authlist = authlist;
56318de8d7fSPeter Avalos 	}
56418de8d7fSPeter Avalos 	for (;;) {
56518de8d7fSPeter Avalos 		Authmethod *method = authmethod_get(authlist);
56618de8d7fSPeter Avalos 		if (method == NULL)
567ce74bacaSMatthew Dillon 			fatal("%s@%s: Permission denied (%s).",
568ce74bacaSMatthew Dillon 			    authctxt->server_user, authctxt->host, authlist);
56918de8d7fSPeter Avalos 		authctxt->method = method;
57018de8d7fSPeter Avalos 
57118de8d7fSPeter Avalos 		/* reset the per method handler */
572664f4763Szrj 		ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_PER_METHOD_MIN,
57318de8d7fSPeter Avalos 		    SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL);
57418de8d7fSPeter Avalos 
57518de8d7fSPeter Avalos 		/* and try new method */
576664f4763Szrj 		if (method->userauth(ssh) != 0) {
57718de8d7fSPeter Avalos 			debug2("we sent a %s packet, wait for reply", method->name);
57818de8d7fSPeter Avalos 			break;
57918de8d7fSPeter Avalos 		} else {
58018de8d7fSPeter Avalos 			debug2("we did not send a packet, disable method");
58118de8d7fSPeter Avalos 			method->enabled = NULL;
58218de8d7fSPeter Avalos 		}
58318de8d7fSPeter Avalos 	}
58418de8d7fSPeter Avalos }
58518de8d7fSPeter Avalos 
586cb5eb4f1SPeter Avalos /* ARGSUSED */
587664f4763Szrj static int
588ce74bacaSMatthew Dillon input_userauth_error(int type, u_int32_t seq, struct ssh *ssh)
58918de8d7fSPeter Avalos {
59050a69bb5SSascha Wildner 	fatal_f("bad message during authentication: type %d", type);
591e9778795SPeter Avalos 	return 0;
59218de8d7fSPeter Avalos }
59318de8d7fSPeter Avalos 
594cb5eb4f1SPeter Avalos /* ARGSUSED */
595664f4763Szrj static int
596ce74bacaSMatthew Dillon input_userauth_banner(int type, u_int32_t seq, struct ssh *ssh)
59718de8d7fSPeter Avalos {
598664f4763Szrj 	char *msg = NULL;
599664f4763Szrj 	size_t len;
600664f4763Szrj 	int r;
60118de8d7fSPeter Avalos 
60250a69bb5SSascha Wildner 	debug3_f("entering");
603664f4763Szrj 	if ((r = sshpkt_get_cstring(ssh, &msg, &len)) != 0 ||
604664f4763Szrj 	    (r = sshpkt_get_cstring(ssh, NULL, NULL)) != 0)
605664f4763Szrj 		goto out;
606e9778795SPeter Avalos 	if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO)
607e9778795SPeter Avalos 		fmprintf(stderr, "%s", msg);
608664f4763Szrj 	r = 0;
609664f4763Szrj  out:
61036e94dc5SPeter Avalos 	free(msg);
611664f4763Szrj 	return r;
61218de8d7fSPeter Avalos }
61318de8d7fSPeter Avalos 
614cb5eb4f1SPeter Avalos /* ARGSUSED */
615664f4763Szrj static int
616ce74bacaSMatthew Dillon input_userauth_success(int type, u_int32_t seq, struct ssh *ssh)
61718de8d7fSPeter Avalos {
618ce74bacaSMatthew Dillon 	Authctxt *authctxt = ssh->authctxt;
619856ea928SPeter Avalos 
62018de8d7fSPeter Avalos 	if (authctxt == NULL)
62150a69bb5SSascha Wildner 		fatal_f("no authentication context");
62236e94dc5SPeter Avalos 	free(authctxt->authlist);
62318de8d7fSPeter Avalos 	authctxt->authlist = NULL;
624856ea928SPeter Avalos 	if (authctxt->method != NULL && authctxt->method->cleanup != NULL)
625664f4763Szrj 		authctxt->method->cleanup(ssh);
62636e94dc5SPeter Avalos 	free(authctxt->methoddata);
62718de8d7fSPeter Avalos 	authctxt->methoddata = NULL;
62818de8d7fSPeter Avalos 	authctxt->success = 1;			/* break out */
629e9778795SPeter Avalos 	return 0;
63018de8d7fSPeter Avalos }
63118de8d7fSPeter Avalos 
632664f4763Szrj #if 0
633664f4763Szrj static int
634ce74bacaSMatthew Dillon input_userauth_success_unexpected(int type, u_int32_t seq, struct ssh *ssh)
635856ea928SPeter Avalos {
636ce74bacaSMatthew Dillon 	Authctxt *authctxt = ssh->authctxt;
637856ea928SPeter Avalos 
638856ea928SPeter Avalos 	if (authctxt == NULL)
63950a69bb5SSascha Wildner 		fatal_f("no authentication context");
640856ea928SPeter Avalos 
641856ea928SPeter Avalos 	fatal("Unexpected authentication success during %s.",
642856ea928SPeter Avalos 	    authctxt->method->name);
643e9778795SPeter Avalos 	return 0;
644856ea928SPeter Avalos }
645664f4763Szrj #endif
646856ea928SPeter Avalos 
647cb5eb4f1SPeter Avalos /* ARGSUSED */
648664f4763Szrj static int
649ce74bacaSMatthew Dillon input_userauth_failure(int type, u_int32_t seq, struct ssh *ssh)
65018de8d7fSPeter Avalos {
651ce74bacaSMatthew Dillon 	Authctxt *authctxt = ssh->authctxt;
65218de8d7fSPeter Avalos 	char *authlist = NULL;
653664f4763Szrj 	u_char partial;
65418de8d7fSPeter Avalos 
65518de8d7fSPeter Avalos 	if (authctxt == NULL)
65618de8d7fSPeter Avalos 		fatal("input_userauth_failure: no authentication context");
65718de8d7fSPeter Avalos 
6580cbfa66cSDaniel Fojt 	if (sshpkt_get_cstring(ssh, &authlist, NULL) != 0 ||
6590cbfa66cSDaniel Fojt 	    sshpkt_get_u8(ssh, &partial) != 0 ||
6600cbfa66cSDaniel Fojt 	    sshpkt_get_end(ssh) != 0)
661664f4763Szrj 		goto out;
66218de8d7fSPeter Avalos 
66336e94dc5SPeter Avalos 	if (partial != 0) {
66450a69bb5SSascha Wildner 		verbose("Authenticated using \"%s\" with partial success.",
66550a69bb5SSascha Wildner 		    authctxt->method->name);
66636e94dc5SPeter Avalos 		/* reset state */
667ce74bacaSMatthew Dillon 		pubkey_reset(authctxt);
66836e94dc5SPeter Avalos 	}
66918de8d7fSPeter Avalos 	debug("Authentications that can continue: %s", authlist);
67018de8d7fSPeter Avalos 
671664f4763Szrj 	userauth(ssh, authlist);
672664f4763Szrj 	authlist = NULL;
673664f4763Szrj  out:
674664f4763Szrj 	free(authlist);
675e9778795SPeter Avalos 	return 0;
67618de8d7fSPeter Avalos }
677cb5eb4f1SPeter Avalos 
678664f4763Szrj /*
679664f4763Szrj  * Format an identity for logging including filename, key type, fingerprint
680664f4763Szrj  * and location (agent, etc.). Caller must free.
681664f4763Szrj  */
682664f4763Szrj static char *
683664f4763Szrj format_identity(Identity *id)
684664f4763Szrj {
685664f4763Szrj 	char *fp = NULL, *ret = NULL;
6860cbfa66cSDaniel Fojt 	const char *note = "";
687664f4763Szrj 
688664f4763Szrj 	if (id->key != NULL) {
689664f4763Szrj 		fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
690664f4763Szrj 		    SSH_FP_DEFAULT);
691664f4763Szrj 	}
6920cbfa66cSDaniel Fojt 	if (id->key) {
6930cbfa66cSDaniel Fojt 		if ((id->key->flags & SSHKEY_FLAG_EXT) != 0)
6940cbfa66cSDaniel Fojt 			note = " token";
6950cbfa66cSDaniel Fojt 		else if (sshkey_is_sk(id->key))
6960cbfa66cSDaniel Fojt 			note = " authenticator";
6970cbfa66cSDaniel Fojt 	}
698664f4763Szrj 	xasprintf(&ret, "%s %s%s%s%s%s%s",
699664f4763Szrj 	    id->filename,
700664f4763Szrj 	    id->key ? sshkey_type(id->key) : "", id->key ? " " : "",
701664f4763Szrj 	    fp ? fp : "",
7020cbfa66cSDaniel Fojt 	    id->userprovided ? " explicit" : "", note,
703664f4763Szrj 	    id->agent_fd != -1 ? " agent" : "");
704664f4763Szrj 	free(fp);
705664f4763Szrj 	return ret;
706664f4763Szrj }
707664f4763Szrj 
708cb5eb4f1SPeter Avalos /* ARGSUSED */
709664f4763Szrj static int
710ce74bacaSMatthew Dillon input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
71118de8d7fSPeter Avalos {
712ce74bacaSMatthew Dillon 	Authctxt *authctxt = ssh->authctxt;
713ce74bacaSMatthew Dillon 	struct sshkey *key = NULL;
71418de8d7fSPeter Avalos 	Identity *id = NULL;
715664f4763Szrj 	int pktype, found = 0, sent = 0;
716664f4763Szrj 	size_t blen;
717664f4763Szrj 	char *pkalg = NULL, *fp = NULL, *ident = NULL;
718664f4763Szrj 	u_char *pkblob = NULL;
719664f4763Szrj 	int r;
72018de8d7fSPeter Avalos 
72118de8d7fSPeter Avalos 	if (authctxt == NULL)
72218de8d7fSPeter Avalos 		fatal("input_userauth_pk_ok: no authentication context");
72318de8d7fSPeter Avalos 
724664f4763Szrj 	if ((r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
725664f4763Szrj 	    (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 ||
726664f4763Szrj 	    (r = sshpkt_get_end(ssh)) != 0)
727664f4763Szrj 		goto done;
72818de8d7fSPeter Avalos 
729664f4763Szrj 	if ((pktype = sshkey_type_from_name(pkalg)) == KEY_UNSPEC) {
73050a69bb5SSascha Wildner 		debug_f("server sent unknown pkalg %s", pkalg);
73118de8d7fSPeter Avalos 		goto done;
73218de8d7fSPeter Avalos 	}
733664f4763Szrj 	if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
73450a69bb5SSascha Wildner 		debug_r(r, "no key from blob. pkalg %s", pkalg);
73518de8d7fSPeter Avalos 		goto done;
73618de8d7fSPeter Avalos 	}
73718de8d7fSPeter Avalos 	if (key->type != pktype) {
73818de8d7fSPeter Avalos 		error("input_userauth_pk_ok: type mismatch "
73918de8d7fSPeter Avalos 		    "for decoded key (received %d, expected %d)",
74018de8d7fSPeter Avalos 		    key->type, pktype);
74118de8d7fSPeter Avalos 		goto done;
74218de8d7fSPeter Avalos 	}
74318de8d7fSPeter Avalos 
74418de8d7fSPeter Avalos 	/*
74518de8d7fSPeter Avalos 	 * search keys in the reverse order, because last candidate has been
74618de8d7fSPeter Avalos 	 * moved to the end of the queue.  this also avoids confusion by
74718de8d7fSPeter Avalos 	 * duplicate keys
74818de8d7fSPeter Avalos 	 */
74918de8d7fSPeter Avalos 	TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) {
750664f4763Szrj 		if (sshkey_equal(key, id->key)) {
751664f4763Szrj 			found = 1;
75218de8d7fSPeter Avalos 			break;
75318de8d7fSPeter Avalos 		}
75418de8d7fSPeter Avalos 	}
755664f4763Szrj 	if (!found || id == NULL) {
756664f4763Szrj 		fp = sshkey_fingerprint(key, options.fingerprint_hash,
757664f4763Szrj 		    SSH_FP_DEFAULT);
75850a69bb5SSascha Wildner 		error_f("server replied with unknown key: %s %s",
759664f4763Szrj 		    sshkey_type(key), fp == NULL ? "<ERROR>" : fp);
760664f4763Szrj 		goto done;
761664f4763Szrj 	}
762664f4763Szrj 	ident = format_identity(id);
763664f4763Szrj 	debug("Server accepts key: %s", ident);
764664f4763Szrj 	sent = sign_and_send_pubkey(ssh, id);
765664f4763Szrj 	r = 0;
76618de8d7fSPeter Avalos  done:
767664f4763Szrj 	sshkey_free(key);
768664f4763Szrj 	free(ident);
769664f4763Szrj 	free(fp);
77036e94dc5SPeter Avalos 	free(pkalg);
77136e94dc5SPeter Avalos 	free(pkblob);
77218de8d7fSPeter Avalos 
77318de8d7fSPeter Avalos 	/* try another method if we did not send a packet */
774664f4763Szrj 	if (r == 0 && sent == 0)
775664f4763Szrj 		userauth(ssh, NULL);
776664f4763Szrj 	return r;
77718de8d7fSPeter Avalos }
77818de8d7fSPeter Avalos 
77918de8d7fSPeter Avalos #ifdef GSSAPI
780664f4763Szrj static int
781664f4763Szrj userauth_gssapi(struct ssh *ssh)
78218de8d7fSPeter Avalos {
783664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
78418de8d7fSPeter Avalos 	Gssctxt *gssctxt = NULL;
78518de8d7fSPeter Avalos 	OM_uint32 min;
786664f4763Szrj 	int r, ok = 0;
787664f4763Szrj 	gss_OID mech = NULL;
78818de8d7fSPeter Avalos 
78918de8d7fSPeter Avalos 	/* Try one GSSAPI method at a time, rather than sending them all at
79018de8d7fSPeter Avalos 	 * once. */
79118de8d7fSPeter Avalos 
792664f4763Szrj 	if (authctxt->gss_supported_mechs == NULL)
793664f4763Szrj 		gss_indicate_mechs(&min, &authctxt->gss_supported_mechs);
79418de8d7fSPeter Avalos 
795664f4763Szrj 	/* Check to see whether the mechanism is usable before we offer it */
796664f4763Szrj 	while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
797664f4763Szrj 	    !ok) {
798664f4763Szrj 		mech = &authctxt->gss_supported_mechs->
799664f4763Szrj 		    elements[authctxt->mech_tried];
80018de8d7fSPeter Avalos 		/* My DER encoding requires length<128 */
801664f4763Szrj 		if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
802664f4763Szrj 		    mech, authctxt->host)) {
80318de8d7fSPeter Avalos 			ok = 1; /* Mechanism works */
80418de8d7fSPeter Avalos 		} else {
805664f4763Szrj 			authctxt->mech_tried++;
80618de8d7fSPeter Avalos 		}
80718de8d7fSPeter Avalos 	}
80818de8d7fSPeter Avalos 
809664f4763Szrj 	if (!ok || mech == NULL)
81018de8d7fSPeter Avalos 		return 0;
81118de8d7fSPeter Avalos 
81218de8d7fSPeter Avalos 	authctxt->methoddata=(void *)gssctxt;
81318de8d7fSPeter Avalos 
814664f4763Szrj 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
815664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
816664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
817664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
818664f4763Szrj 	    (r = sshpkt_put_u32(ssh, 1)) != 0 ||
819664f4763Szrj 	    (r = sshpkt_put_u32(ssh, (mech->length) + 2)) != 0 ||
820664f4763Szrj 	    (r = sshpkt_put_u8(ssh, SSH_GSS_OIDTYPE)) != 0 ||
821664f4763Szrj 	    (r = sshpkt_put_u8(ssh, mech->length)) != 0 ||
822664f4763Szrj 	    (r = sshpkt_put(ssh, mech->elements, mech->length)) != 0 ||
823664f4763Szrj 	    (r = sshpkt_send(ssh)) != 0)
82450a69bb5SSascha Wildner 		fatal_fr(r, "send packet");
82518de8d7fSPeter Avalos 
826664f4763Szrj 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response);
827664f4763Szrj 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
828664f4763Szrj 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error);
829664f4763Szrj 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
83018de8d7fSPeter Avalos 
831664f4763Szrj 	authctxt->mech_tried++; /* Move along to next candidate */
83218de8d7fSPeter Avalos 
83318de8d7fSPeter Avalos 	return 1;
83418de8d7fSPeter Avalos }
83518de8d7fSPeter Avalos 
836664f4763Szrj static void
837664f4763Szrj userauth_gssapi_cleanup(struct ssh *ssh)
838664f4763Szrj {
839664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
840664f4763Szrj 	Gssctxt *gssctxt = (Gssctxt *)authctxt->methoddata;
841664f4763Szrj 
842664f4763Szrj 	ssh_gssapi_delete_ctx(&gssctxt);
843664f4763Szrj 	authctxt->methoddata = NULL;
844664f4763Szrj 
845664f4763Szrj 	free(authctxt->gss_supported_mechs);
846664f4763Szrj 	authctxt->gss_supported_mechs = NULL;
847664f4763Szrj }
848664f4763Szrj 
84918de8d7fSPeter Avalos static OM_uint32
850ce74bacaSMatthew Dillon process_gssapi_token(struct ssh *ssh, gss_buffer_t recv_tok)
85118de8d7fSPeter Avalos {
852ce74bacaSMatthew Dillon 	Authctxt *authctxt = ssh->authctxt;
85318de8d7fSPeter Avalos 	Gssctxt *gssctxt = authctxt->methoddata;
85418de8d7fSPeter Avalos 	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
85518de8d7fSPeter Avalos 	gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
85618de8d7fSPeter Avalos 	gss_buffer_desc gssbuf;
85718de8d7fSPeter Avalos 	OM_uint32 status, ms, flags;
858664f4763Szrj 	int r;
85918de8d7fSPeter Avalos 
86018de8d7fSPeter Avalos 	status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
86118de8d7fSPeter Avalos 	    recv_tok, &send_tok, &flags);
86218de8d7fSPeter Avalos 
86318de8d7fSPeter Avalos 	if (send_tok.length > 0) {
864664f4763Szrj 		u_char type = GSS_ERROR(status) ?
865664f4763Szrj 		    SSH2_MSG_USERAUTH_GSSAPI_ERRTOK :
866664f4763Szrj 		    SSH2_MSG_USERAUTH_GSSAPI_TOKEN;
86718de8d7fSPeter Avalos 
868664f4763Szrj 		if ((r = sshpkt_start(ssh, type)) != 0 ||
869664f4763Szrj 		    (r = sshpkt_put_string(ssh, send_tok.value,
870664f4763Szrj 		    send_tok.length)) != 0 ||
871664f4763Szrj 		    (r = sshpkt_send(ssh)) != 0)
87250a69bb5SSascha Wildner 			fatal_fr(r, "send %u packet", type);
873664f4763Szrj 
87418de8d7fSPeter Avalos 		gss_release_buffer(&ms, &send_tok);
87518de8d7fSPeter Avalos 	}
87618de8d7fSPeter Avalos 
87718de8d7fSPeter Avalos 	if (status == GSS_S_COMPLETE) {
87818de8d7fSPeter Avalos 		/* send either complete or MIC, depending on mechanism */
87918de8d7fSPeter Avalos 		if (!(flags & GSS_C_INTEG_FLAG)) {
880664f4763Szrj 			if ((r = sshpkt_start(ssh,
881664f4763Szrj 			    SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE)) != 0 ||
882664f4763Szrj 			    (r = sshpkt_send(ssh)) != 0)
88350a69bb5SSascha Wildner 				fatal_fr(r, "send completion");
88418de8d7fSPeter Avalos 		} else {
885664f4763Szrj 			struct sshbuf *b;
886664f4763Szrj 
887664f4763Szrj 			if ((b = sshbuf_new()) == NULL)
88850a69bb5SSascha Wildner 				fatal_f("sshbuf_new failed");
889664f4763Szrj 			ssh_gssapi_buildmic(b, authctxt->server_user,
89050a69bb5SSascha Wildner 			    authctxt->service, "gssapi-with-mic",
89150a69bb5SSascha Wildner 			    ssh->kex->session_id);
89218de8d7fSPeter Avalos 
893664f4763Szrj 			if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
89450a69bb5SSascha Wildner 				fatal_f("sshbuf_mutable_ptr failed");
895664f4763Szrj 			gssbuf.length = sshbuf_len(b);
89618de8d7fSPeter Avalos 
89718de8d7fSPeter Avalos 			status = ssh_gssapi_sign(gssctxt, &gssbuf, &mic);
89818de8d7fSPeter Avalos 
89918de8d7fSPeter Avalos 			if (!GSS_ERROR(status)) {
900664f4763Szrj 				if ((r = sshpkt_start(ssh,
901664f4763Szrj 				    SSH2_MSG_USERAUTH_GSSAPI_MIC)) != 0 ||
902664f4763Szrj 				    (r = sshpkt_put_string(ssh, mic.value,
903664f4763Szrj 				    mic.length)) != 0 ||
904664f4763Szrj 				    (r = sshpkt_send(ssh)) != 0)
90550a69bb5SSascha Wildner 					fatal_fr(r, "send MIC");
90618de8d7fSPeter Avalos 			}
90718de8d7fSPeter Avalos 
908664f4763Szrj 			sshbuf_free(b);
90918de8d7fSPeter Avalos 			gss_release_buffer(&ms, &mic);
91018de8d7fSPeter Avalos 		}
91118de8d7fSPeter Avalos 	}
91218de8d7fSPeter Avalos 
91318de8d7fSPeter Avalos 	return status;
91418de8d7fSPeter Avalos }
91518de8d7fSPeter Avalos 
916cb5eb4f1SPeter Avalos /* ARGSUSED */
917664f4763Szrj static int
918ce74bacaSMatthew Dillon input_gssapi_response(int type, u_int32_t plen, struct ssh *ssh)
91918de8d7fSPeter Avalos {
920ce74bacaSMatthew Dillon 	Authctxt *authctxt = ssh->authctxt;
92118de8d7fSPeter Avalos 	Gssctxt *gssctxt;
922664f4763Szrj 	size_t oidlen;
923664f4763Szrj 	u_char *oidv = NULL;
924664f4763Szrj 	int r;
92518de8d7fSPeter Avalos 
92618de8d7fSPeter Avalos 	if (authctxt == NULL)
92718de8d7fSPeter Avalos 		fatal("input_gssapi_response: no authentication context");
92818de8d7fSPeter Avalos 	gssctxt = authctxt->methoddata;
92918de8d7fSPeter Avalos 
93018de8d7fSPeter Avalos 	/* Setup our OID */
931664f4763Szrj 	if ((r = sshpkt_get_string(ssh, &oidv, &oidlen)) != 0)
932664f4763Szrj 		goto done;
93318de8d7fSPeter Avalos 
93418de8d7fSPeter Avalos 	if (oidlen <= 2 ||
93518de8d7fSPeter Avalos 	    oidv[0] != SSH_GSS_OIDTYPE ||
93618de8d7fSPeter Avalos 	    oidv[1] != oidlen - 2) {
93718de8d7fSPeter Avalos 		debug("Badly encoded mechanism OID received");
938664f4763Szrj 		userauth(ssh, NULL);
939664f4763Szrj 		goto ok;
94018de8d7fSPeter Avalos 	}
94118de8d7fSPeter Avalos 
94218de8d7fSPeter Avalos 	if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2))
94318de8d7fSPeter Avalos 		fatal("Server returned different OID than expected");
94418de8d7fSPeter Avalos 
945664f4763Szrj 	if ((r = sshpkt_get_end(ssh)) != 0)
946664f4763Szrj 		goto done;
94718de8d7fSPeter Avalos 
948ce74bacaSMatthew Dillon 	if (GSS_ERROR(process_gssapi_token(ssh, GSS_C_NO_BUFFER))) {
94918de8d7fSPeter Avalos 		/* Start again with next method on list */
95018de8d7fSPeter Avalos 		debug("Trying to start again");
951664f4763Szrj 		userauth(ssh, NULL);
952664f4763Szrj 		goto ok;
95318de8d7fSPeter Avalos 	}
954664f4763Szrj  ok:
955664f4763Szrj 	r = 0;
956664f4763Szrj  done:
957664f4763Szrj 	free(oidv);
958664f4763Szrj 	return r;
95918de8d7fSPeter Avalos }
96018de8d7fSPeter Avalos 
961cb5eb4f1SPeter Avalos /* ARGSUSED */
962664f4763Szrj static int
963ce74bacaSMatthew Dillon input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh)
96418de8d7fSPeter Avalos {
965ce74bacaSMatthew Dillon 	Authctxt *authctxt = ssh->authctxt;
96618de8d7fSPeter Avalos 	gss_buffer_desc recv_tok;
967664f4763Szrj 	u_char *p = NULL;
968664f4763Szrj 	size_t len;
96918de8d7fSPeter Avalos 	OM_uint32 status;
970664f4763Szrj 	int r;
97118de8d7fSPeter Avalos 
97218de8d7fSPeter Avalos 	if (authctxt == NULL)
97318de8d7fSPeter Avalos 		fatal("input_gssapi_response: no authentication context");
97418de8d7fSPeter Avalos 
975664f4763Szrj 	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
976664f4763Szrj 	    (r = sshpkt_get_end(ssh)) != 0)
977664f4763Szrj 		goto out;
97818de8d7fSPeter Avalos 
979664f4763Szrj 	recv_tok.value = p;
980664f4763Szrj 	recv_tok.length = len;
981ce74bacaSMatthew Dillon 	status = process_gssapi_token(ssh, &recv_tok);
98218de8d7fSPeter Avalos 
98318de8d7fSPeter Avalos 	/* Start again with the next method in the list */
984664f4763Szrj 	if (GSS_ERROR(status)) {
985664f4763Szrj 		userauth(ssh, NULL);
986664f4763Szrj 		/* ok */
98718de8d7fSPeter Avalos 	}
988664f4763Szrj 	r = 0;
989664f4763Szrj  out:
990664f4763Szrj 	free(p);
991664f4763Szrj 	return r;
99218de8d7fSPeter Avalos }
99318de8d7fSPeter Avalos 
994cb5eb4f1SPeter Avalos /* ARGSUSED */
995664f4763Szrj static int
996ce74bacaSMatthew Dillon input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh)
99718de8d7fSPeter Avalos {
998ce74bacaSMatthew Dillon 	Authctxt *authctxt = ssh->authctxt;
99918de8d7fSPeter Avalos 	Gssctxt *gssctxt;
100018de8d7fSPeter Avalos 	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
100118de8d7fSPeter Avalos 	gss_buffer_desc recv_tok;
100236e94dc5SPeter Avalos 	OM_uint32 ms;
1003664f4763Szrj 	u_char *p = NULL;
1004664f4763Szrj 	size_t len;
1005664f4763Szrj 	int r;
100618de8d7fSPeter Avalos 
100718de8d7fSPeter Avalos 	if (authctxt == NULL)
100818de8d7fSPeter Avalos 		fatal("input_gssapi_response: no authentication context");
100918de8d7fSPeter Avalos 	gssctxt = authctxt->methoddata;
101018de8d7fSPeter Avalos 
1011664f4763Szrj 	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
1012664f4763Szrj 	    (r = sshpkt_get_end(ssh)) != 0) {
1013664f4763Szrj 		free(p);
1014664f4763Szrj 		return r;
1015664f4763Szrj 	}
101618de8d7fSPeter Avalos 
101718de8d7fSPeter Avalos 	/* Stick it into GSSAPI and see what it says */
1018664f4763Szrj 	recv_tok.value = p;
1019664f4763Szrj 	recv_tok.length = len;
102036e94dc5SPeter Avalos 	(void)ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
102118de8d7fSPeter Avalos 	    &recv_tok, &send_tok, NULL);
1022664f4763Szrj 	free(p);
102318de8d7fSPeter Avalos 	gss_release_buffer(&ms, &send_tok);
102418de8d7fSPeter Avalos 
102518de8d7fSPeter Avalos 	/* Server will be returning a failed packet after this one */
1026e9778795SPeter Avalos 	return 0;
102718de8d7fSPeter Avalos }
102818de8d7fSPeter Avalos 
1029cb5eb4f1SPeter Avalos /* ARGSUSED */
1030664f4763Szrj static int
1031ce74bacaSMatthew Dillon input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
103218de8d7fSPeter Avalos {
1033664f4763Szrj 	char *msg = NULL;
1034664f4763Szrj 	char *lang = NULL;
1035664f4763Szrj 	int r;
103618de8d7fSPeter Avalos 
1037664f4763Szrj 	if ((r = sshpkt_get_u32(ssh, NULL)) != 0 ||	/* maj */
1038664f4763Szrj 	    (r = sshpkt_get_u32(ssh, NULL)) != 0 ||	/* min */
1039664f4763Szrj 	    (r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 ||
1040664f4763Szrj 	    (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0)
1041664f4763Szrj 		goto out;
1042664f4763Szrj 	r = sshpkt_get_end(ssh);
104318de8d7fSPeter Avalos 	debug("Server GSSAPI Error:\n%s", msg);
1044664f4763Szrj  out:
104536e94dc5SPeter Avalos 	free(msg);
104636e94dc5SPeter Avalos 	free(lang);
1047664f4763Szrj 	return r;
104818de8d7fSPeter Avalos }
104918de8d7fSPeter Avalos #endif /* GSSAPI */
105018de8d7fSPeter Avalos 
1051664f4763Szrj static int
1052664f4763Szrj userauth_none(struct ssh *ssh)
105318de8d7fSPeter Avalos {
1054664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
1055664f4763Szrj 	int r;
1056664f4763Szrj 
105718de8d7fSPeter Avalos 	/* initial userauth request */
1058664f4763Szrj 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1059664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1060664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1061664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1062664f4763Szrj 	    (r = sshpkt_send(ssh)) != 0)
106350a69bb5SSascha Wildner 		fatal_fr(r, "send packet");
106418de8d7fSPeter Avalos 	return 1;
106518de8d7fSPeter Avalos }
106618de8d7fSPeter Avalos 
1067664f4763Szrj static int
1068664f4763Szrj userauth_passwd(struct ssh *ssh)
106918de8d7fSPeter Avalos {
1070664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
1071664f4763Szrj 	char *password, *prompt = NULL;
1072856ea928SPeter Avalos 	const char *host = options.host_key_alias ?  options.host_key_alias :
1073856ea928SPeter Avalos 	    authctxt->host;
1074664f4763Szrj 	int r;
107518de8d7fSPeter Avalos 
1076664f4763Szrj 	if (authctxt->attempt_passwd++ >= options.number_of_password_prompts)
107718de8d7fSPeter Avalos 		return 0;
107818de8d7fSPeter Avalos 
1079664f4763Szrj 	if (authctxt->attempt_passwd != 1)
108018de8d7fSPeter Avalos 		error("Permission denied, please try again.");
108118de8d7fSPeter Avalos 
1082664f4763Szrj 	xasprintf(&prompt, "%s@%s's password: ", authctxt->server_user, host);
108318de8d7fSPeter Avalos 	password = read_passphrase(prompt, 0);
1084664f4763Szrj 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1085664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1086664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1087664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1088664f4763Szrj 	    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
1089664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, password)) != 0 ||
1090664f4763Szrj 	    (r = sshpkt_add_padding(ssh, 64)) != 0 ||
1091664f4763Szrj 	    (r = sshpkt_send(ssh)) != 0)
109250a69bb5SSascha Wildner 		fatal_fr(r, "send packet");
109318de8d7fSPeter Avalos 
1094664f4763Szrj 	free(prompt);
1095664f4763Szrj 	if (password != NULL)
1096664f4763Szrj 		freezero(password, strlen(password));
1097664f4763Szrj 
1098664f4763Szrj 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
109918de8d7fSPeter Avalos 	    &input_userauth_passwd_changereq);
110018de8d7fSPeter Avalos 
110118de8d7fSPeter Avalos 	return 1;
110218de8d7fSPeter Avalos }
1103cb5eb4f1SPeter Avalos 
110418de8d7fSPeter Avalos /*
110518de8d7fSPeter Avalos  * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST
110618de8d7fSPeter Avalos  */
1107cb5eb4f1SPeter Avalos /* ARGSUSED */
1108664f4763Szrj static int
1109ce74bacaSMatthew Dillon input_userauth_passwd_changereq(int type, u_int32_t seqnr, struct ssh *ssh)
111018de8d7fSPeter Avalos {
1111ce74bacaSMatthew Dillon 	Authctxt *authctxt = ssh->authctxt;
1112664f4763Szrj 	char *info = NULL, *lang = NULL, *password = NULL, *retype = NULL;
1113ce74bacaSMatthew Dillon 	char prompt[256];
1114ce74bacaSMatthew Dillon 	const char *host;
1115664f4763Szrj 	int r;
111618de8d7fSPeter Avalos 
111718de8d7fSPeter Avalos 	debug2("input_userauth_passwd_changereq");
111818de8d7fSPeter Avalos 
111918de8d7fSPeter Avalos 	if (authctxt == NULL)
112018de8d7fSPeter Avalos 		fatal("input_userauth_passwd_changereq: "
112118de8d7fSPeter Avalos 		    "no authentication context");
1122ce74bacaSMatthew Dillon 	host = options.host_key_alias ? options.host_key_alias : authctxt->host;
112318de8d7fSPeter Avalos 
1124664f4763Szrj 	if ((r = sshpkt_get_cstring(ssh, &info, NULL)) != 0 ||
1125664f4763Szrj 	    (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0)
1126664f4763Szrj 		goto out;
112718de8d7fSPeter Avalos 	if (strlen(info) > 0)
112818de8d7fSPeter Avalos 		logit("%s", info);
1129664f4763Szrj 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1130664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1131664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1132664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1133664f4763Szrj 	    (r = sshpkt_put_u8(ssh, 1)) != 0)	/* additional info */
1134664f4763Szrj 		goto out;
1135664f4763Szrj 
113618de8d7fSPeter Avalos 	snprintf(prompt, sizeof(prompt),
113718de8d7fSPeter Avalos 	    "Enter %.30s@%.128s's old password: ",
1138856ea928SPeter Avalos 	    authctxt->server_user, host);
113918de8d7fSPeter Avalos 	password = read_passphrase(prompt, 0);
1140664f4763Szrj 	if ((r = sshpkt_put_cstring(ssh, password)) != 0)
1141664f4763Szrj 		goto out;
1142664f4763Szrj 
1143664f4763Szrj 	freezero(password, strlen(password));
114418de8d7fSPeter Avalos 	password = NULL;
114518de8d7fSPeter Avalos 	while (password == NULL) {
114618de8d7fSPeter Avalos 		snprintf(prompt, sizeof(prompt),
114718de8d7fSPeter Avalos 		    "Enter %.30s@%.128s's new password: ",
1148856ea928SPeter Avalos 		    authctxt->server_user, host);
114918de8d7fSPeter Avalos 		password = read_passphrase(prompt, RP_ALLOW_EOF);
115018de8d7fSPeter Avalos 		if (password == NULL) {
115118de8d7fSPeter Avalos 			/* bail out */
1152664f4763Szrj 			r = 0;
1153664f4763Szrj 			goto out;
115418de8d7fSPeter Avalos 		}
115518de8d7fSPeter Avalos 		snprintf(prompt, sizeof(prompt),
115618de8d7fSPeter Avalos 		    "Retype %.30s@%.128s's new password: ",
1157856ea928SPeter Avalos 		    authctxt->server_user, host);
115818de8d7fSPeter Avalos 		retype = read_passphrase(prompt, 0);
115918de8d7fSPeter Avalos 		if (strcmp(password, retype) != 0) {
1160664f4763Szrj 			freezero(password, strlen(password));
116118de8d7fSPeter Avalos 			logit("Mismatch; try again, EOF to quit.");
116218de8d7fSPeter Avalos 			password = NULL;
116318de8d7fSPeter Avalos 		}
1164664f4763Szrj 		freezero(retype, strlen(retype));
116518de8d7fSPeter Avalos 	}
1166664f4763Szrj 	if ((r = sshpkt_put_cstring(ssh, password)) != 0 ||
1167664f4763Szrj 	    (r = sshpkt_add_padding(ssh, 64)) != 0 ||
1168664f4763Szrj 	    (r = sshpkt_send(ssh)) != 0)
1169664f4763Szrj 		goto out;
117018de8d7fSPeter Avalos 
1171664f4763Szrj 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
117218de8d7fSPeter Avalos 	    &input_userauth_passwd_changereq);
1173664f4763Szrj 	r = 0;
1174664f4763Szrj  out:
1175664f4763Szrj 	if (password)
1176664f4763Szrj 		freezero(password, strlen(password));
1177664f4763Szrj 	free(info);
1178664f4763Szrj 	free(lang);
1179664f4763Szrj 	return r;
1180e9778795SPeter Avalos }
1181e9778795SPeter Avalos 
1182664f4763Szrj /*
1183664f4763Szrj  * Select an algorithm for publickey signatures.
1184664f4763Szrj  * Returns algorithm (caller must free) or NULL if no mutual algorithm found.
1185664f4763Szrj  *
1186664f4763Szrj  * Call with ssh==NULL to ignore server-sig-algs extension list and
1187664f4763Szrj  * only attempt with the key's base signature type.
1188664f4763Szrj  */
1189664f4763Szrj static char *
1190664f4763Szrj key_sig_algorithm(struct ssh *ssh, const struct sshkey *key)
1191e9778795SPeter Avalos {
1192664f4763Szrj 	char *allowed, *oallowed, *cp, *tmp, *alg = NULL;
119350a69bb5SSascha Wildner 	const char *server_sig_algs;
1194e9778795SPeter Avalos 
1195664f4763Szrj 	/*
1196664f4763Szrj 	 * The signature algorithm will only differ from the key algorithm
1197664f4763Szrj 	 * for RSA keys/certs and when the server advertises support for
1198664f4763Szrj 	 * newer (SHA2) algorithms.
1199664f4763Szrj 	 */
1200664f4763Szrj 	if (ssh == NULL || ssh->kex->server_sig_algs == NULL ||
1201664f4763Szrj 	    (key->type != KEY_RSA && key->type != KEY_RSA_CERT) ||
120250a69bb5SSascha Wildner 	    (key->type == KEY_RSA_CERT && (ssh->compat & SSH_BUG_SIGTYPE))) {
1203664f4763Szrj 		/* Filter base key signature alg against our configuration */
1204664f4763Szrj 		return match_list(sshkey_ssh_name(key),
120550a69bb5SSascha Wildner 		    options.pubkey_accepted_algos, NULL);
1206e9778795SPeter Avalos 	}
1207664f4763Szrj 
1208664f4763Szrj 	/*
120950a69bb5SSascha Wildner 	 * Workaround OpenSSH 7.4 bug: this version supports RSA/SHA-2 but
121050a69bb5SSascha Wildner 	 * fails to advertise it via SSH2_MSG_EXT_INFO.
121150a69bb5SSascha Wildner 	 */
121250a69bb5SSascha Wildner 	server_sig_algs = ssh->kex->server_sig_algs;
121350a69bb5SSascha Wildner 	if (key->type == KEY_RSA && (ssh->compat & SSH_BUG_SIGTYPE74))
121450a69bb5SSascha Wildner 		server_sig_algs = "rsa-sha2-256,rsa-sha2-512";
121550a69bb5SSascha Wildner 
121650a69bb5SSascha Wildner 	/*
1217664f4763Szrj 	 * For RSA keys/certs, since these might have a different sig type:
121850a69bb5SSascha Wildner 	 * find the first entry in PubkeyAcceptedAlgorithms of the right type
1219664f4763Szrj 	 * that also appears in the supported signature algorithms list from
1220664f4763Szrj 	 * the server.
1221664f4763Szrj 	 */
122250a69bb5SSascha Wildner 	oallowed = allowed = xstrdup(options.pubkey_accepted_algos);
1223664f4763Szrj 	while ((cp = strsep(&allowed, ",")) != NULL) {
1224664f4763Szrj 		if (sshkey_type_from_name(cp) != key->type)
1225664f4763Szrj 			continue;
122650a69bb5SSascha Wildner 		tmp = match_list(sshkey_sigalg_by_name(cp),
122750a69bb5SSascha Wildner 		    server_sig_algs, NULL);
1228664f4763Szrj 		if (tmp != NULL)
1229664f4763Szrj 			alg = xstrdup(cp);
1230664f4763Szrj 		free(tmp);
1231664f4763Szrj 		if (alg != NULL)
1232664f4763Szrj 			break;
1233e9778795SPeter Avalos 	}
1234664f4763Szrj 	free(oallowed);
1235664f4763Szrj 	return alg;
123618de8d7fSPeter Avalos }
123718de8d7fSPeter Avalos 
123818de8d7fSPeter Avalos static int
1239e9778795SPeter Avalos identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
1240664f4763Szrj     const u_char *data, size_t datalen, u_int compat, const char *alg)
124118de8d7fSPeter Avalos {
12420cbfa66cSDaniel Fojt 	struct sshkey *sign_key = NULL, *prv = NULL;
1243*ee116499SAntonio Huete Jimenez 	int is_agent = 0, retried = 0, r = SSH_ERR_INTERNAL_ERROR;
12440cbfa66cSDaniel Fojt 	struct notifier_ctx *notifier = NULL;
124550a69bb5SSascha Wildner 	char *fp = NULL, *pin = NULL, *prompt = NULL;
12460cbfa66cSDaniel Fojt 
12470cbfa66cSDaniel Fojt 	*sigp = NULL;
12480cbfa66cSDaniel Fojt 	*lenp = 0;
124918de8d7fSPeter Avalos 
1250664f4763Szrj 	/* The agent supports this key. */
1251664f4763Szrj 	if (id->key != NULL && id->agent_fd != -1) {
1252e9778795SPeter Avalos 		return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp,
1253664f4763Szrj 		    data, datalen, alg, compat);
1254664f4763Szrj 	}
1255e9778795SPeter Avalos 
125618de8d7fSPeter Avalos 	/*
1257664f4763Szrj 	 * We have already loaded the private key or the private key is
1258664f4763Szrj 	 * stored in external hardware.
125918de8d7fSPeter Avalos 	 */
1260ce74bacaSMatthew Dillon 	if (id->key != NULL &&
1261664f4763Szrj 	    (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) {
12620cbfa66cSDaniel Fojt 		sign_key = id->key;
1263*ee116499SAntonio Huete Jimenez 		is_agent = 1;
12640cbfa66cSDaniel Fojt 	} else {
1265664f4763Szrj 		/* Load the private key from the file. */
1266e9778795SPeter Avalos 		if ((prv = load_identity_file(id)) == NULL)
1267e9778795SPeter Avalos 			return SSH_ERR_KEY_NOT_FOUND;
1268ce74bacaSMatthew Dillon 		if (id->key != NULL && !sshkey_equal_public(prv, id->key)) {
126950a69bb5SSascha Wildner 			error_f("private key %s contents do not match public",
127050a69bb5SSascha Wildner 			    id->filename);
12710cbfa66cSDaniel Fojt 			r = SSH_ERR_KEY_NOT_FOUND;
12720cbfa66cSDaniel Fojt 			goto out;
1273ce74bacaSMatthew Dillon 		}
12740cbfa66cSDaniel Fojt 		sign_key = prv;
127550a69bb5SSascha Wildner 	}
1276*ee116499SAntonio Huete Jimenez  retry_pin:
1277*ee116499SAntonio Huete Jimenez 	/* Prompt for touch for non-agent FIDO keys that request UP */
1278*ee116499SAntonio Huete Jimenez 	if (!is_agent && sshkey_is_sk(sign_key) &&
1279*ee116499SAntonio Huete Jimenez 	    (sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
128050a69bb5SSascha Wildner 		/* XXX should batch mode just skip these? */
12810cbfa66cSDaniel Fojt 		if ((fp = sshkey_fingerprint(sign_key,
1282*ee116499SAntonio Huete Jimenez 		    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
128350a69bb5SSascha Wildner 			fatal_f("fingerprint failed");
12840cbfa66cSDaniel Fojt 		notifier = notify_start(options.batch_mode,
12850cbfa66cSDaniel Fojt 		    "Confirm user presence for key %s %s",
12860cbfa66cSDaniel Fojt 		    sshkey_type(sign_key), fp);
12870cbfa66cSDaniel Fojt 		free(fp);
12880cbfa66cSDaniel Fojt 	}
12890cbfa66cSDaniel Fojt 	if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen,
129050a69bb5SSascha Wildner 	    alg, options.sk_provider, pin, compat)) != 0) {
129150a69bb5SSascha Wildner 		debug_fr(r, "sshkey_sign");
1292*ee116499SAntonio Huete Jimenez 		if (!retried && pin == NULL && !is_agent &&
1293*ee116499SAntonio Huete Jimenez 		    sshkey_is_sk(sign_key) &&
129450a69bb5SSascha Wildner 		    r == SSH_ERR_KEY_WRONG_PASSPHRASE) {
129550a69bb5SSascha Wildner 			notify_complete(notifier, NULL);
129650a69bb5SSascha Wildner 			notifier = NULL;
1297*ee116499SAntonio Huete Jimenez 			xasprintf(&prompt, "Enter PIN for %s key %s: ",
1298*ee116499SAntonio Huete Jimenez 			    sshkey_type(sign_key), id->filename);
1299*ee116499SAntonio Huete Jimenez 			pin = read_passphrase(prompt, 0);
130050a69bb5SSascha Wildner 			retried = 1;
130150a69bb5SSascha Wildner 			goto retry_pin;
130250a69bb5SSascha Wildner 		}
13030cbfa66cSDaniel Fojt 		goto out;
13040cbfa66cSDaniel Fojt 	}
130550a69bb5SSascha Wildner 
13060cbfa66cSDaniel Fojt 	/*
13070cbfa66cSDaniel Fojt 	 * PKCS#11 tokens may not support all signature algorithms,
13080cbfa66cSDaniel Fojt 	 * so check what we get back.
13090cbfa66cSDaniel Fojt 	 */
13100cbfa66cSDaniel Fojt 	if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) {
131150a69bb5SSascha Wildner 		debug_fr(r, "sshkey_check_sigtype");
13120cbfa66cSDaniel Fojt 		goto out;
13130cbfa66cSDaniel Fojt 	}
13140cbfa66cSDaniel Fojt 	/* success */
13150cbfa66cSDaniel Fojt 	r = 0;
13160cbfa66cSDaniel Fojt  out:
131750a69bb5SSascha Wildner 	free(prompt);
131850a69bb5SSascha Wildner 	if (pin != NULL)
131950a69bb5SSascha Wildner 		freezero(pin, strlen(pin));
132050a69bb5SSascha Wildner 	notify_complete(notifier, r == 0 ? "User presence confirmed" : NULL);
1321e9778795SPeter Avalos 	sshkey_free(prv);
1322664f4763Szrj 	return r;
132318de8d7fSPeter Avalos }
132418de8d7fSPeter Avalos 
132518de8d7fSPeter Avalos static int
1326ce74bacaSMatthew Dillon id_filename_matches(Identity *id, Identity *private_id)
1327ce74bacaSMatthew Dillon {
1328*ee116499SAntonio Huete Jimenez 	static const char * const suffixes[] = { ".pub", "-cert.pub", NULL };
1329ce74bacaSMatthew Dillon 	size_t len = strlen(id->filename), plen = strlen(private_id->filename);
1330ce74bacaSMatthew Dillon 	size_t i, slen;
1331ce74bacaSMatthew Dillon 
1332ce74bacaSMatthew Dillon 	if (strcmp(id->filename, private_id->filename) == 0)
1333ce74bacaSMatthew Dillon 		return 1;
1334ce74bacaSMatthew Dillon 	for (i = 0; suffixes[i]; i++) {
1335ce74bacaSMatthew Dillon 		slen = strlen(suffixes[i]);
1336ce74bacaSMatthew Dillon 		if (len > slen && plen == len - slen &&
1337ce74bacaSMatthew Dillon 		    strcmp(id->filename + (len - slen), suffixes[i]) == 0 &&
1338ce74bacaSMatthew Dillon 		    memcmp(id->filename, private_id->filename, plen) == 0)
1339ce74bacaSMatthew Dillon 			return 1;
1340ce74bacaSMatthew Dillon 	}
1341ce74bacaSMatthew Dillon 	return 0;
1342ce74bacaSMatthew Dillon }
1343ce74bacaSMatthew Dillon 
1344ce74bacaSMatthew Dillon static int
1345664f4763Szrj sign_and_send_pubkey(struct ssh *ssh, Identity *id)
134618de8d7fSPeter Avalos {
1347664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
1348664f4763Szrj 	struct sshbuf *b = NULL;
1349664f4763Szrj 	Identity *private_id, *sign_id = NULL;
1350664f4763Szrj 	u_char *signature = NULL;
1351664f4763Szrj 	size_t slen = 0, skip = 0;
1352664f4763Szrj 	int r, fallback_sigtype, sent = 0;
1353664f4763Szrj 	char *alg = NULL, *fp = NULL;
1354*ee116499SAntonio Huete Jimenez 	const char *loc = "", *method = "publickey";
1355*ee116499SAntonio Huete Jimenez 	int hostbound = 0;
1356*ee116499SAntonio Huete Jimenez 
1357*ee116499SAntonio Huete Jimenez 	/* prefer host-bound pubkey signatures if supported by server */
1358*ee116499SAntonio Huete Jimenez 	if ((ssh->kex->flags & KEX_HAS_PUBKEY_HOSTBOUND) != 0 &&
1359*ee116499SAntonio Huete Jimenez 	    (options.pubkey_authentication & SSH_PUBKEY_AUTH_HBOUND) != 0) {
1360*ee116499SAntonio Huete Jimenez 		hostbound = 1;
1361*ee116499SAntonio Huete Jimenez 		method = "publickey-hostbound-v00@openssh.com";
1362*ee116499SAntonio Huete Jimenez 	}
136318de8d7fSPeter Avalos 
1364e9778795SPeter Avalos 	if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
1365e9778795SPeter Avalos 	    SSH_FP_DEFAULT)) == NULL)
1366e9778795SPeter Avalos 		return 0;
136718de8d7fSPeter Avalos 
1368*ee116499SAntonio Huete Jimenez 	debug3_f("using %s with %s %s", method, sshkey_type(id->key), fp);
136918de8d7fSPeter Avalos 
1370e9778795SPeter Avalos 	/*
1371e9778795SPeter Avalos 	 * If the key is an certificate, try to find a matching private key
1372e9778795SPeter Avalos 	 * and use it to complete the signature.
1373e9778795SPeter Avalos 	 * If no such private key exists, fall back to trying the certificate
1374e9778795SPeter Avalos 	 * key itself in case it has a private half already loaded.
1375664f4763Szrj 	 * This will try to set sign_id to the private key that will perform
1376664f4763Szrj 	 * the signature.
1377e9778795SPeter Avalos 	 */
1378664f4763Szrj 	if (sshkey_is_cert(id->key)) {
1379e9778795SPeter Avalos 		TAILQ_FOREACH(private_id, &authctxt->keys, next) {
1380e9778795SPeter Avalos 			if (sshkey_equal_public(id->key, private_id->key) &&
1381e9778795SPeter Avalos 			    id->key->type != private_id->key->type) {
1382664f4763Szrj 				sign_id = private_id;
1383e9778795SPeter Avalos 				break;
1384e9778795SPeter Avalos 			}
1385e9778795SPeter Avalos 		}
1386ce74bacaSMatthew Dillon 		/*
1387ce74bacaSMatthew Dillon 		 * Exact key matches are preferred, but also allow
1388ce74bacaSMatthew Dillon 		 * filename matches for non-PKCS#11/agent keys that
1389ce74bacaSMatthew Dillon 		 * didn't load public keys. This supports the case
1390ce74bacaSMatthew Dillon 		 * of keeping just a private key file and public
1391ce74bacaSMatthew Dillon 		 * certificate on disk.
1392ce74bacaSMatthew Dillon 		 */
1393664f4763Szrj 		if (sign_id == NULL &&
1394664f4763Szrj 		    !id->isprivate && id->agent_fd == -1 &&
1395ce74bacaSMatthew Dillon 		    (id->key->flags & SSHKEY_FLAG_EXT) == 0) {
1396ce74bacaSMatthew Dillon 			TAILQ_FOREACH(private_id, &authctxt->keys, next) {
1397ce74bacaSMatthew Dillon 				if (private_id->key == NULL &&
1398ce74bacaSMatthew Dillon 				    id_filename_matches(id, private_id)) {
1399664f4763Szrj 					sign_id = private_id;
1400ce74bacaSMatthew Dillon 					break;
1401ce74bacaSMatthew Dillon 				}
1402ce74bacaSMatthew Dillon 			}
1403ce74bacaSMatthew Dillon 		}
1404664f4763Szrj 		if (sign_id != NULL) {
140550a69bb5SSascha Wildner 			debug2_f("using private key \"%s\"%s for "
140650a69bb5SSascha Wildner 			    "certificate", sign_id->filename,
140750a69bb5SSascha Wildner 			    sign_id->agent_fd != -1 ? " from agent" : "");
1408e9778795SPeter Avalos 		} else {
140950a69bb5SSascha Wildner 			debug_f("no separate private key for certificate "
141050a69bb5SSascha Wildner 			    "\"%s\"", id->filename);
1411e9778795SPeter Avalos 		}
1412e9778795SPeter Avalos 	}
1413e9778795SPeter Avalos 
1414664f4763Szrj 	/*
1415664f4763Szrj 	 * If the above didn't select another identity to do the signing
1416664f4763Szrj 	 * then default to the one we started with.
1417664f4763Szrj 	 */
1418664f4763Szrj 	if (sign_id == NULL)
1419664f4763Szrj 		sign_id = id;
1420664f4763Szrj 
1421664f4763Szrj 	/* assemble and sign data */
1422664f4763Szrj 	for (fallback_sigtype = 0; fallback_sigtype <= 1; fallback_sigtype++) {
1423664f4763Szrj 		free(alg);
1424664f4763Szrj 		slen = 0;
1425664f4763Szrj 		signature = NULL;
1426664f4763Szrj 		if ((alg = key_sig_algorithm(fallback_sigtype ? NULL : ssh,
1427664f4763Szrj 		    id->key)) == NULL) {
142850a69bb5SSascha Wildner 			error_f("no mutual signature supported");
1429664f4763Szrj 			goto out;
1430664f4763Szrj 		}
143150a69bb5SSascha Wildner 		debug3_f("signing using %s %s", alg, fp);
1432664f4763Szrj 
1433664f4763Szrj 		sshbuf_free(b);
1434664f4763Szrj 		if ((b = sshbuf_new()) == NULL)
143550a69bb5SSascha Wildner 			fatal_f("sshbuf_new failed");
143650a69bb5SSascha Wildner 		if (ssh->compat & SSH_OLD_SESSIONID) {
143750a69bb5SSascha Wildner 			if ((r = sshbuf_putb(b, ssh->kex->session_id)) != 0)
143850a69bb5SSascha Wildner 				fatal_fr(r, "sshbuf_putb");
1439664f4763Szrj 		} else {
144050a69bb5SSascha Wildner 			if ((r = sshbuf_put_stringb(b,
144150a69bb5SSascha Wildner 			    ssh->kex->session_id)) != 0)
144250a69bb5SSascha Wildner 				fatal_fr(r, "sshbuf_put_stringb");
1443664f4763Szrj 		}
1444664f4763Szrj 		skip = sshbuf_len(b);
1445664f4763Szrj 		if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1446664f4763Szrj 		    (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 ||
1447664f4763Szrj 		    (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
1448*ee116499SAntonio Huete Jimenez 		    (r = sshbuf_put_cstring(b, method)) != 0 ||
1449664f4763Szrj 		    (r = sshbuf_put_u8(b, 1)) != 0 ||
1450664f4763Szrj 		    (r = sshbuf_put_cstring(b, alg)) != 0 ||
1451664f4763Szrj 		    (r = sshkey_puts(id->key, b)) != 0) {
145250a69bb5SSascha Wildner 			fatal_fr(r, "assemble signed data");
1453664f4763Szrj 		}
1454*ee116499SAntonio Huete Jimenez 		if (hostbound) {
1455*ee116499SAntonio Huete Jimenez 			if (ssh->kex->initial_hostkey == NULL) {
1456*ee116499SAntonio Huete Jimenez 				fatal_f("internal error: initial hostkey "
1457*ee116499SAntonio Huete Jimenez 				    "not recorded");
1458*ee116499SAntonio Huete Jimenez 			}
1459*ee116499SAntonio Huete Jimenez 			if ((r = sshkey_puts(ssh->kex->initial_hostkey, b)) != 0)
1460*ee116499SAntonio Huete Jimenez 				fatal_fr(r, "assemble %s hostkey", method);
1461*ee116499SAntonio Huete Jimenez 		}
146218de8d7fSPeter Avalos 		/* generate signature */
1463664f4763Szrj 		r = identity_sign(sign_id, &signature, &slen,
146450a69bb5SSascha Wildner 		    sshbuf_ptr(b), sshbuf_len(b), ssh->compat, alg);
1465664f4763Szrj 		if (r == 0)
1466664f4763Szrj 			break;
1467664f4763Szrj 		else if (r == SSH_ERR_KEY_NOT_FOUND)
1468664f4763Szrj 			goto out; /* soft failure */
1469664f4763Szrj 		else if (r == SSH_ERR_SIGN_ALG_UNSUPPORTED &&
1470664f4763Szrj 		    !fallback_sigtype) {
1471664f4763Szrj 			if (sign_id->agent_fd != -1)
1472664f4763Szrj 				loc = "agent ";
1473664f4763Szrj 			else if ((sign_id->key->flags & SSHKEY_FLAG_EXT) != 0)
1474664f4763Szrj 				loc = "token ";
1475664f4763Szrj 			logit("%skey %s %s returned incorrect signature type",
1476664f4763Szrj 			    loc, sshkey_type(id->key), fp);
1477664f4763Szrj 			continue;
147818de8d7fSPeter Avalos 		}
147950a69bb5SSascha Wildner 		error_fr(r, "signing failed for %s \"%s\"%s",
14800cbfa66cSDaniel Fojt 		    sshkey_type(sign_id->key), sign_id->filename,
148150a69bb5SSascha Wildner 		    id->agent_fd != -1 ? " from agent" : "");
1482664f4763Szrj 		goto out;
148318de8d7fSPeter Avalos 	}
1484664f4763Szrj 	if (slen == 0 || signature == NULL) /* shouldn't happen */
148550a69bb5SSascha Wildner 		fatal_f("no signature");
148618de8d7fSPeter Avalos 
148718de8d7fSPeter Avalos 	/* append signature */
1488664f4763Szrj 	if ((r = sshbuf_put_string(b, signature, slen)) != 0)
148950a69bb5SSascha Wildner 		fatal_fr(r, "append signature");
149018de8d7fSPeter Avalos 
1491664f4763Szrj #ifdef DEBUG_PK
1492664f4763Szrj 	sshbuf_dump(b, stderr);
1493664f4763Szrj #endif
149418de8d7fSPeter Avalos 	/* skip session id and packet type */
1495664f4763Szrj 	if ((r = sshbuf_consume(b, skip + 1)) != 0)
149650a69bb5SSascha Wildner 		fatal_fr(r, "consume");
149718de8d7fSPeter Avalos 
149818de8d7fSPeter Avalos 	/* put remaining data from buffer into packet */
1499664f4763Szrj 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1500664f4763Szrj 	    (r = sshpkt_putb(ssh, b)) != 0 ||
1501664f4763Szrj 	    (r = sshpkt_send(ssh)) != 0)
150250a69bb5SSascha Wildner 		fatal_fr(r, "enqueue request");
150318de8d7fSPeter Avalos 
1504664f4763Szrj 	/* success */
1505664f4763Szrj 	sent = 1;
1506664f4763Szrj 
1507664f4763Szrj  out:
1508664f4763Szrj 	free(fp);
1509664f4763Szrj 	free(alg);
1510664f4763Szrj 	sshbuf_free(b);
1511664f4763Szrj 	freezero(signature, slen);
1512664f4763Szrj 	return sent;
151318de8d7fSPeter Avalos }
151418de8d7fSPeter Avalos 
151518de8d7fSPeter Avalos static int
1516664f4763Szrj send_pubkey_test(struct ssh *ssh, Identity *id)
151718de8d7fSPeter Avalos {
1518664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
1519664f4763Szrj 	u_char *blob = NULL;
1520664f4763Szrj 	char *alg = NULL;
1521664f4763Szrj 	size_t bloblen;
1522664f4763Szrj 	u_int have_sig = 0;
1523664f4763Szrj 	int sent = 0, r;
152418de8d7fSPeter Avalos 
1525664f4763Szrj 	if ((alg = key_sig_algorithm(ssh, id->key)) == NULL) {
152650a69bb5SSascha Wildner 		debug_f("no mutual signature algorithm");
1527664f4763Szrj 		goto out;
1528664f4763Szrj 	}
152918de8d7fSPeter Avalos 
1530664f4763Szrj 	if ((r = sshkey_to_blob(id->key, &blob, &bloblen)) != 0) {
153118de8d7fSPeter Avalos 		/* we cannot handle this key */
153250a69bb5SSascha Wildner 		debug3_f("cannot handle key");
1533664f4763Szrj 		goto out;
153418de8d7fSPeter Avalos 	}
153518de8d7fSPeter Avalos 	/* register callback for USERAUTH_PK_OK message */
1536664f4763Szrj 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok);
153718de8d7fSPeter Avalos 
1538664f4763Szrj 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1539664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1540664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1541664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1542664f4763Szrj 	    (r = sshpkt_put_u8(ssh, have_sig)) != 0 ||
1543664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, alg)) != 0 ||
1544664f4763Szrj 	    (r = sshpkt_put_string(ssh, blob, bloblen)) != 0 ||
1545664f4763Szrj 	    (r = sshpkt_send(ssh)) != 0)
154650a69bb5SSascha Wildner 		fatal_fr(r, "send packet");
1547664f4763Szrj 	sent = 1;
1548664f4763Szrj 
1549664f4763Szrj  out:
1550664f4763Szrj 	free(alg);
155136e94dc5SPeter Avalos 	free(blob);
1552664f4763Szrj 	return sent;
155318de8d7fSPeter Avalos }
155418de8d7fSPeter Avalos 
1555ce74bacaSMatthew Dillon static struct sshkey *
1556e9778795SPeter Avalos load_identity_file(Identity *id)
155718de8d7fSPeter Avalos {
1558ce74bacaSMatthew Dillon 	struct sshkey *private = NULL;
1559e9778795SPeter Avalos 	char prompt[300], *passphrase, *comment;
15600cbfa66cSDaniel Fojt 	int r, quit = 0, i;
156118de8d7fSPeter Avalos 	struct stat st;
156218de8d7fSPeter Avalos 
15630cbfa66cSDaniel Fojt 	if (stat(id->filename, &st) == -1) {
156450a69bb5SSascha Wildner 		do_log2(id->userprovided ?
156550a69bb5SSascha Wildner 		    SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_DEBUG3,
156650a69bb5SSascha Wildner 		    "no such identity: %s: %s", id->filename, strerror(errno));
156718de8d7fSPeter Avalos 		return NULL;
156818de8d7fSPeter Avalos 	}
156918de8d7fSPeter Avalos 	snprintf(prompt, sizeof prompt,
1570e9778795SPeter Avalos 	    "Enter passphrase for key '%.100s': ", id->filename);
1571e9778795SPeter Avalos 	for (i = 0; i <= options.number_of_password_prompts; i++) {
1572e9778795SPeter Avalos 		if (i == 0)
1573e9778795SPeter Avalos 			passphrase = "";
1574e9778795SPeter Avalos 		else {
157518de8d7fSPeter Avalos 			passphrase = read_passphrase(prompt, 0);
1576e9778795SPeter Avalos 			if (*passphrase == '\0') {
157718de8d7fSPeter Avalos 				debug2("no passphrase given, try next key");
1578e9778795SPeter Avalos 				free(passphrase);
1579e9778795SPeter Avalos 				break;
158018de8d7fSPeter Avalos 			}
1581e9778795SPeter Avalos 		}
1582e9778795SPeter Avalos 		switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename,
15830cbfa66cSDaniel Fojt 		    passphrase, &private, &comment))) {
1584e9778795SPeter Avalos 		case 0:
1585e9778795SPeter Avalos 			break;
1586e9778795SPeter Avalos 		case SSH_ERR_KEY_WRONG_PASSPHRASE:
1587e9778795SPeter Avalos 			if (options.batch_mode) {
1588e9778795SPeter Avalos 				quit = 1;
1589e9778795SPeter Avalos 				break;
1590e9778795SPeter Avalos 			}
1591e9778795SPeter Avalos 			if (i != 0)
1592e9778795SPeter Avalos 				debug2("bad passphrase given, try again...");
1593e9778795SPeter Avalos 			break;
1594e9778795SPeter Avalos 		case SSH_ERR_SYSTEM_ERROR:
1595e9778795SPeter Avalos 			if (errno == ENOENT) {
159650a69bb5SSascha Wildner 				debug2_r(r, "Load key \"%s\"", id->filename);
1597e9778795SPeter Avalos 				quit = 1;
1598e9778795SPeter Avalos 				break;
1599e9778795SPeter Avalos 			}
1600e9778795SPeter Avalos 			/* FALLTHROUGH */
1601e9778795SPeter Avalos 		default:
160250a69bb5SSascha Wildner 			error_r(r, "Load key \"%s\"", id->filename);
1603e9778795SPeter Avalos 			quit = 1;
1604e9778795SPeter Avalos 			break;
1605e9778795SPeter Avalos 		}
16060cbfa66cSDaniel Fojt 		if (private != NULL && sshkey_is_sk(private) &&
16070cbfa66cSDaniel Fojt 		    options.sk_provider == NULL) {
16080cbfa66cSDaniel Fojt 			debug("key \"%s\" is an authenticator-hosted key, "
16090cbfa66cSDaniel Fojt 			    "but no provider specified", id->filename);
16100cbfa66cSDaniel Fojt 			sshkey_free(private);
16110cbfa66cSDaniel Fojt 			private = NULL;
16120cbfa66cSDaniel Fojt 			quit = 1;
16130cbfa66cSDaniel Fojt 		}
1614*ee116499SAntonio Huete Jimenez 		if (!quit && (r = sshkey_check_rsa_length(private,
1615*ee116499SAntonio Huete Jimenez 		    options.required_rsa_size)) != 0) {
1616*ee116499SAntonio Huete Jimenez 			debug_fr(r, "Skipping key %s", id->filename);
1617*ee116499SAntonio Huete Jimenez 			sshkey_free(private);
1618*ee116499SAntonio Huete Jimenez 			private = NULL;
1619*ee116499SAntonio Huete Jimenez 			quit = 1;
1620*ee116499SAntonio Huete Jimenez 		}
1621e9778795SPeter Avalos 		if (!quit && private != NULL && id->agent_fd == -1 &&
1622e9778795SPeter Avalos 		    !(id->key && id->isprivate))
1623e9778795SPeter Avalos 			maybe_add_key_to_agent(id->filename, private, comment,
1624e9778795SPeter Avalos 			    passphrase);
1625664f4763Szrj 		if (i > 0)
1626664f4763Szrj 			freezero(passphrase, strlen(passphrase));
1627e9778795SPeter Avalos 		free(comment);
162818de8d7fSPeter Avalos 		if (private != NULL || quit)
162918de8d7fSPeter Avalos 			break;
163018de8d7fSPeter Avalos 	}
163118de8d7fSPeter Avalos 	return private;
163218de8d7fSPeter Avalos }
163318de8d7fSPeter Avalos 
1634664f4763Szrj static int
1635664f4763Szrj key_type_allowed_by_config(struct sshkey *key)
1636664f4763Szrj {
1637664f4763Szrj 	if (match_pattern_list(sshkey_ssh_name(key),
163850a69bb5SSascha Wildner 	    options.pubkey_accepted_algos, 0) == 1)
1639664f4763Szrj 		return 1;
1640664f4763Szrj 
1641664f4763Szrj 	/* RSA keys/certs might be allowed by alternate signature types */
1642664f4763Szrj 	switch (key->type) {
1643664f4763Szrj 	case KEY_RSA:
1644664f4763Szrj 		if (match_pattern_list("rsa-sha2-512",
164550a69bb5SSascha Wildner 		    options.pubkey_accepted_algos, 0) == 1)
1646664f4763Szrj 			return 1;
1647664f4763Szrj 		if (match_pattern_list("rsa-sha2-256",
164850a69bb5SSascha Wildner 		    options.pubkey_accepted_algos, 0) == 1)
1649664f4763Szrj 			return 1;
1650664f4763Szrj 		break;
1651664f4763Szrj 	case KEY_RSA_CERT:
1652664f4763Szrj 		if (match_pattern_list("rsa-sha2-512-cert-v01@openssh.com",
165350a69bb5SSascha Wildner 		    options.pubkey_accepted_algos, 0) == 1)
1654664f4763Szrj 			return 1;
1655664f4763Szrj 		if (match_pattern_list("rsa-sha2-256-cert-v01@openssh.com",
165650a69bb5SSascha Wildner 		    options.pubkey_accepted_algos, 0) == 1)
1657664f4763Szrj 			return 1;
1658664f4763Szrj 		break;
1659664f4763Szrj 	}
1660664f4763Szrj 	return 0;
1661664f4763Szrj }
1662664f4763Szrj 
1663*ee116499SAntonio Huete Jimenez /* obtain a list of keys from the agent */
1664*ee116499SAntonio Huete Jimenez static int
1665*ee116499SAntonio Huete Jimenez get_agent_identities(struct ssh *ssh, int *agent_fdp,
1666*ee116499SAntonio Huete Jimenez     struct ssh_identitylist **idlistp)
1667*ee116499SAntonio Huete Jimenez {
1668*ee116499SAntonio Huete Jimenez 	int r, agent_fd;
1669*ee116499SAntonio Huete Jimenez 	struct ssh_identitylist *idlist;
1670*ee116499SAntonio Huete Jimenez 
1671*ee116499SAntonio Huete Jimenez 	if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
1672*ee116499SAntonio Huete Jimenez 		if (r != SSH_ERR_AGENT_NOT_PRESENT)
1673*ee116499SAntonio Huete Jimenez 			debug_fr(r, "ssh_get_authentication_socket");
1674*ee116499SAntonio Huete Jimenez 		return r;
1675*ee116499SAntonio Huete Jimenez 	}
1676*ee116499SAntonio Huete Jimenez 	if ((r = ssh_agent_bind_hostkey(agent_fd, ssh->kex->initial_hostkey,
1677*ee116499SAntonio Huete Jimenez 	    ssh->kex->session_id, ssh->kex->initial_sig, 0)) == 0)
1678*ee116499SAntonio Huete Jimenez 		debug_f("bound agent to hostkey");
1679*ee116499SAntonio Huete Jimenez 	else
1680*ee116499SAntonio Huete Jimenez 		debug2_fr(r, "ssh_agent_bind_hostkey");
1681*ee116499SAntonio Huete Jimenez 
1682*ee116499SAntonio Huete Jimenez 	if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
1683*ee116499SAntonio Huete Jimenez 		debug_fr(r, "ssh_fetch_identitylist");
1684*ee116499SAntonio Huete Jimenez 		close(agent_fd);
1685*ee116499SAntonio Huete Jimenez 		return r;
1686*ee116499SAntonio Huete Jimenez 	}
1687*ee116499SAntonio Huete Jimenez 	/* success */
1688*ee116499SAntonio Huete Jimenez 	*agent_fdp = agent_fd;
1689*ee116499SAntonio Huete Jimenez 	*idlistp = idlist;
1690*ee116499SAntonio Huete Jimenez 	debug_f("agent returned %zu keys", idlist->nkeys);
1691*ee116499SAntonio Huete Jimenez 	return 0;
1692*ee116499SAntonio Huete Jimenez }
1693664f4763Szrj 
169418de8d7fSPeter Avalos /*
169518de8d7fSPeter Avalos  * try keys in the following order:
1696e9778795SPeter Avalos  *	1. certificates listed in the config file
1697e9778795SPeter Avalos  *	2. other input certificates
1698e9778795SPeter Avalos  *	3. agent keys that are found in the config file
1699e9778795SPeter Avalos  *	4. other agent keys
1700e9778795SPeter Avalos  *	5. keys that are only listed in the config file
170118de8d7fSPeter Avalos  */
170218de8d7fSPeter Avalos static void
1703*ee116499SAntonio Huete Jimenez pubkey_prepare(struct ssh *ssh, Authctxt *authctxt)
170418de8d7fSPeter Avalos {
1705e9778795SPeter Avalos 	struct identity *id, *id2, *tmp;
1706e9778795SPeter Avalos 	struct idlist agent, files, *preferred;
1707e9778795SPeter Avalos 	struct sshkey *key;
1708e9778795SPeter Avalos 	int agent_fd = -1, i, r, found;
1709e9778795SPeter Avalos 	size_t j;
1710e9778795SPeter Avalos 	struct ssh_identitylist *idlist;
1711664f4763Szrj 	char *ident;
171218de8d7fSPeter Avalos 
171318de8d7fSPeter Avalos 	TAILQ_INIT(&agent);	/* keys from the agent */
171418de8d7fSPeter Avalos 	TAILQ_INIT(&files);	/* keys from the config file */
171518de8d7fSPeter Avalos 	preferred = &authctxt->keys;
171618de8d7fSPeter Avalos 	TAILQ_INIT(preferred);	/* preferred order of keys */
171718de8d7fSPeter Avalos 
171836e94dc5SPeter Avalos 	/* list of keys stored in the filesystem and PKCS#11 */
171918de8d7fSPeter Avalos 	for (i = 0; i < options.num_identity_files; i++) {
172018de8d7fSPeter Avalos 		key = options.identity_keys[i];
17210cbfa66cSDaniel Fojt 		if (key && key->cert &&
17220cbfa66cSDaniel Fojt 		    key->cert->type != SSH2_CERT_TYPE_USER) {
172350a69bb5SSascha Wildner 			debug_f("ignoring certificate %s: not a user "
172450a69bb5SSascha Wildner 			    "certificate", options.identity_files[i]);
1725856ea928SPeter Avalos 			continue;
17260cbfa66cSDaniel Fojt 		}
17270cbfa66cSDaniel Fojt 		if (key && sshkey_is_sk(key) && options.sk_provider == NULL) {
172850a69bb5SSascha Wildner 			debug_f("ignoring authenticator-hosted key %s as no "
17290cbfa66cSDaniel Fojt 			    "SecurityKeyProvider has been specified",
173050a69bb5SSascha Wildner 			    options.identity_files[i]);
17310cbfa66cSDaniel Fojt 			continue;
17320cbfa66cSDaniel Fojt 		}
173318de8d7fSPeter Avalos 		options.identity_keys[i] = NULL;
173418de8d7fSPeter Avalos 		id = xcalloc(1, sizeof(*id));
1735e9778795SPeter Avalos 		id->agent_fd = -1;
173618de8d7fSPeter Avalos 		id->key = key;
173718de8d7fSPeter Avalos 		id->filename = xstrdup(options.identity_files[i]);
173836e94dc5SPeter Avalos 		id->userprovided = options.identity_file_userprovided[i];
173918de8d7fSPeter Avalos 		TAILQ_INSERT_TAIL(&files, id, next);
174018de8d7fSPeter Avalos 	}
1741e9778795SPeter Avalos 	/* list of certificates specified by user */
1742e9778795SPeter Avalos 	for (i = 0; i < options.num_certificate_files; i++) {
1743e9778795SPeter Avalos 		key = options.certificates[i];
1744664f4763Szrj 		if (!sshkey_is_cert(key) || key->cert == NULL ||
17450cbfa66cSDaniel Fojt 		    key->cert->type != SSH2_CERT_TYPE_USER) {
174650a69bb5SSascha Wildner 			debug_f("ignoring certificate %s: not a user "
174750a69bb5SSascha Wildner 			    "certificate", options.identity_files[i]);
1748e9778795SPeter Avalos 			continue;
17490cbfa66cSDaniel Fojt 		}
17500cbfa66cSDaniel Fojt 		if (key && sshkey_is_sk(key) && options.sk_provider == NULL) {
175150a69bb5SSascha Wildner 			debug_f("ignoring authenticator-hosted key "
17520cbfa66cSDaniel Fojt 			    "certificate %s as no "
17530cbfa66cSDaniel Fojt 			    "SecurityKeyProvider has been specified",
175450a69bb5SSascha Wildner 			    options.identity_files[i]);
17550cbfa66cSDaniel Fojt 			continue;
17560cbfa66cSDaniel Fojt 		}
1757e9778795SPeter Avalos 		id = xcalloc(1, sizeof(*id));
1758e9778795SPeter Avalos 		id->agent_fd = -1;
1759e9778795SPeter Avalos 		id->key = key;
1760e9778795SPeter Avalos 		id->filename = xstrdup(options.certificate_files[i]);
1761e9778795SPeter Avalos 		id->userprovided = options.certificate_file_userprovided[i];
1762e9778795SPeter Avalos 		TAILQ_INSERT_TAIL(preferred, id, next);
1763e9778795SPeter Avalos 	}
1764e9778795SPeter Avalos 	/* list of keys supported by the agent */
1765*ee116499SAntonio Huete Jimenez 	if ((r = get_agent_identities(ssh, &agent_fd, &idlist)) == 0) {
1766e9778795SPeter Avalos 		for (j = 0; j < idlist->nkeys; j++) {
1767*ee116499SAntonio Huete Jimenez 			if ((r = sshkey_check_rsa_length(idlist->keys[j],
1768*ee116499SAntonio Huete Jimenez 			    options.required_rsa_size)) != 0) {
1769*ee116499SAntonio Huete Jimenez 				debug_fr(r, "ignoring %s agent key",
1770*ee116499SAntonio Huete Jimenez 				    sshkey_ssh_name(idlist->keys[j]));
1771*ee116499SAntonio Huete Jimenez 				continue;
1772*ee116499SAntonio Huete Jimenez 			}
1773e9778795SPeter Avalos 			found = 0;
1774e9778795SPeter Avalos 			TAILQ_FOREACH(id, &files, next) {
1775e9778795SPeter Avalos 				/*
1776e9778795SPeter Avalos 				 * agent keys from the config file are
1777e9778795SPeter Avalos 				 * preferred
1778e9778795SPeter Avalos 				 */
1779e9778795SPeter Avalos 				if (sshkey_equal(idlist->keys[j], id->key)) {
1780e9778795SPeter Avalos 					TAILQ_REMOVE(&files, id, next);
1781e9778795SPeter Avalos 					TAILQ_INSERT_TAIL(preferred, id, next);
1782e9778795SPeter Avalos 					id->agent_fd = agent_fd;
1783e9778795SPeter Avalos 					found = 1;
1784e9778795SPeter Avalos 					break;
1785e9778795SPeter Avalos 				}
1786e9778795SPeter Avalos 			}
1787e9778795SPeter Avalos 			if (!found && !options.identities_only) {
1788e9778795SPeter Avalos 				id = xcalloc(1, sizeof(*id));
1789e9778795SPeter Avalos 				/* XXX "steals" key/comment from idlist */
1790e9778795SPeter Avalos 				id->key = idlist->keys[j];
1791e9778795SPeter Avalos 				id->filename = idlist->comments[j];
1792e9778795SPeter Avalos 				idlist->keys[j] = NULL;
1793e9778795SPeter Avalos 				idlist->comments[j] = NULL;
1794e9778795SPeter Avalos 				id->agent_fd = agent_fd;
1795e9778795SPeter Avalos 				TAILQ_INSERT_TAIL(&agent, id, next);
1796e9778795SPeter Avalos 			}
1797e9778795SPeter Avalos 		}
1798e9778795SPeter Avalos 		ssh_free_identitylist(idlist);
1799e9778795SPeter Avalos 		/* append remaining agent keys */
180050a69bb5SSascha Wildner 		TAILQ_CONCAT(preferred, &agent, next);
1801e9778795SPeter Avalos 		authctxt->agent_fd = agent_fd;
1802e9778795SPeter Avalos 	}
180336e94dc5SPeter Avalos 	/* Prefer PKCS11 keys that are explicitly listed */
180436e94dc5SPeter Avalos 	TAILQ_FOREACH_SAFE(id, &files, next, tmp) {
180536e94dc5SPeter Avalos 		if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0)
180636e94dc5SPeter Avalos 			continue;
180736e94dc5SPeter Avalos 		found = 0;
180836e94dc5SPeter Avalos 		TAILQ_FOREACH(id2, &files, next) {
180936e94dc5SPeter Avalos 			if (id2->key == NULL ||
18100cbfa66cSDaniel Fojt 			    (id2->key->flags & SSHKEY_FLAG_EXT) != 0)
181136e94dc5SPeter Avalos 				continue;
1812e9778795SPeter Avalos 			if (sshkey_equal(id->key, id2->key)) {
181336e94dc5SPeter Avalos 				TAILQ_REMOVE(&files, id, next);
181436e94dc5SPeter Avalos 				TAILQ_INSERT_TAIL(preferred, id, next);
181536e94dc5SPeter Avalos 				found = 1;
181636e94dc5SPeter Avalos 				break;
181736e94dc5SPeter Avalos 			}
181836e94dc5SPeter Avalos 		}
181936e94dc5SPeter Avalos 		/* If IdentitiesOnly set and key not found then don't use it */
182036e94dc5SPeter Avalos 		if (!found && options.identities_only) {
182136e94dc5SPeter Avalos 			TAILQ_REMOVE(&files, id, next);
1822664f4763Szrj 			freezero(id, sizeof(*id));
182336e94dc5SPeter Avalos 		}
182436e94dc5SPeter Avalos 	}
182518de8d7fSPeter Avalos 	/* append remaining keys from the config file */
182650a69bb5SSascha Wildner 	TAILQ_CONCAT(preferred, &files, next);
182750a69bb5SSascha Wildner 	/* finally, filter by PubkeyAcceptedAlgorithms */
1828e9778795SPeter Avalos 	TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
1829664f4763Szrj 		if (id->key != NULL && !key_type_allowed_by_config(id->key)) {
1830e9778795SPeter Avalos 			debug("Skipping %s key %s - "
183150a69bb5SSascha Wildner 			    "corresponding algo not in PubkeyAcceptedAlgorithms",
1832e9778795SPeter Avalos 			    sshkey_ssh_name(id->key), id->filename);
1833e9778795SPeter Avalos 			TAILQ_REMOVE(preferred, id, next);
1834e9778795SPeter Avalos 			sshkey_free(id->key);
1835e9778795SPeter Avalos 			free(id->filename);
1836e9778795SPeter Avalos 			memset(id, 0, sizeof(*id));
1837e9778795SPeter Avalos 			continue;
1838e9778795SPeter Avalos 		}
183918de8d7fSPeter Avalos 	}
1840664f4763Szrj 	/* List the keys we plan on using */
1841664f4763Szrj 	TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
1842664f4763Szrj 		ident = format_identity(id);
1843664f4763Szrj 		debug("Will attempt key: %s", ident);
1844664f4763Szrj 		free(ident);
1845664f4763Szrj 	}
184650a69bb5SSascha Wildner 	debug2_f("done");
184718de8d7fSPeter Avalos }
184818de8d7fSPeter Avalos 
184918de8d7fSPeter Avalos static void
1850664f4763Szrj pubkey_cleanup(struct ssh *ssh)
185118de8d7fSPeter Avalos {
1852664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
185318de8d7fSPeter Avalos 	Identity *id;
185418de8d7fSPeter Avalos 
1855664f4763Szrj 	if (authctxt->agent_fd != -1) {
1856e9778795SPeter Avalos 		ssh_close_authentication_socket(authctxt->agent_fd);
1857664f4763Szrj 		authctxt->agent_fd = -1;
1858664f4763Szrj 	}
185918de8d7fSPeter Avalos 	for (id = TAILQ_FIRST(&authctxt->keys); id;
186018de8d7fSPeter Avalos 	    id = TAILQ_FIRST(&authctxt->keys)) {
186118de8d7fSPeter Avalos 		TAILQ_REMOVE(&authctxt->keys, id, next);
1862e9778795SPeter Avalos 		sshkey_free(id->key);
186336e94dc5SPeter Avalos 		free(id->filename);
186436e94dc5SPeter Avalos 		free(id);
186518de8d7fSPeter Avalos 	}
186618de8d7fSPeter Avalos }
186718de8d7fSPeter Avalos 
1868ce74bacaSMatthew Dillon static void
1869ce74bacaSMatthew Dillon pubkey_reset(Authctxt *authctxt)
1870ce74bacaSMatthew Dillon {
1871ce74bacaSMatthew Dillon 	Identity *id;
1872ce74bacaSMatthew Dillon 
1873ce74bacaSMatthew Dillon 	TAILQ_FOREACH(id, &authctxt->keys, next)
1874ce74bacaSMatthew Dillon 		id->tried = 0;
1875ce74bacaSMatthew Dillon }
1876ce74bacaSMatthew Dillon 
1877e9778795SPeter Avalos static int
187850a69bb5SSascha Wildner try_identity(struct ssh *ssh, Identity *id)
1879e9778795SPeter Avalos {
1880e9778795SPeter Avalos 	if (!id->key)
1881e9778795SPeter Avalos 		return (0);
1882664f4763Szrj 	if (sshkey_type_plain(id->key->type) == KEY_RSA &&
188350a69bb5SSascha Wildner 	    (ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
1884e9778795SPeter Avalos 		debug("Skipped %s key %s for RSA/MD5 server",
1885664f4763Szrj 		    sshkey_type(id->key), id->filename);
1886e9778795SPeter Avalos 		return (0);
1887e9778795SPeter Avalos 	}
1888ce74bacaSMatthew Dillon 	return 1;
1889e9778795SPeter Avalos }
1890e9778795SPeter Avalos 
1891664f4763Szrj static int
1892664f4763Szrj userauth_pubkey(struct ssh *ssh)
189318de8d7fSPeter Avalos {
1894664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
189518de8d7fSPeter Avalos 	Identity *id;
189618de8d7fSPeter Avalos 	int sent = 0;
1897664f4763Szrj 	char *ident;
189818de8d7fSPeter Avalos 
189918de8d7fSPeter Avalos 	while ((id = TAILQ_FIRST(&authctxt->keys))) {
190018de8d7fSPeter Avalos 		if (id->tried++)
190118de8d7fSPeter Avalos 			return (0);
190218de8d7fSPeter Avalos 		/* move key to the end of the queue */
190318de8d7fSPeter Avalos 		TAILQ_REMOVE(&authctxt->keys, id, next);
190418de8d7fSPeter Avalos 		TAILQ_INSERT_TAIL(&authctxt->keys, id, next);
190518de8d7fSPeter Avalos 		/*
190618de8d7fSPeter Avalos 		 * send a test message if we have the public key. for
190718de8d7fSPeter Avalos 		 * encrypted keys we cannot do this and have to load the
190818de8d7fSPeter Avalos 		 * private key instead
190918de8d7fSPeter Avalos 		 */
191036e94dc5SPeter Avalos 		if (id->key != NULL) {
191150a69bb5SSascha Wildner 			if (try_identity(ssh, id)) {
1912664f4763Szrj 				ident = format_identity(id);
1913664f4763Szrj 				debug("Offering public key: %s", ident);
1914664f4763Szrj 				free(ident);
1915664f4763Szrj 				sent = send_pubkey_test(ssh, id);
191636e94dc5SPeter Avalos 			}
191736e94dc5SPeter Avalos 		} else {
191818de8d7fSPeter Avalos 			debug("Trying private key: %s", id->filename);
1919e9778795SPeter Avalos 			id->key = load_identity_file(id);
192018de8d7fSPeter Avalos 			if (id->key != NULL) {
192150a69bb5SSascha Wildner 				if (try_identity(ssh, id)) {
192218de8d7fSPeter Avalos 					id->isprivate = 1;
1923664f4763Szrj 					sent = sign_and_send_pubkey(ssh, id);
192436e94dc5SPeter Avalos 				}
1925664f4763Szrj 				sshkey_free(id->key);
192618de8d7fSPeter Avalos 				id->key = NULL;
1927ce74bacaSMatthew Dillon 				id->isprivate = 0;
192818de8d7fSPeter Avalos 			}
192918de8d7fSPeter Avalos 		}
193018de8d7fSPeter Avalos 		if (sent)
193118de8d7fSPeter Avalos 			return (sent);
193218de8d7fSPeter Avalos 	}
193318de8d7fSPeter Avalos 	return (0);
193418de8d7fSPeter Avalos }
193518de8d7fSPeter Avalos 
193618de8d7fSPeter Avalos /*
193718de8d7fSPeter Avalos  * Send userauth request message specifying keyboard-interactive method.
193818de8d7fSPeter Avalos  */
1939664f4763Szrj static int
1940664f4763Szrj userauth_kbdint(struct ssh *ssh)
194118de8d7fSPeter Avalos {
1942664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
1943664f4763Szrj 	int r;
194418de8d7fSPeter Avalos 
1945664f4763Szrj 	if (authctxt->attempt_kbdint++ >= options.number_of_password_prompts)
194618de8d7fSPeter Avalos 		return 0;
194718de8d7fSPeter Avalos 	/* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */
1948664f4763Szrj 	if (authctxt->attempt_kbdint > 1 && !authctxt->info_req_seen) {
194918de8d7fSPeter Avalos 		debug3("userauth_kbdint: disable: no info_req_seen");
1950664f4763Szrj 		ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_REQUEST, NULL);
195118de8d7fSPeter Avalos 		return 0;
195218de8d7fSPeter Avalos 	}
195318de8d7fSPeter Avalos 
195418de8d7fSPeter Avalos 	debug2("userauth_kbdint");
1955664f4763Szrj 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
1956664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
1957664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
1958664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
1959664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, "")) != 0 ||		/* lang */
1960664f4763Szrj 	    (r = sshpkt_put_cstring(ssh, options.kbd_interactive_devices ?
1961664f4763Szrj 	    options.kbd_interactive_devices : "")) != 0 ||
1962664f4763Szrj 	    (r = sshpkt_send(ssh)) != 0)
196350a69bb5SSascha Wildner 		fatal_fr(r, "send packet");
196418de8d7fSPeter Avalos 
1965664f4763Szrj 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
196618de8d7fSPeter Avalos 	return 1;
196718de8d7fSPeter Avalos }
196818de8d7fSPeter Avalos 
196918de8d7fSPeter Avalos /*
197018de8d7fSPeter Avalos  * parse INFO_REQUEST, prompt user and send INFO_RESPONSE
197118de8d7fSPeter Avalos  */
1972664f4763Szrj static int
1973ce74bacaSMatthew Dillon input_userauth_info_req(int type, u_int32_t seq, struct ssh *ssh)
197418de8d7fSPeter Avalos {
1975ce74bacaSMatthew Dillon 	Authctxt *authctxt = ssh->authctxt;
1976664f4763Szrj 	char *name = NULL, *inst = NULL, *lang = NULL, *prompt = NULL;
197750a69bb5SSascha Wildner 	char *display_prompt = NULL, *response = NULL;
1978664f4763Szrj 	u_char echo = 0;
197918de8d7fSPeter Avalos 	u_int num_prompts, i;
1980664f4763Szrj 	int r;
198118de8d7fSPeter Avalos 
198250a69bb5SSascha Wildner 	debug2_f("entering");
198318de8d7fSPeter Avalos 
198418de8d7fSPeter Avalos 	if (authctxt == NULL)
198550a69bb5SSascha Wildner 		fatal_f("no authentication context");
198618de8d7fSPeter Avalos 
198718de8d7fSPeter Avalos 	authctxt->info_req_seen = 1;
198818de8d7fSPeter Avalos 
1989664f4763Szrj 	if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0 ||
1990664f4763Szrj 	    (r = sshpkt_get_cstring(ssh, &inst, NULL)) != 0 ||
1991664f4763Szrj 	    (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0)
1992664f4763Szrj 		goto out;
199318de8d7fSPeter Avalos 	if (strlen(name) > 0)
199418de8d7fSPeter Avalos 		logit("%s", name);
199518de8d7fSPeter Avalos 	if (strlen(inst) > 0)
199618de8d7fSPeter Avalos 		logit("%s", inst);
199718de8d7fSPeter Avalos 
1998664f4763Szrj 	if ((r = sshpkt_get_u32(ssh, &num_prompts)) != 0)
1999664f4763Szrj 		goto out;
200018de8d7fSPeter Avalos 	/*
200118de8d7fSPeter Avalos 	 * Begin to build info response packet based on prompts requested.
200218de8d7fSPeter Avalos 	 * We commit to providing the correct number of responses, so if
200318de8d7fSPeter Avalos 	 * further on we run into a problem that prevents this, we have to
200418de8d7fSPeter Avalos 	 * be sure and clean this up and send a correct error response.
200518de8d7fSPeter Avalos 	 */
2006664f4763Szrj 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_INFO_RESPONSE)) != 0 ||
2007664f4763Szrj 	    (r = sshpkt_put_u32(ssh, num_prompts)) != 0)
2008664f4763Szrj 		goto out;
200918de8d7fSPeter Avalos 
201050a69bb5SSascha Wildner 	debug2_f("num_prompts %d", num_prompts);
201118de8d7fSPeter Avalos 	for (i = 0; i < num_prompts; i++) {
2012664f4763Szrj 		if ((r = sshpkt_get_cstring(ssh, &prompt, NULL)) != 0 ||
2013664f4763Szrj 		    (r = sshpkt_get_u8(ssh, &echo)) != 0)
2014664f4763Szrj 			goto out;
201550a69bb5SSascha Wildner 		if (asmprintf(&display_prompt, INT_MAX, NULL, "(%s@%s) %s",
201650a69bb5SSascha Wildner 		    authctxt->server_user, options.host_key_alias ?
201750a69bb5SSascha Wildner 		    options.host_key_alias : authctxt->host, prompt) == -1)
201850a69bb5SSascha Wildner 			fatal_f("asmprintf failed");
201950a69bb5SSascha Wildner 		response = read_passphrase(display_prompt, echo ? RP_ECHO : 0);
2020664f4763Szrj 		if ((r = sshpkt_put_cstring(ssh, response)) != 0)
2021664f4763Szrj 			goto out;
2022664f4763Szrj 		freezero(response, strlen(response));
202336e94dc5SPeter Avalos 		free(prompt);
202450a69bb5SSascha Wildner 		free(display_prompt);
202550a69bb5SSascha Wildner 		display_prompt = response = prompt = NULL;
202618de8d7fSPeter Avalos 	}
2027664f4763Szrj 	/* done with parsing incoming message. */
2028664f4763Szrj 	if ((r = sshpkt_get_end(ssh)) != 0 ||
2029664f4763Szrj 	    (r = sshpkt_add_padding(ssh, 64)) != 0)
2030664f4763Szrj 		goto out;
2031664f4763Szrj 	r = sshpkt_send(ssh);
2032664f4763Szrj  out:
2033664f4763Szrj 	if (response)
2034664f4763Szrj 		freezero(response, strlen(response));
2035664f4763Szrj 	free(prompt);
203650a69bb5SSascha Wildner 	free(display_prompt);
2037664f4763Szrj 	free(name);
2038664f4763Szrj 	free(inst);
2039664f4763Szrj 	free(lang);
2040664f4763Szrj 	return r;
204118de8d7fSPeter Avalos }
204218de8d7fSPeter Avalos 
204318de8d7fSPeter Avalos static int
2044664f4763Szrj ssh_keysign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp,
2045e9778795SPeter Avalos     const u_char *data, size_t datalen)
204618de8d7fSPeter Avalos {
2047e9778795SPeter Avalos 	struct sshbuf *b;
204818de8d7fSPeter Avalos 	struct stat st;
204918de8d7fSPeter Avalos 	pid_t pid;
20500cbfa66cSDaniel Fojt 	int r, to[2], from[2], status;
2051664f4763Szrj 	int sock = ssh_packet_get_connection_in(ssh);
2052e9778795SPeter Avalos 	u_char rversion = 0, version = 2;
2053e9778795SPeter Avalos 	void (*osigchld)(int);
205418de8d7fSPeter Avalos 
2055e9778795SPeter Avalos 	*sigp = NULL;
2056e9778795SPeter Avalos 	*lenp = 0;
205718de8d7fSPeter Avalos 
20580cbfa66cSDaniel Fojt 	if (stat(_PATH_SSH_KEY_SIGN, &st) == -1) {
205950a69bb5SSascha Wildner 		error_f("not installed: %s", strerror(errno));
206018de8d7fSPeter Avalos 		return -1;
206118de8d7fSPeter Avalos 	}
2062e9778795SPeter Avalos 	if (fflush(stdout) != 0) {
206350a69bb5SSascha Wildner 		error_f("fflush: %s", strerror(errno));
2064e9778795SPeter Avalos 		return -1;
2065e9778795SPeter Avalos 	}
20660cbfa66cSDaniel Fojt 	if (pipe(to) == -1) {
206750a69bb5SSascha Wildner 		error_f("pipe: %s", strerror(errno));
206818de8d7fSPeter Avalos 		return -1;
206918de8d7fSPeter Avalos 	}
20700cbfa66cSDaniel Fojt 	if (pipe(from) == -1) {
207150a69bb5SSascha Wildner 		error_f("pipe: %s", strerror(errno));
207218de8d7fSPeter Avalos 		return -1;
207318de8d7fSPeter Avalos 	}
20740cbfa66cSDaniel Fojt 	if ((pid = fork()) == -1) {
207550a69bb5SSascha Wildner 		error_f("fork: %s", strerror(errno));
207618de8d7fSPeter Avalos 		return -1;
207718de8d7fSPeter Avalos 	}
20780cbfa66cSDaniel Fojt 	osigchld = ssh_signal(SIGCHLD, SIG_DFL);
207918de8d7fSPeter Avalos 	if (pid == 0) {
208018de8d7fSPeter Avalos 		close(from[0]);
20810cbfa66cSDaniel Fojt 		if (dup2(from[1], STDOUT_FILENO) == -1)
208250a69bb5SSascha Wildner 			fatal_f("dup2: %s", strerror(errno));
208318de8d7fSPeter Avalos 		close(to[1]);
20840cbfa66cSDaniel Fojt 		if (dup2(to[0], STDIN_FILENO) == -1)
208550a69bb5SSascha Wildner 			fatal_f("dup2: %s", strerror(errno));
208618de8d7fSPeter Avalos 		close(from[1]);
208718de8d7fSPeter Avalos 		close(to[0]);
20880cbfa66cSDaniel Fojt 
20890cbfa66cSDaniel Fojt 		if (dup2(sock, STDERR_FILENO + 1) == -1)
209050a69bb5SSascha Wildner 			fatal_f("dup2: %s", strerror(errno));
20910cbfa66cSDaniel Fojt 		sock = STDERR_FILENO + 1;
20920cbfa66cSDaniel Fojt 		fcntl(sock, F_SETFD, 0);	/* keep the socket on exec */
2093e9778795SPeter Avalos 		closefrom(sock + 1);
20940cbfa66cSDaniel Fojt 
209550a69bb5SSascha Wildner 		debug3_f("[child] pid=%ld, exec %s",
209650a69bb5SSascha Wildner 		    (long)getpid(), _PATH_SSH_KEY_SIGN);
2097e9778795SPeter Avalos 		execl(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *)NULL);
209850a69bb5SSascha Wildner 		fatal_f("exec(%s): %s", _PATH_SSH_KEY_SIGN,
209918de8d7fSPeter Avalos 		    strerror(errno));
210018de8d7fSPeter Avalos 	}
210118de8d7fSPeter Avalos 	close(from[1]);
210218de8d7fSPeter Avalos 	close(to[0]);
21030cbfa66cSDaniel Fojt 	sock = STDERR_FILENO + 1;
210418de8d7fSPeter Avalos 
2105e9778795SPeter Avalos 	if ((b = sshbuf_new()) == NULL)
210650a69bb5SSascha Wildner 		fatal_f("sshbuf_new failed");
2107e9778795SPeter Avalos 	/* send # of sock, data to be signed */
2108ce74bacaSMatthew Dillon 	if ((r = sshbuf_put_u32(b, sock)) != 0 ||
2109e9778795SPeter Avalos 	    (r = sshbuf_put_string(b, data, datalen)) != 0)
211050a69bb5SSascha Wildner 		fatal_fr(r, "buffer error");
2111e9778795SPeter Avalos 	if (ssh_msg_send(to[1], version, b) == -1)
211250a69bb5SSascha Wildner 		fatal_f("couldn't send request");
2113e9778795SPeter Avalos 	sshbuf_reset(b);
2114e9778795SPeter Avalos 	r = ssh_msg_recv(from[0], b);
211518de8d7fSPeter Avalos 	close(from[0]);
211618de8d7fSPeter Avalos 	close(to[1]);
2117e9778795SPeter Avalos 	if (r < 0) {
211850a69bb5SSascha Wildner 		error_f("no reply");
2119e9778795SPeter Avalos 		goto fail;
2120e9778795SPeter Avalos 	}
212118de8d7fSPeter Avalos 
2122e9778795SPeter Avalos 	errno = 0;
21230cbfa66cSDaniel Fojt 	while (waitpid(pid, &status, 0) == -1) {
2124e9778795SPeter Avalos 		if (errno != EINTR) {
212550a69bb5SSascha Wildner 			error_f("waitpid %ld: %s", (long)pid, strerror(errno));
2126e9778795SPeter Avalos 			goto fail;
2127e9778795SPeter Avalos 		}
2128e9778795SPeter Avalos 	}
2129e9778795SPeter Avalos 	if (!WIFEXITED(status)) {
213050a69bb5SSascha Wildner 		error_f("exited abnormally");
2131e9778795SPeter Avalos 		goto fail;
2132e9778795SPeter Avalos 	}
2133e9778795SPeter Avalos 	if (WEXITSTATUS(status) != 0) {
213450a69bb5SSascha Wildner 		error_f("exited with status %d", WEXITSTATUS(status));
2135e9778795SPeter Avalos 		goto fail;
2136e9778795SPeter Avalos 	}
2137e9778795SPeter Avalos 	if ((r = sshbuf_get_u8(b, &rversion)) != 0) {
213850a69bb5SSascha Wildner 		error_fr(r, "buffer error");
2139e9778795SPeter Avalos 		goto fail;
2140e9778795SPeter Avalos 	}
2141e9778795SPeter Avalos 	if (rversion != version) {
214250a69bb5SSascha Wildner 		error_f("bad version");
2143e9778795SPeter Avalos 		goto fail;
2144e9778795SPeter Avalos 	}
2145e9778795SPeter Avalos 	if ((r = sshbuf_get_string(b, sigp, lenp)) != 0) {
214650a69bb5SSascha Wildner 		error_fr(r, "buffer error");
2147e9778795SPeter Avalos  fail:
21480cbfa66cSDaniel Fojt 		ssh_signal(SIGCHLD, osigchld);
2149e9778795SPeter Avalos 		sshbuf_free(b);
215018de8d7fSPeter Avalos 		return -1;
215118de8d7fSPeter Avalos 	}
21520cbfa66cSDaniel Fojt 	ssh_signal(SIGCHLD, osigchld);
2153e9778795SPeter Avalos 	sshbuf_free(b);
215418de8d7fSPeter Avalos 
215518de8d7fSPeter Avalos 	return 0;
215618de8d7fSPeter Avalos }
215718de8d7fSPeter Avalos 
2158664f4763Szrj static int
2159664f4763Szrj userauth_hostbased(struct ssh *ssh)
216018de8d7fSPeter Avalos {
2161664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
2162e9778795SPeter Avalos 	struct sshkey *private = NULL;
2163e9778795SPeter Avalos 	struct sshbuf *b = NULL;
2164e9778795SPeter Avalos 	u_char *sig = NULL, *keyblob = NULL;
2165e9778795SPeter Avalos 	char *fp = NULL, *chost = NULL, *lname = NULL;
2166e9778795SPeter Avalos 	size_t siglen = 0, keylen = 0;
2167e9778795SPeter Avalos 	int i, r, success = 0;
2168e9778795SPeter Avalos 
2169e9778795SPeter Avalos 	if (authctxt->ktypes == NULL) {
217050a69bb5SSascha Wildner 		authctxt->oktypes = xstrdup(options.hostbased_accepted_algos);
2171e9778795SPeter Avalos 		authctxt->ktypes = authctxt->oktypes;
2172e9778795SPeter Avalos 	}
2173e9778795SPeter Avalos 
2174e9778795SPeter Avalos 	/*
217550a69bb5SSascha Wildner 	 * Work through each listed type pattern in HostbasedAcceptedAlgorithms,
2176e9778795SPeter Avalos 	 * trying each hostkey that matches the type in turn.
2177e9778795SPeter Avalos 	 */
2178e9778795SPeter Avalos 	for (;;) {
2179e9778795SPeter Avalos 		if (authctxt->active_ktype == NULL)
2180e9778795SPeter Avalos 			authctxt->active_ktype = strsep(&authctxt->ktypes, ",");
2181e9778795SPeter Avalos 		if (authctxt->active_ktype == NULL ||
2182e9778795SPeter Avalos 		    *authctxt->active_ktype == '\0')
2183e9778795SPeter Avalos 			break;
218450a69bb5SSascha Wildner 		debug3_f("trying key type %s", authctxt->active_ktype);
218518de8d7fSPeter Avalos 
218618de8d7fSPeter Avalos 		/* check for a useful key */
2187e9778795SPeter Avalos 		private = NULL;
2188e9778795SPeter Avalos 		for (i = 0; i < authctxt->sensitive->nkeys; i++) {
2189e9778795SPeter Avalos 			if (authctxt->sensitive->keys[i] == NULL ||
2190e9778795SPeter Avalos 			    authctxt->sensitive->keys[i]->type == KEY_UNSPEC)
2191e9778795SPeter Avalos 				continue;
2192*ee116499SAntonio Huete Jimenez 			if (!sshkey_match_keyname_to_sigalgs(
2193e9778795SPeter Avalos 			    sshkey_ssh_name(authctxt->sensitive->keys[i]),
2194*ee116499SAntonio Huete Jimenez 			    authctxt->active_ktype))
2195e9778795SPeter Avalos 				continue;
219618de8d7fSPeter Avalos 			/* we take and free the key */
2197e9778795SPeter Avalos 			private = authctxt->sensitive->keys[i];
2198e9778795SPeter Avalos 			authctxt->sensitive->keys[i] = NULL;
219918de8d7fSPeter Avalos 			break;
220018de8d7fSPeter Avalos 		}
2201e9778795SPeter Avalos 		/* Found one */
2202e9778795SPeter Avalos 		if (private != NULL)
2203e9778795SPeter Avalos 			break;
2204e9778795SPeter Avalos 		/* No more keys of this type; advance */
2205e9778795SPeter Avalos 		authctxt->active_ktype = NULL;
220618de8d7fSPeter Avalos 	}
2207e9778795SPeter Avalos 	if (private == NULL) {
2208e9778795SPeter Avalos 		free(authctxt->oktypes);
2209e9778795SPeter Avalos 		authctxt->oktypes = authctxt->ktypes = NULL;
2210e9778795SPeter Avalos 		authctxt->active_ktype = NULL;
221118de8d7fSPeter Avalos 		debug("No more client hostkeys for hostbased authentication.");
2212e9778795SPeter Avalos 		goto out;
221318de8d7fSPeter Avalos 	}
2214e9778795SPeter Avalos 
2215e9778795SPeter Avalos 	if ((fp = sshkey_fingerprint(private, options.fingerprint_hash,
2216e9778795SPeter Avalos 	    SSH_FP_DEFAULT)) == NULL) {
221750a69bb5SSascha Wildner 		error_f("sshkey_fingerprint failed");
2218e9778795SPeter Avalos 		goto out;
221918de8d7fSPeter Avalos 	}
2220*ee116499SAntonio Huete Jimenez 	debug_f("trying hostkey %s %s using sigalg %s",
2221*ee116499SAntonio Huete Jimenez 	    sshkey_ssh_name(private), fp, authctxt->active_ktype);
2222e9778795SPeter Avalos 
222318de8d7fSPeter Avalos 	/* figure out a name for the client host */
2224664f4763Szrj 	lname = get_local_name(ssh_packet_get_connection_in(ssh));
2225664f4763Szrj 	if (lname == NULL) {
222650a69bb5SSascha Wildner 		error_f("cannot get local ipaddr/name");
2227e9778795SPeter Avalos 		goto out;
222818de8d7fSPeter Avalos 	}
2229e9778795SPeter Avalos 
2230e9778795SPeter Avalos 	/* XXX sshbuf_put_stringf? */
2231e9778795SPeter Avalos 	xasprintf(&chost, "%s.", lname);
223250a69bb5SSascha Wildner 	debug2_f("chost %s", chost);
223318de8d7fSPeter Avalos 
2234e9778795SPeter Avalos 	/* construct data */
2235e9778795SPeter Avalos 	if ((b = sshbuf_new()) == NULL) {
223650a69bb5SSascha Wildner 		error_f("sshbuf_new failed");
2237e9778795SPeter Avalos 		goto out;
2238e9778795SPeter Avalos 	}
2239e9778795SPeter Avalos 	if ((r = sshkey_to_blob(private, &keyblob, &keylen)) != 0) {
224050a69bb5SSascha Wildner 		error_fr(r, "sshkey_to_blob");
2241e9778795SPeter Avalos 		goto out;
2242e9778795SPeter Avalos 	}
224350a69bb5SSascha Wildner 	if ((r = sshbuf_put_stringb(b, ssh->kex->session_id)) != 0 ||
2244e9778795SPeter Avalos 	    (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
2245e9778795SPeter Avalos 	    (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 ||
2246664f4763Szrj 	    (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
2247e9778795SPeter Avalos 	    (r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 ||
224850a69bb5SSascha Wildner 	    (r = sshbuf_put_cstring(b, authctxt->active_ktype)) != 0 ||
2249e9778795SPeter Avalos 	    (r = sshbuf_put_string(b, keyblob, keylen)) != 0 ||
2250e9778795SPeter Avalos 	    (r = sshbuf_put_cstring(b, chost)) != 0 ||
2251e9778795SPeter Avalos 	    (r = sshbuf_put_cstring(b, authctxt->local_user)) != 0) {
225250a69bb5SSascha Wildner 		error_fr(r, "buffer error");
2253e9778795SPeter Avalos 		goto out;
2254e9778795SPeter Avalos 	}
2255e9778795SPeter Avalos 
2256e9778795SPeter Avalos #ifdef DEBUG_PK
2257e9778795SPeter Avalos 	sshbuf_dump(b, stderr);
2258e9778795SPeter Avalos #endif
2259664f4763Szrj 	if ((r = ssh_keysign(ssh, private, &sig, &siglen,
2260664f4763Szrj 	    sshbuf_ptr(b), sshbuf_len(b))) != 0) {
2261e9778795SPeter Avalos 		error("sign using hostkey %s %s failed",
2262e9778795SPeter Avalos 		    sshkey_ssh_name(private), fp);
2263e9778795SPeter Avalos 		goto out;
2264e9778795SPeter Avalos 	}
2265e9778795SPeter Avalos 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
2266e9778795SPeter Avalos 	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
2267e9778795SPeter Avalos 	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
2268e9778795SPeter Avalos 	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
226950a69bb5SSascha Wildner 	    (r = sshpkt_put_cstring(ssh, authctxt->active_ktype)) != 0 ||
2270e9778795SPeter Avalos 	    (r = sshpkt_put_string(ssh, keyblob, keylen)) != 0 ||
2271e9778795SPeter Avalos 	    (r = sshpkt_put_cstring(ssh, chost)) != 0 ||
2272e9778795SPeter Avalos 	    (r = sshpkt_put_cstring(ssh, authctxt->local_user)) != 0 ||
2273e9778795SPeter Avalos 	    (r = sshpkt_put_string(ssh, sig, siglen)) != 0 ||
2274e9778795SPeter Avalos 	    (r = sshpkt_send(ssh)) != 0) {
227550a69bb5SSascha Wildner 		error_fr(r, "packet error");
2276e9778795SPeter Avalos 		goto out;
2277e9778795SPeter Avalos 	}
2278e9778795SPeter Avalos 	success = 1;
2279e9778795SPeter Avalos 
2280e9778795SPeter Avalos  out:
2281664f4763Szrj 	if (sig != NULL)
2282664f4763Szrj 		freezero(sig, siglen);
2283e9778795SPeter Avalos 	free(keyblob);
2284e9778795SPeter Avalos 	free(lname);
2285e9778795SPeter Avalos 	free(fp);
2286e9778795SPeter Avalos 	free(chost);
2287e9778795SPeter Avalos 	sshkey_free(private);
2288e9778795SPeter Avalos 	sshbuf_free(b);
2289e9778795SPeter Avalos 
2290e9778795SPeter Avalos 	return success;
229118de8d7fSPeter Avalos }
229218de8d7fSPeter Avalos 
229318de8d7fSPeter Avalos /* find auth method */
229418de8d7fSPeter Avalos 
229518de8d7fSPeter Avalos /*
229618de8d7fSPeter Avalos  * given auth method name, if configurable options permit this method fill
229718de8d7fSPeter Avalos  * in auth_ident field and return true, otherwise return false.
229818de8d7fSPeter Avalos  */
229918de8d7fSPeter Avalos static int
230018de8d7fSPeter Avalos authmethod_is_enabled(Authmethod *method)
230118de8d7fSPeter Avalos {
230218de8d7fSPeter Avalos 	if (method == NULL)
230318de8d7fSPeter Avalos 		return 0;
230418de8d7fSPeter Avalos 	/* return false if options indicate this method is disabled */
230518de8d7fSPeter Avalos 	if  (method->enabled == NULL || *method->enabled == 0)
230618de8d7fSPeter Avalos 		return 0;
230718de8d7fSPeter Avalos 	/* return false if batch mode is enabled but method needs interactive mode */
230818de8d7fSPeter Avalos 	if  (method->batch_flag != NULL && *method->batch_flag != 0)
230918de8d7fSPeter Avalos 		return 0;
231018de8d7fSPeter Avalos 	return 1;
231118de8d7fSPeter Avalos }
231218de8d7fSPeter Avalos 
231318de8d7fSPeter Avalos static Authmethod *
231418de8d7fSPeter Avalos authmethod_lookup(const char *name)
231518de8d7fSPeter Avalos {
231618de8d7fSPeter Avalos 	Authmethod *method = NULL;
231718de8d7fSPeter Avalos 	if (name != NULL)
231818de8d7fSPeter Avalos 		for (method = authmethods; method->name != NULL; method++)
231918de8d7fSPeter Avalos 			if (strcmp(name, method->name) == 0)
232018de8d7fSPeter Avalos 				return method;
232118de8d7fSPeter Avalos 	debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
232218de8d7fSPeter Avalos 	return NULL;
232318de8d7fSPeter Avalos }
232418de8d7fSPeter Avalos 
232518de8d7fSPeter Avalos /* XXX internal state */
232618de8d7fSPeter Avalos static Authmethod *current = NULL;
232718de8d7fSPeter Avalos static char *supported = NULL;
232818de8d7fSPeter Avalos static char *preferred = NULL;
232918de8d7fSPeter Avalos 
233018de8d7fSPeter Avalos /*
233118de8d7fSPeter Avalos  * Given the authentication method list sent by the server, return the
233218de8d7fSPeter Avalos  * next method we should try.  If the server initially sends a nil list,
233318de8d7fSPeter Avalos  * use a built-in default list.
233418de8d7fSPeter Avalos  */
233518de8d7fSPeter Avalos static Authmethod *
233618de8d7fSPeter Avalos authmethod_get(char *authlist)
233718de8d7fSPeter Avalos {
233818de8d7fSPeter Avalos 	char *name = NULL;
233918de8d7fSPeter Avalos 	u_int next;
234018de8d7fSPeter Avalos 
234118de8d7fSPeter Avalos 	/* Use a suitable default if we're passed a nil list.  */
234218de8d7fSPeter Avalos 	if (authlist == NULL || strlen(authlist) == 0)
234318de8d7fSPeter Avalos 		authlist = options.preferred_authentications;
234418de8d7fSPeter Avalos 
234518de8d7fSPeter Avalos 	if (supported == NULL || strcmp(authlist, supported) != 0) {
234618de8d7fSPeter Avalos 		debug3("start over, passed a different list %s", authlist);
234736e94dc5SPeter Avalos 		free(supported);
234818de8d7fSPeter Avalos 		supported = xstrdup(authlist);
234918de8d7fSPeter Avalos 		preferred = options.preferred_authentications;
235018de8d7fSPeter Avalos 		debug3("preferred %s", preferred);
235118de8d7fSPeter Avalos 		current = NULL;
235218de8d7fSPeter Avalos 	} else if (current != NULL && authmethod_is_enabled(current))
235318de8d7fSPeter Avalos 		return current;
235418de8d7fSPeter Avalos 
235518de8d7fSPeter Avalos 	for (;;) {
235618de8d7fSPeter Avalos 		if ((name = match_list(preferred, supported, &next)) == NULL) {
235718de8d7fSPeter Avalos 			debug("No more authentication methods to try.");
235818de8d7fSPeter Avalos 			current = NULL;
235918de8d7fSPeter Avalos 			return NULL;
236018de8d7fSPeter Avalos 		}
236118de8d7fSPeter Avalos 		preferred += next;
236218de8d7fSPeter Avalos 		debug3("authmethod_lookup %s", name);
236318de8d7fSPeter Avalos 		debug3("remaining preferred: %s", preferred);
236418de8d7fSPeter Avalos 		if ((current = authmethod_lookup(name)) != NULL &&
236518de8d7fSPeter Avalos 		    authmethod_is_enabled(current)) {
236618de8d7fSPeter Avalos 			debug3("authmethod_is_enabled %s", name);
236718de8d7fSPeter Avalos 			debug("Next authentication method: %s", name);
236836e94dc5SPeter Avalos 			free(name);
236918de8d7fSPeter Avalos 			return current;
237018de8d7fSPeter Avalos 		}
237136e94dc5SPeter Avalos 		free(name);
237218de8d7fSPeter Avalos 	}
237318de8d7fSPeter Avalos }
237418de8d7fSPeter Avalos 
237518de8d7fSPeter Avalos static char *
237618de8d7fSPeter Avalos authmethods_get(void)
237718de8d7fSPeter Avalos {
237818de8d7fSPeter Avalos 	Authmethod *method = NULL;
2379664f4763Szrj 	struct sshbuf *b;
238018de8d7fSPeter Avalos 	char *list;
2381664f4763Szrj 	int r;
238218de8d7fSPeter Avalos 
2383664f4763Szrj 	if ((b = sshbuf_new()) == NULL)
238450a69bb5SSascha Wildner 		fatal_f("sshbuf_new failed");
238518de8d7fSPeter Avalos 	for (method = authmethods; method->name != NULL; method++) {
238618de8d7fSPeter Avalos 		if (authmethod_is_enabled(method)) {
2387664f4763Szrj 			if ((r = sshbuf_putf(b, "%s%s",
2388664f4763Szrj 			    sshbuf_len(b) ? "," : "", method->name)) != 0)
238950a69bb5SSascha Wildner 				fatal_fr(r, "buffer error");
239018de8d7fSPeter Avalos 		}
239118de8d7fSPeter Avalos 	}
2392664f4763Szrj 	if ((list = sshbuf_dup_string(b)) == NULL)
239350a69bb5SSascha Wildner 		fatal_f("sshbuf_dup_string failed");
2394664f4763Szrj 	sshbuf_free(b);
239518de8d7fSPeter Avalos 	return list;
239618de8d7fSPeter Avalos }
2397