xref: /dflybsd-src/crypto/openssh/sshconnect.c (revision 40c002af48fe12a59703b95d8d40ed8111fa7f98)
1*40c002afSPeter Avalos /* $OpenBSD: sshconnect.c,v 1.214 2009/05/28 16:50:16 andreas Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos  * Author: Tatu Ylonen <ylo@cs.hut.fi>
418de8d7fSPeter Avalos  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
518de8d7fSPeter Avalos  *                    All rights reserved
618de8d7fSPeter Avalos  * Code to connect to a remote host, and to perform the client side of the
718de8d7fSPeter Avalos  * login (authentication) dialog.
818de8d7fSPeter Avalos  *
918de8d7fSPeter Avalos  * As far as I am concerned, the code I have written for this software
1018de8d7fSPeter Avalos  * can be used freely for any purpose.  Any derived versions of this
1118de8d7fSPeter Avalos  * software must be clearly marked as such, and if the derived work is
1218de8d7fSPeter Avalos  * incompatible with the protocol description in the RFC file, it must be
1318de8d7fSPeter Avalos  * called by a name other than "ssh" or "Secure Shell".
1418de8d7fSPeter Avalos  */
1518de8d7fSPeter Avalos 
1618de8d7fSPeter Avalos #include "includes.h"
1718de8d7fSPeter Avalos 
1818de8d7fSPeter Avalos #include <sys/types.h>
1918de8d7fSPeter Avalos #include <sys/wait.h>
2018de8d7fSPeter Avalos #include <sys/stat.h>
2118de8d7fSPeter Avalos #include <sys/socket.h>
2218de8d7fSPeter Avalos #ifdef HAVE_SYS_TIME_H
2318de8d7fSPeter Avalos # include <sys/time.h>
2418de8d7fSPeter Avalos #endif
2518de8d7fSPeter Avalos 
2618de8d7fSPeter Avalos #include <netinet/in.h>
2718de8d7fSPeter Avalos #include <arpa/inet.h>
2818de8d7fSPeter Avalos 
2918de8d7fSPeter Avalos #include <ctype.h>
3018de8d7fSPeter Avalos #include <errno.h>
3118de8d7fSPeter Avalos #include <netdb.h>
3218de8d7fSPeter Avalos #ifdef HAVE_PATHS_H
3318de8d7fSPeter Avalos #include <paths.h>
3418de8d7fSPeter Avalos #endif
3518de8d7fSPeter Avalos #include <pwd.h>
3618de8d7fSPeter Avalos #include <stdarg.h>
3718de8d7fSPeter Avalos #include <stdio.h>
3818de8d7fSPeter Avalos #include <stdlib.h>
3918de8d7fSPeter Avalos #include <string.h>
4018de8d7fSPeter Avalos #include <unistd.h>
4118de8d7fSPeter Avalos 
4218de8d7fSPeter Avalos #include "xmalloc.h"
4318de8d7fSPeter Avalos #include "key.h"
4418de8d7fSPeter Avalos #include "hostfile.h"
4518de8d7fSPeter Avalos #include "ssh.h"
4618de8d7fSPeter Avalos #include "rsa.h"
4718de8d7fSPeter Avalos #include "buffer.h"
4818de8d7fSPeter Avalos #include "packet.h"
4918de8d7fSPeter Avalos #include "uidswap.h"
5018de8d7fSPeter Avalos #include "compat.h"
5118de8d7fSPeter Avalos #include "key.h"
5218de8d7fSPeter Avalos #include "sshconnect.h"
5318de8d7fSPeter Avalos #include "hostfile.h"
5418de8d7fSPeter Avalos #include "log.h"
5518de8d7fSPeter Avalos #include "readconf.h"
5618de8d7fSPeter Avalos #include "atomicio.h"
5718de8d7fSPeter Avalos #include "misc.h"
5818de8d7fSPeter Avalos #include "dns.h"
59*40c002afSPeter Avalos #include "roaming.h"
6018de8d7fSPeter Avalos #include "version.h"
6118de8d7fSPeter Avalos 
6218de8d7fSPeter Avalos char *client_version_string = NULL;
6318de8d7fSPeter Avalos char *server_version_string = NULL;
6418de8d7fSPeter Avalos 
6518de8d7fSPeter Avalos static int matching_host_key_dns = 0;
6618de8d7fSPeter Avalos 
6718de8d7fSPeter Avalos /* import */
6818de8d7fSPeter Avalos extern Options options;
6918de8d7fSPeter Avalos extern char *__progname;
7018de8d7fSPeter Avalos extern uid_t original_real_uid;
7118de8d7fSPeter Avalos extern uid_t original_effective_uid;
7218de8d7fSPeter Avalos extern pid_t proxy_command_pid;
7318de8d7fSPeter Avalos 
7418de8d7fSPeter Avalos static int show_other_keys(const char *, Key *);
7518de8d7fSPeter Avalos static void warn_changed_key(Key *);
7618de8d7fSPeter Avalos 
7718de8d7fSPeter Avalos /*
7818de8d7fSPeter Avalos  * Connect to the given ssh server using a proxy command.
7918de8d7fSPeter Avalos  */
8018de8d7fSPeter Avalos static int
8118de8d7fSPeter Avalos ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
8218de8d7fSPeter Avalos {
8318de8d7fSPeter Avalos 	char *command_string, *tmp;
8418de8d7fSPeter Avalos 	int pin[2], pout[2];
8518de8d7fSPeter Avalos 	pid_t pid;
8618de8d7fSPeter Avalos 	char *shell, strport[NI_MAXSERV];
8718de8d7fSPeter Avalos 
8818de8d7fSPeter Avalos 	if ((shell = getenv("SHELL")) == NULL)
8918de8d7fSPeter Avalos 		shell = _PATH_BSHELL;
9018de8d7fSPeter Avalos 
9118de8d7fSPeter Avalos 	/* Convert the port number into a string. */
9218de8d7fSPeter Avalos 	snprintf(strport, sizeof strport, "%hu", port);
9318de8d7fSPeter Avalos 
9418de8d7fSPeter Avalos 	/*
9518de8d7fSPeter Avalos 	 * Build the final command string in the buffer by making the
9618de8d7fSPeter Avalos 	 * appropriate substitutions to the given proxy command.
9718de8d7fSPeter Avalos 	 *
9818de8d7fSPeter Avalos 	 * Use "exec" to avoid "sh -c" processes on some platforms
9918de8d7fSPeter Avalos 	 * (e.g. Solaris)
10018de8d7fSPeter Avalos 	 */
10118de8d7fSPeter Avalos 	xasprintf(&tmp, "exec %s", proxy_command);
10218de8d7fSPeter Avalos 	command_string = percent_expand(tmp, "h", host,
10318de8d7fSPeter Avalos 	    "p", strport, (char *)NULL);
10418de8d7fSPeter Avalos 	xfree(tmp);
10518de8d7fSPeter Avalos 
10618de8d7fSPeter Avalos 	/* Create pipes for communicating with the proxy. */
10718de8d7fSPeter Avalos 	if (pipe(pin) < 0 || pipe(pout) < 0)
10818de8d7fSPeter Avalos 		fatal("Could not create pipes to communicate with the proxy: %.100s",
10918de8d7fSPeter Avalos 		    strerror(errno));
11018de8d7fSPeter Avalos 
11118de8d7fSPeter Avalos 	debug("Executing proxy command: %.500s", command_string);
11218de8d7fSPeter Avalos 
11318de8d7fSPeter Avalos 	/* Fork and execute the proxy command. */
11418de8d7fSPeter Avalos 	if ((pid = fork()) == 0) {
11518de8d7fSPeter Avalos 		char *argv[10];
11618de8d7fSPeter Avalos 
11718de8d7fSPeter Avalos 		/* Child.  Permanently give up superuser privileges. */
11818de8d7fSPeter Avalos 		permanently_drop_suid(original_real_uid);
11918de8d7fSPeter Avalos 
12018de8d7fSPeter Avalos 		/* Redirect stdin and stdout. */
12118de8d7fSPeter Avalos 		close(pin[1]);
12218de8d7fSPeter Avalos 		if (pin[0] != 0) {
12318de8d7fSPeter Avalos 			if (dup2(pin[0], 0) < 0)
12418de8d7fSPeter Avalos 				perror("dup2 stdin");
12518de8d7fSPeter Avalos 			close(pin[0]);
12618de8d7fSPeter Avalos 		}
12718de8d7fSPeter Avalos 		close(pout[0]);
12818de8d7fSPeter Avalos 		if (dup2(pout[1], 1) < 0)
12918de8d7fSPeter Avalos 			perror("dup2 stdout");
13018de8d7fSPeter Avalos 		/* Cannot be 1 because pin allocated two descriptors. */
13118de8d7fSPeter Avalos 		close(pout[1]);
13218de8d7fSPeter Avalos 
13318de8d7fSPeter Avalos 		/* Stderr is left as it is so that error messages get
13418de8d7fSPeter Avalos 		   printed on the user's terminal. */
13518de8d7fSPeter Avalos 		argv[0] = shell;
13618de8d7fSPeter Avalos 		argv[1] = "-c";
13718de8d7fSPeter Avalos 		argv[2] = command_string;
13818de8d7fSPeter Avalos 		argv[3] = NULL;
13918de8d7fSPeter Avalos 
14018de8d7fSPeter Avalos 		/* Execute the proxy command.  Note that we gave up any
14118de8d7fSPeter Avalos 		   extra privileges above. */
14218de8d7fSPeter Avalos 		execv(argv[0], argv);
14318de8d7fSPeter Avalos 		perror(argv[0]);
14418de8d7fSPeter Avalos 		exit(1);
14518de8d7fSPeter Avalos 	}
14618de8d7fSPeter Avalos 	/* Parent. */
14718de8d7fSPeter Avalos 	if (pid < 0)
14818de8d7fSPeter Avalos 		fatal("fork failed: %.100s", strerror(errno));
14918de8d7fSPeter Avalos 	else
15018de8d7fSPeter Avalos 		proxy_command_pid = pid; /* save pid to clean up later */
15118de8d7fSPeter Avalos 
15218de8d7fSPeter Avalos 	/* Close child side of the descriptors. */
15318de8d7fSPeter Avalos 	close(pin[0]);
15418de8d7fSPeter Avalos 	close(pout[1]);
15518de8d7fSPeter Avalos 
15618de8d7fSPeter Avalos 	/* Free the command name. */
15718de8d7fSPeter Avalos 	xfree(command_string);
15818de8d7fSPeter Avalos 
15918de8d7fSPeter Avalos 	/* Set the connection file descriptors. */
16018de8d7fSPeter Avalos 	packet_set_connection(pout[0], pin[1]);
16118de8d7fSPeter Avalos 	packet_set_timeout(options.server_alive_interval,
16218de8d7fSPeter Avalos 	    options.server_alive_count_max);
16318de8d7fSPeter Avalos 
16418de8d7fSPeter Avalos 	/* Indicate OK return */
16518de8d7fSPeter Avalos 	return 0;
16618de8d7fSPeter Avalos }
16718de8d7fSPeter Avalos 
16818de8d7fSPeter Avalos /*
16918de8d7fSPeter Avalos  * Creates a (possibly privileged) socket for use as the ssh connection.
17018de8d7fSPeter Avalos  */
17118de8d7fSPeter Avalos static int
17218de8d7fSPeter Avalos ssh_create_socket(int privileged, struct addrinfo *ai)
17318de8d7fSPeter Avalos {
17418de8d7fSPeter Avalos 	int sock, gaierr;
17518de8d7fSPeter Avalos 	struct addrinfo hints, *res;
17618de8d7fSPeter Avalos 
17718de8d7fSPeter Avalos 	/*
17818de8d7fSPeter Avalos 	 * If we are running as root and want to connect to a privileged
17918de8d7fSPeter Avalos 	 * port, bind our own socket to a privileged port.
18018de8d7fSPeter Avalos 	 */
18118de8d7fSPeter Avalos 	if (privileged) {
18218de8d7fSPeter Avalos 		int p = IPPORT_RESERVED - 1;
18318de8d7fSPeter Avalos 		PRIV_START;
18418de8d7fSPeter Avalos 		sock = rresvport_af(&p, ai->ai_family);
18518de8d7fSPeter Avalos 		PRIV_END;
18618de8d7fSPeter Avalos 		if (sock < 0)
18718de8d7fSPeter Avalos 			error("rresvport: af=%d %.100s", ai->ai_family,
18818de8d7fSPeter Avalos 			    strerror(errno));
18918de8d7fSPeter Avalos 		else
19018de8d7fSPeter Avalos 			debug("Allocated local port %d.", p);
19118de8d7fSPeter Avalos 		return sock;
19218de8d7fSPeter Avalos 	}
19318de8d7fSPeter Avalos 	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
19418de8d7fSPeter Avalos 	if (sock < 0)
19518de8d7fSPeter Avalos 		error("socket: %.100s", strerror(errno));
19618de8d7fSPeter Avalos 
19718de8d7fSPeter Avalos 	/* Bind the socket to an alternative local IP address */
19818de8d7fSPeter Avalos 	if (options.bind_address == NULL)
19918de8d7fSPeter Avalos 		return sock;
20018de8d7fSPeter Avalos 
20118de8d7fSPeter Avalos 	memset(&hints, 0, sizeof(hints));
20218de8d7fSPeter Avalos 	hints.ai_family = ai->ai_family;
20318de8d7fSPeter Avalos 	hints.ai_socktype = ai->ai_socktype;
20418de8d7fSPeter Avalos 	hints.ai_protocol = ai->ai_protocol;
20518de8d7fSPeter Avalos 	hints.ai_flags = AI_PASSIVE;
20618de8d7fSPeter Avalos 	gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
20718de8d7fSPeter Avalos 	if (gaierr) {
20818de8d7fSPeter Avalos 		error("getaddrinfo: %s: %s", options.bind_address,
20918de8d7fSPeter Avalos 		    ssh_gai_strerror(gaierr));
21018de8d7fSPeter Avalos 		close(sock);
21118de8d7fSPeter Avalos 		return -1;
21218de8d7fSPeter Avalos 	}
21318de8d7fSPeter Avalos 	if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
21418de8d7fSPeter Avalos 		error("bind: %s: %s", options.bind_address, strerror(errno));
21518de8d7fSPeter Avalos 		close(sock);
21618de8d7fSPeter Avalos 		freeaddrinfo(res);
21718de8d7fSPeter Avalos 		return -1;
21818de8d7fSPeter Avalos 	}
21918de8d7fSPeter Avalos 	freeaddrinfo(res);
22018de8d7fSPeter Avalos 	return sock;
22118de8d7fSPeter Avalos }
22218de8d7fSPeter Avalos 
22318de8d7fSPeter Avalos static int
22418de8d7fSPeter Avalos timeout_connect(int sockfd, const struct sockaddr *serv_addr,
22518de8d7fSPeter Avalos     socklen_t addrlen, int *timeoutp)
22618de8d7fSPeter Avalos {
22718de8d7fSPeter Avalos 	fd_set *fdset;
22818de8d7fSPeter Avalos 	struct timeval tv, t_start;
22918de8d7fSPeter Avalos 	socklen_t optlen;
23018de8d7fSPeter Avalos 	int optval, rc, result = -1;
23118de8d7fSPeter Avalos 
23218de8d7fSPeter Avalos 	gettimeofday(&t_start, NULL);
23318de8d7fSPeter Avalos 
23418de8d7fSPeter Avalos 	if (*timeoutp <= 0) {
23518de8d7fSPeter Avalos 		result = connect(sockfd, serv_addr, addrlen);
23618de8d7fSPeter Avalos 		goto done;
23718de8d7fSPeter Avalos 	}
23818de8d7fSPeter Avalos 
23918de8d7fSPeter Avalos 	set_nonblock(sockfd);
24018de8d7fSPeter Avalos 	rc = connect(sockfd, serv_addr, addrlen);
24118de8d7fSPeter Avalos 	if (rc == 0) {
24218de8d7fSPeter Avalos 		unset_nonblock(sockfd);
24318de8d7fSPeter Avalos 		result = 0;
24418de8d7fSPeter Avalos 		goto done;
24518de8d7fSPeter Avalos 	}
24618de8d7fSPeter Avalos 	if (errno != EINPROGRESS) {
24718de8d7fSPeter Avalos 		result = -1;
24818de8d7fSPeter Avalos 		goto done;
24918de8d7fSPeter Avalos 	}
25018de8d7fSPeter Avalos 
25118de8d7fSPeter Avalos 	fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS),
25218de8d7fSPeter Avalos 	    sizeof(fd_mask));
25318de8d7fSPeter Avalos 	FD_SET(sockfd, fdset);
25418de8d7fSPeter Avalos 	ms_to_timeval(&tv, *timeoutp);
25518de8d7fSPeter Avalos 
25618de8d7fSPeter Avalos 	for (;;) {
25718de8d7fSPeter Avalos 		rc = select(sockfd + 1, NULL, fdset, NULL, &tv);
25818de8d7fSPeter Avalos 		if (rc != -1 || errno != EINTR)
25918de8d7fSPeter Avalos 			break;
26018de8d7fSPeter Avalos 	}
26118de8d7fSPeter Avalos 
26218de8d7fSPeter Avalos 	switch (rc) {
26318de8d7fSPeter Avalos 	case 0:
26418de8d7fSPeter Avalos 		/* Timed out */
26518de8d7fSPeter Avalos 		errno = ETIMEDOUT;
26618de8d7fSPeter Avalos 		break;
26718de8d7fSPeter Avalos 	case -1:
26818de8d7fSPeter Avalos 		/* Select error */
26918de8d7fSPeter Avalos 		debug("select: %s", strerror(errno));
27018de8d7fSPeter Avalos 		break;
27118de8d7fSPeter Avalos 	case 1:
27218de8d7fSPeter Avalos 		/* Completed or failed */
27318de8d7fSPeter Avalos 		optval = 0;
27418de8d7fSPeter Avalos 		optlen = sizeof(optval);
27518de8d7fSPeter Avalos 		if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval,
27618de8d7fSPeter Avalos 		    &optlen) == -1) {
27718de8d7fSPeter Avalos 			debug("getsockopt: %s", strerror(errno));
27818de8d7fSPeter Avalos 			break;
27918de8d7fSPeter Avalos 		}
28018de8d7fSPeter Avalos 		if (optval != 0) {
28118de8d7fSPeter Avalos 			errno = optval;
28218de8d7fSPeter Avalos 			break;
28318de8d7fSPeter Avalos 		}
28418de8d7fSPeter Avalos 		result = 0;
28518de8d7fSPeter Avalos 		unset_nonblock(sockfd);
28618de8d7fSPeter Avalos 		break;
28718de8d7fSPeter Avalos 	default:
28818de8d7fSPeter Avalos 		/* Should not occur */
28918de8d7fSPeter Avalos 		fatal("Bogus return (%d) from select()", rc);
29018de8d7fSPeter Avalos 	}
29118de8d7fSPeter Avalos 
29218de8d7fSPeter Avalos 	xfree(fdset);
29318de8d7fSPeter Avalos 
29418de8d7fSPeter Avalos  done:
29518de8d7fSPeter Avalos  	if (result == 0 && *timeoutp > 0) {
29618de8d7fSPeter Avalos 		ms_subtract_diff(&t_start, timeoutp);
29718de8d7fSPeter Avalos 		if (*timeoutp <= 0) {
29818de8d7fSPeter Avalos 			errno = ETIMEDOUT;
29918de8d7fSPeter Avalos 			result = -1;
30018de8d7fSPeter Avalos 		}
30118de8d7fSPeter Avalos 	}
30218de8d7fSPeter Avalos 
30318de8d7fSPeter Avalos 	return (result);
30418de8d7fSPeter Avalos }
30518de8d7fSPeter Avalos 
30618de8d7fSPeter Avalos /*
30718de8d7fSPeter Avalos  * Opens a TCP/IP connection to the remote server on the given host.
30818de8d7fSPeter Avalos  * The address of the remote host will be returned in hostaddr.
30918de8d7fSPeter Avalos  * If port is 0, the default port will be used.  If needpriv is true,
31018de8d7fSPeter Avalos  * a privileged port will be allocated to make the connection.
31118de8d7fSPeter Avalos  * This requires super-user privileges if needpriv is true.
31218de8d7fSPeter Avalos  * Connection_attempts specifies the maximum number of tries (one per
31318de8d7fSPeter Avalos  * second).  If proxy_command is non-NULL, it specifies the command (with %h
31418de8d7fSPeter Avalos  * and %p substituted for host and port, respectively) to use to contact
31518de8d7fSPeter Avalos  * the daemon.
31618de8d7fSPeter Avalos  */
31718de8d7fSPeter Avalos int
31818de8d7fSPeter Avalos ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
31918de8d7fSPeter Avalos     u_short port, int family, int connection_attempts, int *timeout_ms,
32018de8d7fSPeter Avalos     int want_keepalive, int needpriv, const char *proxy_command)
32118de8d7fSPeter Avalos {
32218de8d7fSPeter Avalos 	int gaierr;
32318de8d7fSPeter Avalos 	int on = 1;
32418de8d7fSPeter Avalos 	int sock = -1, attempt;
32518de8d7fSPeter Avalos 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
32618de8d7fSPeter Avalos 	struct addrinfo hints, *ai, *aitop;
32718de8d7fSPeter Avalos 
32818de8d7fSPeter Avalos 	debug2("ssh_connect: needpriv %d", needpriv);
32918de8d7fSPeter Avalos 
33018de8d7fSPeter Avalos 	/* If a proxy command is given, connect using it. */
33118de8d7fSPeter Avalos 	if (proxy_command != NULL)
33218de8d7fSPeter Avalos 		return ssh_proxy_connect(host, port, proxy_command);
33318de8d7fSPeter Avalos 
33418de8d7fSPeter Avalos 	/* No proxy command. */
33518de8d7fSPeter Avalos 
33618de8d7fSPeter Avalos 	memset(&hints, 0, sizeof(hints));
33718de8d7fSPeter Avalos 	hints.ai_family = family;
33818de8d7fSPeter Avalos 	hints.ai_socktype = SOCK_STREAM;
33918de8d7fSPeter Avalos 	snprintf(strport, sizeof strport, "%u", port);
34018de8d7fSPeter Avalos 	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
34118de8d7fSPeter Avalos 		fatal("%s: Could not resolve hostname %.100s: %s", __progname,
34218de8d7fSPeter Avalos 		    host, ssh_gai_strerror(gaierr));
34318de8d7fSPeter Avalos 
34418de8d7fSPeter Avalos 	for (attempt = 0; attempt < connection_attempts; attempt++) {
34518de8d7fSPeter Avalos 		if (attempt > 0) {
34618de8d7fSPeter Avalos 			/* Sleep a moment before retrying. */
34718de8d7fSPeter Avalos 			sleep(1);
34818de8d7fSPeter Avalos 			debug("Trying again...");
34918de8d7fSPeter Avalos 		}
35018de8d7fSPeter Avalos 		/*
35118de8d7fSPeter Avalos 		 * Loop through addresses for this host, and try each one in
35218de8d7fSPeter Avalos 		 * sequence until the connection succeeds.
35318de8d7fSPeter Avalos 		 */
35418de8d7fSPeter Avalos 		for (ai = aitop; ai; ai = ai->ai_next) {
35518de8d7fSPeter Avalos 			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
35618de8d7fSPeter Avalos 				continue;
35718de8d7fSPeter Avalos 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
35818de8d7fSPeter Avalos 			    ntop, sizeof(ntop), strport, sizeof(strport),
35918de8d7fSPeter Avalos 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
36018de8d7fSPeter Avalos 				error("ssh_connect: getnameinfo failed");
36118de8d7fSPeter Avalos 				continue;
36218de8d7fSPeter Avalos 			}
36318de8d7fSPeter Avalos 			debug("Connecting to %.200s [%.100s] port %s.",
36418de8d7fSPeter Avalos 				host, ntop, strport);
36518de8d7fSPeter Avalos 
36618de8d7fSPeter Avalos 			/* Create a socket for connecting. */
36718de8d7fSPeter Avalos 			sock = ssh_create_socket(needpriv, ai);
36818de8d7fSPeter Avalos 			if (sock < 0)
36918de8d7fSPeter Avalos 				/* Any error is already output */
37018de8d7fSPeter Avalos 				continue;
37118de8d7fSPeter Avalos 
37218de8d7fSPeter Avalos 			if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
37318de8d7fSPeter Avalos 			    timeout_ms) >= 0) {
37418de8d7fSPeter Avalos 				/* Successful connection. */
37518de8d7fSPeter Avalos 				memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
37618de8d7fSPeter Avalos 				break;
37718de8d7fSPeter Avalos 			} else {
37818de8d7fSPeter Avalos 				debug("connect to address %s port %s: %s",
37918de8d7fSPeter Avalos 				    ntop, strport, strerror(errno));
38018de8d7fSPeter Avalos 				close(sock);
38118de8d7fSPeter Avalos 				sock = -1;
38218de8d7fSPeter Avalos 			}
38318de8d7fSPeter Avalos 		}
38418de8d7fSPeter Avalos 		if (sock != -1)
38518de8d7fSPeter Avalos 			break;	/* Successful connection. */
38618de8d7fSPeter Avalos 	}
38718de8d7fSPeter Avalos 
38818de8d7fSPeter Avalos 	freeaddrinfo(aitop);
38918de8d7fSPeter Avalos 
39018de8d7fSPeter Avalos 	/* Return failure if we didn't get a successful connection. */
39118de8d7fSPeter Avalos 	if (sock == -1) {
39218de8d7fSPeter Avalos 		error("ssh: connect to host %s port %s: %s",
39318de8d7fSPeter Avalos 		    host, strport, strerror(errno));
39418de8d7fSPeter Avalos 		return (-1);
39518de8d7fSPeter Avalos 	}
39618de8d7fSPeter Avalos 
39718de8d7fSPeter Avalos 	debug("Connection established.");
39818de8d7fSPeter Avalos 
39918de8d7fSPeter Avalos 	/* Set SO_KEEPALIVE if requested. */
40018de8d7fSPeter Avalos 	if (want_keepalive &&
40118de8d7fSPeter Avalos 	    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
40218de8d7fSPeter Avalos 	    sizeof(on)) < 0)
40318de8d7fSPeter Avalos 		error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
40418de8d7fSPeter Avalos 
40518de8d7fSPeter Avalos 	/* Set the connection. */
40618de8d7fSPeter Avalos 	packet_set_connection(sock, sock);
40718de8d7fSPeter Avalos 	packet_set_timeout(options.server_alive_interval,
40818de8d7fSPeter Avalos 	    options.server_alive_count_max);
40918de8d7fSPeter Avalos 
41018de8d7fSPeter Avalos 	return 0;
41118de8d7fSPeter Avalos }
41218de8d7fSPeter Avalos 
41318de8d7fSPeter Avalos /*
41418de8d7fSPeter Avalos  * Waits for the server identification string, and sends our own
41518de8d7fSPeter Avalos  * identification string.
41618de8d7fSPeter Avalos  */
417*40c002afSPeter Avalos void
41818de8d7fSPeter Avalos ssh_exchange_identification(int timeout_ms)
41918de8d7fSPeter Avalos {
42018de8d7fSPeter Avalos 	char buf[256], remote_version[256];	/* must be same size! */
42118de8d7fSPeter Avalos 	int remote_major, remote_minor, mismatch;
42218de8d7fSPeter Avalos 	int connection_in = packet_get_connection_in();
42318de8d7fSPeter Avalos 	int connection_out = packet_get_connection_out();
42418de8d7fSPeter Avalos 	int minor1 = PROTOCOL_MINOR_1;
42518de8d7fSPeter Avalos 	u_int i, n;
42618de8d7fSPeter Avalos 	size_t len;
42718de8d7fSPeter Avalos 	int fdsetsz, remaining, rc;
42818de8d7fSPeter Avalos 	struct timeval t_start, t_remaining;
42918de8d7fSPeter Avalos 	fd_set *fdset;
43018de8d7fSPeter Avalos 
43118de8d7fSPeter Avalos 	fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask);
43218de8d7fSPeter Avalos 	fdset = xcalloc(1, fdsetsz);
43318de8d7fSPeter Avalos 
43418de8d7fSPeter Avalos 	/* Read other side's version identification. */
43518de8d7fSPeter Avalos 	remaining = timeout_ms;
43618de8d7fSPeter Avalos 	for (n = 0;;) {
43718de8d7fSPeter Avalos 		for (i = 0; i < sizeof(buf) - 1; i++) {
43818de8d7fSPeter Avalos 			if (timeout_ms > 0) {
43918de8d7fSPeter Avalos 				gettimeofday(&t_start, NULL);
44018de8d7fSPeter Avalos 				ms_to_timeval(&t_remaining, remaining);
44118de8d7fSPeter Avalos 				FD_SET(connection_in, fdset);
44218de8d7fSPeter Avalos 				rc = select(connection_in + 1, fdset, NULL,
44318de8d7fSPeter Avalos 				    fdset, &t_remaining);
44418de8d7fSPeter Avalos 				ms_subtract_diff(&t_start, &remaining);
44518de8d7fSPeter Avalos 				if (rc == 0 || remaining <= 0)
44618de8d7fSPeter Avalos 					fatal("Connection timed out during "
44718de8d7fSPeter Avalos 					    "banner exchange");
44818de8d7fSPeter Avalos 				if (rc == -1) {
44918de8d7fSPeter Avalos 					if (errno == EINTR)
45018de8d7fSPeter Avalos 						continue;
45118de8d7fSPeter Avalos 					fatal("ssh_exchange_identification: "
45218de8d7fSPeter Avalos 					    "select: %s", strerror(errno));
45318de8d7fSPeter Avalos 				}
45418de8d7fSPeter Avalos 			}
45518de8d7fSPeter Avalos 
456*40c002afSPeter Avalos 			len = roaming_atomicio(read, connection_in, &buf[i], 1);
45718de8d7fSPeter Avalos 
45818de8d7fSPeter Avalos 			if (len != 1 && errno == EPIPE)
45918de8d7fSPeter Avalos 				fatal("ssh_exchange_identification: "
46018de8d7fSPeter Avalos 				    "Connection closed by remote host");
46118de8d7fSPeter Avalos 			else if (len != 1)
46218de8d7fSPeter Avalos 				fatal("ssh_exchange_identification: "
46318de8d7fSPeter Avalos 				    "read: %.100s", strerror(errno));
46418de8d7fSPeter Avalos 			if (buf[i] == '\r') {
46518de8d7fSPeter Avalos 				buf[i] = '\n';
46618de8d7fSPeter Avalos 				buf[i + 1] = 0;
46718de8d7fSPeter Avalos 				continue;		/**XXX wait for \n */
46818de8d7fSPeter Avalos 			}
46918de8d7fSPeter Avalos 			if (buf[i] == '\n') {
47018de8d7fSPeter Avalos 				buf[i + 1] = 0;
47118de8d7fSPeter Avalos 				break;
47218de8d7fSPeter Avalos 			}
47318de8d7fSPeter Avalos 			if (++n > 65536)
47418de8d7fSPeter Avalos 				fatal("ssh_exchange_identification: "
47518de8d7fSPeter Avalos 				    "No banner received");
47618de8d7fSPeter Avalos 		}
47718de8d7fSPeter Avalos 		buf[sizeof(buf) - 1] = 0;
47818de8d7fSPeter Avalos 		if (strncmp(buf, "SSH-", 4) == 0)
47918de8d7fSPeter Avalos 			break;
48018de8d7fSPeter Avalos 		debug("ssh_exchange_identification: %s", buf);
48118de8d7fSPeter Avalos 	}
48218de8d7fSPeter Avalos 	server_version_string = xstrdup(buf);
48318de8d7fSPeter Avalos 	xfree(fdset);
48418de8d7fSPeter Avalos 
48518de8d7fSPeter Avalos 	/*
48618de8d7fSPeter Avalos 	 * Check that the versions match.  In future this might accept
48718de8d7fSPeter Avalos 	 * several versions and set appropriate flags to handle them.
48818de8d7fSPeter Avalos 	 */
48918de8d7fSPeter Avalos 	if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
49018de8d7fSPeter Avalos 	    &remote_major, &remote_minor, remote_version) != 3)
49118de8d7fSPeter Avalos 		fatal("Bad remote protocol version identification: '%.100s'", buf);
49218de8d7fSPeter Avalos 	debug("Remote protocol version %d.%d, remote software version %.100s",
49318de8d7fSPeter Avalos 	    remote_major, remote_minor, remote_version);
49418de8d7fSPeter Avalos 
49518de8d7fSPeter Avalos 	compat_datafellows(remote_version);
49618de8d7fSPeter Avalos 	mismatch = 0;
49718de8d7fSPeter Avalos 
49818de8d7fSPeter Avalos 	switch (remote_major) {
49918de8d7fSPeter Avalos 	case 1:
50018de8d7fSPeter Avalos 		if (remote_minor == 99 &&
50118de8d7fSPeter Avalos 		    (options.protocol & SSH_PROTO_2) &&
50218de8d7fSPeter Avalos 		    !(options.protocol & SSH_PROTO_1_PREFERRED)) {
50318de8d7fSPeter Avalos 			enable_compat20();
50418de8d7fSPeter Avalos 			break;
50518de8d7fSPeter Avalos 		}
50618de8d7fSPeter Avalos 		if (!(options.protocol & SSH_PROTO_1)) {
50718de8d7fSPeter Avalos 			mismatch = 1;
50818de8d7fSPeter Avalos 			break;
50918de8d7fSPeter Avalos 		}
51018de8d7fSPeter Avalos 		if (remote_minor < 3) {
51118de8d7fSPeter Avalos 			fatal("Remote machine has too old SSH software version.");
51218de8d7fSPeter Avalos 		} else if (remote_minor == 3 || remote_minor == 4) {
51318de8d7fSPeter Avalos 			/* We speak 1.3, too. */
51418de8d7fSPeter Avalos 			enable_compat13();
51518de8d7fSPeter Avalos 			minor1 = 3;
51618de8d7fSPeter Avalos 			if (options.forward_agent) {
51718de8d7fSPeter Avalos 				logit("Agent forwarding disabled for protocol 1.3");
51818de8d7fSPeter Avalos 				options.forward_agent = 0;
51918de8d7fSPeter Avalos 			}
52018de8d7fSPeter Avalos 		}
52118de8d7fSPeter Avalos 		break;
52218de8d7fSPeter Avalos 	case 2:
52318de8d7fSPeter Avalos 		if (options.protocol & SSH_PROTO_2) {
52418de8d7fSPeter Avalos 			enable_compat20();
52518de8d7fSPeter Avalos 			break;
52618de8d7fSPeter Avalos 		}
52718de8d7fSPeter Avalos 		/* FALLTHROUGH */
52818de8d7fSPeter Avalos 	default:
52918de8d7fSPeter Avalos 		mismatch = 1;
53018de8d7fSPeter Avalos 		break;
53118de8d7fSPeter Avalos 	}
53218de8d7fSPeter Avalos 	if (mismatch)
53318de8d7fSPeter Avalos 		fatal("Protocol major versions differ: %d vs. %d",
53418de8d7fSPeter Avalos 		    (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
53518de8d7fSPeter Avalos 		    remote_major);
53618de8d7fSPeter Avalos 	/* Send our own protocol version identification. */
53718de8d7fSPeter Avalos 	snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s",
53818de8d7fSPeter Avalos 	    compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
53918de8d7fSPeter Avalos 	    compat20 ? PROTOCOL_MINOR_2 : minor1,
54018de8d7fSPeter Avalos 	    SSH_VERSION, compat20 ? "\r\n" : "\n");
541*40c002afSPeter Avalos 	if (roaming_atomicio(vwrite, connection_out, buf, strlen(buf))
542*40c002afSPeter Avalos 	    != strlen(buf))
54318de8d7fSPeter Avalos 		fatal("write: %.100s", strerror(errno));
54418de8d7fSPeter Avalos 	client_version_string = xstrdup(buf);
54518de8d7fSPeter Avalos 	chop(client_version_string);
54618de8d7fSPeter Avalos 	chop(server_version_string);
54718de8d7fSPeter Avalos 	debug("Local version string %.100s", client_version_string);
54818de8d7fSPeter Avalos }
54918de8d7fSPeter Avalos 
55018de8d7fSPeter Avalos /* defaults to 'no' */
55118de8d7fSPeter Avalos static int
55218de8d7fSPeter Avalos confirm(const char *prompt)
55318de8d7fSPeter Avalos {
55418de8d7fSPeter Avalos 	const char *msg, *again = "Please type 'yes' or 'no': ";
55518de8d7fSPeter Avalos 	char *p;
55618de8d7fSPeter Avalos 	int ret = -1;
55718de8d7fSPeter Avalos 
55818de8d7fSPeter Avalos 	if (options.batch_mode)
55918de8d7fSPeter Avalos 		return 0;
56018de8d7fSPeter Avalos 	for (msg = prompt;;msg = again) {
56118de8d7fSPeter Avalos 		p = read_passphrase(msg, RP_ECHO);
56218de8d7fSPeter Avalos 		if (p == NULL ||
56318de8d7fSPeter Avalos 		    (p[0] == '\0') || (p[0] == '\n') ||
56418de8d7fSPeter Avalos 		    strncasecmp(p, "no", 2) == 0)
56518de8d7fSPeter Avalos 			ret = 0;
56618de8d7fSPeter Avalos 		if (p && strncasecmp(p, "yes", 3) == 0)
56718de8d7fSPeter Avalos 			ret = 1;
56818de8d7fSPeter Avalos 		if (p)
56918de8d7fSPeter Avalos 			xfree(p);
57018de8d7fSPeter Avalos 		if (ret != -1)
57118de8d7fSPeter Avalos 			return ret;
57218de8d7fSPeter Avalos 	}
57318de8d7fSPeter Avalos }
57418de8d7fSPeter Avalos 
57518de8d7fSPeter Avalos /*
57618de8d7fSPeter Avalos  * check whether the supplied host key is valid, return -1 if the key
57718de8d7fSPeter Avalos  * is not valid. the user_hostfile will not be updated if 'readonly' is true.
57818de8d7fSPeter Avalos  */
57918de8d7fSPeter Avalos #define RDRW	0
58018de8d7fSPeter Avalos #define RDONLY	1
58118de8d7fSPeter Avalos #define ROQUIET	2
58218de8d7fSPeter Avalos static int
58318de8d7fSPeter Avalos check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
58418de8d7fSPeter Avalos     Key *host_key, int readonly, const char *user_hostfile,
58518de8d7fSPeter Avalos     const char *system_hostfile)
58618de8d7fSPeter Avalos {
58718de8d7fSPeter Avalos 	Key *file_key;
58818de8d7fSPeter Avalos 	const char *type = key_type(host_key);
58918de8d7fSPeter Avalos 	char *ip = NULL, *host = NULL;
59018de8d7fSPeter Avalos 	char hostline[1000], *hostp, *fp, *ra;
59118de8d7fSPeter Avalos 	HostStatus host_status;
59218de8d7fSPeter Avalos 	HostStatus ip_status;
59318de8d7fSPeter Avalos 	int r, local = 0, host_ip_differ = 0;
59418de8d7fSPeter Avalos 	int salen;
59518de8d7fSPeter Avalos 	char ntop[NI_MAXHOST];
59618de8d7fSPeter Avalos 	char msg[1024];
59718de8d7fSPeter Avalos 	int len, host_line, ip_line, cancelled_forwarding = 0;
59818de8d7fSPeter Avalos 	const char *host_file = NULL, *ip_file = NULL;
59918de8d7fSPeter Avalos 
60018de8d7fSPeter Avalos 	/*
60118de8d7fSPeter Avalos 	 * Force accepting of the host key for loopback/localhost. The
60218de8d7fSPeter Avalos 	 * problem is that if the home directory is NFS-mounted to multiple
60318de8d7fSPeter Avalos 	 * machines, localhost will refer to a different machine in each of
60418de8d7fSPeter Avalos 	 * them, and the user will get bogus HOST_CHANGED warnings.  This
60518de8d7fSPeter Avalos 	 * essentially disables host authentication for localhost; however,
60618de8d7fSPeter Avalos 	 * this is probably not a real problem.
60718de8d7fSPeter Avalos 	 */
60818de8d7fSPeter Avalos 	/**  hostaddr == 0! */
60918de8d7fSPeter Avalos 	switch (hostaddr->sa_family) {
61018de8d7fSPeter Avalos 	case AF_INET:
61118de8d7fSPeter Avalos 		local = (ntohl(((struct sockaddr_in *)hostaddr)->
61218de8d7fSPeter Avalos 		    sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
61318de8d7fSPeter Avalos 		salen = sizeof(struct sockaddr_in);
61418de8d7fSPeter Avalos 		break;
61518de8d7fSPeter Avalos 	case AF_INET6:
61618de8d7fSPeter Avalos 		local = IN6_IS_ADDR_LOOPBACK(
61718de8d7fSPeter Avalos 		    &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
61818de8d7fSPeter Avalos 		salen = sizeof(struct sockaddr_in6);
61918de8d7fSPeter Avalos 		break;
62018de8d7fSPeter Avalos 	default:
62118de8d7fSPeter Avalos 		local = 0;
62218de8d7fSPeter Avalos 		salen = sizeof(struct sockaddr_storage);
62318de8d7fSPeter Avalos 		break;
62418de8d7fSPeter Avalos 	}
62518de8d7fSPeter Avalos 	if (options.no_host_authentication_for_localhost == 1 && local &&
62618de8d7fSPeter Avalos 	    options.host_key_alias == NULL) {
62718de8d7fSPeter Avalos 		debug("Forcing accepting of host key for "
62818de8d7fSPeter Avalos 		    "loopback/localhost.");
62918de8d7fSPeter Avalos 		return 0;
63018de8d7fSPeter Avalos 	}
63118de8d7fSPeter Avalos 
63218de8d7fSPeter Avalos 	/*
63318de8d7fSPeter Avalos 	 * We don't have the remote ip-address for connections
63418de8d7fSPeter Avalos 	 * using a proxy command
63518de8d7fSPeter Avalos 	 */
63618de8d7fSPeter Avalos 	if (options.proxy_command == NULL) {
63718de8d7fSPeter Avalos 		if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop),
63818de8d7fSPeter Avalos 		    NULL, 0, NI_NUMERICHOST) != 0)
63918de8d7fSPeter Avalos 			fatal("check_host_key: getnameinfo failed");
64018de8d7fSPeter Avalos 		ip = put_host_port(ntop, port);
64118de8d7fSPeter Avalos 	} else {
64218de8d7fSPeter Avalos 		ip = xstrdup("<no hostip for proxy command>");
64318de8d7fSPeter Avalos 	}
64418de8d7fSPeter Avalos 
64518de8d7fSPeter Avalos 	/*
64618de8d7fSPeter Avalos 	 * Turn off check_host_ip if the connection is to localhost, via proxy
64718de8d7fSPeter Avalos 	 * command or if we don't have a hostname to compare with
64818de8d7fSPeter Avalos 	 */
64918de8d7fSPeter Avalos 	if (options.check_host_ip && (local ||
65018de8d7fSPeter Avalos 	    strcmp(hostname, ip) == 0 || options.proxy_command != NULL))
65118de8d7fSPeter Avalos 		options.check_host_ip = 0;
65218de8d7fSPeter Avalos 
65318de8d7fSPeter Avalos 	/*
65418de8d7fSPeter Avalos 	 * Allow the user to record the key under a different name or
65518de8d7fSPeter Avalos 	 * differentiate a non-standard port.  This is useful for ssh
65618de8d7fSPeter Avalos 	 * tunneling over forwarded connections or if you run multiple
65718de8d7fSPeter Avalos 	 * sshd's on different ports on the same machine.
65818de8d7fSPeter Avalos 	 */
65918de8d7fSPeter Avalos 	if (options.host_key_alias != NULL) {
66018de8d7fSPeter Avalos 		host = xstrdup(options.host_key_alias);
66118de8d7fSPeter Avalos 		debug("using hostkeyalias: %s", host);
66218de8d7fSPeter Avalos 	} else {
66318de8d7fSPeter Avalos 		host = put_host_port(hostname, port);
66418de8d7fSPeter Avalos 	}
66518de8d7fSPeter Avalos 
66618de8d7fSPeter Avalos 	/*
66718de8d7fSPeter Avalos 	 * Store the host key from the known host file in here so that we can
66818de8d7fSPeter Avalos 	 * compare it with the key for the IP address.
66918de8d7fSPeter Avalos 	 */
67018de8d7fSPeter Avalos 	file_key = key_new(host_key->type);
67118de8d7fSPeter Avalos 
67218de8d7fSPeter Avalos 	/*
67318de8d7fSPeter Avalos 	 * Check if the host key is present in the user's list of known
67418de8d7fSPeter Avalos 	 * hosts or in the systemwide list.
67518de8d7fSPeter Avalos 	 */
67618de8d7fSPeter Avalos 	host_file = user_hostfile;
67718de8d7fSPeter Avalos 	host_status = check_host_in_hostfile(host_file, host, host_key,
67818de8d7fSPeter Avalos 	    file_key, &host_line);
67918de8d7fSPeter Avalos 	if (host_status == HOST_NEW) {
68018de8d7fSPeter Avalos 		host_file = system_hostfile;
68118de8d7fSPeter Avalos 		host_status = check_host_in_hostfile(host_file, host, host_key,
68218de8d7fSPeter Avalos 		    file_key, &host_line);
68318de8d7fSPeter Avalos 	}
68418de8d7fSPeter Avalos 	/*
68518de8d7fSPeter Avalos 	 * Also perform check for the ip address, skip the check if we are
68618de8d7fSPeter Avalos 	 * localhost or the hostname was an ip address to begin with
68718de8d7fSPeter Avalos 	 */
68818de8d7fSPeter Avalos 	if (options.check_host_ip) {
68918de8d7fSPeter Avalos 		Key *ip_key = key_new(host_key->type);
69018de8d7fSPeter Avalos 
69118de8d7fSPeter Avalos 		ip_file = user_hostfile;
69218de8d7fSPeter Avalos 		ip_status = check_host_in_hostfile(ip_file, ip, host_key,
69318de8d7fSPeter Avalos 		    ip_key, &ip_line);
69418de8d7fSPeter Avalos 		if (ip_status == HOST_NEW) {
69518de8d7fSPeter Avalos 			ip_file = system_hostfile;
69618de8d7fSPeter Avalos 			ip_status = check_host_in_hostfile(ip_file, ip,
69718de8d7fSPeter Avalos 			    host_key, ip_key, &ip_line);
69818de8d7fSPeter Avalos 		}
69918de8d7fSPeter Avalos 		if (host_status == HOST_CHANGED &&
70018de8d7fSPeter Avalos 		    (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key)))
70118de8d7fSPeter Avalos 			host_ip_differ = 1;
70218de8d7fSPeter Avalos 
70318de8d7fSPeter Avalos 		key_free(ip_key);
70418de8d7fSPeter Avalos 	} else
70518de8d7fSPeter Avalos 		ip_status = host_status;
70618de8d7fSPeter Avalos 
70718de8d7fSPeter Avalos 	key_free(file_key);
70818de8d7fSPeter Avalos 
70918de8d7fSPeter Avalos 	switch (host_status) {
71018de8d7fSPeter Avalos 	case HOST_OK:
71118de8d7fSPeter Avalos 		/* The host is known and the key matches. */
71218de8d7fSPeter Avalos 		debug("Host '%.200s' is known and matches the %s host key.",
71318de8d7fSPeter Avalos 		    host, type);
71418de8d7fSPeter Avalos 		debug("Found key in %s:%d", host_file, host_line);
71518de8d7fSPeter Avalos 		if (options.check_host_ip && ip_status == HOST_NEW) {
71618de8d7fSPeter Avalos 			if (readonly)
71718de8d7fSPeter Avalos 				logit("%s host key for IP address "
71818de8d7fSPeter Avalos 				    "'%.128s' not in list of known hosts.",
71918de8d7fSPeter Avalos 				    type, ip);
72018de8d7fSPeter Avalos 			else if (!add_host_to_hostfile(user_hostfile, ip,
72118de8d7fSPeter Avalos 			    host_key, options.hash_known_hosts))
72218de8d7fSPeter Avalos 				logit("Failed to add the %s host key for IP "
72318de8d7fSPeter Avalos 				    "address '%.128s' to the list of known "
72418de8d7fSPeter Avalos 				    "hosts (%.30s).", type, ip, user_hostfile);
72518de8d7fSPeter Avalos 			else
72618de8d7fSPeter Avalos 				logit("Warning: Permanently added the %s host "
72718de8d7fSPeter Avalos 				    "key for IP address '%.128s' to the list "
72818de8d7fSPeter Avalos 				    "of known hosts.", type, ip);
72918de8d7fSPeter Avalos 		} else if (options.visual_host_key) {
73018de8d7fSPeter Avalos 			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
73118de8d7fSPeter Avalos 			ra = key_fingerprint(host_key, SSH_FP_MD5,
73218de8d7fSPeter Avalos 			    SSH_FP_RANDOMART);
73318de8d7fSPeter Avalos 			logit("Host key fingerprint is %s\n%s\n", fp, ra);
73418de8d7fSPeter Avalos 			xfree(ra);
73518de8d7fSPeter Avalos 			xfree(fp);
73618de8d7fSPeter Avalos 		}
73718de8d7fSPeter Avalos 		break;
73818de8d7fSPeter Avalos 	case HOST_NEW:
73918de8d7fSPeter Avalos 		if (options.host_key_alias == NULL && port != 0 &&
74018de8d7fSPeter Avalos 		    port != SSH_DEFAULT_PORT) {
74118de8d7fSPeter Avalos 			debug("checking without port identifier");
742cb5eb4f1SPeter Avalos 			if (check_host_key(hostname, hostaddr, 0, host_key,
743cb5eb4f1SPeter Avalos 			    ROQUIET, user_hostfile, system_hostfile) == 0) {
74418de8d7fSPeter Avalos 				debug("found matching key w/out port");
74518de8d7fSPeter Avalos 				break;
74618de8d7fSPeter Avalos 			}
74718de8d7fSPeter Avalos 		}
74818de8d7fSPeter Avalos 		if (readonly)
74918de8d7fSPeter Avalos 			goto fail;
75018de8d7fSPeter Avalos 		/* The host is new. */
75118de8d7fSPeter Avalos 		if (options.strict_host_key_checking == 1) {
75218de8d7fSPeter Avalos 			/*
75318de8d7fSPeter Avalos 			 * User has requested strict host key checking.  We
75418de8d7fSPeter Avalos 			 * will not add the host key automatically.  The only
75518de8d7fSPeter Avalos 			 * alternative left is to abort.
75618de8d7fSPeter Avalos 			 */
75718de8d7fSPeter Avalos 			error("No %s host key is known for %.200s and you "
75818de8d7fSPeter Avalos 			    "have requested strict checking.", type, host);
75918de8d7fSPeter Avalos 			goto fail;
76018de8d7fSPeter Avalos 		} else if (options.strict_host_key_checking == 2) {
76118de8d7fSPeter Avalos 			char msg1[1024], msg2[1024];
76218de8d7fSPeter Avalos 
76318de8d7fSPeter Avalos 			if (show_other_keys(host, host_key))
76418de8d7fSPeter Avalos 				snprintf(msg1, sizeof(msg1),
76518de8d7fSPeter Avalos 				    "\nbut keys of different type are already"
76618de8d7fSPeter Avalos 				    " known for this host.");
76718de8d7fSPeter Avalos 			else
76818de8d7fSPeter Avalos 				snprintf(msg1, sizeof(msg1), ".");
76918de8d7fSPeter Avalos 			/* The default */
77018de8d7fSPeter Avalos 			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
77118de8d7fSPeter Avalos 			ra = key_fingerprint(host_key, SSH_FP_MD5,
77218de8d7fSPeter Avalos 			    SSH_FP_RANDOMART);
77318de8d7fSPeter Avalos 			msg2[0] = '\0';
77418de8d7fSPeter Avalos 			if (options.verify_host_key_dns) {
77518de8d7fSPeter Avalos 				if (matching_host_key_dns)
77618de8d7fSPeter Avalos 					snprintf(msg2, sizeof(msg2),
77718de8d7fSPeter Avalos 					    "Matching host key fingerprint"
77818de8d7fSPeter Avalos 					    " found in DNS.\n");
77918de8d7fSPeter Avalos 				else
78018de8d7fSPeter Avalos 					snprintf(msg2, sizeof(msg2),
78118de8d7fSPeter Avalos 					    "No matching host key fingerprint"
78218de8d7fSPeter Avalos 					    " found in DNS.\n");
78318de8d7fSPeter Avalos 			}
78418de8d7fSPeter Avalos 			snprintf(msg, sizeof(msg),
78518de8d7fSPeter Avalos 			    "The authenticity of host '%.200s (%s)' can't be "
78618de8d7fSPeter Avalos 			    "established%s\n"
78718de8d7fSPeter Avalos 			    "%s key fingerprint is %s.%s%s\n%s"
78818de8d7fSPeter Avalos 			    "Are you sure you want to continue connecting "
78918de8d7fSPeter Avalos 			    "(yes/no)? ",
79018de8d7fSPeter Avalos 			    host, ip, msg1, type, fp,
79118de8d7fSPeter Avalos 			    options.visual_host_key ? "\n" : "",
79218de8d7fSPeter Avalos 			    options.visual_host_key ? ra : "",
79318de8d7fSPeter Avalos 			    msg2);
79418de8d7fSPeter Avalos 			xfree(ra);
79518de8d7fSPeter Avalos 			xfree(fp);
79618de8d7fSPeter Avalos 			if (!confirm(msg))
79718de8d7fSPeter Avalos 				goto fail;
79818de8d7fSPeter Avalos 		}
79918de8d7fSPeter Avalos 		/*
80018de8d7fSPeter Avalos 		 * If not in strict mode, add the key automatically to the
80118de8d7fSPeter Avalos 		 * local known_hosts file.
80218de8d7fSPeter Avalos 		 */
80318de8d7fSPeter Avalos 		if (options.check_host_ip && ip_status == HOST_NEW) {
80418de8d7fSPeter Avalos 			snprintf(hostline, sizeof(hostline), "%s,%s",
80518de8d7fSPeter Avalos 			    host, ip);
80618de8d7fSPeter Avalos 			hostp = hostline;
80718de8d7fSPeter Avalos 			if (options.hash_known_hosts) {
80818de8d7fSPeter Avalos 				/* Add hash of host and IP separately */
80918de8d7fSPeter Avalos 				r = add_host_to_hostfile(user_hostfile, host,
81018de8d7fSPeter Avalos 				    host_key, options.hash_known_hosts) &&
81118de8d7fSPeter Avalos 				    add_host_to_hostfile(user_hostfile, ip,
81218de8d7fSPeter Avalos 				    host_key, options.hash_known_hosts);
81318de8d7fSPeter Avalos 			} else {
81418de8d7fSPeter Avalos 				/* Add unhashed "host,ip" */
81518de8d7fSPeter Avalos 				r = add_host_to_hostfile(user_hostfile,
81618de8d7fSPeter Avalos 				    hostline, host_key,
81718de8d7fSPeter Avalos 				    options.hash_known_hosts);
81818de8d7fSPeter Avalos 			}
81918de8d7fSPeter Avalos 		} else {
82018de8d7fSPeter Avalos 			r = add_host_to_hostfile(user_hostfile, host, host_key,
82118de8d7fSPeter Avalos 			    options.hash_known_hosts);
82218de8d7fSPeter Avalos 			hostp = host;
82318de8d7fSPeter Avalos 		}
82418de8d7fSPeter Avalos 
82518de8d7fSPeter Avalos 		if (!r)
82618de8d7fSPeter Avalos 			logit("Failed to add the host to the list of known "
82718de8d7fSPeter Avalos 			    "hosts (%.500s).", user_hostfile);
82818de8d7fSPeter Avalos 		else
82918de8d7fSPeter Avalos 			logit("Warning: Permanently added '%.200s' (%s) to the "
83018de8d7fSPeter Avalos 			    "list of known hosts.", hostp, type);
83118de8d7fSPeter Avalos 		break;
83218de8d7fSPeter Avalos 	case HOST_CHANGED:
83318de8d7fSPeter Avalos 		if (readonly == ROQUIET)
83418de8d7fSPeter Avalos 			goto fail;
83518de8d7fSPeter Avalos 		if (options.check_host_ip && host_ip_differ) {
83618de8d7fSPeter Avalos 			char *key_msg;
83718de8d7fSPeter Avalos 			if (ip_status == HOST_NEW)
83818de8d7fSPeter Avalos 				key_msg = "is unknown";
83918de8d7fSPeter Avalos 			else if (ip_status == HOST_OK)
84018de8d7fSPeter Avalos 				key_msg = "is unchanged";
84118de8d7fSPeter Avalos 			else
84218de8d7fSPeter Avalos 				key_msg = "has a different value";
84318de8d7fSPeter Avalos 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
84418de8d7fSPeter Avalos 			error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
84518de8d7fSPeter Avalos 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
84618de8d7fSPeter Avalos 			error("The %s host key for %s has changed,", type, host);
84718de8d7fSPeter Avalos 			error("and the key for the corresponding IP address %s", ip);
84818de8d7fSPeter Avalos 			error("%s. This could either mean that", key_msg);
84918de8d7fSPeter Avalos 			error("DNS SPOOFING is happening or the IP address for the host");
85018de8d7fSPeter Avalos 			error("and its host key have changed at the same time.");
85118de8d7fSPeter Avalos 			if (ip_status != HOST_NEW)
85218de8d7fSPeter Avalos 				error("Offending key for IP in %s:%d", ip_file, ip_line);
85318de8d7fSPeter Avalos 		}
85418de8d7fSPeter Avalos 		/* The host key has changed. */
85518de8d7fSPeter Avalos 		warn_changed_key(host_key);
85618de8d7fSPeter Avalos 		error("Add correct host key in %.100s to get rid of this message.",
85718de8d7fSPeter Avalos 		    user_hostfile);
85818de8d7fSPeter Avalos 		error("Offending key in %s:%d", host_file, host_line);
85918de8d7fSPeter Avalos 
86018de8d7fSPeter Avalos 		/*
86118de8d7fSPeter Avalos 		 * If strict host key checking is in use, the user will have
86218de8d7fSPeter Avalos 		 * to edit the key manually and we can only abort.
86318de8d7fSPeter Avalos 		 */
86418de8d7fSPeter Avalos 		if (options.strict_host_key_checking) {
86518de8d7fSPeter Avalos 			error("%s host key for %.200s has changed and you have "
86618de8d7fSPeter Avalos 			    "requested strict checking.", type, host);
86718de8d7fSPeter Avalos 			goto fail;
86818de8d7fSPeter Avalos 		}
86918de8d7fSPeter Avalos 
87018de8d7fSPeter Avalos 		/*
87118de8d7fSPeter Avalos 		 * If strict host key checking has not been requested, allow
87218de8d7fSPeter Avalos 		 * the connection but without MITM-able authentication or
87318de8d7fSPeter Avalos 		 * forwarding.
87418de8d7fSPeter Avalos 		 */
87518de8d7fSPeter Avalos 		if (options.password_authentication) {
87618de8d7fSPeter Avalos 			error("Password authentication is disabled to avoid "
87718de8d7fSPeter Avalos 			    "man-in-the-middle attacks.");
87818de8d7fSPeter Avalos 			options.password_authentication = 0;
87918de8d7fSPeter Avalos 			cancelled_forwarding = 1;
88018de8d7fSPeter Avalos 		}
88118de8d7fSPeter Avalos 		if (options.kbd_interactive_authentication) {
88218de8d7fSPeter Avalos 			error("Keyboard-interactive authentication is disabled"
88318de8d7fSPeter Avalos 			    " to avoid man-in-the-middle attacks.");
88418de8d7fSPeter Avalos 			options.kbd_interactive_authentication = 0;
88518de8d7fSPeter Avalos 			options.challenge_response_authentication = 0;
88618de8d7fSPeter Avalos 			cancelled_forwarding = 1;
88718de8d7fSPeter Avalos 		}
88818de8d7fSPeter Avalos 		if (options.challenge_response_authentication) {
88918de8d7fSPeter Avalos 			error("Challenge/response authentication is disabled"
89018de8d7fSPeter Avalos 			    " to avoid man-in-the-middle attacks.");
89118de8d7fSPeter Avalos 			options.challenge_response_authentication = 0;
89218de8d7fSPeter Avalos 			cancelled_forwarding = 1;
89318de8d7fSPeter Avalos 		}
89418de8d7fSPeter Avalos 		if (options.forward_agent) {
89518de8d7fSPeter Avalos 			error("Agent forwarding is disabled to avoid "
89618de8d7fSPeter Avalos 			    "man-in-the-middle attacks.");
89718de8d7fSPeter Avalos 			options.forward_agent = 0;
89818de8d7fSPeter Avalos 			cancelled_forwarding = 1;
89918de8d7fSPeter Avalos 		}
90018de8d7fSPeter Avalos 		if (options.forward_x11) {
90118de8d7fSPeter Avalos 			error("X11 forwarding is disabled to avoid "
90218de8d7fSPeter Avalos 			    "man-in-the-middle attacks.");
90318de8d7fSPeter Avalos 			options.forward_x11 = 0;
90418de8d7fSPeter Avalos 			cancelled_forwarding = 1;
90518de8d7fSPeter Avalos 		}
90618de8d7fSPeter Avalos 		if (options.num_local_forwards > 0 ||
90718de8d7fSPeter Avalos 		    options.num_remote_forwards > 0) {
90818de8d7fSPeter Avalos 			error("Port forwarding is disabled to avoid "
90918de8d7fSPeter Avalos 			    "man-in-the-middle attacks.");
91018de8d7fSPeter Avalos 			options.num_local_forwards =
91118de8d7fSPeter Avalos 			    options.num_remote_forwards = 0;
91218de8d7fSPeter Avalos 			cancelled_forwarding = 1;
91318de8d7fSPeter Avalos 		}
91418de8d7fSPeter Avalos 		if (options.tun_open != SSH_TUNMODE_NO) {
91518de8d7fSPeter Avalos 			error("Tunnel forwarding is disabled to avoid "
91618de8d7fSPeter Avalos 			    "man-in-the-middle attacks.");
91718de8d7fSPeter Avalos 			options.tun_open = SSH_TUNMODE_NO;
91818de8d7fSPeter Avalos 			cancelled_forwarding = 1;
91918de8d7fSPeter Avalos 		}
92018de8d7fSPeter Avalos 		if (options.exit_on_forward_failure && cancelled_forwarding)
92118de8d7fSPeter Avalos 			fatal("Error: forwarding disabled due to host key "
92218de8d7fSPeter Avalos 			    "check failure");
92318de8d7fSPeter Avalos 
92418de8d7fSPeter Avalos 		/*
92518de8d7fSPeter Avalos 		 * XXX Should permit the user to change to use the new id.
92618de8d7fSPeter Avalos 		 * This could be done by converting the host key to an
92718de8d7fSPeter Avalos 		 * identifying sentence, tell that the host identifies itself
92818de8d7fSPeter Avalos 		 * by that sentence, and ask the user if he/she whishes to
92918de8d7fSPeter Avalos 		 * accept the authentication.
93018de8d7fSPeter Avalos 		 */
93118de8d7fSPeter Avalos 		break;
93218de8d7fSPeter Avalos 	case HOST_FOUND:
93318de8d7fSPeter Avalos 		fatal("internal error");
93418de8d7fSPeter Avalos 		break;
93518de8d7fSPeter Avalos 	}
93618de8d7fSPeter Avalos 
93718de8d7fSPeter Avalos 	if (options.check_host_ip && host_status != HOST_CHANGED &&
93818de8d7fSPeter Avalos 	    ip_status == HOST_CHANGED) {
93918de8d7fSPeter Avalos 		snprintf(msg, sizeof(msg),
94018de8d7fSPeter Avalos 		    "Warning: the %s host key for '%.200s' "
94118de8d7fSPeter Avalos 		    "differs from the key for the IP address '%.128s'"
94218de8d7fSPeter Avalos 		    "\nOffending key for IP in %s:%d",
94318de8d7fSPeter Avalos 		    type, host, ip, ip_file, ip_line);
94418de8d7fSPeter Avalos 		if (host_status == HOST_OK) {
94518de8d7fSPeter Avalos 			len = strlen(msg);
94618de8d7fSPeter Avalos 			snprintf(msg + len, sizeof(msg) - len,
94718de8d7fSPeter Avalos 			    "\nMatching host key in %s:%d",
94818de8d7fSPeter Avalos 			    host_file, host_line);
94918de8d7fSPeter Avalos 		}
95018de8d7fSPeter Avalos 		if (options.strict_host_key_checking == 1) {
95118de8d7fSPeter Avalos 			logit("%s", msg);
95218de8d7fSPeter Avalos 			error("Exiting, you have requested strict checking.");
95318de8d7fSPeter Avalos 			goto fail;
95418de8d7fSPeter Avalos 		} else if (options.strict_host_key_checking == 2) {
95518de8d7fSPeter Avalos 			strlcat(msg, "\nAre you sure you want "
95618de8d7fSPeter Avalos 			    "to continue connecting (yes/no)? ", sizeof(msg));
95718de8d7fSPeter Avalos 			if (!confirm(msg))
95818de8d7fSPeter Avalos 				goto fail;
95918de8d7fSPeter Avalos 		} else {
96018de8d7fSPeter Avalos 			logit("%s", msg);
96118de8d7fSPeter Avalos 		}
96218de8d7fSPeter Avalos 	}
96318de8d7fSPeter Avalos 
96418de8d7fSPeter Avalos 	xfree(ip);
96518de8d7fSPeter Avalos 	xfree(host);
96618de8d7fSPeter Avalos 	return 0;
96718de8d7fSPeter Avalos 
96818de8d7fSPeter Avalos fail:
96918de8d7fSPeter Avalos 	xfree(ip);
97018de8d7fSPeter Avalos 	xfree(host);
97118de8d7fSPeter Avalos 	return -1;
97218de8d7fSPeter Avalos }
97318de8d7fSPeter Avalos 
97418de8d7fSPeter Avalos /* returns 0 if key verifies or -1 if key does NOT verify */
97518de8d7fSPeter Avalos int
97618de8d7fSPeter Avalos verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
97718de8d7fSPeter Avalos {
97818de8d7fSPeter Avalos 	struct stat st;
97918de8d7fSPeter Avalos 	int flags = 0;
98018de8d7fSPeter Avalos 
98118de8d7fSPeter Avalos 	if (options.verify_host_key_dns &&
98218de8d7fSPeter Avalos 	    verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) {
98318de8d7fSPeter Avalos 
98418de8d7fSPeter Avalos 		if (flags & DNS_VERIFY_FOUND) {
98518de8d7fSPeter Avalos 
98618de8d7fSPeter Avalos 			if (options.verify_host_key_dns == 1 &&
98718de8d7fSPeter Avalos 			    flags & DNS_VERIFY_MATCH &&
98818de8d7fSPeter Avalos 			    flags & DNS_VERIFY_SECURE)
98918de8d7fSPeter Avalos 				return 0;
99018de8d7fSPeter Avalos 
99118de8d7fSPeter Avalos 			if (flags & DNS_VERIFY_MATCH) {
99218de8d7fSPeter Avalos 				matching_host_key_dns = 1;
99318de8d7fSPeter Avalos 			} else {
99418de8d7fSPeter Avalos 				warn_changed_key(host_key);
99518de8d7fSPeter Avalos 				error("Update the SSHFP RR in DNS with the new "
99618de8d7fSPeter Avalos 				    "host key to get rid of this message.");
99718de8d7fSPeter Avalos 			}
99818de8d7fSPeter Avalos 		}
99918de8d7fSPeter Avalos 	}
100018de8d7fSPeter Avalos 
100118de8d7fSPeter Avalos 	/* return ok if the key can be found in an old keyfile */
100218de8d7fSPeter Avalos 	if (stat(options.system_hostfile2, &st) == 0 ||
100318de8d7fSPeter Avalos 	    stat(options.user_hostfile2, &st) == 0) {
100418de8d7fSPeter Avalos 		if (check_host_key(host, hostaddr, options.port, host_key,
100518de8d7fSPeter Avalos 		    RDONLY, options.user_hostfile2,
100618de8d7fSPeter Avalos 		    options.system_hostfile2) == 0)
100718de8d7fSPeter Avalos 			return 0;
100818de8d7fSPeter Avalos 	}
100918de8d7fSPeter Avalos 	return check_host_key(host, hostaddr, options.port, host_key,
101018de8d7fSPeter Avalos 	    RDRW, options.user_hostfile, options.system_hostfile);
101118de8d7fSPeter Avalos }
101218de8d7fSPeter Avalos 
101318de8d7fSPeter Avalos /*
101418de8d7fSPeter Avalos  * Starts a dialog with the server, and authenticates the current user on the
101518de8d7fSPeter Avalos  * server.  This does not need any extra privileges.  The basic connection
101618de8d7fSPeter Avalos  * to the server must already have been established before this is called.
101718de8d7fSPeter Avalos  * If login fails, this function prints an error and never returns.
101818de8d7fSPeter Avalos  * This function does not require super-user privileges.
101918de8d7fSPeter Avalos  */
102018de8d7fSPeter Avalos void
102118de8d7fSPeter Avalos ssh_login(Sensitive *sensitive, const char *orighost,
102218de8d7fSPeter Avalos     struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms)
102318de8d7fSPeter Avalos {
102418de8d7fSPeter Avalos 	char *host, *cp;
102518de8d7fSPeter Avalos 	char *server_user, *local_user;
102618de8d7fSPeter Avalos 
102718de8d7fSPeter Avalos 	local_user = xstrdup(pw->pw_name);
102818de8d7fSPeter Avalos 	server_user = options.user ? options.user : local_user;
102918de8d7fSPeter Avalos 
103018de8d7fSPeter Avalos 	/* Convert the user-supplied hostname into all lowercase. */
103118de8d7fSPeter Avalos 	host = xstrdup(orighost);
103218de8d7fSPeter Avalos 	for (cp = host; *cp; cp++)
103318de8d7fSPeter Avalos 		if (isupper(*cp))
103418de8d7fSPeter Avalos 			*cp = (char)tolower(*cp);
103518de8d7fSPeter Avalos 
103618de8d7fSPeter Avalos 	/* Exchange protocol version identification strings with the server. */
103718de8d7fSPeter Avalos 	ssh_exchange_identification(timeout_ms);
103818de8d7fSPeter Avalos 
103918de8d7fSPeter Avalos 	/* Put the connection into non-blocking mode. */
104018de8d7fSPeter Avalos 	packet_set_nonblocking();
104118de8d7fSPeter Avalos 
104218de8d7fSPeter Avalos 	/* key exchange */
104318de8d7fSPeter Avalos 	/* authenticate user */
104418de8d7fSPeter Avalos 	if (compat20) {
104518de8d7fSPeter Avalos 		ssh_kex2(host, hostaddr);
104618de8d7fSPeter Avalos 		ssh_userauth2(local_user, server_user, host, sensitive);
104718de8d7fSPeter Avalos 	} else {
104818de8d7fSPeter Avalos 		ssh_kex(host, hostaddr);
104918de8d7fSPeter Avalos 		ssh_userauth1(local_user, server_user, host, sensitive);
105018de8d7fSPeter Avalos 	}
105118de8d7fSPeter Avalos 	xfree(local_user);
105218de8d7fSPeter Avalos }
105318de8d7fSPeter Avalos 
105418de8d7fSPeter Avalos void
105518de8d7fSPeter Avalos ssh_put_password(char *password)
105618de8d7fSPeter Avalos {
105718de8d7fSPeter Avalos 	int size;
105818de8d7fSPeter Avalos 	char *padded;
105918de8d7fSPeter Avalos 
106018de8d7fSPeter Avalos 	if (datafellows & SSH_BUG_PASSWORDPAD) {
106118de8d7fSPeter Avalos 		packet_put_cstring(password);
106218de8d7fSPeter Avalos 		return;
106318de8d7fSPeter Avalos 	}
106418de8d7fSPeter Avalos 	size = roundup(strlen(password) + 1, 32);
106518de8d7fSPeter Avalos 	padded = xcalloc(1, size);
106618de8d7fSPeter Avalos 	strlcpy(padded, password, size);
106718de8d7fSPeter Avalos 	packet_put_string(padded, size);
106818de8d7fSPeter Avalos 	memset(padded, 0, size);
106918de8d7fSPeter Avalos 	xfree(padded);
107018de8d7fSPeter Avalos }
107118de8d7fSPeter Avalos 
107218de8d7fSPeter Avalos static int
107318de8d7fSPeter Avalos show_key_from_file(const char *file, const char *host, int keytype)
107418de8d7fSPeter Avalos {
107518de8d7fSPeter Avalos 	Key *found;
107618de8d7fSPeter Avalos 	char *fp, *ra;
107718de8d7fSPeter Avalos 	int line, ret;
107818de8d7fSPeter Avalos 
107918de8d7fSPeter Avalos 	found = key_new(keytype);
108018de8d7fSPeter Avalos 	if ((ret = lookup_key_in_hostfile_by_type(file, host,
108118de8d7fSPeter Avalos 	    keytype, found, &line))) {
108218de8d7fSPeter Avalos 		fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
108318de8d7fSPeter Avalos 		ra = key_fingerprint(found, SSH_FP_MD5, SSH_FP_RANDOMART);
108418de8d7fSPeter Avalos 		logit("WARNING: %s key found for host %s\n"
108518de8d7fSPeter Avalos 		    "in %s:%d\n"
108618de8d7fSPeter Avalos 		    "%s key fingerprint %s.\n%s\n",
108718de8d7fSPeter Avalos 		    key_type(found), host, file, line,
108818de8d7fSPeter Avalos 		    key_type(found), fp, ra);
108918de8d7fSPeter Avalos 		xfree(ra);
109018de8d7fSPeter Avalos 		xfree(fp);
109118de8d7fSPeter Avalos 	}
109218de8d7fSPeter Avalos 	key_free(found);
109318de8d7fSPeter Avalos 	return (ret);
109418de8d7fSPeter Avalos }
109518de8d7fSPeter Avalos 
109618de8d7fSPeter Avalos /* print all known host keys for a given host, but skip keys of given type */
109718de8d7fSPeter Avalos static int
109818de8d7fSPeter Avalos show_other_keys(const char *host, Key *key)
109918de8d7fSPeter Avalos {
110018de8d7fSPeter Avalos 	int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, -1};
110118de8d7fSPeter Avalos 	int i, found = 0;
110218de8d7fSPeter Avalos 
110318de8d7fSPeter Avalos 	for (i = 0; type[i] != -1; i++) {
110418de8d7fSPeter Avalos 		if (type[i] == key->type)
110518de8d7fSPeter Avalos 			continue;
110618de8d7fSPeter Avalos 		if (type[i] != KEY_RSA1 &&
110718de8d7fSPeter Avalos 		    show_key_from_file(options.user_hostfile2, host, type[i])) {
110818de8d7fSPeter Avalos 			found = 1;
110918de8d7fSPeter Avalos 			continue;
111018de8d7fSPeter Avalos 		}
111118de8d7fSPeter Avalos 		if (type[i] != KEY_RSA1 &&
111218de8d7fSPeter Avalos 		    show_key_from_file(options.system_hostfile2, host, type[i])) {
111318de8d7fSPeter Avalos 			found = 1;
111418de8d7fSPeter Avalos 			continue;
111518de8d7fSPeter Avalos 		}
111618de8d7fSPeter Avalos 		if (show_key_from_file(options.user_hostfile, host, type[i])) {
111718de8d7fSPeter Avalos 			found = 1;
111818de8d7fSPeter Avalos 			continue;
111918de8d7fSPeter Avalos 		}
112018de8d7fSPeter Avalos 		if (show_key_from_file(options.system_hostfile, host, type[i])) {
112118de8d7fSPeter Avalos 			found = 1;
112218de8d7fSPeter Avalos 			continue;
112318de8d7fSPeter Avalos 		}
112418de8d7fSPeter Avalos 		debug2("no key of type %d for host %s", type[i], host);
112518de8d7fSPeter Avalos 	}
112618de8d7fSPeter Avalos 	return (found);
112718de8d7fSPeter Avalos }
112818de8d7fSPeter Avalos 
112918de8d7fSPeter Avalos static void
113018de8d7fSPeter Avalos warn_changed_key(Key *host_key)
113118de8d7fSPeter Avalos {
113218de8d7fSPeter Avalos 	char *fp;
113318de8d7fSPeter Avalos 	const char *type = key_type(host_key);
113418de8d7fSPeter Avalos 
113518de8d7fSPeter Avalos 	fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
113618de8d7fSPeter Avalos 
113718de8d7fSPeter Avalos 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
113818de8d7fSPeter Avalos 	error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
113918de8d7fSPeter Avalos 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
114018de8d7fSPeter Avalos 	error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
114118de8d7fSPeter Avalos 	error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
114218de8d7fSPeter Avalos 	error("It is also possible that the %s host key has just been changed.", type);
114318de8d7fSPeter Avalos 	error("The fingerprint for the %s key sent by the remote host is\n%s.",
114418de8d7fSPeter Avalos 	    type, fp);
114518de8d7fSPeter Avalos 	error("Please contact your system administrator.");
114618de8d7fSPeter Avalos 
114718de8d7fSPeter Avalos 	xfree(fp);
114818de8d7fSPeter Avalos }
114918de8d7fSPeter Avalos 
115018de8d7fSPeter Avalos /*
115118de8d7fSPeter Avalos  * Execute a local command
115218de8d7fSPeter Avalos  */
115318de8d7fSPeter Avalos int
115418de8d7fSPeter Avalos ssh_local_cmd(const char *args)
115518de8d7fSPeter Avalos {
115618de8d7fSPeter Avalos 	char *shell;
115718de8d7fSPeter Avalos 	pid_t pid;
115818de8d7fSPeter Avalos 	int status;
115918de8d7fSPeter Avalos 
116018de8d7fSPeter Avalos 	if (!options.permit_local_command ||
116118de8d7fSPeter Avalos 	    args == NULL || !*args)
116218de8d7fSPeter Avalos 		return (1);
116318de8d7fSPeter Avalos 
116418de8d7fSPeter Avalos 	if ((shell = getenv("SHELL")) == NULL)
116518de8d7fSPeter Avalos 		shell = _PATH_BSHELL;
116618de8d7fSPeter Avalos 
116718de8d7fSPeter Avalos 	pid = fork();
116818de8d7fSPeter Avalos 	if (pid == 0) {
116918de8d7fSPeter Avalos 		debug3("Executing %s -c \"%s\"", shell, args);
117018de8d7fSPeter Avalos 		execl(shell, shell, "-c", args, (char *)NULL);
117118de8d7fSPeter Avalos 		error("Couldn't execute %s -c \"%s\": %s",
117218de8d7fSPeter Avalos 		    shell, args, strerror(errno));
117318de8d7fSPeter Avalos 		_exit(1);
117418de8d7fSPeter Avalos 	} else if (pid == -1)
117518de8d7fSPeter Avalos 		fatal("fork failed: %.100s", strerror(errno));
117618de8d7fSPeter Avalos 	while (waitpid(pid, &status, 0) == -1)
117718de8d7fSPeter Avalos 		if (errno != EINTR)
117818de8d7fSPeter Avalos 			fatal("Couldn't wait for child: %s", strerror(errno));
117918de8d7fSPeter Avalos 
118018de8d7fSPeter Avalos 	if (!WIFEXITED(status))
118118de8d7fSPeter Avalos 		return (1);
118218de8d7fSPeter Avalos 
118318de8d7fSPeter Avalos 	return (WEXITSTATUS(status));
118418de8d7fSPeter Avalos }
1185