xref: /freebsd/crypto/openssh/sshconnect.c (revision 557f75e54ae47df936c7de8fb97ec70c4180a5c0)
1*557f75e5SDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.262 2015/05/28 05:41:29 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 
19bc5531deSDag-Erling Smørgrav #include <sys/param.h>	/* roundup */
20333ee039SDag-Erling Smørgrav #include <sys/types.h>
21333ee039SDag-Erling Smørgrav #include <sys/wait.h>
22333ee039SDag-Erling Smørgrav #include <sys/stat.h>
23333ee039SDag-Erling Smørgrav #include <sys/socket.h>
24333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H
25333ee039SDag-Erling Smørgrav # include <sys/time.h>
26333ee039SDag-Erling Smørgrav #endif
27e8aafc91SKris Kennaway 
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>
404a421b63SDag-Erling Smørgrav #include <signal.h>
41333ee039SDag-Erling Smørgrav #include <stdarg.h>
42333ee039SDag-Erling Smørgrav #include <stdio.h>
43333ee039SDag-Erling Smørgrav #include <stdlib.h>
44333ee039SDag-Erling Smørgrav #include <string.h>
45333ee039SDag-Erling Smørgrav #include <unistd.h>
46333ee039SDag-Erling Smørgrav 
47511b41d2SMark Murray #include "xmalloc.h"
48333ee039SDag-Erling Smørgrav #include "key.h"
49333ee039SDag-Erling Smørgrav #include "hostfile.h"
50333ee039SDag-Erling Smørgrav #include "ssh.h"
51511b41d2SMark Murray #include "rsa.h"
52e8aafc91SKris Kennaway #include "buffer.h"
53511b41d2SMark Murray #include "packet.h"
54511b41d2SMark Murray #include "uidswap.h"
55511b41d2SMark Murray #include "compat.h"
563c6ae118SKris Kennaway #include "key.h"
57e8aafc91SKris Kennaway #include "sshconnect.h"
583c6ae118SKris Kennaway #include "hostfile.h"
59ca3176e7SBrian Feldman #include "log.h"
60a0ee8cc6SDag-Erling Smørgrav #include "misc.h"
61ca3176e7SBrian Feldman #include "readconf.h"
62ca3176e7SBrian Feldman #include "atomicio.h"
63cf2b5f3bSDag-Erling Smørgrav #include "dns.h"
647aee6ffeSDag-Erling Smørgrav #include "roaming.h"
65f7167e0eSDag-Erling Smørgrav #include "monitor_fdpass.h"
66b15c8340SDag-Erling Smørgrav #include "ssh2.h"
67333ee039SDag-Erling Smørgrav #include "version.h"
68bc5531deSDag-Erling Smørgrav #include "authfile.h"
69bc5531deSDag-Erling Smørgrav #include "ssherr.h"
70cf2b5f3bSDag-Erling Smørgrav 
71e8aafc91SKris Kennaway char *client_version_string = NULL;
72e8aafc91SKris Kennaway char *server_version_string = NULL;
73a0ee8cc6SDag-Erling Smørgrav Key *previous_host_key = NULL;
74511b41d2SMark Murray 
75b74df5b2SDag-Erling Smørgrav static int matching_host_key_dns = 0;
76cf2b5f3bSDag-Erling Smørgrav 
774a421b63SDag-Erling Smørgrav static pid_t proxy_command_pid = 0;
784a421b63SDag-Erling Smørgrav 
7980628bacSDag-Erling Smørgrav /* import */
80511b41d2SMark Murray extern Options options;
81511b41d2SMark Murray extern char *__progname;
8280628bacSDag-Erling Smørgrav extern uid_t original_real_uid;
8380628bacSDag-Erling Smørgrav extern uid_t original_effective_uid;
84511b41d2SMark Murray 
854a421b63SDag-Erling Smørgrav static int show_other_keys(struct hostkeys *, Key *);
861ec0d754SDag-Erling Smørgrav static void warn_changed_key(Key *);
87ca3176e7SBrian Feldman 
88f7167e0eSDag-Erling Smørgrav /* Expand a proxy command */
89f7167e0eSDag-Erling Smørgrav static char *
90f7167e0eSDag-Erling Smørgrav expand_proxy_command(const char *proxy_command, const char *user,
91f7167e0eSDag-Erling Smørgrav     const char *host, int port)
92f7167e0eSDag-Erling Smørgrav {
93f7167e0eSDag-Erling Smørgrav 	char *tmp, *ret, strport[NI_MAXSERV];
94f7167e0eSDag-Erling Smørgrav 
95f7167e0eSDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%d", port);
96f7167e0eSDag-Erling Smørgrav 	xasprintf(&tmp, "exec %s", proxy_command);
97f7167e0eSDag-Erling Smørgrav 	ret = percent_expand(tmp, "h", host, "p", strport,
98f7167e0eSDag-Erling Smørgrav 	    "r", options.user, (char *)NULL);
99f7167e0eSDag-Erling Smørgrav 	free(tmp);
100f7167e0eSDag-Erling Smørgrav 	return ret;
101f7167e0eSDag-Erling Smørgrav }
102f7167e0eSDag-Erling Smørgrav 
103f7167e0eSDag-Erling Smørgrav /*
104f7167e0eSDag-Erling Smørgrav  * Connect to the given ssh server using a proxy command that passes a
105f7167e0eSDag-Erling Smørgrav  * a connected fd back to us.
106f7167e0eSDag-Erling Smørgrav  */
107f7167e0eSDag-Erling Smørgrav static int
108f7167e0eSDag-Erling Smørgrav ssh_proxy_fdpass_connect(const char *host, u_short port,
109f7167e0eSDag-Erling Smørgrav     const char *proxy_command)
110f7167e0eSDag-Erling Smørgrav {
111f7167e0eSDag-Erling Smørgrav 	char *command_string;
112f7167e0eSDag-Erling Smørgrav 	int sp[2], sock;
113f7167e0eSDag-Erling Smørgrav 	pid_t pid;
114f7167e0eSDag-Erling Smørgrav 	char *shell;
115f7167e0eSDag-Erling Smørgrav 
116f7167e0eSDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL)
117f7167e0eSDag-Erling Smørgrav 		shell = _PATH_BSHELL;
118f7167e0eSDag-Erling Smørgrav 
119f7167e0eSDag-Erling Smørgrav 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
120f7167e0eSDag-Erling Smørgrav 		fatal("Could not create socketpair to communicate with "
121f7167e0eSDag-Erling Smørgrav 		    "proxy dialer: %.100s", strerror(errno));
122f7167e0eSDag-Erling Smørgrav 
123f7167e0eSDag-Erling Smørgrav 	command_string = expand_proxy_command(proxy_command, options.user,
124f7167e0eSDag-Erling Smørgrav 	    host, port);
125f7167e0eSDag-Erling Smørgrav 	debug("Executing proxy dialer command: %.500s", command_string);
126f7167e0eSDag-Erling Smørgrav 
127f7167e0eSDag-Erling Smørgrav 	/* Fork and execute the proxy command. */
128f7167e0eSDag-Erling Smørgrav 	if ((pid = fork()) == 0) {
129f7167e0eSDag-Erling Smørgrav 		char *argv[10];
130f7167e0eSDag-Erling Smørgrav 
131f7167e0eSDag-Erling Smørgrav 		/* Child.  Permanently give up superuser privileges. */
132f7167e0eSDag-Erling Smørgrav 		permanently_drop_suid(original_real_uid);
133f7167e0eSDag-Erling Smørgrav 
134f7167e0eSDag-Erling Smørgrav 		close(sp[1]);
135f7167e0eSDag-Erling Smørgrav 		/* Redirect stdin and stdout. */
136f7167e0eSDag-Erling Smørgrav 		if (sp[0] != 0) {
137f7167e0eSDag-Erling Smørgrav 			if (dup2(sp[0], 0) < 0)
138f7167e0eSDag-Erling Smørgrav 				perror("dup2 stdin");
139f7167e0eSDag-Erling Smørgrav 		}
140f7167e0eSDag-Erling Smørgrav 		if (sp[0] != 1) {
141f7167e0eSDag-Erling Smørgrav 			if (dup2(sp[0], 1) < 0)
142f7167e0eSDag-Erling Smørgrav 				perror("dup2 stdout");
143f7167e0eSDag-Erling Smørgrav 		}
144f7167e0eSDag-Erling Smørgrav 		if (sp[0] >= 2)
145f7167e0eSDag-Erling Smørgrav 			close(sp[0]);
146f7167e0eSDag-Erling Smørgrav 
147f7167e0eSDag-Erling Smørgrav 		/*
148f7167e0eSDag-Erling Smørgrav 		 * Stderr is left as it is so that error messages get
149f7167e0eSDag-Erling Smørgrav 		 * printed on the user's terminal.
150f7167e0eSDag-Erling Smørgrav 		 */
151f7167e0eSDag-Erling Smørgrav 		argv[0] = shell;
152f7167e0eSDag-Erling Smørgrav 		argv[1] = "-c";
153f7167e0eSDag-Erling Smørgrav 		argv[2] = command_string;
154f7167e0eSDag-Erling Smørgrav 		argv[3] = NULL;
155f7167e0eSDag-Erling Smørgrav 
156f7167e0eSDag-Erling Smørgrav 		/*
157f7167e0eSDag-Erling Smørgrav 		 * Execute the proxy command.
158f7167e0eSDag-Erling Smørgrav 		 * Note that we gave up any extra privileges above.
159f7167e0eSDag-Erling Smørgrav 		 */
160f7167e0eSDag-Erling Smørgrav 		execv(argv[0], argv);
161f7167e0eSDag-Erling Smørgrav 		perror(argv[0]);
162f7167e0eSDag-Erling Smørgrav 		exit(1);
163f7167e0eSDag-Erling Smørgrav 	}
164f7167e0eSDag-Erling Smørgrav 	/* Parent. */
165f7167e0eSDag-Erling Smørgrav 	if (pid < 0)
166f7167e0eSDag-Erling Smørgrav 		fatal("fork failed: %.100s", strerror(errno));
167f7167e0eSDag-Erling Smørgrav 	close(sp[0]);
168f7167e0eSDag-Erling Smørgrav 	free(command_string);
169f7167e0eSDag-Erling Smørgrav 
170f7167e0eSDag-Erling Smørgrav 	if ((sock = mm_receive_fd(sp[1])) == -1)
171f7167e0eSDag-Erling Smørgrav 		fatal("proxy dialer did not pass back a connection");
172f7167e0eSDag-Erling Smørgrav 
173f7167e0eSDag-Erling Smørgrav 	while (waitpid(pid, NULL, 0) == -1)
174f7167e0eSDag-Erling Smørgrav 		if (errno != EINTR)
175f7167e0eSDag-Erling Smørgrav 			fatal("Couldn't wait for child: %s", strerror(errno));
176f7167e0eSDag-Erling Smørgrav 
177f7167e0eSDag-Erling Smørgrav 	/* Set the connection file descriptors. */
178f7167e0eSDag-Erling Smørgrav 	packet_set_connection(sock, sock);
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
18780628bacSDag-Erling Smørgrav ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
188511b41d2SMark Murray {
189f7167e0eSDag-Erling Smørgrav 	char *command_string;
190511b41d2SMark Murray 	int pin[2], pout[2];
191e8aafc91SKris Kennaway 	pid_t pid;
192f7167e0eSDag-Erling Smørgrav 	char *shell;
193420bce64SDag-Erling Smørgrav 
1944a421b63SDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
195d4af9e69SDag-Erling Smørgrav 		shell = _PATH_BSHELL;
196511b41d2SMark Murray 
197511b41d2SMark Murray 	/* Create pipes for communicating with the proxy. */
198511b41d2SMark Murray 	if (pipe(pin) < 0 || pipe(pout) < 0)
199511b41d2SMark Murray 		fatal("Could not create pipes to communicate with the proxy: %.100s",
200511b41d2SMark Murray 		    strerror(errno));
201511b41d2SMark Murray 
202f7167e0eSDag-Erling Smørgrav 	command_string = expand_proxy_command(proxy_command, options.user,
203f7167e0eSDag-Erling Smørgrav 	    host, port);
204511b41d2SMark Murray 	debug("Executing proxy command: %.500s", command_string);
205511b41d2SMark Murray 
206511b41d2SMark Murray 	/* Fork and execute the proxy command. */
207511b41d2SMark Murray 	if ((pid = fork()) == 0) {
208511b41d2SMark Murray 		char *argv[10];
209511b41d2SMark Murray 
210511b41d2SMark Murray 		/* Child.  Permanently give up superuser privileges. */
211333ee039SDag-Erling Smørgrav 		permanently_drop_suid(original_real_uid);
212511b41d2SMark Murray 
213511b41d2SMark Murray 		/* Redirect stdin and stdout. */
214511b41d2SMark Murray 		close(pin[1]);
215511b41d2SMark Murray 		if (pin[0] != 0) {
216511b41d2SMark Murray 			if (dup2(pin[0], 0) < 0)
217511b41d2SMark Murray 				perror("dup2 stdin");
218511b41d2SMark Murray 			close(pin[0]);
219511b41d2SMark Murray 		}
220511b41d2SMark Murray 		close(pout[0]);
221511b41d2SMark Murray 		if (dup2(pout[1], 1) < 0)
222511b41d2SMark Murray 			perror("dup2 stdout");
223511b41d2SMark Murray 		/* Cannot be 1 because pin allocated two descriptors. */
224511b41d2SMark Murray 		close(pout[1]);
225511b41d2SMark Murray 
226511b41d2SMark Murray 		/* Stderr is left as it is so that error messages get
227511b41d2SMark Murray 		   printed on the user's terminal. */
228d4af9e69SDag-Erling Smørgrav 		argv[0] = shell;
229511b41d2SMark Murray 		argv[1] = "-c";
230511b41d2SMark Murray 		argv[2] = command_string;
231511b41d2SMark Murray 		argv[3] = NULL;
232511b41d2SMark Murray 
233511b41d2SMark Murray 		/* Execute the proxy command.  Note that we gave up any
234511b41d2SMark Murray 		   extra privileges above. */
2354a421b63SDag-Erling Smørgrav 		signal(SIGPIPE, SIG_DFL);
236ca3176e7SBrian Feldman 		execv(argv[0], argv);
237ca3176e7SBrian Feldman 		perror(argv[0]);
238511b41d2SMark Murray 		exit(1);
239511b41d2SMark Murray 	}
240511b41d2SMark Murray 	/* Parent. */
241511b41d2SMark Murray 	if (pid < 0)
242511b41d2SMark Murray 		fatal("fork failed: %.100s", strerror(errno));
243f388f5efSDag-Erling Smørgrav 	else
244f388f5efSDag-Erling Smørgrav 		proxy_command_pid = pid; /* save pid to clean up later */
245511b41d2SMark Murray 
246511b41d2SMark Murray 	/* Close child side of the descriptors. */
247511b41d2SMark Murray 	close(pin[0]);
248511b41d2SMark Murray 	close(pout[1]);
249511b41d2SMark Murray 
250511b41d2SMark Murray 	/* Free the command name. */
251e4a9863fSDag-Erling Smørgrav 	free(command_string);
252511b41d2SMark Murray 
253511b41d2SMark Murray 	/* Set the connection file descriptors. */
254511b41d2SMark Murray 	packet_set_connection(pout[0], pin[1]);
255511b41d2SMark Murray 
256af12a3e7SDag-Erling Smørgrav 	/* Indicate OK return */
257af12a3e7SDag-Erling Smørgrav 	return 0;
258511b41d2SMark Murray }
259511b41d2SMark Murray 
2604a421b63SDag-Erling Smørgrav void
2614a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(void)
2624a421b63SDag-Erling Smørgrav {
2634a421b63SDag-Erling Smørgrav 	/*
2644a421b63SDag-Erling Smørgrav 	 * Send SIGHUP to proxy command if used. We don't wait() in
2654a421b63SDag-Erling Smørgrav 	 * case it hangs and instead rely on init to reap the child
2664a421b63SDag-Erling Smørgrav 	 */
2674a421b63SDag-Erling Smørgrav 	if (proxy_command_pid > 1)
2684a421b63SDag-Erling Smørgrav 		kill(proxy_command_pid, SIGHUP);
2694a421b63SDag-Erling Smørgrav }
2704a421b63SDag-Erling Smørgrav 
271511b41d2SMark Murray /*
272511b41d2SMark Murray  * Creates a (possibly privileged) socket for use as the ssh connection.
273511b41d2SMark Murray  */
274af12a3e7SDag-Erling Smørgrav static int
275cf2b5f3bSDag-Erling Smørgrav ssh_create_socket(int privileged, struct addrinfo *ai)
276511b41d2SMark Murray {
277f7167e0eSDag-Erling Smørgrav 	int sock, r, gaierr;
278b83788ffSDag-Erling Smørgrav 	struct addrinfo hints, *res = NULL;
279511b41d2SMark Murray 
280cf2b5f3bSDag-Erling Smørgrav 	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
281b15c8340SDag-Erling Smørgrav 	if (sock < 0) {
282f7167e0eSDag-Erling Smørgrav 		error("socket: %s", strerror(errno));
283b15c8340SDag-Erling Smørgrav 		return -1;
284b15c8340SDag-Erling Smørgrav 	}
285b15c8340SDag-Erling Smørgrav 	fcntl(sock, F_SETFD, FD_CLOEXEC);
286af12a3e7SDag-Erling Smørgrav 
287af12a3e7SDag-Erling Smørgrav 	/* Bind the socket to an alternative local IP address */
288f7167e0eSDag-Erling Smørgrav 	if (options.bind_address == NULL && !privileged)
289af12a3e7SDag-Erling Smørgrav 		return sock;
290af12a3e7SDag-Erling Smørgrav 
291b83788ffSDag-Erling Smørgrav 	if (options.bind_address) {
292af12a3e7SDag-Erling Smørgrav 		memset(&hints, 0, sizeof(hints));
293cf2b5f3bSDag-Erling Smørgrav 		hints.ai_family = ai->ai_family;
294cf2b5f3bSDag-Erling Smørgrav 		hints.ai_socktype = ai->ai_socktype;
295cf2b5f3bSDag-Erling Smørgrav 		hints.ai_protocol = ai->ai_protocol;
296af12a3e7SDag-Erling Smørgrav 		hints.ai_flags = AI_PASSIVE;
297d4af9e69SDag-Erling Smørgrav 		gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
298af12a3e7SDag-Erling Smørgrav 		if (gaierr) {
299af12a3e7SDag-Erling Smørgrav 			error("getaddrinfo: %s: %s", options.bind_address,
300d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(gaierr));
301af12a3e7SDag-Erling Smørgrav 			close(sock);
302af12a3e7SDag-Erling Smørgrav 			return -1;
303511b41d2SMark Murray 		}
304b83788ffSDag-Erling Smørgrav 	}
305f7167e0eSDag-Erling Smørgrav 	/*
306f7167e0eSDag-Erling Smørgrav 	 * If we are running as root and want to connect to a privileged
307f7167e0eSDag-Erling Smørgrav 	 * port, bind our own socket to a privileged port.
308f7167e0eSDag-Erling Smørgrav 	 */
309f7167e0eSDag-Erling Smørgrav 	if (privileged) {
310f7167e0eSDag-Erling Smørgrav 		PRIV_START;
311b83788ffSDag-Erling Smørgrav 		r = bindresvport_sa(sock, res ? res->ai_addr : NULL);
312f7167e0eSDag-Erling Smørgrav 		PRIV_END;
313f7167e0eSDag-Erling Smørgrav 		if (r < 0) {
314f7167e0eSDag-Erling Smørgrav 			error("bindresvport_sa: af=%d %s", ai->ai_family,
315f7167e0eSDag-Erling Smørgrav 			    strerror(errno));
316f7167e0eSDag-Erling Smørgrav 			goto fail;
317f7167e0eSDag-Erling Smørgrav 		}
318f7167e0eSDag-Erling Smørgrav 	} else {
319af12a3e7SDag-Erling Smørgrav 		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
320f7167e0eSDag-Erling Smørgrav 			error("bind: %s: %s", options.bind_address,
321f7167e0eSDag-Erling Smørgrav 			    strerror(errno));
322f7167e0eSDag-Erling Smørgrav  fail:
323af12a3e7SDag-Erling Smørgrav 			close(sock);
324af12a3e7SDag-Erling Smørgrav 			freeaddrinfo(res);
325af12a3e7SDag-Erling Smørgrav 			return -1;
326af12a3e7SDag-Erling Smørgrav 		}
327f7167e0eSDag-Erling Smørgrav 	}
328b83788ffSDag-Erling Smørgrav 	if (res != NULL)
329af12a3e7SDag-Erling Smørgrav 		freeaddrinfo(res);
330511b41d2SMark Murray 	return sock;
331511b41d2SMark Murray }
332511b41d2SMark Murray 
333cf2b5f3bSDag-Erling Smørgrav static int
334cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr,
335d4af9e69SDag-Erling Smørgrav     socklen_t addrlen, int *timeoutp)
336cf2b5f3bSDag-Erling Smørgrav {
337cf2b5f3bSDag-Erling Smørgrav 	fd_set *fdset;
338d4af9e69SDag-Erling Smørgrav 	struct timeval tv, t_start;
339cf2b5f3bSDag-Erling Smørgrav 	socklen_t optlen;
340333ee039SDag-Erling Smørgrav 	int optval, rc, result = -1;
341cf2b5f3bSDag-Erling Smørgrav 
342d4af9e69SDag-Erling Smørgrav 	gettimeofday(&t_start, NULL);
343d4af9e69SDag-Erling Smørgrav 
344d4af9e69SDag-Erling Smørgrav 	if (*timeoutp <= 0) {
345d4af9e69SDag-Erling Smørgrav 		result = connect(sockfd, serv_addr, addrlen);
346d4af9e69SDag-Erling Smørgrav 		goto done;
347d4af9e69SDag-Erling Smørgrav 	}
348cf2b5f3bSDag-Erling Smørgrav 
3491ec0d754SDag-Erling Smørgrav 	set_nonblock(sockfd);
350cf2b5f3bSDag-Erling Smørgrav 	rc = connect(sockfd, serv_addr, addrlen);
3511ec0d754SDag-Erling Smørgrav 	if (rc == 0) {
3521ec0d754SDag-Erling Smørgrav 		unset_nonblock(sockfd);
353d4af9e69SDag-Erling Smørgrav 		result = 0;
354d4af9e69SDag-Erling Smørgrav 		goto done;
3551ec0d754SDag-Erling Smørgrav 	}
356d4af9e69SDag-Erling Smørgrav 	if (errno != EINPROGRESS) {
357d4af9e69SDag-Erling Smørgrav 		result = -1;
358d4af9e69SDag-Erling Smørgrav 		goto done;
359d4af9e69SDag-Erling Smørgrav 	}
360cf2b5f3bSDag-Erling Smørgrav 
361333ee039SDag-Erling Smørgrav 	fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS),
362333ee039SDag-Erling Smørgrav 	    sizeof(fd_mask));
363cf2b5f3bSDag-Erling Smørgrav 	FD_SET(sockfd, fdset);
364d4af9e69SDag-Erling Smørgrav 	ms_to_timeval(&tv, *timeoutp);
365cf2b5f3bSDag-Erling Smørgrav 
366cf2b5f3bSDag-Erling Smørgrav 	for (;;) {
367cf2b5f3bSDag-Erling Smørgrav 		rc = select(sockfd + 1, NULL, fdset, NULL, &tv);
368cf2b5f3bSDag-Erling Smørgrav 		if (rc != -1 || errno != EINTR)
369cf2b5f3bSDag-Erling Smørgrav 			break;
370cf2b5f3bSDag-Erling Smørgrav 	}
371cf2b5f3bSDag-Erling Smørgrav 
372cf2b5f3bSDag-Erling Smørgrav 	switch (rc) {
373cf2b5f3bSDag-Erling Smørgrav 	case 0:
374cf2b5f3bSDag-Erling Smørgrav 		/* Timed out */
375cf2b5f3bSDag-Erling Smørgrav 		errno = ETIMEDOUT;
376cf2b5f3bSDag-Erling Smørgrav 		break;
377cf2b5f3bSDag-Erling Smørgrav 	case -1:
378cf2b5f3bSDag-Erling Smørgrav 		/* Select error */
379cf2b5f3bSDag-Erling Smørgrav 		debug("select: %s", strerror(errno));
380cf2b5f3bSDag-Erling Smørgrav 		break;
381cf2b5f3bSDag-Erling Smørgrav 	case 1:
382cf2b5f3bSDag-Erling Smørgrav 		/* Completed or failed */
383cf2b5f3bSDag-Erling Smørgrav 		optval = 0;
384cf2b5f3bSDag-Erling Smørgrav 		optlen = sizeof(optval);
385cf2b5f3bSDag-Erling Smørgrav 		if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval,
386cf2b5f3bSDag-Erling Smørgrav 		    &optlen) == -1) {
387cf2b5f3bSDag-Erling Smørgrav 			debug("getsockopt: %s", strerror(errno));
388cf2b5f3bSDag-Erling Smørgrav 			break;
389cf2b5f3bSDag-Erling Smørgrav 		}
390cf2b5f3bSDag-Erling Smørgrav 		if (optval != 0) {
391cf2b5f3bSDag-Erling Smørgrav 			errno = optval;
392cf2b5f3bSDag-Erling Smørgrav 			break;
393cf2b5f3bSDag-Erling Smørgrav 		}
394cf2b5f3bSDag-Erling Smørgrav 		result = 0;
3951ec0d754SDag-Erling Smørgrav 		unset_nonblock(sockfd);
396cf2b5f3bSDag-Erling Smørgrav 		break;
397cf2b5f3bSDag-Erling Smørgrav 	default:
398cf2b5f3bSDag-Erling Smørgrav 		/* Should not occur */
399cf2b5f3bSDag-Erling Smørgrav 		fatal("Bogus return (%d) from select()", rc);
400cf2b5f3bSDag-Erling Smørgrav 	}
401cf2b5f3bSDag-Erling Smørgrav 
402e4a9863fSDag-Erling Smørgrav 	free(fdset);
403d4af9e69SDag-Erling Smørgrav 
404d4af9e69SDag-Erling Smørgrav  done:
405d4af9e69SDag-Erling Smørgrav  	if (result == 0 && *timeoutp > 0) {
406d4af9e69SDag-Erling Smørgrav 		ms_subtract_diff(&t_start, timeoutp);
407d4af9e69SDag-Erling Smørgrav 		if (*timeoutp <= 0) {
408d4af9e69SDag-Erling Smørgrav 			errno = ETIMEDOUT;
409d4af9e69SDag-Erling Smørgrav 			result = -1;
410d4af9e69SDag-Erling Smørgrav 		}
411d4af9e69SDag-Erling Smørgrav 	}
412d4af9e69SDag-Erling Smørgrav 
413cf2b5f3bSDag-Erling Smørgrav 	return (result);
414cf2b5f3bSDag-Erling Smørgrav }
415cf2b5f3bSDag-Erling Smørgrav 
416511b41d2SMark Murray /*
417511b41d2SMark Murray  * Opens a TCP/IP connection to the remote server on the given host.
418511b41d2SMark Murray  * The address of the remote host will be returned in hostaddr.
41980628bacSDag-Erling Smørgrav  * If port is 0, the default port will be used.  If needpriv is true,
420511b41d2SMark Murray  * a privileged port will be allocated to make the connection.
42180628bacSDag-Erling Smørgrav  * This requires super-user privileges if needpriv is true.
422511b41d2SMark Murray  * Connection_attempts specifies the maximum number of tries (one per
423511b41d2SMark Murray  * second).  If proxy_command is non-NULL, it specifies the command (with %h
424511b41d2SMark Murray  * and %p substituted for host and port, respectively) to use to contact
425511b41d2SMark Murray  * the daemon.
426511b41d2SMark Murray  */
427f7167e0eSDag-Erling Smørgrav static int
428f7167e0eSDag-Erling Smørgrav ssh_connect_direct(const char *host, struct addrinfo *aitop,
429f7167e0eSDag-Erling Smørgrav     struct sockaddr_storage *hostaddr, u_short port, int family,
430f7167e0eSDag-Erling Smørgrav     int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
431511b41d2SMark Murray {
432ca3176e7SBrian Feldman 	int on = 1;
433ca3176e7SBrian Feldman 	int sock = -1, attempt;
434ca3176e7SBrian Feldman 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
435f7167e0eSDag-Erling Smørgrav 	struct addrinfo *ai;
436511b41d2SMark Murray 
437e73e9afaSDag-Erling Smørgrav 	debug2("ssh_connect: needpriv %d", needpriv);
438511b41d2SMark Murray 
439333ee039SDag-Erling Smørgrav 	for (attempt = 0; attempt < connection_attempts; attempt++) {
44062efe23aSDag-Erling Smørgrav 		if (attempt > 0) {
44162efe23aSDag-Erling Smørgrav 			/* Sleep a moment before retrying. */
44262efe23aSDag-Erling Smørgrav 			sleep(1);
443511b41d2SMark Murray 			debug("Trying again...");
44462efe23aSDag-Erling Smørgrav 		}
445333ee039SDag-Erling Smørgrav 		/*
446333ee039SDag-Erling Smørgrav 		 * Loop through addresses for this host, and try each one in
447333ee039SDag-Erling Smørgrav 		 * sequence until the connection succeeds.
448333ee039SDag-Erling Smørgrav 		 */
449511b41d2SMark Murray 		for (ai = aitop; ai; ai = ai->ai_next) {
450f7167e0eSDag-Erling Smørgrav 			if (ai->ai_family != AF_INET &&
451f7167e0eSDag-Erling Smørgrav 			    ai->ai_family != AF_INET6)
452511b41d2SMark Murray 				continue;
453511b41d2SMark Murray 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
454511b41d2SMark Murray 			    ntop, sizeof(ntop), strport, sizeof(strport),
455511b41d2SMark Murray 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
456511b41d2SMark Murray 				error("ssh_connect: getnameinfo failed");
457511b41d2SMark Murray 				continue;
458511b41d2SMark Murray 			}
459511b41d2SMark Murray 			debug("Connecting to %.200s [%.100s] port %s.",
4601f5ce8f4SBrian Feldman 				host, ntop, strport);
461511b41d2SMark Murray 
462511b41d2SMark Murray 			/* Create a socket for connecting. */
463cf2b5f3bSDag-Erling Smørgrav 			sock = ssh_create_socket(needpriv, ai);
464511b41d2SMark Murray 			if (sock < 0)
465af12a3e7SDag-Erling Smørgrav 				/* Any error is already output */
466511b41d2SMark Murray 				continue;
467511b41d2SMark Murray 
468cf2b5f3bSDag-Erling Smørgrav 			if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
469d4af9e69SDag-Erling Smørgrav 			    timeout_ms) >= 0) {
470511b41d2SMark Murray 				/* Successful connection. */
471c322fe35SKris Kennaway 				memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
472511b41d2SMark Murray 				break;
473511b41d2SMark Murray 			} else {
474f388f5efSDag-Erling Smørgrav 				debug("connect to address %s port %s: %s",
475f388f5efSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
476511b41d2SMark Murray 				close(sock);
477333ee039SDag-Erling Smørgrav 				sock = -1;
478511b41d2SMark Murray 			}
479511b41d2SMark Murray 		}
480333ee039SDag-Erling Smørgrav 		if (sock != -1)
481511b41d2SMark Murray 			break;	/* Successful connection. */
482511b41d2SMark Murray 	}
483511b41d2SMark Murray 
484511b41d2SMark Murray 	/* Return failure if we didn't get a successful connection. */
485333ee039SDag-Erling Smørgrav 	if (sock == -1) {
486aa49c926SDag-Erling Smørgrav 		error("ssh: connect to host %s port %s: %s",
487f388f5efSDag-Erling Smørgrav 		    host, strport, strerror(errno));
488aa49c926SDag-Erling Smørgrav 		return (-1);
489f388f5efSDag-Erling Smørgrav 	}
490511b41d2SMark Murray 
491511b41d2SMark Murray 	debug("Connection established.");
492511b41d2SMark Murray 
4931ec0d754SDag-Erling Smørgrav 	/* Set SO_KEEPALIVE if requested. */
494d4af9e69SDag-Erling Smørgrav 	if (want_keepalive &&
495ca3176e7SBrian Feldman 	    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
496ca3176e7SBrian Feldman 	    sizeof(on)) < 0)
497ca3176e7SBrian Feldman 		error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
498ca3176e7SBrian Feldman 
499511b41d2SMark Murray 	/* Set the connection. */
500511b41d2SMark Murray 	packet_set_connection(sock, sock);
501511b41d2SMark Murray 
502af12a3e7SDag-Erling Smørgrav 	return 0;
503511b41d2SMark Murray }
504511b41d2SMark Murray 
505f7167e0eSDag-Erling Smørgrav int
506f7167e0eSDag-Erling Smørgrav ssh_connect(const char *host, struct addrinfo *addrs,
507f7167e0eSDag-Erling Smørgrav     struct sockaddr_storage *hostaddr, u_short port, int family,
508f7167e0eSDag-Erling Smørgrav     int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
509f7167e0eSDag-Erling Smørgrav {
510f7167e0eSDag-Erling Smørgrav 	if (options.proxy_command == NULL) {
511f7167e0eSDag-Erling Smørgrav 		return ssh_connect_direct(host, addrs, hostaddr, port, family,
512f7167e0eSDag-Erling Smørgrav 		    connection_attempts, timeout_ms, want_keepalive, needpriv);
513f7167e0eSDag-Erling Smørgrav 	} else if (strcmp(options.proxy_command, "-") == 0) {
514f7167e0eSDag-Erling Smørgrav 		packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
515f7167e0eSDag-Erling Smørgrav 		return 0; /* Always succeeds */
516f7167e0eSDag-Erling Smørgrav 	} else if (options.proxy_use_fdpass) {
517f7167e0eSDag-Erling Smørgrav 		return ssh_proxy_fdpass_connect(host, port,
518f7167e0eSDag-Erling Smørgrav 		    options.proxy_command);
519f7167e0eSDag-Erling Smørgrav 	}
520f7167e0eSDag-Erling Smørgrav 	return ssh_proxy_connect(host, port, options.proxy_command);
521f7167e0eSDag-Erling Smørgrav }
522f7167e0eSDag-Erling Smørgrav 
5236888a9beSDag-Erling Smørgrav static void
5246888a9beSDag-Erling Smørgrav send_client_banner(int connection_out, int minor1)
5256888a9beSDag-Erling Smørgrav {
5266888a9beSDag-Erling Smørgrav 	/* Send our own protocol version identification. */
52760c59fadSDag-Erling Smørgrav 	xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s%s",
5286888a9beSDag-Erling Smørgrav 	    compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
5296888a9beSDag-Erling Smørgrav 	    compat20 ? PROTOCOL_MINOR_2 : minor1,
53060c59fadSDag-Erling Smørgrav 	    SSH_VERSION,
5316888a9beSDag-Erling Smørgrav 	    *options.version_addendum == '\0' ? "" : " ",
5326888a9beSDag-Erling Smørgrav 	    options.version_addendum, compat20 ? "\r\n" : "\n");
5336888a9beSDag-Erling Smørgrav 	if (roaming_atomicio(vwrite, connection_out, client_version_string,
5346888a9beSDag-Erling Smørgrav 	    strlen(client_version_string)) != strlen(client_version_string))
5356888a9beSDag-Erling Smørgrav 		fatal("write: %.100s", strerror(errno));
5366888a9beSDag-Erling Smørgrav 	chop(client_version_string);
5376888a9beSDag-Erling Smørgrav 	debug("Local version string %.100s", client_version_string);
5386888a9beSDag-Erling Smørgrav }
5396888a9beSDag-Erling Smørgrav 
540511b41d2SMark Murray /*
541e8aafc91SKris Kennaway  * Waits for the server identification string, and sends our own
542e8aafc91SKris Kennaway  * identification string.
543511b41d2SMark Murray  */
5447aee6ffeSDag-Erling Smørgrav void
545d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(int timeout_ms)
546511b41d2SMark Murray {
547e8aafc91SKris Kennaway 	char buf[256], remote_version[256];	/* must be same size! */
548d4ecd108SDag-Erling Smørgrav 	int remote_major, remote_minor, mismatch;
549e8aafc91SKris Kennaway 	int connection_in = packet_get_connection_in();
550e8aafc91SKris Kennaway 	int connection_out = packet_get_connection_out();
5516888a9beSDag-Erling Smørgrav 	int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0;
552333ee039SDag-Erling Smørgrav 	u_int i, n;
553d4af9e69SDag-Erling Smørgrav 	size_t len;
554d4af9e69SDag-Erling Smørgrav 	int fdsetsz, remaining, rc;
555d4af9e69SDag-Erling Smørgrav 	struct timeval t_start, t_remaining;
556d4af9e69SDag-Erling Smørgrav 	fd_set *fdset;
557d4af9e69SDag-Erling Smørgrav 
558d4af9e69SDag-Erling Smørgrav 	fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask);
559d4af9e69SDag-Erling Smørgrav 	fdset = xcalloc(1, fdsetsz);
560511b41d2SMark Murray 
5616888a9beSDag-Erling Smørgrav 	/*
5626888a9beSDag-Erling Smørgrav 	 * If we are SSH2-only then we can send the banner immediately and
5636888a9beSDag-Erling Smørgrav 	 * save a round-trip.
5646888a9beSDag-Erling Smørgrav 	 */
5656888a9beSDag-Erling Smørgrav 	if (options.protocol == SSH_PROTO_2) {
5666888a9beSDag-Erling Smørgrav 		enable_compat20();
5676888a9beSDag-Erling Smørgrav 		send_client_banner(connection_out, 0);
5686888a9beSDag-Erling Smørgrav 		client_banner_sent = 1;
5696888a9beSDag-Erling Smørgrav 	}
5706888a9beSDag-Erling Smørgrav 
571d4ecd108SDag-Erling Smørgrav 	/* Read other side's version identification. */
572d4af9e69SDag-Erling Smørgrav 	remaining = timeout_ms;
573333ee039SDag-Erling Smørgrav 	for (n = 0;;) {
574e8aafc91SKris Kennaway 		for (i = 0; i < sizeof(buf) - 1; i++) {
575d4af9e69SDag-Erling Smørgrav 			if (timeout_ms > 0) {
576d4af9e69SDag-Erling Smørgrav 				gettimeofday(&t_start, NULL);
577d4af9e69SDag-Erling Smørgrav 				ms_to_timeval(&t_remaining, remaining);
578d4af9e69SDag-Erling Smørgrav 				FD_SET(connection_in, fdset);
579d4af9e69SDag-Erling Smørgrav 				rc = select(connection_in + 1, fdset, NULL,
580d4af9e69SDag-Erling Smørgrav 				    fdset, &t_remaining);
581d4af9e69SDag-Erling Smørgrav 				ms_subtract_diff(&t_start, &remaining);
582d4af9e69SDag-Erling Smørgrav 				if (rc == 0 || remaining <= 0)
583d4af9e69SDag-Erling Smørgrav 					fatal("Connection timed out during "
584d4af9e69SDag-Erling Smørgrav 					    "banner exchange");
585d4af9e69SDag-Erling Smørgrav 				if (rc == -1) {
586d4af9e69SDag-Erling Smørgrav 					if (errno == EINTR)
587d4af9e69SDag-Erling Smørgrav 						continue;
588d4af9e69SDag-Erling Smørgrav 					fatal("ssh_exchange_identification: "
589d4af9e69SDag-Erling Smørgrav 					    "select: %s", strerror(errno));
590d4af9e69SDag-Erling Smørgrav 				}
591d4af9e69SDag-Erling Smørgrav 			}
592d4af9e69SDag-Erling Smørgrav 
5937aee6ffeSDag-Erling Smørgrav 			len = roaming_atomicio(read, connection_in, &buf[i], 1);
594d4ecd108SDag-Erling Smørgrav 
595d4ecd108SDag-Erling Smørgrav 			if (len != 1 && errno == EPIPE)
596d4af9e69SDag-Erling Smørgrav 				fatal("ssh_exchange_identification: "
597d4af9e69SDag-Erling Smørgrav 				    "Connection closed by remote host");
598d4ecd108SDag-Erling Smørgrav 			else if (len != 1)
599d4af9e69SDag-Erling Smørgrav 				fatal("ssh_exchange_identification: "
600d4af9e69SDag-Erling Smørgrav 				    "read: %.100s", strerror(errno));
601e8aafc91SKris Kennaway 			if (buf[i] == '\r') {
602e8aafc91SKris Kennaway 				buf[i] = '\n';
603e8aafc91SKris Kennaway 				buf[i + 1] = 0;
604e8aafc91SKris Kennaway 				continue;		/**XXX wait for \n */
605511b41d2SMark Murray 			}
606e8aafc91SKris Kennaway 			if (buf[i] == '\n') {
607e8aafc91SKris Kennaway 				buf[i + 1] = 0;
608511b41d2SMark Murray 				break;
609e8aafc91SKris Kennaway 			}
610333ee039SDag-Erling Smørgrav 			if (++n > 65536)
611d4af9e69SDag-Erling Smørgrav 				fatal("ssh_exchange_identification: "
612d4af9e69SDag-Erling Smørgrav 				    "No banner received");
613e8aafc91SKris Kennaway 		}
614e8aafc91SKris Kennaway 		buf[sizeof(buf) - 1] = 0;
615c2d3a559SKris Kennaway 		if (strncmp(buf, "SSH-", 4) == 0)
616c2d3a559SKris Kennaway 			break;
617c2d3a559SKris Kennaway 		debug("ssh_exchange_identification: %s", buf);
618c2d3a559SKris Kennaway 	}
619e8aafc91SKris Kennaway 	server_version_string = xstrdup(buf);
620e4a9863fSDag-Erling Smørgrav 	free(fdset);
621511b41d2SMark Murray 
622511b41d2SMark Murray 	/*
623e8aafc91SKris Kennaway 	 * Check that the versions match.  In future this might accept
624e8aafc91SKris Kennaway 	 * several versions and set appropriate flags to handle them.
625511b41d2SMark Murray 	 */
626e8aafc91SKris Kennaway 	if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
627e8aafc91SKris Kennaway 	    &remote_major, &remote_minor, remote_version) != 3)
628e8aafc91SKris Kennaway 		fatal("Bad remote protocol version identification: '%.100s'", buf);
629e8aafc91SKris Kennaway 	debug("Remote protocol version %d.%d, remote software version %.100s",
630e8aafc91SKris Kennaway 	    remote_major, remote_minor, remote_version);
631511b41d2SMark Murray 
632bc5531deSDag-Erling Smørgrav 	active_state->compat = compat_datafellows(remote_version);
633e8aafc91SKris Kennaway 	mismatch = 0;
634e8aafc91SKris Kennaway 
635e8aafc91SKris Kennaway 	switch (remote_major) {
636e8aafc91SKris Kennaway 	case 1:
637e8aafc91SKris Kennaway 		if (remote_minor == 99 &&
638e8aafc91SKris Kennaway 		    (options.protocol & SSH_PROTO_2) &&
639e8aafc91SKris Kennaway 		    !(options.protocol & SSH_PROTO_1_PREFERRED)) {
640e8aafc91SKris Kennaway 			enable_compat20();
641511b41d2SMark Murray 			break;
642e8aafc91SKris Kennaway 		}
643e8aafc91SKris Kennaway 		if (!(options.protocol & SSH_PROTO_1)) {
644e8aafc91SKris Kennaway 			mismatch = 1;
645e8aafc91SKris Kennaway 			break;
646e8aafc91SKris Kennaway 		}
647e8aafc91SKris Kennaway 		if (remote_minor < 3) {
648e8aafc91SKris Kennaway 			fatal("Remote machine has too old SSH software version.");
649ca3176e7SBrian Feldman 		} else if (remote_minor == 3 || remote_minor == 4) {
650e8aafc91SKris Kennaway 			/* We speak 1.3, too. */
651e8aafc91SKris Kennaway 			enable_compat13();
652ca3176e7SBrian Feldman 			minor1 = 3;
653e8aafc91SKris Kennaway 			if (options.forward_agent) {
654cf2b5f3bSDag-Erling Smørgrav 				logit("Agent forwarding disabled for protocol 1.3");
655e8aafc91SKris Kennaway 				options.forward_agent = 0;
656e8aafc91SKris Kennaway 			}
657e8aafc91SKris Kennaway 		}
658e8aafc91SKris Kennaway 		break;
659e8aafc91SKris Kennaway 	case 2:
660e8aafc91SKris Kennaway 		if (options.protocol & SSH_PROTO_2) {
661e8aafc91SKris Kennaway 			enable_compat20();
662e8aafc91SKris Kennaway 			break;
663e8aafc91SKris Kennaway 		}
664e8aafc91SKris Kennaway 		/* FALLTHROUGH */
665511b41d2SMark Murray 	default:
666e8aafc91SKris Kennaway 		mismatch = 1;
667e8aafc91SKris Kennaway 		break;
668511b41d2SMark Murray 	}
669e8aafc91SKris Kennaway 	if (mismatch)
670e8aafc91SKris Kennaway 		fatal("Protocol major versions differ: %d vs. %d",
671e8aafc91SKris Kennaway 		    (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
672e8aafc91SKris Kennaway 		    remote_major);
673f7167e0eSDag-Erling Smørgrav 	if ((datafellows & SSH_BUG_DERIVEKEY) != 0)
674f7167e0eSDag-Erling Smørgrav 		fatal("Server version \"%.100s\" uses unsafe key agreement; "
675f7167e0eSDag-Erling Smørgrav 		    "refusing connection", remote_version);
676f7167e0eSDag-Erling Smørgrav 	if ((datafellows & SSH_BUG_RSASIGMD5) != 0)
677f7167e0eSDag-Erling Smørgrav 		logit("Server version \"%.100s\" uses unsafe RSA signature "
678f7167e0eSDag-Erling Smørgrav 		    "scheme; disabling use of RSA keys", remote_version);
6796888a9beSDag-Erling Smørgrav 	if (!client_banner_sent)
6806888a9beSDag-Erling Smørgrav 		send_client_banner(connection_out, minor1);
681e8aafc91SKris Kennaway 	chop(server_version_string);
682511b41d2SMark Murray }
683511b41d2SMark Murray 
684ca3176e7SBrian Feldman /* defaults to 'no' */
685af12a3e7SDag-Erling Smørgrav static int
686af12a3e7SDag-Erling Smørgrav confirm(const char *prompt)
687511b41d2SMark Murray {
688af12a3e7SDag-Erling Smørgrav 	const char *msg, *again = "Please type 'yes' or 'no': ";
689af12a3e7SDag-Erling Smørgrav 	char *p;
690af12a3e7SDag-Erling Smørgrav 	int ret = -1;
691511b41d2SMark Murray 
692ca3176e7SBrian Feldman 	if (options.batch_mode)
693ca3176e7SBrian Feldman 		return 0;
694af12a3e7SDag-Erling Smørgrav 	for (msg = prompt;;msg = again) {
695af12a3e7SDag-Erling Smørgrav 		p = read_passphrase(msg, RP_ECHO);
696af12a3e7SDag-Erling Smørgrav 		if (p == NULL ||
697af12a3e7SDag-Erling Smørgrav 		    (p[0] == '\0') || (p[0] == '\n') ||
698af12a3e7SDag-Erling Smørgrav 		    strncasecmp(p, "no", 2) == 0)
699af12a3e7SDag-Erling Smørgrav 			ret = 0;
700f388f5efSDag-Erling Smørgrav 		if (p && strncasecmp(p, "yes", 3) == 0)
701af12a3e7SDag-Erling Smørgrav 			ret = 1;
702e4a9863fSDag-Erling Smørgrav 		free(p);
703af12a3e7SDag-Erling Smørgrav 		if (ret != -1)
704af12a3e7SDag-Erling Smørgrav 			return ret;
705511b41d2SMark Murray 	}
706511b41d2SMark Murray }
707511b41d2SMark Murray 
708b15c8340SDag-Erling Smørgrav static int
709b15c8340SDag-Erling Smørgrav check_host_cert(const char *host, const Key *host_key)
710b15c8340SDag-Erling Smørgrav {
711b15c8340SDag-Erling Smørgrav 	const char *reason;
712b15c8340SDag-Erling Smørgrav 
713b15c8340SDag-Erling Smørgrav 	if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) {
714b15c8340SDag-Erling Smørgrav 		error("%s", reason);
715b15c8340SDag-Erling Smørgrav 		return 0;
716b15c8340SDag-Erling Smørgrav 	}
717a0ee8cc6SDag-Erling Smørgrav 	if (buffer_len(host_key->cert->critical) != 0) {
718e2f6069cSDag-Erling Smørgrav 		error("Certificate for %s contains unsupported "
719e2f6069cSDag-Erling Smørgrav 		    "critical options(s)", host);
720b15c8340SDag-Erling Smørgrav 		return 0;
721b15c8340SDag-Erling Smørgrav 	}
722b15c8340SDag-Erling Smørgrav 	return 1;
723b15c8340SDag-Erling Smørgrav }
724b15c8340SDag-Erling Smørgrav 
7254a421b63SDag-Erling Smørgrav static int
7264a421b63SDag-Erling Smørgrav sockaddr_is_local(struct sockaddr *hostaddr)
7274a421b63SDag-Erling Smørgrav {
7284a421b63SDag-Erling Smørgrav 	switch (hostaddr->sa_family) {
7294a421b63SDag-Erling Smørgrav 	case AF_INET:
7304a421b63SDag-Erling Smørgrav 		return (ntohl(((struct sockaddr_in *)hostaddr)->
7314a421b63SDag-Erling Smørgrav 		    sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
7324a421b63SDag-Erling Smørgrav 	case AF_INET6:
7334a421b63SDag-Erling Smørgrav 		return IN6_IS_ADDR_LOOPBACK(
7344a421b63SDag-Erling Smørgrav 		    &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
7354a421b63SDag-Erling Smørgrav 	default:
7364a421b63SDag-Erling Smørgrav 		return 0;
7374a421b63SDag-Erling Smørgrav 	}
7384a421b63SDag-Erling Smørgrav }
7394a421b63SDag-Erling Smørgrav 
7404a421b63SDag-Erling Smørgrav /*
7414a421b63SDag-Erling Smørgrav  * Prepare the hostname and ip address strings that are used to lookup
7424a421b63SDag-Erling Smørgrav  * host keys in known_hosts files. These may have a port number appended.
7434a421b63SDag-Erling Smørgrav  */
7444a421b63SDag-Erling Smørgrav void
7454a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr,
7464a421b63SDag-Erling Smørgrav     u_short port, char **hostfile_hostname, char **hostfile_ipaddr)
7474a421b63SDag-Erling Smørgrav {
7484a421b63SDag-Erling Smørgrav 	char ntop[NI_MAXHOST];
7494a421b63SDag-Erling Smørgrav 	socklen_t addrlen;
7504a421b63SDag-Erling Smørgrav 
7514a421b63SDag-Erling Smørgrav 	switch (hostaddr == NULL ? -1 : hostaddr->sa_family) {
7524a421b63SDag-Erling Smørgrav 	case -1:
7534a421b63SDag-Erling Smørgrav 		addrlen = 0;
7544a421b63SDag-Erling Smørgrav 		break;
7554a421b63SDag-Erling Smørgrav 	case AF_INET:
7564a421b63SDag-Erling Smørgrav 		addrlen = sizeof(struct sockaddr_in);
7574a421b63SDag-Erling Smørgrav 		break;
7584a421b63SDag-Erling Smørgrav 	case AF_INET6:
7594a421b63SDag-Erling Smørgrav 		addrlen = sizeof(struct sockaddr_in6);
7604a421b63SDag-Erling Smørgrav 		break;
7614a421b63SDag-Erling Smørgrav 	default:
7624a421b63SDag-Erling Smørgrav 		addrlen = sizeof(struct sockaddr);
7634a421b63SDag-Erling Smørgrav 		break;
7644a421b63SDag-Erling Smørgrav 	}
7654a421b63SDag-Erling Smørgrav 
7664a421b63SDag-Erling Smørgrav 	/*
7674a421b63SDag-Erling Smørgrav 	 * We don't have the remote ip-address for connections
7684a421b63SDag-Erling Smørgrav 	 * using a proxy command
7694a421b63SDag-Erling Smørgrav 	 */
7704a421b63SDag-Erling Smørgrav 	if (hostfile_ipaddr != NULL) {
7714a421b63SDag-Erling Smørgrav 		if (options.proxy_command == NULL) {
7724a421b63SDag-Erling Smørgrav 			if (getnameinfo(hostaddr, addrlen,
7734a421b63SDag-Erling Smørgrav 			    ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0)
774bc5531deSDag-Erling Smørgrav 			fatal("%s: getnameinfo failed", __func__);
7754a421b63SDag-Erling Smørgrav 			*hostfile_ipaddr = put_host_port(ntop, port);
7764a421b63SDag-Erling Smørgrav 		} else {
7774a421b63SDag-Erling Smørgrav 			*hostfile_ipaddr = xstrdup("<no hostip for proxy "
7784a421b63SDag-Erling Smørgrav 			    "command>");
7794a421b63SDag-Erling Smørgrav 		}
7804a421b63SDag-Erling Smørgrav 	}
7814a421b63SDag-Erling Smørgrav 
7824a421b63SDag-Erling Smørgrav 	/*
7834a421b63SDag-Erling Smørgrav 	 * Allow the user to record the key under a different name or
7844a421b63SDag-Erling Smørgrav 	 * differentiate a non-standard port.  This is useful for ssh
7854a421b63SDag-Erling Smørgrav 	 * tunneling over forwarded connections or if you run multiple
7864a421b63SDag-Erling Smørgrav 	 * sshd's on different ports on the same machine.
7874a421b63SDag-Erling Smørgrav 	 */
7884a421b63SDag-Erling Smørgrav 	if (hostfile_hostname != NULL) {
7894a421b63SDag-Erling Smørgrav 		if (options.host_key_alias != NULL) {
7904a421b63SDag-Erling Smørgrav 			*hostfile_hostname = xstrdup(options.host_key_alias);
7914a421b63SDag-Erling Smørgrav 			debug("using hostkeyalias: %s", *hostfile_hostname);
7924a421b63SDag-Erling Smørgrav 		} else {
7934a421b63SDag-Erling Smørgrav 			*hostfile_hostname = put_host_port(hostname, port);
7944a421b63SDag-Erling Smørgrav 		}
7954a421b63SDag-Erling Smørgrav 	}
7964a421b63SDag-Erling Smørgrav }
7974a421b63SDag-Erling Smørgrav 
798e8aafc91SKris Kennaway /*
799af12a3e7SDag-Erling Smørgrav  * check whether the supplied host key is valid, return -1 if the key
800e146993eSDag-Erling Smørgrav  * is not valid. user_hostfile[0] will not be updated if 'readonly' is true.
801e8aafc91SKris Kennaway  */
802333ee039SDag-Erling Smørgrav #define RDRW	0
803333ee039SDag-Erling Smørgrav #define RDONLY	1
804333ee039SDag-Erling Smørgrav #define ROQUIET	2
805af12a3e7SDag-Erling Smørgrav static int
806333ee039SDag-Erling Smørgrav check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
807e146993eSDag-Erling Smørgrav     Key *host_key, int readonly,
808e146993eSDag-Erling Smørgrav     char **user_hostfiles, u_int num_user_hostfiles,
809e146993eSDag-Erling Smørgrav     char **system_hostfiles, u_int num_system_hostfiles)
810511b41d2SMark Murray {
811e8aafc91SKris Kennaway 	HostStatus host_status;
812e8aafc91SKris Kennaway 	HostStatus ip_status;
813e146993eSDag-Erling Smørgrav 	Key *raw_key = NULL;
814e146993eSDag-Erling Smørgrav 	char *ip = NULL, *host = NULL;
815e146993eSDag-Erling Smørgrav 	char hostline[1000], *hostp, *fp, *ra;
816af12a3e7SDag-Erling Smørgrav 	char msg[1024];
817e146993eSDag-Erling Smørgrav 	const char *type;
8184a421b63SDag-Erling Smørgrav 	const struct hostkey_entry *host_found, *ip_found;
819e146993eSDag-Erling Smørgrav 	int len, cancelled_forwarding = 0;
820e146993eSDag-Erling Smørgrav 	int local = sockaddr_is_local(hostaddr);
821e146993eSDag-Erling Smørgrav 	int r, want_cert = key_is_cert(host_key), host_ip_differ = 0;
822bc5531deSDag-Erling Smørgrav 	int hostkey_trusted = 0; /* Known or explicitly accepted by user */
823e146993eSDag-Erling Smørgrav 	struct hostkeys *host_hostkeys, *ip_hostkeys;
824e146993eSDag-Erling Smørgrav 	u_int i;
825511b41d2SMark Murray 
826e8aafc91SKris Kennaway 	/*
827e8aafc91SKris Kennaway 	 * Force accepting of the host key for loopback/localhost. The
828e8aafc91SKris Kennaway 	 * problem is that if the home directory is NFS-mounted to multiple
829e8aafc91SKris Kennaway 	 * machines, localhost will refer to a different machine in each of
830e8aafc91SKris Kennaway 	 * them, and the user will get bogus HOST_CHANGED warnings.  This
831e8aafc91SKris Kennaway 	 * essentially disables host authentication for localhost; however,
832e8aafc91SKris Kennaway 	 * this is probably not a real problem.
833e8aafc91SKris Kennaway 	 */
834af12a3e7SDag-Erling Smørgrav 	if (options.no_host_authentication_for_localhost == 1 && local &&
835af12a3e7SDag-Erling Smørgrav 	    options.host_key_alias == NULL) {
836ca3176e7SBrian Feldman 		debug("Forcing accepting of host key for "
837ca3176e7SBrian Feldman 		    "loopback/localhost.");
838af12a3e7SDag-Erling Smørgrav 		return 0;
839511b41d2SMark Murray 	}
840511b41d2SMark Murray 
841e8aafc91SKris Kennaway 	/*
8424a421b63SDag-Erling Smørgrav 	 * Prepare the hostname and address strings used for hostkey lookup.
8434a421b63SDag-Erling Smørgrav 	 * In some cases, these will have a port number appended.
844e8aafc91SKris Kennaway 	 */
8454a421b63SDag-Erling Smørgrav 	get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip);
846d4af9e69SDag-Erling Smørgrav 
847ca3176e7SBrian Feldman 	/*
848ca3176e7SBrian Feldman 	 * Turn off check_host_ip if the connection is to localhost, via proxy
849ca3176e7SBrian Feldman 	 * command or if we don't have a hostname to compare with
850ca3176e7SBrian Feldman 	 */
851333ee039SDag-Erling Smørgrav 	if (options.check_host_ip && (local ||
852333ee039SDag-Erling Smørgrav 	    strcmp(hostname, ip) == 0 || options.proxy_command != NULL))
853ca3176e7SBrian Feldman 		options.check_host_ip = 0;
854ca3176e7SBrian Feldman 
8554a421b63SDag-Erling Smørgrav 	host_hostkeys = init_hostkeys();
856e146993eSDag-Erling Smørgrav 	for (i = 0; i < num_user_hostfiles; i++)
857e146993eSDag-Erling Smørgrav 		load_hostkeys(host_hostkeys, host, user_hostfiles[i]);
858e146993eSDag-Erling Smørgrav 	for (i = 0; i < num_system_hostfiles; i++)
859e146993eSDag-Erling Smørgrav 		load_hostkeys(host_hostkeys, host, system_hostfiles[i]);
8604a421b63SDag-Erling Smørgrav 
8614a421b63SDag-Erling Smørgrav 	ip_hostkeys = NULL;
8624a421b63SDag-Erling Smørgrav 	if (!want_cert && options.check_host_ip) {
8634a421b63SDag-Erling Smørgrav 		ip_hostkeys = init_hostkeys();
864e146993eSDag-Erling Smørgrav 		for (i = 0; i < num_user_hostfiles; i++)
865e146993eSDag-Erling Smørgrav 			load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]);
866e146993eSDag-Erling Smørgrav 		for (i = 0; i < num_system_hostfiles; i++)
867e146993eSDag-Erling Smørgrav 			load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]);
868e8aafc91SKris Kennaway 	}
869e8aafc91SKris Kennaway 
870b15c8340SDag-Erling Smørgrav  retry:
8714a421b63SDag-Erling Smørgrav 	/* Reload these as they may have changed on cert->key downgrade */
872b15c8340SDag-Erling Smørgrav 	want_cert = key_is_cert(host_key);
873b15c8340SDag-Erling Smørgrav 	type = key_type(host_key);
874b15c8340SDag-Erling Smørgrav 
875e8aafc91SKris Kennaway 	/*
876b74df5b2SDag-Erling Smørgrav 	 * Check if the host key is present in the user's list of known
877e8aafc91SKris Kennaway 	 * hosts or in the systemwide list.
878e8aafc91SKris Kennaway 	 */
8794a421b63SDag-Erling Smørgrav 	host_status = check_key_in_hostkeys(host_hostkeys, host_key,
8804a421b63SDag-Erling Smørgrav 	    &host_found);
8814a421b63SDag-Erling Smørgrav 
882e8aafc91SKris Kennaway 	/*
883e8aafc91SKris Kennaway 	 * Also perform check for the ip address, skip the check if we are
884b15c8340SDag-Erling Smørgrav 	 * localhost, looking for a certificate, or the hostname was an ip
885b15c8340SDag-Erling Smørgrav 	 * address to begin with.
886e8aafc91SKris Kennaway 	 */
8874a421b63SDag-Erling Smørgrav 	if (!want_cert && ip_hostkeys != NULL) {
8884a421b63SDag-Erling Smørgrav 		ip_status = check_key_in_hostkeys(ip_hostkeys, host_key,
8894a421b63SDag-Erling Smørgrav 		    &ip_found);
890e8aafc91SKris Kennaway 		if (host_status == HOST_CHANGED &&
8914a421b63SDag-Erling Smørgrav 		    (ip_status != HOST_CHANGED ||
8924a421b63SDag-Erling Smørgrav 		    (ip_found != NULL &&
8934a421b63SDag-Erling Smørgrav 		    !key_equal(ip_found->key, host_found->key))))
894e8aafc91SKris Kennaway 			host_ip_differ = 1;
895e8aafc91SKris Kennaway 	} else
896e8aafc91SKris Kennaway 		ip_status = host_status;
897e8aafc91SKris Kennaway 
898e8aafc91SKris Kennaway 	switch (host_status) {
899e8aafc91SKris Kennaway 	case HOST_OK:
900e8aafc91SKris Kennaway 		/* The host is known and the key matches. */
901b15c8340SDag-Erling Smørgrav 		debug("Host '%.200s' is known and matches the %s host %s.",
902b15c8340SDag-Erling Smørgrav 		    host, type, want_cert ? "certificate" : "key");
9034a421b63SDag-Erling Smørgrav 		debug("Found %s in %s:%lu", want_cert ? "CA key" : "key",
9044a421b63SDag-Erling Smørgrav 		    host_found->file, host_found->line);
905b15c8340SDag-Erling Smørgrav 		if (want_cert && !check_host_cert(hostname, host_key))
906b15c8340SDag-Erling Smørgrav 			goto fail;
907ca3176e7SBrian Feldman 		if (options.check_host_ip && ip_status == HOST_NEW) {
908b15c8340SDag-Erling Smørgrav 			if (readonly || want_cert)
909cf2b5f3bSDag-Erling Smørgrav 				logit("%s host key for IP address "
910af12a3e7SDag-Erling Smørgrav 				    "'%.128s' not in list of known hosts.",
911e8aafc91SKris Kennaway 				    type, ip);
912e146993eSDag-Erling Smørgrav 			else if (!add_host_to_hostfile(user_hostfiles[0], ip,
913aa49c926SDag-Erling Smørgrav 			    host_key, options.hash_known_hosts))
914cf2b5f3bSDag-Erling Smørgrav 				logit("Failed to add the %s host key for IP "
915af12a3e7SDag-Erling Smørgrav 				    "address '%.128s' to the list of known "
916*557f75e5SDag-Erling Smørgrav 				    "hosts (%.500s).", type, ip,
917e146993eSDag-Erling Smørgrav 				    user_hostfiles[0]);
918af12a3e7SDag-Erling Smørgrav 			else
919cf2b5f3bSDag-Erling Smørgrav 				logit("Warning: Permanently added the %s host "
920af12a3e7SDag-Erling Smørgrav 				    "key for IP address '%.128s' to the list "
921af12a3e7SDag-Erling Smørgrav 				    "of known hosts.", type, ip);
922d4af9e69SDag-Erling Smørgrav 		} else if (options.visual_host_key) {
923bc5531deSDag-Erling Smørgrav 			fp = sshkey_fingerprint(host_key,
924bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_DEFAULT);
925bc5531deSDag-Erling Smørgrav 			ra = sshkey_fingerprint(host_key,
926bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_RANDOMART);
927bc5531deSDag-Erling Smørgrav 			if (fp == NULL || ra == NULL)
928bc5531deSDag-Erling Smørgrav 				fatal("%s: sshkey_fingerprint fail", __func__);
929d4af9e69SDag-Erling Smørgrav 			logit("Host key fingerprint is %s\n%s\n", fp, ra);
930e4a9863fSDag-Erling Smørgrav 			free(ra);
931e4a9863fSDag-Erling Smørgrav 			free(fp);
932e8aafc91SKris Kennaway 		}
933bc5531deSDag-Erling Smørgrav 		hostkey_trusted = 1;
934e8aafc91SKris Kennaway 		break;
935e8aafc91SKris Kennaway 	case HOST_NEW:
936333ee039SDag-Erling Smørgrav 		if (options.host_key_alias == NULL && port != 0 &&
937333ee039SDag-Erling Smørgrav 		    port != SSH_DEFAULT_PORT) {
938333ee039SDag-Erling Smørgrav 			debug("checking without port identifier");
939cce7d346SDag-Erling Smørgrav 			if (check_host_key(hostname, hostaddr, 0, host_key,
940e146993eSDag-Erling Smørgrav 			    ROQUIET, user_hostfiles, num_user_hostfiles,
941e146993eSDag-Erling Smørgrav 			    system_hostfiles, num_system_hostfiles) == 0) {
942333ee039SDag-Erling Smørgrav 				debug("found matching key w/out port");
943333ee039SDag-Erling Smørgrav 				break;
944333ee039SDag-Erling Smørgrav 			}
945333ee039SDag-Erling Smørgrav 		}
946b15c8340SDag-Erling Smørgrav 		if (readonly || want_cert)
947af12a3e7SDag-Erling Smørgrav 			goto fail;
948e8aafc91SKris Kennaway 		/* The host is new. */
949e8aafc91SKris Kennaway 		if (options.strict_host_key_checking == 1) {
950af12a3e7SDag-Erling Smørgrav 			/*
951af12a3e7SDag-Erling Smørgrav 			 * User has requested strict host key checking.  We
952af12a3e7SDag-Erling Smørgrav 			 * will not add the host key automatically.  The only
953af12a3e7SDag-Erling Smørgrav 			 * alternative left is to abort.
954af12a3e7SDag-Erling Smørgrav 			 */
955af12a3e7SDag-Erling Smørgrav 			error("No %s host key is known for %.200s and you "
956af12a3e7SDag-Erling Smørgrav 			    "have requested strict checking.", type, host);
957af12a3e7SDag-Erling Smørgrav 			goto fail;
958e8aafc91SKris Kennaway 		} else if (options.strict_host_key_checking == 2) {
959cf2b5f3bSDag-Erling Smørgrav 			char msg1[1024], msg2[1024];
960cf2b5f3bSDag-Erling Smørgrav 
9614a421b63SDag-Erling Smørgrav 			if (show_other_keys(host_hostkeys, host_key))
962cf2b5f3bSDag-Erling Smørgrav 				snprintf(msg1, sizeof(msg1),
963cf2b5f3bSDag-Erling Smørgrav 				    "\nbut keys of different type are already"
964cf2b5f3bSDag-Erling Smørgrav 				    " known for this host.");
965cf2b5f3bSDag-Erling Smørgrav 			else
966cf2b5f3bSDag-Erling Smørgrav 				snprintf(msg1, sizeof(msg1), ".");
967e8aafc91SKris Kennaway 			/* The default */
968bc5531deSDag-Erling Smørgrav 			fp = sshkey_fingerprint(host_key,
969bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_DEFAULT);
970bc5531deSDag-Erling Smørgrav 			ra = sshkey_fingerprint(host_key,
971bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_RANDOMART);
972bc5531deSDag-Erling Smørgrav 			if (fp == NULL || ra == NULL)
973bc5531deSDag-Erling Smørgrav 				fatal("%s: sshkey_fingerprint fail", __func__);
974cf2b5f3bSDag-Erling Smørgrav 			msg2[0] = '\0';
975cf2b5f3bSDag-Erling Smørgrav 			if (options.verify_host_key_dns) {
9761ec0d754SDag-Erling Smørgrav 				if (matching_host_key_dns)
977cf2b5f3bSDag-Erling Smørgrav 					snprintf(msg2, sizeof(msg2),
978cf2b5f3bSDag-Erling Smørgrav 					    "Matching host key fingerprint"
979cf2b5f3bSDag-Erling Smørgrav 					    " found in DNS.\n");
980cf2b5f3bSDag-Erling Smørgrav 				else
981cf2b5f3bSDag-Erling Smørgrav 					snprintf(msg2, sizeof(msg2),
982cf2b5f3bSDag-Erling Smørgrav 					    "No matching host key fingerprint"
983cf2b5f3bSDag-Erling Smørgrav 					    " found in DNS.\n");
984cf2b5f3bSDag-Erling Smørgrav 			}
985af12a3e7SDag-Erling Smørgrav 			snprintf(msg, sizeof(msg),
986af12a3e7SDag-Erling Smørgrav 			    "The authenticity of host '%.200s (%s)' can't be "
987f388f5efSDag-Erling Smørgrav 			    "established%s\n"
988d4af9e69SDag-Erling Smørgrav 			    "%s key fingerprint is %s.%s%s\n%s"
989af12a3e7SDag-Erling Smørgrav 			    "Are you sure you want to continue connecting "
990f388f5efSDag-Erling Smørgrav 			    "(yes/no)? ",
991d4af9e69SDag-Erling Smørgrav 			    host, ip, msg1, type, fp,
992d4af9e69SDag-Erling Smørgrav 			    options.visual_host_key ? "\n" : "",
993d4af9e69SDag-Erling Smørgrav 			    options.visual_host_key ? ra : "",
994d4af9e69SDag-Erling Smørgrav 			    msg2);
995e4a9863fSDag-Erling Smørgrav 			free(ra);
996e4a9863fSDag-Erling Smørgrav 			free(fp);
997af12a3e7SDag-Erling Smørgrav 			if (!confirm(msg))
998af12a3e7SDag-Erling Smørgrav 				goto fail;
999bc5531deSDag-Erling Smørgrav 			hostkey_trusted = 1; /* user explicitly confirmed */
1000e8aafc91SKris Kennaway 		}
1001af12a3e7SDag-Erling Smørgrav 		/*
1002af12a3e7SDag-Erling Smørgrav 		 * If not in strict mode, add the key automatically to the
1003af12a3e7SDag-Erling Smørgrav 		 * local known_hosts file.
1004af12a3e7SDag-Erling Smørgrav 		 */
1005aa49c926SDag-Erling Smørgrav 		if (options.check_host_ip && ip_status == HOST_NEW) {
10064a421b63SDag-Erling Smørgrav 			snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
1007aa49c926SDag-Erling Smørgrav 			hostp = hostline;
1008aa49c926SDag-Erling Smørgrav 			if (options.hash_known_hosts) {
1009aa49c926SDag-Erling Smørgrav 				/* Add hash of host and IP separately */
1010e146993eSDag-Erling Smørgrav 				r = add_host_to_hostfile(user_hostfiles[0],
1011e146993eSDag-Erling Smørgrav 				    host, host_key, options.hash_known_hosts) &&
1012e146993eSDag-Erling Smørgrav 				    add_host_to_hostfile(user_hostfiles[0], ip,
1013aa49c926SDag-Erling Smørgrav 				    host_key, options.hash_known_hosts);
1014aa49c926SDag-Erling Smørgrav 			} else {
1015aa49c926SDag-Erling Smørgrav 				/* Add unhashed "host,ip" */
1016e146993eSDag-Erling Smørgrav 				r = add_host_to_hostfile(user_hostfiles[0],
1017aa49c926SDag-Erling Smørgrav 				    hostline, host_key,
1018aa49c926SDag-Erling Smørgrav 				    options.hash_known_hosts);
1019aa49c926SDag-Erling Smørgrav 			}
1020aa49c926SDag-Erling Smørgrav 		} else {
1021e146993eSDag-Erling Smørgrav 			r = add_host_to_hostfile(user_hostfiles[0], host,
1022e146993eSDag-Erling Smørgrav 			    host_key, options.hash_known_hosts);
1023aa49c926SDag-Erling Smørgrav 			hostp = host;
1024aa49c926SDag-Erling Smørgrav 		}
1025aa49c926SDag-Erling Smørgrav 
1026aa49c926SDag-Erling Smørgrav 		if (!r)
1027cf2b5f3bSDag-Erling Smørgrav 			logit("Failed to add the host to the list of known "
1028e146993eSDag-Erling Smørgrav 			    "hosts (%.500s).", user_hostfiles[0]);
1029e8aafc91SKris Kennaway 		else
1030cf2b5f3bSDag-Erling Smørgrav 			logit("Warning: Permanently added '%.200s' (%s) to the "
1031af12a3e7SDag-Erling Smørgrav 			    "list of known hosts.", hostp, type);
1032e8aafc91SKris Kennaway 		break;
1033b15c8340SDag-Erling Smørgrav 	case HOST_REVOKED:
1034b15c8340SDag-Erling Smørgrav 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1035b15c8340SDag-Erling Smørgrav 		error("@       WARNING: REVOKED HOST KEY DETECTED!               @");
1036b15c8340SDag-Erling Smørgrav 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1037b15c8340SDag-Erling Smørgrav 		error("The %s host key for %s is marked as revoked.", type, host);
1038b15c8340SDag-Erling Smørgrav 		error("This could mean that a stolen key is being used to");
1039b15c8340SDag-Erling Smørgrav 		error("impersonate this host.");
1040b15c8340SDag-Erling Smørgrav 
1041b15c8340SDag-Erling Smørgrav 		/*
1042b15c8340SDag-Erling Smørgrav 		 * If strict host key checking is in use, the user will have
1043b15c8340SDag-Erling Smørgrav 		 * to edit the key manually and we can only abort.
1044b15c8340SDag-Erling Smørgrav 		 */
1045b15c8340SDag-Erling Smørgrav 		if (options.strict_host_key_checking) {
1046b15c8340SDag-Erling Smørgrav 			error("%s host key for %.200s was revoked and you have "
1047b15c8340SDag-Erling Smørgrav 			    "requested strict checking.", type, host);
1048b15c8340SDag-Erling Smørgrav 			goto fail;
1049b15c8340SDag-Erling Smørgrav 		}
1050b15c8340SDag-Erling Smørgrav 		goto continue_unsafe;
1051b15c8340SDag-Erling Smørgrav 
1052e8aafc91SKris Kennaway 	case HOST_CHANGED:
1053b15c8340SDag-Erling Smørgrav 		if (want_cert) {
1054b15c8340SDag-Erling Smørgrav 			/*
1055b15c8340SDag-Erling Smørgrav 			 * This is only a debug() since it is valid to have
1056b15c8340SDag-Erling Smørgrav 			 * CAs with wildcard DNS matches that don't match
1057b15c8340SDag-Erling Smørgrav 			 * all hosts that one might visit.
1058b15c8340SDag-Erling Smørgrav 			 */
1059b15c8340SDag-Erling Smørgrav 			debug("Host certificate authority does not "
10604a421b63SDag-Erling Smørgrav 			    "match %s in %s:%lu", CA_MARKER,
10614a421b63SDag-Erling Smørgrav 			    host_found->file, host_found->line);
1062b15c8340SDag-Erling Smørgrav 			goto fail;
1063b15c8340SDag-Erling Smørgrav 		}
1064333ee039SDag-Erling Smørgrav 		if (readonly == ROQUIET)
1065333ee039SDag-Erling Smørgrav 			goto fail;
1066e8aafc91SKris Kennaway 		if (options.check_host_ip && host_ip_differ) {
106721e764dfSDag-Erling Smørgrav 			char *key_msg;
1068e8aafc91SKris Kennaway 			if (ip_status == HOST_NEW)
106921e764dfSDag-Erling Smørgrav 				key_msg = "is unknown";
1070e8aafc91SKris Kennaway 			else if (ip_status == HOST_OK)
107121e764dfSDag-Erling Smørgrav 				key_msg = "is unchanged";
1072e8aafc91SKris Kennaway 			else
107321e764dfSDag-Erling Smørgrav 				key_msg = "has a different value";
1074e8aafc91SKris Kennaway 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1075e8aafc91SKris Kennaway 			error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
1076e8aafc91SKris Kennaway 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1077e8aafc91SKris Kennaway 			error("The %s host key for %s has changed,", type, host);
1078d4af9e69SDag-Erling Smørgrav 			error("and the key for the corresponding IP address %s", ip);
107921e764dfSDag-Erling Smørgrav 			error("%s. This could either mean that", key_msg);
1080e8aafc91SKris Kennaway 			error("DNS SPOOFING is happening or the IP address for the host");
1081ca3176e7SBrian Feldman 			error("and its host key have changed at the same time.");
1082ca3176e7SBrian Feldman 			if (ip_status != HOST_NEW)
10834a421b63SDag-Erling Smørgrav 				error("Offending key for IP in %s:%lu",
10844a421b63SDag-Erling Smørgrav 				    ip_found->file, ip_found->line);
1085e8aafc91SKris Kennaway 		}
1086e8aafc91SKris Kennaway 		/* The host key has changed. */
10871ec0d754SDag-Erling Smørgrav 		warn_changed_key(host_key);
1088e8aafc91SKris Kennaway 		error("Add correct host key in %.100s to get rid of this message.",
1089e146993eSDag-Erling Smørgrav 		    user_hostfiles[0]);
10904a421b63SDag-Erling Smørgrav 		error("Offending %s key in %s:%lu", key_type(host_found->key),
10914a421b63SDag-Erling Smørgrav 		    host_found->file, host_found->line);
1092e8aafc91SKris Kennaway 
1093e8aafc91SKris Kennaway 		/*
1094e8aafc91SKris Kennaway 		 * If strict host key checking is in use, the user will have
1095e8aafc91SKris Kennaway 		 * to edit the key manually and we can only abort.
1096e8aafc91SKris Kennaway 		 */
1097af12a3e7SDag-Erling Smørgrav 		if (options.strict_host_key_checking) {
1098af12a3e7SDag-Erling Smørgrav 			error("%s host key for %.200s has changed and you have "
1099af12a3e7SDag-Erling Smørgrav 			    "requested strict checking.", type, host);
1100af12a3e7SDag-Erling Smørgrav 			goto fail;
1101af12a3e7SDag-Erling Smørgrav 		}
1102e8aafc91SKris Kennaway 
1103b15c8340SDag-Erling Smørgrav  continue_unsafe:
1104e8aafc91SKris Kennaway 		/*
1105e8aafc91SKris Kennaway 		 * If strict host key checking has not been requested, allow
1106cf2b5f3bSDag-Erling Smørgrav 		 * the connection but without MITM-able authentication or
1107333ee039SDag-Erling Smørgrav 		 * forwarding.
1108e8aafc91SKris Kennaway 		 */
1109e8aafc91SKris Kennaway 		if (options.password_authentication) {
1110af12a3e7SDag-Erling Smørgrav 			error("Password authentication is disabled to avoid "
1111af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1112e8aafc91SKris Kennaway 			options.password_authentication = 0;
1113d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1114e8aafc91SKris Kennaway 		}
1115cf2b5f3bSDag-Erling Smørgrav 		if (options.kbd_interactive_authentication) {
1116cf2b5f3bSDag-Erling Smørgrav 			error("Keyboard-interactive authentication is disabled"
1117cf2b5f3bSDag-Erling Smørgrav 			    " to avoid man-in-the-middle attacks.");
1118cf2b5f3bSDag-Erling Smørgrav 			options.kbd_interactive_authentication = 0;
1119cf2b5f3bSDag-Erling Smørgrav 			options.challenge_response_authentication = 0;
1120d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1121cf2b5f3bSDag-Erling Smørgrav 		}
1122cf2b5f3bSDag-Erling Smørgrav 		if (options.challenge_response_authentication) {
1123cf2b5f3bSDag-Erling Smørgrav 			error("Challenge/response authentication is disabled"
1124cf2b5f3bSDag-Erling Smørgrav 			    " to avoid man-in-the-middle attacks.");
1125cf2b5f3bSDag-Erling Smørgrav 			options.challenge_response_authentication = 0;
1126d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1127cf2b5f3bSDag-Erling Smørgrav 		}
1128e8aafc91SKris Kennaway 		if (options.forward_agent) {
1129af12a3e7SDag-Erling Smørgrav 			error("Agent forwarding is disabled to avoid "
1130af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1131e8aafc91SKris Kennaway 			options.forward_agent = 0;
1132d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1133e8aafc91SKris Kennaway 		}
1134ca3176e7SBrian Feldman 		if (options.forward_x11) {
1135af12a3e7SDag-Erling Smørgrav 			error("X11 forwarding is disabled to avoid "
1136af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1137ca3176e7SBrian Feldman 			options.forward_x11 = 0;
1138d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1139ca3176e7SBrian Feldman 		}
1140af12a3e7SDag-Erling Smørgrav 		if (options.num_local_forwards > 0 ||
1141af12a3e7SDag-Erling Smørgrav 		    options.num_remote_forwards > 0) {
1142af12a3e7SDag-Erling Smørgrav 			error("Port forwarding is disabled to avoid "
1143af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1144af12a3e7SDag-Erling Smørgrav 			options.num_local_forwards =
1145af12a3e7SDag-Erling Smørgrav 			    options.num_remote_forwards = 0;
1146d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1147ca3176e7SBrian Feldman 		}
1148333ee039SDag-Erling Smørgrav 		if (options.tun_open != SSH_TUNMODE_NO) {
1149333ee039SDag-Erling Smørgrav 			error("Tunnel forwarding is disabled to avoid "
1150333ee039SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1151333ee039SDag-Erling Smørgrav 			options.tun_open = SSH_TUNMODE_NO;
1152d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1153333ee039SDag-Erling Smørgrav 		}
1154d4af9e69SDag-Erling Smørgrav 		if (options.exit_on_forward_failure && cancelled_forwarding)
1155d4af9e69SDag-Erling Smørgrav 			fatal("Error: forwarding disabled due to host key "
1156d4af9e69SDag-Erling Smørgrav 			    "check failure");
1157d4af9e69SDag-Erling Smørgrav 
1158e8aafc91SKris Kennaway 		/*
1159e8aafc91SKris Kennaway 		 * XXX Should permit the user to change to use the new id.
1160e8aafc91SKris Kennaway 		 * This could be done by converting the host key to an
1161e8aafc91SKris Kennaway 		 * identifying sentence, tell that the host identifies itself
1162b15c8340SDag-Erling Smørgrav 		 * by that sentence, and ask the user if he/she wishes to
1163e8aafc91SKris Kennaway 		 * accept the authentication.
1164e8aafc91SKris Kennaway 		 */
1165e8aafc91SKris Kennaway 		break;
1166f388f5efSDag-Erling Smørgrav 	case HOST_FOUND:
1167f388f5efSDag-Erling Smørgrav 		fatal("internal error");
1168f388f5efSDag-Erling Smørgrav 		break;
1169e8aafc91SKris Kennaway 	}
1170ca3176e7SBrian Feldman 
1171ca3176e7SBrian Feldman 	if (options.check_host_ip && host_status != HOST_CHANGED &&
1172ca3176e7SBrian Feldman 	    ip_status == HOST_CHANGED) {
1173af12a3e7SDag-Erling Smørgrav 		snprintf(msg, sizeof(msg),
1174af12a3e7SDag-Erling Smørgrav 		    "Warning: the %s host key for '%.200s' "
1175af12a3e7SDag-Erling Smørgrav 		    "differs from the key for the IP address '%.128s'"
11764a421b63SDag-Erling Smørgrav 		    "\nOffending key for IP in %s:%lu",
11774a421b63SDag-Erling Smørgrav 		    type, host, ip, ip_found->file, ip_found->line);
1178af12a3e7SDag-Erling Smørgrav 		if (host_status == HOST_OK) {
1179af12a3e7SDag-Erling Smørgrav 			len = strlen(msg);
1180af12a3e7SDag-Erling Smørgrav 			snprintf(msg + len, sizeof(msg) - len,
11814a421b63SDag-Erling Smørgrav 			    "\nMatching host key in %s:%lu",
11824a421b63SDag-Erling Smørgrav 			    host_found->file, host_found->line);
1183af12a3e7SDag-Erling Smørgrav 		}
1184ca3176e7SBrian Feldman 		if (options.strict_host_key_checking == 1) {
1185cf2b5f3bSDag-Erling Smørgrav 			logit("%s", msg);
1186af12a3e7SDag-Erling Smørgrav 			error("Exiting, you have requested strict checking.");
1187af12a3e7SDag-Erling Smørgrav 			goto fail;
1188ca3176e7SBrian Feldman 		} else if (options.strict_host_key_checking == 2) {
1189af12a3e7SDag-Erling Smørgrav 			strlcat(msg, "\nAre you sure you want "
1190af12a3e7SDag-Erling Smørgrav 			    "to continue connecting (yes/no)? ", sizeof(msg));
1191af12a3e7SDag-Erling Smørgrav 			if (!confirm(msg))
1192af12a3e7SDag-Erling Smørgrav 				goto fail;
1193af12a3e7SDag-Erling Smørgrav 		} else {
1194cf2b5f3bSDag-Erling Smørgrav 			logit("%s", msg);
1195ca3176e7SBrian Feldman 		}
1196ca3176e7SBrian Feldman 	}
1197ca3176e7SBrian Feldman 
1198bc5531deSDag-Erling Smørgrav 	if (!hostkey_trusted && options.update_hostkeys) {
1199bc5531deSDag-Erling Smørgrav 		debug("%s: hostkey not known or explicitly trusted: "
1200bc5531deSDag-Erling Smørgrav 		    "disabling UpdateHostkeys", __func__);
1201bc5531deSDag-Erling Smørgrav 		options.update_hostkeys = 0;
1202bc5531deSDag-Erling Smørgrav 	}
1203bc5531deSDag-Erling Smørgrav 
1204e4a9863fSDag-Erling Smørgrav 	free(ip);
1205e4a9863fSDag-Erling Smørgrav 	free(host);
12064a421b63SDag-Erling Smørgrav 	if (host_hostkeys != NULL)
12074a421b63SDag-Erling Smørgrav 		free_hostkeys(host_hostkeys);
12084a421b63SDag-Erling Smørgrav 	if (ip_hostkeys != NULL)
12094a421b63SDag-Erling Smørgrav 		free_hostkeys(ip_hostkeys);
1210af12a3e7SDag-Erling Smørgrav 	return 0;
1211af12a3e7SDag-Erling Smørgrav 
1212af12a3e7SDag-Erling Smørgrav fail:
1213b15c8340SDag-Erling Smørgrav 	if (want_cert && host_status != HOST_REVOKED) {
1214b15c8340SDag-Erling Smørgrav 		/*
1215b15c8340SDag-Erling Smørgrav 		 * No matching certificate. Downgrade cert to raw key and
1216b15c8340SDag-Erling Smørgrav 		 * search normally.
1217b15c8340SDag-Erling Smørgrav 		 */
1218b15c8340SDag-Erling Smørgrav 		debug("No matching CA found. Retry with plain key");
1219b15c8340SDag-Erling Smørgrav 		raw_key = key_from_private(host_key);
1220b15c8340SDag-Erling Smørgrav 		if (key_drop_cert(raw_key) != 0)
1221b15c8340SDag-Erling Smørgrav 			fatal("Couldn't drop certificate");
1222b15c8340SDag-Erling Smørgrav 		host_key = raw_key;
1223b15c8340SDag-Erling Smørgrav 		goto retry;
1224b15c8340SDag-Erling Smørgrav 	}
1225b15c8340SDag-Erling Smørgrav 	if (raw_key != NULL)
1226b15c8340SDag-Erling Smørgrav 		key_free(raw_key);
1227e4a9863fSDag-Erling Smørgrav 	free(ip);
1228e4a9863fSDag-Erling Smørgrav 	free(host);
12294a421b63SDag-Erling Smørgrav 	if (host_hostkeys != NULL)
12304a421b63SDag-Erling Smørgrav 		free_hostkeys(host_hostkeys);
12314a421b63SDag-Erling Smørgrav 	if (ip_hostkeys != NULL)
12324a421b63SDag-Erling Smørgrav 		free_hostkeys(ip_hostkeys);
1233af12a3e7SDag-Erling Smørgrav 	return -1;
1234e8aafc91SKris Kennaway }
1235511b41d2SMark Murray 
1236cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */
1237fe5fd017SMark Murray int
1238af12a3e7SDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
1239fe5fd017SMark Murray {
1240a0ee8cc6SDag-Erling Smørgrav 	int r = -1, flags = 0;
1241bc5531deSDag-Erling Smørgrav 	char *fp = NULL;
1242bc5531deSDag-Erling Smørgrav 	struct sshkey *plain = NULL;
12434a421b63SDag-Erling Smørgrav 
1244bc5531deSDag-Erling Smørgrav 	if ((fp = sshkey_fingerprint(host_key,
1245bc5531deSDag-Erling Smørgrav 	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
1246bc5531deSDag-Erling Smørgrav 		error("%s: fingerprint host key: %s", __func__, ssh_err(r));
1247bc5531deSDag-Erling Smørgrav 		r = -1;
1248bc5531deSDag-Erling Smørgrav 		goto out;
1249bc5531deSDag-Erling Smørgrav 	}
1250fe5fd017SMark Murray 
1251bc5531deSDag-Erling Smørgrav 	debug("Server host key: %s %s",
1252bc5531deSDag-Erling Smørgrav 	    compat20 ? sshkey_ssh_name(host_key) : sshkey_type(host_key), fp);
1253bc5531deSDag-Erling Smørgrav 
1254bc5531deSDag-Erling Smørgrav 	if (sshkey_equal(previous_host_key, host_key)) {
1255bc5531deSDag-Erling Smørgrav 		debug2("%s: server host key %s %s matches cached key",
1256bc5531deSDag-Erling Smørgrav 		    __func__, sshkey_type(host_key), fp);
1257bc5531deSDag-Erling Smørgrav 		r = 0;
1258bc5531deSDag-Erling Smørgrav 		goto out;
1259bc5531deSDag-Erling Smørgrav 	}
1260bc5531deSDag-Erling Smørgrav 
1261bc5531deSDag-Erling Smørgrav 	/* Check in RevokedHostKeys file if specified */
1262bc5531deSDag-Erling Smørgrav 	if (options.revoked_host_keys != NULL) {
1263bc5531deSDag-Erling Smørgrav 		r = sshkey_check_revoked(host_key, options.revoked_host_keys);
1264bc5531deSDag-Erling Smørgrav 		switch (r) {
1265bc5531deSDag-Erling Smørgrav 		case 0:
1266bc5531deSDag-Erling Smørgrav 			break; /* not revoked */
1267bc5531deSDag-Erling Smørgrav 		case SSH_ERR_KEY_REVOKED:
1268bc5531deSDag-Erling Smørgrav 			error("Host key %s %s revoked by file %s",
1269bc5531deSDag-Erling Smørgrav 			    sshkey_type(host_key), fp,
1270bc5531deSDag-Erling Smørgrav 			    options.revoked_host_keys);
1271bc5531deSDag-Erling Smørgrav 			r = -1;
1272bc5531deSDag-Erling Smørgrav 			goto out;
1273bc5531deSDag-Erling Smørgrav 		default:
1274bc5531deSDag-Erling Smørgrav 			error("Error checking host key %s %s in "
1275bc5531deSDag-Erling Smørgrav 			    "revoked keys file %s: %s", sshkey_type(host_key),
1276bc5531deSDag-Erling Smørgrav 			    fp, options.revoked_host_keys, ssh_err(r));
1277bc5531deSDag-Erling Smørgrav 			r = -1;
1278bc5531deSDag-Erling Smørgrav 			goto out;
1279bc5531deSDag-Erling Smørgrav 		}
1280a0ee8cc6SDag-Erling Smørgrav 	}
1281a0ee8cc6SDag-Erling Smørgrav 
12823a0b9b77SXin LI 	if (options.verify_host_key_dns) {
12833a0b9b77SXin LI 		/*
12843a0b9b77SXin LI 		 * XXX certs are not yet supported for DNS, so downgrade
12853a0b9b77SXin LI 		 * them and try the plain key.
12863a0b9b77SXin LI 		 */
1287bc5531deSDag-Erling Smørgrav 		if ((r = sshkey_from_private(host_key, &plain)) != 0)
1288bc5531deSDag-Erling Smørgrav 			goto out;
1289bc5531deSDag-Erling Smørgrav 		if (sshkey_is_cert(plain))
1290bc5531deSDag-Erling Smørgrav 			sshkey_drop_cert(plain);
12913a0b9b77SXin LI 		if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
12921ec0d754SDag-Erling Smørgrav 			if (flags & DNS_VERIFY_FOUND) {
12931ec0d754SDag-Erling Smørgrav 				if (options.verify_host_key_dns == 1 &&
12941ec0d754SDag-Erling Smørgrav 				    flags & DNS_VERIFY_MATCH &&
12953a0b9b77SXin LI 				    flags & DNS_VERIFY_SECURE) {
1296a0ee8cc6SDag-Erling Smørgrav 					r = 0;
1297bc5531deSDag-Erling Smørgrav 					goto out;
12983a0b9b77SXin LI 				}
12991ec0d754SDag-Erling Smørgrav 				if (flags & DNS_VERIFY_MATCH) {
13001ec0d754SDag-Erling Smørgrav 					matching_host_key_dns = 1;
13011ec0d754SDag-Erling Smørgrav 				} else {
13023a0b9b77SXin LI 					warn_changed_key(plain);
13033a0b9b77SXin LI 					error("Update the SSHFP RR in DNS "
13043a0b9b77SXin LI 					    "with the new host key to get rid "
13053a0b9b77SXin LI 					    "of this message.");
1306cf2b5f3bSDag-Erling Smørgrav 				}
1307cf2b5f3bSDag-Erling Smørgrav 			}
13081ec0d754SDag-Erling Smørgrav 		}
13093a0b9b77SXin LI 	}
1310a0ee8cc6SDag-Erling Smørgrav 	r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
1311e146993eSDag-Erling Smørgrav 	    options.user_hostfiles, options.num_user_hostfiles,
1312e146993eSDag-Erling Smørgrav 	    options.system_hostfiles, options.num_system_hostfiles);
1313a0ee8cc6SDag-Erling Smørgrav 
1314bc5531deSDag-Erling Smørgrav out:
1315bc5531deSDag-Erling Smørgrav 	sshkey_free(plain);
1316bc5531deSDag-Erling Smørgrav 	free(fp);
1317a0ee8cc6SDag-Erling Smørgrav 	if (r == 0 && host_key != NULL) {
1318a0ee8cc6SDag-Erling Smørgrav 		key_free(previous_host_key);
1319a0ee8cc6SDag-Erling Smørgrav 		previous_host_key = key_from_private(host_key);
1320a0ee8cc6SDag-Erling Smørgrav 	}
1321a0ee8cc6SDag-Erling Smørgrav 
1322a0ee8cc6SDag-Erling Smørgrav 	return r;
1323fe5fd017SMark Murray }
1324fe5fd017SMark Murray 
1325511b41d2SMark Murray /*
1326511b41d2SMark Murray  * Starts a dialog with the server, and authenticates the current user on the
1327511b41d2SMark Murray  * server.  This does not need any extra privileges.  The basic connection
1328511b41d2SMark Murray  * to the server must already have been established before this is called.
1329511b41d2SMark Murray  * If login fails, this function prints an error and never returns.
1330511b41d2SMark Murray  * This function does not require super-user privileges.
1331511b41d2SMark Murray  */
1332511b41d2SMark Murray void
133380628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost,
13344a421b63SDag-Erling Smørgrav     struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
1335511b41d2SMark Murray {
1336f7167e0eSDag-Erling Smørgrav 	char *host;
1337e8aafc91SKris Kennaway 	char *server_user, *local_user;
1338e8aafc91SKris Kennaway 
1339e8aafc91SKris Kennaway 	local_user = xstrdup(pw->pw_name);
1340e8aafc91SKris Kennaway 	server_user = options.user ? options.user : local_user;
1341511b41d2SMark Murray 
1342511b41d2SMark Murray 	/* Convert the user-supplied hostname into all lowercase. */
1343511b41d2SMark Murray 	host = xstrdup(orighost);
1344f7167e0eSDag-Erling Smørgrav 	lowercase(host);
1345511b41d2SMark Murray 
1346511b41d2SMark Murray 	/* Exchange protocol version identification strings with the server. */
1347d4af9e69SDag-Erling Smørgrav 	ssh_exchange_identification(timeout_ms);
1348511b41d2SMark Murray 
1349511b41d2SMark Murray 	/* Put the connection into non-blocking mode. */
1350511b41d2SMark Murray 	packet_set_nonblocking();
1351511b41d2SMark Murray 
1352511b41d2SMark Murray 	/* key exchange */
1353511b41d2SMark Murray 	/* authenticate user */
1354*557f75e5SDag-Erling Smørgrav 	debug("Authenticating to %s:%d as '%s'", host, port, server_user);
1355e8aafc91SKris Kennaway 	if (compat20) {
13564a421b63SDag-Erling Smørgrav 		ssh_kex2(host, hostaddr, port);
135780628bacSDag-Erling Smørgrav 		ssh_userauth2(local_user, server_user, host, sensitive);
1358e8aafc91SKris Kennaway 	} else {
1359a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_SSH1
1360e8aafc91SKris Kennaway 		ssh_kex(host, hostaddr);
136180628bacSDag-Erling Smørgrav 		ssh_userauth1(local_user, server_user, host, sensitive);
1362a0ee8cc6SDag-Erling Smørgrav #else
1363*557f75e5SDag-Erling Smørgrav 		fatal("ssh1 is not supported");
1364a0ee8cc6SDag-Erling Smørgrav #endif
1365e8aafc91SKris Kennaway 	}
1366e4a9863fSDag-Erling Smørgrav 	free(local_user);
1367511b41d2SMark Murray }
1368e0fbb1d2SBrian Feldman 
1369e0fbb1d2SBrian Feldman void
1370e0fbb1d2SBrian Feldman ssh_put_password(char *password)
1371e0fbb1d2SBrian Feldman {
1372e0fbb1d2SBrian Feldman 	int size;
1373e0fbb1d2SBrian Feldman 	char *padded;
1374e0fbb1d2SBrian Feldman 
1375ca3176e7SBrian Feldman 	if (datafellows & SSH_BUG_PASSWORDPAD) {
1376af12a3e7SDag-Erling Smørgrav 		packet_put_cstring(password);
1377ca3176e7SBrian Feldman 		return;
1378ca3176e7SBrian Feldman 	}
1379e0fbb1d2SBrian Feldman 	size = roundup(strlen(password) + 1, 32);
1380333ee039SDag-Erling Smørgrav 	padded = xcalloc(1, size);
1381e0fbb1d2SBrian Feldman 	strlcpy(padded, password, size);
1382e0fbb1d2SBrian Feldman 	packet_put_string(padded, size);
1383b83788ffSDag-Erling Smørgrav 	explicit_bzero(padded, size);
1384e4a9863fSDag-Erling Smørgrav 	free(padded);
1385e0fbb1d2SBrian Feldman }
1386f388f5efSDag-Erling Smørgrav 
1387f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */
1388f388f5efSDag-Erling Smørgrav static int
13894a421b63SDag-Erling Smørgrav show_other_keys(struct hostkeys *hostkeys, Key *key)
1390f388f5efSDag-Erling Smørgrav {
1391f7167e0eSDag-Erling Smørgrav 	int type[] = {
1392f7167e0eSDag-Erling Smørgrav 		KEY_RSA1,
1393f7167e0eSDag-Erling Smørgrav 		KEY_RSA,
1394f7167e0eSDag-Erling Smørgrav 		KEY_DSA,
1395f7167e0eSDag-Erling Smørgrav 		KEY_ECDSA,
1396f7167e0eSDag-Erling Smørgrav 		KEY_ED25519,
1397f7167e0eSDag-Erling Smørgrav 		-1
1398f7167e0eSDag-Erling Smørgrav 	};
13994a421b63SDag-Erling Smørgrav 	int i, ret = 0;
14004a421b63SDag-Erling Smørgrav 	char *fp, *ra;
14014a421b63SDag-Erling Smørgrav 	const struct hostkey_entry *found;
1402f388f5efSDag-Erling Smørgrav 
1403f388f5efSDag-Erling Smørgrav 	for (i = 0; type[i] != -1; i++) {
1404f388f5efSDag-Erling Smørgrav 		if (type[i] == key->type)
1405f388f5efSDag-Erling Smørgrav 			continue;
14064a421b63SDag-Erling Smørgrav 		if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found))
1407f388f5efSDag-Erling Smørgrav 			continue;
1408bc5531deSDag-Erling Smørgrav 		fp = sshkey_fingerprint(found->key,
1409bc5531deSDag-Erling Smørgrav 		    options.fingerprint_hash, SSH_FP_DEFAULT);
1410bc5531deSDag-Erling Smørgrav 		ra = sshkey_fingerprint(found->key,
1411bc5531deSDag-Erling Smørgrav 		    options.fingerprint_hash, SSH_FP_RANDOMART);
1412bc5531deSDag-Erling Smørgrav 		if (fp == NULL || ra == NULL)
1413bc5531deSDag-Erling Smørgrav 			fatal("%s: sshkey_fingerprint fail", __func__);
14144a421b63SDag-Erling Smørgrav 		logit("WARNING: %s key found for host %s\n"
14154a421b63SDag-Erling Smørgrav 		    "in %s:%lu\n"
14164a421b63SDag-Erling Smørgrav 		    "%s key fingerprint %s.",
14174a421b63SDag-Erling Smørgrav 		    key_type(found->key),
14184a421b63SDag-Erling Smørgrav 		    found->host, found->file, found->line,
14194a421b63SDag-Erling Smørgrav 		    key_type(found->key), fp);
14204a421b63SDag-Erling Smørgrav 		if (options.visual_host_key)
14214a421b63SDag-Erling Smørgrav 			logit("%s", ra);
1422e4a9863fSDag-Erling Smørgrav 		free(ra);
1423e4a9863fSDag-Erling Smørgrav 		free(fp);
14244a421b63SDag-Erling Smørgrav 		ret = 1;
1425f388f5efSDag-Erling Smørgrav 	}
14264a421b63SDag-Erling Smørgrav 	return ret;
1427f388f5efSDag-Erling Smørgrav }
14281ec0d754SDag-Erling Smørgrav 
14291ec0d754SDag-Erling Smørgrav static void
14301ec0d754SDag-Erling Smørgrav warn_changed_key(Key *host_key)
14311ec0d754SDag-Erling Smørgrav {
14321ec0d754SDag-Erling Smørgrav 	char *fp;
14331ec0d754SDag-Erling Smørgrav 
1434bc5531deSDag-Erling Smørgrav 	fp = sshkey_fingerprint(host_key, options.fingerprint_hash,
1435bc5531deSDag-Erling Smørgrav 	    SSH_FP_DEFAULT);
1436bc5531deSDag-Erling Smørgrav 	if (fp == NULL)
1437bc5531deSDag-Erling Smørgrav 		fatal("%s: sshkey_fingerprint fail", __func__);
14381ec0d754SDag-Erling Smørgrav 
14391ec0d754SDag-Erling Smørgrav 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
14401ec0d754SDag-Erling Smørgrav 	error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
14411ec0d754SDag-Erling Smørgrav 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
14421ec0d754SDag-Erling Smørgrav 	error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
14431ec0d754SDag-Erling Smørgrav 	error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
14444a421b63SDag-Erling Smørgrav 	error("It is also possible that a host key has just been changed.");
14451ec0d754SDag-Erling Smørgrav 	error("The fingerprint for the %s key sent by the remote host is\n%s.",
14464a421b63SDag-Erling Smørgrav 	    key_type(host_key), fp);
14471ec0d754SDag-Erling Smørgrav 	error("Please contact your system administrator.");
14481ec0d754SDag-Erling Smørgrav 
1449e4a9863fSDag-Erling Smørgrav 	free(fp);
14501ec0d754SDag-Erling Smørgrav }
1451b74df5b2SDag-Erling Smørgrav 
1452b74df5b2SDag-Erling Smørgrav /*
1453b74df5b2SDag-Erling Smørgrav  * Execute a local command
1454b74df5b2SDag-Erling Smørgrav  */
1455b74df5b2SDag-Erling Smørgrav int
1456b74df5b2SDag-Erling Smørgrav ssh_local_cmd(const char *args)
1457b74df5b2SDag-Erling Smørgrav {
1458b74df5b2SDag-Erling Smørgrav 	char *shell;
1459b74df5b2SDag-Erling Smørgrav 	pid_t pid;
1460b74df5b2SDag-Erling Smørgrav 	int status;
14614a421b63SDag-Erling Smørgrav 	void (*osighand)(int);
1462b74df5b2SDag-Erling Smørgrav 
1463b74df5b2SDag-Erling Smørgrav 	if (!options.permit_local_command ||
1464b74df5b2SDag-Erling Smørgrav 	    args == NULL || !*args)
1465b74df5b2SDag-Erling Smørgrav 		return (1);
1466b74df5b2SDag-Erling Smørgrav 
14674a421b63SDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
1468b74df5b2SDag-Erling Smørgrav 		shell = _PATH_BSHELL;
1469b74df5b2SDag-Erling Smørgrav 
14704a421b63SDag-Erling Smørgrav 	osighand = signal(SIGCHLD, SIG_DFL);
1471b74df5b2SDag-Erling Smørgrav 	pid = fork();
1472b74df5b2SDag-Erling Smørgrav 	if (pid == 0) {
14734a421b63SDag-Erling Smørgrav 		signal(SIGPIPE, SIG_DFL);
1474b74df5b2SDag-Erling Smørgrav 		debug3("Executing %s -c \"%s\"", shell, args);
1475b74df5b2SDag-Erling Smørgrav 		execl(shell, shell, "-c", args, (char *)NULL);
1476b74df5b2SDag-Erling Smørgrav 		error("Couldn't execute %s -c \"%s\": %s",
1477b74df5b2SDag-Erling Smørgrav 		    shell, args, strerror(errno));
1478b74df5b2SDag-Erling Smørgrav 		_exit(1);
1479b74df5b2SDag-Erling Smørgrav 	} else if (pid == -1)
1480b74df5b2SDag-Erling Smørgrav 		fatal("fork failed: %.100s", strerror(errno));
1481b74df5b2SDag-Erling Smørgrav 	while (waitpid(pid, &status, 0) == -1)
1482b74df5b2SDag-Erling Smørgrav 		if (errno != EINTR)
1483b74df5b2SDag-Erling Smørgrav 			fatal("Couldn't wait for child: %s", strerror(errno));
14844a421b63SDag-Erling Smørgrav 	signal(SIGCHLD, osighand);
1485b74df5b2SDag-Erling Smørgrav 
1486b74df5b2SDag-Erling Smørgrav 	if (!WIFEXITED(status))
1487b74df5b2SDag-Erling Smørgrav 		return (1);
1488b74df5b2SDag-Erling Smørgrav 
1489b74df5b2SDag-Erling Smørgrav 	return (WEXITSTATUS(status));
1490b74df5b2SDag-Erling Smørgrav }
1491