xref: /freebsd/crypto/openssh/sshconnect.c (revision 4f52dfbb8d6c4d446500c5b097e3806ec219fbd4)
1*4f52dfbbSDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.287 2017/09/14 04:32:21 djm 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 
27333ee039SDag-Erling Smørgrav #include <netinet/in.h>
28333ee039SDag-Erling Smørgrav #include <arpa/inet.h>
29f7167e0eSDag-Erling Smørgrav #include <rpc/rpc.h>
30333ee039SDag-Erling Smørgrav 
31333ee039SDag-Erling Smørgrav #include <ctype.h>
32333ee039SDag-Erling Smørgrav #include <errno.h>
33b15c8340SDag-Erling Smørgrav #include <fcntl.h>
34333ee039SDag-Erling Smørgrav #include <netdb.h>
35333ee039SDag-Erling Smørgrav #ifdef HAVE_PATHS_H
36333ee039SDag-Erling Smørgrav #include <paths.h>
37333ee039SDag-Erling Smørgrav #endif
38333ee039SDag-Erling Smørgrav #include <pwd.h>
39*4f52dfbbSDag-Erling Smørgrav #ifdef HAVE_POLL_H
40*4f52dfbbSDag-Erling Smørgrav #include <poll.h>
41*4f52dfbbSDag-Erling Smørgrav #endif
424a421b63SDag-Erling Smørgrav #include <signal.h>
43333ee039SDag-Erling Smørgrav #include <stdarg.h>
44333ee039SDag-Erling Smørgrav #include <stdio.h>
45333ee039SDag-Erling Smørgrav #include <stdlib.h>
46333ee039SDag-Erling Smørgrav #include <string.h>
47333ee039SDag-Erling Smørgrav #include <unistd.h>
48333ee039SDag-Erling Smørgrav 
49511b41d2SMark Murray #include "xmalloc.h"
50333ee039SDag-Erling Smørgrav #include "key.h"
51333ee039SDag-Erling Smørgrav #include "hostfile.h"
52333ee039SDag-Erling Smørgrav #include "ssh.h"
53e8aafc91SKris Kennaway #include "buffer.h"
54511b41d2SMark Murray #include "packet.h"
55511b41d2SMark Murray #include "uidswap.h"
56511b41d2SMark Murray #include "compat.h"
573c6ae118SKris Kennaway #include "key.h"
58e8aafc91SKris Kennaway #include "sshconnect.h"
593c6ae118SKris Kennaway #include "hostfile.h"
60ca3176e7SBrian Feldman #include "log.h"
61a0ee8cc6SDag-Erling Smørgrav #include "misc.h"
62ca3176e7SBrian Feldman #include "readconf.h"
63ca3176e7SBrian Feldman #include "atomicio.h"
64cf2b5f3bSDag-Erling Smørgrav #include "dns.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"
70acc1a9efSDag-Erling Smørgrav #include "authfd.h"
71cf2b5f3bSDag-Erling Smørgrav 
72e8aafc91SKris Kennaway char *client_version_string = NULL;
73e8aafc91SKris Kennaway char *server_version_string = NULL;
74*4f52dfbbSDag-Erling Smørgrav struct sshkey *previous_host_key = NULL;
75511b41d2SMark Murray 
76b74df5b2SDag-Erling Smørgrav static int matching_host_key_dns = 0;
77cf2b5f3bSDag-Erling Smørgrav 
784a421b63SDag-Erling Smørgrav static pid_t proxy_command_pid = 0;
794a421b63SDag-Erling Smørgrav 
8080628bacSDag-Erling Smørgrav /* import */
81511b41d2SMark Murray extern Options options;
82511b41d2SMark Murray extern char *__progname;
8380628bacSDag-Erling Smørgrav extern uid_t original_real_uid;
8480628bacSDag-Erling Smørgrav extern uid_t original_effective_uid;
85511b41d2SMark Murray 
86*4f52dfbbSDag-Erling Smørgrav static int show_other_keys(struct hostkeys *, struct sshkey *);
87*4f52dfbbSDag-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
109*4f52dfbbSDag-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 		/* Child.  Permanently give up superuser privileges. */
133f7167e0eSDag-Erling Smørgrav 		permanently_drop_suid(original_real_uid);
134f7167e0eSDag-Erling Smørgrav 
135f7167e0eSDag-Erling Smørgrav 		close(sp[1]);
136f7167e0eSDag-Erling Smørgrav 		/* Redirect stdin and stdout. */
137f7167e0eSDag-Erling Smørgrav 		if (sp[0] != 0) {
138f7167e0eSDag-Erling Smørgrav 			if (dup2(sp[0], 0) < 0)
139f7167e0eSDag-Erling Smørgrav 				perror("dup2 stdin");
140f7167e0eSDag-Erling Smørgrav 		}
141f7167e0eSDag-Erling Smørgrav 		if (sp[0] != 1) {
142f7167e0eSDag-Erling Smørgrav 			if (dup2(sp[0], 1) < 0)
143f7167e0eSDag-Erling Smørgrav 				perror("dup2 stdout");
144f7167e0eSDag-Erling Smørgrav 		}
145f7167e0eSDag-Erling Smørgrav 		if (sp[0] >= 2)
146f7167e0eSDag-Erling Smørgrav 			close(sp[0]);
147f7167e0eSDag-Erling Smørgrav 
148f7167e0eSDag-Erling Smørgrav 		/*
149f7167e0eSDag-Erling Smørgrav 		 * Stderr is left as it is so that error messages get
150f7167e0eSDag-Erling Smørgrav 		 * printed on the user's terminal.
151f7167e0eSDag-Erling Smørgrav 		 */
152f7167e0eSDag-Erling Smørgrav 		argv[0] = shell;
153f7167e0eSDag-Erling Smørgrav 		argv[1] = "-c";
154f7167e0eSDag-Erling Smørgrav 		argv[2] = command_string;
155f7167e0eSDag-Erling Smørgrav 		argv[3] = NULL;
156f7167e0eSDag-Erling Smørgrav 
157f7167e0eSDag-Erling Smørgrav 		/*
158f7167e0eSDag-Erling Smørgrav 		 * Execute the proxy command.
159f7167e0eSDag-Erling Smørgrav 		 * Note that we gave up any extra privileges above.
160f7167e0eSDag-Erling Smørgrav 		 */
161f7167e0eSDag-Erling Smørgrav 		execv(argv[0], argv);
162f7167e0eSDag-Erling Smørgrav 		perror(argv[0]);
163f7167e0eSDag-Erling Smørgrav 		exit(1);
164f7167e0eSDag-Erling Smørgrav 	}
165f7167e0eSDag-Erling Smørgrav 	/* Parent. */
166f7167e0eSDag-Erling Smørgrav 	if (pid < 0)
167f7167e0eSDag-Erling Smørgrav 		fatal("fork failed: %.100s", strerror(errno));
168f7167e0eSDag-Erling Smørgrav 	close(sp[0]);
169f7167e0eSDag-Erling Smørgrav 	free(command_string);
170f7167e0eSDag-Erling Smørgrav 
171f7167e0eSDag-Erling Smørgrav 	if ((sock = mm_receive_fd(sp[1])) == -1)
172f7167e0eSDag-Erling Smørgrav 		fatal("proxy dialer did not pass back a connection");
173acc1a9efSDag-Erling Smørgrav 	close(sp[1]);
174f7167e0eSDag-Erling Smørgrav 
175f7167e0eSDag-Erling Smørgrav 	while (waitpid(pid, NULL, 0) == -1)
176f7167e0eSDag-Erling Smørgrav 		if (errno != EINTR)
177f7167e0eSDag-Erling Smørgrav 			fatal("Couldn't wait for child: %s", strerror(errno));
178f7167e0eSDag-Erling Smørgrav 
179f7167e0eSDag-Erling Smørgrav 	/* Set the connection file descriptors. */
180*4f52dfbbSDag-Erling Smørgrav 	if (ssh_packet_set_connection(ssh, sock, sock) == NULL)
181*4f52dfbbSDag-Erling Smørgrav 		return -1; /* ssh_packet_set_connection logs error */
182f7167e0eSDag-Erling Smørgrav 
183f7167e0eSDag-Erling Smørgrav 	return 0;
184f7167e0eSDag-Erling Smørgrav }
185f7167e0eSDag-Erling Smørgrav 
186511b41d2SMark Murray /*
187511b41d2SMark Murray  * Connect to the given ssh server using a proxy command.
188511b41d2SMark Murray  */
189af12a3e7SDag-Erling Smørgrav static int
190*4f52dfbbSDag-Erling Smørgrav ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port,
191*4f52dfbbSDag-Erling Smørgrav     const char *proxy_command)
192511b41d2SMark Murray {
193f7167e0eSDag-Erling Smørgrav 	char *command_string;
194511b41d2SMark Murray 	int pin[2], pout[2];
195e8aafc91SKris Kennaway 	pid_t pid;
196f7167e0eSDag-Erling Smørgrav 	char *shell;
197420bce64SDag-Erling Smørgrav 
1984a421b63SDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
199d4af9e69SDag-Erling Smørgrav 		shell = _PATH_BSHELL;
200511b41d2SMark Murray 
201511b41d2SMark Murray 	/* Create pipes for communicating with the proxy. */
202511b41d2SMark Murray 	if (pipe(pin) < 0 || pipe(pout) < 0)
203511b41d2SMark Murray 		fatal("Could not create pipes to communicate with the proxy: %.100s",
204511b41d2SMark Murray 		    strerror(errno));
205511b41d2SMark Murray 
206f7167e0eSDag-Erling Smørgrav 	command_string = expand_proxy_command(proxy_command, options.user,
207f7167e0eSDag-Erling Smørgrav 	    host, port);
208511b41d2SMark Murray 	debug("Executing proxy command: %.500s", command_string);
209511b41d2SMark Murray 
210511b41d2SMark Murray 	/* Fork and execute the proxy command. */
211511b41d2SMark Murray 	if ((pid = fork()) == 0) {
212511b41d2SMark Murray 		char *argv[10];
213511b41d2SMark Murray 
214511b41d2SMark Murray 		/* Child.  Permanently give up superuser privileges. */
215333ee039SDag-Erling Smørgrav 		permanently_drop_suid(original_real_uid);
216511b41d2SMark Murray 
217511b41d2SMark Murray 		/* Redirect stdin and stdout. */
218511b41d2SMark Murray 		close(pin[1]);
219511b41d2SMark Murray 		if (pin[0] != 0) {
220511b41d2SMark Murray 			if (dup2(pin[0], 0) < 0)
221511b41d2SMark Murray 				perror("dup2 stdin");
222511b41d2SMark Murray 			close(pin[0]);
223511b41d2SMark Murray 		}
224511b41d2SMark Murray 		close(pout[0]);
225511b41d2SMark Murray 		if (dup2(pout[1], 1) < 0)
226511b41d2SMark Murray 			perror("dup2 stdout");
227511b41d2SMark Murray 		/* Cannot be 1 because pin allocated two descriptors. */
228511b41d2SMark Murray 		close(pout[1]);
229511b41d2SMark Murray 
230511b41d2SMark Murray 		/* Stderr is left as it is so that error messages get
231511b41d2SMark Murray 		   printed on the user's terminal. */
232d4af9e69SDag-Erling Smørgrav 		argv[0] = shell;
233511b41d2SMark Murray 		argv[1] = "-c";
234511b41d2SMark Murray 		argv[2] = command_string;
235511b41d2SMark Murray 		argv[3] = NULL;
236511b41d2SMark Murray 
237511b41d2SMark Murray 		/* Execute the proxy command.  Note that we gave up any
238511b41d2SMark Murray 		   extra privileges above. */
2394a421b63SDag-Erling Smørgrav 		signal(SIGPIPE, SIG_DFL);
240ca3176e7SBrian Feldman 		execv(argv[0], argv);
241ca3176e7SBrian Feldman 		perror(argv[0]);
242511b41d2SMark Murray 		exit(1);
243511b41d2SMark Murray 	}
244511b41d2SMark Murray 	/* Parent. */
245511b41d2SMark Murray 	if (pid < 0)
246511b41d2SMark Murray 		fatal("fork failed: %.100s", strerror(errno));
247f388f5efSDag-Erling Smørgrav 	else
248f388f5efSDag-Erling Smørgrav 		proxy_command_pid = pid; /* save pid to clean up later */
249511b41d2SMark Murray 
250511b41d2SMark Murray 	/* Close child side of the descriptors. */
251511b41d2SMark Murray 	close(pin[0]);
252511b41d2SMark Murray 	close(pout[1]);
253511b41d2SMark Murray 
254511b41d2SMark Murray 	/* Free the command name. */
255e4a9863fSDag-Erling Smørgrav 	free(command_string);
256511b41d2SMark Murray 
257511b41d2SMark Murray 	/* Set the connection file descriptors. */
258*4f52dfbbSDag-Erling Smørgrav 	if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL)
259*4f52dfbbSDag-Erling Smørgrav 		return -1; /* ssh_packet_set_connection logs error */
260511b41d2SMark Murray 
261af12a3e7SDag-Erling Smørgrav 	return 0;
262511b41d2SMark Murray }
263511b41d2SMark Murray 
2644a421b63SDag-Erling Smørgrav void
2654a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(void)
2664a421b63SDag-Erling Smørgrav {
2674a421b63SDag-Erling Smørgrav 	/*
2684a421b63SDag-Erling Smørgrav 	 * Send SIGHUP to proxy command if used. We don't wait() in
2694a421b63SDag-Erling Smørgrav 	 * case it hangs and instead rely on init to reap the child
2704a421b63SDag-Erling Smørgrav 	 */
2714a421b63SDag-Erling Smørgrav 	if (proxy_command_pid > 1)
2724a421b63SDag-Erling Smørgrav 		kill(proxy_command_pid, SIGHUP);
2734a421b63SDag-Erling Smørgrav }
2744a421b63SDag-Erling Smørgrav 
275511b41d2SMark Murray /*
276511b41d2SMark Murray  * Creates a (possibly privileged) socket for use as the ssh connection.
277511b41d2SMark Murray  */
278af12a3e7SDag-Erling Smørgrav static int
279cf2b5f3bSDag-Erling Smørgrav ssh_create_socket(int privileged, struct addrinfo *ai)
280511b41d2SMark Murray {
281f7167e0eSDag-Erling Smørgrav 	int sock, r, gaierr;
282b83788ffSDag-Erling Smørgrav 	struct addrinfo hints, *res = NULL;
283511b41d2SMark Murray 
284cf2b5f3bSDag-Erling Smørgrav 	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
285b15c8340SDag-Erling Smørgrav 	if (sock < 0) {
286f7167e0eSDag-Erling Smørgrav 		error("socket: %s", strerror(errno));
287b15c8340SDag-Erling Smørgrav 		return -1;
288b15c8340SDag-Erling Smørgrav 	}
289b15c8340SDag-Erling Smørgrav 	fcntl(sock, F_SETFD, FD_CLOEXEC);
290af12a3e7SDag-Erling Smørgrav 
291af12a3e7SDag-Erling Smørgrav 	/* Bind the socket to an alternative local IP address */
292f7167e0eSDag-Erling Smørgrav 	if (options.bind_address == NULL && !privileged)
293af12a3e7SDag-Erling Smørgrav 		return sock;
294af12a3e7SDag-Erling Smørgrav 
295b83788ffSDag-Erling Smørgrav 	if (options.bind_address) {
296af12a3e7SDag-Erling Smørgrav 		memset(&hints, 0, sizeof(hints));
297cf2b5f3bSDag-Erling Smørgrav 		hints.ai_family = ai->ai_family;
298cf2b5f3bSDag-Erling Smørgrav 		hints.ai_socktype = ai->ai_socktype;
299cf2b5f3bSDag-Erling Smørgrav 		hints.ai_protocol = ai->ai_protocol;
300af12a3e7SDag-Erling Smørgrav 		hints.ai_flags = AI_PASSIVE;
301d4af9e69SDag-Erling Smørgrav 		gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
302af12a3e7SDag-Erling Smørgrav 		if (gaierr) {
303af12a3e7SDag-Erling Smørgrav 			error("getaddrinfo: %s: %s", options.bind_address,
304d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(gaierr));
305af12a3e7SDag-Erling Smørgrav 			close(sock);
306af12a3e7SDag-Erling Smørgrav 			return -1;
307511b41d2SMark Murray 		}
308b83788ffSDag-Erling Smørgrav 	}
309f7167e0eSDag-Erling Smørgrav 	/*
310f7167e0eSDag-Erling Smørgrav 	 * If we are running as root and want to connect to a privileged
311f7167e0eSDag-Erling Smørgrav 	 * port, bind our own socket to a privileged port.
312f7167e0eSDag-Erling Smørgrav 	 */
313f7167e0eSDag-Erling Smørgrav 	if (privileged) {
314f7167e0eSDag-Erling Smørgrav 		PRIV_START;
315b83788ffSDag-Erling Smørgrav 		r = bindresvport_sa(sock, res ? res->ai_addr : NULL);
316f7167e0eSDag-Erling Smørgrav 		PRIV_END;
317f7167e0eSDag-Erling Smørgrav 		if (r < 0) {
318f7167e0eSDag-Erling Smørgrav 			error("bindresvport_sa: af=%d %s", ai->ai_family,
319f7167e0eSDag-Erling Smørgrav 			    strerror(errno));
320f7167e0eSDag-Erling Smørgrav 			goto fail;
321f7167e0eSDag-Erling Smørgrav 		}
322f7167e0eSDag-Erling Smørgrav 	} else {
323af12a3e7SDag-Erling Smørgrav 		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
324f7167e0eSDag-Erling Smørgrav 			error("bind: %s: %s", options.bind_address,
325f7167e0eSDag-Erling Smørgrav 			    strerror(errno));
326f7167e0eSDag-Erling Smørgrav  fail:
327af12a3e7SDag-Erling Smørgrav 			close(sock);
328af12a3e7SDag-Erling Smørgrav 			freeaddrinfo(res);
329af12a3e7SDag-Erling Smørgrav 			return -1;
330af12a3e7SDag-Erling Smørgrav 		}
331f7167e0eSDag-Erling Smørgrav 	}
332b83788ffSDag-Erling Smørgrav 	if (res != NULL)
333af12a3e7SDag-Erling Smørgrav 		freeaddrinfo(res);
334511b41d2SMark Murray 	return sock;
335511b41d2SMark Murray }
336511b41d2SMark Murray 
337*4f52dfbbSDag-Erling Smørgrav /*
338*4f52dfbbSDag-Erling Smørgrav  * Wait up to *timeoutp milliseconds for fd to be readable. Updates
339*4f52dfbbSDag-Erling Smørgrav  * *timeoutp with time remaining.
340*4f52dfbbSDag-Erling Smørgrav  * Returns 0 if fd ready or -1 on timeout or error (see errno).
341*4f52dfbbSDag-Erling Smørgrav  */
342*4f52dfbbSDag-Erling Smørgrav static int
343*4f52dfbbSDag-Erling Smørgrav waitrfd(int fd, int *timeoutp)
344*4f52dfbbSDag-Erling Smørgrav {
345*4f52dfbbSDag-Erling Smørgrav 	struct pollfd pfd;
346*4f52dfbbSDag-Erling Smørgrav 	struct timeval t_start;
347*4f52dfbbSDag-Erling Smørgrav 	int oerrno, r;
348*4f52dfbbSDag-Erling Smørgrav 
349*4f52dfbbSDag-Erling Smørgrav 	gettimeofday(&t_start, NULL);
350*4f52dfbbSDag-Erling Smørgrav 	pfd.fd = fd;
351*4f52dfbbSDag-Erling Smørgrav 	pfd.events = POLLIN;
352*4f52dfbbSDag-Erling Smørgrav 	for (; *timeoutp >= 0;) {
353*4f52dfbbSDag-Erling Smørgrav 		r = poll(&pfd, 1, *timeoutp);
354*4f52dfbbSDag-Erling Smørgrav 		oerrno = errno;
355*4f52dfbbSDag-Erling Smørgrav 		ms_subtract_diff(&t_start, timeoutp);
356*4f52dfbbSDag-Erling Smørgrav 		errno = oerrno;
357*4f52dfbbSDag-Erling Smørgrav 		if (r > 0)
358*4f52dfbbSDag-Erling Smørgrav 			return 0;
359*4f52dfbbSDag-Erling Smørgrav 		else if (r == -1 && errno != EAGAIN)
360*4f52dfbbSDag-Erling Smørgrav 			return -1;
361*4f52dfbbSDag-Erling Smørgrav 		else if (r == 0)
362*4f52dfbbSDag-Erling Smørgrav 			break;
363*4f52dfbbSDag-Erling Smørgrav 	}
364*4f52dfbbSDag-Erling Smørgrav 	/* timeout */
365*4f52dfbbSDag-Erling Smørgrav 	errno = ETIMEDOUT;
366*4f52dfbbSDag-Erling Smørgrav 	return -1;
367*4f52dfbbSDag-Erling Smørgrav }
368*4f52dfbbSDag-Erling Smørgrav 
369cf2b5f3bSDag-Erling Smørgrav static int
370cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr,
371d4af9e69SDag-Erling Smørgrav     socklen_t addrlen, int *timeoutp)
372cf2b5f3bSDag-Erling Smørgrav {
373*4f52dfbbSDag-Erling Smørgrav 	int optval = 0;
374*4f52dfbbSDag-Erling Smørgrav 	socklen_t optlen = sizeof(optval);
375cf2b5f3bSDag-Erling Smørgrav 
376*4f52dfbbSDag-Erling Smørgrav 	/* No timeout: just do a blocking connect() */
377*4f52dfbbSDag-Erling Smørgrav 	if (*timeoutp <= 0)
378*4f52dfbbSDag-Erling Smørgrav 		return connect(sockfd, serv_addr, addrlen);
379cf2b5f3bSDag-Erling Smørgrav 
3801ec0d754SDag-Erling Smørgrav 	set_nonblock(sockfd);
381*4f52dfbbSDag-Erling Smørgrav 	if (connect(sockfd, serv_addr, addrlen) == 0) {
382*4f52dfbbSDag-Erling Smørgrav 		/* Succeeded already? */
3831ec0d754SDag-Erling Smørgrav 		unset_nonblock(sockfd);
384*4f52dfbbSDag-Erling Smørgrav 		return 0;
385*4f52dfbbSDag-Erling Smørgrav 	} else if (errno != EINPROGRESS)
386*4f52dfbbSDag-Erling Smørgrav 		return -1;
387cf2b5f3bSDag-Erling Smørgrav 
388*4f52dfbbSDag-Erling Smørgrav 	if (waitrfd(sockfd, timeoutp) == -1)
389*4f52dfbbSDag-Erling Smørgrav 		return -1;
390cf2b5f3bSDag-Erling Smørgrav 
391cf2b5f3bSDag-Erling Smørgrav 	/* Completed or failed */
392*4f52dfbbSDag-Erling Smørgrav 	if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) {
393cf2b5f3bSDag-Erling Smørgrav 		debug("getsockopt: %s", strerror(errno));
394*4f52dfbbSDag-Erling Smørgrav 		return -1;
395cf2b5f3bSDag-Erling Smørgrav 	}
396cf2b5f3bSDag-Erling Smørgrav 	if (optval != 0) {
397cf2b5f3bSDag-Erling Smørgrav 		errno = optval;
398*4f52dfbbSDag-Erling Smørgrav 		return -1;
399cf2b5f3bSDag-Erling Smørgrav 	}
4001ec0d754SDag-Erling Smørgrav 	unset_nonblock(sockfd);
401*4f52dfbbSDag-Erling Smørgrav 	return 0;
402cf2b5f3bSDag-Erling Smørgrav }
403cf2b5f3bSDag-Erling Smørgrav 
404511b41d2SMark Murray /*
405511b41d2SMark Murray  * Opens a TCP/IP connection to the remote server on the given host.
406511b41d2SMark Murray  * The address of the remote host will be returned in hostaddr.
40780628bacSDag-Erling Smørgrav  * If port is 0, the default port will be used.  If needpriv is true,
408511b41d2SMark Murray  * a privileged port will be allocated to make the connection.
40980628bacSDag-Erling Smørgrav  * This requires super-user privileges if needpriv is true.
410511b41d2SMark Murray  * Connection_attempts specifies the maximum number of tries (one per
411511b41d2SMark Murray  * second).  If proxy_command is non-NULL, it specifies the command (with %h
412511b41d2SMark Murray  * and %p substituted for host and port, respectively) to use to contact
413511b41d2SMark Murray  * the daemon.
414511b41d2SMark Murray  */
415f7167e0eSDag-Erling Smørgrav static int
416*4f52dfbbSDag-Erling Smørgrav ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop,
417f7167e0eSDag-Erling Smørgrav     struct sockaddr_storage *hostaddr, u_short port, int family,
418f7167e0eSDag-Erling Smørgrav     int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
419511b41d2SMark Murray {
420ca3176e7SBrian Feldman 	int on = 1;
421ca3176e7SBrian Feldman 	int sock = -1, attempt;
422ca3176e7SBrian Feldman 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
423f7167e0eSDag-Erling Smørgrav 	struct addrinfo *ai;
424511b41d2SMark Murray 
425acc1a9efSDag-Erling Smørgrav 	debug2("%s: needpriv %d", __func__, needpriv);
426acc1a9efSDag-Erling Smørgrav 	memset(ntop, 0, sizeof(ntop));
427acc1a9efSDag-Erling Smørgrav 	memset(strport, 0, sizeof(strport));
428511b41d2SMark Murray 
429333ee039SDag-Erling Smørgrav 	for (attempt = 0; attempt < connection_attempts; attempt++) {
43062efe23aSDag-Erling Smørgrav 		if (attempt > 0) {
43162efe23aSDag-Erling Smørgrav 			/* Sleep a moment before retrying. */
43262efe23aSDag-Erling Smørgrav 			sleep(1);
433511b41d2SMark Murray 			debug("Trying again...");
43462efe23aSDag-Erling Smørgrav 		}
435333ee039SDag-Erling Smørgrav 		/*
436333ee039SDag-Erling Smørgrav 		 * Loop through addresses for this host, and try each one in
437333ee039SDag-Erling Smørgrav 		 * sequence until the connection succeeds.
438333ee039SDag-Erling Smørgrav 		 */
439511b41d2SMark Murray 		for (ai = aitop; ai; ai = ai->ai_next) {
440f7167e0eSDag-Erling Smørgrav 			if (ai->ai_family != AF_INET &&
441f7167e0eSDag-Erling Smørgrav 			    ai->ai_family != AF_INET6)
442511b41d2SMark Murray 				continue;
443511b41d2SMark Murray 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
444511b41d2SMark Murray 			    ntop, sizeof(ntop), strport, sizeof(strport),
445511b41d2SMark Murray 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
446acc1a9efSDag-Erling Smørgrav 				error("%s: getnameinfo failed", __func__);
447511b41d2SMark Murray 				continue;
448511b41d2SMark Murray 			}
449511b41d2SMark Murray 			debug("Connecting to %.200s [%.100s] port %s.",
4501f5ce8f4SBrian Feldman 				host, ntop, strport);
451511b41d2SMark Murray 
452511b41d2SMark Murray 			/* Create a socket for connecting. */
453cf2b5f3bSDag-Erling Smørgrav 			sock = ssh_create_socket(needpriv, ai);
454511b41d2SMark Murray 			if (sock < 0)
455af12a3e7SDag-Erling Smørgrav 				/* Any error is already output */
456511b41d2SMark Murray 				continue;
457511b41d2SMark Murray 
458cf2b5f3bSDag-Erling Smørgrav 			if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
459d4af9e69SDag-Erling Smørgrav 			    timeout_ms) >= 0) {
460511b41d2SMark Murray 				/* Successful connection. */
461c322fe35SKris Kennaway 				memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
462511b41d2SMark Murray 				break;
463511b41d2SMark Murray 			} else {
464f388f5efSDag-Erling Smørgrav 				debug("connect to address %s port %s: %s",
465f388f5efSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
466511b41d2SMark Murray 				close(sock);
467333ee039SDag-Erling Smørgrav 				sock = -1;
468511b41d2SMark Murray 			}
469511b41d2SMark Murray 		}
470333ee039SDag-Erling Smørgrav 		if (sock != -1)
471511b41d2SMark Murray 			break;	/* Successful connection. */
472511b41d2SMark Murray 	}
473511b41d2SMark Murray 
474511b41d2SMark Murray 	/* Return failure if we didn't get a successful connection. */
475333ee039SDag-Erling Smørgrav 	if (sock == -1) {
476aa49c926SDag-Erling Smørgrav 		error("ssh: connect to host %s port %s: %s",
477f388f5efSDag-Erling Smørgrav 		    host, strport, strerror(errno));
478aa49c926SDag-Erling Smørgrav 		return (-1);
479f388f5efSDag-Erling Smørgrav 	}
480511b41d2SMark Murray 
481511b41d2SMark Murray 	debug("Connection established.");
482511b41d2SMark Murray 
4831ec0d754SDag-Erling Smørgrav 	/* Set SO_KEEPALIVE if requested. */
484d4af9e69SDag-Erling Smørgrav 	if (want_keepalive &&
485ca3176e7SBrian Feldman 	    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
486ca3176e7SBrian Feldman 	    sizeof(on)) < 0)
487ca3176e7SBrian Feldman 		error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
488ca3176e7SBrian Feldman 
489511b41d2SMark Murray 	/* Set the connection. */
490*4f52dfbbSDag-Erling Smørgrav 	if (ssh_packet_set_connection(ssh, sock, sock) == NULL)
491*4f52dfbbSDag-Erling Smørgrav 		return -1; /* ssh_packet_set_connection logs error */
492511b41d2SMark Murray 
493af12a3e7SDag-Erling Smørgrav         return 0;
494511b41d2SMark Murray }
495511b41d2SMark Murray 
496f7167e0eSDag-Erling Smørgrav int
497*4f52dfbbSDag-Erling Smørgrav ssh_connect(struct ssh *ssh, const char *host, struct addrinfo *addrs,
498f7167e0eSDag-Erling Smørgrav     struct sockaddr_storage *hostaddr, u_short port, int family,
499f7167e0eSDag-Erling Smørgrav     int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
500f7167e0eSDag-Erling Smørgrav {
501f7167e0eSDag-Erling Smørgrav 	if (options.proxy_command == NULL) {
502*4f52dfbbSDag-Erling Smørgrav 		return ssh_connect_direct(ssh, host, addrs, hostaddr, port,
503*4f52dfbbSDag-Erling Smørgrav 		    family, connection_attempts, timeout_ms, want_keepalive,
504*4f52dfbbSDag-Erling Smørgrav 		    needpriv);
505f7167e0eSDag-Erling Smørgrav 	} else if (strcmp(options.proxy_command, "-") == 0) {
506*4f52dfbbSDag-Erling Smørgrav 		if ((ssh_packet_set_connection(ssh,
507*4f52dfbbSDag-Erling Smørgrav 		    STDIN_FILENO, STDOUT_FILENO)) == NULL)
508*4f52dfbbSDag-Erling Smørgrav 			return -1; /* ssh_packet_set_connection logs error */
509*4f52dfbbSDag-Erling Smørgrav 		return 0;
510f7167e0eSDag-Erling Smørgrav 	} else if (options.proxy_use_fdpass) {
511*4f52dfbbSDag-Erling Smørgrav 		return ssh_proxy_fdpass_connect(ssh, host, port,
512f7167e0eSDag-Erling Smørgrav 		    options.proxy_command);
513f7167e0eSDag-Erling Smørgrav 	}
514*4f52dfbbSDag-Erling Smørgrav 	return ssh_proxy_connect(ssh, host, port, options.proxy_command);
515f7167e0eSDag-Erling Smørgrav }
516f7167e0eSDag-Erling Smørgrav 
5176888a9beSDag-Erling Smørgrav static void
5186888a9beSDag-Erling Smørgrav send_client_banner(int connection_out, int minor1)
5196888a9beSDag-Erling Smørgrav {
5206888a9beSDag-Erling Smørgrav 	/* Send our own protocol version identification. */
521*4f52dfbbSDag-Erling Smørgrav 	xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s\n",
522*4f52dfbbSDag-Erling Smørgrav 	    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
5236888a9beSDag-Erling Smørgrav 	    *options.version_addendum == '\0' ? "" : " ",
524*4f52dfbbSDag-Erling Smørgrav 	    options.version_addendum);
525acc1a9efSDag-Erling Smørgrav 	if (atomicio(vwrite, connection_out, client_version_string,
5266888a9beSDag-Erling Smørgrav 	    strlen(client_version_string)) != strlen(client_version_string))
5276888a9beSDag-Erling Smørgrav 		fatal("write: %.100s", strerror(errno));
5286888a9beSDag-Erling Smørgrav 	chop(client_version_string);
5296888a9beSDag-Erling Smørgrav 	debug("Local version string %.100s", client_version_string);
5306888a9beSDag-Erling Smørgrav }
5316888a9beSDag-Erling Smørgrav 
532511b41d2SMark Murray /*
533e8aafc91SKris Kennaway  * Waits for the server identification string, and sends our own
534e8aafc91SKris Kennaway  * identification string.
535511b41d2SMark Murray  */
5367aee6ffeSDag-Erling Smørgrav void
537d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(int timeout_ms)
538511b41d2SMark Murray {
539e8aafc91SKris Kennaway 	char buf[256], remote_version[256];	/* must be same size! */
540d4ecd108SDag-Erling Smørgrav 	int remote_major, remote_minor, mismatch;
541e8aafc91SKris Kennaway 	int connection_in = packet_get_connection_in();
542e8aafc91SKris Kennaway 	int connection_out = packet_get_connection_out();
543333ee039SDag-Erling Smørgrav 	u_int i, n;
544d4af9e69SDag-Erling Smørgrav 	size_t len;
545*4f52dfbbSDag-Erling Smørgrav 	int rc;
546d4af9e69SDag-Erling Smørgrav 
5476888a9beSDag-Erling Smørgrav 	send_client_banner(connection_out, 0);
5486888a9beSDag-Erling Smørgrav 
549d4ecd108SDag-Erling Smørgrav 	/* Read other side's version identification. */
550333ee039SDag-Erling Smørgrav 	for (n = 0;;) {
551e8aafc91SKris Kennaway 		for (i = 0; i < sizeof(buf) - 1; i++) {
552d4af9e69SDag-Erling Smørgrav 			if (timeout_ms > 0) {
553*4f52dfbbSDag-Erling Smørgrav 				rc = waitrfd(connection_in, &timeout_ms);
554*4f52dfbbSDag-Erling Smørgrav 				if (rc == -1 && errno == ETIMEDOUT) {
555d4af9e69SDag-Erling Smørgrav 					fatal("Connection timed out during "
556d4af9e69SDag-Erling Smørgrav 					    "banner exchange");
557*4f52dfbbSDag-Erling Smørgrav 				} else if (rc == -1) {
558*4f52dfbbSDag-Erling Smørgrav 					fatal("%s: %s",
559*4f52dfbbSDag-Erling Smørgrav 					    __func__, strerror(errno));
560d4af9e69SDag-Erling Smørgrav 				}
561d4af9e69SDag-Erling Smørgrav 			}
562d4af9e69SDag-Erling Smørgrav 
563acc1a9efSDag-Erling Smørgrav 			len = atomicio(read, connection_in, &buf[i], 1);
564d4ecd108SDag-Erling Smørgrav 			if (len != 1 && errno == EPIPE)
565d4af9e69SDag-Erling Smørgrav 				fatal("ssh_exchange_identification: "
566d4af9e69SDag-Erling Smørgrav 				    "Connection closed by remote host");
567d4ecd108SDag-Erling Smørgrav 			else if (len != 1)
568d4af9e69SDag-Erling Smørgrav 				fatal("ssh_exchange_identification: "
569d4af9e69SDag-Erling Smørgrav 				    "read: %.100s", strerror(errno));
570e8aafc91SKris Kennaway 			if (buf[i] == '\r') {
571e8aafc91SKris Kennaway 				buf[i] = '\n';
572e8aafc91SKris Kennaway 				buf[i + 1] = 0;
573e8aafc91SKris Kennaway 				continue;		/**XXX wait for \n */
574511b41d2SMark Murray 			}
575e8aafc91SKris Kennaway 			if (buf[i] == '\n') {
576e8aafc91SKris Kennaway 				buf[i + 1] = 0;
577511b41d2SMark Murray 				break;
578e8aafc91SKris Kennaway 			}
579333ee039SDag-Erling Smørgrav 			if (++n > 65536)
580d4af9e69SDag-Erling Smørgrav 				fatal("ssh_exchange_identification: "
581d4af9e69SDag-Erling Smørgrav 				    "No banner received");
582e8aafc91SKris Kennaway 		}
583e8aafc91SKris Kennaway 		buf[sizeof(buf) - 1] = 0;
584c2d3a559SKris Kennaway 		if (strncmp(buf, "SSH-", 4) == 0)
585c2d3a559SKris Kennaway 			break;
586c2d3a559SKris Kennaway 		debug("ssh_exchange_identification: %s", buf);
587c2d3a559SKris Kennaway 	}
588e8aafc91SKris Kennaway 	server_version_string = xstrdup(buf);
589511b41d2SMark Murray 
590511b41d2SMark Murray 	/*
591e8aafc91SKris Kennaway 	 * Check that the versions match.  In future this might accept
592e8aafc91SKris Kennaway 	 * several versions and set appropriate flags to handle them.
593511b41d2SMark Murray 	 */
594e8aafc91SKris Kennaway 	if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
595e8aafc91SKris Kennaway 	    &remote_major, &remote_minor, remote_version) != 3)
596e8aafc91SKris Kennaway 		fatal("Bad remote protocol version identification: '%.100s'", buf);
597e8aafc91SKris Kennaway 	debug("Remote protocol version %d.%d, remote software version %.100s",
598e8aafc91SKris Kennaway 	    remote_major, remote_minor, remote_version);
599511b41d2SMark Murray 
600bc5531deSDag-Erling Smørgrav 	active_state->compat = compat_datafellows(remote_version);
601e8aafc91SKris Kennaway 	mismatch = 0;
602e8aafc91SKris Kennaway 
603e8aafc91SKris Kennaway 	switch (remote_major) {
604*4f52dfbbSDag-Erling Smørgrav 	case 2:
605511b41d2SMark Murray 		break;
606*4f52dfbbSDag-Erling Smørgrav 	case 1:
607*4f52dfbbSDag-Erling Smørgrav 		if (remote_minor != 99)
608e8aafc91SKris Kennaway 			mismatch = 1;
609e8aafc91SKris Kennaway 		break;
610511b41d2SMark Murray 	default:
611e8aafc91SKris Kennaway 		mismatch = 1;
612e8aafc91SKris Kennaway 		break;
613511b41d2SMark Murray 	}
614e8aafc91SKris Kennaway 	if (mismatch)
615e8aafc91SKris Kennaway 		fatal("Protocol major versions differ: %d vs. %d",
616*4f52dfbbSDag-Erling Smørgrav 		    PROTOCOL_MAJOR_2, remote_major);
617f7167e0eSDag-Erling Smørgrav 	if ((datafellows & SSH_BUG_DERIVEKEY) != 0)
618f7167e0eSDag-Erling Smørgrav 		fatal("Server version \"%.100s\" uses unsafe key agreement; "
619f7167e0eSDag-Erling Smørgrav 		    "refusing connection", remote_version);
620f7167e0eSDag-Erling Smørgrav 	if ((datafellows & SSH_BUG_RSASIGMD5) != 0)
621f7167e0eSDag-Erling Smørgrav 		logit("Server version \"%.100s\" uses unsafe RSA signature "
622f7167e0eSDag-Erling Smørgrav 		    "scheme; disabling use of RSA keys", remote_version);
623e8aafc91SKris Kennaway 	chop(server_version_string);
624511b41d2SMark Murray }
625511b41d2SMark Murray 
626ca3176e7SBrian Feldman /* defaults to 'no' */
627af12a3e7SDag-Erling Smørgrav static int
628af12a3e7SDag-Erling Smørgrav confirm(const char *prompt)
629511b41d2SMark Murray {
630af12a3e7SDag-Erling Smørgrav 	const char *msg, *again = "Please type 'yes' or 'no': ";
631af12a3e7SDag-Erling Smørgrav 	char *p;
632af12a3e7SDag-Erling Smørgrav 	int ret = -1;
633511b41d2SMark Murray 
634ca3176e7SBrian Feldman 	if (options.batch_mode)
635ca3176e7SBrian Feldman 		return 0;
636af12a3e7SDag-Erling Smørgrav 	for (msg = prompt;;msg = again) {
637af12a3e7SDag-Erling Smørgrav 		p = read_passphrase(msg, RP_ECHO);
638af12a3e7SDag-Erling Smørgrav 		if (p == NULL ||
639af12a3e7SDag-Erling Smørgrav 		    (p[0] == '\0') || (p[0] == '\n') ||
640af12a3e7SDag-Erling Smørgrav 		    strncasecmp(p, "no", 2) == 0)
641af12a3e7SDag-Erling Smørgrav 			ret = 0;
642f388f5efSDag-Erling Smørgrav 		if (p && strncasecmp(p, "yes", 3) == 0)
643af12a3e7SDag-Erling Smørgrav 			ret = 1;
644e4a9863fSDag-Erling Smørgrav 		free(p);
645af12a3e7SDag-Erling Smørgrav 		if (ret != -1)
646af12a3e7SDag-Erling Smørgrav 			return ret;
647511b41d2SMark Murray 	}
648511b41d2SMark Murray }
649511b41d2SMark Murray 
650b15c8340SDag-Erling Smørgrav static int
651*4f52dfbbSDag-Erling Smørgrav check_host_cert(const char *host, const struct sshkey *host_key)
652b15c8340SDag-Erling Smørgrav {
653b15c8340SDag-Erling Smørgrav 	const char *reason;
654b15c8340SDag-Erling Smørgrav 
655b15c8340SDag-Erling Smørgrav 	if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) {
656b15c8340SDag-Erling Smørgrav 		error("%s", reason);
657b15c8340SDag-Erling Smørgrav 		return 0;
658b15c8340SDag-Erling Smørgrav 	}
659a0ee8cc6SDag-Erling Smørgrav 	if (buffer_len(host_key->cert->critical) != 0) {
660e2f6069cSDag-Erling Smørgrav 		error("Certificate for %s contains unsupported "
661e2f6069cSDag-Erling Smørgrav 		    "critical options(s)", host);
662b15c8340SDag-Erling Smørgrav 		return 0;
663b15c8340SDag-Erling Smørgrav 	}
664b15c8340SDag-Erling Smørgrav 	return 1;
665b15c8340SDag-Erling Smørgrav }
666b15c8340SDag-Erling Smørgrav 
6674a421b63SDag-Erling Smørgrav static int
6684a421b63SDag-Erling Smørgrav sockaddr_is_local(struct sockaddr *hostaddr)
6694a421b63SDag-Erling Smørgrav {
6704a421b63SDag-Erling Smørgrav 	switch (hostaddr->sa_family) {
6714a421b63SDag-Erling Smørgrav 	case AF_INET:
6724a421b63SDag-Erling Smørgrav 		return (ntohl(((struct sockaddr_in *)hostaddr)->
6734a421b63SDag-Erling Smørgrav 		    sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
6744a421b63SDag-Erling Smørgrav 	case AF_INET6:
6754a421b63SDag-Erling Smørgrav 		return IN6_IS_ADDR_LOOPBACK(
6764a421b63SDag-Erling Smørgrav 		    &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
6774a421b63SDag-Erling Smørgrav 	default:
6784a421b63SDag-Erling Smørgrav 		return 0;
6794a421b63SDag-Erling Smørgrav 	}
6804a421b63SDag-Erling Smørgrav }
6814a421b63SDag-Erling Smørgrav 
6824a421b63SDag-Erling Smørgrav /*
6834a421b63SDag-Erling Smørgrav  * Prepare the hostname and ip address strings that are used to lookup
6844a421b63SDag-Erling Smørgrav  * host keys in known_hosts files. These may have a port number appended.
6854a421b63SDag-Erling Smørgrav  */
6864a421b63SDag-Erling Smørgrav void
6874a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr,
6884a421b63SDag-Erling Smørgrav     u_short port, char **hostfile_hostname, char **hostfile_ipaddr)
6894a421b63SDag-Erling Smørgrav {
6904a421b63SDag-Erling Smørgrav 	char ntop[NI_MAXHOST];
6914a421b63SDag-Erling Smørgrav 	socklen_t addrlen;
6924a421b63SDag-Erling Smørgrav 
6934a421b63SDag-Erling Smørgrav 	switch (hostaddr == NULL ? -1 : hostaddr->sa_family) {
6944a421b63SDag-Erling Smørgrav 	case -1:
6954a421b63SDag-Erling Smørgrav 		addrlen = 0;
6964a421b63SDag-Erling Smørgrav 		break;
6974a421b63SDag-Erling Smørgrav 	case AF_INET:
6984a421b63SDag-Erling Smørgrav 		addrlen = sizeof(struct sockaddr_in);
6994a421b63SDag-Erling Smørgrav 		break;
7004a421b63SDag-Erling Smørgrav 	case AF_INET6:
7014a421b63SDag-Erling Smørgrav 		addrlen = sizeof(struct sockaddr_in6);
7024a421b63SDag-Erling Smørgrav 		break;
7034a421b63SDag-Erling Smørgrav 	default:
7044a421b63SDag-Erling Smørgrav 		addrlen = sizeof(struct sockaddr);
7054a421b63SDag-Erling Smørgrav 		break;
7064a421b63SDag-Erling Smørgrav 	}
7074a421b63SDag-Erling Smørgrav 
7084a421b63SDag-Erling Smørgrav 	/*
7094a421b63SDag-Erling Smørgrav 	 * We don't have the remote ip-address for connections
7104a421b63SDag-Erling Smørgrav 	 * using a proxy command
7114a421b63SDag-Erling Smørgrav 	 */
7124a421b63SDag-Erling Smørgrav 	if (hostfile_ipaddr != NULL) {
7134a421b63SDag-Erling Smørgrav 		if (options.proxy_command == NULL) {
7144a421b63SDag-Erling Smørgrav 			if (getnameinfo(hostaddr, addrlen,
7154a421b63SDag-Erling Smørgrav 			    ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0)
716bc5531deSDag-Erling Smørgrav 			fatal("%s: getnameinfo failed", __func__);
7174a421b63SDag-Erling Smørgrav 			*hostfile_ipaddr = put_host_port(ntop, port);
7184a421b63SDag-Erling Smørgrav 		} else {
7194a421b63SDag-Erling Smørgrav 			*hostfile_ipaddr = xstrdup("<no hostip for proxy "
7204a421b63SDag-Erling Smørgrav 			    "command>");
7214a421b63SDag-Erling Smørgrav 		}
7224a421b63SDag-Erling Smørgrav 	}
7234a421b63SDag-Erling Smørgrav 
7244a421b63SDag-Erling Smørgrav 	/*
7254a421b63SDag-Erling Smørgrav 	 * Allow the user to record the key under a different name or
7264a421b63SDag-Erling Smørgrav 	 * differentiate a non-standard port.  This is useful for ssh
7274a421b63SDag-Erling Smørgrav 	 * tunneling over forwarded connections or if you run multiple
7284a421b63SDag-Erling Smørgrav 	 * sshd's on different ports on the same machine.
7294a421b63SDag-Erling Smørgrav 	 */
7304a421b63SDag-Erling Smørgrav 	if (hostfile_hostname != NULL) {
7314a421b63SDag-Erling Smørgrav 		if (options.host_key_alias != NULL) {
7324a421b63SDag-Erling Smørgrav 			*hostfile_hostname = xstrdup(options.host_key_alias);
7334a421b63SDag-Erling Smørgrav 			debug("using hostkeyalias: %s", *hostfile_hostname);
7344a421b63SDag-Erling Smørgrav 		} else {
7354a421b63SDag-Erling Smørgrav 			*hostfile_hostname = put_host_port(hostname, port);
7364a421b63SDag-Erling Smørgrav 		}
7374a421b63SDag-Erling Smørgrav 	}
7384a421b63SDag-Erling Smørgrav }
7394a421b63SDag-Erling Smørgrav 
740e8aafc91SKris Kennaway /*
741af12a3e7SDag-Erling Smørgrav  * check whether the supplied host key is valid, return -1 if the key
742e146993eSDag-Erling Smørgrav  * is not valid. user_hostfile[0] will not be updated if 'readonly' is true.
743e8aafc91SKris Kennaway  */
744333ee039SDag-Erling Smørgrav #define RDRW	0
745333ee039SDag-Erling Smørgrav #define RDONLY	1
746333ee039SDag-Erling Smørgrav #define ROQUIET	2
747af12a3e7SDag-Erling Smørgrav static int
748333ee039SDag-Erling Smørgrav check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
749*4f52dfbbSDag-Erling Smørgrav     struct sshkey *host_key, int readonly,
750e146993eSDag-Erling Smørgrav     char **user_hostfiles, u_int num_user_hostfiles,
751e146993eSDag-Erling Smørgrav     char **system_hostfiles, u_int num_system_hostfiles)
752511b41d2SMark Murray {
753e8aafc91SKris Kennaway 	HostStatus host_status;
754e8aafc91SKris Kennaway 	HostStatus ip_status;
755*4f52dfbbSDag-Erling Smørgrav 	struct sshkey *raw_key = NULL;
756e146993eSDag-Erling Smørgrav 	char *ip = NULL, *host = NULL;
757e146993eSDag-Erling Smørgrav 	char hostline[1000], *hostp, *fp, *ra;
758af12a3e7SDag-Erling Smørgrav 	char msg[1024];
759e146993eSDag-Erling Smørgrav 	const char *type;
7604a421b63SDag-Erling Smørgrav 	const struct hostkey_entry *host_found, *ip_found;
761e146993eSDag-Erling Smørgrav 	int len, cancelled_forwarding = 0;
762e146993eSDag-Erling Smørgrav 	int local = sockaddr_is_local(hostaddr);
763*4f52dfbbSDag-Erling Smørgrav 	int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0;
764bc5531deSDag-Erling Smørgrav 	int hostkey_trusted = 0; /* Known or explicitly accepted by user */
765e146993eSDag-Erling Smørgrav 	struct hostkeys *host_hostkeys, *ip_hostkeys;
766e146993eSDag-Erling Smørgrav 	u_int i;
767511b41d2SMark Murray 
768e8aafc91SKris Kennaway 	/*
769e8aafc91SKris Kennaway 	 * Force accepting of the host key for loopback/localhost. The
770e8aafc91SKris Kennaway 	 * problem is that if the home directory is NFS-mounted to multiple
771e8aafc91SKris Kennaway 	 * machines, localhost will refer to a different machine in each of
772e8aafc91SKris Kennaway 	 * them, and the user will get bogus HOST_CHANGED warnings.  This
773e8aafc91SKris Kennaway 	 * essentially disables host authentication for localhost; however,
774e8aafc91SKris Kennaway 	 * this is probably not a real problem.
775e8aafc91SKris Kennaway 	 */
776af12a3e7SDag-Erling Smørgrav 	if (options.no_host_authentication_for_localhost == 1 && local &&
777af12a3e7SDag-Erling Smørgrav 	    options.host_key_alias == NULL) {
778ca3176e7SBrian Feldman 		debug("Forcing accepting of host key for "
779ca3176e7SBrian Feldman 		    "loopback/localhost.");
780af12a3e7SDag-Erling Smørgrav 		return 0;
781511b41d2SMark Murray 	}
782511b41d2SMark Murray 
783e8aafc91SKris Kennaway 	/*
7844a421b63SDag-Erling Smørgrav 	 * Prepare the hostname and address strings used for hostkey lookup.
7854a421b63SDag-Erling Smørgrav 	 * In some cases, these will have a port number appended.
786e8aafc91SKris Kennaway 	 */
7874a421b63SDag-Erling Smørgrav 	get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip);
788d4af9e69SDag-Erling Smørgrav 
789ca3176e7SBrian Feldman 	/*
790ca3176e7SBrian Feldman 	 * Turn off check_host_ip if the connection is to localhost, via proxy
791ca3176e7SBrian Feldman 	 * command or if we don't have a hostname to compare with
792ca3176e7SBrian Feldman 	 */
793333ee039SDag-Erling Smørgrav 	if (options.check_host_ip && (local ||
794333ee039SDag-Erling Smørgrav 	    strcmp(hostname, ip) == 0 || options.proxy_command != NULL))
795ca3176e7SBrian Feldman 		options.check_host_ip = 0;
796ca3176e7SBrian Feldman 
7974a421b63SDag-Erling Smørgrav 	host_hostkeys = init_hostkeys();
798e146993eSDag-Erling Smørgrav 	for (i = 0; i < num_user_hostfiles; i++)
799e146993eSDag-Erling Smørgrav 		load_hostkeys(host_hostkeys, host, user_hostfiles[i]);
800e146993eSDag-Erling Smørgrav 	for (i = 0; i < num_system_hostfiles; i++)
801e146993eSDag-Erling Smørgrav 		load_hostkeys(host_hostkeys, host, system_hostfiles[i]);
8024a421b63SDag-Erling Smørgrav 
8034a421b63SDag-Erling Smørgrav 	ip_hostkeys = NULL;
8044a421b63SDag-Erling Smørgrav 	if (!want_cert && options.check_host_ip) {
8054a421b63SDag-Erling Smørgrav 		ip_hostkeys = init_hostkeys();
806e146993eSDag-Erling Smørgrav 		for (i = 0; i < num_user_hostfiles; i++)
807e146993eSDag-Erling Smørgrav 			load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]);
808e146993eSDag-Erling Smørgrav 		for (i = 0; i < num_system_hostfiles; i++)
809e146993eSDag-Erling Smørgrav 			load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]);
810e8aafc91SKris Kennaway 	}
811e8aafc91SKris Kennaway 
812b15c8340SDag-Erling Smørgrav  retry:
8134a421b63SDag-Erling Smørgrav 	/* Reload these as they may have changed on cert->key downgrade */
814*4f52dfbbSDag-Erling Smørgrav 	want_cert = sshkey_is_cert(host_key);
815*4f52dfbbSDag-Erling Smørgrav 	type = sshkey_type(host_key);
816b15c8340SDag-Erling Smørgrav 
817e8aafc91SKris Kennaway 	/*
818b74df5b2SDag-Erling Smørgrav 	 * Check if the host key is present in the user's list of known
819e8aafc91SKris Kennaway 	 * hosts or in the systemwide list.
820e8aafc91SKris Kennaway 	 */
8214a421b63SDag-Erling Smørgrav 	host_status = check_key_in_hostkeys(host_hostkeys, host_key,
8224a421b63SDag-Erling Smørgrav 	    &host_found);
8234a421b63SDag-Erling Smørgrav 
824e8aafc91SKris Kennaway 	/*
825e8aafc91SKris Kennaway 	 * Also perform check for the ip address, skip the check if we are
826b15c8340SDag-Erling Smørgrav 	 * localhost, looking for a certificate, or the hostname was an ip
827b15c8340SDag-Erling Smørgrav 	 * address to begin with.
828e8aafc91SKris Kennaway 	 */
8294a421b63SDag-Erling Smørgrav 	if (!want_cert && ip_hostkeys != NULL) {
8304a421b63SDag-Erling Smørgrav 		ip_status = check_key_in_hostkeys(ip_hostkeys, host_key,
8314a421b63SDag-Erling Smørgrav 		    &ip_found);
832e8aafc91SKris Kennaway 		if (host_status == HOST_CHANGED &&
8334a421b63SDag-Erling Smørgrav 		    (ip_status != HOST_CHANGED ||
8344a421b63SDag-Erling Smørgrav 		    (ip_found != NULL &&
835*4f52dfbbSDag-Erling Smørgrav 		    !sshkey_equal(ip_found->key, host_found->key))))
836e8aafc91SKris Kennaway 			host_ip_differ = 1;
837e8aafc91SKris Kennaway 	} else
838e8aafc91SKris Kennaway 		ip_status = host_status;
839e8aafc91SKris Kennaway 
840e8aafc91SKris Kennaway 	switch (host_status) {
841e8aafc91SKris Kennaway 	case HOST_OK:
842e8aafc91SKris Kennaway 		/* The host is known and the key matches. */
843b15c8340SDag-Erling Smørgrav 		debug("Host '%.200s' is known and matches the %s host %s.",
844b15c8340SDag-Erling Smørgrav 		    host, type, want_cert ? "certificate" : "key");
8454a421b63SDag-Erling Smørgrav 		debug("Found %s in %s:%lu", want_cert ? "CA key" : "key",
8464a421b63SDag-Erling Smørgrav 		    host_found->file, host_found->line);
847*4f52dfbbSDag-Erling Smørgrav 		if (want_cert &&
848*4f52dfbbSDag-Erling Smørgrav 		    !check_host_cert(options.host_key_alias == NULL ?
849*4f52dfbbSDag-Erling Smørgrav 		    hostname : options.host_key_alias, host_key))
850b15c8340SDag-Erling Smørgrav 			goto fail;
851ca3176e7SBrian Feldman 		if (options.check_host_ip && ip_status == HOST_NEW) {
852b15c8340SDag-Erling Smørgrav 			if (readonly || want_cert)
853cf2b5f3bSDag-Erling Smørgrav 				logit("%s host key for IP address "
854af12a3e7SDag-Erling Smørgrav 				    "'%.128s' not in list of known hosts.",
855e8aafc91SKris Kennaway 				    type, ip);
856e146993eSDag-Erling Smørgrav 			else if (!add_host_to_hostfile(user_hostfiles[0], ip,
857aa49c926SDag-Erling Smørgrav 			    host_key, options.hash_known_hosts))
858cf2b5f3bSDag-Erling Smørgrav 				logit("Failed to add the %s host key for IP "
859af12a3e7SDag-Erling Smørgrav 				    "address '%.128s' to the list of known "
860557f75e5SDag-Erling Smørgrav 				    "hosts (%.500s).", type, ip,
861e146993eSDag-Erling Smørgrav 				    user_hostfiles[0]);
862af12a3e7SDag-Erling Smørgrav 			else
863cf2b5f3bSDag-Erling Smørgrav 				logit("Warning: Permanently added the %s host "
864af12a3e7SDag-Erling Smørgrav 				    "key for IP address '%.128s' to the list "
865af12a3e7SDag-Erling Smørgrav 				    "of known hosts.", type, ip);
866d4af9e69SDag-Erling Smørgrav 		} else if (options.visual_host_key) {
867bc5531deSDag-Erling Smørgrav 			fp = sshkey_fingerprint(host_key,
868bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_DEFAULT);
869bc5531deSDag-Erling Smørgrav 			ra = sshkey_fingerprint(host_key,
870bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_RANDOMART);
871bc5531deSDag-Erling Smørgrav 			if (fp == NULL || ra == NULL)
872bc5531deSDag-Erling Smørgrav 				fatal("%s: sshkey_fingerprint fail", __func__);
873acc1a9efSDag-Erling Smørgrav 			logit("Host key fingerprint is %s\n%s", fp, ra);
874e4a9863fSDag-Erling Smørgrav 			free(ra);
875e4a9863fSDag-Erling Smørgrav 			free(fp);
876e8aafc91SKris Kennaway 		}
877bc5531deSDag-Erling Smørgrav 		hostkey_trusted = 1;
878e8aafc91SKris Kennaway 		break;
879e8aafc91SKris Kennaway 	case HOST_NEW:
880333ee039SDag-Erling Smørgrav 		if (options.host_key_alias == NULL && port != 0 &&
881333ee039SDag-Erling Smørgrav 		    port != SSH_DEFAULT_PORT) {
882333ee039SDag-Erling Smørgrav 			debug("checking without port identifier");
883cce7d346SDag-Erling Smørgrav 			if (check_host_key(hostname, hostaddr, 0, host_key,
884e146993eSDag-Erling Smørgrav 			    ROQUIET, user_hostfiles, num_user_hostfiles,
885e146993eSDag-Erling Smørgrav 			    system_hostfiles, num_system_hostfiles) == 0) {
886333ee039SDag-Erling Smørgrav 				debug("found matching key w/out port");
887333ee039SDag-Erling Smørgrav 				break;
888333ee039SDag-Erling Smørgrav 			}
889333ee039SDag-Erling Smørgrav 		}
890b15c8340SDag-Erling Smørgrav 		if (readonly || want_cert)
891af12a3e7SDag-Erling Smørgrav 			goto fail;
892e8aafc91SKris Kennaway 		/* The host is new. */
893*4f52dfbbSDag-Erling Smørgrav 		if (options.strict_host_key_checking ==
894*4f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_YES) {
895af12a3e7SDag-Erling Smørgrav 			/*
896af12a3e7SDag-Erling Smørgrav 			 * User has requested strict host key checking.  We
897af12a3e7SDag-Erling Smørgrav 			 * will not add the host key automatically.  The only
898af12a3e7SDag-Erling Smørgrav 			 * alternative left is to abort.
899af12a3e7SDag-Erling Smørgrav 			 */
900af12a3e7SDag-Erling Smørgrav 			error("No %s host key is known for %.200s and you "
901af12a3e7SDag-Erling Smørgrav 			    "have requested strict checking.", type, host);
902af12a3e7SDag-Erling Smørgrav 			goto fail;
903*4f52dfbbSDag-Erling Smørgrav 		} else if (options.strict_host_key_checking ==
904*4f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_ASK) {
905cf2b5f3bSDag-Erling Smørgrav 			char msg1[1024], msg2[1024];
906cf2b5f3bSDag-Erling Smørgrav 
9074a421b63SDag-Erling Smørgrav 			if (show_other_keys(host_hostkeys, host_key))
908cf2b5f3bSDag-Erling Smørgrav 				snprintf(msg1, sizeof(msg1),
909cf2b5f3bSDag-Erling Smørgrav 				    "\nbut keys of different type are already"
910cf2b5f3bSDag-Erling Smørgrav 				    " known for this host.");
911cf2b5f3bSDag-Erling Smørgrav 			else
912cf2b5f3bSDag-Erling Smørgrav 				snprintf(msg1, sizeof(msg1), ".");
913e8aafc91SKris Kennaway 			/* The default */
914bc5531deSDag-Erling Smørgrav 			fp = sshkey_fingerprint(host_key,
915bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_DEFAULT);
916bc5531deSDag-Erling Smørgrav 			ra = sshkey_fingerprint(host_key,
917bc5531deSDag-Erling Smørgrav 			    options.fingerprint_hash, SSH_FP_RANDOMART);
918bc5531deSDag-Erling Smørgrav 			if (fp == NULL || ra == NULL)
919bc5531deSDag-Erling Smørgrav 				fatal("%s: sshkey_fingerprint fail", __func__);
920cf2b5f3bSDag-Erling Smørgrav 			msg2[0] = '\0';
921cf2b5f3bSDag-Erling Smørgrav 			if (options.verify_host_key_dns) {
9221ec0d754SDag-Erling Smørgrav 				if (matching_host_key_dns)
923cf2b5f3bSDag-Erling Smørgrav 					snprintf(msg2, sizeof(msg2),
924cf2b5f3bSDag-Erling Smørgrav 					    "Matching host key fingerprint"
925cf2b5f3bSDag-Erling Smørgrav 					    " found in DNS.\n");
926cf2b5f3bSDag-Erling Smørgrav 				else
927cf2b5f3bSDag-Erling Smørgrav 					snprintf(msg2, sizeof(msg2),
928cf2b5f3bSDag-Erling Smørgrav 					    "No matching host key fingerprint"
929cf2b5f3bSDag-Erling Smørgrav 					    " found in DNS.\n");
930cf2b5f3bSDag-Erling Smørgrav 			}
931af12a3e7SDag-Erling Smørgrav 			snprintf(msg, sizeof(msg),
932af12a3e7SDag-Erling Smørgrav 			    "The authenticity of host '%.200s (%s)' can't be "
933f388f5efSDag-Erling Smørgrav 			    "established%s\n"
934d4af9e69SDag-Erling Smørgrav 			    "%s key fingerprint is %s.%s%s\n%s"
935af12a3e7SDag-Erling Smørgrav 			    "Are you sure you want to continue connecting "
936f388f5efSDag-Erling Smørgrav 			    "(yes/no)? ",
937d4af9e69SDag-Erling Smørgrav 			    host, ip, msg1, type, fp,
938d4af9e69SDag-Erling Smørgrav 			    options.visual_host_key ? "\n" : "",
939d4af9e69SDag-Erling Smørgrav 			    options.visual_host_key ? ra : "",
940d4af9e69SDag-Erling Smørgrav 			    msg2);
941e4a9863fSDag-Erling Smørgrav 			free(ra);
942e4a9863fSDag-Erling Smørgrav 			free(fp);
943af12a3e7SDag-Erling Smørgrav 			if (!confirm(msg))
944af12a3e7SDag-Erling Smørgrav 				goto fail;
945bc5531deSDag-Erling Smørgrav 			hostkey_trusted = 1; /* user explicitly confirmed */
946e8aafc91SKris Kennaway 		}
947af12a3e7SDag-Erling Smørgrav 		/*
948*4f52dfbbSDag-Erling Smørgrav 		 * If in "new" or "off" strict mode, add the key automatically
949*4f52dfbbSDag-Erling Smørgrav 		 * to the local known_hosts file.
950af12a3e7SDag-Erling Smørgrav 		 */
951aa49c926SDag-Erling Smørgrav 		if (options.check_host_ip && ip_status == HOST_NEW) {
9524a421b63SDag-Erling Smørgrav 			snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
953aa49c926SDag-Erling Smørgrav 			hostp = hostline;
954aa49c926SDag-Erling Smørgrav 			if (options.hash_known_hosts) {
955aa49c926SDag-Erling Smørgrav 				/* Add hash of host and IP separately */
956e146993eSDag-Erling Smørgrav 				r = add_host_to_hostfile(user_hostfiles[0],
957e146993eSDag-Erling Smørgrav 				    host, host_key, options.hash_known_hosts) &&
958e146993eSDag-Erling Smørgrav 				    add_host_to_hostfile(user_hostfiles[0], ip,
959aa49c926SDag-Erling Smørgrav 				    host_key, options.hash_known_hosts);
960aa49c926SDag-Erling Smørgrav 			} else {
961aa49c926SDag-Erling Smørgrav 				/* Add unhashed "host,ip" */
962e146993eSDag-Erling Smørgrav 				r = add_host_to_hostfile(user_hostfiles[0],
963aa49c926SDag-Erling Smørgrav 				    hostline, host_key,
964aa49c926SDag-Erling Smørgrav 				    options.hash_known_hosts);
965aa49c926SDag-Erling Smørgrav 			}
966aa49c926SDag-Erling Smørgrav 		} else {
967e146993eSDag-Erling Smørgrav 			r = add_host_to_hostfile(user_hostfiles[0], host,
968e146993eSDag-Erling Smørgrav 			    host_key, options.hash_known_hosts);
969aa49c926SDag-Erling Smørgrav 			hostp = host;
970aa49c926SDag-Erling Smørgrav 		}
971aa49c926SDag-Erling Smørgrav 
972aa49c926SDag-Erling Smørgrav 		if (!r)
973cf2b5f3bSDag-Erling Smørgrav 			logit("Failed to add the host to the list of known "
974e146993eSDag-Erling Smørgrav 			    "hosts (%.500s).", user_hostfiles[0]);
975e8aafc91SKris Kennaway 		else
976cf2b5f3bSDag-Erling Smørgrav 			logit("Warning: Permanently added '%.200s' (%s) to the "
977af12a3e7SDag-Erling Smørgrav 			    "list of known hosts.", hostp, type);
978e8aafc91SKris Kennaway 		break;
979b15c8340SDag-Erling Smørgrav 	case HOST_REVOKED:
980b15c8340SDag-Erling Smørgrav 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
981b15c8340SDag-Erling Smørgrav 		error("@       WARNING: REVOKED HOST KEY DETECTED!               @");
982b15c8340SDag-Erling Smørgrav 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
983b15c8340SDag-Erling Smørgrav 		error("The %s host key for %s is marked as revoked.", type, host);
984b15c8340SDag-Erling Smørgrav 		error("This could mean that a stolen key is being used to");
985b15c8340SDag-Erling Smørgrav 		error("impersonate this host.");
986b15c8340SDag-Erling Smørgrav 
987b15c8340SDag-Erling Smørgrav 		/*
988b15c8340SDag-Erling Smørgrav 		 * If strict host key checking is in use, the user will have
989b15c8340SDag-Erling Smørgrav 		 * to edit the key manually and we can only abort.
990b15c8340SDag-Erling Smørgrav 		 */
991*4f52dfbbSDag-Erling Smørgrav 		if (options.strict_host_key_checking !=
992*4f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_OFF) {
993b15c8340SDag-Erling Smørgrav 			error("%s host key for %.200s was revoked and you have "
994b15c8340SDag-Erling Smørgrav 			    "requested strict checking.", type, host);
995b15c8340SDag-Erling Smørgrav 			goto fail;
996b15c8340SDag-Erling Smørgrav 		}
997b15c8340SDag-Erling Smørgrav 		goto continue_unsafe;
998b15c8340SDag-Erling Smørgrav 
999e8aafc91SKris Kennaway 	case HOST_CHANGED:
1000b15c8340SDag-Erling Smørgrav 		if (want_cert) {
1001b15c8340SDag-Erling Smørgrav 			/*
1002b15c8340SDag-Erling Smørgrav 			 * This is only a debug() since it is valid to have
1003b15c8340SDag-Erling Smørgrav 			 * CAs with wildcard DNS matches that don't match
1004b15c8340SDag-Erling Smørgrav 			 * all hosts that one might visit.
1005b15c8340SDag-Erling Smørgrav 			 */
1006b15c8340SDag-Erling Smørgrav 			debug("Host certificate authority does not "
10074a421b63SDag-Erling Smørgrav 			    "match %s in %s:%lu", CA_MARKER,
10084a421b63SDag-Erling Smørgrav 			    host_found->file, host_found->line);
1009b15c8340SDag-Erling Smørgrav 			goto fail;
1010b15c8340SDag-Erling Smørgrav 		}
1011333ee039SDag-Erling Smørgrav 		if (readonly == ROQUIET)
1012333ee039SDag-Erling Smørgrav 			goto fail;
1013e8aafc91SKris Kennaway 		if (options.check_host_ip && host_ip_differ) {
101421e764dfSDag-Erling Smørgrav 			char *key_msg;
1015e8aafc91SKris Kennaway 			if (ip_status == HOST_NEW)
101621e764dfSDag-Erling Smørgrav 				key_msg = "is unknown";
1017e8aafc91SKris Kennaway 			else if (ip_status == HOST_OK)
101821e764dfSDag-Erling Smørgrav 				key_msg = "is unchanged";
1019e8aafc91SKris Kennaway 			else
102021e764dfSDag-Erling Smørgrav 				key_msg = "has a different value";
1021e8aafc91SKris Kennaway 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1022e8aafc91SKris Kennaway 			error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
1023e8aafc91SKris Kennaway 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1024e8aafc91SKris Kennaway 			error("The %s host key for %s has changed,", type, host);
1025d4af9e69SDag-Erling Smørgrav 			error("and the key for the corresponding IP address %s", ip);
102621e764dfSDag-Erling Smørgrav 			error("%s. This could either mean that", key_msg);
1027e8aafc91SKris Kennaway 			error("DNS SPOOFING is happening or the IP address for the host");
1028ca3176e7SBrian Feldman 			error("and its host key have changed at the same time.");
1029ca3176e7SBrian Feldman 			if (ip_status != HOST_NEW)
10304a421b63SDag-Erling Smørgrav 				error("Offending key for IP in %s:%lu",
10314a421b63SDag-Erling Smørgrav 				    ip_found->file, ip_found->line);
1032e8aafc91SKris Kennaway 		}
1033e8aafc91SKris Kennaway 		/* The host key has changed. */
10341ec0d754SDag-Erling Smørgrav 		warn_changed_key(host_key);
1035e8aafc91SKris Kennaway 		error("Add correct host key in %.100s to get rid of this message.",
1036e146993eSDag-Erling Smørgrav 		    user_hostfiles[0]);
1037*4f52dfbbSDag-Erling Smørgrav 		error("Offending %s key in %s:%lu",
1038*4f52dfbbSDag-Erling Smørgrav 		    sshkey_type(host_found->key),
10394a421b63SDag-Erling Smørgrav 		    host_found->file, host_found->line);
1040e8aafc91SKris Kennaway 
1041e8aafc91SKris Kennaway 		/*
1042e8aafc91SKris Kennaway 		 * If strict host key checking is in use, the user will have
1043e8aafc91SKris Kennaway 		 * to edit the key manually and we can only abort.
1044e8aafc91SKris Kennaway 		 */
1045*4f52dfbbSDag-Erling Smørgrav 		if (options.strict_host_key_checking !=
1046*4f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_OFF) {
1047af12a3e7SDag-Erling Smørgrav 			error("%s host key for %.200s has changed and you have "
1048af12a3e7SDag-Erling Smørgrav 			    "requested strict checking.", type, host);
1049af12a3e7SDag-Erling Smørgrav 			goto fail;
1050af12a3e7SDag-Erling Smørgrav 		}
1051e8aafc91SKris Kennaway 
1052b15c8340SDag-Erling Smørgrav  continue_unsafe:
1053e8aafc91SKris Kennaway 		/*
1054e8aafc91SKris Kennaway 		 * If strict host key checking has not been requested, allow
1055cf2b5f3bSDag-Erling Smørgrav 		 * the connection but without MITM-able authentication or
1056333ee039SDag-Erling Smørgrav 		 * forwarding.
1057e8aafc91SKris Kennaway 		 */
1058e8aafc91SKris Kennaway 		if (options.password_authentication) {
1059af12a3e7SDag-Erling Smørgrav 			error("Password authentication is disabled to avoid "
1060af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1061e8aafc91SKris Kennaway 			options.password_authentication = 0;
1062d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1063e8aafc91SKris Kennaway 		}
1064cf2b5f3bSDag-Erling Smørgrav 		if (options.kbd_interactive_authentication) {
1065cf2b5f3bSDag-Erling Smørgrav 			error("Keyboard-interactive authentication is disabled"
1066cf2b5f3bSDag-Erling Smørgrav 			    " to avoid man-in-the-middle attacks.");
1067cf2b5f3bSDag-Erling Smørgrav 			options.kbd_interactive_authentication = 0;
1068cf2b5f3bSDag-Erling Smørgrav 			options.challenge_response_authentication = 0;
1069d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1070cf2b5f3bSDag-Erling Smørgrav 		}
1071cf2b5f3bSDag-Erling Smørgrav 		if (options.challenge_response_authentication) {
1072cf2b5f3bSDag-Erling Smørgrav 			error("Challenge/response authentication is disabled"
1073cf2b5f3bSDag-Erling Smørgrav 			    " to avoid man-in-the-middle attacks.");
1074cf2b5f3bSDag-Erling Smørgrav 			options.challenge_response_authentication = 0;
1075d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1076cf2b5f3bSDag-Erling Smørgrav 		}
1077e8aafc91SKris Kennaway 		if (options.forward_agent) {
1078af12a3e7SDag-Erling Smørgrav 			error("Agent forwarding is disabled to avoid "
1079af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1080e8aafc91SKris Kennaway 			options.forward_agent = 0;
1081d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1082e8aafc91SKris Kennaway 		}
1083ca3176e7SBrian Feldman 		if (options.forward_x11) {
1084af12a3e7SDag-Erling Smørgrav 			error("X11 forwarding is disabled to avoid "
1085af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1086ca3176e7SBrian Feldman 			options.forward_x11 = 0;
1087d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1088ca3176e7SBrian Feldman 		}
1089af12a3e7SDag-Erling Smørgrav 		if (options.num_local_forwards > 0 ||
1090af12a3e7SDag-Erling Smørgrav 		    options.num_remote_forwards > 0) {
1091af12a3e7SDag-Erling Smørgrav 			error("Port forwarding is disabled to avoid "
1092af12a3e7SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1093af12a3e7SDag-Erling Smørgrav 			options.num_local_forwards =
1094af12a3e7SDag-Erling Smørgrav 			    options.num_remote_forwards = 0;
1095d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1096ca3176e7SBrian Feldman 		}
1097333ee039SDag-Erling Smørgrav 		if (options.tun_open != SSH_TUNMODE_NO) {
1098333ee039SDag-Erling Smørgrav 			error("Tunnel forwarding is disabled to avoid "
1099333ee039SDag-Erling Smørgrav 			    "man-in-the-middle attacks.");
1100333ee039SDag-Erling Smørgrav 			options.tun_open = SSH_TUNMODE_NO;
1101d4af9e69SDag-Erling Smørgrav 			cancelled_forwarding = 1;
1102333ee039SDag-Erling Smørgrav 		}
1103d4af9e69SDag-Erling Smørgrav 		if (options.exit_on_forward_failure && cancelled_forwarding)
1104d4af9e69SDag-Erling Smørgrav 			fatal("Error: forwarding disabled due to host key "
1105d4af9e69SDag-Erling Smørgrav 			    "check failure");
1106d4af9e69SDag-Erling Smørgrav 
1107e8aafc91SKris Kennaway 		/*
1108e8aafc91SKris Kennaway 		 * XXX Should permit the user to change to use the new id.
1109e8aafc91SKris Kennaway 		 * This could be done by converting the host key to an
1110e8aafc91SKris Kennaway 		 * identifying sentence, tell that the host identifies itself
1111b15c8340SDag-Erling Smørgrav 		 * by that sentence, and ask the user if he/she wishes to
1112e8aafc91SKris Kennaway 		 * accept the authentication.
1113e8aafc91SKris Kennaway 		 */
1114e8aafc91SKris Kennaway 		break;
1115f388f5efSDag-Erling Smørgrav 	case HOST_FOUND:
1116f388f5efSDag-Erling Smørgrav 		fatal("internal error");
1117f388f5efSDag-Erling Smørgrav 		break;
1118e8aafc91SKris Kennaway 	}
1119ca3176e7SBrian Feldman 
1120ca3176e7SBrian Feldman 	if (options.check_host_ip && host_status != HOST_CHANGED &&
1121ca3176e7SBrian Feldman 	    ip_status == HOST_CHANGED) {
1122af12a3e7SDag-Erling Smørgrav 		snprintf(msg, sizeof(msg),
1123af12a3e7SDag-Erling Smørgrav 		    "Warning: the %s host key for '%.200s' "
1124af12a3e7SDag-Erling Smørgrav 		    "differs from the key for the IP address '%.128s'"
11254a421b63SDag-Erling Smørgrav 		    "\nOffending key for IP in %s:%lu",
11264a421b63SDag-Erling Smørgrav 		    type, host, ip, ip_found->file, ip_found->line);
1127af12a3e7SDag-Erling Smørgrav 		if (host_status == HOST_OK) {
1128af12a3e7SDag-Erling Smørgrav 			len = strlen(msg);
1129af12a3e7SDag-Erling Smørgrav 			snprintf(msg + len, sizeof(msg) - len,
11304a421b63SDag-Erling Smørgrav 			    "\nMatching host key in %s:%lu",
11314a421b63SDag-Erling Smørgrav 			    host_found->file, host_found->line);
1132af12a3e7SDag-Erling Smørgrav 		}
1133*4f52dfbbSDag-Erling Smørgrav 		if (options.strict_host_key_checking ==
1134*4f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_ASK) {
1135af12a3e7SDag-Erling Smørgrav 			strlcat(msg, "\nAre you sure you want "
1136af12a3e7SDag-Erling Smørgrav 			    "to continue connecting (yes/no)? ", sizeof(msg));
1137af12a3e7SDag-Erling Smørgrav 			if (!confirm(msg))
1138af12a3e7SDag-Erling Smørgrav 				goto fail;
1139*4f52dfbbSDag-Erling Smørgrav 		} else if (options.strict_host_key_checking !=
1140*4f52dfbbSDag-Erling Smørgrav 		    SSH_STRICT_HOSTKEY_OFF) {
1141*4f52dfbbSDag-Erling Smørgrav 			logit("%s", msg);
1142*4f52dfbbSDag-Erling Smørgrav 			error("Exiting, you have requested strict checking.");
1143*4f52dfbbSDag-Erling Smørgrav 			goto fail;
1144af12a3e7SDag-Erling Smørgrav 		} else {
1145cf2b5f3bSDag-Erling Smørgrav 			logit("%s", msg);
1146ca3176e7SBrian Feldman 		}
1147ca3176e7SBrian Feldman 	}
1148ca3176e7SBrian Feldman 
1149bc5531deSDag-Erling Smørgrav 	if (!hostkey_trusted && options.update_hostkeys) {
1150bc5531deSDag-Erling Smørgrav 		debug("%s: hostkey not known or explicitly trusted: "
1151bc5531deSDag-Erling Smørgrav 		    "disabling UpdateHostkeys", __func__);
1152bc5531deSDag-Erling Smørgrav 		options.update_hostkeys = 0;
1153bc5531deSDag-Erling Smørgrav 	}
1154bc5531deSDag-Erling Smørgrav 
1155e4a9863fSDag-Erling Smørgrav 	free(ip);
1156e4a9863fSDag-Erling Smørgrav 	free(host);
11574a421b63SDag-Erling Smørgrav 	if (host_hostkeys != NULL)
11584a421b63SDag-Erling Smørgrav 		free_hostkeys(host_hostkeys);
11594a421b63SDag-Erling Smørgrav 	if (ip_hostkeys != NULL)
11604a421b63SDag-Erling Smørgrav 		free_hostkeys(ip_hostkeys);
1161af12a3e7SDag-Erling Smørgrav 	return 0;
1162af12a3e7SDag-Erling Smørgrav 
1163af12a3e7SDag-Erling Smørgrav fail:
1164b15c8340SDag-Erling Smørgrav 	if (want_cert && host_status != HOST_REVOKED) {
1165b15c8340SDag-Erling Smørgrav 		/*
1166b15c8340SDag-Erling Smørgrav 		 * No matching certificate. Downgrade cert to raw key and
1167b15c8340SDag-Erling Smørgrav 		 * search normally.
1168b15c8340SDag-Erling Smørgrav 		 */
1169b15c8340SDag-Erling Smørgrav 		debug("No matching CA found. Retry with plain key");
1170*4f52dfbbSDag-Erling Smørgrav 		if ((r = sshkey_from_private(host_key, &raw_key)) != 0)
1171*4f52dfbbSDag-Erling Smørgrav 			fatal("%s: sshkey_from_private: %s",
1172*4f52dfbbSDag-Erling Smørgrav 			    __func__, ssh_err(r));
1173*4f52dfbbSDag-Erling Smørgrav 		if ((r = sshkey_drop_cert(raw_key)) != 0)
1174*4f52dfbbSDag-Erling Smørgrav 			fatal("Couldn't drop certificate: %s", ssh_err(r));
1175b15c8340SDag-Erling Smørgrav 		host_key = raw_key;
1176b15c8340SDag-Erling Smørgrav 		goto retry;
1177b15c8340SDag-Erling Smørgrav 	}
1178b15c8340SDag-Erling Smørgrav 	if (raw_key != NULL)
1179*4f52dfbbSDag-Erling Smørgrav 		sshkey_free(raw_key);
1180e4a9863fSDag-Erling Smørgrav 	free(ip);
1181e4a9863fSDag-Erling Smørgrav 	free(host);
11824a421b63SDag-Erling Smørgrav 	if (host_hostkeys != NULL)
11834a421b63SDag-Erling Smørgrav 		free_hostkeys(host_hostkeys);
11844a421b63SDag-Erling Smørgrav 	if (ip_hostkeys != NULL)
11854a421b63SDag-Erling Smørgrav 		free_hostkeys(ip_hostkeys);
1186af12a3e7SDag-Erling Smørgrav 	return -1;
1187e8aafc91SKris Kennaway }
1188511b41d2SMark Murray 
1189cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */
1190fe5fd017SMark Murray int
1191*4f52dfbbSDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key)
1192fe5fd017SMark Murray {
1193acc1a9efSDag-Erling Smørgrav 	u_int i;
1194a0ee8cc6SDag-Erling Smørgrav 	int r = -1, flags = 0;
1195acc1a9efSDag-Erling Smørgrav 	char valid[64], *fp = NULL, *cafp = NULL;
1196bc5531deSDag-Erling Smørgrav 	struct sshkey *plain = NULL;
11974a421b63SDag-Erling Smørgrav 
1198bc5531deSDag-Erling Smørgrav 	if ((fp = sshkey_fingerprint(host_key,
1199bc5531deSDag-Erling Smørgrav 	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
1200bc5531deSDag-Erling Smørgrav 		error("%s: fingerprint host key: %s", __func__, ssh_err(r));
1201bc5531deSDag-Erling Smørgrav 		r = -1;
1202bc5531deSDag-Erling Smørgrav 		goto out;
1203bc5531deSDag-Erling Smørgrav 	}
1204fe5fd017SMark Murray 
1205acc1a9efSDag-Erling Smørgrav 	if (sshkey_is_cert(host_key)) {
1206acc1a9efSDag-Erling Smørgrav 		if ((cafp = sshkey_fingerprint(host_key->cert->signature_key,
1207acc1a9efSDag-Erling Smørgrav 		    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
1208acc1a9efSDag-Erling Smørgrav 			error("%s: fingerprint CA key: %s",
1209acc1a9efSDag-Erling Smørgrav 			    __func__, ssh_err(r));
1210acc1a9efSDag-Erling Smørgrav 			r = -1;
1211acc1a9efSDag-Erling Smørgrav 			goto out;
1212acc1a9efSDag-Erling Smørgrav 		}
1213acc1a9efSDag-Erling Smørgrav 		sshkey_format_cert_validity(host_key->cert,
1214acc1a9efSDag-Erling Smørgrav 		    valid, sizeof(valid));
1215acc1a9efSDag-Erling Smørgrav 		debug("Server host certificate: %s %s, serial %llu "
1216acc1a9efSDag-Erling Smørgrav 		    "ID \"%s\" CA %s %s valid %s",
1217acc1a9efSDag-Erling Smørgrav 		    sshkey_ssh_name(host_key), fp,
1218acc1a9efSDag-Erling Smørgrav 		    (unsigned long long)host_key->cert->serial,
1219acc1a9efSDag-Erling Smørgrav 		    host_key->cert->key_id,
1220acc1a9efSDag-Erling Smørgrav 		    sshkey_ssh_name(host_key->cert->signature_key), cafp,
1221acc1a9efSDag-Erling Smørgrav 		    valid);
1222acc1a9efSDag-Erling Smørgrav 		for (i = 0; i < host_key->cert->nprincipals; i++) {
1223acc1a9efSDag-Erling Smørgrav 			debug2("Server host certificate hostname: %s",
1224acc1a9efSDag-Erling Smørgrav 			    host_key->cert->principals[i]);
1225acc1a9efSDag-Erling Smørgrav 		}
1226acc1a9efSDag-Erling Smørgrav 	} else {
1227*4f52dfbbSDag-Erling Smørgrav 		debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp);
1228acc1a9efSDag-Erling Smørgrav 	}
1229bc5531deSDag-Erling Smørgrav 
1230bc5531deSDag-Erling Smørgrav 	if (sshkey_equal(previous_host_key, host_key)) {
1231bc5531deSDag-Erling Smørgrav 		debug2("%s: server host key %s %s matches cached key",
1232bc5531deSDag-Erling Smørgrav 		    __func__, sshkey_type(host_key), fp);
1233bc5531deSDag-Erling Smørgrav 		r = 0;
1234bc5531deSDag-Erling Smørgrav 		goto out;
1235bc5531deSDag-Erling Smørgrav 	}
1236bc5531deSDag-Erling Smørgrav 
1237bc5531deSDag-Erling Smørgrav 	/* Check in RevokedHostKeys file if specified */
1238bc5531deSDag-Erling Smørgrav 	if (options.revoked_host_keys != NULL) {
1239bc5531deSDag-Erling Smørgrav 		r = sshkey_check_revoked(host_key, options.revoked_host_keys);
1240bc5531deSDag-Erling Smørgrav 		switch (r) {
1241bc5531deSDag-Erling Smørgrav 		case 0:
1242bc5531deSDag-Erling Smørgrav 			break; /* not revoked */
1243bc5531deSDag-Erling Smørgrav 		case SSH_ERR_KEY_REVOKED:
1244bc5531deSDag-Erling Smørgrav 			error("Host key %s %s revoked by file %s",
1245bc5531deSDag-Erling Smørgrav 			    sshkey_type(host_key), fp,
1246bc5531deSDag-Erling Smørgrav 			    options.revoked_host_keys);
1247bc5531deSDag-Erling Smørgrav 			r = -1;
1248bc5531deSDag-Erling Smørgrav 			goto out;
1249bc5531deSDag-Erling Smørgrav 		default:
1250bc5531deSDag-Erling Smørgrav 			error("Error checking host key %s %s in "
1251bc5531deSDag-Erling Smørgrav 			    "revoked keys file %s: %s", sshkey_type(host_key),
1252bc5531deSDag-Erling Smørgrav 			    fp, options.revoked_host_keys, ssh_err(r));
1253bc5531deSDag-Erling Smørgrav 			r = -1;
1254bc5531deSDag-Erling Smørgrav 			goto out;
1255bc5531deSDag-Erling Smørgrav 		}
1256a0ee8cc6SDag-Erling Smørgrav 	}
1257a0ee8cc6SDag-Erling Smørgrav 
12583a0b9b77SXin LI 	if (options.verify_host_key_dns) {
12593a0b9b77SXin LI 		/*
12603a0b9b77SXin LI 		 * XXX certs are not yet supported for DNS, so downgrade
12613a0b9b77SXin LI 		 * them and try the plain key.
12623a0b9b77SXin LI 		 */
1263bc5531deSDag-Erling Smørgrav 		if ((r = sshkey_from_private(host_key, &plain)) != 0)
1264bc5531deSDag-Erling Smørgrav 			goto out;
1265bc5531deSDag-Erling Smørgrav 		if (sshkey_is_cert(plain))
1266bc5531deSDag-Erling Smørgrav 			sshkey_drop_cert(plain);
12673a0b9b77SXin LI 		if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
12681ec0d754SDag-Erling Smørgrav 			if (flags & DNS_VERIFY_FOUND) {
12691ec0d754SDag-Erling Smørgrav 				if (options.verify_host_key_dns == 1 &&
12701ec0d754SDag-Erling Smørgrav 				    flags & DNS_VERIFY_MATCH &&
12713a0b9b77SXin LI 				    flags & DNS_VERIFY_SECURE) {
1272a0ee8cc6SDag-Erling Smørgrav 					r = 0;
1273bc5531deSDag-Erling Smørgrav 					goto out;
12743a0b9b77SXin LI 				}
12751ec0d754SDag-Erling Smørgrav 				if (flags & DNS_VERIFY_MATCH) {
12761ec0d754SDag-Erling Smørgrav 					matching_host_key_dns = 1;
12771ec0d754SDag-Erling Smørgrav 				} else {
12783a0b9b77SXin LI 					warn_changed_key(plain);
12793a0b9b77SXin LI 					error("Update the SSHFP RR in DNS "
12803a0b9b77SXin LI 					    "with the new host key to get rid "
12813a0b9b77SXin LI 					    "of this message.");
1282cf2b5f3bSDag-Erling Smørgrav 				}
1283cf2b5f3bSDag-Erling Smørgrav 			}
12841ec0d754SDag-Erling Smørgrav 		}
12853a0b9b77SXin LI 	}
1286a0ee8cc6SDag-Erling Smørgrav 	r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
1287e146993eSDag-Erling Smørgrav 	    options.user_hostfiles, options.num_user_hostfiles,
1288e146993eSDag-Erling Smørgrav 	    options.system_hostfiles, options.num_system_hostfiles);
1289a0ee8cc6SDag-Erling Smørgrav 
1290bc5531deSDag-Erling Smørgrav out:
1291bc5531deSDag-Erling Smørgrav 	sshkey_free(plain);
1292bc5531deSDag-Erling Smørgrav 	free(fp);
1293acc1a9efSDag-Erling Smørgrav 	free(cafp);
1294a0ee8cc6SDag-Erling Smørgrav 	if (r == 0 && host_key != NULL) {
1295*4f52dfbbSDag-Erling Smørgrav 		sshkey_free(previous_host_key);
1296*4f52dfbbSDag-Erling Smørgrav 		r = sshkey_from_private(host_key, &previous_host_key);
1297a0ee8cc6SDag-Erling Smørgrav 	}
1298a0ee8cc6SDag-Erling Smørgrav 
1299a0ee8cc6SDag-Erling Smørgrav 	return r;
1300fe5fd017SMark Murray }
1301fe5fd017SMark Murray 
1302511b41d2SMark Murray /*
1303511b41d2SMark Murray  * Starts a dialog with the server, and authenticates the current user on the
1304511b41d2SMark Murray  * server.  This does not need any extra privileges.  The basic connection
1305511b41d2SMark Murray  * to the server must already have been established before this is called.
1306511b41d2SMark Murray  * If login fails, this function prints an error and never returns.
1307511b41d2SMark Murray  * This function does not require super-user privileges.
1308511b41d2SMark Murray  */
1309511b41d2SMark Murray void
131080628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost,
13114a421b63SDag-Erling Smørgrav     struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
1312511b41d2SMark Murray {
1313f7167e0eSDag-Erling Smørgrav 	char *host;
1314e8aafc91SKris Kennaway 	char *server_user, *local_user;
1315e8aafc91SKris Kennaway 
1316e8aafc91SKris Kennaway 	local_user = xstrdup(pw->pw_name);
1317e8aafc91SKris Kennaway 	server_user = options.user ? options.user : local_user;
1318511b41d2SMark Murray 
1319511b41d2SMark Murray 	/* Convert the user-supplied hostname into all lowercase. */
1320511b41d2SMark Murray 	host = xstrdup(orighost);
1321f7167e0eSDag-Erling Smørgrav 	lowercase(host);
1322511b41d2SMark Murray 
1323511b41d2SMark Murray 	/* Exchange protocol version identification strings with the server. */
1324d4af9e69SDag-Erling Smørgrav 	ssh_exchange_identification(timeout_ms);
1325511b41d2SMark Murray 
1326511b41d2SMark Murray 	/* Put the connection into non-blocking mode. */
1327511b41d2SMark Murray 	packet_set_nonblocking();
1328511b41d2SMark Murray 
1329511b41d2SMark Murray 	/* key exchange */
1330511b41d2SMark Murray 	/* authenticate user */
1331557f75e5SDag-Erling Smørgrav 	debug("Authenticating to %s:%d as '%s'", host, port, server_user);
13324a421b63SDag-Erling Smørgrav 	ssh_kex2(host, hostaddr, port);
133380628bacSDag-Erling Smørgrav 	ssh_userauth2(local_user, server_user, host, sensitive);
1334e4a9863fSDag-Erling Smørgrav 	free(local_user);
1335511b41d2SMark Murray }
1336e0fbb1d2SBrian Feldman 
1337e0fbb1d2SBrian Feldman void
1338e0fbb1d2SBrian Feldman ssh_put_password(char *password)
1339e0fbb1d2SBrian Feldman {
1340e0fbb1d2SBrian Feldman 	int size;
1341e0fbb1d2SBrian Feldman 	char *padded;
1342e0fbb1d2SBrian Feldman 
1343ca3176e7SBrian Feldman 	if (datafellows & SSH_BUG_PASSWORDPAD) {
1344af12a3e7SDag-Erling Smørgrav 		packet_put_cstring(password);
1345ca3176e7SBrian Feldman 		return;
1346ca3176e7SBrian Feldman 	}
1347ca86bcf2SDag-Erling Smørgrav 	size = ROUNDUP(strlen(password) + 1, 32);
1348333ee039SDag-Erling Smørgrav 	padded = xcalloc(1, size);
1349e0fbb1d2SBrian Feldman 	strlcpy(padded, password, size);
1350e0fbb1d2SBrian Feldman 	packet_put_string(padded, size);
1351b83788ffSDag-Erling Smørgrav 	explicit_bzero(padded, size);
1352e4a9863fSDag-Erling Smørgrav 	free(padded);
1353e0fbb1d2SBrian Feldman }
1354f388f5efSDag-Erling Smørgrav 
1355f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */
1356f388f5efSDag-Erling Smørgrav static int
1357*4f52dfbbSDag-Erling Smørgrav show_other_keys(struct hostkeys *hostkeys, struct sshkey *key)
1358f388f5efSDag-Erling Smørgrav {
1359f7167e0eSDag-Erling Smørgrav 	int type[] = {
1360f7167e0eSDag-Erling Smørgrav 		KEY_RSA,
1361f7167e0eSDag-Erling Smørgrav 		KEY_DSA,
1362f7167e0eSDag-Erling Smørgrav 		KEY_ECDSA,
1363f7167e0eSDag-Erling Smørgrav 		KEY_ED25519,
1364f7167e0eSDag-Erling Smørgrav 		-1
1365f7167e0eSDag-Erling Smørgrav 	};
13664a421b63SDag-Erling Smørgrav 	int i, ret = 0;
13674a421b63SDag-Erling Smørgrav 	char *fp, *ra;
13684a421b63SDag-Erling Smørgrav 	const struct hostkey_entry *found;
1369f388f5efSDag-Erling Smørgrav 
1370f388f5efSDag-Erling Smørgrav 	for (i = 0; type[i] != -1; i++) {
1371f388f5efSDag-Erling Smørgrav 		if (type[i] == key->type)
1372f388f5efSDag-Erling Smørgrav 			continue;
13734a421b63SDag-Erling Smørgrav 		if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found))
1374f388f5efSDag-Erling Smørgrav 			continue;
1375bc5531deSDag-Erling Smørgrav 		fp = sshkey_fingerprint(found->key,
1376bc5531deSDag-Erling Smørgrav 		    options.fingerprint_hash, SSH_FP_DEFAULT);
1377bc5531deSDag-Erling Smørgrav 		ra = sshkey_fingerprint(found->key,
1378bc5531deSDag-Erling Smørgrav 		    options.fingerprint_hash, SSH_FP_RANDOMART);
1379bc5531deSDag-Erling Smørgrav 		if (fp == NULL || ra == NULL)
1380bc5531deSDag-Erling Smørgrav 			fatal("%s: sshkey_fingerprint fail", __func__);
13814a421b63SDag-Erling Smørgrav 		logit("WARNING: %s key found for host %s\n"
13824a421b63SDag-Erling Smørgrav 		    "in %s:%lu\n"
13834a421b63SDag-Erling Smørgrav 		    "%s key fingerprint %s.",
13844a421b63SDag-Erling Smørgrav 		    key_type(found->key),
13854a421b63SDag-Erling Smørgrav 		    found->host, found->file, found->line,
13864a421b63SDag-Erling Smørgrav 		    key_type(found->key), fp);
13874a421b63SDag-Erling Smørgrav 		if (options.visual_host_key)
13884a421b63SDag-Erling Smørgrav 			logit("%s", ra);
1389e4a9863fSDag-Erling Smørgrav 		free(ra);
1390e4a9863fSDag-Erling Smørgrav 		free(fp);
13914a421b63SDag-Erling Smørgrav 		ret = 1;
1392f388f5efSDag-Erling Smørgrav 	}
13934a421b63SDag-Erling Smørgrav 	return ret;
1394f388f5efSDag-Erling Smørgrav }
13951ec0d754SDag-Erling Smørgrav 
13961ec0d754SDag-Erling Smørgrav static void
1397*4f52dfbbSDag-Erling Smørgrav warn_changed_key(struct sshkey *host_key)
13981ec0d754SDag-Erling Smørgrav {
13991ec0d754SDag-Erling Smørgrav 	char *fp;
14001ec0d754SDag-Erling Smørgrav 
1401bc5531deSDag-Erling Smørgrav 	fp = sshkey_fingerprint(host_key, options.fingerprint_hash,
1402bc5531deSDag-Erling Smørgrav 	    SSH_FP_DEFAULT);
1403bc5531deSDag-Erling Smørgrav 	if (fp == NULL)
1404bc5531deSDag-Erling Smørgrav 		fatal("%s: sshkey_fingerprint fail", __func__);
14051ec0d754SDag-Erling Smørgrav 
14061ec0d754SDag-Erling Smørgrav 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
14071ec0d754SDag-Erling Smørgrav 	error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
14081ec0d754SDag-Erling Smørgrav 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
14091ec0d754SDag-Erling Smørgrav 	error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
14101ec0d754SDag-Erling Smørgrav 	error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
14114a421b63SDag-Erling Smørgrav 	error("It is also possible that a host key has just been changed.");
14121ec0d754SDag-Erling Smørgrav 	error("The fingerprint for the %s key sent by the remote host is\n%s.",
14134a421b63SDag-Erling Smørgrav 	    key_type(host_key), fp);
14141ec0d754SDag-Erling Smørgrav 	error("Please contact your system administrator.");
14151ec0d754SDag-Erling Smørgrav 
1416e4a9863fSDag-Erling Smørgrav 	free(fp);
14171ec0d754SDag-Erling Smørgrav }
1418b74df5b2SDag-Erling Smørgrav 
1419b74df5b2SDag-Erling Smørgrav /*
1420b74df5b2SDag-Erling Smørgrav  * Execute a local command
1421b74df5b2SDag-Erling Smørgrav  */
1422b74df5b2SDag-Erling Smørgrav int
1423b74df5b2SDag-Erling Smørgrav ssh_local_cmd(const char *args)
1424b74df5b2SDag-Erling Smørgrav {
1425b74df5b2SDag-Erling Smørgrav 	char *shell;
1426b74df5b2SDag-Erling Smørgrav 	pid_t pid;
1427b74df5b2SDag-Erling Smørgrav 	int status;
14284a421b63SDag-Erling Smørgrav 	void (*osighand)(int);
1429b74df5b2SDag-Erling Smørgrav 
1430b74df5b2SDag-Erling Smørgrav 	if (!options.permit_local_command ||
1431b74df5b2SDag-Erling Smørgrav 	    args == NULL || !*args)
1432b74df5b2SDag-Erling Smørgrav 		return (1);
1433b74df5b2SDag-Erling Smørgrav 
14344a421b63SDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
1435b74df5b2SDag-Erling Smørgrav 		shell = _PATH_BSHELL;
1436b74df5b2SDag-Erling Smørgrav 
14374a421b63SDag-Erling Smørgrav 	osighand = signal(SIGCHLD, SIG_DFL);
1438b74df5b2SDag-Erling Smørgrav 	pid = fork();
1439b74df5b2SDag-Erling Smørgrav 	if (pid == 0) {
14404a421b63SDag-Erling Smørgrav 		signal(SIGPIPE, SIG_DFL);
1441b74df5b2SDag-Erling Smørgrav 		debug3("Executing %s -c \"%s\"", shell, args);
1442b74df5b2SDag-Erling Smørgrav 		execl(shell, shell, "-c", args, (char *)NULL);
1443b74df5b2SDag-Erling Smørgrav 		error("Couldn't execute %s -c \"%s\": %s",
1444b74df5b2SDag-Erling Smørgrav 		    shell, args, strerror(errno));
1445b74df5b2SDag-Erling Smørgrav 		_exit(1);
1446b74df5b2SDag-Erling Smørgrav 	} else if (pid == -1)
1447b74df5b2SDag-Erling Smørgrav 		fatal("fork failed: %.100s", strerror(errno));
1448b74df5b2SDag-Erling Smørgrav 	while (waitpid(pid, &status, 0) == -1)
1449b74df5b2SDag-Erling Smørgrav 		if (errno != EINTR)
1450b74df5b2SDag-Erling Smørgrav 			fatal("Couldn't wait for child: %s", strerror(errno));
14514a421b63SDag-Erling Smørgrav 	signal(SIGCHLD, osighand);
1452b74df5b2SDag-Erling Smørgrav 
1453b74df5b2SDag-Erling Smørgrav 	if (!WIFEXITED(status))
1454b74df5b2SDag-Erling Smørgrav 		return (1);
1455b74df5b2SDag-Erling Smørgrav 
1456b74df5b2SDag-Erling Smørgrav 	return (WEXITSTATUS(status));
1457b74df5b2SDag-Erling Smørgrav }
1458acc1a9efSDag-Erling Smørgrav 
1459acc1a9efSDag-Erling Smørgrav void
1460*4f52dfbbSDag-Erling Smørgrav maybe_add_key_to_agent(char *authfile, struct sshkey *private, char *comment,
1461acc1a9efSDag-Erling Smørgrav     char *passphrase)
1462acc1a9efSDag-Erling Smørgrav {
1463acc1a9efSDag-Erling Smørgrav 	int auth_sock = -1, r;
1464acc1a9efSDag-Erling Smørgrav 
1465acc1a9efSDag-Erling Smørgrav 	if (options.add_keys_to_agent == 0)
1466acc1a9efSDag-Erling Smørgrav 		return;
1467acc1a9efSDag-Erling Smørgrav 
1468acc1a9efSDag-Erling Smørgrav 	if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
1469acc1a9efSDag-Erling Smørgrav 		debug3("no authentication agent, not adding key");
1470acc1a9efSDag-Erling Smørgrav 		return;
1471acc1a9efSDag-Erling Smørgrav 	}
1472acc1a9efSDag-Erling Smørgrav 
1473acc1a9efSDag-Erling Smørgrav 	if (options.add_keys_to_agent == 2 &&
1474acc1a9efSDag-Erling Smørgrav 	    !ask_permission("Add key %s (%s) to agent?", authfile, comment)) {
1475acc1a9efSDag-Erling Smørgrav 		debug3("user denied adding this key");
1476d93a896eSDag-Erling Smørgrav 		close(auth_sock);
1477acc1a9efSDag-Erling Smørgrav 		return;
1478acc1a9efSDag-Erling Smørgrav 	}
1479acc1a9efSDag-Erling Smørgrav 
1480acc1a9efSDag-Erling Smørgrav 	if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0,
1481acc1a9efSDag-Erling Smørgrav 	    (options.add_keys_to_agent == 3))) == 0)
1482acc1a9efSDag-Erling Smørgrav 		debug("identity added to agent: %s", authfile);
1483acc1a9efSDag-Erling Smørgrav 	else
1484acc1a9efSDag-Erling Smørgrav 		debug("could not add identity to agent: %s (%d)", authfile, r);
1485d93a896eSDag-Erling Smørgrav 	close(auth_sock);
1486acc1a9efSDag-Erling Smørgrav }
1487