xref: /openssh-portable/sshconnect.c (revision 31d8d231)
1*31d8d231Sdjm@openbsd.org /* $OpenBSD: sshconnect.c,v 1.352 2021/04/03 06:18:41 djm Exp $ */
2d4a8b7e3SDamien Miller /*
395def098SDamien Miller  * Author: Tatu Ylonen <ylo@cs.hut.fi>
495def098SDamien Miller  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
595def098SDamien Miller  *                    All rights reserved
695def098SDamien Miller  * Code to connect to a remote host, and to perform the client side of the
795def098SDamien Miller  * login (authentication) dialog.
8e4340be5SDamien Miller  *
9e4340be5SDamien Miller  * As far as I am concerned, the code I have written for this software
10e4340be5SDamien Miller  * can be used freely for any purpose.  Any derived versions of this
11e4340be5SDamien Miller  * software must be clearly marked as such, and if the derived work is
12e4340be5SDamien Miller  * incompatible with the protocol description in the RFC file, it must be
13e4340be5SDamien Miller  * called by a name other than "ssh" or "Secure Shell".
14d4a8b7e3SDamien Miller  */
15d4a8b7e3SDamien Miller 
16d4a8b7e3SDamien Miller #include "includes.h"
179cf6d077SDamien Miller 
189cf6d077SDamien Miller #include <sys/types.h>
199cf6d077SDamien Miller #include <sys/wait.h>
20f17883e6SDamien Miller #include <sys/stat.h>
218ec8c3e9SDamien Miller #include <sys/socket.h>
229aec9194SDamien Miller #ifdef HAVE_SYS_TIME_H
239aec9194SDamien Miller # include <sys/time.h>
249aec9194SDamien Miller #endif
258ec8c3e9SDamien Miller 
26ac2e3026Sdjm@openbsd.org #include <net/if.h>
278ec8c3e9SDamien Miller #include <netinet/in.h>
2846aa3e0cSDarren Tucker #include <arpa/inet.h>
2903e2003aSDamien Miller 
30c7b06369SDamien Miller #include <ctype.h>
3139972493SDarren Tucker #include <errno.h>
326e7fe1c0SDarren Tucker #include <fcntl.h>
3333313ebcSdjm@openbsd.org #include <limits.h>
34be43ebf9SDamien Miller #include <netdb.h>
356645e7a7SDamien Miller #ifdef HAVE_PATHS_H
3603e2003aSDamien Miller #include <paths.h>
376645e7a7SDamien Miller #endif
389f2abc47SDamien Miller #include <pwd.h>
394540428cSdjm@openbsd.org #ifdef HAVE_POLL_H
404540428cSdjm@openbsd.org #include <poll.h>
414540428cSdjm@openbsd.org #endif
429c0c31d2SDamien Miller #include <signal.h>
43a7a73ee3SDamien Miller #include <stdio.h>
44e7a1e5cfSDamien Miller #include <stdlib.h>
4572687c8eSderaadt@openbsd.org #include <stdarg.h>
46e3476ed0SDamien Miller #include <string.h>
47e6b3b610SDamien Miller #include <unistd.h>
48b59162daSDarren Tucker #ifdef HAVE_IFADDRS_H
49ac2e3026Sdjm@openbsd.org # include <ifaddrs.h>
50b59162daSDarren Tucker #endif
51d4a8b7e3SDamien Miller 
52d4a8b7e3SDamien Miller #include "xmalloc.h"
53d7834353SDamien Miller #include "hostfile.h"
54d7834353SDamien Miller #include "ssh.h"
55cecee2d6Smarkus@openbsd.org #include "sshbuf.h"
56d4a8b7e3SDamien Miller #include "packet.h"
57d4a8b7e3SDamien Miller #include "compat.h"
585467fbcbSmarkus@openbsd.org #include "sshkey.h"
59eba71babSDamien Miller #include "sshconnect.h"
60226cfa03SBen Lindstrom #include "log.h"
617acefbbcSDamien Miller #include "misc.h"
62226cfa03SBen Lindstrom #include "readconf.h"
63226cfa03SBen Lindstrom #include "atomicio.h"
6437876e91SDamien Miller #include "dns.h"
651262b663SDamien Miller #include "monitor_fdpass.h"
660a80ca19SDamien Miller #include "ssh2.h"
67b757677dSDamien Miller #include "version.h"
685e39a499Sdjm@openbsd.org #include "authfile.h"
695e39a499Sdjm@openbsd.org #include "ssherr.h"
70f361df47Sjcs@openbsd.org #include "authfd.h"
710a843d9aSdjm@openbsd.org #include "kex.h"
7237876e91SDamien Miller 
7354d90aceSmarkus@openbsd.org struct sshkey *previous_host_key = NULL;
741383bd8eSDamien Miller 
75c1af1d5fSDamien Miller static int matching_host_key_dns = 0;
767392ae62SDamien Miller 
77a41ccca6SDamien Miller static pid_t proxy_command_pid = 0;
78a41ccca6SDamien Miller 
79f9c4884cSBen Lindstrom /* import */
80ccef7c4fSdjm@openbsd.org extern int debug_flag;
81aae6c614SDamien Miller extern Options options;
8234132e54SDamien Miller extern char *__progname;
83aae6c614SDamien Miller 
8454d90aceSmarkus@openbsd.org static int show_other_keys(struct hostkeys *, struct sshkey *);
8554d90aceSmarkus@openbsd.org static void warn_changed_key(struct sshkey *);
863ed66405SBen Lindstrom 
871262b663SDamien Miller /* Expand a proxy command */
881262b663SDamien Miller static char *
expand_proxy_command(const char * proxy_command,const char * user,const char * host,const char * host_arg,int port)891262b663SDamien Miller expand_proxy_command(const char *proxy_command, const char *user,
90b8a4ca2eSnaddy@openbsd.org     const char *host, const char *host_arg, int port)
911262b663SDamien Miller {
921262b663SDamien Miller 	char *tmp, *ret, strport[NI_MAXSERV];
938df5774aSdtucker@openbsd.org 	const char *keyalias = options.host_key_alias ?
948df5774aSdtucker@openbsd.org 	    options.host_key_alias : host_arg;
951262b663SDamien Miller 
960faf747eSDamien Miller 	snprintf(strport, sizeof strport, "%d", port);
971262b663SDamien Miller 	xasprintf(&tmp, "exec %s", proxy_command);
98fbe24b14Sdjm@openbsd.org 	ret = percent_expand(tmp,
99fbe24b14Sdjm@openbsd.org 	    "h", host,
1008df5774aSdtucker@openbsd.org 	    "k", keyalias,
101fbe24b14Sdjm@openbsd.org 	    "n", host_arg,
102fbe24b14Sdjm@openbsd.org 	    "p", strport,
103fbe24b14Sdjm@openbsd.org 	    "r", options.user,
104fbe24b14Sdjm@openbsd.org 	    (char *)NULL);
1051262b663SDamien Miller 	free(tmp);
1061262b663SDamien Miller 	return ret;
1071262b663SDamien Miller }
1081262b663SDamien Miller 
1091262b663SDamien Miller /*
1101262b663SDamien Miller  * Connect to the given ssh server using a proxy command that passes a
1111262b663SDamien Miller  * a connected fd back to us.
1121262b663SDamien Miller  */
1131262b663SDamien Miller static int
ssh_proxy_fdpass_connect(struct ssh * ssh,const char * host,const char * host_arg,u_short port,const char * proxy_command)114fbe24b14Sdjm@openbsd.org ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host,
115fbe24b14Sdjm@openbsd.org     const char *host_arg, u_short port, const char *proxy_command)
1161262b663SDamien Miller {
1171262b663SDamien Miller 	char *command_string;
1181262b663SDamien Miller 	int sp[2], sock;
1191262b663SDamien Miller 	pid_t pid;
1201262b663SDamien Miller 	char *shell;
1211262b663SDamien Miller 
1221262b663SDamien Miller 	if ((shell = getenv("SHELL")) == NULL)
1231262b663SDamien Miller 		shell = _PATH_BSHELL;
1241262b663SDamien Miller 
1254d28fa78Sderaadt@openbsd.org 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1)
1261262b663SDamien Miller 		fatal("Could not create socketpair to communicate with "
1271262b663SDamien Miller 		    "proxy dialer: %.100s", strerror(errno));
1281262b663SDamien Miller 
1291262b663SDamien Miller 	command_string = expand_proxy_command(proxy_command, options.user,
130b8a4ca2eSnaddy@openbsd.org 	    host, host_arg, port);
1311262b663SDamien Miller 	debug("Executing proxy dialer command: %.500s", command_string);
1321262b663SDamien Miller 
1331262b663SDamien Miller 	/* Fork and execute the proxy command. */
1341262b663SDamien Miller 	if ((pid = fork()) == 0) {
1351262b663SDamien Miller 		char *argv[10];
1361262b663SDamien Miller 
1371262b663SDamien Miller 		close(sp[1]);
1381262b663SDamien Miller 		/* Redirect stdin and stdout. */
1391262b663SDamien Miller 		if (sp[0] != 0) {
1404d28fa78Sderaadt@openbsd.org 			if (dup2(sp[0], 0) == -1)
1411262b663SDamien Miller 				perror("dup2 stdin");
1421262b663SDamien Miller 		}
1431262b663SDamien Miller 		if (sp[0] != 1) {
1444d28fa78Sderaadt@openbsd.org 			if (dup2(sp[0], 1) == -1)
1451262b663SDamien Miller 				perror("dup2 stdout");
1461262b663SDamien Miller 		}
1471262b663SDamien Miller 		if (sp[0] >= 2)
1481262b663SDamien Miller 			close(sp[0]);
1491262b663SDamien Miller 
1501262b663SDamien Miller 		/*
151ccef7c4fSdjm@openbsd.org 		 * Stderr is left for non-ControlPersist connections is so
152ccef7c4fSdjm@openbsd.org 		 * error messages may be printed on the user's terminal.
1531262b663SDamien Miller 		 */
1547fca94edSdtucker@openbsd.org 		if (!debug_flag && options.control_path != NULL &&
155396d32f3Sdjm@openbsd.org 		    options.control_persist && stdfd_devnull(0, 0, 1) == -1)
156816036f1Sdjm@openbsd.org 			error_f("stdfd_devnull failed");
1581262b663SDamien Miller 		argv[0] = shell;
1591262b663SDamien Miller 		argv[1] = "-c";
1601262b663SDamien Miller 		argv[2] = command_string;
1611262b663SDamien Miller 		argv[3] = NULL;
1621262b663SDamien Miller 
1631262b663SDamien Miller 		/*
1641262b663SDamien Miller 		 * Execute the proxy command.
1651262b663SDamien Miller 		 * Note that we gave up any extra privileges above.
1661262b663SDamien Miller 		 */
1671262b663SDamien Miller 		execv(argv[0], argv);
1681262b663SDamien Miller 		perror(argv[0]);
1691262b663SDamien Miller 		exit(1);
1701262b663SDamien Miller 	}
1711262b663SDamien Miller 	/* Parent. */
1724d28fa78Sderaadt@openbsd.org 	if (pid == -1)
1731262b663SDamien Miller 		fatal("fork failed: %.100s", strerror(errno));
1741262b663SDamien Miller 	close(sp[0]);
1751262b663SDamien Miller 	free(command_string);
1761262b663SDamien Miller 
1771262b663SDamien Miller 	if ((sock = mm_receive_fd(sp[1])) == -1)
1781262b663SDamien Miller 		fatal("proxy dialer did not pass back a connection");
179fc77ccdcSmarkus@openbsd.org 	close(sp[1]);
1801262b663SDamien Miller 
1811262b663SDamien Miller 	while (waitpid(pid, NULL, 0) == -1)
1821262b663SDamien Miller 		if (errno != EINTR)
1831262b663SDamien Miller 			fatal("Couldn't wait for child: %s", strerror(errno));
1841262b663SDamien Miller 
1851262b663SDamien Miller 	/* Set the connection file descriptors. */
186dbee4119Sdjm@openbsd.org 	if (ssh_packet_set_connection(ssh, sock, sock) == NULL)
187dbee4119Sdjm@openbsd.org 		return -1; /* ssh_packet_set_connection logs error */
1881262b663SDamien Miller 
1891262b663SDamien Miller 	return 0;
1901262b663SDamien Miller }
1911262b663SDamien Miller 
19295def098SDamien Miller /*
19395def098SDamien Miller  * Connect to the given ssh server using a proxy command.
19495def098SDamien Miller  */
195bba81213SBen Lindstrom static int
ssh_proxy_connect(struct ssh * ssh,const char * host,const char * host_arg,u_short port,const char * proxy_command)196fbe24b14Sdjm@openbsd.org ssh_proxy_connect(struct ssh *ssh, const char *host, const char *host_arg,
197fbe24b14Sdjm@openbsd.org     u_short port, const char *proxy_command)
198d4a8b7e3SDamien Miller {
1991262b663SDamien Miller 	char *command_string;
200d4a8b7e3SDamien Miller 	int pin[2], pout[2];
201166fca88SDamien Miller 	pid_t pid;
2021262b663SDamien Miller 	char *shell;
2031d824ab2SDamien Miller 
20438d9a965SDamien Miller 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
2051d824ab2SDamien Miller 		shell = _PATH_BSHELL;
206d4a8b7e3SDamien Miller 
207d4a8b7e3SDamien Miller 	/* Create pipes for communicating with the proxy. */
2084d28fa78Sderaadt@openbsd.org 	if (pipe(pin) == -1 || pipe(pout) == -1)
209d4a8b7e3SDamien Miller 		fatal("Could not create pipes to communicate with the proxy: %.100s",
210d4a8b7e3SDamien Miller 		    strerror(errno));
211d4a8b7e3SDamien Miller 
2121262b663SDamien Miller 	command_string = expand_proxy_command(proxy_command, options.user,
213b8a4ca2eSnaddy@openbsd.org 	    host, host_arg, port);
214d4a8b7e3SDamien Miller 	debug("Executing proxy command: %.500s", command_string);
215d4a8b7e3SDamien Miller 
216d4a8b7e3SDamien Miller 	/* Fork and execute the proxy command. */
21795def098SDamien Miller 	if ((pid = fork()) == 0) {
218d4a8b7e3SDamien Miller 		char *argv[10];
219d4a8b7e3SDamien Miller 
220d4a8b7e3SDamien Miller 		/* Redirect stdin and stdout. */
221d4a8b7e3SDamien Miller 		close(pin[1]);
22295def098SDamien Miller 		if (pin[0] != 0) {
2234d28fa78Sderaadt@openbsd.org 			if (dup2(pin[0], 0) == -1)
224d4a8b7e3SDamien Miller 				perror("dup2 stdin");
225d4a8b7e3SDamien Miller 			close(pin[0]);
226d4a8b7e3SDamien Miller 		}
227d4a8b7e3SDamien Miller 		close(pout[0]);
2284d28fa78Sderaadt@openbsd.org 		if (dup2(pout[1], 1) == -1)
229d4a8b7e3SDamien Miller 			perror("dup2 stdout");
23095def098SDamien Miller 		/* Cannot be 1 because pin allocated two descriptors. */
23195def098SDamien Miller 		close(pout[1]);
232d4a8b7e3SDamien Miller 
233ccef7c4fSdjm@openbsd.org 		/*
234ccef7c4fSdjm@openbsd.org 		 * Stderr is left for non-ControlPersist connections is so
235ccef7c4fSdjm@openbsd.org 		 * error messages may be printed on the user's terminal.
236ccef7c4fSdjm@openbsd.org 		 */
2377fca94edSdtucker@openbsd.org 		if (!debug_flag && options.control_path != NULL &&
238396d32f3Sdjm@openbsd.org 		    options.control_persist && stdfd_devnull(0, 0, 1) == -1)
239816036f1Sdjm@openbsd.org 			error_f("stdfd_devnull failed");
2411d824ab2SDamien Miller 		argv[0] = shell;
242d4a8b7e3SDamien Miller 		argv[1] = "-c";
243d4a8b7e3SDamien Miller 		argv[2] = command_string;
244d4a8b7e3SDamien Miller 		argv[3] = NULL;
245d4a8b7e3SDamien Miller 
246*31d8d231Sdjm@openbsd.org 		/*
247*31d8d231Sdjm@openbsd.org 		 * Execute the proxy command.  Note that we gave up any
248*31d8d231Sdjm@openbsd.org 		 * extra privileges above.
249*31d8d231Sdjm@openbsd.org 		 */
2503bf2a6acSdtucker@openbsd.org 		ssh_signal(SIGPIPE, SIG_DFL);
251fcd5d60aSKevin Steves 		execv(argv[0], argv);
252fcd5d60aSKevin Steves 		perror(argv[0]);
253d4a8b7e3SDamien Miller 		exit(1);
254d4a8b7e3SDamien Miller 	}
255d4a8b7e3SDamien Miller 	/* Parent. */
2564d28fa78Sderaadt@openbsd.org 	if (pid == -1)
257d4a8b7e3SDamien Miller 		fatal("fork failed: %.100s", strerror(errno));
2588c4e18a6SDamien Miller 	else
2598c4e18a6SDamien Miller 		proxy_command_pid = pid; /* save pid to clean up later */
260d4a8b7e3SDamien Miller 
261d4a8b7e3SDamien Miller 	/* Close child side of the descriptors. */
262d4a8b7e3SDamien Miller 	close(pin[0]);
263d4a8b7e3SDamien Miller 	close(pout[1]);
264d4a8b7e3SDamien Miller 
265d4a8b7e3SDamien Miller 	/* Free the command name. */
266a627d42eSDarren Tucker 	free(command_string);
267d4a8b7e3SDamien Miller 
268d4a8b7e3SDamien Miller 	/* Set the connection file descriptors. */
269dbee4119Sdjm@openbsd.org 	if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL)
270dbee4119Sdjm@openbsd.org 		return -1; /* ssh_packet_set_connection logs error */
271d4a8b7e3SDamien Miller 
272f9cedb9cSBen Lindstrom 	return 0;
273d4a8b7e3SDamien Miller }
274d4a8b7e3SDamien Miller 
275a41ccca6SDamien Miller void
ssh_kill_proxy_command(void)276a41ccca6SDamien Miller ssh_kill_proxy_command(void)
277a41ccca6SDamien Miller {
278a41ccca6SDamien Miller 	/*
279a41ccca6SDamien Miller 	 * Send SIGHUP to proxy command if used. We don't wait() in
280a41ccca6SDamien Miller 	 * case it hangs and instead rely on init to reap the child
281a41ccca6SDamien Miller 	 */
282a41ccca6SDamien Miller 	if (proxy_command_pid > 1)
28345fcdaa1SDamien Miller 		kill(proxy_command_pid, SIGHUP);
284a41ccca6SDamien Miller }
285a41ccca6SDamien Miller 
286b59162daSDarren Tucker #ifdef HAVE_IFADDRS_H
28795def098SDamien Miller /*
288ac2e3026Sdjm@openbsd.org  * Search a interface address list (returned from getifaddrs(3)) for an
289001aa554Sdjm@openbsd.org  * address that matches the desired address family on the specified interface.
290ac2e3026Sdjm@openbsd.org  * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure.
291ac2e3026Sdjm@openbsd.org  */
292ac2e3026Sdjm@openbsd.org static int
check_ifaddrs(const char * ifname,int af,const struct ifaddrs * ifaddrs,struct sockaddr_storage * resultp,socklen_t * rlenp)293ac2e3026Sdjm@openbsd.org check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs,
294ac2e3026Sdjm@openbsd.org     struct sockaddr_storage *resultp, socklen_t *rlenp)
295ac2e3026Sdjm@openbsd.org {
296ac2e3026Sdjm@openbsd.org 	struct sockaddr_in6 *sa6;
297ac2e3026Sdjm@openbsd.org 	struct sockaddr_in *sa;
298ac2e3026Sdjm@openbsd.org 	struct in6_addr *v6addr;
299ac2e3026Sdjm@openbsd.org 	const struct ifaddrs *ifa;
300ac2e3026Sdjm@openbsd.org 	int allow_local;
302ac2e3026Sdjm@openbsd.org 	/*
303ac2e3026Sdjm@openbsd.org 	 * Prefer addresses that are not loopback or linklocal, but use them
304ac2e3026Sdjm@openbsd.org 	 * if nothing else matches.
305ac2e3026Sdjm@openbsd.org 	 */
306ac2e3026Sdjm@openbsd.org 	for (allow_local = 0; allow_local < 2; allow_local++) {
307ac2e3026Sdjm@openbsd.org 		for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
308ac2e3026Sdjm@openbsd.org 			if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL ||
309ac2e3026Sdjm@openbsd.org 			    (ifa->ifa_flags & IFF_UP) == 0 ||
310ac2e3026Sdjm@openbsd.org 			    ifa->ifa_addr->sa_family != af ||
311ac2e3026Sdjm@openbsd.org 			    strcmp(ifa->ifa_name, options.bind_interface) != 0)
312ac2e3026Sdjm@openbsd.org 				continue;
313ac2e3026Sdjm@openbsd.org 			switch (ifa->ifa_addr->sa_family) {
314ac2e3026Sdjm@openbsd.org 			case AF_INET:
315ac2e3026Sdjm@openbsd.org 				sa = (struct sockaddr_in *)ifa->ifa_addr;
316ac2e3026Sdjm@openbsd.org 				if (!allow_local && sa->sin_addr.s_addr ==
317ac2e3026Sdjm@openbsd.org 				    htonl(INADDR_LOOPBACK))
318ac2e3026Sdjm@openbsd.org 					continue;
319ac2e3026Sdjm@openbsd.org 				if (*rlenp < sizeof(struct sockaddr_in)) {
320816036f1Sdjm@openbsd.org 					error_f("v4 addr doesn't fit");
321ac2e3026Sdjm@openbsd.org 					return -1;
322ac2e3026Sdjm@openbsd.org 				}
323ac2e3026Sdjm@openbsd.org 				*rlenp = sizeof(struct sockaddr_in);
324ac2e3026Sdjm@openbsd.org 				memcpy(resultp, sa, *rlenp);
325ac2e3026Sdjm@openbsd.org 				return 0;
326ac2e3026Sdjm@openbsd.org 			case AF_INET6:
327ac2e3026Sdjm@openbsd.org 				sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
328ac2e3026Sdjm@openbsd.org 				v6addr = &sa6->sin6_addr;
329ac2e3026Sdjm@openbsd.org 				if (!allow_local &&
330ac2e3026Sdjm@openbsd.org 				    (IN6_IS_ADDR_LINKLOCAL(v6addr) ||
331ac2e3026Sdjm@openbsd.org 				    IN6_IS_ADDR_LOOPBACK(v6addr)))
332ac2e3026Sdjm@openbsd.org 					continue;
333ac2e3026Sdjm@openbsd.org 				if (*rlenp < sizeof(struct sockaddr_in6)) {
334816036f1Sdjm@openbsd.org 					error_f("v6 addr doesn't fit");
335ac2e3026Sdjm@openbsd.org 					return -1;
336ac2e3026Sdjm@openbsd.org 				}
337ac2e3026Sdjm@openbsd.org 				*rlenp = sizeof(struct sockaddr_in6);
338ac2e3026Sdjm@openbsd.org 				memcpy(resultp, sa6, *rlenp);
339ac2e3026Sdjm@openbsd.org 				return 0;
340ac2e3026Sdjm@openbsd.org 			}
341ac2e3026Sdjm@openbsd.org 		}
342ac2e3026Sdjm@openbsd.org 	}
343ac2e3026Sdjm@openbsd.org 	return -1;
344ac2e3026Sdjm@openbsd.org }
345b59162daSDarren Tucker #endif
347ac2e3026Sdjm@openbsd.org /*
3483ba6e688Sdtucker@openbsd.org  * Creates a socket for use as the ssh connection.
34995def098SDamien Miller  */
350bba81213SBen Lindstrom static int
ssh_create_socket(struct addrinfo * ai)35195d41e90Sdtucker@openbsd.org ssh_create_socket(struct addrinfo *ai)
352d4a8b7e3SDamien Miller {
353258dc8bbSdtucker@openbsd.org 	int sock, r;
354ac2e3026Sdjm@openbsd.org 	struct sockaddr_storage bindaddr;
355ac2e3026Sdjm@openbsd.org 	socklen_t bindaddrlen = 0;
356d1a7a9c0SDamien Miller 	struct addrinfo hints, *res = NULL;
357b59162daSDarren Tucker #ifdef HAVE_IFADDRS_H
358ac2e3026Sdjm@openbsd.org 	struct ifaddrs *ifaddrs = NULL;
359b59162daSDarren Tucker #endif
360ac2e3026Sdjm@openbsd.org 	char ntop[NI_MAXHOST];
361d4a8b7e3SDamien Miller 
3627bd98e7fSDarren Tucker 	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
3634d28fa78Sderaadt@openbsd.org 	if (sock == -1) {
364e6e52f8cSDarren Tucker 		error("socket: %s", strerror(errno));
3656e7fe1c0SDarren Tucker 		return -1;
3666e7fe1c0SDarren Tucker 	}
3676e7fe1c0SDarren Tucker 	fcntl(sock, F_SETFD, FD_CLOEXEC);
368e0f88041SBen Lindstrom 
36933313ebcSdjm@openbsd.org 	/* Use interactive QOS (if specified) until authentication completed */
37033313ebcSdjm@openbsd.org 	if (options.ip_qos_interactive != INT_MAX)
37133313ebcSdjm@openbsd.org 		set_sock_tos(sock, options.ip_qos_interactive);
373e0f88041SBen Lindstrom 	/* Bind the socket to an alternative local IP address */
37495d41e90Sdtucker@openbsd.org 	if (options.bind_address == NULL && options.bind_interface == NULL)
375e0f88041SBen Lindstrom 		return sock;
376e0f88041SBen Lindstrom 
377ac2e3026Sdjm@openbsd.org 	if (options.bind_address != NULL) {
378e0f88041SBen Lindstrom 		memset(&hints, 0, sizeof(hints));
3792372ace5SDamien Miller 		hints.ai_family = ai->ai_family;
3802372ace5SDamien Miller 		hints.ai_socktype = ai->ai_socktype;
3812372ace5SDamien Miller 		hints.ai_protocol = ai->ai_protocol;
382e0f88041SBen Lindstrom 		hints.ai_flags = AI_PASSIVE;
383ac2e3026Sdjm@openbsd.org 		if ((r = getaddrinfo(options.bind_address, NULL,
384ac2e3026Sdjm@openbsd.org 		    &hints, &res)) != 0) {
385e0f88041SBen Lindstrom 			error("getaddrinfo: %s: %s", options.bind_address,
386ac2e3026Sdjm@openbsd.org 			    ssh_gai_strerror(r));
387ac2e3026Sdjm@openbsd.org 			goto fail;
388d4a8b7e3SDamien Miller 		}
3893e19fb97Sdtucker@openbsd.org 		if (res == NULL) {
390ac2e3026Sdjm@openbsd.org 			error("getaddrinfo: no addrs");
391ac2e3026Sdjm@openbsd.org 			goto fail;
3923e19fb97Sdtucker@openbsd.org 		}
393ac2e3026Sdjm@openbsd.org 		memcpy(&bindaddr, res->ai_addr, res->ai_addrlen);
394ac2e3026Sdjm@openbsd.org 		bindaddrlen = res->ai_addrlen;
395ac2e3026Sdjm@openbsd.org 	} else if (options.bind_interface != NULL) {
396b59162daSDarren Tucker #ifdef HAVE_IFADDRS_H
397ac2e3026Sdjm@openbsd.org 		if ((r = getifaddrs(&ifaddrs)) != 0) {
398ac2e3026Sdjm@openbsd.org 			error("getifaddrs: %s: %s", options.bind_interface,
399ac2e3026Sdjm@openbsd.org 			    strerror(errno));
400ac2e3026Sdjm@openbsd.org 			goto fail;
401ac2e3026Sdjm@openbsd.org 		}
402ac2e3026Sdjm@openbsd.org 		bindaddrlen = sizeof(bindaddr);
403ac2e3026Sdjm@openbsd.org 		if (check_ifaddrs(options.bind_interface, ai->ai_family,
404ac2e3026Sdjm@openbsd.org 		    ifaddrs, &bindaddr, &bindaddrlen) != 0) {
405ac2e3026Sdjm@openbsd.org 			logit("getifaddrs: %s: no suitable addresses",
406ac2e3026Sdjm@openbsd.org 			    options.bind_interface);
407ac2e3026Sdjm@openbsd.org 			goto fail;
408ac2e3026Sdjm@openbsd.org 		}
409b59162daSDarren Tucker #else
410b59162daSDarren Tucker 		error("BindInterface not supported on this platform.");
411b59162daSDarren Tucker #endif
412ac2e3026Sdjm@openbsd.org 	}
413ac2e3026Sdjm@openbsd.org 	if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen,
414ac2e3026Sdjm@openbsd.org 	    ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) {
415816036f1Sdjm@openbsd.org 		error_f("getnameinfo failed: %s", ssh_gai_strerror(r));
416ac2e3026Sdjm@openbsd.org 		goto fail;
417d1a7a9c0SDamien Miller 	}
418258dc8bbSdtucker@openbsd.org 	if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) {
419ac2e3026Sdjm@openbsd.org 		error("bind %s: %s", ntop, strerror(errno));
420ac2e3026Sdjm@openbsd.org 		goto fail;
421ac2e3026Sdjm@openbsd.org 	}
422816036f1Sdjm@openbsd.org 	debug_f("bound to %s", ntop);
423ac2e3026Sdjm@openbsd.org 	/* success */
424ac2e3026Sdjm@openbsd.org 	goto out;
425e6e52f8cSDarren Tucker fail:
426e0f88041SBen Lindstrom 	close(sock);
427ac2e3026Sdjm@openbsd.org 	sock = -1;
428ac2e3026Sdjm@openbsd.org  out:
429d1a7a9c0SDamien Miller 	if (res != NULL)
430e0f88041SBen Lindstrom 		freeaddrinfo(res);
431b59162daSDarren Tucker #ifdef HAVE_IFADDRS_H
432ac2e3026Sdjm@openbsd.org 	if (ifaddrs != NULL)
433ac2e3026Sdjm@openbsd.org 		freeifaddrs(ifaddrs);
434b59162daSDarren Tucker #endif
435d4a8b7e3SDamien Miller 	return sock;
436d4a8b7e3SDamien Miller }
437d4a8b7e3SDamien Miller 
4384540428cSdjm@openbsd.org /*
43934132e54SDamien Miller  * Opens a TCP/IP connection to the remote server on the given host.
44034132e54SDamien Miller  * The address of the remote host will be returned in hostaddr.
44195d41e90Sdtucker@openbsd.org  * If port is 0, the default port will be used.
44295def098SDamien Miller  * Connection_attempts specifies the maximum number of tries (one per
44395def098SDamien Miller  * second).  If proxy_command is non-NULL, it specifies the command (with %h
44495def098SDamien Miller  * and %p substituted for host and port, respectively) to use to contact
44595def098SDamien Miller  * the daemon.
44695def098SDamien Miller  */
4470faf747eSDamien Miller static int
ssh_connect_direct(struct ssh * ssh,const char * host,struct addrinfo * aitop,struct sockaddr_storage * hostaddr,u_short port,int connection_attempts,int * timeout_ms,int want_keepalive)448dbee4119Sdjm@openbsd.org ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop,
449a336ce8cSkn@openbsd.org     struct sockaddr_storage *hostaddr, u_short port, int connection_attempts,
450a336ce8cSkn@openbsd.org     int *timeout_ms, int want_keepalive)
451d4a8b7e3SDamien Miller {
452483b3b63Sdtucker@openbsd.org 	int on = 1, saved_timeout_ms = *timeout_ms;
4537c77991fSdjm@openbsd.org 	int oerrno, sock = -1, attempt;
454bf555ba6SBen Lindstrom 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
4550faf747eSDamien Miller 	struct addrinfo *ai;
456d4a8b7e3SDamien Miller 
457816036f1Sdjm@openbsd.org 	debug3_f("entering");
45888b6fcdeSdjm@openbsd.org 	memset(ntop, 0, sizeof(ntop));
45988b6fcdeSdjm@openbsd.org 	memset(strport, 0, sizeof(strport));
460d4a8b7e3SDamien Miller 
46156e5e6adSDamien Miller 	for (attempt = 0; attempt < connection_attempts; attempt++) {
462f4bcd10cSDamien Miller 		if (attempt > 0) {
463f4bcd10cSDamien Miller 			/* Sleep a moment before retrying. */
464f4bcd10cSDamien Miller 			sleep(1);
465d4a8b7e3SDamien Miller 			debug("Trying again...");
466f4bcd10cSDamien Miller 		}
46756e5e6adSDamien Miller 		/*
46856e5e6adSDamien Miller 		 * Loop through addresses for this host, and try each one in
46956e5e6adSDamien Miller 		 * sequence until the connection succeeds.
47056e5e6adSDamien Miller 		 */
47134132e54SDamien Miller 		for (ai = aitop; ai; ai = ai->ai_next) {
4720faf747eSDamien Miller 			if (ai->ai_family != AF_INET &&
4737c77991fSdjm@openbsd.org 			    ai->ai_family != AF_INET6) {
4747c77991fSdjm@openbsd.org 				errno = EAFNOSUPPORT;
47534132e54SDamien Miller 				continue;
4767c77991fSdjm@openbsd.org 			}
47734132e54SDamien Miller 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
47834132e54SDamien Miller 			    ntop, sizeof(ntop), strport, sizeof(strport),
47934132e54SDamien Miller 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
4807c77991fSdjm@openbsd.org 				oerrno = errno;
481816036f1Sdjm@openbsd.org 				error_f("getnameinfo failed");
4827c77991fSdjm@openbsd.org 				errno = oerrno;
48334132e54SDamien Miller 				continue;
484d4a8b7e3SDamien Miller 			}
48534132e54SDamien Miller 			debug("Connecting to %.200s [%.100s] port %s.",
48634132e54SDamien Miller 				host, ntop, strport);
487d4a8b7e3SDamien Miller 
488d4a8b7e3SDamien Miller 			/* Create a socket for connecting. */
48995d41e90Sdtucker@openbsd.org 			sock = ssh_create_socket(ai);
490a60c5dcfSstsp@openbsd.org 			if (sock < 0) {
491f9cedb9cSBen Lindstrom 				/* Any error is already output */
4927c77991fSdjm@openbsd.org 				errno = 0;
49334132e54SDamien Miller 				continue;
494a60c5dcfSstsp@openbsd.org 			}
495d4a8b7e3SDamien Miller 
496483b3b63Sdtucker@openbsd.org 			*timeout_ms = saved_timeout_ms;
497b78d5eb6SDamien Miller 			if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
49867bd062bSDamien Miller 			    timeout_ms) >= 0) {
499d4a8b7e3SDamien Miller 				/* Successful connection. */
50095fe91bdSDamien Miller 				memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
501d4a8b7e3SDamien Miller 				break;
50234132e54SDamien Miller 			} else {
5037c77991fSdjm@openbsd.org 				oerrno = errno;
504728aa7e1SBen Lindstrom 				debug("connect to address %s port %s: %s",
505728aa7e1SBen Lindstrom 				    ntop, strport, strerror(errno));
506d4a8b7e3SDamien Miller 				close(sock);
50756e5e6adSDamien Miller 				sock = -1;
5087c77991fSdjm@openbsd.org 				errno = oerrno;
509d4a8b7e3SDamien Miller 			}
510d4a8b7e3SDamien Miller 		}
51156e5e6adSDamien Miller 		if (sock != -1)
51234132e54SDamien Miller 			break;	/* Successful connection. */
513d4a8b7e3SDamien Miller 	}
51434132e54SDamien Miller 
515d4a8b7e3SDamien Miller 	/* Return failure if we didn't get a successful connection. */
51656e5e6adSDamien Miller 	if (sock == -1) {
517b2161e37SDarren Tucker 		error("ssh: connect to host %s port %s: %s",
5187c77991fSdjm@openbsd.org 		    host, strport, errno == 0 ? "failure" : strerror(errno));
5197c77991fSdjm@openbsd.org 		return -1;
520a6cd75c4SBen Lindstrom 	}
521d4a8b7e3SDamien Miller 
522d4a8b7e3SDamien Miller 	debug("Connection established.");
523d4a8b7e3SDamien Miller 
52412c150e7SDamien Miller 	/* Set SO_KEEPALIVE if requested. */
52567bd062bSDamien Miller 	if (want_keepalive &&
526bf555ba6SBen Lindstrom 	    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
5274d28fa78Sderaadt@openbsd.org 	    sizeof(on)) == -1)
528bf555ba6SBen Lindstrom 		error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
529bf555ba6SBen Lindstrom 
530d4a8b7e3SDamien Miller 	/* Set the connection. */
531dbee4119Sdjm@openbsd.org 	if (ssh_packet_set_connection(ssh, sock, sock) == NULL)
532dbee4119Sdjm@openbsd.org 		return -1; /* ssh_packet_set_connection logs error */
533d4a8b7e3SDamien Miller 
534f9cedb9cSBen Lindstrom 	return 0;
535d4a8b7e3SDamien Miller }
536d4a8b7e3SDamien Miller 
5370faf747eSDamien Miller int
ssh_connect(struct ssh * ssh,const char * host,const char * host_arg,struct addrinfo * addrs,struct sockaddr_storage * hostaddr,u_short port,int connection_attempts,int * timeout_ms,int want_keepalive)538fbe24b14Sdjm@openbsd.org ssh_connect(struct ssh *ssh, const char *host, const char *host_arg,
539fbe24b14Sdjm@openbsd.org     struct addrinfo *addrs, struct sockaddr_storage *hostaddr, u_short port,
540a336ce8cSkn@openbsd.org     int connection_attempts, int *timeout_ms, int want_keepalive)
5410faf747eSDamien Miller {
5428e7bac35Smarkus@openbsd.org 	int in, out;
5440faf747eSDamien Miller 	if (options.proxy_command == NULL) {
545dbee4119Sdjm@openbsd.org 		return ssh_connect_direct(ssh, host, addrs, hostaddr, port,
546a336ce8cSkn@openbsd.org 		    connection_attempts, timeout_ms, want_keepalive);
5470faf747eSDamien Miller 	} else if (strcmp(options.proxy_command, "-") == 0) {
5484d28fa78Sderaadt@openbsd.org 		if ((in = dup(STDIN_FILENO)) == -1 ||
5494d28fa78Sderaadt@openbsd.org 		    (out = dup(STDOUT_FILENO)) == -1) {
5508e7bac35Smarkus@openbsd.org 			if (in >= 0)
5518e7bac35Smarkus@openbsd.org 				close(in);
552816036f1Sdjm@openbsd.org 			error_f("dup() in/out failed");
5538e7bac35Smarkus@openbsd.org 			return -1; /* ssh_packet_set_connection logs error */
5548e7bac35Smarkus@openbsd.org 		}
5558e7bac35Smarkus@openbsd.org 		if ((ssh_packet_set_connection(ssh, in, out)) == NULL)
556dbee4119Sdjm@openbsd.org 			return -1; /* ssh_packet_set_connection logs error */
557dbee4119Sdjm@openbsd.org 		return 0;
5580faf747eSDamien Miller 	} else if (options.proxy_use_fdpass) {
559fbe24b14Sdjm@openbsd.org 		return ssh_proxy_fdpass_connect(ssh, host, host_arg, port,
5600faf747eSDamien Miller 		    options.proxy_command);
5610faf747eSDamien Miller 	}
562fbe24b14Sdjm@openbsd.org 	return ssh_proxy_connect(ssh, host, host_arg, port,
563fbe24b14Sdjm@openbsd.org 	    options.proxy_command);
5640faf747eSDamien Miller }
5650faf747eSDamien Miller 
566b6c06d93SBen Lindstrom /* defaults to 'no' */
567bba81213SBen Lindstrom static int
confirm(const char * prompt,const char * fingerprint)56805b9a466Sdtucker@openbsd.org confirm(const char *prompt, const char *fingerprint)
569d4a8b7e3SDamien Miller {
57049d795c6SDamien Miller 	const char *msg, *again = "Please type 'yes' or 'no': ";
57105b9a466Sdtucker@openbsd.org 	const char *again_fp = "Please type 'yes', 'no' or the fingerprint: ";
57265cf8730Sdtucker@openbsd.org 	char *p, *cp;
57349d795c6SDamien Miller 	int ret = -1;
574d4a8b7e3SDamien Miller 
575b6c06d93SBen Lindstrom 	if (options.batch_mode)
576b6c06d93SBen Lindstrom 		return 0;
57705b9a466Sdtucker@openbsd.org 	for (msg = prompt;;msg = fingerprint ? again_fp : again) {
57865cf8730Sdtucker@openbsd.org 		cp = p = read_passphrase(msg, RP_ECHO);
579e0ce54c0Sdjm@openbsd.org 		if (p == NULL)
580e0ce54c0Sdjm@openbsd.org 			return 0;
58165cf8730Sdtucker@openbsd.org 		p += strspn(p, " \t"); /* skip leading whitespace */
58265cf8730Sdtucker@openbsd.org 		p[strcspn(p, " \t\n")] = '\0'; /* remove trailing whitespace */
583e0ce54c0Sdjm@openbsd.org 		if (p[0] == '\0' || strcasecmp(p, "no") == 0)
58449d795c6SDamien Miller 			ret = 0;
58505b9a466Sdtucker@openbsd.org 		else if (strcasecmp(p, "yes") == 0 || (fingerprint != NULL &&
586c3c786c3Sdjm@openbsd.org 		    strcmp(p, fingerprint) == 0))
58749d795c6SDamien Miller 			ret = 1;
58865cf8730Sdtucker@openbsd.org 		free(cp);
58949d795c6SDamien Miller 		if (ret != -1)
59049d795c6SDamien Miller 			return ret;
591d4a8b7e3SDamien Miller 	}
592d4a8b7e3SDamien Miller }
593d4a8b7e3SDamien Miller 
5940a80ca19SDamien Miller static int
sockaddr_is_local(struct sockaddr * hostaddr)595d925dcd8SDamien Miller sockaddr_is_local(struct sockaddr *hostaddr)
596d925dcd8SDamien Miller {
597d925dcd8SDamien Miller 	switch (hostaddr->sa_family) {
598d925dcd8SDamien Miller 	case AF_INET:
599d925dcd8SDamien Miller 		return (ntohl(((struct sockaddr_in *)hostaddr)->
600d925dcd8SDamien Miller 		    sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
601d925dcd8SDamien Miller 	case AF_INET6:
602d925dcd8SDamien Miller 		return IN6_IS_ADDR_LOOPBACK(
603d925dcd8SDamien Miller 		    &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
604d925dcd8SDamien Miller 	default:
605d925dcd8SDamien Miller 		return 0;
606d925dcd8SDamien Miller 	}
607d925dcd8SDamien Miller }