xref: /freebsd/crypto/openssh/sshconnect.c (revision 190cef3d52236565eb22e18b33e9e865ec634aa3)
1*190cef3dSDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.304 2018/07/27 05:34:42 dtucker Exp $ */
2511b41d2SMark Murray /*
3511b41d2SMark Murray  * Author: Tatu Ylonen <ylo@cs.hut.fi>
4511b41d2SMark Murray  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5511b41d2SMark Murray  *                    All rights reserved
6511b41d2SMark Murray  * Code to connect to a remote host, and to perform the client side of the
7511b41d2SMark Murray  * login (authentication) dialog.
842f71286SMark Murray  *
9c2d3a559SKris Kennaway  * As far as I am concerned, the code I have written for this software
10c2d3a559SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
11c2d3a559SKris Kennaway  * software must be clearly marked as such, and if the derived work is
12c2d3a559SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
13c2d3a559SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
14511b41d2SMark Murray  */
15511b41d2SMark Murray 
16511b41d2SMark Murray #include "includes.h"
17cf783db1SDag-Erling Smørgrav __RCSID("$FreeBSD$");
18511b41d2SMark Murray 
19333ee039SDag-Erling Smørgrav #include <sys/types.h>
20333ee039SDag-Erling Smørgrav #include <sys/wait.h>
21333ee039SDag-Erling Smørgrav #include <sys/stat.h>
22333ee039SDag-Erling Smørgrav #include <sys/socket.h>
23333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H
24333ee039SDag-Erling Smørgrav # include <sys/time.h>
25333ee039SDag-Erling Smørgrav #endif
26e8aafc91SKris Kennaway 
2747dd1d1bSDag-Erling Smørgrav #include <net/if.h>
28333ee039SDag-Erling Smørgrav #include <netinet/in.h>
29333ee039SDag-Erling Smørgrav #include <arpa/inet.h>
30f7167e0eSDag-Erling Smørgrav #include <rpc/rpc.h>
31333ee039SDag-Erling Smørgrav 
32333ee039SDag-Erling Smørgrav #include <ctype.h>
33333ee039SDag-Erling Smørgrav #include <errno.h>
34b15c8340SDag-Erling Smørgrav #include <fcntl.h>
35333ee039SDag-Erling Smørgrav #include <netdb.h>
36333ee039SDag-Erling Smørgrav #ifdef HAVE_PATHS_H
37333ee039SDag-Erling Smørgrav #include <paths.h>
38333ee039SDag-Erling Smørgrav #endif
39333ee039SDag-Erling Smørgrav #include <pwd.h>
404f52dfbbSDag-Erling Smørgrav #ifdef HAVE_POLL_H
414f52dfbbSDag-Erling Smørgrav #include <poll.h>
424f52dfbbSDag-Erling Smørgrav #endif
434a421b63SDag-Erling Smørgrav #include <signal.h>
44333ee039SDag-Erling Smørgrav #include <stdarg.h>
45333ee039SDag-Erling Smørgrav #include <stdio.h>
46333ee039SDag-Erling Smørgrav #include <stdlib.h>
47333ee039SDag-Erling Smørgrav #include <string.h>
48333ee039SDag-Erling Smørgrav #include <unistd.h>
4947dd1d1bSDag-Erling Smørgrav #ifdef HAVE_IFADDRS_H
5047dd1d1bSDag-Erling Smørgrav # include <ifaddrs.h>
5147dd1d1bSDag-Erling Smørgrav #endif
52333ee039SDag-Erling Smørgrav 
53511b41d2SMark Murray #include "xmalloc.h"
54333ee039SDag-Erling Smørgrav #include "hostfile.h"
55333ee039SDag-Erling Smørgrav #include "ssh.h"
56*190cef3dSDag-Erling Smørgrav #include "sshbuf.h"
57511b41d2SMark Murray #include "packet.h"
58511b41d2SMark Murray #include "compat.h"
59*190cef3dSDag-Erling Smørgrav #include "sshkey.h"
60e8aafc91SKris Kennaway #include "sshconnect.h"
613c6ae118SKris Kennaway #include "hostfile.h"
62ca3176e7SBrian Feldman #include "log.h"
63a0ee8cc6SDag-Erling Smørgrav #include "misc.h"
64ca3176e7SBrian Feldman #include "readconf.h"
65ca3176e7SBrian Feldman #include "atomicio.h"
66cf2b5f3bSDag-Erling Smørgrav #include "dns.h"
67f7167e0eSDag-Erling Smørgrav #include "monitor_fdpass.h"
68b15c8340SDag-Erling Smørgrav #include "ssh2.h"
69333ee039SDag-Erling Smørgrav #include "version.h"
70bc5531deSDag-Erling Smørgrav #include "authfile.h"
71bc5531deSDag-Erling Smørgrav #include "ssherr.h"
72acc1a9efSDag-Erling Smørgrav #include "authfd.h"
73cf2b5f3bSDag-Erling Smørgrav 
74e8aafc91SKris Kennaway char *client_version_string = NULL;
75e8aafc91SKris Kennaway char *server_version_string = NULL;
764f52dfbbSDag-Erling Smørgrav struct sshkey *previous_host_key = NULL;
77511b41d2SMark Murray 
78b74df5b2SDag-Erling Smørgrav static int matching_host_key_dns = 0;
79cf2b5f3bSDag-Erling Smørgrav 
804a421b63SDag-Erling Smørgrav static pid_t proxy_command_pid = 0;
814a421b63SDag-Erling Smørgrav 
8280628bacSDag-Erling Smørgrav /* import */
83511b41d2SMark Murray extern Options options;
84511b41d2SMark Murray extern char *__progname;
85511b41d2SMark Murray 
864f52dfbbSDag-Erling Smørgrav static int show_other_keys(struct hostkeys *, struct sshkey *);
874f52dfbbSDag-Erling Smørgrav static void warn_changed_key(struct sshkey *);
88ca3176e7SBrian Feldman 
89f7167e0eSDag-Erling Smørgrav /* Expand a proxy command */
90f7167e0eSDag-Erling Smørgrav static char *
91f7167e0eSDag-Erling Smørgrav expand_proxy_command(const char *proxy_command, const char *user,
92f7167e0eSDag-Erling Smørgrav     const char *host, int port)
93f7167e0eSDag-Erling Smørgrav {
94f7167e0eSDag-Erling Smørgrav 	char *tmp, *ret, strport[NI_MAXSERV];
95f7167e0eSDag-Erling Smørgrav 
96f7167e0eSDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%d", port);
97f7167e0eSDag-Erling Smørgrav 	xasprintf(&tmp, "exec %s", proxy_command);
98f7167e0eSDag-Erling Smørgrav 	ret = percent_expand(tmp, "h", host, "p", strport,
99f7167e0eSDag-Erling Smørgrav 	    "r", options.user, (char *)NULL);
100f7167e0eSDag-Erling Smørgrav 	free(tmp);
101f7167e0eSDag-Erling Smørgrav 	return ret;
102f7167e0eSDag-Erling Smørgrav }
103f7167e0eSDag-Erling Smørgrav 
104f7167e0eSDag-Erling Smørgrav /*
105f7167e0eSDag-Erling Smørgrav  * Connect to the given ssh server using a proxy command that passes a
106f7167e0eSDag-Erling Smørgrav  * a connected fd back to us.
107f7167e0eSDag-Erling Smørgrav  */
108f7167e0eSDag-Erling Smørgrav static int
1094f52dfbbSDag-Erling Smørgrav ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host, u_short port,
110f7167e0eSDag-Erling Smørgrav     const char *proxy_command)
111f7167e0eSDag-Erling Smørgrav {
112f7167e0eSDag-Erling Smørgrav 	char *command_string;
113f7167e0eSDag-Erling Smørgrav 	int sp[2], sock;
114f7167e0eSDag-Erling Smørgrav 	pid_t pid;
115f7167e0eSDag-Erling Smørgrav 	char *shell;
116f7167e0eSDag-Erling Smørgrav 
117f7167e0eSDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL)
118f7167e0eSDag-Erling Smørgrav 		shell = _PATH_BSHELL;
119f7167e0eSDag-Erling Smørgrav 
120f7167e0eSDag-Erling Smørgrav 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
121f7167e0eSDag-Erling Smørgrav 		fatal("Could not create socketpair to communicate with "
122f7167e0eSDag-Erling Smørgrav 		    "proxy dialer: %.100s", strerror(errno));
123f7167e0eSDag-Erling Smørgrav 
124f7167e0eSDag-Erling Smørgrav 	command_string = expand_proxy_command(proxy_command, options.user,
125f7167e0eSDag-Erling Smørgrav 	    host, port);
126f7167e0eSDag-Erling Smørgrav 	debug("Executing proxy dialer command: %.500s", command_string);
127f7167e0eSDag-Erling Smørgrav 
128f7167e0eSDag-Erling Smørgrav 	/* Fork and execute the proxy command. */
129f7167e0eSDag-Erling Smørgrav 	if ((pid = fork()) == 0) {
130f7167e0eSDag-Erling Smørgrav 		char *argv[10];
131f7167e0eSDag-Erling Smørgrav 
132f7167e0eSDag-Erling Smørgrav 		close(sp[1]);
133f7167e0eSDag-Erling Smørgrav 		/* Redirect stdin and stdout. */
134f7167e0eSDag-Erling Smørgrav 		if (sp[0] != 0) {
135f7167e0eSDag-Erling Smørgrav 			if (dup2(sp[0], 0) < 0)
136f7167e0eSDag-Erling Smørgrav 				perror("dup2 stdin");
137f7167e0eSDag-Erling Smørgrav 		}
138f7167e0eSDag-Erling Smørgrav 		if (sp[0] != 1) {
139f7167e0eSDag-Erling Smørgrav 			if (dup2(sp[0], 1) < 0)
140f7167e0eSDag-Erling Smørgrav 				perror("dup2 stdout");
141f7167e0eSDag-Erling Smørgrav 		}
142f7167e0eSDag-Erling Smørgrav 		if (sp[0] >= 2)
143f7167e0eSDag-Erling Smørgrav 			close(sp[0]);
144f7167e0eSDag-Erling Smørgrav 
145f7167e0eSDag-Erling Smørgrav 		/*
146f7167e0eSDag-Erling Smørgrav 		 * Stderr is left as it is so that error messages get
147f7167e0eSDag-Erling Smørgrav 		 * printed on the user's terminal.
148f7167e0eSDag-Erling Smørgrav 		 */
149f7167e0eSDag-Erling Smørgrav 		argv[0] = shell;
150f7167e0eSDag-Erling Smørgrav 		argv[1] = "-c";
151f7167e0eSDag-Erling Smørgrav 		argv[2] = command_string;
152f7167e0eSDag-Erling Smørgrav 		argv[3] = NULL;
153f7167e0eSDag-Erling Smørgrav 
154f7167e0eSDag-Erling Smørgrav 		/*
155f7167e0eSDag-Erling Smørgrav 		 * Execute the proxy command.
156f7167e0eSDag-Erling Smørgrav 		 * Note that we gave up any extra privileges above.
157f7167e0eSDag-Erling Smørgrav 		 */
158f7167e0eSDag-Erling Smørgrav 		execv(argv[0], argv);
159f7167e0eSDag-Erling Smørgrav 		perror(argv[0]);
160f7167e0eSDag-Erling Smørgrav 		exit(1);
161f7167e0eSDag-Erling Smørgrav 	}
162f7167e0eSDag-Erling Smørgrav 	/* Parent. */
163f7167e0eSDag-Erling Smørgrav 	if (pid < 0)
164f7167e0eSDag-Erling Smørgrav 		fatal("fork failed: %.100s", strerror(errno));
165f7167e0eSDag-Erling Smørgrav 	close(sp[0]);
166f7167e0eSDag-Erling Smørgrav 	free(command_string);
167f7167e0eSDag-Erling Smørgrav 
168f7167e0eSDag-Erling Smørgrav 	if ((sock = mm_receive_fd(sp[1])) == -1)
169f7167e0eSDag-Erling Smørgrav 		fatal("proxy dialer did not pass back a connection");
170acc1a9efSDag-Erling Smørgrav 	close(sp[1]);
171f7167e0eSDag-Erling Smørgrav 
172f7167e0eSDag-Erling Smørgrav 	while (waitpid(pid, NULL, 0) == -1)
173f7167e0eSDag-Erling Smørgrav 		if (errno != EINTR)
174f7167e0eSDag-Erling Smørgrav 			fatal("Couldn't wait for child: %s", strerror(errno));
175f7167e0eSDag-Erling Smørgrav 
176f7167e0eSDag-Erling Smørgrav 	/* Set the connection file descriptors. */
1774f52dfbbSDag-Erling Smørgrav 	if (ssh_packet_set_connection(ssh, sock, sock) == NULL)
1784f52dfbbSDag-Erling Smørgrav 		return -1; /* ssh_packet_set_connection logs error */
179f7167e0eSDag-Erling Smørgrav 
180f7167e0eSDag-Erling Smørgrav 	return 0;
181f7167e0eSDag-Erling Smørgrav }
182f7167e0eSDag-Erling Smørgrav 
183511b41d2SMark Murray /*
184511b41d2SMark Murray  * Connect to the given ssh server using a proxy command.
185511b41d2SMark Murray  */
186af12a3e7SDag-Erling Smørgrav static int
1874f52dfbbSDag-Erling Smørgrav ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port,
1884f52dfbbSDag-Erling Smørgrav     const char *proxy_command)
189511b41d2SMark Murray {
190f7167e0eSDag-Erling Smørgrav 	char *command_string;
191511b41d2SMark Murray 	int pin[2], pout[2];
192e8aafc91SKris Kennaway 	pid_t pid;
193f7167e0eSDag-Erling Smørgrav 	char *shell;
194420bce64SDag-Erling Smørgrav 
1954a421b63SDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
196d4af9e69SDag-Erling Smørgrav 		shell = _PATH_BSHELL;
197511b41d2SMark Murray 
198511b41d2SMark Murray 	/* Create pipes for communicating with the proxy. */
199511b41d2SMark Murray 	if (pipe(pin) < 0 || pipe(pout) < 0)
200511b41d2SMark Murray 		fatal("Could not create pipes to communicate with the proxy: %.100s",
201511b41d2SMark Murray 		    strerror(errno));
202511b41d2SMark Murray 
203f7167e0eSDag-Erling Smørgrav 	command_string = expand_proxy_command(proxy_command, options.user,
204f7167e0eSDag-Erling Smørgrav 	    host, port);
205511b41d2SMark Murray 	debug("Executing proxy command: %.500s", command_string);
206511b41d2SMark Murray 
207511b41d2SMark Murray 	/* Fork and execute the proxy command. */
208511b41d2SMark Murray 	if ((pid = fork()) == 0) {
209511b41d2SMark Murray 		char *argv[10];
210511b41d2SMark Murray 
211511b41d2SMark Murray 		/* Redirect stdin and stdout. */
212511b41d2SMark Murray 		close(pin[1]);
213511b41d2SMark Murray 		if (pin[0] != 0) {
214511b41d2SMark Murray 			if (dup2(pin[0], 0) < 0)
215511b41d2SMark Murray 				perror("dup2 stdin");
216511b41d2SMark Murray 			close(pin[0]);
217511b41d2SMark Murray 		}
218511b41d2SMark Murray 		close(pout[0]);
219511b41d2SMark Murray 		if (dup2(pout[1], 1) < 0)
220511b41d2SMark Murray 			perror("dup2 stdout");
221511b41d2SMark Murray 		/* Cannot be 1 because pin allocated two descriptors. */
222511b41d2SMark Murray 		close(pout[1]);
223511b41d2SMark Murray 
224511b41d2SMark Murray 		/* Stderr is left as it is so that error messages get
225511b41d2SMark Murray 		   printed on the user's terminal. */
226d4af9e69SDag-Erling Smørgrav 		argv[0] = shell;
227511b41d2SMark Murray 		argv[1] = "-c";
228511b41d2SMark Murray 		argv[2] = command_string;
229511b41d2SMark Murray 		argv[3] = NULL;
230511b41d2SMark Murray 
231511b41d2SMark Murray 		/* Execute the proxy command.  Note that we gave up any
232511b41d2SMark Murray 		   extra privileges above. */
2334a421b63SDag-Erling Smørgrav 		signal(SIGPIPE, SIG_DFL);
234ca3176e7SBrian Feldman 		execv(argv[0], argv);
235ca3176e7SBrian Feldman 		perror(argv[0]);
236511b41d2SMark Murray 		exit(1);
237511b41d2SMark Murray 	}
238511b41d2SMark Murray 	/* Parent. */
239511b41d2SMark Murray 	if (pid < 0)
240511b41d2SMark Murray 		fatal("fork failed: %.100s", strerror(errno));
241f388f5efSDag-Erling Smørgrav 	else
242f388f5efSDag-Erling Smørgrav 		proxy_command_pid = pid; /* save pid to clean up later */
243511b41d2SMark Murray 
244511b41d2SMark Murray 	/* Close child side of the descriptors. */
245511b41d2SMark Murray 	close(pin[0]);
246511b41d2SMark Murray 	close(pout[1]);
247511b41d2SMark Murray 
248511b41d2SMark Murray 	/* Free the command name. */
249e4a9863fSDag-Erling Smørgrav 	free(command_string);
250511b41d2SMark Murray 
251511b41d2SMark Murray 	/* Set the connection file descriptors. */
2524f52dfbbSDag-Erling Smørgrav 	if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL)
2534f52dfbbSDag-Erling Smørgrav 		return -1; /* ssh_packet_set_connection logs error */
254511b41d2SMark Murray 
255af12a3e7SDag-Erling Smørgrav 	return 0;
256511b41d2SMark Murray }
257511b41d2SMark Murray 
2584a421b63SDag-Erling Smørgrav void
2594a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(void)
2604a421b63SDag-Erling Smørgrav {
2614a421b63SDag-Erling Smørgrav 	/*
2624a421b63SDag-Erling Smørgrav 	 * Send SIGHUP to proxy command if used. We don't wait() in
2634a421b63SDag-Erling Smørgrav 	 * case it hangs and instead rely on init to reap the child
2644a421b63SDag-Erling Smørgrav 	 */
2654a421b63SDag-Erling Smørgrav 	if (proxy_command_pid > 1)
2664a421b63SDag-Erling Smørgrav 		kill(proxy_command_pid, SIGHUP);
2674a421b63SDag-Erling Smørgrav }
2684a421b63SDag-Erling Smørgrav 
26947dd1d1bSDag-Erling Smørgrav #ifdef HAVE_IFADDRS_H
27047dd1d1bSDag-Erling Smørgrav /*
27147dd1d1bSDag-Erling Smørgrav  * Search a interface address list (returned from getifaddrs(3)) for an
272*190cef3dSDag-Erling Smørgrav  * address that matches the desired address family on the specified interface.
27347dd1d1bSDag-Erling Smørgrav  * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure.
27447dd1d1bSDag-Erling Smørgrav  */
27547dd1d1bSDag-Erling Smørgrav static int
27647dd1d1bSDag-Erling Smørgrav check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs,
27747dd1d1bSDag-Erling Smørgrav     struct sockaddr_storage *resultp, socklen_t *rlenp)
27847dd1d1bSDag-Erling Smørgrav {
27947dd1d1bSDag-Erling Smørgrav 	struct sockaddr_in6 *sa6;
28047dd1d1bSDag-Erling Smørgrav 	struct sockaddr_in *sa;
28147dd1d1bSDag-Erling Smørgrav 	struct in6_addr *v6addr;
28247dd1d1bSDag-Erling Smørgrav 	const struct ifaddrs *ifa;
28347dd1d1bSDag-Erling Smørgrav 	int allow_local;
28447dd1d1bSDag-Erling Smørgrav 
28547dd1d1bSDag-Erling Smørgrav 	/*
28647dd1d1bSDag-Erling Smørgrav 	 * Prefer addresses that are not loopback or linklocal, but use them
28747dd1d1bSDag-Erling Smørgrav 	 * if nothing else matches.
28847dd1d1bSDag-Erling Smørgrav 	 */
28947dd1d1bSDag-Erling Smørgrav 	for (allow_local = 0; allow_local < 2; allow_local++) {
29047dd1d1bSDag-Erling Smørgrav 		for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
29147dd1d1bSDag-Erling Smørgrav 			if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL ||
29247dd1d1bSDag-Erling Smørgrav 			    (ifa->ifa_flags & IFF_UP) == 0 ||
29347dd1d1bSDag-Erling Smørgrav 			    ifa->ifa_addr->sa_family != af ||
29447dd1d1bSDag-Erling Smørgrav 			    strcmp(ifa->ifa_name, options.bind_interface) != 0)
29547dd1d1bSDag-Erling Smørgrav 				continue;
29647dd1d1bSDag-Erling Smørgrav 			switch (ifa->ifa_addr->sa_family) {
29747dd1d1bSDag-Erling Smørgrav 			case AF_INET:
29847dd1d1bSDag-Erling Smørgrav 				sa = (struct sockaddr_in *)ifa->ifa_addr;
29947dd1d1bSDag-Erling Smørgrav 				if (!allow_local && sa->sin_addr.s_addr ==
30047dd1d1bSDag-Erling Smørgrav 				    htonl(INADDR_LOOPBACK))
30147dd1d1bSDag-Erling Smørgrav 					continue;
30247dd1d1bSDag-Erling Smørgrav 				if (*rlenp < sizeof(struct sockaddr_in)) {
30347dd1d1bSDag-Erling Smørgrav 					error("%s: v4 addr doesn't fit",
30447dd1d1bSDag-Erling Smørgrav 					    __func__);
30547dd1d1bSDag-Erling Smørgrav 					return -1;
30647dd1d1bSDag-Erling Smørgrav 				}
30747dd1d1bSDag-Erling Smørgrav 				*rlenp = sizeof(struct sockaddr_in);
30847dd1d1bSDag-Erling Smørgrav 				memcpy(resultp, sa, *rlenp);
30947dd1d1bSDag-Erling Smørgrav 				return 0;
31047dd1d1bSDag-Erling Smørgrav 			case AF_INET6:
31147dd1d1bSDag-Erling Smørgrav 				sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
31247dd1d1bSDag-Erling Smørgrav 				v6addr = &sa6->sin6_addr;
31347dd1d1bSDag-Erling Smørgrav 				if (!allow_local &&
31447dd1d1bSDag-Erling Smørgrav 				    (IN6_IS_ADDR_LINKLOCAL(v6addr) ||
31547dd1d1bSDag-Erling Smørgrav 				    IN6_IS_ADDR_LOOPBACK(v6addr)))
31647dd1d1bSDag-Erling Smørgrav 					continue;
31747dd1d1bSDag-Erling Smørgrav 				if (*rlenp < sizeof(struct sockaddr_in6)) {
31847dd1d1bSDag-Erling Smørgrav 					error("%s: v6 addr doesn't fit",
31947dd1d1bSDag-Erling Smørgrav 					    __func__);
32047dd1d1bSDag-Erling Smørgrav 					return -1;
32147dd1d1bSDag-Erling Smørgrav 				}
32247dd1d1bSDag-Erling Smørgrav 				*rlenp = sizeof(struct sockaddr_in6);
32347dd1d1bSDag-Erling Smørgrav 				memcpy(resultp, sa6, *rlenp);
32447dd1d1bSDag-Erling Smørgrav 				return 0;
32547dd1d1bSDag-Erling Smørgrav 			}
32647dd1d1bSDag-Erling Smørgrav 		}
32747dd1d1bSDag-Erling Smørgrav 	}
32847dd1d1bSDag-Erling Smørgrav 	return -1;
32947dd1d1bSDag-Erling Smørgrav }
33047dd1d1bSDag-Erling Smørgrav #endif
33147dd1d1bSDag-Erling Smørgrav 
332511b41d2SMark Murray /*
333*190cef3dSDag-Erling Smørgrav  * Creates a socket for use as the ssh connection.
334511b41d2SMark Murray  */
335af12a3e7SDag-Erling Smørgrav static int
336*190cef3dSDag-Erling Smørgrav ssh_create_socket(struct addrinfo *ai)
337511b41d2SMark Murray {
338*190cef3dSDag-Erling Smørgrav 	int sock, r;
33947dd1d1bSDag-Erling Smørgrav 	struct sockaddr_storage bindaddr;
34047dd1d1bSDag-Erling Smørgrav 	socklen_t bindaddrlen = 0;
341b83788ffSDag-Erling Smørgrav 	struct addrinfo hints, *res = NULL;
34247dd1d1bSDag-Erling Smørgrav #ifdef HAVE_IFADDRS_H
34347dd1d1bSDag-Erling Smørgrav 	struct ifaddrs *ifaddrs = NULL;
34447dd1d1bSDag-Erling Smørgrav #endif
34547dd1d1bSDag-Erling Smørgrav 	char ntop[NI_MAXHOST];
346511b41d2SMark Murray 
347cf2b5f3bSDag-Erling Smørgrav 	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
348b15c8340SDag-Erling Smørgrav 	if (sock < 0) {
349f7167e0eSDag-Erling Smørgrav 		error("socket: %s", strerror(errno));
350b15c8340SDag-Erling Smørgrav 		return -1;
351b15c8340SDag-Erling Smørgrav 	}
352b15c8340SDag-Erling Smørgrav 	fcntl(sock, F_SETFD, FD_CLOEXEC);
353af12a3e7SDag-Erling Smørgrav 
354af12a3e7SDag-Erling Smørgrav 	/* Bind the socket to an alternative local IP address */
355*190cef3dSDag-Erling Smørgrav 	if (options.bind_address == NULL && options.bind_interface == NULL)
356af12a3e7SDag-Erling Smørgrav 		return sock;
357af12a3e7SDag-Erling Smørgrav 
35847dd1d1bSDag-Erling Smørgrav 	if (options.bind_address != NULL) {
359af12a3e7SDag-Erling Smørgrav 		memset(&hints, 0, sizeof(hints));
360cf2b5f3bSDag-Erling Smørgrav 		hints.ai_family = ai->ai_family;
361cf2b5f3bSDag-Erling Smørgrav 		hints.ai_socktype = ai->ai_socktype;
362cf2b5f3bSDag-Erling Smørgrav 		hints.ai_protocol = ai->ai_protocol;
363af12a3e7SDag-Erling Smørgrav 		hints.ai_flags = AI_PASSIVE;
36447dd1d1bSDag-Erling Smørgrav 		if ((r = getaddrinfo(options.bind_address, NULL,
36547dd1d1bSDag-Erling Smørgrav 		    &hints, &res)) != 0) {
366af12a3e7SDag-Erling Smørgrav 			error("getaddrinfo: %s: %s", options.bind_address,
36747dd1d1bSDag-Erling Smørgrav 			    ssh_gai_strerror(r));
36847dd1d1bSDag-Erling Smørgrav 			goto fail;
369511b41d2SMark Murray 		}
37047dd1d1bSDag-Erling Smørgrav 		if (res == NULL) {
37147dd1d1bSDag-Erling Smørgrav 			error("getaddrinfo: no addrs");
37247dd1d1bSDag-Erling Smørgrav 			goto fail;
37347dd1d1bSDag-Erling Smørgrav 		}
37447dd1d1bSDag-Erling Smørgrav 		if (res->ai_addrlen > sizeof(bindaddr)) {
37547dd1d1bSDag-Erling Smørgrav 			error("%s: addr doesn't fit", __func__);
37647dd1d1bSDag-Erling Smørgrav 			goto fail;
37747dd1d1bSDag-Erling Smørgrav 		}
37847dd1d1bSDag-Erling Smørgrav 		memcpy(&bindaddr, res->ai_addr, res->ai_addrlen);
37947dd1d1bSDag-Erling Smørgrav 		bindaddrlen = res->ai_addrlen;
38047dd1d1bSDag-Erling Smørgrav 	} else if (options.bind_interface != NULL) {
38147dd1d1bSDag-Erling Smørgrav #ifdef HAVE_IFADDRS_H
38247dd1d1bSDag-Erling Smørgrav 		if ((r = getifaddrs(&ifaddrs)) != 0) {
38347dd1d1bSDag-Erling Smørgrav 			error("getifaddrs: %s: %s", options.bind_interface,
38447dd1d1bSDag-Erling Smørgrav 			      strerror(errno));
38547dd1d1bSDag-Erling Smørgrav 			goto fail;
38647dd1d1bSDag-Erling Smørgrav 		}
38747dd1d1bSDag-Erling Smørgrav 		bindaddrlen = sizeof(bindaddr);
38847dd1d1bSDag-Erling Smørgrav 		if (check_ifaddrs(options.bind_interface, ai->ai_family,
38947dd1d1bSDag-Erling Smørgrav 		    ifaddrs, &bindaddr, &bindaddrlen) != 0) {
39047dd1d1bSDag-Erling Smørgrav 			logit("getifaddrs: %s: no suitable addresses",
39147dd1d1bSDag-Erling Smørgrav 			      options.bind_interface);
39247dd1d1bSDag-Erling Smørgrav 			goto fail;
39347dd1d1bSDag-Erling Smørgrav 		}
39447dd1d1bSDag-Erling Smørgrav #else
39547dd1d1bSDag-Erling Smørgrav 		error("BindInterface not supported on this platform.");
39647dd1d1bSDag-Erling Smørgrav #endif
39747dd1d1bSDag-Erling Smørgrav 	}
39847dd1d1bSDag-Erling Smørgrav 	if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen,
39947dd1d1bSDag-Erling Smørgrav 	    ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) {
40047dd1d1bSDag-Erling Smørgrav 		error("%s: getnameinfo failed: %s", __func__,
40147dd1d1bSDag-Erling Smørgrav 		    ssh_gai_strerror(r));
40247dd1d1bSDag-Erling Smørgrav 		goto fail;
403b83788ffSDag-Erling Smørgrav 	}
404*190cef3dSDag-Erling Smørgrav 	if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) {
40547dd1d1bSDag-Erling Smørgrav 		error("bind %s: %s", ntop, strerror(errno));
40647dd1d1bSDag-Erling Smørgrav 		goto fail;
40747dd1d1bSDag-Erling Smørgrav 	}
40847dd1d1bSDag-Erling Smørgrav 	debug("%s: bound to %s", __func__, ntop);
40947dd1d1bSDag-Erling Smørgrav 	/* success */
41047dd1d1bSDag-Erling Smørgrav 	goto out;
411f7167e0eSDag-Erling Smørgrav fail:
412af12a3e7SDag-Erling Smørgrav 	close(sock);
41347dd1d1bSDag-Erling Smørgrav 	sock = -1;
41447dd1d1bSDag-Erling Smørgrav  out:
415b83788ffSDag-Erling Smørgrav 	if (res != NULL)
416af12a3e7SDag-Erling Smørgrav 		freeaddrinfo(res);
41747dd1d1bSDag-Erling Smørgrav #ifdef HAVE_IFADDRS_H
41847dd1d1bSDag-Erling Smørgrav 	if (ifaddrs != NULL)
41947dd1d1bSDag-Erling Smørgrav 		freeifaddrs(ifaddrs);
42047dd1d1bSDag-Erling Smørgrav #endif
421511b41d2SMark Murray 	return sock;
422511b41d2SMark Murray }
423511b41d2SMark Murray 
4244f52dfbbSDag-Erling Smørgrav /*
4254f52dfbbSDag-Erling Smørgrav  * Wait up to *timeoutp milliseconds for fd to be readable. Updates
4264f52dfbbSDag-Erling Smørgrav  * *timeoutp with time remaining.
4274f52dfbbSDag-Erling Smørgrav  * Returns 0 if fd ready or -1 on timeout or error (see errno).
4284f52dfbbSDag-Erling Smørgrav  */
4294f52dfbbSDag-Erling Smørgrav static int
4304f52dfbbSDag-Erling Smørgrav waitrfd(int fd, int *timeoutp)
4314f52dfbbSDag-Erling Smørgrav {
4324f52dfbbSDag-Erling Smørgrav 	struct pollfd pfd;
4334f52dfbbSDag-Erling Smørgrav 	struct timeval t_start;
4344f52dfbbSDag-Erling Smørgrav 	int oerrno, r;
4354f52dfbbSDag-Erling Smørgrav 
43647dd1d1bSDag-Erling Smørgrav 	monotime_tv(&t_start);
4374f52dfbbSDag-Erling Smørgrav 	pfd.fd = fd;
4384f52dfbbSDag-Erling Smørgrav 	pfd.events = POLLIN;
4394f52dfbbSDag-Erling Smørgrav 	for (; *timeoutp >= 0;) {
4404f52dfbbSDag-Erling Smørgrav 		r = poll(&pfd, 1, *timeoutp);
4414f52dfbbSDag-Erling Smørgrav 		oerrno = errno;
4424f52dfbbSDag-Erling Smørgrav 		ms_subtract_diff(&t_start, timeoutp);
4434f52dfbbSDag-Erling Smørgrav 		errno = oerrno;
4444f52dfbbSDag-Erling Smørgrav 		if (r > 0)
4454f52dfbbSDag-Erling Smørgrav 			return 0;
4464f52dfbbSDag-Erling Smørgrav 		else if (r == -1 && errno != EAGAIN)
4474f52dfbbSDag-Erling Smørgrav 			return -1;
4484f52dfbbSDag-Erling Smørgrav 		else if (r == 0)
4494f52dfbbSDag-Erling Smørgrav 			break;
4504f52dfbbSDag-Erling Smørgrav 	}
4514f52dfbbSDag-Erling Smørgrav 	/* timeout */
4524f52dfbbSDag-Erling Smørgrav 	errno = ETIMEDOUT;
4534f52dfbbSDag-Erling Smørgrav 	return -1;
4544f52dfbbSDag-Erling Smørgrav }
4554f52dfbbSDag-Erling Smørgrav 
456cf2b5f3bSDag-Erling Smørgrav static int
457cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr,
458d4af9e69SDag-Erling Smørgrav     socklen_t addrlen, int *timeoutp)
459cf2b5f3bSDag-Erling Smørgrav {
4604f52dfbbSDag-Erling Smørgrav 	int optval = 0;
4614f52dfbbSDag-Erling Smørgrav 	socklen_t optlen = sizeof(optval);
462cf2b5f3bSDag-Erling Smørgrav 
4634f52dfbbSDag-Erling Smørgrav 	/* No timeout: just do a blocking connect() */
4644f52dfbbSDag-Erling Smørgrav 	if (*timeoutp <= 0)
4654f52dfbbSDag-Erling Smørgrav 		return connect(sockfd, serv_addr, addrlen);
466cf2b5f3bSDag-Erling Smørgrav 
4671ec0d754SDag-Erling Smørgrav 	set_nonblock(sockfd);
4684f52dfbbSDag-Erling Smørgrav 	if (connect(sockfd, serv_addr, addrlen) == 0) {
4694f52dfbbSDag-Erling Smørgrav 		/* Succeeded already? */
4701ec0d754SDag-Erling Smørgrav 		unset_nonblock(sockfd);
4714f52dfbbSDag-Erling Smørgrav 		return 0;
4724f52dfbbSDag-Erling Smørgrav 	} else if (errno != EINPROGRESS)
4734f52dfbbSDag-Erling Smørgrav 		return -1;
474cf2b5f3bSDag-Erling Smørgrav 
4754f52dfbbSDag-Erling Smørgrav 	if (waitrfd(sockfd, timeoutp) == -1)
4764f52dfbbSDag-Erling Smørgrav 		return -1;
477cf2b5f3bSDag-Erling Smørgrav 
478cf2b5f3bSDag-Erling Smørgrav 	/* Completed or failed */
4794f52dfbbSDag-Erling Smørgrav 	if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) {
480cf2b5f3bSDag-Erling Smørgrav 		debug("getsockopt: %s", strerror(errno));
4814f52dfbbSDag-Erling Smørgrav 		return -1;
482cf2b5f3bSDag-Erling Smørgrav 	}
483cf2b5f3bSDag-Erling Smørgrav 	if (optval != 0) {
484cf2b5f3bSDag-Erling Smørgrav 		errno = optval;
4854f52dfbbSDag-Erling Smørgrav 		return -1;
486cf2b5f3bSDag-Erling Smørgrav 	}
4871ec0d754SDag-Erling Smørgrav 	unset_nonblock(sockfd);
4884f52dfbbSDag-Erling Smørgrav 	return 0;
489cf2b5f3bSDag-Erling Smørgrav }
490cf2b5f3bSDag-Erling Smørgrav 
491511b41d2SMark Murray /*
492511b41d2SMark Murray  * Opens a TCP/IP connection to the remote server on the given host.
493511b41d2SMark Murray  * The address of the remote host will be returned in hostaddr.
494*190cef3dSDag-Erling Smørgrav  * If port is 0, the default port will be used.
495511b41d2SMark Murray  * Connection_attempts specifies the maximum number of tries (one per
496511b41d2SMark Murray  * second).  If proxy_command is non-NULL, it specifies the command (with %h
497511b41d2SMark Murray  * and %p substituted for host and port, respectively) to use to contact
498511b41d2SMark Murray  * the daemon.
499511b41d2SMark Murray  */
500f7167e0eSDag-Erling Smørgrav static int
5014f52dfbbSDag-Erling Smørgrav ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop,
502f7167e0eSDag-Erling Smørgrav     struct sockaddr_storage *hostaddr, u_short port, int family,
503*190cef3dSDag-Erling Smørgrav     int connection_attempts, int *timeout_ms, int want_keepalive)
504511b41d2SMark Murray {
505ca3176e7SBrian Feldman 	int on = 1;
50647dd1d1bSDag-Erling Smørgrav 	int oerrno, sock = -1, attempt;
507ca3176e7SBrian Feldman 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
508f7167e0eSDag-Erling Smørgrav 	struct addrinfo *ai;
509511b41d2SMark Murray 
510*190cef3dSDag-Erling Smørgrav 	debug2("%s", __func__);
511acc1a9efSDag-Erling Smørgrav 	memset(ntop, 0, sizeof(ntop));
512acc1a9efSDag-Erling Smørgrav 	memset(strport, 0, sizeof(strport));
513511b41d2SMark Murray 
514333ee039SDag-Erling Smørgrav 	for (attempt = 0; attempt < connection_attempts; attempt++) {
51562efe23aSDag-Erling Smørgrav 		if (attempt > 0) {
51662efe23aSDag-Erling Smørgrav 			/* Sleep a moment before retrying. */
51762efe23aSDag-Erling Smørgrav 			sleep(1);
518511b41d2SMark Murray 			debug("Trying again...");
51962efe23aSDag-Erling Smørgrav 		}
520333ee039SDag-Erling Smørgrav 		/*
521333ee039SDag-Erling Smørgrav 		 * Loop through addresses for this host, and try each one in
522333ee039SDag-Erling Smørgrav 		 * sequence until the connection succeeds.
523333ee039SDag-Erling Smørgrav 		 */
524511b41d2SMark Murray 		for (ai = aitop; ai; ai = ai->ai_next) {
525f7167e0eSDag-Erling Smørgrav 			if (ai->ai_family != AF_INET &&
52647dd1d1bSDag-Erling Smørgrav 			    ai->ai_family != AF_INET6) {
52747dd1d1bSDag-Erling Smørgrav 				errno = EAFNOSUPPORT;
528511b41d2SMark Murray 				continue;
52947dd1d1bSDag-Erling Smørgrav 			}
530511b41d2SMark Murray 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
531511b41d2SMark Murray 			    ntop, sizeof(ntop), strport, sizeof(strport),
532511b41d2SMark Murray 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
53347dd1d1bSDag-Erling Smørgrav 				oerrno = errno;
534acc1a9efSDag-Erling Smørgrav 				error("%s: getnameinfo failed", __func__);
53547dd1d1bSDag-Erling Smørgrav 				errno = oerrno;
536511b41d2SMark Murray 				continue;
537511b41d2SMark Murray 			}
538511b41d2SMark Murray 			debug("Connecting to %.200s [%.100s] port %s.",
5391f5ce8f4SBrian Feldman 				host, ntop, strport);
540511b41d2SMark Murray 
541511b41d2SMark Murray 			/* Create a socket for connecting. */
542*190cef3dSDag-Erling Smørgrav 			sock = ssh_create_socket(ai);
54347dd1d1bSDag-Erling Smørgrav 			if (sock < 0) {
544af12a3e7SDag-Erling Smørgrav 				/* Any error is already output */
54547dd1d1bSDag-Erling Smørgrav 				errno = 0;
546511b41d2SMark Murray 				continue;
54747dd1d1bSDag-Erling Smørgrav 			}
548511b41d2SMark Murray 
549cf2b5f3bSDag-Erling Smørgrav 			if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
550d4af9e69SDag-Erling Smørgrav 			    timeout_ms) >= 0) {
551511b41d2SMark Murray 				/* Successful connection. */
552c322fe35SKris Kennaway 				memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
553511b41d2SMark Murray 				break;
554511b41d2SMark Murray 			} else {
55547dd1d1bSDag-Erling Smørgrav 				oerrno = errno;
556f388f5efSDag-Erling Smørgrav 				debug("connect to address %s port %s: %s",
557f388f5efSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
558511b41d2SMark Murray 				close(sock);
559333ee039SDag-Erling Smørgrav 				sock = -1;
56047dd1d1bSDag-Erling Smørgrav 				errno = oerrno;
561511b41d2SMark Murray 			}
562511b41d2SMark Murray 		}
563333ee039SDag-Erling Smørgrav 		if (sock != -1)
564511b41d2SMark Murray 			break;	/* Successful connection. */
565511b41d2SMark Murray 	}
566511b41d2SMark Murray 
567511b41d2SMark Murray 	/* Return failure if we didn't get a successful connection. */
568333ee039SDag-Erling Smørgrav 	if (sock == -1) {
569aa49c926SDag-Erling Smørgrav 		error("ssh: connect to host %s port %s: %s",
57047dd1d1bSDag-Erling Smørgrav 		    host, strport, errno == 0 ? "failure" : strerror(errno));
57147dd1d1bSDag-Erling Smørgrav 		return -1;
572f388f5efSDag-Erling Smørgrav 	}
573511b41d2SMark Murray 
574511b41d2SMark Murray 	debug("Connection established.");
575511b41d2SMark Murray 
5761ec0d754SDag-Erling Smørgrav 	/* Set SO_KEEPALIVE if requested. */
577d4af9e69SDag-Erling Smørgrav 	if (want_keepalive &&
578ca3176e7SBrian Feldman 	    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
579ca3176e7SBrian Feldman 	    sizeof(on)) < 0)
580ca3176e7SBrian Feldman 		error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
581ca3176e7SBrian Feldman 
582511b41d2SMark Murray 	/* Set the connection. */
5834f52dfbbSDag-Erling Smørgrav 	if (ssh_packet_set_connection(ssh, sock, sock) == NULL)
5844f52dfbbSDag-Erling Smørgrav 		return -1; /* ssh_packet_set_connection logs error */
585511b41d2SMark Murray 
586af12a3e7SDag-Erling Smørgrav         return 0;
587511b41d2SMark Murray }
588511b41d2SMark Murray 
589f7167e0eSDag-Erling Smørgrav int
5904f52dfbbSDag-Erling Smørgrav ssh_connect(struct ssh *ssh, const char *host, struct addrinfo *addrs,
591f7167e0eSDag-Erling Smørgrav     struct sockaddr_storage *hostaddr, u_short port, int family,
592*190cef3dSDag-Erling Smørgrav     int connection_attempts, int *timeout_ms, int want_keepalive)
593f7167e0eSDag-Erling Smørgrav {
594f7167e0eSDag-Erling Smørgrav 	if (options.proxy_command == NULL) {
5954f52dfbbSDag-Erling Smørgrav 		return ssh_connect_direct(ssh, host, addrs, hostaddr, port,
596*190cef3dSDag-Erling Smørgrav 		    family, connection_attempts, timeout_ms, want_keepalive);
597f7167e0eSDag-Erling Smørgrav 	} else if (strcmp(options.proxy_command, "-") == 0) {
5984f52dfbbSDag-Erling Smørgrav 		if ((ssh_packet_set_connection(ssh,
5994f52dfbbSDag-Erling Smørgrav 		    STDIN_FILENO, STDOUT_FILENO)) == NULL)
6004f52dfbbSDag-Erling Smørgrav 			return -1; /* ssh_packet_set_connection logs error */
6014f52dfbbSDag-Erling Smørgrav 		return 0;
602f7167e0eSDag-Erling Smørgrav 	} else if (options.proxy_use_fdpass) {
6034f52dfbbSDag-Erling Smørgrav 		return ssh_proxy_fdpass_connect(ssh, host, port,
604f7167e0eSDag-Erling Smørgrav 		    options.proxy_command);
605f7167e0eSDag-Erling Smørgrav 	}
6064f52dfbbSDag-Erling Smørgrav 	return ssh_proxy_connect(ssh, host, port, options.proxy_command);
607f7167e0eSDag-Erling Smørgrav }
608f7167e0eSDag-Erling Smørgrav 
6096888a9beSDag-Erling Smørgrav static void
6106888a9beSDag-Erling Smørgrav send_client_banner(int connection_out, int minor1)
6116888a9beSDag-Erling Smørgrav {
6126888a9beSDag-Erling Smørgrav 	/* Send our own protocol version identification. */
6134f52dfbbSDag-Erling Smørgrav 	xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s\n",
6144f52dfbbSDag-Erling Smørgrav 	    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
6156888a9beSDag-Erling Smørgrav 	    *options.version_addendum == '\0' ? "" : " ",
6164f52dfbbSDag-Erling Smørgrav 	    options.version_addendum);
617acc1a9efSDag-Erling Smørgrav 	if (atomicio(vwrite, connection_out, client_version_string,
6186888a9beSDag-Erling Smørgrav 	    strlen(client_version_string)) != strlen(client_version_string))
6196888a9beSDag-Erling Smørgrav 		fatal("write: %.100s", strerror(errno));
6206888a9beSDag-Erling Smørgrav 	chop(client_version_string);
6216888a9beSDag-Erling Smørgrav 	debug("Local version string %.100s", client_version_string);
6226888a9beSDag-Erling Smørgrav }
6236888a9beSDag-Erling Smørgrav 
624511b41d2SMark Murray /*
625e8aafc91SKris Kennaway  * Waits for the server identification string, and sends our own
626e8aafc91SKris Kennaway  * identification string.
627511b41d2SMark Murray  */
6287aee6ffeSDag-Erling Smørgrav void
629d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(int timeout_ms)
630511b41d2SMark Murray {
631e8aafc91SKris Kennaway 	char buf[256], remote_version[256];	/* must be same size! */
632d4ecd108SDag-Erling Smørgrav 	int remote_major, remote_minor, mismatch;
633e8aafc91SKris Kennaway 	int connection_in = packet_get_connection_in();
634e8aafc91SKris Kennaway 	int connection_out = packet_get_connection_out();
635333ee039SDag-Erling Smørgrav 	u_int i, n;
636d4af9e69SDag-Erling Smørgrav 	size_t len;
6374f52dfbbSDag-Erling Smørgrav 	int rc;
638d4af9e69SDag-Erling Smørgrav 
6396888a9beSDag-Erling Smørgrav 	send_client_banner(connection_out, 0);
6406888a9beSDag-Erling Smørgrav 
641d4ecd108SDag-Erling Smørgrav 	/* Read other side's version identification. */
642333ee039SDag-Erling Smørgrav 	for (n = 0;;) {
643e8aafc91SKris Kennaway 		for (i = 0; i < sizeof(buf) - 1; i++) {
644d4af9e69SDag-Erling Smørgrav 			if (timeout_ms > 0) {
6454f52dfbbSDag-Erling Smørgrav 				rc = waitrfd(connection_in, &timeout_ms);
6464f52dfbbSDag-Erling Smørgrav 				if (rc == -1 && errno == ETIMEDOUT) {
647d4af9e69SDag-Erling Smørgrav 					fatal("Connection timed out during "
648d4af9e69SDag-Erling Smørgrav 					    "banner exchange");
6494f52dfbbSDag-Erling Smørgrav 				} else if (rc == -1) {
6504f52dfbbSDag-Erling Smørgrav 					fatal("%s: %s",
6514f52dfbbSDag-Erling Smørgrav 					    __func__, strerror(errno));
652d4af9e69SDag-Erling Smørgrav 				}
653d4af9e69SDag-Erling Smørgrav 			}
654d4af9e69SDag-Erling Smørgrav 
655acc1a9efSDag-Erling Smørgrav 			len = atomicio(read, connection_in, &buf[i], 1);
656d4ecd108SDag-Erling Smørgrav 			if (len != 1 && errno == EPIPE)
657d4af9e69SDag-Erling Smørgrav 				fatal("ssh_exchange_identification: "
658d4af9e69SDag-Erling Smørgrav 				    "Connection closed by remote host");
659d4ecd108SDag-Erling Smørgrav 			else if (len != 1)
660d4af9e69SDag-Erling Smørgrav 				fatal("ssh_exchange_identification: "
661d4af9e69SDag-Erling Smørgrav 				    "read: %.100s", strerror(errno));
662e8aafc91SKris Kennaway 			if (buf[i] == '\r') {
663e8aafc91SKris Kennaway 				buf[i] = '\n';
664e8aafc91SKris Kennaway 				buf[i + 1] = 0;
665e8aafc91SKris Kennaway 				continue;		/**XXX wait for \n */
666511b41d2SMark Murray 			}
667e8aafc91SKris Kennaway 			if (buf[i] == '\n') {
668e8aafc91SKris Kennaway 				buf[i + 1] = 0;
669511b41d2SMark Murray 				break;
670e8aafc91SKris Kennaway 			}
671333ee039SDag-Erling Smørgrav 			if (++n > 65536)
672d4af9e69SDag-Erling Smørgrav 				fatal("ssh_exchange_identification: "
673d4af9e69SDag-Erling Smørgrav 				    "No banner received");
674e8aafc91SKris Kennaway 		}
675e8aafc91SKris Kennaway 		buf[sizeof(buf) - 1] = 0;
676c2d3a559SKris Kennaway 		if (strncmp(buf, "SSH-", 4) == 0)
677c2d3a559SKris Kennaway 			break;
678c2d3a559SKris Kennaway 		debug("ssh_exchange_identification: %s", buf);
679c2d3a559SKris Kennaway 	}
680e8aafc91SKris Kennaway 	server_version_string = xstrdup(buf);
681511b41d2SMark Murray 
682511b41d2SMark Murray 	/*
683e8aafc91SKris Kennaway 	 * Check that the versions match.  In future this might accept
684e8aafc91SKris Kennaway 	 * several versions and set appropriate flags to handle them.
685511b41d2SMark Murray 	 */
686e8aafc91SKris Kennaway 	if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
687e8aafc91SKris Kennaway 	    &remote_major, &remote_minor, remote_version) != 3)
688e8aafc91SKris Kennaway 		fatal("Bad remote protocol version identification: '%.100s'", buf);
689e8aafc91SKris Kennaway 	debug("Remote protocol version %d.%d, remote software version %.100s",
690e8aafc91SKris Kennaway 	    remote_major, remote_minor, remote_version);
691511b41d2SMark Murray 
692bc5531deSDag-Erling Smørgrav 	active_state->compat = compat_datafellows(remote_version);
693e8aafc91SKris Kennaway 	mismatch = 0;
694e8aafc91SKris Kennaway 
695e8aafc91SKris Kennaway 	switch (remote_major) {
6964f52dfbbSDag-Erling Smørgrav 	case 2:
697511b41d2SMark Murray 		break;
6984f52dfbbSDag-Erling Smørgrav 	case 1:
6994f52dfbbSDag-Erling Smørgrav 		if (remote_minor != 99)
700e8aafc91SKris Kennaway 			mismatch = 1;
701e8aafc91SKris Kennaway 		break;
702511b41d2SMark Murray 	default:
703e8aafc91SKris Kennaway 		mismatch = 1;
704e8aafc91SKris Kennaway 		break;
705511b41d2SMark Murray 	}
706e8aafc91SKris Kennaway 	if (mismatch)
707e8aafc91SKris Kennaway 		fatal("Protocol major versions differ: %d vs. %d",
7084f52dfbbSDag-Erling Smørgrav 		    PROTOCOL_MAJOR_2, remote_major);
709f7167e0eSDag-Erling Smørgrav 	if ((datafellows & SSH_BUG_RSASIGMD5) != 0)
710f7167e0eSDag-Erling Smørgrav 		logit("Server version \"%.100s\" uses unsafe RSA signature "
711f7167e0eSDag-Erling Smørgrav 		    "scheme; disabling use of RSA keys", remote_version);
712e8aafc91SKris Kennaway 	chop(server_version_string);
713511b41d2SMark Murray }
714511b41d2SMark Murray 
715ca3176e7SBrian Feldman /* defaults to 'no' */
716af12a3e7SDag-Erling Smørgrav static int
717af12a3e7SDag-Erling Smørgrav confirm(const char *prompt)
718511b41d2SMark Murray {
719af12a3e7SDag-Erling Smørgrav 	const char *msg, *again = "Please type 'yes' or 'no': ";
720af12a3e7SDag-Erling Smørgrav 	char *p;
721af12a3e7SDag-Erling Smørgrav 	int ret = -1;
722511b41d2SMark Murray 
723ca3176e7SBrian Feldman 	if (options.batch_mode)
724ca3176e7SBrian Feldman 		return 0;
725af12a3e7SDag-Erling Smørgrav 	for (msg = prompt;;msg = again) {
726af12a3e7SDag-Erling Smørgrav 		p = read_passphrase(msg, RP_ECHO);
72747dd1d1bSDag-Erling Smørgrav 		if (p == NULL)
72847dd1d1bSDag-Erling Smørgrav 			return 0;
72947dd1d1bSDag-Erling Smørgrav 		p[strcspn(p, "\n")] = '\0';
73047dd1d1bSDag-Erling Smørgrav 		if (p[0] == '\0' || strcasecmp(p, "no") == 0)
731af12a3e7SDag-Erling Smørgrav 			ret = 0;
73247dd1d1bSDag-Erling Smørgrav 		else if (strcasecmp(p, "yes") == 0)
733af12a3e7SDag-Erling Smørgrav 			ret = 1;
734e4a9863fSDag-Erling Smørgrav 		free(p);
735af12a3e7SDag-Erling Smørgrav 		if (ret != -1)
736af12a3e7SDag-Erling Smørgrav 			return ret;
737511b41d2SMark Murray 	}
738511b41d2SMark Murray }
739511b41d2SMark Murray 
740b15c8340SDag-Erling Smørgrav static int
7414f52dfbbSDag-Erling Smørgrav check_host_cert(const char *host, const struct sshkey *host_key)
742b15c8340SDag-Erling Smørgrav {
743b15c8340SDag-Erling Smørgrav 	const char *reason;
744b15c8340SDag-Erling Smørgrav 
745*190cef3dSDag-Erling Smørgrav 	if (sshkey_cert_check_authority(host_key, 1, 0, host, &reason) != 0) {
746b15c8340SDag-Erling Smørgrav 		error("%s", reason);
747b15c8340SDag-Erling Smørgrav 		return 0;
748b15c8340SDag-Erling Smørgrav 	}
749*190cef3dSDag-Erling Smørgrav 	if (sshbuf_len(host_key->cert->critical) != 0) {
750e2f6069cSDag-Erling Smørgrav 		error("Certificate for %s contains unsupported "
751e2f6069cSDag-Erling Smørgrav 		    "critical options(s)", host);
752b15c8340SDag-Erling Smørgrav 		return 0;
753b15c8340SDag-Erling Smørgrav 	}
754b15c8340SDag-Erling Smørgrav 	return 1;
755b15c8340SDag-Erling Smørgrav }
756b15c8340SDag-Erling Smørgrav 
7574a421b63SDag-Erling Smørgrav static int
7584a421b63SDag-Erling Smørgrav sockaddr_is_local(struct sockaddr *hostaddr)
7594a421b63SDag-Erling Smørgrav {
7604a421b63SDag-Erling Smørgrav 	switch (hostaddr->sa_family) {
7614a421b63SDag-Erling Smørgrav 	case AF_INET:
7624a421b63SDag-Erling Smørgrav 		return (ntohl(((struct sockaddr_in *)hostaddr)->
7634a421b63SDag-Erling Smørgrav 		    sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
7644a421b63SDag-Erling Smørgrav 	case AF_INET6:
7654a421b63SDag-Erling Smørgrav 		return IN6_IS_ADDR_LOOPBACK(
7664a421b63SDag-Erling Smørgrav 		    &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
7674a421b63SDag-Erling Smørgrav 	default:
7684a421b63SDag-Erling Smørgrav 		return 0;
7694a421b63SDag-Erling Smørgrav 	}
7704a421b63SDag-Erling Smørgrav }
7714a421b63SDag-Erling Smørgrav 
7724a421b63SDag-Erling Smørgrav /*
7734a421b63SDag-Erling Smørgrav  * Prepare the hostname and ip address strings that are used to lookup
7744a421b63SDag-Erling Smørgrav  * host keys in known_hosts files. These may have a port number appended.
7754a421b63SDag-Erling Smørgrav  */
7764a421b63SDag-Erling Smørgrav void
7774a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr,
7784a421b63SDag-Erling Smørgrav     u_short port, char **hostfile_hostname, char **hostfile_ipaddr)
7794a421b63SDag-Erling Smørgrav {
7804a421b63SDag-Erling Smørgrav 	char ntop[NI_MAXHOST];
7814a421b63SDag-Erling Smørgrav 	socklen_t addrlen;
7824a421b63SDag-Erling Smørgrav 
7834a421b63SDag-Erling Smørgrav 	switch (hostaddr == NULL ? -1 : hostaddr->sa_family) {
7844a421b63SDag-Erling Smørgrav 	case -1:
7854a421b63SDag-Erling Smørgrav 		addrlen = 0;
7864a421b63SDag-Erling Smørgrav 		break;
7874a421b63SDag-Erling Smørgrav 	case AF_INET:
7884a421b63SDag-Erling Smørgrav 		addrlen = sizeof(struct sockaddr_in);
7894a421b63SDag-Erling Smørgrav 		break;
7904a421b63SDag-Erling Smørgrav 	case AF_INET6:
7914a421b63SDag-Erling Smørgrav 		addrlen = sizeof(struct sockaddr_in6);
7924a421b63SDag-Erling Smørgrav 		break;
7934a421b63SDag-Erling Smørgrav 	default:
7944a421b63SDag-Erling Smørgrav 		addrlen = sizeof(struct sockaddr);
7954a421b63SDag-Erling Smørgrav 		break;
7964a421b63SDag-Erling Smørgrav 	}
7974a421b63SDag-Erling Smørgrav 
7984a421b63SDag-Erling Smørgrav 	/*
7994a421b63SDag-Erling Smørgrav 	 * We don't have the remote ip-address for connections
8004a421b63SDag-Erling Smørgrav 	 * using a proxy command
8014a421b63SDag-Erling Smørgrav 	 */
8024a421b63SDag-Erling Smørgrav 	if (hostfile_ipaddr != NULL) {
8034a421b63SDag-Erling Smørgrav 		if (options.proxy_command == NULL) {
8044a421b63SDag-Erling Smørgrav 			if (getnameinfo(hostaddr, addrlen,
8054a421b63SDag-Erling Smørgrav 			    ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0)
806bc5531deSDag-Erling Smørgrav 			fatal("%s: getnameinfo failed", __func__);
8074a421b63SDag-Erling Smørgrav 			*hostfile_ipaddr = put_host_port(ntop, port);
8084a421b63SDag-Erling Smørgrav 		} else {
8094a421b63SDag-Erling Smørgrav 			*hostfile_ipaddr = xstrdup("<no hostip for proxy "
8104a421b63SDag-Erling Smørgrav 			    "command>");
8114a421b63SDag-Erling Smørgrav 		}
8124a421b63SDag-Erling Smørgrav 	}
8134a421b63SDag-Erling Smørgrav 
8144a421b63SDag-Erling Smørgrav 	/*
8154a421b63SDag-Erling Smørgrav 	 * Allow the user to record the key under a different name or
8164a421b63SDag-Erling Smørgrav 	 * differentiate a non-standard port.  This is useful for ssh
8174a421b63SDag-Erling Smørgrav 	 * tunneling over forwarded connections or if you run multiple
8184a421b63SDag-Erling Smørgrav 	 * sshd's on different ports on the same machine.
8194a421b63SDag-Erling Smørgrav 	 */
8204a421b63SDag-Erling Smørgrav 	if (hostfile_hostname != NULL) {
8214a421b63SDag-Erling Smørgrav 		if (options.host_key_alias != NULL) {
8224a421b63SDag-Erling Smørgrav 			*hostfile_hostname = xstrdup(options.host_key_alias);
8234a421b63SDag-Erling Smørgrav 			debug("using hostkeyalias: %s", *hostfile_hostname);
8244a421b63SDag-Erling Smørgrav 		} else {
8254a421b63SDag-Erling Smørgrav 			*hostfile_hostname = put_host_port(hostname, port);
8264a421b63SDag-Erling Smørgrav 		}
8274a421b63SDag-Erling Smørgrav 	}
8284a421b63SDag-Erling Smørgrav }
8294a421b63SDag-Erling Smørgrav 
830e8aafc91SKris Kennaway /*
831af12a3e7SDag-Erling Smørgrav  * check whether the supplied host key is valid, return -1 if the key
832e146993eSDag-Erling Smørgrav  * is not valid. user_hostfile[0] will not be updated if 'readonly' is true.
833e8aafc91SKris Kennaway  */
834333ee039SDag-Erling Smørgrav #define RDRW	0
835333ee039SDag-Erling Smørgrav #define RDONLY	1
836333ee039SDag-Erling Smørgrav #define ROQUIET	2
837af12a3e7SDag-Erling Smørgrav static int
838333ee039SDag-Erling Smørgrav check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
8394f52dfbbSDag-Erling Smørgrav     struct sshkey *host_key, int readonly,
840e146993eSDag-Erling Smørgrav     char **user_hostfiles, u_int num_user_hostfiles,
841e146993eSDag-Erling Smørgrav     char **system_hostfiles, u_int num_system_hostfiles)
842511b41d2SMark Murray {
843e8aafc91SKris Kennaway 	HostStatus host_status;
844e8aafc91SKris Kennaway 	HostStatus ip_status;
8454f52dfbbSDag-Erling Smørgrav 	struct sshkey *raw_key = NULL;
846e146993eSDag-Erling Smørgrav 	char *ip = NULL, *host = NULL;
847e146993eSDag-Erling Smørgrav 	char hostline[1000], *hostp, *fp, *ra;
848af12a3e7SDag-Erling Smørgrav 	char msg[1024];
849e146993eSDag-Erling Smørgrav 	const char *type;
8504a421b63SDag-Erling Smørgrav 	const struct hostkey_entry *host_found, *ip_found;
851e146993eSDag-Erling Smørgrav 	int len, cancelled_forwarding = 0;
852e146993eSDag-Erling Smørgrav 	int local = sockaddr_is_local(hostaddr);
8534f52dfbbSDag-Erling Smørgrav 	int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0;
854bc5531deSDag-Erling Smørgrav 	int hostkey_trusted = 0; /* Known or explicitly accepted by user */
855e146993eSDag-Erling Smørgrav 	struct hostkeys *host_hostkeys, *ip_hostkeys;
856e146993eSDag-Erling Smørgrav 	u_int i;
857511b41d2SMark Murray 
858e8aafc91SKris Kennaway 	/*
859e8aafc91SKris Kennaway 	 * Force accepting of the host key for loopback/localhost. The
860e8aafc91SKris Kennaway 	 * problem is that if the home directory is NFS-mounted to multiple
861e8aafc91SKris Kennaway 	 * machines, localhost will refer to a different machine in each of
862e8aafc91SKris Kennaway 	 * them, and the user will get bogus HOST_CHANGED warnings.  This
863e8aafc91SKris Kennaway 	 * essentially disables host authentication for localhost; however,
864e8aafc91SKris Kennaway 	 * this is probably not a real problem.
865e8aafc91SKris Kennaway 	 */
866af12a3e7SDag-Erling Smørgrav 	if (options.no_host_authentication_for_localhost == 1 && local &&
867af12a3e7SDag-Erling Smørgrav 	    options.host_key_alias == NULL) {
868ca3176e7SBrian Feldman 		debug("Forcing accepting of host key for "
869ca3176e7SBrian Feldman 		    "loopback/localhost.");
870af12a3e7SDag-Erling Smørgrav 		return 0;
871511b41d2SMark Murray 	}
872511b41d2SMark Murray 
873e8aafc91SKris Kennaway 	/*
8744a421b63SDag-Erling Smørgrav 	 * Prepare the hostname and address strings used for hostkey lookup.
8754a421b63SDag-Erling Smørgrav 	 * In some cases, these will have a port number appended.
876e8aafc91SKris Kennaway 	 */
8774a421b63SDag-Erling Smørgrav 	get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip);
878d4af9e69SDag-Erling Smørgrav 
879ca3176e7SBrian Feldman 	/*
880ca3176e7SBrian Feldman 	 * Turn off check_host_ip if the connection is to localhost, via proxy
881ca3176e7SBrian Feldman 	 * command or if we don't have a hostname to compare with
882ca3176e7SBrian Feldman 	 */
883333ee039SDag-Erling Smørgrav 	if (options.check_host_ip && (local ||
884333ee039SDag-Erling Smørgrav 	    strcmp(hostname, ip) == 0 || options.proxy_command != NULL))
885ca3176e7SBrian Feldman 		options.check_host_ip = 0;
886ca3176e7SBrian Feldman 
8874a421b63SDag-Erling Smørgrav 	host_hostkeys = init_hostkeys();
888e146993eSDag-Erling Smørgrav 	for (i = 0; i < num_user_hostfiles; i++)
889e146993eSDag-Erling Smørgrav 		load_hostkeys(host_hostkeys, host, user_hostfiles[i]);
890e146993eSDag-Erling Smørgrav 	for (i = 0; i < num_system_hostfiles; i++)
891e146993eSDag-Erling Smørgrav 		load_hostkeys(host_hostkeys, host, system_hostfiles[i]);
8924a421b63SDag-Erling Smørgrav 
8934a421b63SDag-Erling Smørgrav 	ip_hostkeys = NULL;
8944a421b63SDag-Erling Smørgrav 	if (!want_cert && options.check_host_ip) {
8954a421b63SDag-Erling Smørgrav 		ip_hostkeys = init_hostkeys();
896e146993eSDag-Erling Smørgrav 		for (i = 0; i < num_user_hostfiles; i++)
897e146993eSDag-Erling Smørgrav 			load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]);
898e146993eSDag-Erling Smørgrav 		for (i = 0; i < num_system_hostfiles; i++)
899e146993eSDag-Erling Smørgrav 			load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]);
900e8aafc91SKris Kennaway 	}
901e8aafc91SKris Kennaway 
902b15c8340SDag-Erling Smørgrav  retry:
9034a421b63SDag-Erling Smørgrav 	/* Reload these as they may have changed on cert->key downgrade */
9044f52dfbbSDag-Erling Smørgrav 	want_cert = sshkey_is_cert(host_key);
9054f52dfbbSDag-Erling Smørgrav 	type = sshkey_type(host_key);
906b15c8340SDag-Erling Smørgrav 
907e8aafc91SKris Kennaway 	/*
908b74df5b2SDag-Erling Smørgrav 	 * Check if the host key is present in the user's list of known
909e8aafc91SKris Kennaway 	 * hosts or in the systemwide list.
910e8aafc91SKris Kennaway 	 */
9114a421b63SDag-Erling Smørgrav 	host_status = check_key_in_hostkeys(host_hostkeys, host_key,
9124a421b63SDag-Erling Smørgrav 	    &host_found);
9134a421b63SDag-Erling Smørgrav 
914e8aafc91SKris Kennaway 	/*
915e8aafc91SKris Kennaway 	 * Also perform check for the ip address, skip the check if we are
916b15c8340SDag-Erling Smørgrav 	 * localhost, looking for a certificate, or the hostname was an ip
917b15c8340SDag-Erling Smørgrav 	 * address to begin with.
918e8aafc91SKris Kennaway 	 */
9194a421b63SDag-Erling Smørgrav 	if (!want_cert && ip_hostkeys != NULL) {
9204a421b63SDag-Erling Smørgrav 		ip_status = check_key_in_hostkeys(ip_hostkeys, host_key,
9214a421b63SDag-Erling Smørgrav 		    &ip_found);
922e8aafc91SKris Kennaway 		if (host_status == HOST_CHANGED &&
9234a421b63SDag-Erling Smørgrav 		    (ip_status != HOST_CHANGED ||
9244a421b63SDag-Erling Smørgrav 		    (ip_found != NULL &&
9254f52dfbbSDag-Erling Smørgrav 		    !sshkey_equal(ip_found->key, host_found->key))))
926e8aafc91SKris Kennaway 			host_ip_differ = 1;
927e8aafc91SKris Kennaway 	} else
928e8aafc91SKris Kennaway 		ip_status = host_status;
929e8aafc91SKris Kennaway 
930e8aafc91SKris Kennaway 	switch (host_status) {
931e8aafc91SKris Kennaway 	case HOST_OK:
932e8aafc91SKris Kennaway 		/* The host is known and the key matches. */
933b15c8340SDag-Erling Smørgrav 		debug("Host '%.200s' is known and matches the %s host %s.",
934b15c8340SDag-Erling Smørgrav 		    host, type, want_cert ? "certificate" : "key");
9354a421b63SDag-Erling Smørgrav 		debug("Found %s in %s:%lu", want_cert ? "CA key" : "key",
9364a421b63SDag-Erling Smørgrav 		    host_found->file, host_found->line);
9374f52dfbbSDag-Erling Smørgrav 		if (want_cert &&
9384f52dfbbSDag-Erling Smørgrav 		    !check_host_cert(options.host_key_alias == NULL ?
9394f52dfbbSDag-Erling Smørgrav 		    hostname : options.host_key_alias, host_key))
940b15c8340SDag-Erling Smørgrav 			goto fail;
941ca3176e7SBrian Feldman 		if (options.check_host_ip && ip_status == HOST_NEW) {
942b15c8340SDag-Erling Smørgrav 			if (readonly || want_cert)
943cf2b5f3bSDag-Erling Smørgrav 				logit("%s host key for IP address "
944af12a3e7SDag-Erling Smørgrav 				    "'%.128s' not in list of known hosts.",
945e8aafc91SKris Kennaway 				    type, ip);
946e146993eSDag-Erling Smørgrav 			else if (!add_host_to_hostfile(user_hostfiles[0], ip,
947aa49c926SDag-Erling Smørgrav 			    host_key, options.hash_known_hosts))
948cf2b5f3bSDag-Erling Smørgrav 				logit("Failed to add the %s host key for IP "
949af12a3e7SDag-Erling Smørgrav 				    "address '%.128s' to the list of known "
950557f75e5SDag-Erling Smørgrav 				    "hosts (%.500s).", type, ip,
951e146993eSDag-Erling Smørgrav 				    user_hostfiles[0]);
952af12a3e7SDag-Erling Smørgrav 			else
953cf2b5f3bSDag-Erling Smørgrav 				logit("Warning: Permanently added the %s host "
954af12a3e7SDag-Erling Smørgrav 				    "key for IP address '%.128s' to the list "
955af12a3e7SDag-Erling Smørgrav 				    "of known hosts.", type, ip);
956d4af9e69SDag-Erling Smørgrav 		} else if (options.visual_host_key) {
957bc5531deSDag-Erling Smørgrav 			fp = sshkey_fingerprint(host_key,
958bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_DEFAULT);
959bc5531deSDag-Erling Smørgrav 			ra = sshkey_fingerprint(host_key,
960bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_RANDOMART);
961bc5531deSDag-Erling Smørgrav 			if (fp == NULL || ra == NULL)
962bc5531deSDag-Erling Smørgrav 				fatal("%s: sshkey_fingerprint fail", __func__);
963acc1a9efSDag-Erling Smørgrav 			logit("Host key fingerprint is %s\n%s", fp, ra);
964e4a9863fSDag-Erling Smørgrav 			free(ra);
965e4a9863fSDag-Erling Smørgrav 			free(fp);
966e8aafc91SKris Kennaway 		}
967bc5531deSDag-Erling Smørgrav 		hostkey_trusted = 1;
968e8aafc91SKris Kennaway 		break;
969e8aafc91SKris Kennaway 	case HOST_NEW:
970333ee039SDag-Erling Smørgrav 		if (options.host_key_alias == NULL && port != 0 &&
971333ee039SDag-Erling Smørgrav 		    port != SSH_DEFAULT_PORT) {
972333ee039SDag-Erling Smørgrav 			debug("checking without port identifier");
973cce7d346SDag-Erling Smørgrav 			if (check_host_key(hostname, hostaddr, 0, host_key,
974e146993eSDag-Erling Smørgrav 			    ROQUIET, user_hostfiles, num_user_hostfiles,
975e146993eSDag-Erling Smørgrav 			    system_hostfiles, num_system_hostfiles) == 0) {
976333ee039SDag-Erling Smørgrav 				debug("found matching key w/out port");
977333ee039SDag-Erling Smørgrav 				break;
978333ee039SDag-Erling Smørgrav 			}
979333ee039SDag-Erling Smørgrav 		}
980b15c8340SDag-Erling Smørgrav 		if (readonly || want_cert)
981af12a3e7SDag-Erling Smørgrav 			goto fail;
982e8aafc91SKris Kennaway 		/* The host is new. */
9834f52dfbbSDag-Erling Smørgrav 		if (options.strict_host_key_checking ==
9844f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_YES) {
985af12a3e7SDag-Erling Smørgrav 			/*
986af12a3e7SDag-Erling Smørgrav 			 * User has requested strict host key checking.  We
987af12a3e7SDag-Erling Smørgrav 			 * will not add the host key automatically.  The only
988af12a3e7SDag-Erling Smørgrav 			 * alternative left is to abort.
989af12a3e7SDag-Erling Smørgrav 			 */
990af12a3e7SDag-Erling Smørgrav 			error("No %s host key is known for %.200s and you "
991af12a3e7SDag-Erling Smørgrav 			    "have requested strict checking.", type, host);
992af12a3e7SDag-Erling Smørgrav 			goto fail;
9934f52dfbbSDag-Erling Smørgrav 		} else if (options.strict_host_key_checking ==
9944f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_ASK) {
995cf2b5f3bSDag-Erling Smørgrav 			char msg1[1024], msg2[1024];
996cf2b5f3bSDag-Erling Smørgrav 
9974a421b63SDag-Erling Smørgrav 			if (show_other_keys(host_hostkeys, host_key))
998cf2b5f3bSDag-Erling Smørgrav 				snprintf(msg1, sizeof(msg1),
999cf2b5f3bSDag-Erling Smørgrav 				    "\nbut keys of different type are already"
1000cf2b5f3bSDag-Erling Smørgrav 				    " known for this host.");
1001cf2b5f3bSDag-Erling Smørgrav 			else
1002cf2b5f3bSDag-Erling Smørgrav 				snprintf(msg1, sizeof(msg1), ".");
1003e8aafc91SKris Kennaway 			/* The default */
1004bc5531deSDag-Erling Smørgrav 			fp = sshkey_fingerprint(host_key,
1005bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_DEFAULT);
1006bc5531deSDag-Erling Smørgrav 			ra = sshkey_fingerprint(host_key,
1007bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_RANDOMART);
1008bc5531deSDag-Erling Smørgrav 			if (fp == NULL || ra == NULL)
1009bc5531deSDag-Erling Smørgrav 				fatal("%s: sshkey_fingerprint fail", __func__);
1010cf2b5f3bSDag-Erling Smørgrav 			msg2[0] = '\0';
1011cf2b5f3bSDag-Erling Smørgrav 			if (options.verify_host_key_dns) {
10121ec0d754SDag-Erling Smørgrav 				if (matching_host_key_dns)
1013cf2b5f3bSDag-Erling Smørgrav 					snprintf(msg2, sizeof(msg2),
1014cf2b5f3bSDag-Erling Smørgrav 					    "Matching host key fingerprint"
1015cf2b5f3bSDag-Erling Smørgrav 					    " found in DNS.\n");
1016cf2b5f3bSDag-Erling Smørgrav 				else
1017cf2b5f3bSDag-Erling Smørgrav 					snprintf(msg2, sizeof(msg2),
1018cf2b5f3bSDag-Erling Smørgrav 					    "No matching host key fingerprint"
1019cf2b5f3bSDag-Erling Smørgrav 					    " found in DNS.\n");
1020cf2b5f3bSDag-Erling Smørgrav 			}
1021af12a3e7SDag-Erling Smørgrav 			snprintf(msg, sizeof(msg),
1022af12a3e7SDag-Erling Smørgrav 			    "The authenticity of host '%.200s (%s)' can't be "
1023f388f5efSDag-Erling Smørgrav 			    "established%s\n"
1024d4af9e69SDag-Erling Smørgrav 			    "%s key fingerprint is %s.%s%s\n%s"
1025af12a3e7SDag-Erling Smørgrav 			    "Are you sure you want to continue connecting "
1026f388f5efSDag-Erling Smørgrav 			    "(yes/no)? ",
1027d4af9e69SDag-Erling Smørgrav 			    host, ip, msg1, type, fp,
1028d4af9e69SDag-Erling Smørgrav 			    options.visual_host_key ? "\n" : "",
1029d4af9e69SDag-Erling Smørgrav 			    options.visual_host_key ? ra : "",
1030d4af9e69SDag-Erling Smørgrav 			    msg2);
1031e4a9863fSDag-Erling Smørgrav 			free(ra);
1032e4a9863fSDag-Erling Smørgrav 			free(fp);
1033af12a3e7SDag-Erling Smørgrav 			if (!confirm(msg))
1034af12a3e7SDag-Erling Smørgrav 				goto fail;
1035bc5531deSDag-Erling Smørgrav 			hostkey_trusted = 1; /* user explicitly confirmed */
1036e8aafc91SKris Kennaway 		}
1037af12a3e7SDag-Erling Smørgrav 		/*
10384f52dfbbSDag-Erling Smørgrav 		 * If in "new" or "off" strict mode, add the key automatically
10394f52dfbbSDag-Erling Smørgrav 		 * to the local known_hosts file.
1040af12a3e7SDag-Erling Smørgrav 		 */
1041aa49c926SDag-Erling Smørgrav 		if (options.check_host_ip && ip_status == HOST_NEW) {
10424a421b63SDag-Erling Smørgrav 			snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
1043aa49c926SDag-Erling Smørgrav 			hostp = hostline;
1044aa49c926SDag-Erling Smørgrav 			if (options.hash_known_hosts) {
1045aa49c926SDag-Erling Smørgrav 				/* Add hash of host and IP separately */
1046e146993eSDag-Erling Smørgrav 				r = add_host_to_hostfile(user_hostfiles[0],
1047e146993eSDag-Erling Smørgrav 				    host, host_key, options.hash_known_hosts) &&
1048e146993eSDag-Erling Smørgrav 				    add_host_to_hostfile(user_hostfiles[0], ip,
1049aa49c926SDag-Erling Smørgrav 				    host_key, options.hash_known_hosts);
1050aa49c926SDag-Erling Smørgrav 			} else {
1051aa49c926SDag-Erling Smørgrav 				/* Add unhashed "host,ip" */
1052e146993eSDag-Erling Smørgrav 				r = add_host_to_hostfile(user_hostfiles[0],
1053aa49c926SDag-Erling Smørgrav 				    hostline, host_key,
1054aa49c926SDag-Erling Smørgrav 				    options.hash_known_hosts);
1055aa49c926SDag-Erling Smørgrav 			}
1056aa49c926SDag-Erling Smørgrav 		} else {
1057e146993eSDag-Erling Smørgrav 			r = add_host_to_hostfile(user_hostfiles[0], host,
1058e146993eSDag-Erling Smørgrav 			    host_key, options.hash_known_hosts);
1059aa49c926SDag-Erling Smørgrav 			hostp = host;
1060aa49c926SDag-Erling Smørgrav 		}
1061aa49c926SDag-Erling Smørgrav 
1062aa49c926SDag-Erling Smørgrav 		if (!r)
1063cf2b5f3bSDag-Erling Smørgrav 			logit("Failed to add the host to the list of known "
1064e146993eSDag-Erling Smørgrav 			    "hosts (%.500s).", user_hostfiles[0]);
1065e8aafc91SKris Kennaway 		else
1066cf2b5f3bSDag-Erling Smørgrav 			logit("Warning: Permanently added '%.200s' (%s) to the "
1067af12a3e7SDag-Erling Smørgrav 			    "list of known hosts.", hostp, type);
1068e8aafc91SKris Kennaway 		break;
1069b15c8340SDag-Erling Smørgrav 	case HOST_REVOKED:
1070b15c8340SDag-Erling Smørgrav 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1071b15c8340SDag-Erling Smørgrav 		error("@       WARNING: REVOKED HOST KEY DETECTED!               @");
1072b15c8340SDag-Erling Smørgrav 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1073b15c8340SDag-Erling Smørgrav 		error("The %s host key for %s is marked as revoked.", type, host);
1074b15c8340SDag-Erling Smørgrav 		error("This could mean that a stolen key is being used to");
1075b15c8340SDag-Erling Smørgrav 		error("impersonate this host.");
1076b15c8340SDag-Erling Smørgrav 
1077b15c8340SDag-Erling Smørgrav 		/*
1078b15c8340SDag-Erling Smørgrav 		 * If strict host key checking is in use, the user will have
1079b15c8340SDag-Erling Smørgrav 		 * to edit the key manually and we can only abort.
1080b15c8340SDag-Erling Smørgrav 		 */
10814f52dfbbSDag-Erling Smørgrav 		if (options.strict_host_key_checking !=
10824f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_OFF) {
1083b15c8340SDag-Erling Smørgrav 			error("%s host key for %.200s was revoked and you have "
1084b15c8340SDag-Erling Smørgrav 			    "requested strict checking.", type, host);
1085b15c8340SDag-Erling Smørgrav 			goto fail;
1086b15c8340SDag-Erling Smørgrav 		}
1087b15c8340SDag-Erling Smørgrav 		goto continue_unsafe;
1088b15c8340SDag-Erling Smørgrav 
1089e8aafc91SKris Kennaway 	case HOST_CHANGED:
1090b15c8340SDag-Erling Smørgrav 		if (want_cert) {
1091b15c8340SDag-Erling Smørgrav 			/*
1092b15c8340SDag-Erling Smørgrav 			 * This is only a debug() since it is valid to have
1093b15c8340SDag-Erling Smørgrav 			 * CAs with wildcard DNS matches that don't match
1094b15c8340SDag-Erling Smørgrav 			 * all hosts that one might visit.
1095b15c8340SDag-Erling Smørgrav 			 */
1096b15c8340SDag-Erling Smørgrav 			debug("Host certificate authority does not "
10974a421b63SDag-Erling Smørgrav 			    "match %s in %s:%lu", CA_MARKER,
10984a421b63SDag-Erling Smørgrav 			    host_found->file, host_found->line);
1099b15c8340SDag-Erling Smørgrav 			goto fail;
1100b15c8340SDag-Erling Smørgrav 		}
1101333ee039SDag-Erling Smørgrav 		if (readonly == ROQUIET)
1102333ee039SDag-Erling Smørgrav 			goto fail;
1103e8aafc91SKris Kennaway 		if (options.check_host_ip && host_ip_differ) {
110421e764dfSDag-Erling Smørgrav 			char *key_msg;
1105e8aafc91SKris Kennaway 			if (ip_status == HOST_NEW)
110621e764dfSDag-Erling Smørgrav 				key_msg = "is unknown";
1107e8aafc91SKris Kennaway 			else if (ip_status == HOST_OK)
110821e764dfSDag-Erling Smørgrav 				key_msg = "is unchanged";
1109e8aafc91SKris Kennaway 			else
111021e764dfSDag-Erling Smørgrav 				key_msg = "has a different value";
1111e8aafc91SKris Kennaway 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1112e8aafc91SKris Kennaway 			error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
1113e8aafc91SKris Kennaway 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1114e8aafc91SKris Kennaway 			error("The %s host key for %s has changed,", type, host);
1115d4af9e69SDag-Erling Smørgrav 			error("and the key for the corresponding IP address %s", ip);
111621e764dfSDag-Erling Smørgrav 			error("%s. This could either mean that", key_msg);
1117e8aafc91SKris Kennaway 			error("DNS SPOOFING is happening or the IP address for the host");
1118ca3176e7SBrian Feldman 			error("and its host key have changed at the same time.");
1119ca3176e7SBrian Feldman 			if (ip_status != HOST_NEW)
11204a421b63SDag-Erling Smørgrav 				error("Offending key for IP in %s:%lu",
11214a421b63SDag-Erling Smørgrav 				    ip_found->file, ip_found->line);
1122e8aafc91SKris Kennaway 		}
1123e8aafc91SKris Kennaway 		/* The host key has changed. */
11241ec0d754SDag-Erling Smørgrav 		warn_changed_key(host_key);
1125e8aafc91SKris Kennaway 		error("Add correct host key in %.100s to get rid of this message.",
1126e146993eSDag-Erling Smørgrav 		    user_hostfiles[0]);
11274f52dfbbSDag-Erling Smørgrav 		error("Offending %s key in %s:%lu",
11284f52dfbbSDag-Erling Smørgrav 		    sshkey_type(host_found->key),
11294a421b63SDag-Erling Smørgrav 		    host_found->file, host_found->line);
1130e8aafc91SKris Kennaway 
1131e8aafc91SKris Kennaway 		/*
1132e8aafc91SKris Kennaway 		 * If strict host key checking is in use, the user will have
1133e8aafc91SKris Kennaway 		 * to edit the key manually and we can only abort.
1134e8aafc91SKris Kennaway 		 */
11354f52dfbbSDag-Erling Smørgrav 		if (options.strict_host_key_checking !=
11364f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_OFF) {
1137af12a3e7SDag-Erling Smørgrav 			error("%s host key for %.200s has changed and you have "
1138af12a3e7SDag-Erling Smørgrav 			    "requested strict checking.", type, host);
1139af12a3e7SDag-Erling Smørgrav 			goto fail;
1140af12a3e7SDag-Erling Smørgrav 		}
1141e8aafc91SKris Kennaway 
1142b15c8340SDag-Erling Smørgrav  continue_unsafe:
1143e8aafc91SKris Kennaway 		/*
1144e8aafc91SKris Kennaway 		 * If strict host key checking has not been requested, allow
1145cf2b5f3bSDag-Erling Smørgrav 		 * the connection but without MITM-able authentication or
1146333ee039SDag-Erling Smørgrav 		 * forwarding.
1147e8aafc91SKris Kennaway 		 */
1148e8aafc91SKris Kennaway 		if (options.password_authentication) {
1149af12a3e7SDag-Erling Smørgrav 			error("Password authentication is disabled to avoid "
1150af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1151e8aafc91SKris Kennaway 			options.password_authentication = 0;
1152d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1153e8aafc91SKris Kennaway 		}
1154cf2b5f3bSDag-Erling Smørgrav 		if (options.kbd_interactive_authentication) {
1155cf2b5f3bSDag-Erling Smørgrav 			error("Keyboard-interactive authentication is disabled"
1156cf2b5f3bSDag-Erling Smørgrav 			    " to avoid man-in-the-middle attacks.");
1157cf2b5f3bSDag-Erling Smørgrav 			options.kbd_interactive_authentication = 0;
1158cf2b5f3bSDag-Erling Smørgrav 			options.challenge_response_authentication = 0;
1159d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1160cf2b5f3bSDag-Erling Smørgrav 		}
1161cf2b5f3bSDag-Erling Smørgrav 		if (options.challenge_response_authentication) {
1162cf2b5f3bSDag-Erling Smørgrav 			error("Challenge/response authentication is disabled"
1163cf2b5f3bSDag-Erling Smørgrav 			    " to avoid man-in-the-middle attacks.");
1164cf2b5f3bSDag-Erling Smørgrav 			options.challenge_response_authentication = 0;
1165d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1166cf2b5f3bSDag-Erling Smørgrav 		}
1167e8aafc91SKris Kennaway 		if (options.forward_agent) {
1168af12a3e7SDag-Erling Smørgrav 			error("Agent forwarding is disabled to avoid "
1169af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1170e8aafc91SKris Kennaway 			options.forward_agent = 0;
1171d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1172e8aafc91SKris Kennaway 		}
1173ca3176e7SBrian Feldman 		if (options.forward_x11) {
1174af12a3e7SDag-Erling Smørgrav 			error("X11 forwarding is disabled to avoid "
1175af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1176ca3176e7SBrian Feldman 			options.forward_x11 = 0;
1177d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1178ca3176e7SBrian Feldman 		}
1179af12a3e7SDag-Erling Smørgrav 		if (options.num_local_forwards > 0 ||
1180af12a3e7SDag-Erling Smørgrav 		    options.num_remote_forwards > 0) {
1181af12a3e7SDag-Erling Smørgrav 			error("Port forwarding is disabled to avoid "
1182af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1183af12a3e7SDag-Erling Smørgrav 			options.num_local_forwards =
1184af12a3e7SDag-Erling Smørgrav 			    options.num_remote_forwards = 0;
1185d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1186ca3176e7SBrian Feldman 		}
1187333ee039SDag-Erling Smørgrav 		if (options.tun_open != SSH_TUNMODE_NO) {
1188333ee039SDag-Erling Smørgrav 			error("Tunnel forwarding is disabled to avoid "
1189333ee039SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1190333ee039SDag-Erling Smørgrav 			options.tun_open = SSH_TUNMODE_NO;
1191d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1192333ee039SDag-Erling Smørgrav 		}
1193d4af9e69SDag-Erling Smørgrav 		if (options.exit_on_forward_failure && cancelled_forwarding)
1194d4af9e69SDag-Erling Smørgrav 			fatal("Error: forwarding disabled due to host key "
1195d4af9e69SDag-Erling Smørgrav 			    "check failure");
1196d4af9e69SDag-Erling Smørgrav 
1197e8aafc91SKris Kennaway 		/*
1198e8aafc91SKris Kennaway 		 * XXX Should permit the user to change to use the new id.
1199e8aafc91SKris Kennaway 		 * This could be done by converting the host key to an
1200e8aafc91SKris Kennaway 		 * identifying sentence, tell that the host identifies itself
1201b15c8340SDag-Erling Smørgrav 		 * by that sentence, and ask the user if he/she wishes to
1202e8aafc91SKris Kennaway 		 * accept the authentication.
1203e8aafc91SKris Kennaway 		 */
1204e8aafc91SKris Kennaway 		break;
1205f388f5efSDag-Erling Smørgrav 	case HOST_FOUND:
1206f388f5efSDag-Erling Smørgrav 		fatal("internal error");
1207f388f5efSDag-Erling Smørgrav 		break;
1208e8aafc91SKris Kennaway 	}
1209ca3176e7SBrian Feldman 
1210ca3176e7SBrian Feldman 	if (options.check_host_ip && host_status != HOST_CHANGED &&
1211ca3176e7SBrian Feldman 	    ip_status == HOST_CHANGED) {
1212af12a3e7SDag-Erling Smørgrav 		snprintf(msg, sizeof(msg),
1213af12a3e7SDag-Erling Smørgrav 		    "Warning: the %s host key for '%.200s' "
1214af12a3e7SDag-Erling Smørgrav 		    "differs from the key for the IP address '%.128s'"
12154a421b63SDag-Erling Smørgrav 		    "\nOffending key for IP in %s:%lu",
12164a421b63SDag-Erling Smørgrav 		    type, host, ip, ip_found->file, ip_found->line);
1217af12a3e7SDag-Erling Smørgrav 		if (host_status == HOST_OK) {
1218af12a3e7SDag-Erling Smørgrav 			len = strlen(msg);
1219af12a3e7SDag-Erling Smørgrav 			snprintf(msg + len, sizeof(msg) - len,
12204a421b63SDag-Erling Smørgrav 			    "\nMatching host key in %s:%lu",
12214a421b63SDag-Erling Smørgrav 			    host_found->file, host_found->line);
1222af12a3e7SDag-Erling Smørgrav 		}
12234f52dfbbSDag-Erling Smørgrav 		if (options.strict_host_key_checking ==
12244f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_ASK) {
1225af12a3e7SDag-Erling Smørgrav 			strlcat(msg, "\nAre you sure you want "
1226af12a3e7SDag-Erling Smørgrav 			    "to continue connecting (yes/no)? ", sizeof(msg));
1227af12a3e7SDag-Erling Smørgrav 			if (!confirm(msg))
1228af12a3e7SDag-Erling Smørgrav 				goto fail;
12294f52dfbbSDag-Erling Smørgrav 		} else if (options.strict_host_key_checking !=
12304f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_OFF) {
12314f52dfbbSDag-Erling Smørgrav 			logit("%s", msg);
12324f52dfbbSDag-Erling Smørgrav 			error("Exiting, you have requested strict checking.");
12334f52dfbbSDag-Erling Smørgrav 			goto fail;
1234af12a3e7SDag-Erling Smørgrav 		} else {
1235cf2b5f3bSDag-Erling Smørgrav 			logit("%s", msg);
1236ca3176e7SBrian Feldman 		}
1237ca3176e7SBrian Feldman 	}
1238ca3176e7SBrian Feldman 
1239bc5531deSDag-Erling Smørgrav 	if (!hostkey_trusted && options.update_hostkeys) {
1240bc5531deSDag-Erling Smørgrav 		debug("%s: hostkey not known or explicitly trusted: "
1241bc5531deSDag-Erling Smørgrav 		    "disabling UpdateHostkeys", __func__);
1242bc5531deSDag-Erling Smørgrav 		options.update_hostkeys = 0;
1243bc5531deSDag-Erling Smørgrav 	}
1244bc5531deSDag-Erling Smørgrav 
1245e4a9863fSDag-Erling Smørgrav 	free(ip);
1246e4a9863fSDag-Erling Smørgrav 	free(host);
12474a421b63SDag-Erling Smørgrav 	if (host_hostkeys != NULL)
12484a421b63SDag-Erling Smørgrav 		free_hostkeys(host_hostkeys);
12494a421b63SDag-Erling Smørgrav 	if (ip_hostkeys != NULL)
12504a421b63SDag-Erling Smørgrav 		free_hostkeys(ip_hostkeys);
1251af12a3e7SDag-Erling Smørgrav 	return 0;
1252af12a3e7SDag-Erling Smørgrav 
1253af12a3e7SDag-Erling Smørgrav fail:
1254b15c8340SDag-Erling Smørgrav 	if (want_cert && host_status != HOST_REVOKED) {
1255b15c8340SDag-Erling Smørgrav 		/*
1256b15c8340SDag-Erling Smørgrav 		 * No matching certificate. Downgrade cert to raw key and
1257b15c8340SDag-Erling Smørgrav 		 * search normally.
1258b15c8340SDag-Erling Smørgrav 		 */
1259b15c8340SDag-Erling Smørgrav 		debug("No matching CA found. Retry with plain key");
12604f52dfbbSDag-Erling Smørgrav 		if ((r = sshkey_from_private(host_key, &raw_key)) != 0)
12614f52dfbbSDag-Erling Smørgrav 			fatal("%s: sshkey_from_private: %s",
12624f52dfbbSDag-Erling Smørgrav 			    __func__, ssh_err(r));
12634f52dfbbSDag-Erling Smørgrav 		if ((r = sshkey_drop_cert(raw_key)) != 0)
12644f52dfbbSDag-Erling Smørgrav 			fatal("Couldn't drop certificate: %s", ssh_err(r));
1265b15c8340SDag-Erling Smørgrav 		host_key = raw_key;
1266b15c8340SDag-Erling Smørgrav 		goto retry;
1267b15c8340SDag-Erling Smørgrav 	}
12684f52dfbbSDag-Erling Smørgrav 	sshkey_free(raw_key);
1269e4a9863fSDag-Erling Smørgrav 	free(ip);
1270e4a9863fSDag-Erling Smørgrav 	free(host);
12714a421b63SDag-Erling Smørgrav 	if (host_hostkeys != NULL)
12724a421b63SDag-Erling Smørgrav 		free_hostkeys(host_hostkeys);
12734a421b63SDag-Erling Smørgrav 	if (ip_hostkeys != NULL)
12744a421b63SDag-Erling Smørgrav 		free_hostkeys(ip_hostkeys);
1275af12a3e7SDag-Erling Smørgrav 	return -1;
1276e8aafc91SKris Kennaway }
1277511b41d2SMark Murray 
1278cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */
1279fe5fd017SMark Murray int
12804f52dfbbSDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key)
1281fe5fd017SMark Murray {
1282acc1a9efSDag-Erling Smørgrav 	u_int i;
1283a0ee8cc6SDag-Erling Smørgrav 	int r = -1, flags = 0;
1284acc1a9efSDag-Erling Smørgrav 	char valid[64], *fp = NULL, *cafp = NULL;
1285bc5531deSDag-Erling Smørgrav 	struct sshkey *plain = NULL;
12864a421b63SDag-Erling Smørgrav 
1287bc5531deSDag-Erling Smørgrav 	if ((fp = sshkey_fingerprint(host_key,
1288bc5531deSDag-Erling Smørgrav 	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
1289bc5531deSDag-Erling Smørgrav 		error("%s: fingerprint host key: %s", __func__, ssh_err(r));
1290bc5531deSDag-Erling Smørgrav 		r = -1;
1291bc5531deSDag-Erling Smørgrav 		goto out;
1292bc5531deSDag-Erling Smørgrav 	}
1293fe5fd017SMark Murray 
1294acc1a9efSDag-Erling Smørgrav 	if (sshkey_is_cert(host_key)) {
1295acc1a9efSDag-Erling Smørgrav 		if ((cafp = sshkey_fingerprint(host_key->cert->signature_key,
1296acc1a9efSDag-Erling Smørgrav 		    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
1297acc1a9efSDag-Erling Smørgrav 			error("%s: fingerprint CA key: %s",
1298acc1a9efSDag-Erling Smørgrav 			    __func__, ssh_err(r));
1299acc1a9efSDag-Erling Smørgrav 			r = -1;
1300acc1a9efSDag-Erling Smørgrav 			goto out;
1301acc1a9efSDag-Erling Smørgrav 		}
1302acc1a9efSDag-Erling Smørgrav 		sshkey_format_cert_validity(host_key->cert,
1303acc1a9efSDag-Erling Smørgrav 		    valid, sizeof(valid));
1304acc1a9efSDag-Erling Smørgrav 		debug("Server host certificate: %s %s, serial %llu "
1305acc1a9efSDag-Erling Smørgrav 		    "ID \"%s\" CA %s %s valid %s",
1306acc1a9efSDag-Erling Smørgrav 		    sshkey_ssh_name(host_key), fp,
1307acc1a9efSDag-Erling Smørgrav 		    (unsigned long long)host_key->cert->serial,
1308acc1a9efSDag-Erling Smørgrav 		    host_key->cert->key_id,
1309acc1a9efSDag-Erling Smørgrav 		    sshkey_ssh_name(host_key->cert->signature_key), cafp,
1310acc1a9efSDag-Erling Smørgrav 		    valid);
1311acc1a9efSDag-Erling Smørgrav 		for (i = 0; i < host_key->cert->nprincipals; i++) {
1312acc1a9efSDag-Erling Smørgrav 			debug2("Server host certificate hostname: %s",
1313acc1a9efSDag-Erling Smørgrav 			    host_key->cert->principals[i]);
1314acc1a9efSDag-Erling Smørgrav 		}
1315acc1a9efSDag-Erling Smørgrav 	} else {
13164f52dfbbSDag-Erling Smørgrav 		debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp);
1317acc1a9efSDag-Erling Smørgrav 	}
1318bc5531deSDag-Erling Smørgrav 
1319bc5531deSDag-Erling Smørgrav 	if (sshkey_equal(previous_host_key, host_key)) {
1320bc5531deSDag-Erling Smørgrav 		debug2("%s: server host key %s %s matches cached key",
1321bc5531deSDag-Erling Smørgrav 		    __func__, sshkey_type(host_key), fp);
1322bc5531deSDag-Erling Smørgrav 		r = 0;
1323bc5531deSDag-Erling Smørgrav 		goto out;
1324bc5531deSDag-Erling Smørgrav 	}
1325bc5531deSDag-Erling Smørgrav 
1326bc5531deSDag-Erling Smørgrav 	/* Check in RevokedHostKeys file if specified */
1327bc5531deSDag-Erling Smørgrav 	if (options.revoked_host_keys != NULL) {
1328bc5531deSDag-Erling Smørgrav 		r = sshkey_check_revoked(host_key, options.revoked_host_keys);
1329bc5531deSDag-Erling Smørgrav 		switch (r) {
1330bc5531deSDag-Erling Smørgrav 		case 0:
1331bc5531deSDag-Erling Smørgrav 			break; /* not revoked */
1332bc5531deSDag-Erling Smørgrav 		case SSH_ERR_KEY_REVOKED:
1333bc5531deSDag-Erling Smørgrav 			error("Host key %s %s revoked by file %s",
1334bc5531deSDag-Erling Smørgrav 			    sshkey_type(host_key), fp,
1335bc5531deSDag-Erling Smørgrav 			    options.revoked_host_keys);
1336bc5531deSDag-Erling Smørgrav 			r = -1;
1337bc5531deSDag-Erling Smørgrav 			goto out;
1338bc5531deSDag-Erling Smørgrav 		default:
1339bc5531deSDag-Erling Smørgrav 			error("Error checking host key %s %s in "
1340bc5531deSDag-Erling Smørgrav 			    "revoked keys file %s: %s", sshkey_type(host_key),
1341bc5531deSDag-Erling Smørgrav 			    fp, options.revoked_host_keys, ssh_err(r));
1342bc5531deSDag-Erling Smørgrav 			r = -1;
1343bc5531deSDag-Erling Smørgrav 			goto out;
1344bc5531deSDag-Erling Smørgrav 		}
1345a0ee8cc6SDag-Erling Smørgrav 	}
1346a0ee8cc6SDag-Erling Smørgrav 
13473a0b9b77SXin LI 	if (options.verify_host_key_dns) {
13483a0b9b77SXin LI 		/*
13493a0b9b77SXin LI 		 * XXX certs are not yet supported for DNS, so downgrade
13503a0b9b77SXin LI 		 * them and try the plain key.
13513a0b9b77SXin LI 		 */
1352bc5531deSDag-Erling Smørgrav 		if ((r = sshkey_from_private(host_key, &plain)) != 0)
1353bc5531deSDag-Erling Smørgrav 			goto out;
1354bc5531deSDag-Erling Smørgrav 		if (sshkey_is_cert(plain))
1355bc5531deSDag-Erling Smørgrav 			sshkey_drop_cert(plain);
13563a0b9b77SXin LI 		if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
13571ec0d754SDag-Erling Smørgrav 			if (flags & DNS_VERIFY_FOUND) {
13581ec0d754SDag-Erling Smørgrav 				if (options.verify_host_key_dns == 1 &&
13591ec0d754SDag-Erling Smørgrav 				    flags & DNS_VERIFY_MATCH &&
13603a0b9b77SXin LI 				    flags & DNS_VERIFY_SECURE) {
1361a0ee8cc6SDag-Erling Smørgrav 					r = 0;
1362bc5531deSDag-Erling Smørgrav 					goto out;
13633a0b9b77SXin LI 				}
13641ec0d754SDag-Erling Smørgrav 				if (flags & DNS_VERIFY_MATCH) {
13651ec0d754SDag-Erling Smørgrav 					matching_host_key_dns = 1;
13661ec0d754SDag-Erling Smørgrav 				} else {
13673a0b9b77SXin LI 					warn_changed_key(plain);
13683a0b9b77SXin LI 					error("Update the SSHFP RR in DNS "
13693a0b9b77SXin LI 					    "with the new host key to get rid "
13703a0b9b77SXin LI 					    "of this message.");
1371cf2b5f3bSDag-Erling Smørgrav 				}
1372cf2b5f3bSDag-Erling Smørgrav 			}
13731ec0d754SDag-Erling Smørgrav 		}
13743a0b9b77SXin LI 	}
1375a0ee8cc6SDag-Erling Smørgrav 	r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
1376e146993eSDag-Erling Smørgrav 	    options.user_hostfiles, options.num_user_hostfiles,
1377e146993eSDag-Erling Smørgrav 	    options.system_hostfiles, options.num_system_hostfiles);
1378a0ee8cc6SDag-Erling Smørgrav 
1379bc5531deSDag-Erling Smørgrav out:
1380bc5531deSDag-Erling Smørgrav 	sshkey_free(plain);
1381bc5531deSDag-Erling Smørgrav 	free(fp);
1382acc1a9efSDag-Erling Smørgrav 	free(cafp);
1383a0ee8cc6SDag-Erling Smørgrav 	if (r == 0 && host_key != NULL) {
13844f52dfbbSDag-Erling Smørgrav 		sshkey_free(previous_host_key);
13854f52dfbbSDag-Erling Smørgrav 		r = sshkey_from_private(host_key, &previous_host_key);
1386a0ee8cc6SDag-Erling Smørgrav 	}
1387a0ee8cc6SDag-Erling Smørgrav 
1388a0ee8cc6SDag-Erling Smørgrav 	return r;
1389fe5fd017SMark Murray }
1390fe5fd017SMark Murray 
1391511b41d2SMark Murray /*
1392511b41d2SMark Murray  * Starts a dialog with the server, and authenticates the current user on the
1393511b41d2SMark Murray  * server.  This does not need any extra privileges.  The basic connection
1394511b41d2SMark Murray  * to the server must already have been established before this is called.
1395511b41d2SMark Murray  * If login fails, this function prints an error and never returns.
1396511b41d2SMark Murray  * This function does not require super-user privileges.
1397511b41d2SMark Murray  */
1398511b41d2SMark Murray void
139980628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost,
14004a421b63SDag-Erling Smørgrav     struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
1401511b41d2SMark Murray {
1402f7167e0eSDag-Erling Smørgrav 	char *host;
1403e8aafc91SKris Kennaway 	char *server_user, *local_user;
1404e8aafc91SKris Kennaway 
1405e8aafc91SKris Kennaway 	local_user = xstrdup(pw->pw_name);
1406e8aafc91SKris Kennaway 	server_user = options.user ? options.user : local_user;
1407511b41d2SMark Murray 
1408511b41d2SMark Murray 	/* Convert the user-supplied hostname into all lowercase. */
1409511b41d2SMark Murray 	host = xstrdup(orighost);
1410f7167e0eSDag-Erling Smørgrav 	lowercase(host);
1411511b41d2SMark Murray 
1412511b41d2SMark Murray 	/* Exchange protocol version identification strings with the server. */
1413d4af9e69SDag-Erling Smørgrav 	ssh_exchange_identification(timeout_ms);
1414511b41d2SMark Murray 
1415511b41d2SMark Murray 	/* Put the connection into non-blocking mode. */
1416511b41d2SMark Murray 	packet_set_nonblocking();
1417511b41d2SMark Murray 
1418511b41d2SMark Murray 	/* key exchange */
1419511b41d2SMark Murray 	/* authenticate user */
1420557f75e5SDag-Erling Smørgrav 	debug("Authenticating to %s:%d as '%s'", host, port, server_user);
14214a421b63SDag-Erling Smørgrav 	ssh_kex2(host, hostaddr, port);
142280628bacSDag-Erling Smørgrav 	ssh_userauth2(local_user, server_user, host, sensitive);
1423e4a9863fSDag-Erling Smørgrav 	free(local_user);
1424511b41d2SMark Murray }
1425e0fbb1d2SBrian Feldman 
1426e0fbb1d2SBrian Feldman void
1427e0fbb1d2SBrian Feldman ssh_put_password(char *password)
1428e0fbb1d2SBrian Feldman {
1429e0fbb1d2SBrian Feldman 	int size;
1430e0fbb1d2SBrian Feldman 	char *padded;
1431e0fbb1d2SBrian Feldman 
1432ca3176e7SBrian Feldman 	if (datafellows & SSH_BUG_PASSWORDPAD) {
1433af12a3e7SDag-Erling Smørgrav 		packet_put_cstring(password);
1434ca3176e7SBrian Feldman 		return;
1435ca3176e7SBrian Feldman 	}
1436ca86bcf2SDag-Erling Smørgrav 	size = ROUNDUP(strlen(password) + 1, 32);
1437333ee039SDag-Erling Smørgrav 	padded = xcalloc(1, size);
1438e0fbb1d2SBrian Feldman 	strlcpy(padded, password, size);
1439e0fbb1d2SBrian Feldman 	packet_put_string(padded, size);
1440b83788ffSDag-Erling Smørgrav 	explicit_bzero(padded, size);
1441e4a9863fSDag-Erling Smørgrav 	free(padded);
1442e0fbb1d2SBrian Feldman }
1443f388f5efSDag-Erling Smørgrav 
1444f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */
1445f388f5efSDag-Erling Smørgrav static int
14464f52dfbbSDag-Erling Smørgrav show_other_keys(struct hostkeys *hostkeys, struct sshkey *key)
1447f388f5efSDag-Erling Smørgrav {
1448f7167e0eSDag-Erling Smørgrav 	int type[] = {
1449f7167e0eSDag-Erling Smørgrav 		KEY_RSA,
1450f7167e0eSDag-Erling Smørgrav 		KEY_DSA,
1451f7167e0eSDag-Erling Smørgrav 		KEY_ECDSA,
1452f7167e0eSDag-Erling Smørgrav 		KEY_ED25519,
145347dd1d1bSDag-Erling Smørgrav 		KEY_XMSS,
1454f7167e0eSDag-Erling Smørgrav 		-1
1455f7167e0eSDag-Erling Smørgrav 	};
14564a421b63SDag-Erling Smørgrav 	int i, ret = 0;
14574a421b63SDag-Erling Smørgrav 	char *fp, *ra;
14584a421b63SDag-Erling Smørgrav 	const struct hostkey_entry *found;
1459f388f5efSDag-Erling Smørgrav 
1460f388f5efSDag-Erling Smørgrav 	for (i = 0; type[i] != -1; i++) {
1461f388f5efSDag-Erling Smørgrav 		if (type[i] == key->type)
1462f388f5efSDag-Erling Smørgrav 			continue;
14634a421b63SDag-Erling Smørgrav 		if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found))
1464f388f5efSDag-Erling Smørgrav 			continue;
1465bc5531deSDag-Erling Smørgrav 		fp = sshkey_fingerprint(found->key,
1466bc5531deSDag-Erling Smørgrav 		    options.fingerprint_hash, SSH_FP_DEFAULT);
1467bc5531deSDag-Erling Smørgrav 		ra = sshkey_fingerprint(found->key,
1468bc5531deSDag-Erling Smørgrav 		    options.fingerprint_hash, SSH_FP_RANDOMART);
1469bc5531deSDag-Erling Smørgrav 		if (fp == NULL || ra == NULL)
1470bc5531deSDag-Erling Smørgrav 			fatal("%s: sshkey_fingerprint fail", __func__);
14714a421b63SDag-Erling Smørgrav 		logit("WARNING: %s key found for host %s\n"
14724a421b63SDag-Erling Smørgrav 		    "in %s:%lu\n"
14734a421b63SDag-Erling Smørgrav 		    "%s key fingerprint %s.",
1474*190cef3dSDag-Erling Smørgrav 		    sshkey_type(found->key),
14754a421b63SDag-Erling Smørgrav 		    found->host, found->file, found->line,
1476*190cef3dSDag-Erling Smørgrav 		    sshkey_type(found->key), fp);
14774a421b63SDag-Erling Smørgrav 		if (options.visual_host_key)
14784a421b63SDag-Erling Smørgrav 			logit("%s", ra);
1479e4a9863fSDag-Erling Smørgrav 		free(ra);
1480e4a9863fSDag-Erling Smørgrav 		free(fp);
14814a421b63SDag-Erling Smørgrav 		ret = 1;
1482f388f5efSDag-Erling Smørgrav 	}
14834a421b63SDag-Erling Smørgrav 	return ret;
1484f388f5efSDag-Erling Smørgrav }
14851ec0d754SDag-Erling Smørgrav 
14861ec0d754SDag-Erling Smørgrav static void
14874f52dfbbSDag-Erling Smørgrav warn_changed_key(struct sshkey *host_key)
14881ec0d754SDag-Erling Smørgrav {
14891ec0d754SDag-Erling Smørgrav 	char *fp;
14901ec0d754SDag-Erling Smørgrav 
1491bc5531deSDag-Erling Smørgrav 	fp = sshkey_fingerprint(host_key, options.fingerprint_hash,
1492bc5531deSDag-Erling Smørgrav 	    SSH_FP_DEFAULT);
1493bc5531deSDag-Erling Smørgrav 	if (fp == NULL)
1494bc5531deSDag-Erling Smørgrav 		fatal("%s: sshkey_fingerprint fail", __func__);
14951ec0d754SDag-Erling Smørgrav 
14961ec0d754SDag-Erling Smørgrav 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
14971ec0d754SDag-Erling Smørgrav 	error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
14981ec0d754SDag-Erling Smørgrav 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
14991ec0d754SDag-Erling Smørgrav 	error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
15001ec0d754SDag-Erling Smørgrav 	error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
15014a421b63SDag-Erling Smørgrav 	error("It is also possible that a host key has just been changed.");
15021ec0d754SDag-Erling Smørgrav 	error("The fingerprint for the %s key sent by the remote host is\n%s.",
1503*190cef3dSDag-Erling Smørgrav 	    sshkey_type(host_key), fp);
15041ec0d754SDag-Erling Smørgrav 	error("Please contact your system administrator.");
15051ec0d754SDag-Erling Smørgrav 
1506e4a9863fSDag-Erling Smørgrav 	free(fp);
15071ec0d754SDag-Erling Smørgrav }
1508b74df5b2SDag-Erling Smørgrav 
1509b74df5b2SDag-Erling Smørgrav /*
1510b74df5b2SDag-Erling Smørgrav  * Execute a local command
1511b74df5b2SDag-Erling Smørgrav  */
1512b74df5b2SDag-Erling Smørgrav int
1513b74df5b2SDag-Erling Smørgrav ssh_local_cmd(const char *args)
1514b74df5b2SDag-Erling Smørgrav {
1515b74df5b2SDag-Erling Smørgrav 	char *shell;
1516b74df5b2SDag-Erling Smørgrav 	pid_t pid;
1517b74df5b2SDag-Erling Smørgrav 	int status;
15184a421b63SDag-Erling Smørgrav 	void (*osighand)(int);
1519b74df5b2SDag-Erling Smørgrav 
1520b74df5b2SDag-Erling Smørgrav 	if (!options.permit_local_command ||
1521b74df5b2SDag-Erling Smørgrav 	    args == NULL || !*args)
1522b74df5b2SDag-Erling Smørgrav 		return (1);
1523b74df5b2SDag-Erling Smørgrav 
15244a421b63SDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
1525b74df5b2SDag-Erling Smørgrav 		shell = _PATH_BSHELL;
1526b74df5b2SDag-Erling Smørgrav 
15274a421b63SDag-Erling Smørgrav 	osighand = signal(SIGCHLD, SIG_DFL);
1528b74df5b2SDag-Erling Smørgrav 	pid = fork();
1529b74df5b2SDag-Erling Smørgrav 	if (pid == 0) {
15304a421b63SDag-Erling Smørgrav 		signal(SIGPIPE, SIG_DFL);
1531b74df5b2SDag-Erling Smørgrav 		debug3("Executing %s -c \"%s\"", shell, args);
1532b74df5b2SDag-Erling Smørgrav 		execl(shell, shell, "-c", args, (char *)NULL);
1533b74df5b2SDag-Erling Smørgrav 		error("Couldn't execute %s -c \"%s\": %s",
1534b74df5b2SDag-Erling Smørgrav 		    shell, args, strerror(errno));
1535b74df5b2SDag-Erling Smørgrav 		_exit(1);
1536b74df5b2SDag-Erling Smørgrav 	} else if (pid == -1)
1537b74df5b2SDag-Erling Smørgrav 		fatal("fork failed: %.100s", strerror(errno));
1538b74df5b2SDag-Erling Smørgrav 	while (waitpid(pid, &status, 0) == -1)
1539b74df5b2SDag-Erling Smørgrav 		if (errno != EINTR)
1540b74df5b2SDag-Erling Smørgrav 			fatal("Couldn't wait for child: %s", strerror(errno));
15414a421b63SDag-Erling Smørgrav 	signal(SIGCHLD, osighand);
1542b74df5b2SDag-Erling Smørgrav 
1543b74df5b2SDag-Erling Smørgrav 	if (!WIFEXITED(status))
1544b74df5b2SDag-Erling Smørgrav 		return (1);
1545b74df5b2SDag-Erling Smørgrav 
1546b74df5b2SDag-Erling Smørgrav 	return (WEXITSTATUS(status));
1547b74df5b2SDag-Erling Smørgrav }
1548acc1a9efSDag-Erling Smørgrav 
1549acc1a9efSDag-Erling Smørgrav void
155047dd1d1bSDag-Erling Smørgrav maybe_add_key_to_agent(char *authfile, const struct sshkey *private,
155147dd1d1bSDag-Erling Smørgrav     char *comment, char *passphrase)
1552acc1a9efSDag-Erling Smørgrav {
1553acc1a9efSDag-Erling Smørgrav 	int auth_sock = -1, r;
1554acc1a9efSDag-Erling Smørgrav 
1555acc1a9efSDag-Erling Smørgrav 	if (options.add_keys_to_agent == 0)
1556acc1a9efSDag-Erling Smørgrav 		return;
1557acc1a9efSDag-Erling Smørgrav 
1558acc1a9efSDag-Erling Smørgrav 	if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
1559acc1a9efSDag-Erling Smørgrav 		debug3("no authentication agent, not adding key");
1560acc1a9efSDag-Erling Smørgrav 		return;
1561acc1a9efSDag-Erling Smørgrav 	}
1562acc1a9efSDag-Erling Smørgrav 
1563acc1a9efSDag-Erling Smørgrav 	if (options.add_keys_to_agent == 2 &&
1564acc1a9efSDag-Erling Smørgrav 	    !ask_permission("Add key %s (%s) to agent?", authfile, comment)) {
1565acc1a9efSDag-Erling Smørgrav 		debug3("user denied adding this key");
1566d93a896eSDag-Erling Smørgrav 		close(auth_sock);
1567acc1a9efSDag-Erling Smørgrav 		return;
1568acc1a9efSDag-Erling Smørgrav 	}
1569acc1a9efSDag-Erling Smørgrav 
1570acc1a9efSDag-Erling Smørgrav 	if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0,
157147dd1d1bSDag-Erling Smørgrav 	    (options.add_keys_to_agent == 3), 0)) == 0)
1572acc1a9efSDag-Erling Smørgrav 		debug("identity added to agent: %s", authfile);
1573acc1a9efSDag-Erling Smørgrav 	else
1574acc1a9efSDag-Erling Smørgrav 		debug("could not add identity to agent: %s (%d)", authfile, r);
1575d93a896eSDag-Erling Smørgrav 	close(auth_sock);
1576acc1a9efSDag-Erling Smørgrav }
1577